├── mail_gateway ├── tests │ ├── __init__.py │ └── common.py ├── tools │ ├── __init__.py │ └── discuss.py ├── controllers │ ├── __init__.py │ └── gateway.py ├── readme │ ├── ROADMAP.md │ ├── CREDITS.md │ ├── CONTRIBUTORS.md │ ├── CONFIGURATION.rst │ ├── DESCRIPTION.md │ └── USAGE.md ├── pyproject.toml ├── static │ ├── description │ │ └── icon.png │ └── src │ │ ├── components │ │ ├── gateway_follower │ │ │ ├── gateway_follower.scss │ │ │ ├── gateway_follower.xml │ │ │ └── gateway_follower.esm.js │ │ ├── chatter │ │ │ ├── chatter.esm.js │ │ │ └── chatter.xml │ │ ├── message_notification_popover_content │ │ │ └── message_notification_popover_content.xml │ │ └── message │ │ │ ├── message.xml │ │ │ └── message_patch.esm.js │ │ ├── core │ │ ├── common │ │ │ ├── persona_model_patch.esm.js │ │ │ ├── mail_composer_send_dropdown.xml │ │ │ ├── composer_model_patch.esm.js │ │ │ ├── store_service_patch.esm.js │ │ │ ├── message_model_patch.esm.js │ │ │ ├── discuss_app_model_patch.esm.js │ │ │ ├── message_actions.esm.js │ │ │ ├── mail_composer_send_dropdown.esm.js │ │ │ ├── notification_model_patch.esm.js │ │ │ └── thread_model_patch.esm.js │ │ └── web │ │ │ ├── discuss_sidebar_category_item_patch.xml │ │ │ ├── discuss_app_category_model_patch.esm.js │ │ │ └── gateway_core_web_service.esm.js │ │ └── models │ │ ├── gateway.esm.js │ │ ├── gateway_channel.esm.js │ │ └── gateway_follower.esm.js ├── __init__.py ├── wizards │ ├── __init__.py │ ├── mail_message_gateway_send.py │ ├── mail_message_gateway_link.xml │ ├── mail_guest_manage.xml │ ├── mail_message_gateway_send.xml │ ├── mail_message_gateway_link.py │ ├── mail_compose_gateway_message.xml │ ├── mail_guest_manage.py │ └── mail_compose_gateway_message.py ├── models │ ├── __init__.py │ ├── res_users_settings.py │ ├── res_users.py │ ├── mail_guest.py │ ├── ir_websocket.py │ ├── mail_notification.py │ ├── mail_gateway_abstract.py │ ├── res_partner.py │ └── discuss_channel.py ├── hooks.py ├── views │ ├── res_users_settings_views.xml │ ├── mail_guest_views.xml │ ├── res_partner_gateway_channel.xml │ └── mail_gateway.xml ├── __manifest__.py └── security │ ├── ir.model.access.csv │ └── security.xml ├── mail_activity_unlink_log ├── __init__.py ├── readme │ ├── CONTRIBUTORS.md │ └── DESCRIPTION.md ├── models │ ├── __init__.py │ └── mail_activity.py ├── tests │ ├── __init__.py │ └── test_activity_unlink.py ├── pyproject.toml ├── static │ └── description │ │ └── icon.png ├── __manifest__.py ├── i18n │ ├── mail_activity_unlink_log.pot │ ├── it.po │ └── es.po ├── data │ └── unlink_message.xml └── README.rst ├── mail_gateway_whatsapp ├── tools │ ├── __init__.py │ └── const.py ├── pyproject.toml ├── wizards │ ├── __init__.py │ ├── mail_compose_gateway_message.py │ ├── mail_compose_gateway_message.xml │ └── whatsapp_composer.xml ├── readme │ ├── ROADMAP.md │ ├── CREDITS.md │ ├── USAGE.md │ ├── DESCRIPTION.md │ ├── CONTRIBUTORS.md │ └── CONFIGURE.md ├── tests │ └── __init__.py ├── __init__.py ├── static │ ├── description │ │ ├── icon.png │ │ └── icon.svg │ └── src │ │ └── components │ │ ├── message │ │ ├── message.xml │ │ └── message_patch.esm.js │ │ ├── send_whatsapp_button │ │ ├── send_whatsapp_button.xml │ │ └── send_whatsapp_button.esm.js │ │ └── phone_field │ │ ├── phone_field.esm.js │ │ └── phone_field.xml ├── models │ ├── __init__.py │ ├── res_partner.py │ ├── discuss_channel.py │ ├── mail_thread.py │ └── mail_gateway.py ├── security │ ├── security.xml │ └── ir.model.access.csv └── __manifest__.py ├── res_company_mastodon_link ├── __init__.py ├── models │ ├── __init__.py │ └── res_company.py ├── readme │ ├── CONTRIBUTORS.md │ └── DESCRIPTION.md ├── pyproject.toml ├── static │ └── description │ │ ├── icon.png │ │ └── res_company_form.png ├── demo │ └── demo_res_company.xml ├── __manifest__.py ├── views │ └── view_res_company.xml ├── i18n │ ├── res_company_mastodon_link.pot │ ├── it.po │ └── fr.po └── README.rst ├── mail_notification_with_history ├── __init__.py ├── tests │ ├── __init__.py │ └── test_mail_notification_with_history.py ├── models │ ├── __init__.py │ ├── mail_thread.py │ └── mail_message.py ├── pyproject.toml ├── readme │ ├── CREDITS.md │ ├── CONTRIBUTORS.md │ ├── INSTALL.md │ └── DESCRIPTION.md ├── static │ └── description │ │ └── icon.png ├── __manifest__.py ├── data │ └── mail_data.xml ├── i18n │ ├── mail_notification_with_history.pot │ ├── pt_BR.po │ ├── it.po │ └── es.po └── README.rst ├── mail_thread_create_nolog ├── controllers │ ├── __init__.py │ └── thread.py ├── tests │ ├── __init__.py │ └── test_create_nolog.py ├── __init__.py ├── models │ ├── __init__.py │ ├── mail_thread.py │ └── mail_message.py ├── readme │ ├── ROADMAP.md │ ├── CREDITS.md │ ├── CONTRIBUTORS.md │ └── DESCRIPTION.md ├── pyproject.toml ├── static │ └── description │ │ └── icon.png ├── __manifest__.py ├── i18n │ ├── mail_thread_create_nolog.pot │ ├── it.po │ └── es.po └── README.rst ├── mail_gateway_telegram ├── tests │ ├── __init__.py │ └── sticker.tgs ├── __init__.py ├── pyproject.toml ├── readme │ ├── CONTRIBUTORS.md │ ├── CREDITS.md │ ├── USAGE.md │ ├── DESCRIPTION.md │ └── CONFIGURE.md ├── static │ ├── description │ │ ├── icon.png │ │ └── icon.svg │ └── src │ │ ├── components │ │ └── message │ │ │ └── message.xml │ │ └── models │ │ ├── message.esm.js │ │ ├── message_view.esm.js │ │ └── notification.esm.js ├── models │ ├── __init__.py │ ├── mail_gateway.py │ └── mail_gateway_channel.py ├── __manifest__.py ├── views │ └── mail_gateway.xml └── i18n │ └── mail_gateway_telegram.pot ├── checklog-odoo.cfg ├── requirements.txt ├── prettier.config.cjs ├── setup └── _metapackage │ └── pyproject.toml ├── .editorconfig ├── .ruff.toml ├── .copier-answers.yml ├── .gitignore ├── .github └── workflows │ ├── pre-commit.yml │ ├── test.yml │ └── stale.yml ├── README.md ├── .pylintrc-mandatory └── .pylintrc /mail_gateway/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mail_gateway/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from . import discuss 2 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /mail_gateway/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from . import gateway 2 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from . import const 2 | -------------------------------------------------------------------------------- /res_company_mastodon_link/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Enric Tobella 2 | -------------------------------------------------------------------------------- /mail_notification_with_history/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from . import thread 2 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mail_activity 2 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_create_nolog 2 | -------------------------------------------------------------------------------- /res_company_mastodon_link/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import res_company 2 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_activity_unlink 2 | -------------------------------------------------------------------------------- /mail_gateway/readme/ROADMAP.md: -------------------------------------------------------------------------------- 1 | 1. Make possible to schedule gateway messages. 2 | -------------------------------------------------------------------------------- /mail_gateway_telegram/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_mail_gateway_telegram 2 | -------------------------------------------------------------------------------- /checklog-odoo.cfg: -------------------------------------------------------------------------------- 1 | [checklog-odoo] 2 | ignore= 3 | WARNING.* 0 failed, 0 error\(s\).* 4 | -------------------------------------------------------------------------------- /mail_gateway_telegram/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | 3 | # from . import services 4 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | from . import controllers 3 | -------------------------------------------------------------------------------- /mail_notification_with_history/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_mail_notification_with_history 2 | -------------------------------------------------------------------------------- /mail_gateway/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mail_thread 2 | from . import mail_message 3 | -------------------------------------------------------------------------------- /res_company_mastodon_link/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Sylvain LE GAL \ 2 | -------------------------------------------------------------------------------- /mail_notification_with_history/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mail_message 2 | from . import mail_thread 3 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/readme/ROADMAP.md: -------------------------------------------------------------------------------- 1 | - Ability to configure the create_nolog context key by data model 2 | -------------------------------------------------------------------------------- /mail_gateway_telegram/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /res_company_mastodon_link/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /mail_gateway/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/mail_gateway/static/description/icon.png -------------------------------------------------------------------------------- /mail_gateway_telegram/tests/sticker.tgs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/mail_gateway_telegram/tests/sticker.tgs -------------------------------------------------------------------------------- /mail_gateway_whatsapp/wizards/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mail_compose_gateway_message 2 | from . import whatsapp_composer 3 | -------------------------------------------------------------------------------- /mail_notification_with_history/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /mail_gateway/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | This work has been funded by AEOdoo (Asociación Española de Odoo - 2 | ) 3 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/readme/ROADMAP.md: -------------------------------------------------------------------------------- 1 | **WhatsApp templates** 2 | 3 | - Add support for Variables 4 | - Add support for Buttons 5 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_mail_gateway_whatsapp 2 | from . import test_mail_whatsapp_template 3 | -------------------------------------------------------------------------------- /mail_gateway/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Enric Tobella 2 | - Olga Marco 3 | 4 | - [Tecnativa](https://tecnativa.com) 5 | - Carlos Roca 6 | -------------------------------------------------------------------------------- /mail_gateway_telegram/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Enric Tobella \ 2 | - Olga Marco \ 3 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | The migration of this module from 14.0 to 18.0 was financially supported by Camptocamp. 2 | -------------------------------------------------------------------------------- /mail_gateway_telegram/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | This work has been funded by AEOdoo (Asociación Española de Odoo - 2 | ) 3 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | from . import tools 3 | 4 | # from . import services 5 | from . import wizards 6 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | This work has been funded by AEOdoo (Asociación Española de Odoo - 2 | ) 3 | -------------------------------------------------------------------------------- /mail_notification_with_history/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | The migration of this module from 14.0 to 18.0 was financially supported by Camptocamp. 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # generated from manifests external_dependencies 2 | cairosvg 3 | lottie 4 | python-telegram-bot 5 | requests_toolbelt 6 | -------------------------------------------------------------------------------- /mail_gateway_telegram/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/mail_gateway_telegram/static/description/icon.png -------------------------------------------------------------------------------- /mail_gateway_whatsapp/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/mail_gateway_whatsapp/static/description/icon.png -------------------------------------------------------------------------------- /mail_activity_unlink_log/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/mail_activity_unlink_log/static/description/icon.png -------------------------------------------------------------------------------- /mail_gateway_telegram/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mail_gateway_channel 2 | from . import mail_gateway 3 | from . import mail_gateway_telegram 4 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - [Camptocamp](https://www.camptocamp.com): 2 | - Sébastien Alix 3 | - Chau Le 4 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/mail_thread_create_nolog/static/description/icon.png -------------------------------------------------------------------------------- /res_company_mastodon_link/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/res_company_mastodon_link/static/description/icon.png -------------------------------------------------------------------------------- /mail_notification_with_history/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/mail_notification_with_history/static/description/icon.png -------------------------------------------------------------------------------- /mail_gateway/__init__.py: -------------------------------------------------------------------------------- 1 | from . import controllers 2 | from . import models 3 | from . import tools 4 | 5 | from .hooks import pre_init_hook 6 | from . import wizards 7 | -------------------------------------------------------------------------------- /res_company_mastodon_link/static/description/res_company_form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/social/HEAD/res_company_mastodon_link/static/description/res_company_form.png -------------------------------------------------------------------------------- /mail_gateway_whatsapp/readme/USAGE.md: -------------------------------------------------------------------------------- 1 | 1. Access Gateway 2 | 2. Wait until someone starts a conversation. 3 | 3. Now you will be able to respond and receive messages to this person. 4 | -------------------------------------------------------------------------------- /mail_gateway/wizards/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mail_guest_manage 2 | from . import mail_message_gateway_send 3 | from . import mail_message_gateway_link 4 | from . import mail_compose_gateway_message 5 | -------------------------------------------------------------------------------- /mail_gateway_telegram/readme/USAGE.md: -------------------------------------------------------------------------------- 1 | 1. Access Gateway 2 | 2. Wait until someone starts a conversation with your bot. 3 | 3. Now you will be able to respond and receive messages to this person. 4 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module allows to respond whatsapp chats. 2 | 3 | This way, a group of users can respond customers or any other set of 4 | partners in an integrated way. 5 | -------------------------------------------------------------------------------- /mail_notification_with_history/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - [Camptocamp](https://www.camptocamp.com) 2 | 3 | > - Thierry Ducrest \<\> 4 | > - Tris Doan \<\> 5 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Olga Marco \<\> 2 | 3 | - Enric Tobella \<\> 4 | 5 | - [Tecnativa](https://www.tecnativa.com): 6 | 7 | > - Carlos Lopez 8 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This addon enforces the creation of a message when an activity is 2 | unlinked. This change tries to avoid a possible bad practice of deletion 3 | of an activity without notifying the deletion. 4 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mail_gateway 2 | from . import mail_thread 3 | from . import mail_gateway_whatsapp 4 | from . import discuss_channel 5 | from . import res_partner 6 | from . import mail_whatsapp_template 7 | -------------------------------------------------------------------------------- /mail_gateway/readme/CONFIGURATION.rst: -------------------------------------------------------------------------------- 1 | - Access in development mode 2 | - Go to `Settings / Technical / Email / Gateway` 3 | - Create a gateway. Follow the instruction of the specific tab in order to integrate it. 4 | - Start receiving notifications 5 | -------------------------------------------------------------------------------- /mail_notification_with_history/readme/INSTALL.md: -------------------------------------------------------------------------------- 1 | After installing the module its behaviour needs to be activated. This is 2 | done by setting the following class variable 3 | \_mail_notification_include_history to True, on the model that we would 4 | like to activate it. 5 | -------------------------------------------------------------------------------- /mail_notification_with_history/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | Followers of a discussion in the chatter of Odoo can be informed by 2 | email of a new message being created. This module adds in that email the 3 | history of the chatter discussion to help the recipient with more 4 | context. 5 | -------------------------------------------------------------------------------- /res_company_mastodon_link/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module adds a new "Mastodon URL" field, at company level. 2 | 3 | ![res_company_form](../static/description/res_company_form.png) 4 | 5 | Mastodon is a decentralized social network developed in Open Source. 6 | More information at . 7 | -------------------------------------------------------------------------------- /mail_notification_with_history/models/mail_thread.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | 4 | from odoo import models 5 | 6 | 7 | class MailThread(models.AbstractModel): 8 | _inherit = "mail.thread" 9 | 10 | _mail_notification_include_history = False 11 | -------------------------------------------------------------------------------- /mail_gateway_telegram/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module allows to respond telegram chats as a telegram bot. 2 | 3 | This way, a group of users can respond customers or any other set of 4 | partners in an integrated way. 5 | 6 | It is not intended to be integrated on default chatter as users don't 7 | need to review again when one has responded. 8 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module will block the creation in the database of 'Record created' 2 | messages on data models inheriting from mail.thread, but will instead 3 | generate this message on the fly when Odoo displays the messages of a 4 | record. 5 | 6 | This allows to reduce the size of the mail_message table. 7 | -------------------------------------------------------------------------------- /mail_gateway/static/src/components/gateway_follower/gateway_follower.scss: -------------------------------------------------------------------------------- 1 | .o_gateway_composer_selector { 2 | select { 3 | width: unset; 4 | display: inline-block; 5 | min-width: 15%; 6 | } 7 | 8 | .o_gateway_composer_selector_partner { 9 | min-width: 30%; 10 | display: inline-block; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /res_company_mastodon_link/models/res_company.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2025 - Today: Sylvain LE GAL (http://www.grap.coop) 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class Company(models.Model): 8 | _inherit = "res.company" 9 | 10 | social_mastodon = fields.Char("Mastodon Account") 11 | -------------------------------------------------------------------------------- /mail_gateway/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ir_websocket 2 | from . import mail_gateway_abstract 3 | from . import mail_message 4 | from . import mail_notification 5 | from . import discuss_channel 6 | from . import mail_gateway 7 | from . import res_partner 8 | from . import mail_guest 9 | from . import res_users 10 | from . import res_users_settings 11 | from . import mail_thread 12 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/persona_model_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {Persona} from "@mail/core/common/persona_model"; 2 | import {Record} from "@mail/core/common/record"; 3 | import {patch} from "@web/core/utils/patch"; 4 | 5 | patch(Persona.prototype, { 6 | setup() { 7 | super.setup(); 8 | this.gateway_channels = Record.many("GatewayChannel"); 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /mail_gateway/hooks.py: -------------------------------------------------------------------------------- 1 | def pre_init_hook(env): 2 | """ 3 | The objective of this hook is to speed up the installation 4 | of the module on an existing Odoo instance. 5 | 6 | Without this script, big databases can take a long time to install this 7 | module. 8 | """ 9 | env.cr.execute( 10 | """ALTER TABLE mail_message 11 | ADD COLUMN IF NOT EXISTS gateway_channel_id int""" 12 | ) 13 | -------------------------------------------------------------------------------- /mail_gateway/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module will allow you to integrate an external chat system in your 2 | Odoo system. It requires extra modules with the specific configuration 3 | of each chat system, like mail_gateway_telegram or 4 | mail_gateway_whatsapp. 5 | 6 | This way, a group of users can respond customers or any other set of 7 | partners within Odoo, but the messages will be sent through the external 8 | chat system. 9 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/security/security.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WhatsApp template: multicompany 5 | 6 | [('company_id', 'in', company_ids + [False])] 7 | 8 | 9 | -------------------------------------------------------------------------------- /res_company_mastodon_link/demo/demo_res_company.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | https://pouet.chapril.org/@oca_france 9 | 10 | 11 | -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | 3 | const config = { 4 | // https://github.com/prettier/prettier/issues/15388#issuecomment-1717746872 5 | plugins: [require.resolve("@prettier/plugin-xml")], 6 | bracketSpacing: false, 7 | printWidth: 88, 8 | proseWrap: "always", 9 | semi: true, 10 | trailingComma: "es5", 11 | xmlWhitespaceSensitivity: "preserve", 12 | }; 13 | 14 | module.exports = config; 15 | -------------------------------------------------------------------------------- /mail_gateway_telegram/models/mail_gateway.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | from odoo import fields, models 4 | 5 | 6 | class MailGateway(models.Model): 7 | _inherit = "mail.gateway" 8 | 9 | telegram_security_key = fields.Char() 10 | gateway_type = fields.Selection( 11 | selection_add=[("telegram", "Telegram")], ondelete={"telegram": "cascade"} 12 | ) 13 | -------------------------------------------------------------------------------- /mail_gateway/models/res_users_settings.py: -------------------------------------------------------------------------------- 1 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 2 | from odoo import fields, models 3 | 4 | 5 | class ResUsersSettings(models.Model): 6 | _inherit = "res.users.settings" 7 | 8 | is_discuss_sidebar_category_gateway_open = fields.Boolean( 9 | string="Gateway Category Open", 10 | default=True, 11 | help="The gateway category in the sidebar will be open", 12 | ) 13 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/security/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 2 | access_whatsapp_composer,access.whatsapp.composer,model_whatsapp_composer,base.group_user,1,1,1,0 3 | access_mail_whatsapp_template_group_system,mail_whatsapp_template_group_system,model_mail_whatsapp_template,base.group_system,1,1,1,1 4 | access_mail_whatsapp_template_group_user,mail_whatsapp_template_group_user,model_mail_whatsapp_template,base.group_user,1,0,0,0 5 | -------------------------------------------------------------------------------- /mail_gateway/models/res_users.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class ResUsers(models.Model): 8 | _inherit = "res.users" 9 | 10 | gateway_ids = fields.Many2many("mail.gateway") 11 | 12 | def _init_messaging(self, store): 13 | result = super()._init_messaging(store) 14 | store.add({"gateways": self.gateway_ids.gateway_info()}) 15 | return result 16 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/static/src/components/message/message.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /mail_gateway_telegram/static/src/components/message/message.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 CreuBlanca 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | { 5 | "name": "Mail Activities: log on unlink", 6 | "summary": """ 7 | Leave a message when an activity is unlinked""", 8 | "version": "18.0.1.0.0", 9 | "license": "AGPL-3", 10 | "author": "CreuBlanca,Odoo Community Association (OCA)", 11 | "website": "https://github.com/OCA/social", 12 | "depends": ["mail"], 13 | "data": ["data/unlink_message.xml"], 14 | } 15 | -------------------------------------------------------------------------------- /mail_gateway/static/src/components/chatter/chatter.esm.js: -------------------------------------------------------------------------------- 1 | import {Chatter} from "@mail/chatter/web_portal/chatter"; 2 | import {GatewayFollower} from "../gateway_follower/gateway_follower.esm"; 3 | import {patch} from "@web/core/utils/patch"; 4 | 5 | patch(Chatter, { 6 | components: {...Chatter.components, GatewayFollower}, 7 | }); 8 | 9 | patch(Chatter.prototype, { 10 | toggleComposer(mode = false) { 11 | this.state.thread.composer.isGateway = mode === "gateway"; 12 | super.toggleComposer(mode); 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /mail_gateway/static/src/components/message_notification_popover_content/message_notification_popover_content.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /mail_gateway/views/res_users_settings_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | res.users.settings 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /mail_gateway/tools/discuss.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 Tecnativa - Carlos Roca 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo.addons.mail.tools.discuss import Store 5 | 6 | original_one_id = Store.one_id 7 | 8 | 9 | def extended_one_id(record, /, *, as_thread=False): 10 | result = original_one_id(record, as_thread=as_thread) 11 | if result and record._name == "res.partner": 12 | result["gateway_channels"] = record.sudo().gateway_channel_ids.mail_format() 13 | return result 14 | 15 | 16 | Store.one_id = staticmethod(extended_one_id) 17 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/web/discuss_sidebar_category_item_patch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) 3 | { 4 | "name": "Mail Thread - Fake create log", 5 | "summary": "Display a fake (non-stored) create log in the chatter.", 6 | "version": "18.0.1.0.2", 7 | "category": "Mail", 8 | "website": "https://github.com/OCA/social", 9 | "author": "Camptocamp, Odoo Community Association (OCA)", 10 | "maintainers": ["sebalix"], 11 | "development_status": "Alpha", 12 | "license": "AGPL-3", 13 | "installable": True, 14 | "depends": ["mail"], 15 | } 16 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/static/src/components/send_whatsapp_button/send_whatsapp_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | Whatsapp 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /mail_gateway/tests/common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo.tests.common import HttpCase 5 | 6 | 7 | class MailGatewayTestCase(HttpCase): 8 | @classmethod 9 | def setUpClass(cls): 10 | super().setUpClass() 11 | cls._setup_env() 12 | 13 | @classmethod 14 | def _setup_context(cls): 15 | return dict( 16 | cls.env.context, tracking_disable=True, test_queue_job_no_delay=True 17 | ) 18 | 19 | @classmethod 20 | def _setup_env(cls): 21 | cls.env = cls.env(context=cls._setup_context()) 22 | -------------------------------------------------------------------------------- /setup/_metapackage/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "odoo-addons-oca-social" 3 | version = "18.0.20251114.0" 4 | dependencies = [ 5 | "odoo-addon-mail_activity_unlink_log==18.0.*", 6 | "odoo-addon-mail_gateway==18.0.*", 7 | "odoo-addon-mail_gateway_telegram==18.0.*", 8 | "odoo-addon-mail_gateway_whatsapp==18.0.*", 9 | "odoo-addon-mail_notification_with_history==18.0.*", 10 | "odoo-addon-mail_thread_create_nolog==18.0.*", 11 | "odoo-addon-res_company_mastodon_link==18.0.*", 12 | ] 13 | classifiers=[ 14 | "Programming Language :: Python", 15 | "Framework :: Odoo", 16 | "Framework :: Odoo :: 18.0", 17 | ] 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Configuration for known file extensions 2 | [*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}] 3 | charset = utf-8 4 | end_of_line = lf 5 | indent_size = 4 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.{json,yml,yaml,rst,md}] 11 | indent_size = 2 12 | 13 | # Do not configure editor for libs and autogenerated content 14 | [{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}] 15 | charset = unset 16 | end_of_line = unset 17 | indent_size = unset 18 | indent_style = unset 19 | insert_final_newline = false 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /mail_gateway_telegram/static/src/models/message.esm.js: -------------------------------------------------------------------------------- 1 | /** @odoo-module **/ 2 | 3 | import {registerPatch} from "@mail/model/model_core"; 4 | 5 | registerPatch({ 6 | name: "Message", 7 | fields: { 8 | avatarUrl: { 9 | compute() { 10 | if ( 11 | !this.author && 12 | !this.guestAuthor && 13 | this.gateway_type === "telegram" 14 | ) { 15 | return "/mail_gateway_telegram/static/description/icon.png"; 16 | } 17 | return this._super(); 18 | }, 19 | }, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/static/src/components/message/message_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {Message} from "@mail/core/common/message"; 2 | import {patch} from "@web/core/utils/patch"; 3 | import {url} from "@web/core/utils/urls"; 4 | 5 | patch(Message.prototype, { 6 | get authorAvatarUrl() { 7 | if ( 8 | this.message.gateway_type && 9 | this.message.gateway_type.includes("whatsapp") && 10 | !["partner", "guest"].includes(this.message.author?.type) 11 | ) { 12 | return url("/mail_gateway_whatsapp/static/description/icon.png"); 13 | } 14 | return super.authorAvatarUrl; 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /mail_gateway/views/mail_guest_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mail.guest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /res_company_mastodon_link/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2025 - Today: Sylvain LE GAL (http://www.grap.coop) 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | { 5 | "name": "Social Media - Mastodon", 6 | "summary": "Add mastodon url at company model", 7 | "version": "18.0.1.0.0", 8 | "license": "AGPL-3", 9 | "author": "GRAP, Odoo Community Association (OCA)", 10 | "maintainers": ["legalsylvain"], 11 | "website": "https://github.com/OCA/social", 12 | "installable": True, 13 | "depends": ["social_media"], 14 | "data": ["views/view_res_company.xml"], 15 | "demo": ["demo/demo_res_company.xml"], 16 | } 17 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/mail_composer_send_dropdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | env.model.config.resModel === "mail.compose.message" 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /mail_gateway/readme/USAGE.md: -------------------------------------------------------------------------------- 1 | When external messages are received, they will be directly sent to the 2 | discuss menu. Answering to these messages will send the answer to the 3 | external contact. We can assign this messages to any record using the 4 | message actions. Also, we can assign the sender to a partner using the 5 | followers menu and selecting the partner. 6 | 7 | On a standard record associated to a partner with external chat, we can 8 | send messages to the external contact directly selecting the methods of 9 | the partner. To use this, we just need to use the 10 | 11 | It is recomended to enable chatter notification to all users that will 12 | receive messages from gateways. 13 | -------------------------------------------------------------------------------- /mail_notification_with_history/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | 4 | { 5 | "name": "Mail Notification With History", 6 | "summary": """Add the previous chatter discussion into new email notifications.""", 7 | "version": "18.0.1.0.0", 8 | "category": "Social Network", 9 | "website": "https://github.com/OCA/social", 10 | "author": "Camptocamp, Odoo Community Association (OCA)", 11 | "license": "AGPL-3", 12 | "installable": True, 13 | "depends": ["mail"], 14 | "data": ["data/mail_data.xml"], 15 | "development_status": "Beta", 16 | "maintainers": ["TDu"], 17 | } 18 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/models/res_partner.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import models 5 | 6 | 7 | class ResPartner(models.Model): 8 | _name = "res.partner" 9 | _inherit = ["mail.thread.phone", "res.partner"] 10 | 11 | def _whatsapp_get_partner(self): 12 | return self 13 | 14 | def _phone_get_number_fields(self): 15 | """This method returns the fields to use to find the number to use to 16 | send an SMS on a record.""" 17 | result = set(super()._phone_get_number_fields()) 18 | result.add("mobile") 19 | result.add("phone") 20 | return list(result) 21 | -------------------------------------------------------------------------------- /mail_gateway/static/src/models/gateway.esm.js: -------------------------------------------------------------------------------- 1 | import {Record} from "@mail/core/common/record"; 2 | 3 | export class Gateway extends Record { 4 | static id = "id"; 5 | /** @type {Object.} */ 6 | static records = {}; 7 | /** @returns {import("models").Gateway} */ 8 | static get(data) { 9 | return super.get(data); 10 | } 11 | /** @returns {import("models").Gateway|import("models").Gateway[]} */ 12 | static insert() { 13 | return super.insert(...arguments); 14 | } 15 | /** @type {Number} */ 16 | id; 17 | /** @type {String} */ 18 | type; 19 | /** @type {String} */ 20 | name; 21 | } 22 | Gateway.register(); 23 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/composer_model_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {Composer} from "@mail/core/common/composer_model"; 2 | import {patch} from "@web/core/utils/patch"; 3 | import {url} from "@web/core/utils/urls"; 4 | 5 | patch(Composer, { 6 | get resUrl() { 7 | if (!this.gateway_thread_data) { 8 | return super.resUrl; 9 | } 10 | return `${url("/web")}#model=${this.gateway_thread_data.model}&id=${ 11 | this.gateway_thread_data.id 12 | }`; 13 | }, 14 | }); 15 | 16 | patch(Composer.prototype, { 17 | setup() { 18 | super.setup(); 19 | this.gateway_channel = false; 20 | this.gateway_partner = false; 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /mail_gateway/models/mail_guest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | from odoo.addons.mail.tools.discuss import Store 7 | 8 | 9 | class MailGuest(models.Model): 10 | _inherit = "mail.guest" 11 | 12 | gateway_id = fields.Many2one("mail.gateway") 13 | gateway_token = fields.Char() 14 | 15 | def _to_store(self, store: Store, /, *, fields=None): 16 | result = super()._to_store(store, fields=fields) 17 | if not fields or "gateway_id" in fields: 18 | for record in self: 19 | store.add(record, {"gateway": {"id": record.gateway_id.id}}) 20 | return result 21 | -------------------------------------------------------------------------------- /mail_gateway/wizards/mail_message_gateway_send.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class MailMessageGatewaySend(models.TransientModel): 8 | _name = "mail.message.gateway.send" 9 | _description = "Send Message through gateway" 10 | 11 | message_id = fields.Many2one("mail.message", required=True) 12 | partner_id = fields.Many2one("res.partner", required=True) 13 | gateway_channel_id = fields.Many2one( 14 | "res.partner.gateway.channel", 15 | required=True, 16 | ) 17 | 18 | def send(self): 19 | self.message_id._send_to_gateway_thread(self.gateway_channel_id) 20 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/web/discuss_app_category_model_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {DiscussAppCategory} from "@mail/core/public_web/discuss_app_category_model"; 2 | import {compareDatetime} from "@mail/utils/common/misc"; 3 | import {patch} from "@web/core/utils/patch"; 4 | 5 | patch(DiscussAppCategory.prototype, { 6 | /** 7 | * @param {import("models").Thread} t1 8 | * @param {import("models").Thread} t2 9 | */ 10 | sortThreads(t1, t2) { 11 | if (this.id === "gateway") { 12 | return ( 13 | compareDatetime(t2.lastInterestDateTime, t1.lastInterestDateTime) || 14 | t2.id - t1.id 15 | ); 16 | } 17 | return super.sortThreads(t1, t2); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /res_company_mastodon_link/views/view_res_company.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | res.company 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /mail_gateway/static/src/models/gateway_channel.esm.js: -------------------------------------------------------------------------------- 1 | import {Record} from "@mail/core/common/record"; 2 | 3 | export class GatewayChannel extends Record { 4 | static id = "id"; 5 | /** @type {Object.} */ 6 | static records = {}; 7 | /** @returns {import("models").GatewayChannel} */ 8 | static get(data) { 9 | return super.get(data); 10 | } 11 | /** @returns {import("models").GatewayChannel|import("models").GatewayChannel[]} */ 12 | static insert() { 13 | return super.insert(...arguments); 14 | } 15 | /** @type {Number} */ 16 | id; 17 | /** @type {String} */ 18 | name; 19 | gateway = Record.one("Gateway"); 20 | } 21 | GatewayChannel.register(); 22 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/models/mail_thread.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | 4 | from odoo import api, models 5 | 6 | 7 | class MailThread(models.AbstractModel): 8 | _inherit = "mail.thread" 9 | 10 | @api.model_create_multi 11 | def create(self, vals_list): 12 | # Overridden to not create 'Record created' messages that increase 13 | # the size of 'mail_message' table for little value. 14 | # Instead a message will be generated on the fly when the Odoo client 15 | # will retrieve the list of messages for a given record. 16 | return super(MailThread, self.with_context(mail_create_nolog=True)).create( 17 | vals_list 18 | ) 19 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/store_service_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {Store} from "@mail/core/common/store_service"; 2 | import {patch} from "@web/core/utils/patch"; 3 | 4 | patch(Store.prototype, { 5 | async fetchData(thread, ...args) { 6 | const result = await super.fetchData(thread, ...args); 7 | thread.gateway_followers = result.gateway_followers; 8 | return result; 9 | }, 10 | async getMessagePostParams(params) { 11 | const post_params = await super.getMessagePostParams(...arguments); 12 | if (params.thread.gateway_notifications) { 13 | post_params.post_data.gateway_notifications = 14 | params.thread.gateway_notifications; 15 | } 16 | return post_params; 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | 2 | target-version = "py310" 3 | fix = true 4 | 5 | [lint] 6 | extend-select = [ 7 | "B", 8 | "C90", 9 | "E501", # line too long (default 88) 10 | "I", # isort 11 | "UP", # pyupgrade 12 | ] 13 | extend-safe-fixes = ["UP008"] 14 | exclude = ["setup/*"] 15 | 16 | [format] 17 | exclude = ["setup/*"] 18 | 19 | [lint.per-file-ignores] 20 | "__init__.py" = ["F401", "I001"] # ignore unused and unsorted imports in __init__.py 21 | "__manifest__.py" = ["B018"] # useless expression 22 | 23 | [lint.isort] 24 | section-order = ["future", "standard-library", "third-party", "odoo", "odoo-addons", "first-party", "local-folder"] 25 | 26 | [lint.isort.sections] 27 | "odoo" = ["odoo"] 28 | "odoo-addons" = ["odoo.addons"] 29 | 30 | [lint.mccabe] 31 | max-complexity = 16 32 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/i18n/mail_thread_create_nolog.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_thread_create_nolog 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: mail_thread_create_nolog 17 | #: model:ir.model,name:mail_thread_create_nolog.model_mail_thread 18 | msgid "Email Thread" 19 | msgstr "" 20 | 21 | #. module: mail_thread_create_nolog 22 | #: model:ir.model,name:mail_thread_create_nolog.model_mail_message 23 | msgid "Message" 24 | msgstr "" 25 | -------------------------------------------------------------------------------- /mail_gateway/static/src/models/gateway_follower.esm.js: -------------------------------------------------------------------------------- 1 | import {Record} from "@mail/core/common/record"; 2 | 3 | export class GatewayFollower extends Record { 4 | static id = "id"; 5 | /** @type {Object.} */ 6 | static records = {}; 7 | /** @returns {import("models").GatewayFollower} */ 8 | static get(data) { 9 | return super.get(data); 10 | } 11 | /** @returns {import("models").GatewayFollower|import("models").GatewayFollower[]} */ 12 | static insert() { 13 | return super.insert(...arguments); 14 | } 15 | /** @type {Number} */ 16 | id; 17 | /** @type {String} */ 18 | name; 19 | partner = Record.one("Persona"); 20 | channel = Record.one("GatewayChannel"); 21 | } 22 | 23 | GatewayFollower.register(); 24 | -------------------------------------------------------------------------------- /mail_gateway/models/ir_websocket.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | from odoo import models 4 | from odoo.http import request 5 | 6 | from odoo.addons.bus.websocket import wsrequest 7 | 8 | 9 | class IrWebsocket(models.AbstractModel): 10 | _inherit = "ir.websocket" 11 | 12 | def _build_bus_channel_list(self, channels): 13 | req = request or wsrequest 14 | result = super()._build_bus_channel_list(channels) 15 | if req.session.uid: 16 | if req.env.user.has_group("mail_gateway.gateway_user"): 17 | for channel in req.env["discuss.channel"].search( 18 | [("channel_type", "=", "gateway")] 19 | ): 20 | result.append(channel) 21 | return result 22 | -------------------------------------------------------------------------------- /mail_gateway_telegram/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | { 5 | "name": "Mail Telegram Gateway", 6 | "summary": """ 7 | Set a gateway for telegram""", 8 | "version": "18.0.1.0.0", 9 | "license": "AGPL-3", 10 | "author": "Creu Blanca,Dixmit,Odoo Community Association (OCA)", 11 | "website": "https://github.com/OCA/social", 12 | "depends": ["mail_gateway"], 13 | "data": ["views/mail_gateway.xml"], 14 | "external_dependencies": {"python": ["python-telegram-bot", "lottie", "cairosvg"]}, 15 | "assets": { 16 | "mail.assets_messaging": [ 17 | "mail_gateway_telegram/static/src/models/**/*.js", 18 | "mail_gateway_telegram/static/src/components/**/*.xml", 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/static/src/components/phone_field/phone_field.esm.js: -------------------------------------------------------------------------------- 1 | import {PhoneField} from "@web/views/fields/phone/phone_field"; 2 | import {SendWhatsappButton} from "../send_whatsapp_button/send_whatsapp_button.esm"; 3 | import {patch} from "@web/core/utils/patch"; 4 | 5 | patch(PhoneField, { 6 | components: { 7 | ...PhoneField.components, 8 | SendWhatsappButton, 9 | }, 10 | defaultProps: { 11 | ...PhoneField.defaultProps, 12 | enableButton: true, 13 | }, 14 | props: { 15 | ...PhoneField.props, 16 | enableButton: {type: Boolean, optional: true}, 17 | }, 18 | extractProps: ({attrs}) => { 19 | return { 20 | enableButton: attrs.options.enable_sms, 21 | placeholder: attrs.placeholder, 22 | }; 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /res_company_mastodon_link/i18n/res_company_mastodon_link.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * res_company_mastodon_link 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: res_company_mastodon_link 17 | #: model:ir.model,name:res_company_mastodon_link.model_res_company 18 | msgid "Companies" 19 | msgstr "" 20 | 21 | #. module: res_company_mastodon_link 22 | #: model:ir.model.fields,field_description:res_company_mastodon_link.field_res_company__social_mastodon 23 | msgid "Mastodon Account" 24 | msgstr "" 25 | -------------------------------------------------------------------------------- /mail_gateway_telegram/static/src/models/message_view.esm.js: -------------------------------------------------------------------------------- 1 | /** @odoo-module **/ 2 | 3 | import {registerPatch} from "@mail/model/model_core"; 4 | 5 | registerPatch({ 6 | name: "MessageView", 7 | fields: { 8 | notificationIconClassName: { 9 | compute() { 10 | if (this.message && this.message.gateway_type === "telegram") { 11 | return "fa fa-paper-plane"; 12 | } 13 | return this._super(); 14 | }, 15 | }, 16 | failureNotificationIconClassName: { 17 | compute() { 18 | if (this.message && this.message.gateway_type === "telegram") { 19 | return "fa fa-paper-plane"; 20 | } 21 | return this._super(); 22 | }, 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/message_model_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {Message} from "@mail/core/common/message_model"; 2 | import {patch} from "@web/core/utils/patch"; 3 | import {url} from "@web/core/utils/urls"; 4 | import {stateToUrl} from "@web/core/browser/router"; 5 | 6 | patch(Message.prototype, { 7 | setup() { 8 | super.setup(...arguments); 9 | this.gateway_thread_data = {}; 10 | }, 11 | get resUrl() { 12 | if (!this.gateway_thread_data) { 13 | return super.resUrl; 14 | } 15 | return url( 16 | stateToUrl({ 17 | model: this.gateway_thread_data.model, 18 | resId: this.gateway_thread_data.id, 19 | }) 20 | ); 21 | }, 22 | get editable() { 23 | return super.editable && !this.gateway_type; 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /mail_gateway_telegram/static/src/models/notification.esm.js: -------------------------------------------------------------------------------- 1 | /** @odoo-module **/ 2 | 3 | import {registerPatch} from "@mail/model/model_core"; 4 | 5 | registerPatch({ 6 | name: "Notification", 7 | fields: { 8 | iconClass: { 9 | compute() { 10 | if ( 11 | this.notification_type === "gateway" && 12 | this.gateway_type === "telegram" 13 | ) { 14 | switch (this.notification_status) { 15 | case "sent": 16 | return "fa fa-paper-plane"; 17 | case "exception": 18 | return "fa fa-paper-plane text-danger"; 19 | } 20 | } 21 | return this._super(); 22 | }, 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/models/discuss_channel.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import models 5 | from odoo.tools.misc import file_path 6 | 7 | from odoo.addons.base.models.avatar_mixin import get_hsl_from_seed 8 | 9 | 10 | class DiscussChannel(models.Model): 11 | _inherit = "discuss.channel" 12 | 13 | def _generate_avatar_gateway(self): 14 | if self.gateway_id.gateway_type == "whatsapp": 15 | path = file_path("mail_gateway_whatsapp/static/description/icon.svg") 16 | with open(path) as f: 17 | avatar = f.read() 18 | 19 | bgcolor = get_hsl_from_seed(self.uuid) 20 | avatar = avatar.replace("fill:#875a7b", f"fill:{bgcolor}") 21 | return avatar 22 | return super()._generate_avatar_gateway() 23 | -------------------------------------------------------------------------------- /mail_gateway_telegram/models/mail_gateway_channel.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import models 5 | from odoo.tools.misc import file_path 6 | 7 | from odoo.addons.base.models.avatar_mixin import get_hsl_from_seed 8 | 9 | 10 | class MailChannel(models.Model): 11 | _inherit = "discuss.channel" 12 | 13 | def _generate_avatar_gateway(self): 14 | if self.gateway_id.gateway_type == "telegram": 15 | path = file_path("mail_gateway_telegram/static/description/icon.svg") 16 | with open(path) as f: 17 | avatar = f.read() 18 | 19 | bgcolor = get_hsl_from_seed(self.uuid) 20 | avatar = avatar.replace("fill:#875a7b", f"fill:{bgcolor}") 21 | return avatar 22 | return super()._generate_avatar_gateway() 23 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/static/src/components/phone_field/phone_field.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /mail_gateway/static/src/components/message/message.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 14 | 15 | on 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /res_company_mastodon_link/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * res_company_mastodon_link 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: Automatically generated\n" 10 | "Language-Team: none\n" 11 | "Language: it\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: \n" 15 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 16 | 17 | #. module: res_company_mastodon_link 18 | #: model:ir.model,name:res_company_mastodon_link.model_res_company 19 | msgid "Companies" 20 | msgstr "" 21 | 22 | #. module: res_company_mastodon_link 23 | #: model:ir.model.fields,field_description:res_company_mastodon_link.field_res_company__social_mastodon 24 | msgid "Mastodon Account" 25 | msgstr "" 26 | -------------------------------------------------------------------------------- /.copier-answers.yml: -------------------------------------------------------------------------------- 1 | # Do NOT update manually; changes here will be overwritten by Copier 2 | _commit: v1.29 3 | _src_path: git+https://github.com/OCA/oca-addons-repo-template 4 | additional_ruff_rules: [] 5 | ci: GitHub 6 | convert_readme_fragments_to_markdown: true 7 | enable_checklog_odoo: true 8 | generate_requirements_txt: true 9 | github_check_license: true 10 | github_ci_extra_env: {} 11 | github_enable_codecov: true 12 | github_enable_makepot: true 13 | github_enable_stale_action: true 14 | github_enforce_dev_status_compatibility: true 15 | include_wkhtmltopdf: false 16 | odoo_test_flavor: Both 17 | odoo_version: 18.0 18 | org_name: Odoo Community Association (OCA) 19 | org_slug: OCA 20 | rebel_module_groups: [] 21 | repo_description: social 22 | repo_name: social 23 | repo_slug: social 24 | repo_website: https://github.com/OCA/social 25 | use_pyproject_toml: true 26 | use_ruff: true 27 | 28 | -------------------------------------------------------------------------------- /res_company_mastodon_link/i18n/fr.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * res_company_mastodon_link 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2025-02-10 22:02+0000\n" 10 | "PO-Revision-Date: 2025-02-10 22:02+0000\n" 11 | "Last-Translator: \n" 12 | "Language-Team: \n" 13 | "Language: \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: \n" 17 | "Plural-Forms: \n" 18 | 19 | #. module: res_company_mastodon_link 20 | #: model:ir.model,name:res_company_mastodon_link.model_res_company 21 | msgid "Companies" 22 | msgstr "Sociétés" 23 | 24 | #. module: res_company_mastodon_link 25 | #: model:ir.model.fields,field_description:res_company_mastodon_link.field_res_company__social_mastodon 26 | msgid "Mastodon Account" 27 | msgstr "Compte Mastodon" 28 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_thread_create_nolog 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-10-29 12:06+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 5.6.2\n" 18 | 19 | #. module: mail_thread_create_nolog 20 | #: model:ir.model,name:mail_thread_create_nolog.model_mail_thread 21 | msgid "Email Thread" 22 | msgstr "Discussione e-mail" 23 | 24 | #. module: mail_thread_create_nolog 25 | #: model:ir.model,name:mail_thread_create_nolog.model_mail_message 26 | msgid "Message" 27 | msgstr "Messaggio" 28 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/discuss_app_model_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {DiscussApp} from "@mail/core/public_web/discuss_app_model"; 2 | import {Record} from "@mail/core/common/record"; 3 | 4 | import {_t} from "@web/core/l10n/translation"; 5 | import {patch} from "@web/core/utils/patch"; 6 | 7 | patch(DiscussApp.prototype, { 8 | setup(env) { 9 | super.setup(env); 10 | this.gateway = Record.one("DiscussAppCategory", { 11 | compute() { 12 | return { 13 | extraClass: "o-mail-DiscussSidebarCategory-gateway", 14 | id: "gateway", 15 | name: _t("Gateway"), 16 | isOpen: false, 17 | canView: false, 18 | canAdd: true, 19 | addTitle: _t("Search Gateway Channel"), 20 | serverStateKey: "is_discuss_sidebar_category_gateway_open", 21 | }; 22 | }, 23 | }); 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Creu Blanca 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | { 5 | "name": "Mail Whatsapp Gateway", 6 | "summary": """ 7 | Set a gateway for whatsapp""", 8 | "version": "18.0.1.0.0", 9 | "license": "AGPL-3", 10 | "author": "Creu Blanca, Dixmit, Odoo Community Association (OCA)", 11 | "website": "https://github.com/OCA/social", 12 | "depends": ["mail_gateway", "phone_validation"], 13 | "external_dependencies": {"python": ["requests_toolbelt"]}, 14 | "data": [ 15 | "security/security.xml", 16 | "security/ir.model.access.csv", 17 | "wizards/whatsapp_composer.xml", 18 | "wizards/mail_compose_gateway_message.xml", 19 | "views/mail_whatsapp_template_views.xml", 20 | "views/mail_gateway.xml", 21 | ], 22 | "assets": { 23 | "web.assets_backend": [ 24 | "mail_gateway_whatsapp/static/src/components/**/*", 25 | ], 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/message_actions.esm.js: -------------------------------------------------------------------------------- 1 | import {_t} from "@web/core/l10n/translation"; 2 | import {messageActionsRegistry} from "@mail/core/common/message_actions"; 3 | 4 | messageActionsRegistry 5 | .add("link_gateway_to_thread", { 6 | condition: (component) => { 7 | return ( 8 | component.props.message.gateway_type && 9 | component.props.thread.model === "discuss.channel" 10 | ); 11 | }, 12 | icon: "fa fa-link", 13 | title: _t("Link to thread"), 14 | onClick: (component) => component.onClickLinkGatewayToThread(), 15 | sequence: 20, 16 | }) 17 | .add("send_with_gateway", { 18 | condition: (component) => 19 | !component.props.message.gateway_type && 20 | component.props.thread.model !== "discuss.channel", 21 | icon: "fa fa-share-square-o", 22 | title: _t("Send with gateway"), 23 | onClick: (component) => component.onClickSendWithGateway(), 24 | sequence: 20, 25 | }); 26 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/wizards/mail_compose_gateway_message.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Tecnativa - Carlos López 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | import markupsafe 5 | 6 | from odoo import api, fields, models 7 | 8 | 9 | class MailComposeGatewayMessage(models.TransientModel): 10 | _inherit = "mail.compose.gateway.message" 11 | 12 | whatsapp_template_id = fields.Many2one( 13 | "mail.whatsapp.template", 14 | domain="""[ 15 | ('state', '=', 'approved'), 16 | ('is_supported', '=', True) 17 | ]""", 18 | ) 19 | 20 | @api.onchange("whatsapp_template_id") 21 | def onchange_whatsapp_template_id(self): 22 | if self.whatsapp_template_id: 23 | self.body = markupsafe.Markup(self.whatsapp_template_id.body) 24 | 25 | def _action_send_mail(self, auto_commit=False): 26 | if self.whatsapp_template_id: 27 | self = self.with_context(whatsapp_template_id=self.whatsapp_template_id.id) 28 | return super()._action_send_mail(auto_commit=auto_commit) 29 | -------------------------------------------------------------------------------- /mail_gateway/wizards/mail_message_gateway_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | mail.message.gateway.link 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 |
23 |
24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/i18n/es.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_thread_create_nolog 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 14.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-01-28 17:33+0000\n" 10 | "Last-Translator: Ivorra78 \n" 11 | "Language-Team: none\n" 12 | "Language: es\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: mail_thread_create_nolog 20 | #: model:ir.model,name:mail_thread_create_nolog.model_mail_thread 21 | msgid "Email Thread" 22 | msgstr "Hilo de Correo Electrónico" 23 | 24 | #. module: mail_thread_create_nolog 25 | #: model:ir.model,name:mail_thread_create_nolog.model_mail_message 26 | msgid "Message" 27 | msgstr "Mensaje" 28 | 29 | #~ msgid "Display Name" 30 | #~ msgstr "Mostrar Nombre" 31 | 32 | #~ msgid "ID" 33 | #~ msgstr "ID" 34 | 35 | #~ msgid "Last Modified on" 36 | #~ msgstr "Última Modificación el" 37 | -------------------------------------------------------------------------------- /mail_notification_with_history/data/mail_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27 | 28 | -------------------------------------------------------------------------------- /mail_gateway/static/src/components/gateway_follower/gateway_follower.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 9 | 26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /mail_notification_with_history/i18n/mail_notification_with_history.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_notification_with_history 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: mail_notification_with_history 17 | #: model_terms:ir.ui.view,arch_db:mail_notification_with_history.mail_notification_layout 18 | msgid "Discussion history in Odoo:" 19 | msgstr "" 20 | 21 | #. module: mail_notification_with_history 22 | #: model:ir.model,name:mail_notification_with_history.model_mail_thread 23 | msgid "Email Thread" 24 | msgstr "" 25 | 26 | #. module: mail_notification_with_history 27 | #: model:ir.model,name:mail_notification_with_history.model_mail_message 28 | msgid "Message" 29 | msgstr "" 30 | 31 | #. module: mail_notification_with_history 32 | #: model_terms:ir.ui.view,arch_db:mail_notification_with_history.mail_notification_layout 33 | msgid "commented on" 34 | msgstr "" 35 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/mail_composer_send_dropdown.esm.js: -------------------------------------------------------------------------------- 1 | import "@mail/chatter/web/mail_composer_send_dropdown"; // <- side-effect 2 | import {registry} from "@web/core/registry"; 3 | import {patch} from "@web/core/utils/patch"; 4 | 5 | const def = registry.category("view_widgets").get("mail_composer_send_dropdown"); 6 | const MailComposerSendDropdownClass = def.component; 7 | 8 | patch(MailComposerSendDropdownClass.prototype, { 9 | async onClickSend() { 10 | const model = this.env.model.config.resModel; 11 | if (model === "mail.compose.message") { 12 | return await super.onClickSend(...arguments); 13 | } 14 | this.buttonState.disabled = true; 15 | if (await this.props.record.save()) { 16 | const method = this.props.record.data.scheduled_date 17 | ? "action_schedule_message" 18 | : "action_send_mail"; 19 | this.actionService.doAction( 20 | await this.orm.call(model, method, [this.props.record.resId], { 21 | context: this.props.record.context, 22 | }) 23 | ); 24 | } 25 | this.buttonState.disabled = false; 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /mail_gateway/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | { 5 | "name": "Mail Gateway", 6 | "summary": "Base module for gateway communications", 7 | "version": "18.0.1.0.3", 8 | "license": "AGPL-3", 9 | "author": "Creu Blanca,Dixmit,Odoo Community Association (OCA)", 10 | "website": "https://github.com/OCA/social", 11 | "depends": ["mail"], 12 | "pre_init_hook": "pre_init_hook", 13 | "data": [ 14 | "wizards/mail_compose_gateway_message.xml", 15 | "wizards/mail_message_gateway_link.xml", 16 | "wizards/mail_message_gateway_send.xml", 17 | "wizards/mail_guest_manage.xml", 18 | "security/security.xml", 19 | "security/ir.model.access.csv", 20 | "views/mail_gateway.xml", 21 | "views/res_partner_gateway_channel.xml", 22 | "views/mail_guest_views.xml", 23 | "views/res_users_settings_views.xml", 24 | ], 25 | "assets": { 26 | "web.assets_backend": [ 27 | "mail_gateway/static/src/components/**/*", 28 | "mail_gateway/static/src/core/**/*", 29 | "mail_gateway/static/src/models/**/*", 30 | ], 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /mail_notification_with_history/models/mail_message.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | 4 | from odoo import models 5 | 6 | 7 | class Message(models.Model): 8 | _inherit = "mail.message" 9 | 10 | def _get_notification_message_history(self): 11 | """Get the list of messages to include into an email notification history.""" 12 | if ( 13 | not self.model 14 | or not self.env[self.model]._mail_notification_include_history 15 | ): 16 | return self.browse() 17 | domain = self._get_notification_message_history_domain() 18 | messages = self.env["mail.message"].search(domain, order="date desc") 19 | return messages - self 20 | 21 | def _get_notification_message_history_domain(self): 22 | """Return the domain for email and send message comments.""" 23 | return [ 24 | ("model", "=", self.model), 25 | ("res_id", "=", self.res_id), 26 | "|", 27 | "&", 28 | ("message_type", "=", "comment"), 29 | ("subtype_id", "=", self.env.ref("mail.mt_comment").id), 30 | ("message_type", "=", "email"), 31 | ] 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | /.venv 5 | /.pytest_cache 6 | /.ruff_cache 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | bin/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | eggs/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | *.eggs 27 | 28 | # Windows installers 29 | *.msi 30 | 31 | # Debian packages 32 | *.deb 33 | 34 | # Redhat packages 35 | *.rpm 36 | 37 | # MacOS packages 38 | *.dmg 39 | *.pkg 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .coverage 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | 53 | # Translations 54 | *.mo 55 | 56 | # Pycharm 57 | .idea 58 | 59 | # Eclipse 60 | .settings 61 | 62 | # Visual Studio cache/options directory 63 | .vs/ 64 | .vscode 65 | 66 | # OSX Files 67 | .DS_Store 68 | 69 | # Django stuff: 70 | *.log 71 | 72 | # Mr Developer 73 | .mr.developer.cfg 74 | .project 75 | .pydevproject 76 | 77 | # Rope 78 | .ropeproject 79 | 80 | # Sphinx documentation 81 | docs/_build/ 82 | 83 | # Backup files 84 | *~ 85 | *.swp 86 | 87 | # OCA rules 88 | !static/lib/ 89 | -------------------------------------------------------------------------------- /mail_notification_with_history/i18n/pt_BR.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_notification_with_history 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: Automatically generated\n" 10 | "Language-Team: none\n" 11 | "Language: pt_BR\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: \n" 15 | "Plural-Forms: nplurals=2; plural=n > 1;\n" 16 | 17 | #. module: mail_notification_with_history 18 | #: model_terms:ir.ui.view,arch_db:mail_notification_with_history.mail_notification_layout 19 | msgid "Discussion history in Odoo:" 20 | msgstr "" 21 | 22 | #. module: mail_notification_with_history 23 | #: model:ir.model,name:mail_notification_with_history.model_mail_thread 24 | msgid "Email Thread" 25 | msgstr "" 26 | 27 | #. module: mail_notification_with_history 28 | #: model:ir.model,name:mail_notification_with_history.model_mail_message 29 | msgid "Message" 30 | msgstr "" 31 | 32 | #. module: mail_notification_with_history 33 | #: model_terms:ir.ui.view,arch_db:mail_notification_with_history.mail_notification_layout 34 | msgid "commented on" 35 | msgstr "" 36 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/wizards/mail_compose_gateway_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | mail.compose.gateway.message 7 | 11 | 12 | 13 | 17 | 18 | 19 | whatsapp_template_id 20 | 21 | 22 | whatsapp_template_id 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/notification_model_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {Notification} from "@mail/core/common/notification_model"; 2 | import {patch} from "@web/core/utils/patch"; 3 | 4 | patch(Notification.prototype, { 5 | get icon() { 6 | if (this.gateway_type) { 7 | return `fa fa-${this.gateway_type}`; 8 | } 9 | return super.icon; 10 | }, 11 | get statusIcon() { 12 | if (!this.gateway_type) { 13 | return super.statusIcon; 14 | } 15 | // It can be overriden on the gateway implementation 16 | switch (this.notification_status) { 17 | case "process": 18 | return "fa fa-hourglass-half"; 19 | case "pending": 20 | return "fa fa-paper-plane-o"; 21 | case "sent": 22 | return `fa fa-${this.gateway_type} text-success`; 23 | case "bounce": 24 | return `fa fa-${this.gateway_type} text-danger`; 25 | case "exception": 26 | return `fa fa-${this.gateway_type} text-danger`; 27 | case "ready": 28 | return "fa fa-send-o"; 29 | case "canceled": 30 | return "fa fa-trash-o"; 31 | } 32 | return ""; 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /mail_gateway/security/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 2 | access_res_partner_gateway_channel_portal,res.partner.gateway.channel.portal,model_res_partner_gateway_channel,base.group_portal,1,0,0,0 3 | access_res_partner_gateway_channel_user,res.partner.gateway.channel,model_res_partner_gateway_channel,base.group_user,1,0,0,0 4 | manage_res_partner_gateway_channel_user,res.partner.gateway.channel,model_res_partner_gateway_channel,gateway_user,1,1,1,1 5 | access_mail_message_gateway_send_user,mail.message.gateway.send,model_mail_message_gateway_send,base.group_user,1,1,1,0 6 | access_mail_gateway_portal,mail.gateway.portal,model_mail_gateway,base.group_portal,1,0,0,0 7 | access_mail_gateway_public,mail.gateway.public,model_mail_gateway,base.group_public,1,0,0,0 8 | access_mail_gateway_user,mail.gateway.user,model_mail_gateway,base.group_user,1,0,0,0 9 | access_mail_guest_manage,mail.gateway.internal,model_mail_guest_manage,base.group_user,1,1,1,1 10 | access_mail_message_gateway_link,mail.message.link.all,model_mail_message_gateway_link,base.group_user,1,1,1,1 11 | access_mail_gateway_system,mail_gateway,model_mail_gateway,base.group_system,1,1,1,1 12 | access_mail_compose_gateway_message,access.mail.compose.gateway.message,model_mail_compose_gateway_message,base.group_user,1,1,1,0 13 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/web/gateway_core_web_service.esm.js: -------------------------------------------------------------------------------- 1 | import {reactive} from "@odoo/owl"; 2 | import {registry} from "@web/core/registry"; 3 | import {user} from "@web/core/user"; 4 | 5 | export class GatewayCoreWeb { 6 | constructor(env, services) { 7 | Object.assign(this, { 8 | busService: services.bus_service, 9 | }); 10 | /** @type {import("@mail/core/common/store_service").Store} */ 11 | this.store = services["mail.store"]; 12 | } 13 | setup() { 14 | if (user.settings?.is_discuss_sidebar_category_gateway_open) { 15 | this.store.discuss.gateway.isOpen = true; 16 | } 17 | this.busService.subscribe("res.users.settings", (payload) => { 18 | if (payload) { 19 | this.store.discuss.gateway.isOpen = 20 | payload.is_discuss_sidebar_category_gateway_open ?? 21 | this.store.discuss.gateway.isOpen; 22 | } 23 | }); 24 | } 25 | } 26 | 27 | export const gatewayCoreWeb = { 28 | dependencies: ["bus_service", "mail.store"], 29 | start(env, services) { 30 | const gateway_core_web = reactive(new GatewayCoreWeb(env, services)); 31 | gateway_core_web.setup(); 32 | return gateway_core_web; 33 | }, 34 | }; 35 | 36 | registry.category("services").add("mail_gateway.core.web", gatewayCoreWeb); 37 | -------------------------------------------------------------------------------- /mail_gateway/wizards/mail_guest_manage.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | mail.guest.manage 7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 |
30 |
31 |
32 |
33 |
34 | -------------------------------------------------------------------------------- /mail_notification_with_history/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_notification_with_history 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-11-05 11:06+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 5.6.2\n" 18 | 19 | #. module: mail_notification_with_history 20 | #: model_terms:ir.ui.view,arch_db:mail_notification_with_history.mail_notification_layout 21 | msgid "Discussion history in Odoo:" 22 | msgstr "Cronologia discussione in Odoo:" 23 | 24 | #. module: mail_notification_with_history 25 | #: model:ir.model,name:mail_notification_with_history.model_mail_thread 26 | msgid "Email Thread" 27 | msgstr "Discussione e-mail" 28 | 29 | #. module: mail_notification_with_history 30 | #: model:ir.model,name:mail_notification_with_history.model_mail_message 31 | msgid "Message" 32 | msgstr "Messaggio" 33 | 34 | #. module: mail_notification_with_history 35 | #: model_terms:ir.ui.view,arch_db:mail_notification_with_history.mail_notification_layout 36 | msgid "commented on" 37 | msgstr "commentato in" 38 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/i18n/mail_activity_unlink_log.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_activity_unlink_log 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: mail_activity_unlink_log 17 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 18 | msgid "(originally assigned to" 19 | msgstr "" 20 | 21 | #. module: mail_activity_unlink_log 22 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 23 | msgid "Original note:" 24 | msgstr "" 25 | 26 | #. module: mail_activity_unlink_log 27 | #: model:mail.message.subtype,name:mail_activity_unlink_log.mt_activities_unlink 28 | msgid "Activities unlink" 29 | msgstr "" 30 | 31 | #. module: mail_activity_unlink_log 32 | #: model:ir.model,name:mail_activity_unlink_log.model_mail_activity 33 | msgid "Activity" 34 | msgstr "" 35 | 36 | #. module: mail_activity_unlink_log 37 | #: model:ir.model,name:mail_activity_unlink_log.model_mail_activity_mixin 38 | msgid "Activity Mixin" 39 | msgstr "" 40 | 41 | #. module: mail_activity_unlink_log 42 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 43 | msgid "deleted" 44 | msgstr "" 45 | -------------------------------------------------------------------------------- /mail_gateway/wizards/mail_message_gateway_send.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | mail.message.gateway.send 7 | 8 |
9 | 10 | 11 | 12 | 17 | 22 | 23 |
24 |
32 |
33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/controllers/thread.py: -------------------------------------------------------------------------------- 1 | from odoo import http 2 | from odoo.http import request 3 | 4 | from odoo.addons.mail.controllers import thread as mail_thread 5 | 6 | 7 | class ThreadController(mail_thread.ThreadController): 8 | @http.route("/mail/thread/messages", methods=["POST"], type="json", auth="user") 9 | def mail_thread_messages( 10 | self, 11 | thread_model, 12 | thread_id, 13 | search_term=None, 14 | before=None, 15 | after=None, 16 | around=None, 17 | limit=30, 18 | ): 19 | result = super().mail_thread_messages( 20 | thread_model, thread_id, search_term, before, after, around, limit 21 | ) 22 | # Parameter after is only set when refreshing the mail thread view 23 | # to fetch new messages from other users. That means if it's there, 24 | # the ".. created" message was already generated on the fly and we 25 | # do not need to generate it again 26 | if after is not None: 27 | return result 28 | 29 | result["data"].setdefault("mail.message", []) 30 | 31 | domain = [ 32 | ("res_id", "=", int(thread_id)), 33 | ("model", "=", thread_model), 34 | ("message_type", "!=", "user_notification"), 35 | ] 36 | 37 | message = request.env["mail.message"]._generate_messsage(domain) 38 | 39 | if message: 40 | result["data"]["mail.message"].append(message) 41 | result["messages"].append(message["id"]) 42 | 43 | return result 44 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/models/mail_activity.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 CreuBlanca 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import models 5 | 6 | 7 | class MailActivity(models.Model): 8 | _inherit = "mail.activity" 9 | 10 | def _action_done(self, *args, **kwargs): 11 | return super( 12 | MailActivity, self.with_context(activity_unlink_no_message=True) 13 | )._action_done(*args, **kwargs) 14 | 15 | def unlink(self): 16 | if not self.env.context.get("activity_unlink_no_message"): 17 | for activity in self: 18 | record = self.env[activity.res_model].browse(activity.res_id) 19 | record.message_post_with_source( 20 | "mail_activity_unlink_log.message_activity_unlink", 21 | render_values={ 22 | "activity": activity, 23 | "display_assignee": activity.user_id != self.env.user, 24 | }, 25 | subtype_id=self.env["ir.model.data"]._xmlid_to_res_id( 26 | "mail_activity_unlink_log.mt_activities_unlink" 27 | ), 28 | mail_activity_type_id=activity.activity_type_id.id, 29 | ) 30 | return super().unlink() 31 | 32 | 33 | class MailActivityMixin(models.AbstractModel): 34 | _inherit = "mail.activity.mixin" 35 | 36 | def unlink(self): 37 | return super( 38 | MailActivityMixin, self.with_context(activity_unlink_no_message=True) 39 | ).unlink() 40 | -------------------------------------------------------------------------------- /mail_notification_with_history/i18n/es.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_notification_with_history 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 14.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-01-28 11:33+0000\n" 10 | "Last-Translator: Ivorra78 \n" 11 | "Language-Team: none\n" 12 | "Language: es\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: mail_notification_with_history 20 | #: model_terms:ir.ui.view,arch_db:mail_notification_with_history.mail_notification_layout 21 | msgid "Discussion history in Odoo:" 22 | msgstr "Historial de discusiones en Odoo:" 23 | 24 | #. module: mail_notification_with_history 25 | #: model:ir.model,name:mail_notification_with_history.model_mail_thread 26 | msgid "Email Thread" 27 | msgstr "Hilo de Correo Electrónico" 28 | 29 | #. module: mail_notification_with_history 30 | #: model:ir.model,name:mail_notification_with_history.model_mail_message 31 | msgid "Message" 32 | msgstr "Mensaje" 33 | 34 | #. module: mail_notification_with_history 35 | #: model_terms:ir.ui.view,arch_db:mail_notification_with_history.mail_notification_layout 36 | msgid "commented on" 37 | msgstr "comentado en" 38 | 39 | #~ msgid "Display Name" 40 | #~ msgstr "Mostrar Nombre" 41 | 42 | #~ msgid "ID" 43 | #~ msgstr "ID" 44 | 45 | #~ msgid "Last Modified on" 46 | #~ msgstr "Última Modificación el" 47 | -------------------------------------------------------------------------------- /mail_gateway/wizards/mail_message_gateway_link.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import api, fields, models 5 | 6 | 7 | class MailMessageGatewayLink(models.TransientModel): 8 | _name = "mail.message.gateway.link" 9 | _description = "Link message from gateway" 10 | 11 | message_id = fields.Many2one("mail.message") 12 | resource_ref = fields.Reference( 13 | string="Record reference", selection="_selection_target_model" 14 | ) 15 | 16 | @api.model 17 | def _selection_target_model(self): 18 | models = self.env["ir.model"].search([("is_mail_thread", "=", True)]) 19 | return [(model.model, model.name) for model in models] 20 | 21 | def link_message(self): 22 | new_message = self.resource_ref.message_post( 23 | body=self.message_id.body, 24 | author_id=self.message_id.author_id.id, 25 | gateway_type=self.message_id.gateway_type, 26 | date=self.message_id.date, 27 | # message_id=update.message.message_id, 28 | subtype_xmlid="mail.mt_comment", 29 | message_type="comment", 30 | attachment_ids=self.message_id.attachment_ids.ids, 31 | gateway_notifications=[], # Avoid sending notifications 32 | ) 33 | self.message_id.gateway_message_id = new_message 34 | self.message_id._bus_send_store( 35 | self.message_id, 36 | { 37 | "gateway_thread_data": self.message_id.sudo().gateway_thread_data, 38 | }, 39 | ) 40 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/data/unlink_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Activities unlink 6 | 7 | 8 | 9 | 10 | 11 | 12 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /mail_gateway/static/src/components/gateway_follower/gateway_follower.esm.js: -------------------------------------------------------------------------------- 1 | import {Component} from "@odoo/owl"; 2 | 3 | export class GatewayFollower extends Component { 4 | static template = "mail_gateway.GatewayFollowerView"; 5 | static props = ["follower", "composer"]; 6 | setup() { 7 | this.channel = false; 8 | this.follower_channel_ids = this.props.follower.gateway_channels.map( 9 | (channel) => channel.id 10 | ); 11 | this._clearGatewayNotifications(); 12 | } 13 | get composerGatewayFollower() { 14 | return this.props.follower; 15 | } 16 | _getMessageData() { 17 | return { 18 | partner_id: this.props.follower.id, 19 | channel_type: "gateway", 20 | gateway_channel_id: this.channel, 21 | }; 22 | } 23 | _clearGatewayNotifications() { 24 | this.props.composer.thread.gateway_notifications = 25 | this.props.composer.thread.gateway_notifications.filter( 26 | (gateway_notification) => { 27 | return !this.follower_channel_ids.includes( 28 | gateway_notification.gateway_channel_id 29 | ); 30 | } 31 | ); 32 | } 33 | onChangeGatewayChannel(ev) { 34 | this.channel = parseInt(ev.target.options[ev.target.selectedIndex].value, 10); 35 | if (this.channel) { 36 | this.props.composer.thread.gateway_notifications.push( 37 | this._getMessageData() 38 | ); 39 | } else { 40 | this._clearGatewayNotifications(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/static/src/components/send_whatsapp_button/send_whatsapp_button.esm.js: -------------------------------------------------------------------------------- 1 | import {useService} from "@web/core/utils/hooks"; 2 | import {_t} from "@web/core/l10n/translation"; 3 | import {user} from "@web/core/user"; 4 | import {Component, status} from "@odoo/owl"; 5 | 6 | export class SendWhatsappButton extends Component { 7 | setup() { 8 | this.action = useService("action"); 9 | this.title = _t("Send Whatsapp Message"); 10 | } 11 | get phoneHref() { 12 | return "sms:" + this.props.record.data[this.props.name].replace(/\s+/g, ""); 13 | } 14 | async onClick() { 15 | await this.props.record.save(); 16 | this.action.doAction( 17 | { 18 | type: "ir.actions.act_window", 19 | target: "new", 20 | name: this.title, 21 | res_model: "whatsapp.composer", 22 | views: [[false, "form"]], 23 | context: { 24 | ...user.context, 25 | default_res_model: this.props.record.resModel, 26 | default_res_id: this.props.record.resId, 27 | default_number_field_name: this.props.name, 28 | default_composition_mode: "comment", 29 | }, 30 | }, 31 | { 32 | onClose: () => { 33 | if (status(this) !== "destroyed") { 34 | this.props.record.load(); 35 | this.props.record.model.notify(); 36 | } 37 | }, 38 | } 39 | ); 40 | } 41 | } 42 | SendWhatsappButton.template = "mail_gateway_whatsapp.SendWhatsappButton"; 43 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_activity_unlink_log 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 14.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2025-02-24 11:06+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 5.6.2\n" 18 | 19 | #. module: mail_activity_unlink_log 20 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 21 | msgid "(originally assigned to" 22 | msgstr "(assegnato originariamente a" 23 | 24 | #. module: mail_activity_unlink_log 25 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 26 | msgid "Original note:" 27 | msgstr "Nota origine:" 28 | 29 | #. module: mail_activity_unlink_log 30 | #: model:mail.message.subtype,name:mail_activity_unlink_log.mt_activities_unlink 31 | msgid "Activities unlink" 32 | msgstr "Scollega attività" 33 | 34 | #. module: mail_activity_unlink_log 35 | #: model:ir.model,name:mail_activity_unlink_log.model_mail_activity 36 | msgid "Activity" 37 | msgstr "Attività" 38 | 39 | #. module: mail_activity_unlink_log 40 | #: model:ir.model,name:mail_activity_unlink_log.model_mail_activity_mixin 41 | msgid "Activity Mixin" 42 | msgstr "Mixin attività" 43 | 44 | #. module: mail_activity_unlink_log 45 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 46 | msgid "deleted" 47 | msgstr "cancellata" 48 | -------------------------------------------------------------------------------- /mail_gateway/wizards/mail_compose_gateway_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | mail.compose.gateway.message 7 | 8 | primary 9 | 10 | 13 | 14 | 1 15 | 16 | 17 | 18 | 25 | 26 | 27 | 1 28 | 0 29 | 30 | 34 | True 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/tests/test_activity_unlink.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 CreuBlanca 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo.addons.base.tests.common import BaseCommon 5 | 6 | 7 | class TestActivityUnlink(BaseCommon): 8 | @classmethod 9 | def setUpClass(cls): 10 | super().setUpClass() 11 | cls.unlink_subtype = cls.env.ref( 12 | "mail_activity_unlink_log.mt_activities_unlink" 13 | ) 14 | 15 | def test_done(self): 16 | self.assertFalse( 17 | self.partner.message_ids.filtered( 18 | lambda r: r.subtype_id == self.unlink_subtype 19 | ) 20 | ) 21 | self.activity = self.partner.activity_schedule( 22 | act_type_xmlid="mail.mail_activity_data_todo" 23 | ) 24 | self.assertTrue(self.partner.activity_ids) 25 | self.activity.action_done() 26 | self.assertFalse(self.partner.activity_ids) 27 | self.assertFalse( 28 | self.partner.message_ids.filtered( 29 | lambda r: r.subtype_id == self.unlink_subtype 30 | ) 31 | ) 32 | 33 | def test_unlink(self): 34 | self.assertFalse( 35 | self.partner.message_ids.filtered( 36 | lambda r: r.subtype_id == self.unlink_subtype 37 | ) 38 | ) 39 | self.activity = self.partner.activity_schedule( 40 | act_type_xmlid="mail.mail_activity_data_todo" 41 | ) 42 | self.assertTrue(self.partner.activity_ids) 43 | self.activity.unlink() 44 | self.assertFalse(self.partner.activity_ids) 45 | self.assertTrue( 46 | self.partner.message_ids.filtered( 47 | lambda r: r.subtype_id == self.unlink_subtype 48 | ) 49 | ) 50 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: pre-commit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "18.0*" 7 | push: 8 | branches: 9 | - "18.0" 10 | - "18.0-ocabot-*" 11 | 12 | jobs: 13 | pre-commit: 14 | runs-on: ubuntu-22.04 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-python@v5 18 | with: 19 | python-version: "3.11" 20 | - name: Get python version 21 | run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV 22 | - uses: actions/cache@v4 23 | with: 24 | path: ~/.cache/pre-commit 25 | key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} 26 | - name: Install pre-commit 27 | run: pip install pre-commit 28 | - name: Run pre-commit 29 | run: pre-commit run --all-files --show-diff-on-failure --color=always 30 | env: 31 | # Consider valid a PR that changes README fragments but doesn't 32 | # change the README.rst file itself. It's not really a problem 33 | # because the bot will update it anyway after merge. This way, we 34 | # lower the barrier for functional contributors that want to fix the 35 | # readme fragments, while still letting developers get README 36 | # auto-generated (which also helps functionals when using runboat). 37 | # DOCS https://pre-commit.com/#temporarily-disabling-hooks 38 | SKIP: oca-gen-addon-readme 39 | - name: Check that all files generated by pre-commit are in git 40 | run: | 41 | newfiles="$(git ls-files --others --exclude-from=.gitignore)" 42 | if [ "$newfiles" != "" ] ; then 43 | echo "Please check-in the following files:" 44 | echo "$newfiles" 45 | exit 1 46 | fi 47 | -------------------------------------------------------------------------------- /mail_gateway_telegram/readme/CONFIGURE.md: -------------------------------------------------------------------------------- 1 | ## Create the bot 2 | 3 | 1. Create a Bot on telegram 4 | 2. Create a broker following the examples on 5 | with the TOKEN provided 6 | 7 | ## Configure Odoo 8 | 9 | 1. Access on debug mode 10 | 2. Access Settings \> Technical Settings \> Email \> Mail Gateway. 11 | 3. Access Telegram and start a converstation with BotFather. 12 | 4. Create a bot using the command /newbot. The system will ask for a 13 | bot name. Remember that it needs to end with the word bot. 14 | 5. Copy the token to access the HTTP API to the token field. 15 | 6. Define Webhook key an webhook secret of your choice in its 16 | corresponding field, in order to secure the connection. 17 | 7. Press save button and the integrate webhook smart button will 18 | appear. 19 | 8. Press the Integrate webhook button. 20 | 9. If you want to add an extra layer of security, you can check Has New 21 | Channel Security and define a Telegram security key. New chats will 22 | be created only with the command /start SECURITY_KEY. 23 | 24 | ## Limitations 25 | 26 | The Webhook functionality can only be used if your system is accessible 27 | from website. On local installations it might be problematic as Telegram 28 | will not be able to contact your system. In that case, you might need to 29 | create a telegram bot that will send data to you on an external process 30 | with the following code. 31 | 32 | ``` python 33 | from telegram.ext import Filters, MessageHandler, Updater 34 | import requests 35 | dp = Updater(YOUR_TOKEN) 36 | 37 | def message_callback(update, _context): 38 | requests.post(YOUR_CONTROLLER, json=update.to_dict()) 39 | 40 | 41 | dp.dispatcher.add_handler(MessageHandler(Filters.all, message_callback)) 42 | dp.start_polling() 43 | dp.idle() 44 | ``` 45 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/tests/test_create_nolog.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) 3 | 4 | import json 5 | 6 | from odoo.tests.common import tagged 7 | 8 | from odoo.addons.base.tests.common import HttpCaseWithUserDemo 9 | 10 | 11 | @tagged("post_install", "-at_install") 12 | class TestMailThreadCreateNoLog(HttpCaseWithUserDemo): 13 | @classmethod 14 | def setUpClass(cls): 15 | super().setUpClass() 16 | cls.partner = cls.env["res.partner"].create({"name": "TEST"}) 17 | cls.message_model = cls.env["mail.message"] 18 | 19 | def test_message_fetch(self): 20 | session = self.authenticate("demo", "demo") 21 | 22 | response = self.url_open( 23 | "/mail/thread/messages", 24 | data=json.dumps( 25 | { 26 | "params": { 27 | "thread_model": self.partner._name, 28 | "thread_id": self.partner.id, 29 | } 30 | } 31 | ), 32 | headers={ 33 | "Content-Type": "application/json", 34 | "Cookie": f"session_id={session.sid};", 35 | }, 36 | ) 37 | 38 | self.assertEqual(response.status_code, 200) 39 | 40 | data = response.json() 41 | 42 | create_msg = data["result"]["data"]["mail.message"][-1] 43 | 44 | # # We get a creation message 45 | self.assertEqual(create_msg["model"], self.partner._name) 46 | self.assertEqual(create_msg["res_id"], self.partner.id) 47 | self.assertEqual(create_msg["author"]["id"], self.env.user.partner_id.id) 48 | self.assertEqual(create_msg["body"], self.partner._creation_message()) 49 | # But it doesn't exist in the DB 50 | self.assertFalse(self.message_model.browse(create_msg["id"]).exists()) 51 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/i18n/es.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_activity_unlink_log 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 14.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-01-28 11:33+0000\n" 10 | "Last-Translator: Ivorra78 \n" 11 | "Language-Team: none\n" 12 | "Language: es\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: mail_activity_unlink_log 20 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 21 | msgid "(originally assigned to" 22 | msgstr "(originalmente asignado a" 23 | 24 | #. module: mail_activity_unlink_log 25 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 26 | msgid "Original note:" 27 | msgstr "Nota original:" 28 | 29 | #. module: mail_activity_unlink_log 30 | #: model:mail.message.subtype,name:mail_activity_unlink_log.mt_activities_unlink 31 | msgid "Activities unlink" 32 | msgstr "Desvincular actividades" 33 | 34 | #. module: mail_activity_unlink_log 35 | #: model:ir.model,name:mail_activity_unlink_log.model_mail_activity 36 | msgid "Activity" 37 | msgstr "Actividades" 38 | 39 | #. module: mail_activity_unlink_log 40 | #: model:ir.model,name:mail_activity_unlink_log.model_mail_activity_mixin 41 | msgid "Activity Mixin" 42 | msgstr "Mezclador de Actividad" 43 | 44 | #. module: mail_activity_unlink_log 45 | #: model_terms:ir.ui.view,arch_db:mail_activity_unlink_log.message_activity_unlink 46 | msgid "deleted" 47 | msgstr "borrado" 48 | 49 | #~ msgid "Display Name" 50 | #~ msgstr "Mostrar Nombre" 51 | 52 | #~ msgid "ID" 53 | #~ msgstr "ID" 54 | 55 | #~ msgid "Last Modified on" 56 | #~ msgstr "Última Modificación el" 57 | -------------------------------------------------------------------------------- /mail_gateway/models/mail_notification.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | from odoo.addons.mail.tools.discuss import Store 7 | 8 | 9 | class MailNotification(models.Model): 10 | _inherit = "mail.notification" 11 | 12 | gateway_channel_id = fields.Many2one("discuss.channel") 13 | notification_type = fields.Selection( 14 | selection_add=[("gateway", "Gateway")], ondelete={"gateway": "cascade"} 15 | ) 16 | gateway_message_id = fields.Char(readonly=True) 17 | gateway_failure_reason = fields.Text( 18 | readonly=True, 19 | help="Failure reason. This is usually the exception thrown by the" 20 | " email server, stored to ease the debugging of mailing issues.", 21 | ) 22 | gateway_type = fields.Selection( 23 | selection=lambda r: r.env["mail.gateway"]._fields["gateway_type"].selection 24 | ) 25 | 26 | def _set_read_gateway(self): 27 | self.sudo().write({"is_read": True, "read_date": fields.Datetime.now()}) 28 | 29 | def _to_store(self, store: Store, /): 30 | result = super()._to_store(store) 31 | for record in self: 32 | store.add( 33 | record, 34 | { 35 | "gateway_type": record.gateway_type, 36 | "channel_name": record.gateway_channel_id.name, 37 | }, 38 | ) 39 | return result 40 | 41 | def send_gateway(self, auto_commit=False, raise_exception=False, parse_mode="HTML"): 42 | for record in self: 43 | gateway = record.gateway_channel_id.gateway_id 44 | self.env[f"mail.gateway.{gateway.gateway_type}"]._send( 45 | gateway, 46 | record, 47 | auto_commit=auto_commit, 48 | raise_exception=raise_exception, 49 | parse_mode=parse_mode, 50 | ) 51 | -------------------------------------------------------------------------------- /mail_gateway/static/src/core/common/thread_model_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {assignDefined, assignIn} from "@mail/utils/common/misc"; 2 | import {Record} from "@mail/core/common/record"; 3 | import {Thread} from "@mail/core/common/thread_model"; 4 | import {patch} from "@web/core/utils/patch"; 5 | import {url} from "@web/core/utils/urls"; 6 | 7 | patch(Thread, { 8 | _insert(data) { 9 | const thread = super._insert(...arguments); 10 | if (thread.type === "gateway") { 11 | assignIn(thread, data, ["anonymous_name", "gateway"]); 12 | this.store.discuss.gateway.threads.add(thread); 13 | } 14 | return thread; 15 | }, 16 | }); 17 | 18 | patch(Thread.prototype, { 19 | setup() { 20 | super.setup(); 21 | this.gateway = Record.one("Gateway"); 22 | this.operator = Record.one("Persona"); 23 | this.gateway_notifications = []; 24 | this.gateway_followers = Record.many("Persona"); 25 | }, 26 | get isChatChannel() { 27 | return this.channel_type === "gateway" || super.isChatChannel; 28 | }, 29 | get hasMemberList() { 30 | return this.channel_type === "gateway" || super.hasMemberList; 31 | }, 32 | get avatarUrl() { 33 | if (this.channel_type !== "gateway") { 34 | return super.avatarUrl; 35 | } 36 | return url( 37 | `/web/image/discuss.channel/${this.id}/avatar_128`, 38 | assignDefined({}, {unique: this.avatarCacheKey}) 39 | ); 40 | }, 41 | /** @param {Object} data */ 42 | update(data) { 43 | super.update(data); 44 | if ("gateway_id" in data && this.channel_type === "gateway") { 45 | this.gateway = data.gateway_id; 46 | } 47 | }, 48 | _computeDiscussAppCategory() { 49 | if (this.channel_type === "gateway") { 50 | return this.store.discuss.gateway; 51 | } 52 | return super._computeDiscussAppCategory(...arguments); 53 | }, 54 | }); 55 | -------------------------------------------------------------------------------- /mail_gateway/wizards/mail_guest_manage.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class MailGuestManage(models.TransientModel): 8 | _name = "mail.guest.manage" 9 | _description = "Assign gateway guest to a partner" 10 | 11 | guest_id = fields.Many2one("mail.guest", required=True) 12 | partner_id = fields.Many2one("res.partner") 13 | 14 | def create_partner(self): 15 | partner = self.env["res.partner"].create(self._get_partner_vals()) 16 | self._merge_partner(partner) 17 | return partner.get_formview_action() 18 | 19 | def _get_partner_vals(self): 20 | return { 21 | "name": self.guest_id.name, 22 | } 23 | 24 | def _merge_partner(self, partner): 25 | self.env["res.partner.gateway.channel"].create( 26 | { 27 | "name": self.guest_id.gateway_id.name, 28 | "partner_id": partner.id, 29 | "gateway_id": self.guest_id.gateway_id.id, 30 | "gateway_token": self.guest_id.gateway_token, 31 | } 32 | ) 33 | for member in self.env["discuss.channel.member"].search( 34 | [("guest_id", "=", self.guest_id.id)] 35 | ): 36 | self.env["discuss.channel.member"].create( 37 | self._channel_member_vals(member, partner) 38 | ) 39 | member.unlink() 40 | self.env["mail.message"].search( 41 | [("author_guest_id", "=", self.guest_id.id)] 42 | ).write( 43 | { 44 | "author_id": partner.id, 45 | } 46 | ) 47 | 48 | def _channel_member_vals(self, member, partner): 49 | return { 50 | "guest_id": False, 51 | "channel_id": member.channel_id.id, 52 | "partner_id": partner.id, 53 | } 54 | 55 | def merge_partner(self): 56 | self._merge_partner(self.partner_id) 57 | return self.partner_id.get_formview_action() 58 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/tools/const.py: -------------------------------------------------------------------------------- 1 | # https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/supported-languages # noqa: B501 E501 2 | # res.lang not matching with supported languages(iso codes) 3 | supported_languages = [ 4 | ("af", "Afrikaans"), 5 | ("sq", "Albanian"), 6 | ("ar", "Arabic"), 7 | ("az", "Azerbaijani"), 8 | ("bn", "Bengali"), 9 | ("bg", "Bulgarian"), 10 | ("ca", "Catalan"), 11 | ("zh_CN", "Chinese (CHN)"), 12 | ("zh_HK", "Chinese (HKG)"), 13 | ("zh_TW", "Chinese (TAI)"), 14 | ("hr", "Croatian"), 15 | ("cs", "Czech"), 16 | ("da", "Danish"), 17 | ("nl", "Dutch"), 18 | ("en", "English"), 19 | ("en_GB", "English (UK)"), 20 | ("en_US", "English (US)"), 21 | ("et", "Estonian"), 22 | ("fil", "Filipino"), 23 | ("fi", "Finnish"), 24 | ("fr", "French"), 25 | ("ka", "Georgian"), 26 | ("de", "German"), 27 | ("el", "Greek"), 28 | ("gu", "Gujarati"), 29 | ("ha", "Hausa"), 30 | ("he", "Hebrew"), 31 | ("hi", "Hindi"), 32 | ("hu", "Hungarian"), 33 | ("id", "Indonesian"), 34 | ("ga", "Irish"), 35 | ("it", "Italian"), 36 | ("ja", "Japanese"), 37 | ("kn", "Kannada"), 38 | ("kk", "Kazakh"), 39 | ("rw_RW", "Kinyarwanda"), 40 | ("ko", "Korean"), 41 | ("ky_KG", "Kyrgyz (Kyrgyzstan)"), 42 | ("lo", "Lao"), 43 | ("lv", "Latvian"), 44 | ("lt", "Lithuanian"), 45 | ("mk", "Macedonian"), 46 | ("ms", "Malay"), 47 | ("ml", "Malayalam"), 48 | ("mr", "Marathi"), 49 | ("nb", "Norwegian"), 50 | ("fa", "Persian"), 51 | ("pl", "Polish"), 52 | ("pt_BR", "Portuguese (BR)"), 53 | ("pt_PT", "Portuguese (POR)"), 54 | ("pa", "Punjabi"), 55 | ("ro", "Romanian"), 56 | ("ru", "Russian"), 57 | ("sr", "Serbian"), 58 | ("sk", "Slovak"), 59 | ("sl", "Slovenian"), 60 | ("es", "Spanish"), 61 | ("es_AR", "Spanish (ARG)"), 62 | ("es_ES", "Spanish (SPA)"), 63 | ("es_MX", "Spanish (MEX)"), 64 | ("sw", "Swahili"), 65 | ("sv", "Swedish"), 66 | ("ta", "Tamil"), 67 | ("te", "Telugu"), 68 | ("th", "Thai"), 69 | ("tr", "Turkish"), 70 | ("uk", "Ukrainian"), 71 | ("ur", "Urdu"), 72 | ("uz", "Uzbek"), 73 | ("vi", "Vietnamese"), 74 | ("zu", "Zulu"), 75 | ] 76 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/models/mail_thread.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 CreuBlanca 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import _, models 5 | from odoo.exceptions import UserError 6 | 7 | 8 | class MailThread(models.AbstractModel): 9 | _inherit = "mail.thread" 10 | 11 | def _get_whatsapp_channel_vals(self, token, gateway, partner): 12 | result = { 13 | "gateway_channel_token": token, 14 | "gateway_id": gateway.id, 15 | } 16 | if partner: 17 | result["partner_id"] = partner.id 18 | result["name"] = partner.display_name 19 | return result 20 | 21 | def _whatsapp_get_channel(self, field_name, gateway): 22 | sanitized_number = self._phone_format(number=self[field_name]) 23 | if not sanitized_number: 24 | raise UserError(_("Phone cannot be sanitized")) 25 | # Avoid the plus sign prefix to match the whatsapp token 26 | sanitized_number = sanitized_number.replace("+", "") 27 | partner = self._whatsapp_get_partner() 28 | if not self.env["res.partner.gateway.channel"].search( 29 | [ 30 | ("partner_id", "=", partner.id), 31 | ("gateway_id", "=", gateway.id), 32 | ("gateway_token", "=", sanitized_number), 33 | ] 34 | ): 35 | self.env["res.partner.gateway.channel"].create( 36 | { 37 | "name": gateway.name, 38 | "partner_id": partner.id, 39 | "gateway_id": gateway.id, 40 | "gateway_token": sanitized_number, 41 | } 42 | ) 43 | return self.env["mail.gateway.whatsapp"]._get_channel( 44 | gateway, 45 | sanitized_number, 46 | { 47 | "contacts": [ 48 | { 49 | "wa_id": sanitized_number, 50 | "profile": {"name": partner.display_name}, 51 | } 52 | ], 53 | "messages": [{"from": sanitized_number}], 54 | }, 55 | force_create=True, 56 | ) 57 | 58 | def _whatsapp_get_partner(self): 59 | if "partner_id" in self._fields: 60 | return self.partner_id 61 | return None 62 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/readme/CONFIGURE.md: -------------------------------------------------------------------------------- 1 | ## First steps 2 | 3 | You need to create a WhatsApp Business Account (WABA), a Meta App and 4 | define a phone number. You can follow this 5 | [steps](https://developers.facebook.com/micro_site/url/?click_from_context_menu=true&country=ES&destination=https%3A%2F%2Fwww.facebook.com%2Fbusiness%2Fhelp%2F2087193751603668&event_type=click&last_nav_impression_id=0m3TRxrxOlly1eRmB&max_percent_page_viewed=22&max_viewport_height_px=1326&max_viewport_width_px=2560&orig_http_referrer=https%3A%2F%2Fdevelopers.facebook.com%2Fdocs%2Fwhatsapp%2Fcloud-api%2Fget-started-for-bsps%3Flocale%3Den_US&orig_request_uri=https%3A%2F%2Fdevelopers.facebook.com%2Fajax%2Fpagelet%2Fgeneric.php%2FDeveloperNotificationsPayloadPagelet%3Ffb_dtsg_ag%3D--sanitized--%26data%3D%257B%2522businessUserID%2522%253Anull%252C%2522cursor%2522%253Anull%252C%2522length%2522%253A15%252C%2522clientRequestID%2522%253A%2522js_k6%2522%257D%26__usid%3D6-Trd7hi4itpm%253APrd7ifiub2tvy%253A0-Ard7g9twdm0p1-RV%253D6%253AF%253D%26locale%3Den_US%26jazoest%3D24920®ion=emea&scrolled=false&session_id=1jLoVJNU6iVMaw3ml&site=developers). 6 | 7 | If you create a test Business Account, passwords will change every 24 8 | hours. 9 | 10 | In order to make the webhook accessible, the system must be public. 11 | 12 | ## Configure the gateway 13 | 14 | Once you have created the Meta App, you need to add the gateway and 15 | webhook. In order to make it you must follow this steps: 16 | 17 | - Access Settings \> Emails \> Mail Gateway 18 | - Create a Gateway of type WhatsApp 19 | 20 | > - Use the Meta App authentication key as Token field 21 | > - Use the Meta App Phone Number ID as Whatsapp from Phone field 22 | > - Use the Meta Account Business ID as Whatsapp account field (only if 23 | > you need sync templates) 24 | > - Write your own Webhook key 25 | > - Use the Application Secret Key on Whatsapp Security Key. It will be 26 | > used in order to validate the data 27 | > - Press the Integrate Webhook Key. In this case, it will not integrate 28 | > it, we need to make it manually 29 | > - Copy the webhook URL 30 | 31 | - Access [Facebook Apps website](https://developers.facebook.com/apps/) 32 | - Access your App then Whatsapp \> Configuration 33 | - Create your webhook using your URL and put the Whatsapp Security Key 34 | as validation Key 35 | - Administer the Webhook and activate the messages webhook 36 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/wizards/whatsapp_composer.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | whatsapp.composer.form (in mail_gateway_whatsapp) 7 | whatsapp.composer 8 | 9 |
10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
39 |
40 |
41 |
42 | 43 | 44 | Send Whatsapp Message 45 | whatsapp.composer 46 | form 47 | {} 48 | new 49 | 50 |
51 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "18.0*" 7 | push: 8 | branches: 9 | - "18.0" 10 | - "18.0-ocabot-*" 11 | 12 | jobs: 13 | unreleased-deps: 14 | runs-on: ubuntu-latest 15 | name: Detect unreleased dependencies 16 | steps: 17 | - uses: actions/checkout@v4 18 | - run: | 19 | for reqfile in requirements.txt test-requirements.txt ; do 20 | if [ -f ${reqfile} ] ; then 21 | result=0 22 | # reject non-comment lines that contain a / (i.e. URLs, relative paths) 23 | grep "^[^#].*/" ${reqfile} || result=$? 24 | if [ $result -eq 0 ] ; then 25 | echo "Unreleased dependencies found in ${reqfile}." 26 | exit 1 27 | fi 28 | fi 29 | done 30 | test: 31 | runs-on: ubuntu-22.04 32 | container: ${{ matrix.container }} 33 | name: ${{ matrix.name }} 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | include: 38 | - container: ghcr.io/oca/oca-ci/py3.10-odoo18.0:latest 39 | name: test with Odoo 40 | - container: ghcr.io/oca/oca-ci/py3.10-ocb18.0:latest 41 | name: test with OCB 42 | makepot: "true" 43 | services: 44 | postgres: 45 | image: postgres:12.0 46 | env: 47 | POSTGRES_USER: odoo 48 | POSTGRES_PASSWORD: odoo 49 | POSTGRES_DB: odoo 50 | ports: 51 | - 5432:5432 52 | env: 53 | OCA_ENABLE_CHECKLOG_ODOO: "1" 54 | steps: 55 | - uses: actions/checkout@v4 56 | with: 57 | persist-credentials: false 58 | - name: Install addons and dependencies 59 | run: oca_install_addons 60 | - name: Check licenses 61 | run: manifestoo -d . check-licenses 62 | - name: Check development status 63 | run: manifestoo -d . check-dev-status --default-dev-status=Beta 64 | - name: Initialize test db 65 | run: oca_init_test_database 66 | - name: Run tests 67 | run: oca_run_tests 68 | - uses: codecov/codecov-action@v4 69 | with: 70 | token: ${{ secrets.CODECOV_TOKEN }} 71 | - name: Update .pot files 72 | run: oca_export_and_push_pot https://x-access-token:${{ secrets.GIT_PUSH_TOKEN }}@github.com/${{ github.repository }} 73 | if: ${{ matrix.makepot == 'true' && github.event_name == 'push' && github.repository_owner == 'OCA' }} 74 | -------------------------------------------------------------------------------- /mail_gateway/static/src/components/chatter/chatter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 24 | 25 | 29 | 32 |
36 | To: 37 | 42 | 46 | 47 |
48 |
49 | 50 |
51 | 55 | state.composerType !== 'note' and state.thread.recipients.length > 0 and state.composerType !== 'gateway' 58 | 59 |
60 |
61 | -------------------------------------------------------------------------------- /mail_gateway/views/res_partner_gateway_channel.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Partner Gateway Channel Form 7 | res.partner.gateway.channel 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 | 22 | Partner Gateway Channel Tree 23 | res.partner.gateway.channel 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Partner Gateway Channel Tree 35 | res.partner.gateway.channel 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Gateway Partner Channels 47 | res.partner.gateway.channel 48 | list,form 49 | [] 50 | {} 51 | 52 | 53 | Gateway Partner Channels 54 | 55 | 56 | 57 | 58 |
59 | -------------------------------------------------------------------------------- /mail_gateway/static/src/components/message/message_patch.esm.js: -------------------------------------------------------------------------------- 1 | import {Message} from "@mail/core/common/message"; 2 | import {_t} from "@web/core/l10n/translation"; 3 | import {patch} from "@web/core/utils/patch"; 4 | 5 | patch(Message.prototype, { 6 | hasAuthorClickable() { 7 | if ( 8 | this.message.gateway_type && 9 | this.message.author?.type === "guest" && 10 | this.message.author.id 11 | ) { 12 | return true; 13 | } 14 | return super.hasAuthorClickable(); 15 | }, 16 | getAuthorText() { 17 | if (this.hasAuthorClickable() && this.message.gateway_type) { 18 | return _t("Create partner"); 19 | } 20 | return super.getAuthorText(); 21 | }, 22 | onClickAuthor(ev) { 23 | if (this.message.gateway_type && this.hasAuthorClickable()) { 24 | ev.stopPropagation(); 25 | return this.env.services.action.doAction({ 26 | name: _t("Manage guest"), 27 | type: "ir.actions.act_window", 28 | res_model: "mail.guest.manage", 29 | context: {default_guest_id: this.message.author.id}, 30 | views: [[false, "form"]], 31 | target: "new", 32 | }); 33 | } 34 | return super.onClickAuthor(...arguments); 35 | }, 36 | onClickLinkGatewayToThread() { 37 | this.env.services.action.doAction({ 38 | name: _t("Link Message to thread"), 39 | type: "ir.actions.act_window", 40 | res_model: "mail.message.gateway.link", 41 | context: {default_message_id: this.message.id}, 42 | views: [[false, "form"]], 43 | target: "new", 44 | }); 45 | }, 46 | onClickSendWithGateway() { 47 | this.env.services.action.doAction({ 48 | name: _t("Send with gateway"), 49 | type: "ir.actions.act_window", 50 | res_model: "mail.message.gateway.send", 51 | context: { 52 | ...this.props.message.gateway_channel_data, 53 | default_message_id: this.props.message.id, 54 | }, 55 | views: [[false, "form"]], 56 | target: "new", 57 | }); 58 | }, 59 | openGatewayThreadRecord() { 60 | this.store.env.services.action.doAction({ 61 | type: "ir.actions.act_window", 62 | res_id: this.message.gateway_thread_data.id, 63 | res_model: this.message.gateway_thread_data.model, 64 | views: [[false, "form"]], 65 | }); 66 | }, 67 | }); 68 | -------------------------------------------------------------------------------- /mail_gateway_telegram/views/mail_gateway.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | mail.gateway.form (in mail_gateway_telegram) 8 | mail.gateway 9 | 10 | 11 | 12 | 17 | 18 | 19 | 24 |
25 |
    26 |
  1. Access Telegram and start a converstation with BotFather.
  2. 31 |
  3. Create a bot using the command /newbot. The system will ask for a bot name. Remember that it needs to end with the word bot.
  4. 33 |
  5. Copy the token given by BotFather to access the HTTP API to the token field.
  6. 35 |
  7. Define a Webhook key and a webhook secret of your choice in its corresponding field, in order to secure the connection.
  8. 37 |
  9. Press the save button and the integrate webhook smart button will appear.
  10. 39 |
  11. Press the Integrate webhook button.
  12. 40 |
41 |
42 |
43 | If you want to add an extra layer of security, you can check Has New Channel Security 45 | and define a Telegram security key. 46 | New chats will be created only with the command /start SECURITY_KEY. 48 |
49 |
50 |
51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /mail_gateway/models/mail_gateway_abstract.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | from odoo import Command, models 4 | 5 | 6 | class MailGatewayAbstract(models.AbstractModel): 7 | _name = "mail.gateway.abstract" 8 | _description = "Gateway abstract for functions" 9 | 10 | def _verify_update(self, bot_data, kwargs): 11 | return True 12 | 13 | def _receive_update(self, gateway, kwargs): 14 | pass 15 | 16 | def _post_process_message(self, message, channel): 17 | self.env["mail.notification"].search( 18 | [("gateway_channel_id", "=", channel.id), ("is_read", "=", False)] 19 | )._set_read_gateway() 20 | 21 | def _post_process_reply(self, related_message): 22 | pass 23 | 24 | def _update_content_after_hook(self, channel, message): 25 | pass 26 | 27 | def _set_webhook(self, gateway): 28 | gateway.integrated_webhook_state = "integrated" 29 | 30 | def _remove_webhook(self, gateway): 31 | gateway.integrated_webhook_state = False 32 | 33 | def _get_channel(self, gateway, token, update, force_create=False): 34 | chat_id = gateway._get_channel_id(token) 35 | if chat_id: 36 | return gateway.env["discuss.channel"].browse(chat_id) 37 | if not force_create and gateway.has_new_channel_security: 38 | return False 39 | channel = gateway.env["discuss.channel"].create( 40 | self._get_channel_vals(gateway, token, update) 41 | ) 42 | channel._broadcast(channel.channel_member_ids.mapped("partner_id").ids) 43 | return channel 44 | 45 | def _get_author(self, gateway, update): 46 | return False 47 | 48 | def _get_channel_vals(self, gateway, token, update): 49 | author = self._get_author(gateway, update) 50 | members = [ 51 | Command.create({"partner_id": partner.id, "unpin_dt": False}) 52 | for partner in gateway.member_ids.partner_id 53 | ] 54 | if author: 55 | members.append( 56 | Command.create( 57 | { 58 | "partner_id": author._name == "res.partner" and author.id, 59 | "guest_id": author._name == "mail.guest" and author.id, 60 | } 61 | ) 62 | ) 63 | return { 64 | "gateway_channel_token": token, 65 | "gateway_id": gateway.id, 66 | "channel_type": "gateway", 67 | "channel_member_ids": members, 68 | "company_id": gateway.company_id.id, 69 | } 70 | 71 | def _send( 72 | self, 73 | gateway, 74 | record, 75 | auto_commit=False, 76 | raise_exception=False, 77 | parse_mode=False, 78 | ): 79 | raise NotImplementedError() 80 | 81 | def _get_message_body(self, record): 82 | return record.mail_message_id.body 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Runboat](https://img.shields.io/badge/runboat-Try%20me-875A7B.png)](https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=18.0) 3 | [![Pre-commit Status](https://github.com/OCA/social/actions/workflows/pre-commit.yml/badge.svg?branch=18.0)](https://github.com/OCA/social/actions/workflows/pre-commit.yml?query=branch%3A18.0) 4 | [![Build Status](https://github.com/OCA/social/actions/workflows/test.yml/badge.svg?branch=18.0)](https://github.com/OCA/social/actions/workflows/test.yml?query=branch%3A18.0) 5 | [![codecov](https://codecov.io/gh/OCA/social/branch/18.0/graph/badge.svg)](https://codecov.io/gh/OCA/social) 6 | [![Translation Status](https://translation.odoo-community.org/widgets/social-18-0/-/svg-badge.svg)](https://translation.odoo-community.org/engage/social-18-0/?utm_source=widget) 7 | 8 | 9 | 10 | # social 11 | 12 | social 13 | 14 | 15 | 16 | 17 | 18 | [//]: # (addons) 19 | 20 | Available addons 21 | ---------------- 22 | addon | version | maintainers | summary 23 | --- | --- | --- | --- 24 | [mail_activity_unlink_log](mail_activity_unlink_log/) | 18.0.1.0.0 | | Leave a message when an activity is unlinked 25 | [mail_gateway](mail_gateway/) | 18.0.1.0.3 | | Base module for gateway communications 26 | [mail_gateway_telegram](mail_gateway_telegram/) | 18.0.1.0.0 | | Set a gateway for telegram 27 | [mail_gateway_whatsapp](mail_gateway_whatsapp/) | 18.0.1.0.0 | | Set a gateway for whatsapp 28 | [mail_notification_with_history](mail_notification_with_history/) | 18.0.1.0.0 | TDu | Add the previous chatter discussion into new email notifications. 29 | [mail_thread_create_nolog](mail_thread_create_nolog/) | 18.0.1.0.2 | sebalix | Display a fake (non-stored) create log in the chatter. 30 | [res_company_mastodon_link](res_company_mastodon_link/) | 18.0.1.0.0 | legalsylvain | Add mastodon url at company model 31 | 32 | [//]: # (end addons) 33 | 34 | 35 | 36 | ## Licenses 37 | 38 | This repository is licensed under [AGPL-3.0](LICENSE). 39 | 40 | However, each module can have a totally different license, as long as they adhere to Odoo Community Association (OCA) 41 | policy. Consult each module's `__manifest__.py` file, which contains a `license` key 42 | that explains its license. 43 | 44 | ---- 45 | OCA, or the [Odoo Community Association](http://odoo-community.org/), is a nonprofit 46 | organization whose mission is to support the collaborative development of Odoo features 47 | and promote its widespread use. 48 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: "0 12 * * 0" 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Stale PRs and issues policy 12 | uses: actions/stale@v9 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | # General settings. 16 | ascending: true 17 | remove-stale-when-updated: true 18 | # Pull Requests settings. 19 | # 120+30 day stale policy for PRs 20 | # * Except PRs marked as "no stale" 21 | days-before-pr-stale: 120 22 | days-before-pr-close: 30 23 | exempt-pr-labels: "no stale" 24 | stale-pr-label: "stale" 25 | stale-pr-message: > 26 | There hasn't been any activity on this pull request in the past 4 months, so 27 | it has been marked as stale and it will be closed automatically if no 28 | further activity occurs in the next 30 days. 29 | 30 | If you want this PR to never become stale, please ask a PSC member to apply 31 | the "no stale" label. 32 | # Issues settings. 33 | # 180+30 day stale policy for open issues 34 | # * Except Issues marked as "no stale" 35 | days-before-issue-stale: 180 36 | days-before-issue-close: 30 37 | exempt-issue-labels: "no stale,needs more information" 38 | stale-issue-label: "stale" 39 | stale-issue-message: > 40 | There hasn't been any activity on this issue in the past 6 months, so it has 41 | been marked as stale and it will be closed automatically if no further 42 | activity occurs in the next 30 days. 43 | 44 | If you want this issue to never become stale, please ask a PSC member to 45 | apply the "no stale" label. 46 | 47 | # 15+30 day stale policy for issues pending more information 48 | # * Issues that are pending more information 49 | # * Except Issues marked as "no stale" 50 | - name: Needs more information stale issues policy 51 | uses: actions/stale@v9 52 | with: 53 | repo-token: ${{ secrets.GITHUB_TOKEN }} 54 | ascending: true 55 | only-labels: "needs more information" 56 | exempt-issue-labels: "no stale" 57 | days-before-stale: 15 58 | days-before-close: 30 59 | days-before-pr-stale: -1 60 | days-before-pr-close: -1 61 | remove-stale-when-updated: true 62 | stale-issue-label: "stale" 63 | stale-issue-message: > 64 | This issue needs more information and there hasn't been any activity 65 | recently, so it has been marked as stale and it will be closed automatically 66 | if no further activity occurs in the next 30 days. 67 | 68 | If you think this is a mistake, please ask a PSC member to remove the "needs 69 | more information" label. 70 | -------------------------------------------------------------------------------- /.pylintrc-mandatory: -------------------------------------------------------------------------------- 1 | 2 | [MASTER] 3 | load-plugins=pylint_odoo 4 | score=n 5 | 6 | [ODOOLINT] 7 | readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" 8 | manifest-required-authors=Odoo Community Association (OCA) 9 | manifest-required-keys=license 10 | manifest-deprecated-keys=description,active 11 | license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 12 | valid-odoo-versions=18.0 13 | 14 | [MESSAGES CONTROL] 15 | disable=all 16 | 17 | enable=anomalous-backslash-in-string, 18 | api-one-deprecated, 19 | api-one-multi-together, 20 | assignment-from-none, 21 | attribute-deprecated, 22 | class-camelcase, 23 | dangerous-default-value, 24 | dangerous-view-replace-wo-priority, 25 | development-status-allowed, 26 | duplicate-id-csv, 27 | duplicate-key, 28 | duplicate-xml-fields, 29 | duplicate-xml-record-id, 30 | eval-referenced, 31 | eval-used, 32 | incoherent-interpreter-exec-perm, 33 | license-allowed, 34 | manifest-author-string, 35 | manifest-deprecated-key, 36 | manifest-required-author, 37 | manifest-required-key, 38 | manifest-version-format, 39 | method-compute, 40 | method-inverse, 41 | method-required-super, 42 | method-search, 43 | openerp-exception-warning, 44 | pointless-statement, 45 | pointless-string-statement, 46 | print-used, 47 | redundant-keyword-arg, 48 | redundant-modulename-xml, 49 | reimported, 50 | relative-import, 51 | return-in-init, 52 | rst-syntax-error, 53 | sql-injection, 54 | too-few-format-args, 55 | translation-field, 56 | translation-required, 57 | unreachable, 58 | use-vim-comment, 59 | wrong-tabs-instead-of-spaces, 60 | xml-syntax-error, 61 | attribute-string-redundant, 62 | character-not-valid-in-resource-link, 63 | consider-merging-classes-inherited, 64 | context-overridden, 65 | create-user-wo-reset-password, 66 | dangerous-filter-wo-user, 67 | dangerous-qweb-replace-wo-priority, 68 | deprecated-data-xml-node, 69 | deprecated-openerp-xml-node, 70 | duplicate-po-message-definition, 71 | except-pass, 72 | file-not-used, 73 | invalid-commit, 74 | manifest-maintainers-list, 75 | missing-newline-extrafiles, 76 | missing-readme, 77 | missing-return, 78 | odoo-addons-relative-import, 79 | old-api7-method-defined, 80 | po-msgstr-variables, 81 | po-syntax-error, 82 | renamed-field-parameter, 83 | resource-not-exist, 84 | str-format-used, 85 | test-folder-imported, 86 | translation-contains-variable, 87 | translation-positional-used, 88 | unnecessary-utf8-coding-comment, 89 | website-manifest-key-not-valid-uri, 90 | xml-attribute-translatable, 91 | xml-deprecated-qweb-directive, 92 | xml-deprecated-tree-attribute, 93 | external-request-timeout 94 | 95 | [REPORTS] 96 | msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 97 | output-format=colorized 98 | reports=no 99 | -------------------------------------------------------------------------------- /mail_gateway/security/security.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Gateway 5 | 6 | 7 | User 8 | 9 | 13 | 14 | 15 | discuss.channel: access gateway 16 | 17 | 18 | [('channel_type', '=', 'gateway'), ('company_id', 'in', company_ids + [False]) ] 21 | 22 | 23 | 24 | 25 | 26 | 27 | Mail.gateway: multicompany rule 28 | 29 | [('company_id', 'in', company_ids + [False]) ] 32 | 33 | 34 | 35 | 36 | 37 | 38 | res.partner.gateway.channel: multicompany rule 39 | 40 | [('company_id', 'in', company_ids + [False]) ] 43 | 44 | 45 | 46 | 47 | 48 | 49 | discuss.channel.member: write its own entries on gateway channels members 52 | 53 | 54 | [('channel_id.channel_type', '=', 'gateway')] 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /mail_gateway_telegram/static/description/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 34 | 36 | 42 | 46 | 50 | 51 | 52 | Telegram_logo 54 | 61 | 65 | 69 | 73 | 75 | 76 | 78 | Telegram_logo 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/models/mail_gateway.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Creu Blanca 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | import requests 4 | from werkzeug.urls import url_join 5 | 6 | from odoo import _, api, fields, models 7 | from odoo.exceptions import UserError 8 | 9 | BASE_URL = "https://graph.facebook.com/" 10 | 11 | 12 | class MailGateway(models.Model): 13 | _inherit = "mail.gateway" 14 | 15 | whatsapp_security_key = fields.Char() 16 | gateway_type = fields.Selection( 17 | selection_add=[("whatsapp", "WhatsApp")], ondelete={"whatsapp": "cascade"} 18 | ) 19 | whatsapp_from_phone = fields.Char() 20 | whatsapp_version = fields.Char(default="23.0") 21 | whatsapp_account_id = fields.Char() 22 | whatsapp_template_ids = fields.One2many("mail.whatsapp.template", "gateway_id") 23 | whatsapp_template_count = fields.Integer(compute="_compute_whatsapp_template_count") 24 | 25 | @api.depends("whatsapp_template_ids") 26 | def _compute_whatsapp_template_count(self): 27 | for gateway in self: 28 | gateway.whatsapp_template_count = len(gateway.whatsapp_template_ids) 29 | 30 | def button_import_whatsapp_template(self): 31 | self.ensure_one() 32 | WhatsappTemplate = self.env["mail.whatsapp.template"] 33 | if not self.whatsapp_account_id: 34 | raise UserError(_("WhatsApp Account is required to import templates.")) 35 | meta_info = {} 36 | template_url = url_join( 37 | BASE_URL, 38 | f"v{self.whatsapp_version}/{self.whatsapp_account_id}/message_templates", 39 | ) 40 | try: 41 | meta_request = requests.get( 42 | template_url, 43 | headers={"Authorization": f"Bearer {self.token}"}, 44 | timeout=10, 45 | ) 46 | meta_request.raise_for_status() 47 | meta_info = meta_request.json() 48 | except Exception as err: 49 | raise UserError(str(err)) from err 50 | current_templates = WhatsappTemplate.with_context(active_test=False).search( 51 | [("gateway_id", "=", self.id)] 52 | ) 53 | templates_by_id = {t.template_uid: t for t in current_templates} 54 | create_vals = [] 55 | for template_data in meta_info.get("data", []): 56 | ws_template = templates_by_id.get(template_data["id"]) 57 | if ws_template: 58 | ws_template.write( 59 | WhatsappTemplate._prepare_values_to_import(self, template_data) 60 | ) 61 | else: 62 | create_vals.append( 63 | WhatsappTemplate._prepare_values_to_import(self, template_data) 64 | ) 65 | WhatsappTemplate.create(create_vals) 66 | return { 67 | "type": "ir.actions.client", 68 | "tag": "display_notification", 69 | "params": { 70 | "title": _("WathsApp Templates"), 71 | "type": "success", 72 | "message": _("Synchronization successfully."), 73 | "next": {"type": "ir.actions.act_window_close"}, 74 | }, 75 | } 76 | -------------------------------------------------------------------------------- /mail_gateway/models/res_partner.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import api, fields, models 5 | 6 | 7 | class ResPartner(models.Model): 8 | """Update of res.partner class to take into account the gateway.""" 9 | 10 | _inherit = "res.partner" 11 | 12 | gateway_channel_ids = fields.One2many( 13 | "res.partner.gateway.channel", inverse_name="partner_id" 14 | ) 15 | 16 | def _get_channels_as_member(self): 17 | channels = super()._get_channels_as_member() 18 | if self.env.user.has_group("mail_gateway.gateway_user"): 19 | channels |= self.env["discuss.channel"].search( 20 | [ 21 | ("channel_type", "=", "gateway"), 22 | ( 23 | "channel_member_ids", 24 | "in", 25 | self.env["discuss.channel.member"] 26 | .sudo() 27 | ._search( 28 | [ 29 | ("partner_id", "=", self.id), 30 | ("is_pinned", "=", True), 31 | ] 32 | ), 33 | ), 34 | ] 35 | ) 36 | return channels 37 | 38 | 39 | class ResPartnerGatewayChannel(models.Model): 40 | _name = "res.partner.gateway.channel" 41 | _description = "Technical data used to get the gateway author" 42 | 43 | name = fields.Char(related="gateway_id.name") 44 | partner_id = fields.Many2one( 45 | "res.partner", required=True, readonly=True, ondelete="cascade" 46 | ) 47 | gateway_id = fields.Many2one( 48 | "mail.gateway", required=True, readonly=True, ondelete="cascade" 49 | ) 50 | gateway_token = fields.Char(readonly=True) 51 | company_id = fields.Many2one( 52 | "res.company", related="gateway_id.company_id", store=True 53 | ) 54 | 55 | @api.depends_context("mail_gateway_partner_info") 56 | def _compute_display_name(self): 57 | # Be able to tell to which partner belongs the gateway partner channel 58 | # e.g.: picking it from a selector 59 | if not self.env.context.get("mail_gateway_partner_info"): 60 | return super()._compute_display_name() 61 | for gateway_channel in self: 62 | gateway_channel.display_name = ( 63 | f"{gateway_channel.partner_id.display_name} ({gateway_channel.name})" 64 | ) 65 | 66 | _sql_constraints = [ 67 | ( 68 | "unique_partner_gateway", 69 | "UNIQUE(partner_id, gateway_id)", 70 | "Partner can only have one configuration for each gateway.", 71 | ), 72 | ] 73 | 74 | def mail_format(self): 75 | return [r._mail_format() for r in self] 76 | 77 | def _mail_format(self): 78 | return { 79 | "id": self.id, 80 | "name": self.name, 81 | "gateway": { 82 | "id": self.gateway_id.id, 83 | "name": self.gateway_id.name, 84 | "type": self.gateway_id.gateway_type, 85 | }, 86 | } 87 | -------------------------------------------------------------------------------- /mail_gateway/controllers/gateway.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). 3 | 4 | import json 5 | import logging 6 | 7 | from odoo.http import Controller, request, route 8 | 9 | from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context 10 | 11 | _logger = logging.getLogger(__name__) 12 | 13 | 14 | class GatewayController(Controller): 15 | @route( 16 | "/gateway///update", 17 | type="http", 18 | auth="public", 19 | methods=["GET", "POST"], 20 | csrf=False, 21 | ) 22 | @add_guest_to_context 23 | def post_update(self, usage, token, *args, **kwargs): 24 | if request.httprequest.method == "GET": 25 | bot_data = request.env["mail.gateway"]._get_gateway( 26 | token, gateway_type=usage, state="pending" 27 | ) 28 | if not bot_data: 29 | return request.make_response( 30 | json.dumps({}), 31 | [ 32 | ("Content-Type", "application/json"), 33 | ], 34 | ) 35 | return ( 36 | request.env[f"mail.gateway.{usage}"] 37 | .with_user(bot_data["webhook_user_id"]) 38 | .with_company(bot_data["company_id"]) 39 | ._receive_get_update(bot_data, request, **kwargs) 40 | ) 41 | bot_data = request.env["mail.gateway"]._get_gateway( 42 | token, gateway_type=usage, state="integrated" 43 | ) 44 | if not bot_data: 45 | _logger.warning( 46 | "Gateway was not found for token %s with usage %s", token, usage 47 | ) 48 | return request.make_response( 49 | json.dumps({}), 50 | [ 51 | ("Content-Type", "application/json"), 52 | ], 53 | ) 54 | jsonrequest = json.loads( 55 | request.httprequest.get_data().decode(request.httprequest.charset) 56 | ) 57 | dispatcher = ( 58 | request.env[f"mail.gateway.{usage}"] 59 | .with_user(bot_data["webhook_user_id"]) 60 | .with_context(no_gateway_notification=True) 61 | ) 62 | if not dispatcher._verify_update(bot_data, jsonrequest): 63 | _logger.warning( 64 | "Message could not be verified for token %s with usage %s", token, usage 65 | ) 66 | return request.make_response( 67 | json.dumps({}), 68 | [ 69 | ("Content-Type", "application/json"), 70 | ], 71 | ) 72 | _logger.debug( 73 | "Received message for token %s with usage %s: %s", 74 | token, 75 | usage, 76 | json.dumps(jsonrequest), 77 | ) 78 | gateway = dispatcher.env["mail.gateway"].browse(bot_data["id"]) 79 | dispatcher._receive_update(gateway, jsonrequest) 80 | return request.make_response( 81 | json.dumps({}), 82 | [ 83 | ("Content-Type", "application/json"), 84 | ], 85 | ) 86 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/models/mail_message.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | 4 | from odoo import api, models 5 | 6 | 7 | class MailMessage(models.AbstractModel): 8 | _inherit = "mail.message" 9 | 10 | def _get_message_fields(self): 11 | return [ 12 | "body", 13 | "date", 14 | "message_type", 15 | "model", 16 | "res_id", 17 | ] 18 | 19 | @api.model 20 | def _generate_messsage(self, domain): 21 | # Generate a creation message only if messages are fetched for a record. 22 | # In that case there is no specific record implied, so no need to generate the 23 | # the message. 24 | record = self._messages_for_record(domain) 25 | 26 | if record and record._log_access: 27 | author = record.create_uid.partner_id 28 | creation_message = record._creation_message() 29 | create_message_record = self.new( 30 | { 31 | "body": creation_message, 32 | "date": record.create_date, 33 | "model": record._name, 34 | "res_id": record.id, 35 | "message_type": "notification", 36 | } 37 | ) 38 | 39 | data = create_message_record._read_format( 40 | self._get_message_fields(), load=False 41 | )[0] 42 | 43 | data.update( 44 | { 45 | # An ID is required by the thread client-side 46 | # so we generate one that doesn't really exist 47 | # but based on the current thread so it'll always be 48 | # unique (used by the thread_cache client model). 49 | "id": self._get_create_message_id(domain), 50 | # Author is not preserved above when creating the fake-record 51 | "author": { 52 | "id": author.id, 53 | "name": author.name, 54 | # 'partner' type will return odoo bot avatar 55 | "type": "partner", 56 | }, 57 | } 58 | ) 59 | 60 | return data 61 | 62 | return None 63 | 64 | def _get_create_message_id(self, domain): 65 | # Odoo JS client needs an ID to manage its cache (thread_cache). 66 | # Return the negative version of the greatest message ID 67 | # of the current record so it's always unique. 68 | return -self.search(domain, limit=1, order="id DESC").id 69 | 70 | @api.model 71 | def _messages_for_record(self, domain): 72 | """Return the record from the domain used in `message_fetch`.""" 73 | model = res_id = None 74 | for part in domain: 75 | if len(part) == 3 and part[0] == "model" and part[1] == "=": 76 | model = part[2] 77 | if len(part) == 3 and part[0] == "res_id" and part[1] == "=": 78 | res_id = part[2] 79 | if model and res_id: 80 | break 81 | if model and res_id: 82 | return self.env[model].browse(res_id).exists() 83 | return self.browse() 84 | -------------------------------------------------------------------------------- /mail_gateway_whatsapp/static/description/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 36 | 37 | 44 | 48 | 49 | -------------------------------------------------------------------------------- /mail_gateway/wizards/mail_compose_gateway_message.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class MailComposeGatewayMessage(models.TransientModel): 8 | _name = "mail.compose.gateway.message" 9 | _inherit = "mail.compose.message" 10 | _description = "Mail Compose Gateway Message" 11 | 12 | wizard_partner_ids = fields.Many2many( 13 | "res.partner", 14 | "mail_compose_gateway_message_res_partner_rel", 15 | "wizard_id", 16 | "partner_id", 17 | ) 18 | wizard_channel_ids = fields.Many2many( 19 | "res.partner.gateway.channel", 20 | "mail_compose_gateway_message_gateway_channel_rel", 21 | "wizard_id", 22 | "channel_id", 23 | ) 24 | attachment_ids = fields.Many2many( 25 | "ir.attachment", 26 | "mail_compose_gateway_message_ir_attachments_rel", 27 | "wizard_id", 28 | "attachment_id", 29 | "Attachments", 30 | ) 31 | partner_ids = fields.Many2many( 32 | "res.partner", 33 | "mail_compose_gateway_message_res_partner_rel", 34 | "wizard_id", 35 | "partner_id", 36 | "Additional Contacts", 37 | domain=lambda r: r._partner_ids_domain(), 38 | ) 39 | # Dummy compatibility with other OCA modules 40 | # OCA/mail_attach_existing_attachment 41 | object_attachment_ids = fields.Many2many( 42 | comodel_name="ir.attachment", 43 | relation="mail_compose_gateway_message_ir_attachments_object_rel", 44 | column1="wizard_id", 45 | column2="attachment_id", 46 | string="Object Attachments", 47 | ) 48 | # OCA/mail_composer_cc_bcc 49 | partner_cc_ids = fields.Many2many( 50 | comodel_name="res.partner", 51 | relation="mail_compose_gateway_message_res_partner_cc_rel", 52 | column1="wizard_id", 53 | column2="partner_id", 54 | string="Cc", 55 | ) 56 | partner_bcc_ids = fields.Many2many( 57 | comodel_name="res.partner", 58 | relation="mail_compose_gateway_message_res_partner_bcc_rel", 59 | column1="wizard_id", 60 | column2="partner_id", 61 | string="Bcc", 62 | ) 63 | 64 | def _partner_ids_domain(self): 65 | return [("id", "in", self.env.context.get("default_partner_ids", []))] 66 | 67 | def _prepare_mail_values_dynamic(self, res_ids): 68 | self.ensure_one() 69 | values = super()._prepare_mail_values_dynamic(res_ids) 70 | values[res_ids[0]]["gateway_notifications"] = [ 71 | { 72 | "partner_id": channel.partner_id.id, 73 | "channel_type": "gateway", 74 | "gateway_channel_id": channel.id, 75 | } 76 | for channel in self.wizard_channel_ids 77 | ] 78 | return values 79 | 80 | def _prepare_mail_values_static(self): 81 | values = super()._prepare_mail_values_static() 82 | values["gateway_notifications"] = [ 83 | { 84 | "partner_id": channel.partner_id.id, 85 | "channel_type": "gateway", 86 | "gateway_channel_id": channel.id, 87 | } 88 | for channel in self.wizard_channel_ids 89 | ] 90 | return values 91 | -------------------------------------------------------------------------------- /mail_activity_unlink_log/README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://odoo-community.org/readme-banner-image 2 | :target: https://odoo-community.org/get-involved?utm_source=readme 3 | :alt: Odoo Community Association 4 | 5 | ============================== 6 | Mail Activities: log on unlink 7 | ============================== 8 | 9 | .. 10 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 11 | !! This file is generated by oca-gen-addon-readme !! 12 | !! changes will be overwritten. !! 13 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 14 | !! source digest: sha256:9196aedd490fc01c791256582122032365ce7d26a11a5c0e13b2ae66777b06c3 15 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 16 | 17 | .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png 18 | :target: https://odoo-community.org/page/development-status 19 | :alt: Beta 20 | .. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png 21 | :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html 22 | :alt: License: AGPL-3 23 | .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github 24 | :target: https://github.com/OCA/social/tree/18.0/mail_activity_unlink_log 25 | :alt: OCA/social 26 | .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png 27 | :target: https://translation.odoo-community.org/projects/social-18-0/social-18-0-mail_activity_unlink_log 28 | :alt: Translate me on Weblate 29 | .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png 30 | :target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=18.0 31 | :alt: Try me on Runboat 32 | 33 | |badge1| |badge2| |badge3| |badge4| |badge5| 34 | 35 | This addon enforces the creation of a message when an activity is 36 | unlinked. This change tries to avoid a possible bad practice of deletion 37 | of an activity without notifying the deletion. 38 | 39 | **Table of contents** 40 | 41 | .. contents:: 42 | :local: 43 | 44 | Bug Tracker 45 | =========== 46 | 47 | Bugs are tracked on `GitHub Issues `_. 48 | In case of trouble, please check there if your issue has already been reported. 49 | If you spotted it first, help us to smash it by providing a detailed and welcomed 50 | `feedback `_. 51 | 52 | Do not contact contributors directly about support or help with technical issues. 53 | 54 | Credits 55 | ======= 56 | 57 | Authors 58 | ------- 59 | 60 | * CreuBlanca 61 | 62 | Contributors 63 | ------------ 64 | 65 | - Enric Tobella 66 | 67 | Maintainers 68 | ----------- 69 | 70 | This module is maintained by the OCA. 71 | 72 | .. image:: https://odoo-community.org/logo.png 73 | :alt: Odoo Community Association 74 | :target: https://odoo-community.org 75 | 76 | OCA, or the Odoo Community Association, is a nonprofit organization whose 77 | mission is to support the collaborative development of Odoo features and 78 | promote its widespread use. 79 | 80 | This module is part of the `OCA/social `_ project on GitHub. 81 | 82 | You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 83 | -------------------------------------------------------------------------------- /mail_notification_with_history/tests/test_mail_notification_with_history.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | 4 | from unittest.mock import Mock, patch 5 | 6 | from odoo.addons.base.tests.common import SavepointCaseWithUserDemo 7 | 8 | 9 | class TestMailNotificationWithHistory(SavepointCaseWithUserDemo): 10 | @classmethod 11 | def setUpClass(cls): 12 | super().setUpClass() 13 | cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) 14 | cls.base_template = cls.env.ref("mail.mail_notification_layout") 15 | cls.mail_message = cls.env.ref("mail.mail_message_channel_1_2_1") 16 | cls.message_subtype = cls.env.ref("mail.mt_comment") 17 | cls.render_values = { 18 | "button_access": {"title": "View Record", "url": "http://127.0.0.1"}, 19 | "actions": [], 20 | "company": cls.env.company, 21 | "has_button_access": True, 22 | "is_discussion": True, 23 | "lang": "en_US", 24 | "message": cls.mail_message, 25 | "subtype": cls.message_subtype, 26 | } 27 | 28 | def test_thread_history_is_included(self): 29 | with patch( 30 | ( 31 | "odoo.addons.mail_notification_with_history.models.mail_thread." 32 | "MailThread._mail_notification_include_history" 33 | ), 34 | new=Mock(return_value=True), 35 | ): 36 | body = self.env["ir.qweb"]._render( 37 | "mail.mail_notification_layout", 38 | self.render_values, 39 | minimal_qcontext=True, 40 | ) 41 | self.assertTrue(body.find("Discussion") >= 0) 42 | 43 | def test_thread_history_is_not_included(self): 44 | body = self.env["ir.qweb"]._render( 45 | "mail.mail_notification_layout", 46 | self.render_values, 47 | minimal_qcontext=True, 48 | ) 49 | self.assertTrue(body.find("Discussion") == -1) 50 | 51 | def test_domain_message_in_history(self): 52 | """Check the good number of message is returned for the history.""" 53 | # There is already two messages from demo data and we create... 54 | # One email message that should be included 55 | self.env["mail.message"].create( 56 | { 57 | "message_type": "email", 58 | "model": self.mail_message.model, 59 | "res_id": self.mail_message.res_id, 60 | } 61 | ) 62 | # One comment (message) should be included 63 | self.env["mail.message"].create( 64 | { 65 | "message_type": "comment", 66 | "model": self.mail_message.model, 67 | "res_id": self.mail_message.res_id, 68 | "subtype_id": self.env.ref("mail.mt_comment").id, 69 | } 70 | ) 71 | # One comment (internal note) should NOT be included 72 | self.env["mail.message"].create( 73 | { 74 | "message_type": "comment", 75 | "model": self.mail_message.model, 76 | "res_id": self.mail_message.res_id, 77 | "subtype_id": self.env.ref("mail.mt_note").id, 78 | } 79 | ) 80 | with patch( 81 | ( 82 | "odoo.addons.mail_notification_with_history.models.mail_thread." 83 | "MailThread._mail_notification_include_history" 84 | ), 85 | new=Mock(return_value=True), 86 | ): 87 | history = self.mail_message._get_notification_message_history() 88 | # So that is four message to include in history 89 | self.assertEqual(len(history), 4) 90 | -------------------------------------------------------------------------------- /res_company_mastodon_link/README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://odoo-community.org/readme-banner-image 2 | :target: https://odoo-community.org/get-involved?utm_source=readme 3 | :alt: Odoo Community Association 4 | 5 | ======================= 6 | Social Media - Mastodon 7 | ======================= 8 | 9 | .. 10 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 11 | !! This file is generated by oca-gen-addon-readme !! 12 | !! changes will be overwritten. !! 13 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 14 | !! source digest: sha256:e66dc2e58afa509ecc8de301b6b383dc51db9d2038ab0c7d9bcc0fdfe0dd46aa 15 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 16 | 17 | .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png 18 | :target: https://odoo-community.org/page/development-status 19 | :alt: Beta 20 | .. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png 21 | :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html 22 | :alt: License: AGPL-3 23 | .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github 24 | :target: https://github.com/OCA/social/tree/18.0/res_company_mastodon_link 25 | :alt: OCA/social 26 | .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png 27 | :target: https://translation.odoo-community.org/projects/social-18-0/social-18-0-res_company_mastodon_link 28 | :alt: Translate me on Weblate 29 | .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png 30 | :target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=18.0 31 | :alt: Try me on Runboat 32 | 33 | |badge1| |badge2| |badge3| |badge4| |badge5| 34 | 35 | This module adds a new "Mastodon URL" field, at company level. 36 | 37 | |res_company_form| 38 | 39 | Mastodon is a decentralized social network developed in Open Source. 40 | More information at https://joinmastodon.org/. 41 | 42 | .. |res_company_form| image:: https://raw.githubusercontent.com/OCA/social/18.0/res_company_mastodon_link/static/description/res_company_form.png 43 | 44 | **Table of contents** 45 | 46 | .. contents:: 47 | :local: 48 | 49 | Bug Tracker 50 | =========== 51 | 52 | Bugs are tracked on `GitHub Issues `_. 53 | In case of trouble, please check there if your issue has already been reported. 54 | If you spotted it first, help us to smash it by providing a detailed and welcomed 55 | `feedback `_. 56 | 57 | Do not contact contributors directly about support or help with technical issues. 58 | 59 | Credits 60 | ======= 61 | 62 | Authors 63 | ------- 64 | 65 | * GRAP 66 | 67 | Contributors 68 | ------------ 69 | 70 | - Sylvain LE GAL 71 | <`https://twitter.com/legalsylvain\\> >`__ 72 | 73 | Maintainers 74 | ----------- 75 | 76 | This module is maintained by the OCA. 77 | 78 | .. image:: https://odoo-community.org/logo.png 79 | :alt: Odoo Community Association 80 | :target: https://odoo-community.org 81 | 82 | OCA, or the Odoo Community Association, is a nonprofit organization whose 83 | mission is to support the collaborative development of Odoo features and 84 | promote its widespread use. 85 | 86 | .. |maintainer-legalsylvain| image:: https://github.com/legalsylvain.png?size=40px 87 | :target: https://github.com/legalsylvain 88 | :alt: legalsylvain 89 | 90 | Current `maintainer `__: 91 | 92 | |maintainer-legalsylvain| 93 | 94 | This module is part of the `OCA/social `_ project on GitHub. 95 | 96 | You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 97 | -------------------------------------------------------------------------------- /mail_gateway/models/discuss_channel.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Dixmit 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | import base64 5 | 6 | from odoo import api, fields, models 7 | 8 | from odoo.addons.mail.tools.discuss import Store 9 | 10 | 11 | class MailChannel(models.Model): 12 | _inherit = "discuss.channel" 13 | 14 | gateway_channel_token = fields.Char() 15 | anonymous_name = fields.Char() # Same field we will use on im_livechat 16 | gateway_id = fields.Many2one("mail.gateway") 17 | gateway_message_ids = fields.One2many( 18 | "mail.notification", 19 | inverse_name="gateway_channel_id", 20 | ) 21 | company_id = fields.Many2one("res.company", default=False) 22 | channel_type = fields.Selection( 23 | selection_add=[("gateway", "Gateway")], ondelete={"gateway": "set default"} 24 | ) 25 | gateway_token = fields.Char( 26 | related="gateway_id.token", 27 | string="Gateway related Token", 28 | required=False, 29 | ) 30 | 31 | def _to_store(self, store: Store): 32 | result = super()._to_store(store) 33 | if not self: 34 | return result 35 | for record in self: 36 | store.add( 37 | record, 38 | { 39 | "gateway": { 40 | "id": record.gateway_id.id, 41 | "name": record.gateway_id.name, 42 | "type": record.gateway_id.gateway_type, 43 | }, 44 | "gateway_name": record.gateway_id.name, 45 | "gateway_id": record.gateway_id.id, 46 | }, 47 | ) 48 | return result 49 | 50 | def _generate_avatar_gateway(self): 51 | # We will use this function to set a default avatar on each module 52 | return False 53 | 54 | def _generate_avatar(self): 55 | if self.channel_type not in ("gateway"): 56 | return super()._generate_avatar() 57 | avatar = self._generate_avatar_gateway() 58 | if not avatar: 59 | return False 60 | return base64.b64encode(avatar.encode()) 61 | 62 | @api.returns("mail.message", lambda value: value.id) 63 | def message_post( 64 | self, *, message_type="notification", gateway_type=False, **kwargs 65 | ): 66 | message = super().message_post( 67 | message_type=message_type, 68 | gateway_type=gateway_type or self.gateway_id.gateway_type, 69 | **kwargs, 70 | ) 71 | if ( 72 | self.gateway_id 73 | and not self.env.context.get("no_gateway_notification", False) 74 | and message.message_type != "notification" 75 | ): 76 | self.env["mail.notification"].create( 77 | { 78 | "mail_message_id": message.id, 79 | "gateway_channel_id": self.id, 80 | "notification_type": "gateway", 81 | "gateway_type": self.gateway_id.gateway_type, 82 | } 83 | ).send_gateway() 84 | return message 85 | 86 | def _message_update_content( 87 | self, 88 | message, 89 | body, 90 | attachment_ids=None, 91 | partner_ids=None, 92 | strict=True, 93 | **kwargs, 94 | ): 95 | res = super()._message_update_content( 96 | message=message, 97 | body=body, 98 | attachment_ids=attachment_ids, 99 | partner_ids=partner_ids, 100 | strict=strict, 101 | **kwargs, 102 | ) 103 | if self.channel_type == "gateway" and message.gateway_notification_ids: 104 | self.env[ 105 | f"mail.gateway.{self.gateway_id.gateway_type}" 106 | ]._update_content_after_hook(self, message) 107 | return res 108 | -------------------------------------------------------------------------------- /mail_gateway_telegram/i18n/mail_gateway_telegram.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * mail_gateway_telegram 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: mail_gateway_telegram 17 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 18 | msgid "Access Telegram and start a converstation with" 19 | msgstr "" 20 | 21 | #. module: mail_gateway_telegram 22 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 23 | msgid "BotFather" 24 | msgstr "" 25 | 26 | #. module: mail_gateway_telegram 27 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 28 | msgid "" 29 | "Copy the token given by BotFather to access the HTTP API to the token" 30 | " field." 31 | msgstr "" 32 | 33 | #. module: mail_gateway_telegram 34 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 35 | msgid "" 36 | "Create a bot using the command /newbot. The system will ask for a bot name. " 37 | "Remember that it needs to end with the word bot." 38 | msgstr "" 39 | 40 | #. module: mail_gateway_telegram 41 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 42 | msgid "" 43 | "Define a Webhook key and a webhook secret of your choice in its " 44 | "corresponding field, in order to secure the connection." 45 | msgstr "" 46 | 47 | #. module: mail_gateway_telegram 48 | #: model:ir.model,name:mail_gateway_telegram.model_discuss_channel 49 | msgid "Discussion Channel" 50 | msgstr "" 51 | 52 | #. module: mail_gateway_telegram 53 | #: model:ir.model.fields,field_description:mail_gateway_telegram.field_mail_gateway__gateway_type 54 | msgid "Gateway Type" 55 | msgstr "" 56 | 57 | #. module: mail_gateway_telegram 58 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 59 | msgid "" 60 | "If you want to add an extra layer of security, you can check Has New Channel Security\n" 61 | " and define a Telegram security key.\n" 62 | " New chats will be created only with the command /start SECURITY_KEY." 63 | msgstr "" 64 | 65 | #. module: mail_gateway_telegram 66 | #: model:ir.model,name:mail_gateway_telegram.model_mail_gateway 67 | msgid "Mail Gateway" 68 | msgstr "" 69 | 70 | #. module: mail_gateway_telegram 71 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 72 | msgid "Press the Integrate webhook button." 73 | msgstr "" 74 | 75 | #. module: mail_gateway_telegram 76 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 77 | msgid "" 78 | "Press the save button and the integrate webhook smart button will appear." 79 | msgstr "" 80 | 81 | #. module: mail_gateway_telegram 82 | #: model:ir.model.fields.selection,name:mail_gateway_telegram.selection__mail_gateway__gateway_type__telegram 83 | msgid "Telegram" 84 | msgstr "" 85 | 86 | #. module: mail_gateway_telegram 87 | #: model:ir.model,name:mail_gateway_telegram.model_mail_gateway_telegram 88 | msgid "Telegram Gateway services" 89 | msgstr "" 90 | 91 | #. module: mail_gateway_telegram 92 | #: model:ir.model.fields,field_description:mail_gateway_telegram.field_mail_gateway__telegram_security_key 93 | msgid "Telegram Security Key" 94 | msgstr "" 95 | 96 | #. module: mail_gateway_telegram 97 | #: model_terms:ir.ui.view,arch_db:mail_gateway_telegram.mail_gateway_form_view 98 | msgid "Telegram configuration" 99 | msgstr "" 100 | 101 | #. module: mail_gateway_telegram 102 | #. odoo-python 103 | #: code:addons/mail_gateway_telegram/models/mail_gateway_telegram.py:0 104 | msgid "Unable to send the telegram message" 105 | msgstr "" 106 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | [MASTER] 4 | load-plugins=pylint_odoo 5 | score=n 6 | 7 | [ODOOLINT] 8 | readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" 9 | manifest-required-authors=Odoo Community Association (OCA) 10 | manifest-required-keys=license 11 | manifest-deprecated-keys=description,active 12 | license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 13 | valid-odoo-versions=18.0 14 | 15 | [MESSAGES CONTROL] 16 | disable=all 17 | 18 | # This .pylintrc contains optional AND mandatory checks and is meant to be 19 | # loaded in an IDE to have it check everything, in the hope this will make 20 | # optional checks more visible to contributors who otherwise never look at a 21 | # green travis to see optional checks that failed. 22 | # .pylintrc-mandatory containing only mandatory checks is used the pre-commit 23 | # config as a blocking check. 24 | 25 | enable=anomalous-backslash-in-string, 26 | api-one-deprecated, 27 | api-one-multi-together, 28 | assignment-from-none, 29 | attribute-deprecated, 30 | class-camelcase, 31 | dangerous-default-value, 32 | dangerous-view-replace-wo-priority, 33 | development-status-allowed, 34 | duplicate-id-csv, 35 | duplicate-key, 36 | duplicate-xml-fields, 37 | duplicate-xml-record-id, 38 | eval-referenced, 39 | eval-used, 40 | incoherent-interpreter-exec-perm, 41 | license-allowed, 42 | manifest-author-string, 43 | manifest-deprecated-key, 44 | manifest-required-author, 45 | manifest-required-key, 46 | manifest-version-format, 47 | method-compute, 48 | method-inverse, 49 | method-required-super, 50 | method-search, 51 | openerp-exception-warning, 52 | pointless-statement, 53 | pointless-string-statement, 54 | print-used, 55 | redundant-keyword-arg, 56 | redundant-modulename-xml, 57 | reimported, 58 | relative-import, 59 | return-in-init, 60 | rst-syntax-error, 61 | sql-injection, 62 | too-few-format-args, 63 | translation-field, 64 | translation-required, 65 | unreachable, 66 | use-vim-comment, 67 | wrong-tabs-instead-of-spaces, 68 | xml-syntax-error, 69 | attribute-string-redundant, 70 | character-not-valid-in-resource-link, 71 | consider-merging-classes-inherited, 72 | context-overridden, 73 | create-user-wo-reset-password, 74 | dangerous-filter-wo-user, 75 | dangerous-qweb-replace-wo-priority, 76 | deprecated-data-xml-node, 77 | deprecated-openerp-xml-node, 78 | duplicate-po-message-definition, 79 | except-pass, 80 | file-not-used, 81 | invalid-commit, 82 | manifest-maintainers-list, 83 | missing-newline-extrafiles, 84 | missing-readme, 85 | missing-return, 86 | odoo-addons-relative-import, 87 | old-api7-method-defined, 88 | po-msgstr-variables, 89 | po-syntax-error, 90 | renamed-field-parameter, 91 | resource-not-exist, 92 | str-format-used, 93 | test-folder-imported, 94 | translation-contains-variable, 95 | translation-positional-used, 96 | unnecessary-utf8-coding-comment, 97 | website-manifest-key-not-valid-uri, 98 | xml-attribute-translatable, 99 | xml-deprecated-qweb-directive, 100 | xml-deprecated-tree-attribute, 101 | external-request-timeout, 102 | # messages that do not cause the lint step to fail 103 | consider-merging-classes-inherited, 104 | create-user-wo-reset-password, 105 | dangerous-filter-wo-user, 106 | deprecated-module, 107 | file-not-used, 108 | invalid-commit, 109 | missing-manifest-dependency, 110 | missing-newline-extrafiles, 111 | missing-readme, 112 | no-utf8-coding-comment, 113 | odoo-addons-relative-import, 114 | old-api7-method-defined, 115 | redefined-builtin, 116 | too-complex, 117 | unnecessary-utf8-coding-comment 118 | 119 | 120 | [REPORTS] 121 | msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 122 | output-format=colorized 123 | reports=no 124 | -------------------------------------------------------------------------------- /mail_notification_with_history/README.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Mail Notification With History 3 | ============================== 4 | 5 | .. 6 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 7 | !! This file is generated by oca-gen-addon-readme !! 8 | !! changes will be overwritten. !! 9 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 10 | !! source digest: sha256:758c6aa38b53fe83b11dd396bdd24dc7ed5e52fc91d34f93fb45709e899fc543 11 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 12 | 13 | .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png 14 | :target: https://odoo-community.org/page/development-status 15 | :alt: Beta 16 | .. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png 17 | :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html 18 | :alt: License: AGPL-3 19 | .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github 20 | :target: https://github.com/OCA/social/tree/18.0/mail_notification_with_history 21 | :alt: OCA/social 22 | .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png 23 | :target: https://translation.odoo-community.org/projects/social-18-0/social-18-0-mail_notification_with_history 24 | :alt: Translate me on Weblate 25 | .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png 26 | :target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=18.0 27 | :alt: Try me on Runboat 28 | 29 | |badge1| |badge2| |badge3| |badge4| |badge5| 30 | 31 | Followers of a discussion in the chatter of Odoo can be informed by 32 | email of a new message being created. This module adds in that email the 33 | history of the chatter discussion to help the recipient with more 34 | context. 35 | 36 | **Table of contents** 37 | 38 | .. contents:: 39 | :local: 40 | 41 | Installation 42 | ============ 43 | 44 | After installing the module its behaviour needs to be activated. This is 45 | done by setting the following class variable 46 | \_mail_notification_include_history to True, on the model that we would 47 | like to activate it. 48 | 49 | Bug Tracker 50 | =========== 51 | 52 | Bugs are tracked on `GitHub Issues `_. 53 | In case of trouble, please check there if your issue has already been reported. 54 | If you spotted it first, help us to smash it by providing a detailed and welcomed 55 | `feedback `_. 56 | 57 | Do not contact contributors directly about support or help with technical issues. 58 | 59 | Credits 60 | ======= 61 | 62 | Authors 63 | ------- 64 | 65 | * Camptocamp 66 | 67 | Contributors 68 | ------------ 69 | 70 | - `Camptocamp `__ 71 | 72 | - Thierry Ducrest 73 | - Tris Doan 74 | 75 | Other credits 76 | ------------- 77 | 78 | The migration of this module from 14.0 to 18.0 was financially supported 79 | by Camptocamp. 80 | 81 | Maintainers 82 | ----------- 83 | 84 | This module is maintained by the OCA. 85 | 86 | .. image:: https://odoo-community.org/logo.png 87 | :alt: Odoo Community Association 88 | :target: https://odoo-community.org 89 | 90 | OCA, or the Odoo Community Association, is a nonprofit organization whose 91 | mission is to support the collaborative development of Odoo features and 92 | promote its widespread use. 93 | 94 | .. |maintainer-TDu| image:: https://github.com/TDu.png?size=40px 95 | :target: https://github.com/TDu 96 | :alt: TDu 97 | 98 | Current `maintainer `__: 99 | 100 | |maintainer-TDu| 101 | 102 | This module is part of the `OCA/social `_ project on GitHub. 103 | 104 | You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 105 | -------------------------------------------------------------------------------- /mail_gateway/views/mail_gateway.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | mail.gateway.form 7 | mail.gateway 8 | 9 |
10 |
11 | 12 |
13 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 | 59 | 60 | 61 | 62 | mail.gateway.search 63 | mail.gateway 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | mail.gateway.tree 72 | mail.gateway 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Gateway 82 | mail.gateway 83 | list,form 84 | [] 85 | {} 86 | 87 | 88 | Gateway 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /mail_thread_create_nolog/README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://odoo-community.org/readme-banner-image 2 | :target: https://odoo-community.org/get-involved?utm_source=readme 3 | :alt: Odoo Community Association 4 | 5 | ============================= 6 | Mail Thread - Fake create log 7 | ============================= 8 | 9 | .. 10 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 11 | !! This file is generated by oca-gen-addon-readme !! 12 | !! changes will be overwritten. !! 13 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 14 | !! source digest: sha256:3d7f4ed6697a207e460cb66425f9d1e7c22c0b5b6bbff819fc6703369eec07c4 15 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 16 | 17 | .. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png 18 | :target: https://odoo-community.org/page/development-status 19 | :alt: Alpha 20 | .. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png 21 | :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html 22 | :alt: License: AGPL-3 23 | .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github 24 | :target: https://github.com/OCA/social/tree/18.0/mail_thread_create_nolog 25 | :alt: OCA/social 26 | .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png 27 | :target: https://translation.odoo-community.org/projects/social-18-0/social-18-0-mail_thread_create_nolog 28 | :alt: Translate me on Weblate 29 | .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png 30 | :target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=18.0 31 | :alt: Try me on Runboat 32 | 33 | |badge1| |badge2| |badge3| |badge4| |badge5| 34 | 35 | This module will block the creation in the database of 'Record created' 36 | messages on data models inheriting from mail.thread, but will instead 37 | generate this message on the fly when Odoo displays the messages of a 38 | record. 39 | 40 | This allows to reduce the size of the mail_message table. 41 | 42 | .. IMPORTANT:: 43 | This is an alpha version, the data model and design can change at any time without warning. 44 | Only for development or testing purpose, do not use in production. 45 | `More details on development status `_ 46 | 47 | **Table of contents** 48 | 49 | .. contents:: 50 | :local: 51 | 52 | Known issues / Roadmap 53 | ====================== 54 | 55 | - Ability to configure the create_nolog context key by data model 56 | 57 | Bug Tracker 58 | =========== 59 | 60 | Bugs are tracked on `GitHub Issues `_. 61 | In case of trouble, please check there if your issue has already been reported. 62 | If you spotted it first, help us to smash it by providing a detailed and welcomed 63 | `feedback `_. 64 | 65 | Do not contact contributors directly about support or help with technical issues. 66 | 67 | Credits 68 | ======= 69 | 70 | Authors 71 | ------- 72 | 73 | * Camptocamp 74 | 75 | Contributors 76 | ------------ 77 | 78 | - `Camptocamp `__: 79 | 80 | - Sébastien Alix 81 | 82 | - Chau Le chaulb@trobz.com 83 | 84 | Other credits 85 | ------------- 86 | 87 | The migration of this module from 14.0 to 18.0 was financially supported 88 | by Camptocamp. 89 | 90 | Maintainers 91 | ----------- 92 | 93 | This module is maintained by the OCA. 94 | 95 | .. image:: https://odoo-community.org/logo.png 96 | :alt: Odoo Community Association 97 | :target: https://odoo-community.org 98 | 99 | OCA, or the Odoo Community Association, is a nonprofit organization whose 100 | mission is to support the collaborative development of Odoo features and 101 | promote its widespread use. 102 | 103 | .. |maintainer-sebalix| image:: https://github.com/sebalix.png?size=40px 104 | :target: https://github.com/sebalix 105 | :alt: sebalix 106 | 107 | Current `maintainer `__: 108 | 109 | |maintainer-sebalix| 110 | 111 | This module is part of the `OCA/social `_ project on GitHub. 112 | 113 | You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 114 | --------------------------------------------------------------------------------