From 79f37d98035f930d4dcfb6aa7284b654919d7fbe Mon Sep 17 00:00:00 2001 From: mbauskar Date: Tue, 14 Mar 2017 17:48:48 +0530 Subject: [PATCH] [enhance] mark email as spam, trash, add to contact --- .../doctype/communication/communication.js | 52 +++++++- .../doctype/communication/communication.json | 78 +++++++++-- .../doctype/communication/communication.py | 38 ++++-- .../communication/communication_list.js | 2 +- frappe/core/notifications.py | 1 + .../email_flag_queue/email_flag_queue.json | 9 +- frappe/email/doctype/email_rule/__init__.py | 0 frappe/email/doctype/email_rule/email_rule.js | 8 ++ .../email/doctype/email_rule/email_rule.json | 122 ++++++++++++++++++ frappe/email/doctype/email_rule/email_rule.py | 10 ++ .../doctype/email_rule/test_email_rule.py | 10 ++ frappe/email/inbox.py | 102 ++++++++++++++- 12 files changed, 393 insertions(+), 39 deletions(-) create mode 100644 frappe/email/doctype/email_rule/__init__.py create mode 100644 frappe/email/doctype/email_rule/email_rule.js create mode 100644 frappe/email/doctype/email_rule/email_rule.json create mode 100644 frappe/email/doctype/email_rule/email_rule.py create mode 100644 frappe/email/doctype/email_rule/test_email_rule.py diff --git a/frappe/core/doctype/communication/communication.js b/frappe/core/doctype/communication/communication.js index 305d8fe586..4735469481 100644 --- a/frappe/core/doctype/communication/communication.js +++ b/frappe/core/doctype/communication/communication.js @@ -60,9 +60,9 @@ frappe.ui.form.on("Communication", { frm.add_custom_button(__("Reply"), function() { frm.trigger('reply'); - }, "Actions"); + }); - frm.add_custom_button(__("Reply-All"), function() { + frm.add_custom_button(__("Reply All"), function() { frm.trigger('reply_all'); }, "Actions"); @@ -70,13 +70,24 @@ frappe.ui.form.on("Communication", { frm.trigger('forward_mail'); }, "Actions"); - frm.add_custom_button(__("Add to Contact"), function() { - frm.trigger('add_to_contact'); - }, "Actions"); - frm.add_custom_button(__("Mark as {0}", [frm.doc.seen? "Unread": "Read"]), function() { frm.trigger('mark_as_read_unread'); }, "Actions"); + + frm.add_custom_button(__("Add Contact"), function() { + frm.trigger('add_to_contact'); + }, "Actions"); + + if(frm.doc.email_status != "Spam") + frm.add_custom_button(__("Mark as Spam"), function() { + frm.trigger('mark_as_spam'); + }, "Actions"); + + if(frm.doc.email_status != "Trash") { + frm.add_custom_button(__("Move To Trash"), function() { + frm.trigger('move_to_trash'); + }, "Actions"); + } } }, show_relink_dialog: function(frm){ @@ -195,7 +206,7 @@ frappe.ui.form.on("Communication", { names = fullname.split(" ") first_name = names[0] - last_name = names.length >= 2? last_name[names.length - 1]: "" + last_name = names.length >= 2? names[names.length - 1]: "" frappe.route_options = { "email_id": frm.doc.sender, @@ -203,5 +214,32 @@ frappe.ui.form.on("Communication", { "last_name": last_name, } frappe.new_doc("Contact") + }, + + mark_as_spam: function(frm) { + frappe.call({ + method: "frappe.email.inbox.mark_as_spam", + args: { + communication: frm.doc.name, + sender: frm.doc.sender + }, + freeze: true, + callback: function(r) { + frappe.msgprint("Email has been marked as spam") + } + }) + }, + + move_to_trash: function(frm) { + frappe.call({ + method: "frappe.email.inbox.mark_as_trash", + args: { + communication: frm.doc.name + }, + freeze: true, + callback: function(r) { + frappe.msgprint("Email has been moved to trash") + } + }) } }); diff --git a/frappe/core/doctype/communication/communication.json b/frappe/core/doctype/communication/communication.json index e15e776624..58e5ab7c68 100644 --- a/frappe/core/doctype/communication/communication.json +++ b/frappe/core/doctype/communication/communication.json @@ -1223,13 +1223,42 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "uid", + "fieldtype": "Int", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "UID", + "length": 0, + "no_copy": 1, + "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_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "signature", - "fieldtype": "Data", + "fieldname": "email_status", + "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -1237,7 +1266,37 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Signature", + "label": "Email Status", + "length": 0, + "no_copy": 0, + "options": "Open\nSpam\nTrash", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "has_attachment", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Has Attachment", "length": 0, "no_copy": 0, "permlevel": 0, @@ -1257,6 +1316,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "depends_on": "eval: doc.rating > 0", "fieldname": "feedback_section", "fieldtype": "Section Break", "hidden": 0, @@ -1363,7 +1423,7 @@ "cancel": 0, "create": 1, "delete": 1, - "email": 0, + "email": 1, "export": 0, "if_owner": 0, "import": 0, @@ -1375,7 +1435,7 @@ "set_user_permissions": 0, "share": 1, "submit": 0, - "write": 1 + "write": 0 }, { "amend": 0, @@ -1403,7 +1463,7 @@ "cancel": 0, "create": 0, "delete": 0, - "email": 0, + "email": 1, "export": 0, "if_owner": 1, "import": 0, @@ -1416,16 +1476,16 @@ "share": 0, "submit": 0, "user_permission_doctypes": "[\"Email Account\"]", - "write": 1 + "write": 0 } ], "quick_entry": 0, "read_only": 0, "read_only_onload": 0, "search_fields": "subject", - "show_name_in_global_search": 1, + "show_name_in_global_search": 0, "sort_order": "DESC", "title_field": "subject", "track_changes": 1, - "track_seen": 0 + "track_seen": 1 } \ No newline at end of file diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index ed79637652..0520a14081 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -22,19 +22,22 @@ class Communication(Document): """Communication represents an external communication like Email.""" def onload(self): """create email flag queue""" - flag = frappe.db.get_value("Email Flag Queue", { - "communication": self.name, - "is_completed": 0}) - if flag: - return + if self.communication_type == "Communication" and self.communication_medium == "Email" \ + and self.sent_or_received == "Received": + + flag = frappe.db.get_value("Email Flag Queue", { + "communication": self.name, + "is_completed": 0}) + if flag: + return - frappe.get_doc({ - "doctype": "Email Flag Queue", - "action": "Read", - "communication": self.name, - "flag": "(\\SEEN)" - }).insert(ignore_permissions=True) - frappe.db.commit() + frappe.get_doc({ + "doctype": "Email Flag Queue", + "action": "Read", + "communication": self.name, + "flag": "(\\SEEN)" + }).insert(ignore_permissions=True) + frappe.db.commit() def validate(self): if self.reference_doctype and self.reference_name: @@ -64,6 +67,10 @@ class Communication(Document): def after_insert(self): if not (self.reference_doctype and self.reference_name): return + + if self.reference_doctype == "Communication" and self.sent_or_received == "Sent": + frappe.db.set_value("Communication", self.reference_name, "status", "Replied") + if self.communication_type in ("Communication", "Comment"): # send new comment to listening clients frappe.publish_realtime('new_communication', self.as_dict(), @@ -113,6 +120,13 @@ class Communication(Document): else: self.status = "Closed" + # set email status to spam + email_rule = frappe.db.get_value("Email Rule", { "email_id": self.sender, "is_spam":1 }) + if self.communication_type == "Communication" and self.communication_medium == "Email" \ + and self.sent_or_received == "Sent" and email_rule: + + self.email_status = "Spam" + def set_sender_full_name(self): if not self.sender_full_name and self.sender: if self.sender == "Administrator": diff --git a/frappe/core/doctype/communication/communication_list.js b/frappe/core/doctype/communication/communication_list.js index b534a5fbc0..b085239138 100644 --- a/frappe/core/doctype/communication/communication_list.js +++ b/frappe/core/doctype/communication/communication_list.js @@ -2,7 +2,7 @@ frappe.listview_settings['Communication'] = { add_fields: [ "sent_or_received","recipients", "subject", "communication_medium", "communication_type", - "sender", "seen" + "sender", "seen", "reference_doctype", "reference_name" ], filters: [["status", "=", "Open"]], diff --git a/frappe/core/notifications.py b/frappe/core/notifications.py index e48e52f48c..3cebb9c34b 100644 --- a/frappe/core/notifications.py +++ b/frappe/core/notifications.py @@ -73,6 +73,7 @@ def get_unread_emails(): FROM `tabCommunication` WHERE communication_type='Communication' AND communication_medium="Email" + AND email_status not in ("Spam", "Trash") AND email_account in ( SELECT distinct email_account from `tabUser Email` WHERE parent=%(user)s ) diff --git a/frappe/email/doctype/email_flag_queue/email_flag_queue.json b/frappe/email/doctype/email_flag_queue/email_flag_queue.json index 3e1d124978..ec4729ba77 100644 --- a/frappe/email/doctype/email_flag_queue/email_flag_queue.json +++ b/frappe/email/doctype/email_flag_queue/email_flag_queue.json @@ -1,5 +1,6 @@ { "allow_copy": 1, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -45,7 +46,7 @@ "collapsible": 0, "columns": 0, "fieldname": "communication", - "fieldtype": "Link", + "fieldtype": "Data", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -56,7 +57,7 @@ "label": "Communication", "length": 0, "no_copy": 0, - "options": "Communication", + "options": "", "permlevel": 0, "precision": "", "print_hide": 0, @@ -129,17 +130,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 1, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-03-01 05:24:47.756892", + "modified": "2017-03-13 07:54:07.987640", "modified_by": "Administrator", "module": "Email", "name": "Email Flag Queue", diff --git a/frappe/email/doctype/email_rule/__init__.py b/frappe/email/doctype/email_rule/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/email/doctype/email_rule/email_rule.js b/frappe/email/doctype/email_rule/email_rule.js new file mode 100644 index 0000000000..974bcd4e51 --- /dev/null +++ b/frappe/email/doctype/email_rule/email_rule.js @@ -0,0 +1,8 @@ +// Copyright (c) 2017, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Email Rule', { + refresh: function(frm) { + + } +}); diff --git a/frappe/email/doctype/email_rule/email_rule.json b/frappe/email/doctype/email_rule/email_rule.json new file mode 100644 index 0000000000..68d72ac1db --- /dev/null +++ b/frappe/email/doctype/email_rule/email_rule.json @@ -0,0 +1,122 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:email_id", + "beta": 0, + "creation": "2017-03-13 09:20:56.387135", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "email_id", + "fieldtype": "Data", + "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": "Email ID", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_spam", + "fieldtype": "Check", + "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": "Is Spam", + "length": 0, + "no_copy": 0, + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 1, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-03-13 09:26:38.441858", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Rule", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/frappe/email/doctype/email_rule/email_rule.py b/frappe/email/doctype/email_rule/email_rule.py new file mode 100644 index 0000000000..220798bbdc --- /dev/null +++ b/frappe/email/doctype/email_rule/email_rule.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class EmailRule(Document): + pass diff --git a/frappe/email/doctype/email_rule/test_email_rule.py b/frappe/email/doctype/email_rule/test_email_rule.py new file mode 100644 index 0000000000..3c7f9c83e6 --- /dev/null +++ b/frappe/email/doctype/email_rule/test_email_rule.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestEmailRule(unittest.TestCase): + pass diff --git a/frappe/email/inbox.py b/frappe/email/inbox.py index 8322e25d0d..62462d450e 100644 --- a/frappe/email/inbox.py +++ b/frappe/email/inbox.py @@ -17,20 +17,29 @@ def get_email_accounts(user=None): "all_accounts": "" } - email_accounts.append({ - "email_account": "Sent", - "email_id": "Sent Mail" - }) - all_accounts = ",".join([ account.get("email_account") for account in accounts ]) if len(accounts) > 1: email_accounts.append({ "email_account": all_accounts, "email_id": "All Accounts" }) - email_accounts.extend(accounts) + email_accounts.extend([ + { + "email_account": "Sent", + "email_id": "Sent Mail" + }, + { + "email_account": "Spam", + "email_id": "Spam" + }, + { + "email_account": "Trash", + "email_id": "Trash" + } + ]) + return { "email_accounts": email_accounts, "all_accounts": all_accounts @@ -77,3 +86,84 @@ def create_email_flag_queue(names, action, flag="(\\Seen)"): update_modified=False) except Found: pass + +@frappe.whitelist() +def mark_as_trash(communication): + """set email status to trash""" + frappe.db.set_value("Communication", communication, "email_status", "Trash") + +@frappe.whitelist() +def mark_as_spam(communication, sender): + """ set email status to spam """ + email_rule = frappe.db.get_value("Email Rule", { "email_id": sender }) + if not email_rule: + frappe.get_doc({ + "doctype": "Email Rule", + "email_id": sender, + "is_spam": 1 + }).insert(ignore_permissions=True) + frappe.db.set_value("Communication", communication, "email_status", "Spam") + +def link_communication_to_document(doc, reference_doctype, reference_name, ignore_communication_links): + if not ignore_communication_links: + doc.reference_doctype = reference_doctype + doc.reference_name = reference_name + doc.status = "Linked" + doc.save(ignore_permissions=True) + +@frappe.whitelist() +def make_issue_from_communication(communication, ignore_communication_links=False): + """ raise a issue from email """ + + doc = frappe.get_doc("Communication", communication) + issue = frappe.get_doc({ + "doctype": "Issue", + "subject": doc.subject, + "raised_by": doc.sender + }).insert(ignore_permissions=True) + + link_communication_to_document(doc, "Issue", issue.name, ignore_communication_links) + + return issue.name + +@frappe.whitelist() +def make_lead_from_communication(communication, ignore_communication_links=False): + """ raise a issue from email """ + + doc = frappe.get_doc("Communication", communication) + frappe.errprint(doc.sender_full_name) + lead_name = frappe.db.get_value("Lead", {"email_id": doc.sender}) + if not lead_name: + lead = frappe.get_doc({ + "doctype": "Lead", + "lead_name": doc.sender_full_name, + "email_id": doc.sender + }) + lead.flags.ignore_mandatory = True + lead.flags.ignore_permissions = True + lead.insert() + + lead_name = lead.name + + link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links) + return lead_name + +@frappe.whitelist() +def make_opportunity_from_communication(communication, ignore_communication_links=False): + doc = frappe.get_doc("Communication", communication) + + lead = doc.reference_name if doc.reference_doctype == "Lead" else None + if not lead: + lead = make_lead_from_communication(communication, ignore_communication_links=True) + + enquiry_from = "Lead" + + opportunity = frappe.get_doc({ + "doctype": "Opportunity", + "enquiry_from": enquiry_from, + "lead": lead + }).insert(ignore_permissions=True) + + link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links) + + return opportunity.name