emails: added premailer for inline style formatting to make emails render nicer on all email clients.

This commit is contained in:
Marcin Kuzminski 2020-03-26 12:40:21 +01:00
parent ab2dd4f364
commit 6a0c148cae
6 changed files with 145 additions and 13 deletions

View file

@ -171,6 +171,17 @@ self: super: {
license = [ pkgs.lib.licenses.mit ];
};
};
"cachetools" = super.buildPythonPackage {
name = "cachetools-3.1.1";
doCheck = false;
src = fetchurl {
url = "https://files.pythonhosted.org/packages/ae/37/7fd45996b19200e0cb2027a0b6bef4636951c4ea111bfad36c71287247f6/cachetools-3.1.1.tar.gz";
sha256 = "16m69l6n6y1r1y7cklm92rr7v69ldig2n3lbl3j323w5jz7d78lf";
};
meta = {
license = [ pkgs.lib.licenses.mit ];
};
};
"celery" = super.buildPythonPackage {
name = "celery-4.3.0";
doCheck = false;
@ -372,6 +383,17 @@ self: super: {
license = [ pkgs.lib.licenses.bsdOriginal ];
};
};
"cssutils" = super.buildPythonPackage {
name = "cssutils-1.0.2";
doCheck = false;
src = fetchurl {
url = "https://files.pythonhosted.org/packages/5c/0b/c5f29d29c037e97043770b5e7c740b6252993e4b57f029b3cd03c78ddfec/cssutils-1.0.2.tar.gz";
sha256 = "1bxchrbqzapwijap0yhlxdil1w9bmwvgx77aizlkhc2mcxjg1z52";
};
meta = {
license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL 2.1 or later, see also http://cthedot.de/cssutils/"; } ];
};
};
"decorator" = super.buildPythonPackage {
name = "decorator-4.1.2";
doCheck = false;
@ -1237,6 +1259,24 @@ self: super: {
license = [ pkgs.lib.licenses.mit ];
};
};
"premailer" = super.buildPythonPackage {
name = "premailer-3.6.1";
doCheck = false;
propagatedBuildInputs = [
self."lxml"
self."cssselect"
self."cssutils"
self."requests"
self."cachetools"
];
src = fetchurl {
url = "https://files.pythonhosted.org/packages/62/da/2f43cdf9d3d79c80c4856a12389a1f257d65fe9ccc44bc6b4383c8a18e33/premailer-3.6.1.tar.gz";
sha256 = "08pshx7a110k4ll20x0xhpvyn3kkipkrbgxjjn7ncdxs54ihdhgw";
};
meta = {
license = [ pkgs.lib.licenses.psfl { fullName = "Python"; } ];
};
};
"prompt-toolkit" = super.buildPythonPackage {
name = "prompt-toolkit-1.0.18";
doCheck = false;
@ -1833,6 +1873,7 @@ self: super: {
self."pastedeploy"
self."pastescript"
self."peppercorn"
self."premailer"
self."psutil"
self."py-bcrypt"
self."pycurl"

View file

@ -36,6 +36,7 @@ paste==3.4.0
pastedeploy==2.1.0
pastescript==3.2.0
peppercorn==0.6
premailer==3.6.1
psutil==5.7.0
py-bcrypt==0.4
pycurl==7.43.0.3

View file

@ -49,6 +49,7 @@ def send_email(recipients, subject, body='', html_body='', email_config=None):
:param subject: subject of the mail
:param body: body of the mail
:param html_body: html version of body
:param email_config: specify custom configuration for mailer
"""
log = get_logger(send_email)

View file

@ -26,6 +26,7 @@ Model for notifications
import logging
import traceback
import premailer
from pyramid.threadlocal import get_current_request
from sqlalchemy.sql.expression import false, true
@ -328,6 +329,12 @@ class EmailNotificationModel(BaseModel):
'rhodecode:templates/email_templates/pull_request_update.mako',
}
premailer_instance = premailer.Premailer(
cssutils_logging_level=logging.DEBUG,
cssutils_logging_handler=logging.getLogger().handlers[0]
if logging.getLogger().handlers else None,
)
def __init__(self):
"""
Example usage::
@ -391,4 +398,11 @@ class EmailNotificationModel(BaseModel):
# render WHOLE template
body = email_template.render(None, **_kwargs)
try:
# Inline CSS styles and conversion
body = self.premailer_instance.transform(body)
except Exception:
log.exception('Failed to parse body with premailer')
pass
return subject, headers, body, body_plaintext

View file

@ -594,9 +594,21 @@ div.annotatediv { margin-left: 2px; margin-right: 4px; }
.CodeMirror ::-moz-selection { background: @rchighlightblue; }
.code { display: block; border:0px !important; }
.code-highlight, /* TODO: dan: merge codehilite into code-highlight */
/* This can be generated with `pygmentize -S default -f html` */
.codehilite {
/*ElasticMatch is custom RhodeCode TAG*/
.c-ElasticMatch {
background-color: #faffa6;
padding: 0.2em;
}
}
/* This can be generated with `pygmentize -S default -f html` */
.code-highlight,
.codehilite {
/*ElasticMatch is custom RhodeCode TAG*/
.c-ElasticMatch { background-color: #faffa6; padding: 0.2em;}
.hll { background-color: #ffffcc }
.c { color: #408080; font-style: italic } /* Comment */

View file

@ -484,21 +484,84 @@ ${self.plaintext_footer()}
background-color: #F5F5F5
}
/*elasticmatch is custom rhodecode tag*/
.codehilite .c-ElasticMatch {
background-color: #faffa6;
padding: 0.2em;
}
.codehilite .c-ElasticMatch { background-color: #faffa6; padding: 0.2em;}
.codehilite .hll { background-color: #ffffcc }
.codehilite .c { color: #408080; font-style: italic } /* Comment */
.codehilite .err { border: none } /* Error */
.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
.codehilite .o { color: #666666 } /* Operator */
.codehilite .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
.codehilite .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
.codehilite .ge { font-style: italic } /* Generic.Emph */
.codehilite .gr { color: #FF0000 } /* Generic.Error */
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.codehilite .gi { color: #00A000 } /* Generic.Inserted */
.codehilite .go { color: #888888 } /* Generic.Output */
.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.codehilite .gs { font-weight: bold } /* Generic.Strong */
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.codehilite .gt { color: #0044DD } /* Generic.Traceback */
.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.codehilite .kt { color: #B00040 } /* Keyword.Type */
.codehilite .m { color: #666666 } /* Literal.Number */
.codehilite .s { color: #BA2121 } /* Literal.String */
.codehilite .na { color: #7D9029 } /* Name.Attribute */
.codehilite .nb { color: #008000 } /* Name.Builtin */
.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
.codehilite .no { color: #880000 } /* Name.Constant */
.codehilite .nd { color: #AA22FF } /* Name.Decorator */
.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.codehilite .nf { color: #0000FF } /* Name.Function */
.codehilite .nl { color: #A0A000 } /* Name.Label */
.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
.codehilite .nv { color: #19177C } /* Name.Variable */
.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
.codehilite .mb { color: #666666 } /* Literal.Number.Bin */
.codehilite .mf { color: #666666 } /* Literal.Number.Float */
.codehilite .mh { color: #666666 } /* Literal.Number.Hex */
.codehilite .mi { color: #666666 } /* Literal.Number.Integer */
.codehilite .mo { color: #666666 } /* Literal.Number.Oct */
.codehilite .sa { color: #BA2121 } /* Literal.String.Affix */
.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
.codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */
.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.codehilite .sx { color: #008000 } /* Literal.String.Other */
.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
.codehilite .fm { color: #0000FF } /* Name.Function.Magic */
.codehilite .vc { color: #19177C } /* Name.Variable.Class */
.codehilite .vg { color: #19177C } /* Name.Variable.Global */
.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
.codehilite .vm { color: #19177C } /* Name.Variable.Magic */
.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */
</style>
<!-- Targeting Windows Mobile -->
<!--[if IEMobile 7]>
<style type="text/css">
</style>
<![endif]-->
<!--[if gte mso 9]>
<style>
/* Target Outlook 2007 and 2010 */
</style>
<![endif]-->
</head>
<body>