diff --git a/mail_layout_force/README.rst b/mail_layout_force/README.rst new file mode 100644 index 0000000000..63036992d6 --- /dev/null +++ b/mail_layout_force/README.rst @@ -0,0 +1,118 @@ +================= +Mail Layout Force +================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/15.0/mail_layout_force + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-15-0/social-15-0-mail_layout_force + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/205/15.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Odoo will add a default email layout on most commercial communications. + +The email layout is a ``QWeb`` view that ends up wrapping the message body +when sending an email. It usually displays the related document reference, +the company logo, and a small footer saying "Powered by Odoo". + +There are notably two main layouts used in Odoo, and the user can't control when +they're used, as it's hardcoded into the different applications. + +* ``mail.mail_notification_light`` +* ``mail.mail_notification_paynow`` + +This module allows to force a specific layout for a given ``email.template``, +effectively overwritting the one hardcoded by Odoo. + +This allows you to fully customize the way Odoo emails are rendered and sent +to your customers. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +# Go to Configuration > Technical > Emails > Templates +# Open the desired ``email.template`` record. +# In Advanced Parameters tab, find the Force Layout field. + +You can leave it empty to use the default email layout (chosen by Odoo). +You can force a custom email layout of your own. +You can use the *Mail: No-Layout notification template* to prevent Odoo +from adding a layout. + +To configure a custom layout of your own, some technical knowledge is needed. +You can see how the existing layouts are defined for details or inspiration: + +* ``mail.mail_notification_light`` +* ``mail.mail_notification_paynow`` +* ``mail.mail_notification_borders`` + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* `Camptocamp `_ + + * Iván Todorovich + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-ivantodorovich| image:: https://github.com/ivantodorovich.png?size=40px + :target: https://github.com/ivantodorovich + :alt: ivantodorovich + +Current `maintainer `__: + +|maintainer-ivantodorovich| + +This module is part of the `OCA/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_layout_force/__init__.py b/mail_layout_force/__init__.py new file mode 100644 index 0000000000..aee8895e7a --- /dev/null +++ b/mail_layout_force/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/mail_layout_force/__manifest__.py b/mail_layout_force/__manifest__.py new file mode 100644 index 0000000000..8d2466409a --- /dev/null +++ b/mail_layout_force/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author Iván Todorovich +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Mail Layout Force", + "summary": "Force a mail layout on selected email templates", + "version": "15.0.1.0.0", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["ivantodorovich"], + "website": "https://github.com/OCA/social", + "license": "AGPL-3", + "category": "Marketing", + "depends": ["mail"], + "demo": ["demo/mail_layout.xml"], + "data": ["data/mail_layout.xml", "views/mail_template.xml"], +} diff --git a/mail_layout_force/data/mail_layout.xml b/mail_layout_force/data/mail_layout.xml new file mode 100644 index 0000000000..7575cbd5d7 --- /dev/null +++ b/mail_layout_force/data/mail_layout.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/mail_layout_force/demo/mail_layout.xml b/mail_layout_force/demo/mail_layout.xml new file mode 100644 index 0000000000..b218fda2ec --- /dev/null +++ b/mail_layout_force/demo/mail_layout.xml @@ -0,0 +1,115 @@ + + + + + + + diff --git a/mail_layout_force/i18n/mail_layout_force.pot b/mail_layout_force/i18n/mail_layout_force.pot new file mode 100644 index 0000000000..0a32cd6c11 --- /dev/null +++ b/mail_layout_force/i18n/mail_layout_force.pot @@ -0,0 +1,60 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_layout_force +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: mail_layout_force +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_compose_message__display_name +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_template__display_name +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_thread__display_name +msgid "Display Name" +msgstr "" + +#. module: mail_layout_force +#: model:ir.model,name:mail_layout_force.model_mail_template +msgid "Email Templates" +msgstr "" + +#. module: mail_layout_force +#: model:ir.model,name:mail_layout_force.model_mail_thread +msgid "Email Thread" +msgstr "" + +#. module: mail_layout_force +#: model:ir.model,name:mail_layout_force.model_mail_compose_message +msgid "Email composition wizard" +msgstr "" + +#. module: mail_layout_force +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_template__force_email_layout_id +msgid "Force Layout" +msgstr "" + +#. module: mail_layout_force +#: model:ir.model.fields,help:mail_layout_force.field_mail_template__force_email_layout_id +msgid "Force a mail layout for this template." +msgstr "" + +#. module: mail_layout_force +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_compose_message__id +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_template__id +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_thread__id +msgid "ID" +msgstr "" + +#. module: mail_layout_force +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_compose_message____last_update +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_template____last_update +#: model:ir.model.fields,field_description:mail_layout_force.field_mail_thread____last_update +msgid "Last Modified on" +msgstr "" diff --git a/mail_layout_force/models/__init__.py b/mail_layout_force/models/__init__.py new file mode 100644 index 0000000000..89e090b241 --- /dev/null +++ b/mail_layout_force/models/__init__.py @@ -0,0 +1,2 @@ +from . import mail_template +from . import mail_thread diff --git a/mail_layout_force/models/mail_template.py b/mail_layout_force/models/mail_template.py new file mode 100644 index 0000000000..31d12ec622 --- /dev/null +++ b/mail_layout_force/models/mail_template.py @@ -0,0 +1,44 @@ +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author Iván Todorovich +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class MailTemplate(models.Model): + _inherit = "mail.template" + + force_email_layout_id = fields.Many2one( + comodel_name="ir.ui.view", + string="Force Layout", + domain=[("type", "=", "qweb"), ("mode", "=", "primary")], + context={"default_type": "qweb"}, + help="Force a mail layout for this template.", + ) + + def _ensure_force_email_layout_xml_id(self): + missing = self.force_email_layout_id.filtered(lambda rec: not rec.xml_id) + if missing: + vals = [ + { + "module": "__export__", + "name": "force_email_layout_%s" % rec.id, + "model": rec._name, + "res_id": rec.id, + } + for rec in missing + ] + self.env["ir.model.data"].sudo().create(vals) + self.force_email_layout_id.invalidate_cache(["xml_id"]) + + @api.model_create_multi + def create(self, vals_list): + records = super().create(vals_list) + records._ensure_force_email_layout_xml_id() + return records + + def write(self, vals): + res = super().write(vals) + if "force_email_layout_id" in vals: + self._ensure_force_email_layout_xml_id() + return res diff --git a/mail_layout_force/models/mail_thread.py b/mail_layout_force/models/mail_thread.py new file mode 100644 index 0000000000..25be8bddae --- /dev/null +++ b/mail_layout_force/models/mail_thread.py @@ -0,0 +1,20 @@ +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author Iván Todorovich +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + def message_post_with_template( + self, template_id, email_layout_xmlid=None, **kwargs + ): + # OVERRIDE to force the email_layout_xmlid defined on the mail.template + template = self.env["mail.template"].sudo().browse(template_id) + if template.force_email_layout_id: + email_layout_xmlid = template.force_email_layout_id.xml_id + return super().message_post_with_template( + template_id, email_layout_xmlid=email_layout_xmlid, **kwargs + ) diff --git a/mail_layout_force/readme/CONFIGURE.rst b/mail_layout_force/readme/CONFIGURE.rst new file mode 100644 index 0000000000..6e42c41e01 --- /dev/null +++ b/mail_layout_force/readme/CONFIGURE.rst @@ -0,0 +1,15 @@ +# Go to Configuration > Technical > Emails > Templates +# Open the desired ``email.template`` record. +# In Advanced Parameters tab, find the Force Layout field. + +You can leave it empty to use the default email layout (chosen by Odoo). +You can force a custom email layout of your own. +You can use the *Mail: No-Layout notification template* to prevent Odoo +from adding a layout. + +To configure a custom layout of your own, some technical knowledge is needed. +You can see how the existing layouts are defined for details or inspiration: + +* ``mail.mail_notification_light`` +* ``mail.mail_notification_paynow`` +* ``mail.mail_notification_borders`` diff --git a/mail_layout_force/readme/CONTRIBUTORS.rst b/mail_layout_force/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..59b447f28a --- /dev/null +++ b/mail_layout_force/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Camptocamp `_ + + * Iván Todorovich diff --git a/mail_layout_force/readme/DESCRIPTION.rst b/mail_layout_force/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..e00348fe71 --- /dev/null +++ b/mail_layout_force/readme/DESCRIPTION.rst @@ -0,0 +1,17 @@ +Odoo will add a default email layout on most commercial communications. + +The email layout is a ``QWeb`` view that ends up wrapping the message body +when sending an email. It usually displays the related document reference, +the company logo, and a small footer saying "Powered by Odoo". + +There are notably two main layouts used in Odoo, and the user can't control when +they're used, as it's hardcoded into the different applications. + +* ``mail.mail_notification_light`` +* ``mail.mail_notification_paynow`` + +This module allows to force a specific layout for a given ``email.template``, +effectively overwritting the one hardcoded by Odoo. + +This allows you to fully customize the way Odoo emails are rendered and sent +to your customers. diff --git a/mail_layout_force/static/description/icon.png b/mail_layout_force/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/mail_layout_force/static/description/icon.png differ diff --git a/mail_layout_force/static/description/index.html b/mail_layout_force/static/description/index.html new file mode 100644 index 0000000000..f5b962643f --- /dev/null +++ b/mail_layout_force/static/description/index.html @@ -0,0 +1,458 @@ + + + + + + +Mail Layout Force + + + +
+

