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 }}
+