Browse Source

[IMP] mail_inline_css: Improve and add test cov

pull/326/head
Timon Tschanz 5 years ago
committed by Patrick Tombez
parent
commit
8edf8e6e84
  1. 121
      mail_inline_css/README.rst
  2. 15
      mail_inline_css/__manifest__.py
  3. 10
      mail_inline_css/demo/demo_mail_template.xml
  4. 84
      mail_inline_css/demo/demo_template.xml
  5. 2
      mail_inline_css/models/__init__.py
  6. 22
      mail_inline_css/models/mail.py
  7. 28
      mail_inline_css/models/mail_template.py
  8. 4
      mail_inline_css/readme/CONTRIBUTORS.rst
  9. 4
      mail_inline_css/readme/DESCRIPTION.rst
  10. 425
      mail_inline_css/static/description/index.html
  11. 1
      mail_inline_css/tests/__init__.py
  12. 62
      mail_inline_css/tests/test_mail_inline_styles.py

121
mail_inline_css/README.rst

@ -1,109 +1,80 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl
:alt: License: AGPL-3
===============
Mail Inline CSS
===============
When you send HTML emails you can't use style tags but instead you have
to put inline ``style`` attributes on every element. So from this:
.. code:: html
<html>
<style type="text/css">
h1 { border:1px solid black }
p { color:red;}
</style>
<h1 style="font-weight:bolder">Peter</h1>
<p>Hej</p>
</html>
You want this:
.. code:: html
<html>
<h1 style="font-weight:bolder; border:1px solid black">Peter</h1>
<p style="color:red">Hej</p>
</html>
This module use premailer library to do this.
It parses an HTML page, looks up ``style`` blocks
and parses the CSS. It then uses the ``lxml.html`` parser to modify the
DOM tree of the page accordingly.
Installation
============
To install this module, you need first to install `premailer` python library with:
.. code:: bash
pip install premailer
Usage
=====
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Just use any mail template as Odoo standard feature
.. |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/11.0/mail_inline_css
:alt: OCA/social
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/social-11-0/social-11-0-mail_inline_css
: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/11.0
:alt: Try me on Runbot
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/205/11
|badge1| |badge2| |badge3| |badge4| |badge5|
Convert styles to inline styles and make them play nice with emails.
Note:
Odoo with module web_editor already implements this feature on the client side (js).
This module brings this server side feature for cases without js part. It could the more stable way over the Odoo versions with a stable api in a dedicated library
with adhoc python unit tests.
This module sole usage is to provide the same parsing functionality as in
the web editor but for the templates imported directly in database.
**Table of contents**
.. contents::
:local:
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/social/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Bugs are tracked on `GitHub Issues <https://github.com/OCA/social/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 <https://github.com/OCA/social/issues/new?body=module:%20mail_inline_css%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Images
------
Authors
~~~~~~~
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
* Akretion
* camptocamp
Contributors
------------
~~~~~~~~~~~~
* David BEAL <david.beal@akretion.com>
* Akim Juillerat <akim.juillerat@camptocamp.com>
* Simone Orsi <simone.orsi@camptocamp.com>
* Patrick Tombez <patrick.tombez@camptocamp.com>
Do not contact contributors directly about support or help with technical issues.
Funders
-------
The development of this module has been financially supported by:
Maintainers
~~~~~~~~~~~
* Akretion
Maintainer
----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
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.
To contribute to this module, please visit https://odoo-community.org.
This module is part of the `OCA/social <https://github.com/OCA/social/tree/11.0/mail_inline_css>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

15
mail_inline_css/__manifest__.py

@ -1,17 +1,26 @@
# Copyright 2017 David BEAL @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Mail Inline CSS',
"summary": "Convert style tags in inline style in your mails",
'version': '11.0.1.0.0',
'author': 'Akretion, Odoo Community Association (OCA)',
'author': 'Akretion, camptocamp, Odoo Community Association (OCA)',
'website': 'https://github.com/OCA/social',
'license': 'AGPL-3',
'category': 'Tools',
'depends': ['mail'],
'installable': True,
"external_dependencies": {
"python": ['premailer'],
},
"depends": [
"email_template_qweb",
],
"data": [
],
"demo": [
"demo/demo_template.xml",
"demo/demo_mail_template.xml",
],
}

