Browse Source

Email header (#3748)

* Fix fonts for emails

* Email header with indicator

* Login emails with header

* Add tests for email header

* fix codacy
version-14
Faris Ansari 8 years ago
committed by Rushabh Mehta
parent
commit
5a498616b6
11 changed files with 126 additions and 34 deletions
  1. +1
    -1
      frappe/__init__.py
  2. +1
    -1
      frappe/core/doctype/user/user.py
  3. +19
    -14
      frappe/email/email_body.py
  4. +1
    -1
      frappe/email/queue.py
  5. +30
    -1
      frappe/email/test_email_body.py
  6. +32
    -4
      frappe/public/css/email.css
  7. +36
    -4
      frappe/public/less/email.less
  8. +6
    -4
      frappe/templates/emails/email_header.html
  9. +0
    -1
      frappe/templates/emails/new_user.html
  10. +0
    -2
      frappe/templates/emails/password_reset.html
  11. +0
    -1
      frappe/templates/emails/password_update.html

+ 1
- 1
frappe/__init__.py View File

@@ -380,7 +380,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message
attachments=None, content=None, doctype=None, name=None, reply_to=None,
cc=[], message_id=None, in_reply_to=None, send_after=None, expose_recipients=None,
send_priority=1, communication=None, retry=1, now=None, read_receipt=None, is_notification=False,
inline_images=None, template=None, args=None, header=False):
inline_images=None, template=None, args=None, header=None):
"""Send email using user's default **Email Account** or global default **Email Account**.




+ 1
- 1
frappe/core/doctype/user/user.py View File

@@ -279,7 +279,7 @@ class User(Document):
sender = frappe.session.user not in STANDARD_USERS and get_formatted_email(frappe.session.user) or None

frappe.sendmail(recipients=self.email, sender=sender, subject=subject,
template=template, args=args,
template=template, args=args, header=[subject, "green"],
delayed=(not now) if now!=None else self.flags.delay_emails, retry=3)

def a_system_manager_should_exist(self):


+ 19
- 14
frappe/email/email_body.py View File

@@ -15,7 +15,7 @@ from email.mime.multipart import MIMEMultipart
def get_email(recipients, sender='', msg='', subject='[No Subject]',
text_content = None, footer=None, print_html=None, formatted=None, attachments=None,
content=None, reply_to=None, cc=[], email_account=None, expose_recipients=None,
inline_images=[], header=False):
inline_images=[], header=None):
""" Prepare an email with the following format:
- multipart/mixed
- multipart/alternative
@@ -76,7 +76,7 @@ class EMail:
self.email_account = email_account or get_outgoing_email_account()

