diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py
index 18025b61f7..6505d15394 100644
--- a/frappe/core/doctype/communication/communication.py
+++ b/frappe/core/doctype/communication/communication.py
@@ -66,9 +66,9 @@ class Communication(Document):
if not self.outgoing_email_account:
self.outgoing_email_account = frappe.db.get_value("Email Account", {"default_outgoing": 1}, "email_id")
- def notify(self, print_html=None, print_format=None, attachments=None, except_sender=False):
+ def notify(self, print_html=None, print_format=None, attachments=None, except_recipient=False):
self.prepare_to_notify(print_html, print_format, attachments)
- recipients = self.get_recipients(except_sender=except_sender)
+ recipients = self.get_recipients(except_recipient=except_recipient)
frappe.sendmail(
recipients=recipients,
@@ -109,13 +109,17 @@ class Communication(Document):
attachments = json.loads(attachments)
for a in attachments:
- try:
- file = get_file(a)
- self.attachments.append({"fname": file[0], "fcontent": file[1]})
- except IOError:
- frappe.throw(_("Unable to find attachment {0}").format(a))
+ if isinstance(a, basestring):
+ # is it a filename?
+ try:
+ file = get_file(a)
+ self.attachments.append({"fname": file[0], "fcontent": file[1]})
+ except IOError:
+ frappe.throw(_("Unable to find attachment {0}").format(a))
+ else:
+ self.attachments.append(a)
- def get_recipients(self, except_sender=False):
+ def get_recipients(self, except_recipient=False):
"""Build a list of users to which this email should go to"""
recipients = self.get_earlier_participants()
@@ -123,15 +127,15 @@ class Communication(Document):
recipients += [s.strip() for s in self.recipients.split(",")]
recipients += self.get_assignees()
recipients += self.get_starrers()
- recipients = filter(lambda e: e and e!="Administrator", list(set(recipients)))
+ recipients = filter(lambda e: e and e!="Administrator" and e!=self.sender, list(set(recipients)))
# remove unsubscribed recipients
unsubscribed = [d[0] for d in frappe.db.get_all("User", ["name"], {"thread_notify": 0}, as_list=True)]
recipients = filter(lambda e: e not in unsubscribed, recipients)
- if except_sender:
- # while pulling email, don't send email to current sender and recipients
- recipients = filter(lambda e: not (e==self.sender or e==self.recipients), recipients)
+ if except_recipient:
+ # while pulling email, don't send email to current recipient
+ recipients = filter(lambda e: e!=self.recipients, recipients)
return recipients
diff --git a/frappe/core/page/desktop/desktop.js b/frappe/core/page/desktop/desktop.js
index 5d13a604e4..7a8b1ec089 100644
--- a/frappe/core/page/desktop/desktop.js
+++ b/frappe/core/page/desktop/desktop.js
@@ -83,14 +83,17 @@ $.extend(frappe.desktop, {
}
// filter valid icons
+ var out = [];
for (var i=0, l=user_desktop_items.length; i < l; i++) {
var m = user_desktop_items[i];
var module = frappe.get_module(m);
- module.app_icon = frappe.ui.app_icon.get_html(m);
+ if (module) {
+ module.app_icon = frappe.ui.app_icon.get_html(m);
+ out.push(m);
+ }
}
-
- return user_desktop_items;
+ return out;
},
setup_icon_click: function() {
diff --git a/frappe/core/page/desktop/desktop_icon_grid.html b/frappe/core/page/desktop/desktop_icon_grid.html
index 242cc2d990..3af6a837f9 100644
--- a/frappe/core/page/desktop/desktop_icon_grid.html
+++ b/frappe/core/page/desktop/desktop_icon_grid.html
@@ -2,7 +2,7 @@
{% for (var i=0, l=desktop_items.length; i < l; i++) {
var module = frappe.get_module(desktop_items[i]);
- if (user_desktop_items.indexOf(module.name)===-1 && !module.force_show) { continue; }
+ if (!module || (user_desktop_items.indexOf(module.name)===-1 && !module.force_show)) { continue; }
%}
{%= frappe.render_template("desktop_module_icon", module) %}
{% } %}
diff --git a/frappe/database.py b/frappe/database.py
index e0cf979a78..b6994297e0 100644
--- a/frappe/database.py
+++ b/frappe/database.py
@@ -733,9 +733,9 @@ class Database:
fields = [fields]
if not constraint_name:
constraint_name = "unique_" + "_".join(fields)
-
- if not frappe.db.sql("""select CONSTRAINT_NAME from information_schema.TABLE_CONSTRAINTS
- where table_name=%s and constraint_type='UNIQUE' and CONSTRAINT_NAME=%s""",
+
+ if not frappe.db.sql("""select CONSTRAINT_NAME from information_schema.TABLE_CONSTRAINTS
+ where table_name=%s and constraint_type='UNIQUE' and CONSTRAINT_NAME=%s""",
('tab' + doctype, constraint_name)):
frappe.db.commit()
frappe.db.sql("""alter table `tab%s`
diff --git a/frappe/email/bulk.py b/frappe/email/bulk.py
index 290455af60..2c84fe2b70 100644
--- a/frappe/email/bulk.py
+++ b/frappe/email/bulk.py
@@ -9,7 +9,7 @@ from frappe.email.smtp import SMTPServer, get_outgoing_email_account
from frappe.email.email_body import get_email, get_formatted_html
from frappe.utils.verified_command import get_signed_params, verify_request
from html2text import html2text
-from frappe.utils import get_url, nowdate
+from frappe.utils import get_url, nowdate, encode
class BulkLimitCrossedError(frappe.ValidationError): pass
@@ -175,7 +175,8 @@ def flush(from_test=False):
(email["name"],), auto_commit=auto_commit)
try:
if not from_test:
- smtpserver.sess.sendmail(email["sender"], email["recipient"], email["message"])
+ smtpserver.sess.sendmail(email["sender"], email["recipient"], encode(email["message"]))
+
frappe.db.sql("""update `tabBulk Email` set status='Sent' where name=%s""",
(email["name"],), auto_commit=auto_commit)
diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py
index 035ccd10c6..642da61353 100644
--- a/frappe/email/doctype/email_account/email_account.py
+++ b/frappe/email/doctype/email_account/email_account.py
@@ -125,13 +125,13 @@ class EmailAccount(Document):
# save attachments
email.save_attachments_in_doc(communication)
- if self.enable_auto_reply:
- self.send_auto_reply(communication)
+ if self.enable_auto_reply and getattr(communication, "is_first", False):
+ self.send_auto_reply(communication, email)
# notify all participants of this thread
# convert content to HTML - by default text parts of replies are used.
communication.content = markdown2.markdown(communication.content)
- communication.notify(attachments=email.attachments, except_sender = True)
+ communication.notify(attachments=email.attachments, except_recipient = True)
def set_thread(self, communication, email):
"""Appends communication to parent based on thread ID. Will extract
@@ -175,18 +175,27 @@ class EmailAccount(Document):
parent.flags.ignore_mandatory = True
parent.insert(ignore_permissions=True)
+ communication.is_first = True
+
if parent:
communication.reference_doctype = parent.doctype
communication.reference_name = parent.name
- def send_auto_reply(self, communication):
+ def send_auto_reply(self, communication, email):
"""Send auto reply if set."""
if self.auto_reply_message:
- frappe.sendmail(recipients = [communication.from_email],
+ communication.set_incoming_outgoing_accounts()
+
+ frappe.sendmail(recipients = [email.from_email],
sender = self.email_id,
+ reply_to = communication.incoming_email_account,
subject = _("Re: ") + communication.subject,
- content = self.auto_reply_message or\
+ content = self.auto_reply_message or \
frappe.get_template("templates/emails/auto_reply.html").render(communication.as_dict()),
+ reference_doctype = communication.reference_doctype,
+ reference_name = communication.reference_name,
+ message_id = communication.name,
+ unsubscribe_message = _("Leave this conversation"),
bulk=True)
def get_unreplied_notification_emails(self):
diff --git a/frappe/email/receive.py b/frappe/email/receive.py
index 64cf1242ec..85cf24ec5c 100644
--- a/frappe/email/receive.py
+++ b/frappe/email/receive.py
@@ -6,9 +6,10 @@ import time
import _socket, poplib
import frappe
from frappe import _
-from frappe.utils import extract_email_id, convert_utc_to_user_timezone, now, cint
+from frappe.utils import extract_email_id, convert_utc_to_user_timezone, now, cint, cstr
from frappe.utils.scheduler import log
from email_reply_parser import EmailReplyParser
+from email.header import decode_header
class EmailSizeExceededError(frappe.ValidationError): pass
class EmailTimeoutError(frappe.ValidationError): pass
@@ -237,7 +238,7 @@ class Email:
def get_attachment(self, part, charset):
self.attachments.append({
'content_type': part.get_content_type(),
- 'fname': part.get_filename(),
+ 'fname': cstr(decode_header(part.get_filename())[0][0]),
'fcontent': part.get_payload(decode=True),
})
diff --git a/frappe/model/db_schema.py b/frappe/model/db_schema.py
index a54509b275..7c93c05e3d 100644
--- a/frappe/model/db_schema.py
+++ b/frappe/model/db_schema.py
@@ -24,7 +24,7 @@ type_map = {
,'Check': ('int', '1')
,'Small Text': ('text', '')
,'Long Text': ('longtext', '')
- ,'Code': ('text', '')
+ ,'Code': ('longtext', '')
,'Text Editor': ('longtext', '')
,'Date': ('date', '')
,'Datetime': ('datetime', '6')
diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js
index 2801ecf1bc..9088d5aa5c 100644
--- a/frappe/public/js/frappe/form/formatters.js
+++ b/frappe/public/js/frappe/form/formatters.js
@@ -158,6 +158,9 @@ frappe.form.formatters = {
} else {
return "
" + value + "";
}
+ },
+ Email: function(value) {
+ return $("
").text(value).html();
}
}
diff --git a/frappe/templates/emails/standard.html b/frappe/templates/emails/standard.html
index e1f9874260..c30f1b986f 100644
--- a/frappe/templates/emails/standard.html
+++ b/frappe/templates/emails/standard.html
@@ -10,8 +10,10 @@
{{ content }}
-
- {{ footer }}
+
{{ print_html or "" }}