mirror of https://github.com/OCA/social.git
[IMP] mail_layout_force
This commit improves the process of replacing the layout when sending an email without selecting an email template.pull/1620/head
parent
f5c4912a5c
commit
36cf86810b
|
@ -37,11 +37,14 @@ 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.message_notification_email``
|
||||
* ``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.
|
||||
effectively overwritting the one hardcoded by Odoo. Additionally, it enables
|
||||
forcing a custom layout for emails that do not use an existing ``email.template``
|
||||
record (e.g., when sending an email from the chatter).
|
||||
|
||||
This allows you to fully customize the way Odoo emails are rendered and sent
|
||||
to your customers.
|
||||
|
@ -54,9 +57,9 @@ to your customers.
|
|||
Configuration
|
||||
=============
|
||||
|
||||
# Go to Configuration > Technical > Emails > Templates
|
||||
# Open the desired ``email.template`` record.
|
||||
# In Advanced Parameters tab, find the Force Layout field.
|
||||
#. Go to Settings > 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.
|
||||
|
@ -70,6 +73,16 @@ You can see how the existing layouts are defined for details or inspiration:
|
|||
* ``mail.mail_notification_paynow``
|
||||
* ``mail.mail_notification_borders``
|
||||
|
||||
To force a new custom layout for emails that do not use an existing ``email.template``
|
||||
record (e.g., emails sent from the chatter):
|
||||
|
||||
#. Go to Settings > Technical > User Interface > Views.
|
||||
#. Copy the current layout (e.g., mail.message_notification_email) to create a new one, and remove any parts you don’t need.
|
||||
#. Open the layout that you want to swap with a substitute. Then, under the Layout Mapping tab:
|
||||
* Set ``Substitute Layout`` to the new custom layout you created.
|
||||
* Set ``Models`` if you want to apply the replacement only to specific models. If left empty,
|
||||
the email layout will be replaced for all models.
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
|
@ -95,6 +108,10 @@ Contributors
|
|||
|
||||
* Iván Todorovich <ivan.todorovich@camptocamp.com>
|
||||
* Abraham Anes <abrahamanes@gmail.com>
|
||||
* `Quartile <https://www.quartile.co>`_
|
||||
|
||||
* Aung Ko Ko Lin
|
||||
* Yoshi Tashiro
|
||||
|
||||
Maintainers
|
||||
~~~~~~~~~~~
|
||||
|
|
|
@ -13,5 +13,10 @@
|
|||
"category": "Marketing",
|
||||
"depends": ["mail"],
|
||||
"demo": ["demo/mail_layout.xml"],
|
||||
"data": ["data/mail_layout.xml", "views/mail_template.xml"],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"data/mail_layout.xml",
|
||||
"views/ir_ui_views.xml",
|
||||
"views/mail_template.xml",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
from . import email_layout_mapping
|
||||
from . import ir_ui_view
|
||||
from . import mail_template
|
||||
from . import mail_thread
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Copyright 2025 Quartile (https://www.quartile.co)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class EmailLayoutMapping(models.Model):
|
||||
_name = "email.layout.mapping"
|
||||
_description = "Email Layout Mapping"
|
||||
|
||||
layout_id = fields.Many2one("ir.ui.view", ondelete="cascade")
|
||||
substitute_layout_id = fields.Many2one(
|
||||
"ir.ui.view",
|
||||
domain=[("type", "=", "qweb")],
|
||||
required=True,
|
||||
help="Select a target layout.",
|
||||
)
|
||||
model_ids = fields.Many2many(
|
||||
"ir.model", string="Models", help="Select models that the swapping applies to."
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
# Copyright 2025 Quartile (https://www.quartile.co)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class IrUiView(models.Model):
|
||||
_inherit = "ir.ui.view"
|
||||
|
||||
layout_mapping_line_ids = fields.One2many("email.layout.mapping", "layout_id")
|
|
@ -18,3 +18,58 @@ class MailThread(models.AbstractModel):
|
|||
return super().message_post_with_template(
|
||||
template_id, email_layout_xmlid=email_layout_xmlid, **kwargs
|
||||
)
|
||||
|
||||
def _notify_thread_by_email(
|
||||
self,
|
||||
message,
|
||||
recipients_data,
|
||||
msg_vals=False,
|
||||
mail_auto_delete=True,
|
||||
model_description=False,
|
||||
force_email_company=False,
|
||||
force_email_lang=False,
|
||||
resend_existing=False,
|
||||
force_send=True,
|
||||
send_after_commit=True,
|
||||
subtitles=None,
|
||||
**kwargs
|
||||
):
|
||||
msg_vals = msg_vals or {}
|
||||
layout_xmlid = (
|
||||
msg_vals.get("email_layout_xmlid")
|
||||
or message.email_layout_xmlid
|
||||
or "mail.mail_notification_layout"
|
||||
)
|
||||
layout = self.env.ref(layout_xmlid, raise_if_not_found=True)
|
||||
res_model = (
|
||||
self.env["ir.model"].sudo().search([("model", "=", self._name)], limit=1)
|
||||
)
|
||||
mapping = self.env["email.layout.mapping"].search(
|
||||
[("layout_id", "=", layout.id), ("model_ids", "in", res_model.ids)],
|
||||
limit=1,
|
||||
)
|
||||
if not mapping:
|
||||
mapping = self.env["email.layout.mapping"].search(
|
||||
[("layout_id", "=", layout.id), ("model_ids", "=", False)], limit=1
|
||||
)
|
||||
if mapping:
|
||||
substitute_layout = mapping.substitute_layout_id
|
||||
if not substitute_layout.xml_id:
|
||||
substitute_layout._export_rows([["id"]])
|
||||
# Refresh cache to get xml_id assigned by _export_rows
|
||||
substitute_layout.invalidate_recordset()
|
||||
msg_vals["email_layout_xmlid"] = mapping.substitute_layout_id.xml_id
|
||||
return super()._notify_thread_by_email(
|
||||
message,
|
||||
recipients_data,
|
||||
msg_vals=msg_vals,
|
||||
mail_auto_delete=mail_auto_delete,
|
||||
model_description=model_description,
|
||||
force_email_company=force_email_company,
|
||||
force_email_lang=force_email_lang,
|
||||
resend_existing=resend_existing,
|
||||
force_send=force_send,
|
||||
send_after_commit=send_after_commit,
|
||||
subtitles=subtitles,
|
||||
**kwargs
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Go to Configuration > Technical > Emails > Templates
|
||||
# Open the desired ``email.template`` record.
|
||||
# In Advanced Parameters tab, find the Force Layout field.
|
||||
#. Go to Settings > 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.
|
||||
|
@ -13,3 +13,13 @@ 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``
|
||||
|
||||
To force a new custom layout for emails that do not use an existing ``email.template``
|
||||
record (e.g., emails sent from the chatter):
|
||||
|
||||
#. Go to Settings > Technical > User Interface > Views.
|
||||
#. Copy the current layout (e.g., mail.message_notification_email) to create a new one, and remove any parts you don’t need.
|
||||
#. Open the layout that you want to swap with a substitute. Then, under the Layout Mapping tab:
|
||||
* Set ``Substitute Layout`` to the new custom layout you created.
|
||||
* Set ``Models`` if you want to apply the replacement only to specific models. If left empty,
|
||||
the email layout will be replaced for all models.
|
||||
|
|
|
@ -2,3 +2,7 @@
|
|||
|
||||
* Iván Todorovich <ivan.todorovich@camptocamp.com>
|
||||
* Abraham Anes <abrahamanes@gmail.com>
|
||||
* `Quartile <https://www.quartile.co>`_
|
||||
|
||||
* Aung Ko Ko Lin
|
||||
* Yoshi Tashiro
|
||||
|
|
|
@ -7,11 +7,14 @@ 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.message_notification_email``
|
||||
* ``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.
|
||||
effectively overwritting the one hardcoded by Odoo. Additionally, it enables
|
||||
forcing a custom layout for emails that do not use an existing ``email.template``
|
||||
record (e.g., when sending an email from the chatter).
|
||||
|
||||
This allows you to fully customize the way Odoo emails are rendered and sent
|
||||
to your customers.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_email_layout_mapping_all,email.layout.mapping.all,model_email_layout_mapping,,1,0,0,0
|
||||
access_email_layout_mapping_admin,email.layout.mapping.admin,model_email_layout_mapping,base.group_system,1,1,1,1
|
|
|
@ -376,11 +376,14 @@ the company logo, and a small footer saying “Powered by Odoo”.</p>
|
|||
<p>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.</p>
|
||||
<ul class="simple">
|
||||
<li><tt class="docutils literal">mail.message_notification_email</tt></li>
|
||||
<li><tt class="docutils literal">mail.mail_notification_light</tt></li>
|
||||
<li><tt class="docutils literal">mail.mail_notification_paynow</tt></li>
|
||||
</ul>
|
||||
<p>This module allows to force a specific layout for a given <tt class="docutils literal">email.template</tt>,
|
||||
effectively overwritting the one hardcoded by Odoo.</p>
|
||||
effectively overwritting the one hardcoded by Odoo. Additionally, it enables
|
||||
forcing a custom layout for emails that do not use an existing <tt class="docutils literal">email.template</tt>
|
||||
record (e.g., when sending an email from the chatter).</p>
|
||||
<p>This allows you to fully customize the way Odoo emails are rendered and sent
|
||||
to your customers.</p>
|
||||
<p><strong>Table of contents</strong></p>
|
||||
|
@ -398,9 +401,11 @@ to your customers.</p>
|
|||
</div>
|
||||
<div class="section" id="configuration">
|
||||
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
|
||||
<p># Go to Configuration > Technical > Emails > Templates
|
||||
# Open the desired <tt class="docutils literal">email.template</tt> record.
|
||||
# In Advanced Parameters tab, find the Force Layout field.</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to Settings > Technical > Emails > Templates</li>
|
||||
<li>Open the desired <tt class="docutils literal">email.template</tt> record.</li>
|
||||
<li>In Advanced Parameters tab, find the Force Layout field.</li>
|
||||
</ol>
|
||||
<p>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 <em>Mail: No-Layout notification template</em> to prevent Odoo
|
||||
|
@ -412,6 +417,22 @@ You can see how the existing layouts are defined for details or inspiration:</p>
|
|||
<li><tt class="docutils literal">mail.mail_notification_paynow</tt></li>
|
||||
<li><tt class="docutils literal">mail.mail_notification_borders</tt></li>
|
||||
</ul>
|
||||
<p>To force a new custom layout for emails that do not use an existing <tt class="docutils literal">email.template</tt>
|
||||
record (e.g., emails sent from the chatter):</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to Settings > Technical > User Interface > Views.</li>
|
||||
<li>Copy the current layout (e.g., mail.message_notification_email) to create a new one, and remove any parts you don’t need.</li>
|
||||
<li><dl class="first docutils">
|
||||
<dt>Open the layout that you want to swap with a substitute. Then, under the Layout Mapping tab:</dt>
|
||||
<dd><ul class="first last">
|
||||
<li>Set <tt class="docutils literal">Substitute Layout</tt> to the new custom layout you created.</li>
|
||||
<li>Set <tt class="docutils literal">Models</tt> if you want to apply the replacement only to specific models. If left empty,
|
||||
the email layout will be replaced for all models.</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
|
||||
|
@ -441,6 +462,12 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
|
|||
</li>
|
||||
<li><p class="first">Abraham Anes <<a class="reference external" href="mailto:abrahamanes@gmail.com">abrahamanes@gmail.com</a>></p>
|
||||
</li>
|
||||
<li><p class="first"><a class="reference external" href="https://www.quartile.co">Quartile</a></p>
|
||||
<ul class="simple">
|
||||
<li>Aung Ko Ko Lin</li>
|
||||
<li>Yoshi Tashiro</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# @author Iván Todorovich <ivan.todorovich@camptocamp.com>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests import TransactionCase
|
||||
|
||||
|
||||
|
@ -18,6 +19,20 @@ class TestMailLayoutForce(TransactionCase):
|
|||
"arch": "<t t-name='test'><h1></h1><t t-out='message.body'/></t>",
|
||||
}
|
||||
)
|
||||
cls.mail_notification_layout = cls.env.ref("mail.mail_notification_layout")
|
||||
cls.layout_substitute = cls.env["ir.ui.view"].create(
|
||||
{
|
||||
"name": "Substitute Layout",
|
||||
"type": "qweb",
|
||||
"mode": "primary",
|
||||
"arch": """<?xml version="1.0"?>
|
||||
<t t-name="custom_mail_notification_layout">
|
||||
<div t-out="message.body"/>
|
||||
<div t-if="signature" t-out="signature" style="font-size: 13px;"/>
|
||||
<h1>Substituted</h1>
|
||||
</t>""",
|
||||
}
|
||||
)
|
||||
cls.template = cls.env["mail.template"].create(
|
||||
{
|
||||
"name": "Test Template",
|
||||
|
@ -71,3 +86,32 @@ class TestMailLayoutForce(TransactionCase):
|
|||
composer._action_send_mail()
|
||||
message = self.partner.message_ids[-1]
|
||||
self.assertEqual(message.mail_ids.body_html.strip(), "<h1></h1><p>Test</p>")
|
||||
|
||||
def test_chatter_message_uses_default_layout(self):
|
||||
self.partner.message_post(
|
||||
body="Test Message",
|
||||
email_layout_xmlid=self.mail_notification_layout.xml_id,
|
||||
message_type="comment",
|
||||
subtype_xmlid="mail.mt_comment",
|
||||
mail_auto_delete=False,
|
||||
force_send=True,
|
||||
)
|
||||
message = self.partner.message_ids[-1]
|
||||
self.assertNotIn("<h1>Substituted</h1>", message.mail_ids.body_html)
|
||||
self.assertIn("Test Message", message.mail_ids.body_html)
|
||||
|
||||
def test_chatter_message_uses_substituted_layout(self):
|
||||
self.mail_notification_layout.layout_mapping_line_ids = [
|
||||
Command.create({"substitute_layout_id": self.layout_substitute.id})
|
||||
]
|
||||
self.partner.message_post(
|
||||
body="Test Message",
|
||||
email_layout_xmlid=self.mail_notification_layout.xml_id,
|
||||
message_type="comment",
|
||||
subtype_xmlid="mail.mt_comment",
|
||||
mail_auto_delete=False,
|
||||
force_send=True,
|
||||
)
|
||||
message = self.partner.message_ids[-1]
|
||||
self.assertIn("<h1>Substituted</h1>", message.mail_ids.body_html)
|
||||
self.assertIn("Test Message", message.mail_ids.body_html)
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="view_ir_ui_view_form_inherit" model="ir.ui.view">
|
||||
<field name="name">ir.ui.view.form.inherit</field>
|
||||
<field name="model">ir.ui.view</field>
|
||||
<field name="inherit_id" ref="base.view_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='inherit_children']" position="after">
|
||||
<page
|
||||
name="layout_mapping"
|
||||
string="Layout Mapping"
|
||||
attrs="{'invisible': [('type', '!=', 'qweb')]}"
|
||||
>
|
||||
<field name="layout_mapping_line_ids" nolabel="1">
|
||||
<tree editable="bottom">
|
||||
<field name="substitute_layout_id" />
|
||||
<field name="model_ids" widget="many2many_tags" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
Loading…
Reference in New Issue