Mail Layout Force

+ + +

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

+

Odoo will add a default email layout on most commercial communications.

+

The email layout is a QWeb view that ends up wrapping the message body +when sending an email. It usually displays the related document reference, +the company logo, and a small footer saying “Powered by Odoo”.

+

There are notably two main layouts used in Odoo, and the user can’t control when +they’re used, as it’s hardcoded into the different applications.

+
    +
  • mail.mail_notification_light
  • +
  • mail.mail_notification_paynow
  • +
+

This module allows to force a specific layout for a given email.template, +effectively overwritting the one hardcoded by Odoo.

+

This allows you to fully customize the way Odoo emails are rendered and sent +to your customers.

+

Table of contents

+ +
+

Configuration

+

# Go to Configuration > Technical > Emails > Templates +# Open the desired email.template record. +# In Advanced Parameters tab, find the Force Layout field.

+

You can leave it empty to use the default email layout (chosen by Odoo). +You can force a custom email layout of your own. +You can use the Mail: No-Layout notification template to prevent Odoo +from adding a layout.

+

To configure a custom layout of your own, some technical knowledge is needed. +You can see how the existing layouts are defined for details or inspiration:

+
    +
  • mail.mail_notification_light
  • +
  • mail.mail_notification_paynow
  • +
  • mail.mail_notification_borders
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

