* Fix fonts for emails * Email header with indicator * Login emails with header * Add tests for email header * fix codacyversion-14
@@ -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**. | |||
@@ -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): | |||
@@ -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 |
@@ -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. | |||
@@ -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)] |
@@ -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; | |||
@@ -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; | |||
@@ -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> |
@@ -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> | |||
@@ -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> | |||
@@ -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> |