def set_html(self, message, text_content = None, footer=None, print_html=None,
formatted=None, inline_images=None, header=False):
formatted=None, inline_images=None, header=None):
"""Attach message in the html portion of multipart/alternative"""
if not formatted:
formatted = get_formatted_html(self.subject, message, footer, print_html,
@@ -233,12 +233,12 @@ class EMail:
self.make()
return self.msg_root.as_string()

def get_formatted_html(subject, message, footer=None, print_html=None, email_account=None, header=False):
def get_formatted_html(subject, message, footer=None, print_html=None, email_account=None, header=None):
if not email_account:
email_account = get_outgoing_email_account(False)

rendered_email = frappe.get_template("templates/emails/standard.html").render({
"header": get_header() if header else None,
"header": get_header(header),
"content": message,
"signature": get_signature(email_account),
"footer": get_footer(email_account, footer),
@@ -416,22 +416,27 @@ def get_filecontent_from_path(path):
return None


def get_header():
def get_header(header=None):
""" Build header from template """
from frappe.utils.jinja import get_email_from_template

default_brand_image = 'assets/frappe/images/favicon.png' # svg doesn't work in email
email_brand_image = frappe.get_hooks('email_brand_image')
if len(email_brand_image):
email_brand_image = email_brand_image[-1]
else:
email_brand_image = default_brand_image
if not header: return None

if isinstance(header, basestring):
# header = 'My Title'
header = [header, None]
if len(header) == 1:
# header = ['My Title']
header.append(None)
# header = ['My Title', 'orange']
title, indicator = header

brand_text = frappe.get_hooks('app_title')[-1]
if not title:
title = frappe.get_hooks('app_title')[-1]

email_header, text = get_email_from_template('email_header', {
'brand_image': email_brand_image,
'brand_text': brand_text
'header_title': title,
'indicator': indicator
})

return email_header

+ 1
- 1
frappe/email/queue.py View File

@@ -23,7 +23,7 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content=
attachments=None, reply_to=None, cc=[], message_id=None, in_reply_to=None, send_after=None,
expose_recipients=None, send_priority=1, communication=None, now=False, read_receipt=None,
queue_separately=False, is_notification=False, add_unsubscribe_link=1, inline_images=None,
header=False):
header=None):
"""Add email to sending queue (Email Queue)

:param recipients: List of recipients.


+ 30
- 1
frappe/email/test_email_body.py View File

@@ -4,7 +4,7 @@ from __future__ import unicode_literals

import frappe, unittest, os, base64
from frappe.email.email_body import (replace_filename_with_cid,
get_email, inline_style_in_html)
get_email, inline_style_in_html, get_header)

class TestEmailBody(unittest.TestCase):
def setUp(self):
@@ -107,6 +107,35 @@ w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
'''
self.assertTrue(transformed_html in inline_style_in_html(html))

def test_email_header(self):
email_html = '''
<h3>Hey John Doe!</h3>
<p>This is embedded image you asked for</p>
'''
email_string = get_email(
recipients=['test@example.com'],
sender='me@example.com',
subject='Test Subject',
content=email_html,
header=['Email Title', 'green']
).as_string()

self.assertTrue('''<span class=3D"indicator indicator-green" style=3D"background-color:#98=
d85b; border-radius:8px; display:inline-block; height:8px; margin-right:5px=
; width:8px" bgcolor=3D"#98d85b" height=3D"8" width=3D"8"></span>''' in email_string)
self.assertTrue('<span>Email Title</span>' in email_string)

def test_get_email_header(self):
html = get_header(['This is test', 'orange'])
self.assertTrue('<span class="indicator indicator-orange"></span>' in html)
self.assertTrue('<span>This is test</span>' in html)

html = get_header(['This is another test'])
self.assertTrue('<span>This is another test</span>' in html)

html = get_header('This is string')
self.assertTrue('<span>This is string</span>' in html)


def fixed_column_width(string, chunk_size):
parts = [string[0+i:chunk_size+i] for i in range(0, len(string), chunk_size)]

+ 32
- 4
frappe/public/css/email.css View File

@@ -12,6 +12,9 @@ hr {
.body-table {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
.body-table td {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
.email-header,
.email-body,
.email-footer {
@@ -26,18 +29,20 @@ hr {
font-size: 12px;
}
.email-header {
background: #fafbfc;
border: 1px solid #d1d8dd;
border-radius: 3px 3px 0 0;
border-radius: 4px 4px 0 0;
}
.email-header .brand-image {
width: 24px;
height: 24px;
display: block;
}
.email-header-title {
font-weight: bold;
}
.body-table.has-header .email-body {
border: 1px solid #d1d8dd;
border-radius: 0 0 3px 3px;
border-radius: 0 0 4px 4px;
border-top: none;
}
.body-table.has-header .email-footer {
@@ -76,7 +81,7 @@ hr {
font-weight: bold;
}
.table > thead > tr > th {
vertical-align: bottom;
vertical-align: middle;
border-bottom: 2px solid #d1d8dd;
}
.table > thead:first-child > tr:first-child > th {
@@ -116,6 +121,29 @@ hr {
.text-small {
font-size: 10px;
}
.indicator {
width: 8px;
height: 8px;
border-radius: 8px;
background-color: #b8c2cc;
display: inline-block;
margin-right: 5px;
}
.indicator.indicator-blue {
background-color: #5e64ff;
}
.indicator.indicator-green {
background-color: #98d85b;
}
.indicator.indicator-orange {
background-color: #ffa00a;
}
.indicator.indicator-red {
background-color: #ff5858;
}
.indicator.indicator-yellow {
background-color: #FEEF72;
}
/* auto email report */
.report-title {
margin-bottom: 20px;


+ 36
- 4
frappe/public/less/email.less View File

@@ -16,6 +16,10 @@ hr {

.body-table {
font-family: @font-stack;

td {
font-family: @font-stack;
}
}

.email-header, .email-body, .email-footer {
@@ -33,9 +37,8 @@ hr {
}

.email-header {
background: @light-bg;
border: 1px solid @border-color;
border-radius: 3px 3px 0 0;
border-radius: 4px 4px 0 0;

.brand-image {
width: 24px;
@@ -44,10 +47,14 @@ hr {
}
}

.email-header-title {
font-weight: bold;
}

.body-table.has-header {
.email-body {
border: 1px solid @border-color;
border-radius: 0 0 3px 3px;
border-radius: 0 0 4px 4px;
border-top: none;
}

@@ -93,7 +100,7 @@ hr {
}

& > thead > tr > th {
vertical-align: bottom;
vertical-align: middle;
border-bottom: 2px solid @border-color;
}

@@ -145,6 +152,31 @@ hr {
font-size: @text-small;
}

.indicator {
width: 8px;
height: 8px;
border-radius: 8px;
background-color: @indicator-darkgrey;
display: inline-block;
margin-right: 5px;

&.indicator-blue {
background-color: @indicator-blue;
}
&.indicator-green {
background-color: @indicator-green;
}
&.indicator-orange {
background-color: @indicator-orange;
}
&.indicator-red {
background-color: @indicator-red;
}
&.indicator-yellow {
background-color: @indicator-yellow;
}
}

/* auto email report */
.report-title {
margin-bottom: 20px;


+ 6
- 4
frappe/templates/emails/email_header.html View File

@@ -1,11 +1,13 @@
<table class="email-header" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td width="15"></td>
<td width="35" height="50">
<img class="brand-image" embed="{{brand_image}}" alt="{{brand_text}}">
</td>
<td>
<p>{{ brand_text }}</p>
<p class="email-header-title">
{% if indicator %}
<span class="indicator indicator-{{indicator}}"></span>
{% endif %}
<span>{{ header_title }}</span>
</p>
</td>
<td width="15"></td>
</tr>

+ 0
- 1
frappe/templates/emails/new_user.html View File

@@ -1,4 +1,3 @@
{% if title %}<h3>{{ title }}</h3>{% endif %}
<p>{{_("Dear")}} {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p>
<p>{{_("A new account has been created for you at {0}").format(site_url)}}.</p>
<p>{{_("Your login id is")}}: <b>{{ user }}</b>


+ 0
- 2
frappe/templates/emails/password_reset.html View File

@@ -1,5 +1,3 @@
<h3>{{_("Password Reset")}}</h3>

<p>{{_("Dear")}} {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p>
<p>{{_("Please click on the following link to set your new password")}}:</p>
<p><a class="btn btn-primary" href="{{ link }}">Reset your password</a></p>


+ 0
- 1
frappe/templates/emails/password_update.html View File

@@ -1,4 +1,3 @@
<h3>{{_("Password Update Notification")}}</h3>
<p>{{_("Dear")}} {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p>
<p>{{_("Your password has been updated. Here is your new password")}}: <b>{{ new_password }}</b></p>
<p>{{_("Thank you")}},<br>

Loading…
Cancel
Save