ivantodorovich

+

This module is part of the OCA/social project on GitHub.

+

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

+
+
+
+ + diff --git a/mail_layout_force/tests/__init__.py b/mail_layout_force/tests/__init__.py new file mode 100644 index 0000000000..9fd17597d3 --- /dev/null +++ b/mail_layout_force/tests/__init__.py @@ -0,0 +1 @@ +from . import test_mail_layout_force diff --git a/mail_layout_force/tests/test_mail_layout_force.py b/mail_layout_force/tests/test_mail_layout_force.py new file mode 100644 index 0000000000..8ef77222cc --- /dev/null +++ b/mail_layout_force/tests/test_mail_layout_force.py @@ -0,0 +1,73 @@ +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author Iván Todorovich +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import TransactionCase + + +class TestMailLayoutForce(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.layout_noop = cls.env.ref("mail_layout_force.mail_layout_noop") + cls.layout_test = cls.env["ir.ui.view"].create( + { + "name": "Test Layout", + "type": "qweb", + "mode": "primary", + "arch": "

", + } + ) + cls.template = cls.env["mail.template"].create( + { + "name": "Test Template", + "body_html": "

Test

", + "subject": "Test", + "model_id": cls.env.ref("base.model_res_partner").id, + "auto_delete": False, + } + ) + cls.partner = cls.env.ref("base.res_partner_10") + cls.partner.message_ids.unlink() + cls.partner.message_subscribe([cls.partner.id]) + + def test_noop_layout(self): + self.template.force_email_layout_id = self.layout_noop + self.partner.message_post_with_template( + self.template.id, + # This is ignored because the template has a force_email_layout_id + email_layout_xmlid="mail.mail_notification_light", + ) + message = self.partner.message_ids[-1] + self.assertEqual(message.mail_ids.body_html.strip(), "

Test

") + + def test_custom_layout(self): + self.template.force_email_layout_id = self.layout_test + self.partner.message_post_with_template( + self.template.id, + # This is ignored because the template has a force_email_layout_id + email_layout_xmlid="mail.mail_notification_light", + ) + message = self.partner.message_ids[-1] + self.assertEqual(message.mail_ids.body_html.strip(), "

Test

") + + def test_custom_layout_composer(self): + self.template.force_email_layout_id = self.layout_test + composer = ( + self.env["mail.compose.message"] + .with_context( + # This is ignored because the template has a force_email_layout_id + custom_layout="mail.mail_notification_light" + ) + .create( + { + "res_id": self.partner.id, + "model": self.partner._name, + "template_id": self.template.id, + } + ) + ) + composer._onchange_template_id_wrapper() + composer._action_send_mail() + message = self.partner.message_ids[-1] + self.assertEqual(message.mail_ids.body_html.strip(), "

Test

") diff --git a/mail_layout_force/views/mail_template.xml b/mail_layout_force/views/mail_template.xml new file mode 100644 index 0000000000..b0a80ffb15 --- /dev/null +++ b/mail_layout_force/views/mail_template.xml @@ -0,0 +1,19 @@ + + + + + + mail.template + + + + + + + + + diff --git a/mail_layout_force/wizards/__init__.py b/mail_layout_force/wizards/__init__.py new file mode 100644 index 0000000000..b528d997d1 --- /dev/null +++ b/mail_layout_force/wizards/__init__.py @@ -0,0 +1 @@ +from . import mail_compose_message diff --git a/mail_layout_force/wizards/mail_compose_message.py b/mail_layout_force/wizards/mail_compose_message.py new file mode 100644 index 0000000000..231203e7d5 --- /dev/null +++ b/mail_layout_force/wizards/mail_compose_message.py @@ -0,0 +1,22 @@ +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author Iván Todorovich +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class MailComposer(models.TransientModel): + _inherit = "mail.compose.message" + + def _action_send_mail(self, auto_commit=False): + # OVERRIDE to force the email_layout_xmlid defined on the mail.template + res = [] + for rec in self: + if rec.template_id.force_email_layout_id: + rec = rec.with_context( + custom_layout=self.template_id.force_email_layout_id.xml_id + ) + res.append( + super(MailComposer, rec)._action_send_mail(auto_commit=auto_commit) + ) + return all(res) diff --git a/setup/mail_layout_force/odoo/addons/mail_layout_force b/setup/mail_layout_force/odoo/addons/mail_layout_force new file mode 120000 index 0000000000..17c5a92206 --- /dev/null +++ b/setup/mail_layout_force/odoo/addons/mail_layout_force @@ -0,0 +1 @@ +../../../../mail_layout_force \ No newline at end of file diff --git a/setup/mail_layout_force/setup.py b/setup/mail_layout_force/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/mail_layout_force/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)