10
mail_inline_css/demo/demo_mail_template.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="0">
<record id="email_template_demo" model="mail.template">
<field name="name">Inline styles demo</field>
<field name="body_type">qweb</field>
<field name="body_view_id" ref="demo_email_hello" />
<field name="model_id" ref="base.model_res_users" />
<field name="subject">Demo email inline styles</field>
</record>
</odoo>

84
mail_inline_css/demo/demo_template.xml

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="0">
<template id="demo_email_hello" name="demo email hello">
<html>
<body>
<!-- TODO: move to LESS file
If we put the link as it is `premailer` won't be able to render it.
To ease this, we could:
1. find the less file on FS
2. compile to less
3. populate style block
-->
<style>
#main_wrapper {
max-width: 620px;
margin: 0 auto;
border: 1px solid #ccc;
font-size: 18px;
font-family: verdana;
color: #6B6E71;
}
a { color: #3BA0A6; text-decoration: none }
#main_header, footer, main > div { padding: 30px 40px }
#main_header table { width: 100% }
#main_header table td { width: 50% }
#main_logo { max-width: 300px }
#main_header .right {
text-align: right;
vertical-align: top;
font-size: 140%;
}
#main_header .date_today { text-transform: uppercase; opacity: 0.7; color: #FF0000 }
footer { padding-top: 0; font-size: 120% }
footer address { font-style: normal }
.greeting { padding-top: 0; padding-bottom: 0 }
.image-wrapper {
min-height: 250px;
}
.image-wrapper.location-map {
margin: 0 -40px;
}
.pt0 { padding-top: 0 }
a.contact { cursor: pointer }
</style>
<div id="main_wrapper">
<div id="main_header">
<table>
<tbody>
<tr>
<td class="left">
<img id="main_logo" t-attf-src="data:image;base64,{{env.user.company_id.logo}}" />
</td>
<td class="right">
<span class="date_today" t-esc="time.strftime('%%d %%B %%Y')" />
</td>
</tr>
</tbody>
</table>
</div>
<main id="main_content">
<div class="greeting">
<p>Hello <span t-field="object.name" />.</p>
</div>
<div id="content">
<p>This e-mail styles are inline rendered although its template defines styles as embedded CSS!</p>
</div>
</main>
<footer id="main_footer">
<div class="company_info">
<p class="website"><a href="https://www.example.com">www.example.com</a></p>
<div class="address">
<div t-field="env.user.company_id.partner_id"
t-options='{
"widget": "contact",
"fields": ["name", "address", "phone", "mobile", "email"]
}'/>
</div>
</div>
</footer>
</div>
</body>
</html>
</template>
</odoo>

2
mail_inline_css/models/__init__.py

@ -1 +1 @@
from . import mail
from . import mail_template

22
mail_inline_css/models/mail.py

@ -1,22 +0,0 @@
# Copyright 2017 David BEAL @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from odoo import models
_logger = logging.getLogger(__name__)
try:
from premailer import transform
except (ImportError, IOError) as err:
_logger.debug(err)
class MailTemplate(models.Model):
_inherit = 'mail.template'
def generate_email(self, res_ids, fields=None):
res = super().generate_email(res_ids, fields=fields)
if 'body_html' in res:
res['body_html'] = transform(res['body_html'])
return res

28
mail_inline_css/models/mail_template.py

@ -0,0 +1,28 @@
# Copyright 2017 David BEAL @ Akretion
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, models
try:
from premailer import transform
except (ImportError, IOError) as err:
import logging
_logger = logging.getLogger(__name__)
_logger.debug(err)
class MailTemplate(models.Model):
_inherit = 'mail.template'
@api.multi
def generate_email(self, res_ids, fields=None):
"""Use `premailer` to convert styles to inline styles."""
result = super().generate_email(res_ids, fields=fields)
if isinstance(res_ids, int):
result['body_html'] = transform(result['body_html'])
else:
for __, data in result.items():
data['body_html'] = transform(data['body_html'])
return result

