* added blank carbon copy * [FEAT] BCC in emails * removed test_communication, fixed comment * fixed codacyversion-14
@@ -378,7 +378,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message | |||
as_markdown=False, delayed=True, reference_doctype=None, reference_name=None, | |||
unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, | |||
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, | |||
cc=[], bcc=[], 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=None): | |||
"""Send email using user's default **Email Account** or global default **Email Account**. | |||
@@ -426,7 +426,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message | |||
subject=subject, message=message, text_content=text_content, | |||
reference_doctype = doctype or reference_doctype, reference_name = name or reference_name, | |||
unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message, | |||
attachments=attachments, reply_to=reply_to, cc=cc, message_id=message_id, in_reply_to=in_reply_to, | |||
attachments=attachments, reply_to=reply_to, cc=cc, bcc=bcc, message_id=message_id, in_reply_to=in_reply_to, | |||
send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority, | |||
communication=communication, now=now, read_receipt=read_receipt, is_notification=is_notification, | |||
inline_images=inline_images, header=header) | |||
@@ -15,6 +15,7 @@ | |||
"engine": "InnoDB", | |||
"fields": [ | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -43,6 +44,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -73,6 +75,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -104,6 +107,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -134,6 +138,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -162,6 +167,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -192,6 +198,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -223,6 +230,39 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"depends_on": "eval:doc.communication_medium===\"Email\"", | |||
"fieldname": "bcc", | |||
"fieldtype": "Code", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_global_search": 0, | |||
"in_list_view": 0, | |||
"in_standard_filter": 0, | |||
"label": "BCC", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Email", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"remember_last_selected_value": 0, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -252,6 +292,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -283,6 +324,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -311,6 +353,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -340,6 +383,7 @@ | |||
"width": "400" | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -369,6 +413,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -398,6 +443,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -429,6 +475,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -459,6 +506,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -487,6 +535,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -518,6 +567,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -548,6 +598,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -576,6 +627,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -605,6 +657,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -634,6 +687,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -662,6 +716,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -691,6 +746,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -720,6 +776,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -750,6 +807,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -780,6 +838,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -810,6 +869,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -841,6 +901,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -871,6 +932,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -901,6 +963,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -929,6 +992,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -959,6 +1023,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -989,6 +1054,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1019,6 +1085,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1049,6 +1116,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1079,6 +1147,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1109,6 +1178,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1138,6 +1208,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1166,6 +1237,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -1195,6 +1267,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1224,6 +1297,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -1253,6 +1327,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1283,6 +1358,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1312,6 +1388,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -1342,6 +1419,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1371,6 +1449,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1411,7 +1490,7 @@ | |||
"issingle": 0, | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"modified": "2017-03-29 23:06:16.469149", | |||
"modified": "2017-10-25 12:53:49.547620", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Communication", | |||
@@ -1477,26 +1556,6 @@ | |||
"submit": 0, | |||
"user_permission_doctypes": "[\"Email Account\"]", | |||
"write": 0 | |||
}, | |||
{ | |||
"amend": 0, | |||
"apply_user_permissions": 0, | |||
"cancel": 0, | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"export": 1, | |||
"if_owner": 0, | |||
"import": 0, | |||
"permlevel": 0, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "Super Email User", | |||
"set_user_permissions": 0, | |||
"share": 1, | |||
"submit": 0, | |||
"write": 0 | |||
} | |||
], | |||
"quick_entry": 0, | |||
@@ -189,7 +189,7 @@ class Communication(Document): | |||
self.notify(print_html, print_format, attachments, recipients) | |||
def notify(self, print_html=None, print_format=None, attachments=None, | |||
recipients=None, cc=None, fetched_from_email_account=False): | |||
recipients=None, cc=None, bcc=None,fetched_from_email_account=False): | |||
"""Calls a delayed task 'sendmail' that enqueus email in Email Queue queue | |||
:param print_html: Send given value as HTML attachment | |||
@@ -200,13 +200,13 @@ class Communication(Document): | |||
:param fetched_from_email_account: True when pulling email, the notification shouldn't go to the main recipient | |||
""" | |||
notify(self, print_html, print_format, attachments, recipients, cc, | |||
notify(self, print_html, print_format, attachments, recipients, cc, bcc, | |||
fetched_from_email_account) | |||
def _notify(self, print_html=None, print_format=None, attachments=None, | |||
recipients=None, cc=None): | |||
recipients=None, cc=None, bcc=None): | |||
_notify(self, print_html, print_format, attachments, recipients, cc) | |||
_notify(self, print_html, print_format, attachments, recipients, cc, bcc) | |||
def bot_reply(self): | |||
if self.comment_type == 'Bot' and self.communication_type == 'Chat': | |||
@@ -22,7 +22,7 @@ from frappe.utils.background_jobs import enqueue | |||
@frappe.whitelist() | |||
def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent", | |||
sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False, | |||
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, flags=None,read_receipt=None): | |||
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None, flags=None,read_receipt=None): | |||
"""Make a new communication. | |||
:param doctype: Reference DocType. | |||
@@ -58,6 +58,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = | |||
"sender_full_name":sender_full_name, | |||
"recipients": recipients, | |||
"cc": cc or None, | |||
"bcc": bcc or None, | |||
"communication_medium": communication_medium, | |||
"sent_or_received": sent_or_received, | |||
"reference_doctype": doctype, | |||
@@ -102,10 +103,13 @@ def validate_email(doc): | |||
for email in split_emails(doc.cc): | |||
validate_email_add(email, throw=True) | |||
for email in split_emails(doc.bcc): | |||
validate_email_add(email, throw=True) | |||
# validate sender | |||
def notify(doc, print_html=None, print_format=None, attachments=None, | |||
recipients=None, cc=None, fetched_from_email_account=False): | |||
recipients=None, cc=None, bcc=None, fetched_from_email_account=False): | |||
"""Calls a delayed task 'sendmail' that enqueus email in Email Queue queue | |||
:param print_html: Send given value as HTML attachment | |||
@@ -113,10 +117,11 @@ def notify(doc, print_html=None, print_format=None, attachments=None, | |||
:param attachments: A list of filenames that should be attached when sending this email | |||
:param recipients: Email recipients | |||
:param cc: Send email as CC to | |||
:param bcc: Send email as BCC to | |||
:param fetched_from_email_account: True when pulling email, the notification shouldn't go to the main recipient | |||
""" | |||
recipients, cc = get_recipients_and_cc(doc, recipients, cc, | |||
recipients, cc, bcc = get_recipients_cc_and_bcc(doc, recipients, cc, bcc, | |||
fetched_from_email_account=fetched_from_email_account) | |||
if not recipients: | |||
@@ -127,16 +132,16 @@ def notify(doc, print_html=None, print_format=None, attachments=None, | |||
if frappe.flags.in_test: | |||
# for test cases, run synchronously | |||
doc._notify(print_html=print_html, print_format=print_format, attachments=attachments, | |||
recipients=recipients, cc=cc) | |||
recipients=recipients, cc=cc, bcc=None) | |||
else: | |||
check_email_limit(list(set(doc.sent_email_addresses))) | |||
enqueue(sendmail, queue="default", timeout=300, event="sendmail", | |||
communication_name=doc.name, | |||
print_html=print_html, print_format=print_format, attachments=attachments, | |||
recipients=recipients, cc=cc, lang=frappe.local.lang, session=frappe.local.session) | |||
recipients=recipients, cc=cc, bcc=bcc, lang=frappe.local.lang, session=frappe.local.session) | |||
def _notify(doc, print_html=None, print_format=None, attachments=None, | |||
recipients=None, cc=None): | |||
recipients=None, cc=None, bcc=None): | |||
prepare_to_notify(doc, print_html, print_format, attachments) | |||
@@ -148,6 +153,7 @@ def _notify(doc, print_html=None, print_format=None, attachments=None, | |||
frappe.sendmail( | |||
recipients=(recipients or []), | |||
cc=(cc or []), | |||
bcc=(bcc or []), | |||
expose_recipients="header", | |||
sender=doc.sender, | |||
reply_to=doc.incoming_email_account, | |||
@@ -190,7 +196,7 @@ def update_parent_mins_to_first_response(doc): | |||
parent.run_method('notify_communication', doc) | |||
parent.notify_update() | |||
def get_recipients_and_cc(doc, recipients, cc, fetched_from_email_account=False): | |||
def get_recipients_cc_and_bcc(doc, recipients, cc, bcc, fetched_from_email_account=False): | |||
doc.all_email_addresses = [] | |||
doc.sent_email_addresses = [] | |||
doc.previous_email_sender = None | |||
@@ -201,6 +207,9 @@ def get_recipients_and_cc(doc, recipients, cc, fetched_from_email_account=False) | |||
if not cc: | |||
cc = get_cc(doc, recipients, fetched_from_email_account=fetched_from_email_account) | |||
if not bcc: | |||
bcc = get_bcc(doc, recipients, fetched_from_email_account=fetched_from_email_account) | |||
if fetched_from_email_account: | |||
# email was already sent to the original recipient by the sender's email service | |||
original_recipients, recipients = recipients, [] | |||
@@ -216,10 +225,13 @@ def get_recipients_and_cc(doc, recipients, cc, fetched_from_email_account=False) | |||
# don't cc to people who already received the mail from sender's email service | |||
cc = list(set(cc) - set(original_cc) - set(original_recipients)) | |||
original_bcc = split_emails(doc.bcc) | |||
bcc = list(set(bcc) - set(original_bcc) - set(original_recipients)) | |||
if 'Administrator' in recipients: | |||
recipients.remove('Administrator') | |||
return recipients, cc | |||
return recipients, cc, bcc | |||
def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None): | |||
"""Prepare to make multipart MIME Email | |||
@@ -345,6 +357,34 @@ def get_cc(doc, recipients=None, fetched_from_email_account=False): | |||
return cc | |||
def get_bcc(doc, recipients=None, fetched_from_email_account=False): | |||
"""Build a list of email addresses for BCC""" | |||
bcc = split_emails(doc.bcc) | |||
if doc.reference_doctype and doc.reference_name: | |||
if fetched_from_email_account: | |||
bcc.append(get_owner_email(doc)) | |||
bcc += get_assignees(doc) | |||
if getattr(doc, "send_me_a_copy", False) and doc.sender not in bcc: | |||
bcc.append(doc.sender) | |||
if bcc: | |||
exclude = [] | |||
exclude += [d[0] for d in frappe.db.get_all("User", ["name"], {"thread_notify": 0}, as_list=True)] | |||
exclude += [(parse_addr(email)[1] or "").lower() for email in recipients] | |||
if fetched_from_email_account: | |||
# exclude sender when pulling email | |||
exclude += [parse_addr(doc.sender)[1]] | |||
if doc.reference_doctype and doc.reference_name: | |||
exclude += [d[0] for d in frappe.db.get_all("Email Unsubscribe", ["email"], | |||
{"reference_doctype": doc.reference_doctype, "reference_name": doc.reference_name}, as_list=True)] | |||
bcc = filter_email_list(doc, bcc, exclude, is_bcc=True) | |||
return bcc | |||
def add_attachments(name, attachments): | |||
'''Add attachments to the given Communiction''' | |||
@@ -360,7 +400,7 @@ def add_attachments(name, attachments): | |||
save_url(attach.file_url, attach.file_name, "Communication", name, | |||
"Home/Attachments", attach.is_private) | |||
def filter_email_list(doc, email_list, exclude, is_cc=False): | |||
def filter_email_list(doc, email_list, exclude, is_cc=False, is_bcc=False): | |||
# temp variables | |||
filtered = [] | |||
email_address_list = [] | |||
@@ -382,6 +422,11 @@ def filter_email_list(doc, email_list, exclude, is_cc=False): | |||
# don't send to disabled users | |||
continue | |||
if is_bcc: | |||
is_user_enabled = frappe.db.get_value("User", email_address, "enabled") | |||
if is_user_enabled==0: | |||
continue | |||
# make sure of case-insensitive uniqueness of email address | |||
if email_address not in email_address_list: | |||
# append the full email i.e. "Human <human@example.com>" | |||
@@ -416,7 +461,7 @@ def get_attach_link(doc, print_format): | |||
}) | |||
def sendmail(communication_name, print_html=None, print_format=None, attachments=None, | |||
recipients=None, cc=None, lang=None, session=None): | |||
recipients=None, cc=None, bcc=None, lang=None, session=None): | |||
try: | |||
if lang: | |||
@@ -432,7 +477,7 @@ def sendmail(communication_name, print_html=None, print_format=None, attachments | |||
try: | |||
communication = frappe.get_doc("Communication", communication_name) | |||
communication._notify(print_html=print_html, print_format=print_format, attachments=attachments, | |||
recipients=recipients, cc=cc) | |||
recipients=recipients, cc=cc, bcc=bcc) | |||
except MySQLdb.OperationalError as e: | |||
# deadlock, try again | |||
@@ -453,6 +498,7 @@ def sendmail(communication_name, print_html=None, print_format=None, attachments | |||
"attachments": attachments, | |||
"recipients": recipients, | |||
"cc": cc, | |||
"bcc": bcc, | |||
"lang": lang | |||
})) | |||
frappe.logger(__name__).error(traceback) | |||
@@ -15,7 +15,7 @@ from email.header import Header | |||
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, | |||
content=None, reply_to=None, cc=[], bcc=[], email_account=None, expose_recipients=None, | |||
inline_images=[], header=None): | |||
""" Prepare an email with the following format: | |||
- multipart/mixed | |||
@@ -27,7 +27,7 @@ def get_email(recipients, sender='', msg='', subject='[No Subject]', | |||
- attachment | |||
""" | |||
content = content or msg | |||
emailobj = EMail(sender, recipients, subject, reply_to=reply_to, cc=cc, email_account=email_account, expose_recipients=expose_recipients) | |||
emailobj = EMail(sender, recipients, subject, reply_to=reply_to, cc=cc, bcc=bcc, email_account=email_account, expose_recipients=expose_recipients) | |||
if not content.strip().startswith("<"): | |||
content = markdown(content) | |||
@@ -51,7 +51,7 @@ class EMail: | |||
Also provides a clean way to add binary `FileData` attachments | |||
Also sets all messages as multipart/alternative for cleaner reading in text-only clients | |||
""" | |||
def __init__(self, sender='', recipients=(), subject='', alternative=0, reply_to=None, cc=(), email_account=None, expose_recipients=None): | |||
def __init__(self, sender='', recipients=(), subject='', alternative=0, reply_to=None, cc=(), bcc=(), email_account=None, expose_recipients=None): | |||
from email import charset as Charset | |||
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') | |||
@@ -72,6 +72,7 @@ class EMail: | |||
self.msg_alternative = MIMEMultipart('alternative') | |||
self.msg_root.attach(self.msg_alternative) | |||
self.cc = cc or [] | |||
self.bcc = bcc or [] | |||
self.html_set = False | |||
self.email_account = email_account or get_outgoing_email_account(sender=sender) | |||
@@ -176,8 +177,9 @@ class EMail: | |||
self.recipients = [strip(r) for r in self.recipients] | |||
self.cc = [strip(r) for r in self.cc] | |||
self.bcc = [strip(r) for r in self.bcc] | |||
for e in self.recipients + (self.cc or []): | |||
for e in self.recipients + (self.cc or []) + (self.bcc or []): | |||
validate_email_add(e, True) | |||
def replace_sender(self): | |||
@@ -207,6 +209,7 @@ class EMail: | |||
"To": ', '.join(self.recipients) if self.expose_recipients=="header" else "<!--recipient-->", | |||
"Date": email.utils.formatdate(), | |||
"Reply-To": self.reply_to if self.reply_to else None, | |||
"Bcc": ', '.join(self.bcc) if self.bcc else None, | |||
"CC": ', '.join(self.cc) if self.cc and self.expose_recipients=="header" else None, | |||
'X-Frappe-Site': get_url(), | |||
} | |||
@@ -21,7 +21,7 @@ class EmailLimitCrossedError(frappe.ValidationError): pass | |||
def send(recipients=None, sender=None, subject=None, message=None, text_content=None, reference_doctype=None, | |||
reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, | |||
attachments=None, reply_to=None, cc=[], message_id=None, in_reply_to=None, send_after=None, | |||
attachments=None, reply_to=None, cc=[], bcc=[], 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=None): | |||
@@ -61,6 +61,9 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content= | |||
if isinstance(cc, string_types): | |||
cc = split_emails(cc) | |||
if isinstance(bcc, string_types): | |||
bcc = split_emails(bcc) | |||
if isinstance(send_after, int): | |||
send_after = add_days(nowdate(), send_after) | |||
@@ -112,6 +115,7 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content= | |||
attachments=attachments, | |||
reply_to=reply_to, | |||
cc=cc, | |||
bcc=bcc, | |||
message_id=message_id, | |||
in_reply_to=in_reply_to, | |||
send_after=send_after, | |||
@@ -174,6 +178,7 @@ def get_email_queue(recipients, sender, subject, **kwargs): | |||
attachments=kwargs.get('attachments'), | |||
reply_to=kwargs.get('reply_to'), | |||
cc=kwargs.get('cc'), | |||
bcc=kwargs.get('bcc'), | |||
email_account=kwargs.get('email_account'), | |||
expose_recipients=kwargs.get('expose_recipients'), | |||
inline_images=kwargs.get('inline_images'), | |||
@@ -194,7 +199,7 @@ def get_email_queue(recipients, sender, subject, **kwargs): | |||
frappe.log_error('Invalid Email ID Sender: {0}, Recipients: {1}'.format(mail.sender, | |||
', '.join(mail.recipients)), 'Email Not Sent') | |||
e.set_recipients(recipients + kwargs.get('cc', [])) | |||
e.set_recipients(recipients + kwargs.get('cc', []) + kwargs.get('bcc', [])) | |||
e.reference_doctype = kwargs.get('reference_doctype') | |||
e.reference_name = kwargs.get('reference_name') | |||
e.add_unsubscribe_link = kwargs.get("add_unsubscribe_link") | |||
@@ -204,6 +209,7 @@ def get_email_queue(recipients, sender, subject, **kwargs): | |||
e.communication = kwargs.get('communication') | |||
e.send_after = kwargs.get('send_after') | |||
e.show_as_cc = ",".join(kwargs.get('cc', [])) | |||
e.show_as_bcc = ",".join(kwargs.get('bcc', [])) | |||
e.insert(ignore_permissions=True) | |||
return e | |||
@@ -48,8 +48,9 @@ frappe.views.CommunicationComposer = Class.extend({ | |||
get_fields: function() { | |||
var fields= [ | |||
{label:__("To"), fieldtype:"Data", reqd: 0, fieldname:"recipients",length:524288}, | |||
{fieldtype: "Section Break", collapsible: 1, label: __("CC & Standard Reply")}, | |||
{fieldtype: "Section Break", collapsible: 1, label: __("CC, BCC & Standard Reply")}, | |||
{label:__("CC"), fieldtype:"Data", fieldname:"cc", length:524288}, | |||
{label:__("BCC"), fieldtype:"Data", fieldname:"bcc", length:524288}, | |||
{label:__("Standard Reply"), fieldtype:"Link", options:"Standard Reply", | |||
fieldname:"standard_reply"}, | |||
{fieldtype: "Section Break"}, | |||
@@ -109,6 +110,7 @@ frappe.views.CommunicationComposer = Class.extend({ | |||
this.dialog.fields_dict.recipients.set_value(this.recipients || ''); | |||
this.dialog.fields_dict.cc.set_value(this.cc || ''); | |||
this.dialog.fields_dict.bcc.set_value(this.bcc || ''); | |||
if(this.dialog.fields_dict.sender) { | |||
this.dialog.fields_dict.sender.set_value(this.sender || ''); | |||
@@ -123,6 +125,7 @@ frappe.views.CommunicationComposer = Class.extend({ | |||
if(!this.forward && !this.recipients && this.last_email) { | |||
this.recipients = this.last_email.sender; | |||
this.cc = this.last_email.cc; | |||
this.bcc = this.last_email.bcc; | |||
} | |||
if(!this.forward && !this.recipients) { | |||
@@ -446,6 +449,7 @@ frappe.views.CommunicationComposer = Class.extend({ | |||
// concat in cc | |||
if ( form_values[df.fieldname] ) { | |||
form_values.cc = ( form_values.cc ? (form_values.cc + ", ") : "" ) + df.fieldname; | |||
form_values.bcc = ( form_values.bcc ? (form_values.bcc + ", ") : "" ) + df.fieldname; | |||
} | |||
delete form_values[df.fieldname]; | |||
@@ -484,6 +488,7 @@ frappe.views.CommunicationComposer = Class.extend({ | |||
args: { | |||
recipients: form_values.recipients, | |||
cc: form_values.cc, | |||
bcc: form_values.bcc, | |||
subject: form_values.subject, | |||
content: form_values.content, | |||
doctype: me.doc.doctype, | |||
@@ -594,7 +599,8 @@ frappe.views.CommunicationComposer = Class.extend({ | |||
var me = this; | |||
[ | |||
this.dialog.fields_dict.recipients.input, | |||
this.dialog.fields_dict.cc.input | |||
this.dialog.fields_dict.cc.input, | |||
this.dialog.fields_dict.bcc.input | |||
].map(function(input) { | |||
me.setup_awesomplete_for_input(input); | |||
}); | |||