diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix --- a/pkgs/python-packages.nix +++ b/pkgs/python-packages.nix @@ -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" diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/rhodecode/lib/celerylib/tasks.py b/rhodecode/lib/celerylib/tasks.py --- a/rhodecode/lib/celerylib/tasks.py +++ b/rhodecode/lib/celerylib/tasks.py @@ -49,6 +49,7 @@ def send_email(recipients, subject, body :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) diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py --- a/rhodecode/model/notification.py +++ b/rhodecode/model/notification.py @@ -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 diff --git a/rhodecode/public/css/code-block.less b/rhodecode/public/css/code-block.less --- a/rhodecode/public/css/code-block.less +++ b/rhodecode/public/css/code-block.less @@ -594,9 +594,21 @@ div.annotatediv { margin-left: 2px; marg .CodeMirror ::-moz-selection { background: @rchighlightblue; } .code { display: block; border:0px !important; } + .code-highlight, /* TODO: dan: merge codehilite into code-highlight */ +.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 */ diff --git a/rhodecode/templates/email_templates/base.mako b/rhodecode/templates/email_templates/base.mako --- a/rhodecode/templates/email_templates/base.mako +++ b/rhodecode/templates/email_templates/base.mako @@ -484,21 +484,84 @@ text_monospace = "'Menlo', 'Liberation M 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 */ - - - -