4
mail_inline_css/readme/CONTRIBUTORS.rst

@ -0,0 +1,4 @@
* David BEAL <david.beal@akretion.com>
* Akim Juillerat <akim.juillerat@camptocamp.com>
* Simone Orsi <simone.orsi@camptocamp.com>
* Patrick Tombez <patrick.tombez@camptocamp.com>

4
mail_inline_css/readme/DESCRIPTION.rst

@ -0,0 +1,4 @@
Convert styles to inline styles and make them play nice with emails.
This module sole usage is to provide the same parsing functionality as in
the web editor but for the templates imported directly in database.

425
mail_inline_css/static/description/index.html

@ -0,0 +1,425 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" />
<title>Mail Inline CSS</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="mail-inline-css">
<h1 class="title">Mail Inline CSS</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/social/tree/11.0/mail_inline_css"><img alt="OCA/social" src="https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/social-11-0/social-11-0-mail_inline_css"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/205/11.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>Convert styles to inline styles and make them play nice with emails.</p>
<p>This module sole usage is to provide the same parsing functionality as in
the web editor but for the templates imported directly in database.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#bug-tracker" id="id1">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id2">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id3">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id4">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id5">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id1">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/social/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/social/issues/new?body=module:%20mail_inline_css%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id2">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id3">Authors</a></h2>
<ul class="simple">
<li>Akretion</li>
<li>camptocamp</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id4">Contributors</a></h2>
<ul class="simple">
<li>David BEAL &lt;<a class="reference external" href="mailto:david.beal&#64;akretion.com">david.beal&#64;akretion.com</a>&gt;</li>
<li>Akim Juillerat &lt;<a class="reference external" href="mailto:akim.juillerat&#64;camptocamp.com">akim.juillerat&#64;camptocamp.com</a>&gt;</li>
<li>Simone Orsi &lt;<a class="reference external" href="mailto:simone.orsi&#64;camptocamp.com">simone.orsi&#64;camptocamp.com</a>&gt;</li>
<li>Patrick Tombez &lt;<a class="reference external" href="mailto:patrick.tombez&#64;camptocamp.com">patrick.tombez&#64;camptocamp.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id5">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/social/tree/11.0/mail_inline_css">OCA/social</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

1
mail_inline_css/tests/__init__.py

@ -0,0 +1 @@
from . import test_mail_inline_styles

62
mail_inline_css/tests/test_mail_inline_styles.py

@ -0,0 +1,62 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from lxml import html
from odoo.tests import SavepointCase
class TestMailInlineStyles(SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.mail_template = cls.env.ref(
'mail_inline_css.email_template_demo')
cls.demo_user = cls.env.ref('base.user_demo')
def to_xml_node(self, html_):
return html.fragments_fromstring(html_)
def parse_node_style(self, node):
""" Convert node CSS string to Python dict"""
res = {}
for style in node.attrib.get('style', '').split(';'):
l = style.split(':')
res[l[0].strip()] = l[1].strip()
return res
def find_by_id(self, node, html_id):
return node.xpath('//*[@id="{}"]'.format(html_id))
def assertNodeStyle(self, node, expected):
self.assertIn('style', node.attrib)
self.assertEqual(self.parse_node_style(node), expected)
def test_generate_mail(self):
res = self.mail_template.generate_email(
[self.demo_user.id], fields=['body_html']
)
body_html_string = res[self.demo_user.id].get('body_html')
html_node = self.to_xml_node(body_html_string)[0]
expected = {
'main_logo': {
'max-width': '300px'
},
'main_wrapper': {
'max-width': '620px',
'margin': '0 auto',
'border': '1px solid #ccc',
'font-size': '18px',
'font-family': 'verdana',
'color': '#6B6E71'
},
'main_footer': {
'padding-top': '0',
'font-size': '120%',
'padding': '30px 40px'
}
}
for html_id, expected_style in expected.items():
node = self.find_by_id(html_node, html_id)[0]
self.assertNodeStyle(node, expected_style)
Loading…
Cancel
Save