[enhance] communication inbox viewversion-14
@@ -3,14 +3,6 @@ from frappe import _ | |||
def get_data(): | |||
return [ | |||
{ | |||
"module_name": "Email", | |||
"color": "grey", | |||
"icon": "octicon octicon-mail", | |||
"type": "page", | |||
"link": "email_inbox", | |||
"label": _("Email Inbox") | |||
}, | |||
{ | |||
"module_name": "Desk", | |||
"label": _("Tools"), | |||
@@ -58,21 +58,36 @@ frappe.ui.form.on("Communication", { | |||
&& frm.doc.communication_medium == "Email" | |||
&& frm.doc.sent_or_received == "Received") { | |||
frm.add_custom_button(__("Mark as {0}", [frm.doc.seen? "Unread": "Read"]), function() { | |||
frm.trigger('mark_as_read_unread'); | |||
}, "Actions"); | |||
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"); | |||
frm.add_custom_button(__("Forward"), function() { | |||
frm.trigger('forward_mail'); | |||
}, "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){ | |||
@@ -183,5 +198,48 @@ frappe.ui.form.on("Communication", { | |||
sender: sender_email_id, | |||
attachments: frm.doc.attachments | |||
} | |||
}, | |||
add_to_contact: function(frm) { | |||
var me = this; | |||
fullname = frm.doc.sender_full_name || "" | |||
names = fullname.split(" ") | |||
first_name = names[0] | |||
last_name = names.length >= 2? names[names.length - 1]: "" | |||
frappe.route_options = { | |||
"email_id": frm.doc.sender, | |||
"first_name": first_name, | |||
"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") | |||
} | |||
}) | |||
} | |||
}); |
@@ -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 | |||
} |
@@ -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 | |||
frappe.get_doc({ | |||
"doctype": "Email Flag Queue", | |||
"action": "Read", | |||
"communication": self.name, | |||
"flag": "(\\SEEN)" | |||
}).insert(ignore_permissions=True) | |||
frappe.db.commit() | |||
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() | |||
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": | |||
@@ -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"]], | |||
@@ -16,6 +16,7 @@ def get_notification_config(): | |||
"for_other": { | |||
"Likes": "frappe.core.notifications.get_unseen_likes", | |||
"Chat": "frappe.core.notifications.get_unread_messages", | |||
"Email": "frappe.core.notifications.get_unread_emails", | |||
} | |||
} | |||
@@ -63,3 +64,19 @@ def get_unseen_likes(): | |||
and owner is not null and owner!=%(user)s | |||
and reference_owner=%(user)s | |||
and seen=0""", {"user": frappe.session.user})[0][0] | |||
def get_unread_emails(): | |||
"returns unread emails for a user" | |||
return frappe.db.sql("""\ | |||
SELECT count(*) | |||
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 | |||
) | |||
AND modified >= DATE_SUB(NOW(),INTERVAL 1 YEAR) | |||
AND seen=0 | |||
""", {"user": frappe.session.user})[0][0] |
@@ -43,9 +43,21 @@ $.extend(frappe.desktop, { | |||
link: 'modules' | |||
}; | |||
explore_icon.app_icon = frappe.ui.app_icon.get_html(explore_icon); | |||
all_icons.push(explore_icon); | |||
var inbox_icon = { | |||
module_name: 'Communication', | |||
label: 'Email Inbox', | |||
_label: __('Email Inbox'), | |||
_id: 'Email Inbox', | |||
_doctype: 'Communication', | |||
icon: 'fa fa-envelope-o', | |||
color: '#589494', | |||
link: 'List/Communication/Inbox' | |||
} | |||
inbox_icon.app_icon = frappe.ui.app_icon.get_html(inbox_icon) | |||
all_icons.push(inbox_icon); | |||
frappe.desktop.wrapper.html(frappe.render_template(template, { | |||
// all visible icons | |||
desktop_items: all_icons, | |||
@@ -569,7 +569,6 @@ class EmailAccount(Document): | |||
if self.email_sync_option == "ALL": | |||
max_uid = get_max_email_uid(self.name) | |||
last_uid = max_uid + int(self.initial_sync_count or 100) if max_uid == 1 else "*" | |||
print "UID {}:{}".format(max_uid, last_uid) | |||
return "UID {}:{}".format(max_uid, last_uid) | |||
else: | |||
return self.email_sync_option or "UNSEEN" | |||
@@ -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", | |||
@@ -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) { | |||
} | |||
}); |
@@ -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 | |||
} |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -1,84 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors | |||
# For license information, please see license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
import json | |||
from frappe.model.document import Document | |||
from frappe.desk.form.load import get_attachments | |||
@frappe.whitelist() | |||
def get_email_content(name): | |||
docinfo = frappe.desk.form.load.get_attachments("Communication", name) | |||
content = frappe.db.get_value("Communication", name, "content") | |||
return docinfo, content | |||
@frappe.whitelist() | |||
def create_flag_queue(names, action, flag): | |||
names = json.loads(names) | |||
class Found(Exception): | |||
pass | |||
for item in names: | |||
if item.get("uid"): | |||
state = frappe.db.get_value("Communication", item.get("name"), "seen") | |||
frappe.errprint(state) | |||
# check states are correct | |||
if (action =='Read' and state == 0) or (action =='Unread' and state == 1): | |||
try: | |||
queue = frappe.db.sql("""select name, action, flag from `tabEmail Flag Queue` | |||
where communication = %(name)s""", {"name":item.get("name")}, as_dict=True) | |||
for q in queue: | |||
# is same email with same flag | |||
if q.flag == flag: | |||
# to prevent flag local and server states being out of sync | |||
if q.action != action: | |||
frappe.delete_doc("Email Flag Queue", q.name) | |||
raise Found | |||
flag_queue = frappe.get_doc({ | |||
"doctype": "Email Flag Queue", | |||
"communication": item.get("name"), | |||
"action": action, | |||
"flag": flag | |||
}) | |||
flag_queue.save(ignore_permissions=True); | |||
except Found: | |||
pass | |||
@frappe.whitelist() | |||
def setnomatch(name): | |||
frappe.db.set_value("Communication", name, "nomatch", 1, update_modified=False) | |||
@frappe.whitelist() | |||
def update_local_flags(names, field, val): | |||
names = json.loads(names) | |||
for d in names: | |||
frappe.db.set_value("Communication", d.get("name"), field, val, update_modified=False) | |||
@frappe.whitelist() | |||
def get_accounts(user): | |||
email_accounts = [] | |||
accounts = frappe.get_all("User Email", filters={ "parent": user }, | |||
fields=["email_account as account", "email_id as title"], | |||
distinct=True, order_by="idx") | |||
if not accounts: | |||
return None | |||
all_accounts = ",".join([ account.get("account") for account in accounts ]) | |||
if len(accounts) > 1: | |||
email_accounts.append({ | |||
"account": all_accounts, | |||
"title": "All Accounts" | |||
}) | |||
email_accounts.extend(accounts) | |||
return { | |||
"email_accounts": email_accounts, | |||
"all_accounts": all_accounts | |||
} |
@@ -1,665 +0,0 @@ | |||
frappe.provide("frappe.email") | |||
frappe.pages['email_inbox'].on_page_load = function(wrapper) { | |||
frappe.ui.make_app_page({ | |||
parent: wrapper, | |||
title: 'Email Inbox', | |||
icon: 'fa fa-inbox', | |||
single_column: false | |||
}); | |||
frappe.model.with_doctype('Communication', function() { | |||
wrapper.inbox = new frappe.email.EmailInbox({ | |||
method: 'frappe.desk.reportview.get', | |||
wrapper: wrapper, | |||
page: wrapper.page, | |||
no_loading: true | |||
}); | |||
}); | |||
}; | |||
frappe.pages['email_inbox'].refresh = function(wrapper) { | |||
if (wrapper.inbox) { | |||
wrapper.inbox.refresh() | |||
} | |||
}; | |||
frappe.email.EmailInbox = frappe.ui.Listing.extend({ | |||
init: function(opts) { | |||
$.extend(this, opts); | |||
wrap = this; | |||
this.wrapper = opts.wrapper; | |||
this.page_length = 20; | |||
this.start = 0; | |||
this.no_result_message = 'No Emails to Display'; | |||
this.get_accounts(); | |||
}, | |||
setup_inbox: function() { | |||
var me = this; | |||
// setup listing | |||
me.make({ | |||
doctype: 'Communication', | |||
page: me.page, | |||
method: 'frappe.desk.reportview.get', | |||
get_args: me.get_args, | |||
parent: me.page.main, | |||
start: 0, | |||
show_filters: true | |||
}); | |||
this.render_sidebar(); | |||
this.render_headers(); | |||
this.render_buttons(); | |||
this.init_select_all(); | |||
this.setup_notifications(); | |||
this.refresh(); | |||
}, | |||
get_accounts: function() { | |||
// get all the configured email account for the user | |||
var me = this; | |||
frappe.call({ | |||
method: 'frappe.email.page.email_inbox.get_accounts', | |||
args: { | |||
'user': user | |||
}, | |||
callback:function(r){ | |||
me.page.sidebar.empty() | |||
if(!r.message) { | |||
frappe.msgprint(__("No Email Account assigned to you. Please contact your System Administrator")); | |||
setTimeout(function() { | |||
if (frappe.session.user==="Administrator") | |||
frappe.set_route("List", "User"); | |||
else | |||
frappe.set_route(''); | |||
}, 3000); | |||
} | |||
me.accounts = r.message; | |||
me.setup_inbox(); | |||
} | |||
}); | |||
}, | |||
refresh:function(){ | |||
delete frappe.route_flags.create_contact; | |||
delete frappe.route_flags.update_contact; | |||
this.run(); | |||
}, | |||
render_sidebar: function (data) { | |||
var me = this; | |||
frappe.call({ | |||
method: 'frappe.email.page.email_inbox.get_accounts', | |||
args:{user:frappe.user["name"]}, | |||
async:false, | |||
callback:function(list){ | |||
var buttons = '<div class="layout-main-section overlay-sidebar">'; | |||
if (list["message"]){ | |||
me.accounts = []; | |||
var rows = ""; | |||
for (var i = 0;i<list["message"].length;i++) | |||
{ | |||
rows += '<div class="list-row inbox-select"> <div class="row"><a class="inbox-item ellipsis col-md-12" title ="'+list["message"][i]["email_id"]+'" data-account="'+list["message"][i]["email_account"]+'" style="margin-left: 10px;">'+list["message"][i]["email_id"]+'</a> </div></div>'; | |||
me.accounts.push({name:list["message"][i]["email_account"],email:list["message"][i]["email_id"]}) | |||
} | |||
me.allaccounts = $.map(me.accounts,function(v){return v.name}).join(","); | |||
buttons += '<div class="list-row inbox-select list-row-head" style="font-weight:bold"> <div class="row"><a class="inbox-item ellipsis col-md-12 " title ="All Accounts" data-account="'+me.allaccounts+'" style="margin-left: 10px;">All Accounts</a> </div></div>'; | |||
buttons += rows; | |||
buttons += '<div class="list-row inbox-select"> <div class="row"><a class="inbox-item ellipsis col-md-12 " title ="Sent" data-account="Sent" style="margin-left: 10px;">Sent</a> </div></div>'; | |||
me.account = me.allaccounts; | |||
me.default_filters=[ | |||
["Communication", "communication_type", "=", "Communication"], | |||
["Communication", "email_account", "in", me.account], | |||
["Communication", "sent_or_received", "=", "Received"]] | |||
me.page.sidebar.empty().append(buttons); | |||
$(".inbox-select").on("click",function(btn){ | |||
me.account = $(btn.currentTarget).find(".inbox-item").data("account"); | |||
$(me.page.sidebar).find(".list-row").removeClass("list-row-head").css("font-weight","normal"); | |||
$(btn.currentTarget).closest(".list-row").addClass("list-row-head").css("font-weight","bold"); | |||
me.cur_page = 1; | |||
$(me.page.main).find(".list-select-all,.list-row-checkbox").prop("checked",false); | |||
me.toggle_actions(); | |||
if(me.account=="Sent"){ | |||
me.filter_list.default_filters=[ | |||
["Communication", "communication_type", "=", "Communication"], | |||
["Communication", "sent_or_received", "=", "Sent"]] | |||
}else { | |||
me.filter_list.default_filters = [ | |||
["Communication", "communication_type", "=", "Communication"], | |||
["Communication", "email_account", "in", me.account], | |||
["Communication", "sent_or_received", "=", "Received"]]; | |||
} | |||
me.filter_list.clear_filters(); | |||
if (me.filter_list.reload_stats){me.filter_list.reload_stats()} | |||
me.refresh(); | |||
}); | |||
} | |||
} | |||
}) | |||
}, | |||
toggle_accounts: function() { | |||
$(this.page.main).find(".list-select-all,.list-delete").prop("checked", false); | |||
this.toggle_actions(); | |||
this.filter_list.clear_filters(); | |||
this.refresh(); | |||
}, | |||
render_headers: function(){ | |||
$(".layout-main-section-wrapper").css("padding-left","0px").css("padding-right","0px"); | |||
var data = { | |||
"start":this.start, | |||
"page_length":this.page_length.toString() | |||
}; | |||
headers_html = frappe.render_template("inbox_headers", data) | |||
this.list_header = $(headers_html).appendTo(this.page.main.find(".list-headers")); | |||
}, | |||
get_args: function(){ | |||
var args = { | |||
doctype: this.doctype, | |||
fields: [ | |||
"name", "sender", "sender_full_name", "communication_date", "recipients", | |||
"cc","communication_medium", "subject", "status" ,"reference_doctype", | |||
"reference_name", "timeline_doctype", "timeline_name", "timeline_label", | |||
"sent_or_received", "uid", "message_id", "seen" | |||
], | |||
filters: this.get_email_filters(), | |||
order_by: 'communication_date desc', | |||
save_list_settings: false | |||
}; | |||
return args; | |||
}, | |||
get_email_filters: function() { | |||
filters = this.filter_list.get_filters() | |||
if(this.account == "Sent") { | |||
this.filter_list.default_filters = [ | |||
["Communication", "communication_type", "=", "Communication"], | |||
["Communication", "communication_medium", "=", "Email"], | |||
["Communication", "user", "=", user], | |||
["Communication", "sent_or_received", "=", "Sent"] | |||
] | |||
} else { | |||
this.filter_list.default_filters = [ | |||
["Communication", "communication_type", "=", "Communication"], | |||
["Communication", "communication_medium", "=", "Email"], | |||
["Communication", "sent_or_received", "=", "Received"], | |||
["Communication", "email_account", "in", this.account], | |||
] | |||
} | |||
$.extend(filters, this.filter_list.default_filters) | |||
return filters; | |||
}, | |||
setup_notifications: function() { | |||
// setup real time email notification using frappe.realtime | |||
var me = this; | |||
frappe.realtime.on("new_email", function(data) { | |||
for(var i =0; i<me.accounts.length; i++) { | |||
if (data.account == me.accounts[i].name) { | |||
frappe.utils.notify(data.account, "you have "+data.number+" new emails", {}, function () { | |||
window.focus(); | |||
me.account = data.account; | |||
$(me.page.sidebar).find(".list-row").removeClass("list-row-head").css("font-weight", "normal"); | |||
$('.inbox-item[data-account="' + data.account + '" ]').closest(".list-row").addClass("list-row-head").css("font-weight","bold"); | |||
me.refresh(); | |||
}); | |||
if(!me.fresh &&(data.account == me.account || me.account == me.all_accounts)) { | |||
me.fresh = true; | |||
me.refresh(); | |||
} | |||
} | |||
} | |||
me.fresh = false | |||
}); | |||
}, | |||
render_list: function(data) { | |||
var me = this | |||
$(me.wrapper).find(".result-list").empty(); | |||
$.each(data, function(idx, email) { | |||
$(frappe.render_template("inbox_list", {data: email})) | |||
.data("data", email) | |||
.appendTo($(me.wrapper).find(".result-list")) | |||
}); | |||
//click action | |||
$(me.wrapper).find(".result-list").find(".list-row").click(function(btn) { | |||
if ($(btn.target).hasClass("noclick")) | |||
return | |||
var row = $(btn.target).closest(".list-row").data("data"); | |||
if($(btn.target).hasClass("relink-link")){ | |||
me.relink(row); | |||
return | |||
} | |||
if(me.account != "Sent") { | |||
if ($(btn.target).hasClass("company-link")) { | |||
me.company_select(row, true); | |||
return | |||
} | |||
} | |||
me.email_open(row); | |||
}); | |||
}, | |||
company_select: function(row, nomatch) { | |||
var me = this; | |||
var fields = [ | |||
{ | |||
"fieldtype": "Heading", | |||
"label": __("Create new Contact to Match Email Address"), | |||
"fieldname": "Option1" | |||
}, | |||
{ | |||
"fieldtype": "Button", | |||
"label": __("Create/Add new Contact"), | |||
"fieldname":"newcontact", | |||
"description": __('Create new Contact for a Customer, Supplier, User or Organisation to Match "') + row.sender + __('" Against') | |||
} | |||
]; | |||
if (!nomatch) { | |||
fields.push({ | |||
"fieldtype": "Heading", | |||
"label": __("Do not Match"), | |||
"fieldname": "Option3" | |||
}); | |||
fields.push({ | |||
"fieldtype": "Button", | |||
"label": __("Do not Match"), | |||
"fieldname":"nomatch" | |||
}) | |||
} | |||
var d = new frappe.ui.Dialog ({ | |||
title: __("Match Emails to a Company"), | |||
fields: fields | |||
}); | |||
d.get_input("newcontact").on("click", function (frm) { | |||
d.hide(); | |||
delete frappe.route_flags.update_contact; | |||
frappe.route_flags.create_contact = 1; | |||
var name_split = row.sender_full_name?row.sender_full_name.split(' '):["",""]; | |||
row.nomatch = 1; | |||
frappe.route_options = { | |||
"email_id": row.sender, | |||
"first_name": name_split[0], | |||
"last_name":name_split[name_split.length-1], | |||
"status": "Passive" | |||
}; | |||
frappe.model.with_doctype("Contact", function() { | |||
var doc = frappe.model.get_new_doc("Contact"); | |||
frappe.set_route("Form", "Contact", doc.name); | |||
}) | |||
}); | |||
if (!nomatch) { | |||
d.get_input("nomatch").on("click", function (frm) { | |||
d.hide(); | |||
frappe.call({ | |||
method: 'frappe.email.page.email_inbox.setnomatch', | |||
args: { | |||
name: row.name | |||
} | |||
}); | |||
row.nomatch = 1; | |||
if (!nomatch) { | |||
me.email_open(row) | |||
} | |||
}); | |||
} | |||
d.show(); | |||
}, | |||
email_open: function(row) { | |||
var me = this; | |||
me.actions_opened = false; | |||
if(me.open_email == row.name){ | |||
return | |||
} | |||
me.open_email = row.name | |||
//mark email as read | |||
if(me.account!="Sent") { | |||
this.mark_read(row); | |||
} | |||
//start of open email | |||
var emailitem = new frappe.ui.Dialog ({ | |||
title: __(row.subject), | |||
fields: [{ | |||
"fieldtype": "HTML", | |||
"fieldname": "email" | |||
}] | |||
}); | |||
//prompt for match | |||
if (!row.timeline_label && !row.nomatch && me.account!="Sent") { | |||
setTimeout(function () { | |||
if (frappe.ui.open_dialogs.indexOf(emailitem) != -1 && !me.actions_opened) { | |||
me.company_select(row) | |||
}}, 4000); | |||
} | |||
var c = me.prepare_email(row); | |||
emailitem.fields_dict.email.$wrapper.html(frappe.render_template("inbox_email", {data:c})); | |||
$(emailitem.$wrapper).find(".reply").find("a").attr("target", "_blank"); | |||
//Action buttons | |||
$(emailitem.$wrapper).find(".text-right").prepend(frappe.render_template("inbox_email_actions",{data:row})).on("click", function () { | |||
me.actions_opened = true; | |||
}); | |||
$(emailitem.$wrapper).find(".relink-link").on("click", function () { | |||
me.relink(row); }); | |||
$(emailitem.$wrapper).find(".delete-link").on("click", function () { | |||
me.delete_email({n:row.name, u:row.uid}); | |||
emailitem.hide() | |||
}); | |||
$(emailitem.$wrapper).find(".company-link").on("click", function () { | |||
me.company_select(row, true)}); | |||
me.add_reply_btn_event(emailitem, c); | |||
//adjust sizing | |||
$(".modal-dialog").addClass("modal-lg"); | |||
$(emailitem.$wrapper).find(".modal-title") | |||
.parent() | |||
.removeClass("col-xs-7") | |||
.addClass("col-xs-7 col-sm-8 col-md-9"); | |||
$(emailitem.$wrapper).find(".text-right") | |||
.parent() | |||
.removeClass("col-xs-5") | |||
.addClass("col-xs-5 col-sm-4 col-md-3"); | |||
//setup close | |||
emailitem.onhide = function() { | |||
me.open_email = null | |||
} | |||
emailitem.show(); | |||
}, | |||
add_reply_btn_event: function (emailitem, c) { | |||
var me = this; | |||
//reply | |||
$(emailitem.$wrapper).find(".reply-link").on("click", function () { | |||
var sender = ""; | |||
for (var i=0;i<me.accounts.length;i++){ | |||
if(me.accounts[i].name===me.account){ | |||
sender = me.accounts[i].email; | |||
break; | |||
} | |||
} | |||
new frappe.views.CommunicationComposer({ | |||
doc: { | |||
doctype: c.reference_doctype, | |||
name: c.reference_name | |||
}, | |||
sender: sender, | |||
subject: "Re: " + c.subject, | |||
recipients: c.sender, | |||
last_email: c, | |||
attachments: c.attachments | |||
}); | |||
}); | |||
//reply-all | |||
$(emailitem.$wrapper).find(".reply-all-link").on("click", function () { | |||
var sender = ""; | |||
for (var i=0;i<me.accounts.length;i++){ | |||
if(me.accounts[i].name===me.account){ | |||
sender = me.accounts[i].email; | |||
break; | |||
} | |||
} | |||
new frappe.views.CommunicationComposer({ | |||
doc: { | |||
doctype: c.reference_doctype, | |||
name: c.reference_name | |||
}, | |||
sender:sender, | |||
subject: "Re: " + c.subject, | |||
recipients: (c.sender + (c.recipients ? ", "+c.recipients:"") + (c.cc ? ", "+c.cc:"")).replace(sender,""), | |||
last_email: c, | |||
attachments:c.attachments | |||
}); | |||
}); | |||
//forward | |||
$(emailitem.$wrapper).find(".forward-link").on("click", function () { | |||
var sender = ""; | |||
for (var i=0;i<me.accounts.length;i++){ | |||
if(me.accounts[i].name===me.account){ | |||
sender = me.accounts[i].email; | |||
break; | |||
} | |||
} | |||
var communication = new frappe.views.CommunicationComposer({ | |||
doc: { | |||
doctype: c.reference_doctype, | |||
name: c.reference_name | |||
}, | |||
sender:sender, | |||
subject: "FW: " + c.subject, | |||
last_email: c, | |||
forward:true, | |||
attachments:c.attachments | |||
}); | |||
$(communication.dialog.fields_dict.select_attachments.wrapper).find("input[type=checkbox]").prop("checked",true) | |||
}); | |||
}, | |||
relink: function(row){ | |||
var me = this; | |||
var callback = function(frm){ | |||
$(me.wrapper).find(".row-named[data-name="+row.name+"]").find(".reference-document") | |||
.html(values["reference_name"]) | |||
.attr("href",'#Form/'+values["reference_doctype"]+ '/'+values["reference_name"]) | |||
.attr("title","Linked Doctype: "+values["reference_doctype"]); | |||
row.reference_doctype = values["reference_doctype"]; | |||
row.reference_name = values["reference_name"]; | |||
} | |||
frappe.timeline.relink_dialog(row.name, row.reference_doctype, row.reference_name, callback); | |||
}, | |||
prepare_email: function(c){ | |||
var me = this; | |||
frappe.call({ | |||
method:'frappe.email.page.email_inbox.get_email_content', | |||
args:{ | |||
doctype:"Communication", | |||
name:c.name | |||
}, | |||
async:false, | |||
callback:function(r){ | |||
c.attachments =r["message"][0]; | |||
c.content = r["message"][1]; | |||
} | |||
}); | |||
c.doctype = "Communication"; | |||
c.comment_on = comment_when(c.communication_date); | |||
if (c.attachments && typeof c.attachments === "string") | |||
c.attachments = JSON.parse(c.attachments); | |||
if (!c.comment_type) | |||
c.comment_type = "Email"; | |||
c.comment = c.content; | |||
if (c.comment_type == "Email") { | |||
c.comment = c.comment.split("<!-- original-reply -->")[0]; | |||
c.comment = frappe.utils.strip_original_content(c.comment); | |||
c.comment = frappe.dom.remove_script_and_style(c.comment); | |||
c.original_comment = c.comment; | |||
c.comment = frappe.utils.toggle_blockquote(c.comment); | |||
} | |||
if (!frappe.utils.is_html(c.comment)) { | |||
c.comment_html = frappe.markdown(__(c.comment)); | |||
} else { | |||
c.comment_html = c.comment; | |||
c.comment_html = frappe.utils.strip_whitespace(c.comment_html); | |||
c.comment_html = c.comment_html.replace(/</g,"<").replace(/>/g,">") | |||
} | |||
// bold @mentions | |||
if (c.comment_type === "Comment") { | |||
c.comment_html = c.comment_html.replace(/(^|\W)(@\w+)/g, "$1<b>$2</b>"); | |||
} | |||
return c | |||
}, | |||
init_select_all: function () { | |||
var me = this; | |||
$(".list-select-all").on("click", function () { | |||
$(me.wrapper).find('.list-row-checkbox').prop("checked", $(this).prop("checked")); | |||
me.toggle_actions(); | |||
}); | |||
$(me.wrapper).on("click", ".list-row-checkbox", function (event) { | |||
me.toggle_actions(); | |||
// multi-select using shift key | |||
var $this = $(this); | |||
if (event.shiftKey && $this.prop("checked")) { | |||
var $end_row = $this.parents(".list-row"); | |||
var $start_row = $end_row.prevAll(".list-row") | |||
.find(".list-row-checkbox:checked").last().parents(".list-row"); | |||
if ($start_row) { | |||
$start_row.nextUntil($end_row).find(".list-row-checkbox").prop("checked", true); | |||
} | |||
} | |||
}); | |||
// after delete, hide delete button | |||
me.toggle_actions(); | |||
}, | |||
render_buttons: function(){ | |||
var me = this; | |||
me.page.add_action_item("Delete", function(){ me.delete_email() }); | |||
me.page.add_action_item("Mark as Unread", function(){ me.mark_unread() }); | |||
me.page.add_action_item("Mark as Read", function(){ me.mark_read() }); | |||
me.page.set_primary_action("New Email", function(){ | |||
var sender = ""; | |||
for (var i=0;i<me.accounts.length;i++){ | |||
if(me.accounts[i].name===me.account){ | |||
sender = me.accounts[i].email | |||
break; | |||
} | |||
} | |||
new frappe.views.CommunicationComposer({ | |||
doc: {}, | |||
sender: sender | |||
}); | |||
}, "fa-plus", "New Email"); | |||
}, | |||
toggle_actions: function () { | |||
var me = this; | |||
if (me.page.main.find(".list-row-checkbox:checked").length) { | |||
//show buttons | |||
$(me.page.actions_btn_group).show(); | |||
$(me.page.btn_primary).hide() | |||
} else { | |||
//hide button | |||
$(me.page.actions_btn_group).hide(); | |||
$(me.page.btn_primary).show() | |||
} | |||
}, | |||
delete_email: function(data){ | |||
var me = this; | |||
if (!data) { | |||
var names = $.map(me.action_checked_items('.data("data")'), function(v){ | |||
return {name:v.name, uid:v.uid} | |||
}); | |||
me.action_checked_items('.remove()') | |||
} else { | |||
var names = [{name:data.name, uid:data.uid}] | |||
} | |||
}, | |||
mark_unread: function(){ | |||
var me = this; | |||
var names = $.map(me.action_checked_items('.data("data")'), function(v){ | |||
return {name:v.name, uid:v.uid} | |||
}); | |||
me.create_flag_queue(names, "Unread", "SEEN"); | |||
me.action_checked_items('.css("font-weight", "BOLD")'); | |||
me.update_local_flags(names, "seen", "0") | |||
}, | |||
mark_read: function(data){ | |||
var me = this; | |||
if (!data) { | |||
var names = $.map(me.action_checked_items('.data("data")'), function(v){return {name:v.name, uid:v.uid}}); | |||
me.action_checked_items('.css("font-weight", "normal")') | |||
} else { | |||
var names = [{name:data.name, uid:data.uid}]; | |||
$(".row-named").filter("[data-name=" + data.name + "]").css("font-weight", "normal") | |||
} | |||
me.create_flag_queue(names, "Read", "SEEN"); | |||
me.update_local_flags(names, "seen", "1"); | |||
}, | |||
create_flag_queue: function(names, action, flag){ | |||
frappe.call({ | |||
method: 'frappe.email.page.email_inbox.create_flag_queue', | |||
args:{ | |||
names: JSON.stringify(names), | |||
action: action, | |||
flag: flag | |||
} | |||
}) | |||
}, | |||
update_local_flags:function(names,field,val){ | |||
frappe.call({ | |||
method: 'frappe.email.page.email_inbox.update_local_flags', | |||
args:{ | |||
names: JSON.stringify(names), | |||
field: field, | |||
val: val | |||
} | |||
}) | |||
$('.list-row-checkbox:checked').prop( "checked", false ); | |||
}, | |||
action_checked_items: function(action) { | |||
return $.map(this.page.main.find('.list-row-checkbox:checked'), function(e) { | |||
return eval('$(e).closest(".row-named")'+action); | |||
}); | |||
}, | |||
}); |
@@ -1,19 +0,0 @@ | |||
<ul class="list-unstyled sidebar-menu user-actions"> | |||
<li class="divider"></li> | |||
</ul> | |||
<ul class="list-unstyled sidebar-menu standard-actions"> | |||
<li class="divider"></li> | |||
<li class="list-link" data-view="List"> | |||
<a class="email-account" data-account="Sent">{%= __("Sent") %}</a> | |||
</li> | |||
</ul> | |||
<ul class="list-unstyled sidebar-menu standard-actions"> | |||
<li class="divider"></li> | |||
<li class="list-link"><h6>Email Accounts</h6></li> | |||
{% for(var i=0, l=data.length; i<l; i++) { %} | |||
<li class="divider"></li> | |||
<li class="list-link" data-view="List"> | |||
<a class="email-account {%= i==0?"strong": "" %}" data-account="{%= data[i].account %}">{%= __(data[i].title) %}</a> | |||
</li> | |||
{% } %} | |||
</ul> |
@@ -1,45 +0,0 @@ | |||
<div class="list-row row-named" data-name="{%= data.name %}" style=" {%= data.seen ? "font-weight:normal" : "font-weight:bold" %}"> | |||
<div class="row doclist-row has-checkbox"> | |||
<div class="col-mg-5 col-sm-12 list-row-left"> | |||
<div class="row"> | |||
<div class="col-sm-2 list-col ellipsis col-xs-12"> | |||
<span class="list-value"> | |||
<input class="list-row-checkbox noclick" type="checkbox" style="margin: 0 7px 0 0; vertical-align: middle;"> | |||
{% if (data.sender_full_name){var sender = data.sender_full_name} else {var sender = data.sender} %} | |||
<a class="grey" title="{%= sender %}">{%= sender %}</a> | |||
</span> | |||
</div> | |||
<div class="col-sm-4 list-col ellipsis col-xs-12"> | |||
<span class="list-value"> | |||
<a class="grey" title="{%= data.name %}">{%= data.subject %}</a> | |||
</span> | |||
</div> | |||
<div class="col-sm-2 list-col ellipsis hidden-xs"> | |||
<span class="list-value"> | |||
<a class="h6 text-muted grey noclick" data-filter="timeline_doctype,=,{%= data.timeline_doctype %}" title="Linked Doctype: {%= data.reference_doctype %}" href="#Form/{%= data.timeline_doctype %}/{%= data.timeline_name %}">{%= data.timeline_label%}</a> | |||
</span> | |||
</div> | |||
<div class="col-sm-2 list-col ellipsis hidden-xs"> | |||
<span class="list-value"> | |||
<i class="h6 fa fa-link text-muted relink-link pull-right" style="padding: 0px 0px; margin-right: 10px; font-size: 1.1em;"></i> | |||
<a class="text-muted grey noclick reference-document" title="Linked Doctype: {%= data.reference_doctype %}" href="#Form/{%= data.reference_doctype %}/{%= data.reference_name %}">{%= data.reference_name %}</a> | |||
</span> | |||
</div> | |||
<div class="col-sm-1 list-col ellipsis hidden-xs"> | |||
<span class="list-value"> | |||
<span> | |||
{% if(data.has_attachment){ %} | |||
<i class="fa fa-paperclip fa-large"></i> | |||
{% } %} | |||
</span> | |||
</span> | |||
</div> | |||
<div class="col-sm-1 list-col ellipsis hidden-xs" > | |||
<span class="list-value"> | |||
{%= comment_when(data.communication_date,true) %} | |||
</span> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -171,3 +171,4 @@ execute:frappe.rename_doc('Country', 'Syrian Arab Republic', 'Syria', ignore_if_ | |||
frappe.patches.v8_0.rename_listsettings_to_usersettings | |||
frappe.patches.v7_2.update_communications | |||
frappe.patches.v8_0.deprecate_integration_broker | |||
frappe.patches.v8_0.setup_email_inbox |
@@ -0,0 +1,22 @@ | |||
import frappe, json | |||
def execute(): | |||
""" | |||
depricate email inbox page if exists | |||
remove desktop icon for email inbox page if exists | |||
patch to remove Custom DocPerm for communication | |||
""" | |||
if frappe.db.exists("Page", "email_inbox"): | |||
frappe.delete_doc("Page", "email_inbox") | |||
desktop_icon = frappe.db.get_value("Desktop Icon", { | |||
"module_name": "Email", | |||
"type": "Page", | |||
"link": "email_inbox" | |||
}) | |||
if desktop_icon: | |||
frappe.delete_doc("Desktop Icon", desktop_icon) | |||
frappe.db.sql("""update `tabCustom DocPerm` set `write`=0, email=1 where parent='Communication'""") |
@@ -255,6 +255,8 @@ | |||
"public/js/frappe/views/image/image_view_item_main_head.html", | |||
"public/js/frappe/views/image/photoswipe_dom.html", | |||
"public/js/frappe/views/inbox/inbox_no_result.html", | |||
"public/js/frappe/views/inbox/select_email_inbox.html", | |||
"public/js/frappe/views/inbox/inbox_view_item_row.html", | |||
"public/js/frappe/views/inbox/inbox_view_item_main_head.html", | |||
@@ -595,3 +595,6 @@ select.form-control { | |||
box-shadow: none; | |||
} | |||
} | |||
body[data-route^="Form/Communication"] textarea[data-fieldname="subject"] { | |||
height: 80px !important; | |||
} |
@@ -374,3 +374,10 @@ | |||
.gantt .details-container .standard-image { | |||
display: block; | |||
} | |||
.inbox-attachment, | |||
.inbox-link { | |||
margin-right: 7px; | |||
} | |||
.select-inbox { | |||
padding: 30px 30px; | |||
} |
@@ -13,9 +13,15 @@ | |||
</div> | |||
</div> | |||
<div class="timeline-new-email"> | |||
<button class="btn btn-default btn-new-email btn-xs"> | |||
{% if(doctype === "Communication") { %} | |||
<button class="btn btn-default btn-reply-email btn-xs"> | |||
{%= __("Reply") %} | |||
</button> | |||
{% } else { %} | |||
<button class="btn btn-default btn-new-email btn-xs"> | |||
{%= __("New Email") %} | |||
</button> | |||
{% } %} | |||
</div> | |||
<div class="timeline-items"> | |||
@@ -11,7 +11,7 @@ frappe.ui.form.Timeline = Class.extend({ | |||
make: function() { | |||
var me = this; | |||
this.wrapper = $(frappe.render_template("timeline", | |||
{})).appendTo(this.parent); | |||
{doctype: this.frm.doctype})).appendTo(this.parent); | |||
this.list = this.wrapper.find(".timeline-items"); | |||
this.input = this.wrapper.find(".form-control"); | |||
@@ -35,15 +35,7 @@ frappe.ui.form.Timeline = Class.extend({ | |||
} | |||
}); | |||
this.email_button = this.wrapper.find(".btn-new-email") | |||
.on("click", function() { | |||
new frappe.views.CommunicationComposer({ | |||
doc: me.frm.doc, | |||
txt: frappe.markdown(me.input.val()), | |||
frm: me.frm, | |||
recipients: me.get_recipient() | |||
}) | |||
}); | |||
this.setup_email_button(); | |||
this.list.on("click", ".toggle-blockquote", function() { | |||
$(this).parent().siblings("blockquote").toggleClass("hidden"); | |||
@@ -82,6 +74,33 @@ frappe.ui.form.Timeline = Class.extend({ | |||
}, | |||
setup_email_button: function() { | |||
var me = this; | |||
selector = this.frm.doctype === "Communication"? ".btn-reply-email": ".btn-new-email" | |||
this.email_button = this.wrapper.find(selector) | |||
.on("click", function() { | |||
args = { | |||
doc: me.frm.doc, | |||
frm: me.frm, | |||
recipients: me.get_recipient() | |||
} | |||
if(me.frm.doctype === "Communication") { | |||
$.extend(args, { | |||
txt: "", | |||
last_email: me.frm.doc, | |||
recipients: me.frm.doc.sender, | |||
subject: __("Re: {0}", [me.frm.doc.subject]), | |||
}); | |||
} else { | |||
$.extend(args, { | |||
txt: frappe.markdown(me.input.val()) | |||
}); | |||
} | |||
new frappe.views.CommunicationComposer(args) | |||
}); | |||
}, | |||
refresh: function(scroll_to_end) { | |||
var me = this; | |||
@@ -524,5 +524,20 @@ frappe.views.ListRenderer = Class.extend({ | |||
render_icon: function (parent, icon_class, label) { | |||
var icon_html = `<i class='${icon_class}' title='${__(label) || ''}'></i>`; | |||
$(parent).append(icon_html); | |||
} | |||
}, | |||
make_no_result: function () { | |||
var new_button = frappe.boot.user.can_create.includes(this.doctype) | |||
? (`<p><button class='btn btn-primary btn-sm' | |||
list_view_doc='${this.doctype}'> | |||
${__('Make a new ' + __(this.doctype))} | |||
</button></p>`) | |||
: ''; | |||
var no_result_message = | |||
`<div class='msg-box no-border'> | |||
<p>${__('No {0} found', [__(this.doctype)])}</p> | |||
${new_button} | |||
</div>`; | |||
return no_result_message; | |||
}, | |||
}); |
@@ -40,7 +40,6 @@ | |||
{{ __("Email Inbox") }} <span class="caret"></span> | |||
</a> | |||
<ul class="dropdown-menu email-account-dropdown" style="max-height: 300px; overflow-y: auto;"> | |||
<li class="new-email-account"><a>{{ __("New Email Account") }}</a></li> | |||
</ul> | |||
</div> | |||
</li> | |||
@@ -247,6 +247,12 @@ frappe.views.ListSidebar = Class.extend({ | |||
var $dropdown = this.page.sidebar.find('.email-account-dropdown'); | |||
var divider = false; | |||
if(has_common(roles, ["System Manager", "Administrator"])) { | |||
$('<li class="new-email-account"><a>'+ __("New Email Account") +'</a></li>') | |||
.appendTo($dropdown) | |||
} | |||
accounts = frappe.boot.email_accounts; | |||
accounts.forEach(function(account) { | |||
@@ -158,6 +158,7 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
this.init_filters(); | |||
this.set_title(); | |||
this.init_headers(); | |||
this.no_result_message = this.list_renderer.make_no_result() | |||
}, | |||
setup_list_renderer: function () { | |||
@@ -327,7 +328,7 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
page_length: this.list_renderer.page_length, | |||
show_filters: false, | |||
new_doctype: this.doctype, | |||
no_result_message: this.make_no_result(), | |||
no_result_message: this.list_renderer.make_no_result(), | |||
show_no_result: function() { | |||
return me.list_renderer.show_no_result; | |||
} | |||
@@ -346,8 +347,12 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
if (this.list_renderer.settings.list_view_doc) { | |||
this.list_renderer.settings.list_view_doc(this); | |||
} else { | |||
$(this.wrapper).on('click', `button[list_view_doc='${this.doctype}']`, function () { | |||
me.make_new_doc.apply(me, [me.doctype]); | |||
doctype = this.list_renderer.no_result_doctype? this.list_renderer.no_result_doctype: this.doctype | |||
$(this.wrapper).on('click', `button[list_view_doc='${doctype}']`, function () { | |||
if (me.list_renderer.make_new_doc) | |||
me.list_renderer.make_new_doc() | |||
else | |||
me.make_new_doc.apply(me, [me.doctype]); | |||
}); | |||
} | |||
}, | |||
@@ -492,22 +497,6 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
}) | |||
}, | |||
make_no_result: function () { | |||
var new_button = frappe.boot.user.can_create.includes(this.doctype) | |||
? (`<p><button class='btn btn-primary btn-sm' | |||
list_view_doc='${this.doctype}'> | |||
${__('Make a new ' + __(this.doctype))} | |||
</button></p>`) | |||
: ''; | |||
var no_result_message = | |||
`<div class='msg-box no-border'> | |||
<p>${__('No {0} found', [__(this.doctype)])}</p> | |||
${new_button} | |||
</div>`; | |||
return no_result_message; | |||
}, | |||
get_args: function () { | |||
var args = { | |||
doctype: this.doctype, | |||
@@ -83,6 +83,7 @@ frappe.ui.notifications.config = { | |||
"ToDo": { label: __("To Do") }, | |||
"Chat": { label: __("Chat"), route: "chat"}, | |||
"Event": { label: __("Calendar"), route: "List/Event/Calendar" }, | |||
"Email": { label: __("Email"), route: "List/Communication/Inbox" }, | |||
"Likes": { | |||
label: __("Likes"), | |||
click: function() { | |||
@@ -86,13 +86,12 @@ frappe.views.CommunicationComposer = Class.extend({ | |||
// add from if user has access to multiple email accounts | |||
email_accounts = frappe.boot.email_accounts.filter(function(account, idx){ | |||
return !inList(["All Accounts", "Sent"], account.email_account) | |||
return !inList(["All Accounts", "Sent", "Spam", "Trash"], account.email_account) | |||
}) | |||
if(frappe.boot.email_accounts && email_accounts.length > 1) { | |||
fields = [ | |||
{label: __("From"), fieldtype: "Select", reqd: 1, fieldname: "sender", | |||
options: accounts.map(function(e) { return e.email_id; }) } | |||
options: email_accounts.map(function(e) { return e.email_id; }) } | |||
].concat(fields); | |||
} | |||
@@ -0,0 +1,8 @@ | |||
<div class="msg-box no-border"> | |||
<p>{{ __("No {} found", [doctype]) }}</p> | |||
<p> | |||
<button class="btn btn-primary btn-sm btn-no-result" list_view_doc="{{ doctype }}"> | |||
{{__(label) }} | |||
</button> | |||
</p> | |||
</div> |
@@ -8,27 +8,35 @@ frappe.views.InboxView = frappe.views.ListRenderer.extend({ | |||
name: 'Inbox', | |||
render_view: function(values) { | |||
var me = this; | |||
var email_account = this.get_email_account(); | |||
this.emails = values; | |||
// save email account in user_settings | |||
frappe.model.user_settings.save("Communication", 'Inbox', { | |||
last_email_account: email_account | |||
last_email_account: this.current_email_account | |||
}); | |||
this.render_inbox_view(); | |||
}, | |||
render_inbox_view: function() { | |||
var html = this.emails.map(this.render_email_row.bind(this)).join(""); | |||
var html = "" | |||
email_account = this.get_current_email_account() | |||
if(email_account) | |||
html = this.emails.map(this.render_email_row.bind(this)).join(""); | |||
else | |||
html = this.get_inbox_selector_html() | |||
this.container = $('<div>') | |||
.addClass('inbox-container') | |||
.appendTo(this.wrapper); | |||
this.container.append(html); | |||
if(!this.current_email_account) | |||
this.bind_email_inbox_selector() | |||
}, | |||
render_email_row: function(email) { | |||
if(!email.css_seen && email.seen) | |||
email.css_seen = "seen" | |||
if(email.has_attachment) | |||
email.attachment_html = '<span class="text-muted"><i class="fa fa-paperclip fa-large"></i></span>' | |||
return frappe.render_template("inbox_view_item_row", { | |||
data: email, | |||
@@ -37,19 +45,19 @@ frappe.views.InboxView = frappe.views.ListRenderer.extend({ | |||
}, | |||
set_defaults: function() { | |||
this._super(); | |||
this.show_no_result = false; | |||
this.page_title = __("Email Inbox"); | |||
}, | |||
init_settings: function() { | |||
this._super(); | |||
// this.show_no_result = false; | |||
this.filters = this.get_inbox_filters(); | |||
}, | |||
should_refresh: function() { | |||
var to_refresh = this._super(); | |||
if(!to_refresh) { | |||
this.last_email_account = this.current_email_account || ''; | |||
this.current_email_account = this.get_email_account(); | |||
this.current_email_account = this.get_current_email_account(); | |||
this.is_sent_emails = this.current_email_account === "Sent"? true: false | |||
to_refresh = this.current_email_account !== this.last_email_account; | |||
@@ -57,44 +65,115 @@ frappe.views.InboxView = frappe.views.ListRenderer.extend({ | |||
if(to_refresh){ | |||
this.list_view.page.main.find(".list-headers").empty(); | |||
this.list_view.init_headers(); | |||
} | |||
return to_refresh; | |||
}, | |||
get_inbox_filters: function() { | |||
var email_account = this.get_email_account(); | |||
var email_account = this.get_current_email_account(); | |||
var default_filters = [ | |||
["Communication", "communication_type", "=", "Communication", true], | |||
["Communication", "communication_medium", "=", "Email", true], | |||
] | |||
var filters = [] | |||
if (email_account === "Sent") | |||
if (email_account === "Sent") { | |||
filters = default_filters.concat([ | |||
["Communication", "sent_or_received", "=", "Sent", true] | |||
["Communication", "sent_or_received", "=", "Sent", true], | |||
["Communication", "email_status", "not in", "Spam,Trash", true], | |||
]) | |||
else | |||
} | |||
else if (inList(["Spam", "Trash"], email_account)) { | |||
filters = default_filters.concat([ | |||
["Communication", "email_status", "=", email_account, true], | |||
["Communication", "email_account", "in", frappe.boot.all_accounts, true] | |||
]) | |||
} | |||
else { | |||
filters = default_filters.concat([ | |||
["Communication", "sent_or_received", "=", "Received", true], | |||
["Communication", "email_account", "=", email_account, true] | |||
["Communication", "email_account", "=", email_account, true], | |||
["Communication", "email_status", "not in", "Spam,Trash", true], | |||
]) | |||
} | |||
return filters | |||
}, | |||
get_header_html: function() { | |||
var header = frappe.render_template('inbox_view_item_main_head', { | |||
_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable) | |||
&& !this.no_delete), | |||
is_sent_emails: this.is_sent_emails | |||
}); | |||
var header = "" | |||
if(this.current_email_account) { | |||
header = frappe.render_template('inbox_view_item_main_head', { | |||
_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable) | |||
&& !this.no_delete), | |||
is_sent_emails: this.is_sent_emails | |||
}); | |||
} | |||
return header; | |||
}, | |||
get_email_account: function() { | |||
get_current_email_account: function() { | |||
var route = frappe.get_route(); | |||
if(!route[3] || !frappe.boot.email_accounts.find(b => b.email_account === route[3])) { | |||
frappe.throw(__(`Email Account <b>${route[3] || ''}</b> not found`)); | |||
return; | |||
// frappe.throw(__(`Email Account <b>${route[3] || ''}</b> not found`)); | |||
return ""; | |||
} | |||
return route[3]; | |||
}, | |||
make_no_result: function () { | |||
var no_result_message = "" | |||
email_account = this.get_current_email_account(); | |||
if (inList(["Spam", "Trash"], email_account)) { | |||
return __("No {0} mail", [email_account]) | |||
} else if(!email_account) { | |||
// email account is not configured | |||
this.no_result_doctype = "Email Account" | |||
args = { | |||
doctype: "Email Account", | |||
label: "New Email Account" | |||
} | |||
} else { | |||
// no sent mail | |||
this.no_result_doctype = "Communication"; | |||
args = { | |||
doctype: "Communication", | |||
label: "Compose Email" | |||
} | |||
} | |||
var no_result_message = frappe.render_template("inbox_no_result", args) | |||
return no_result_message; | |||
}, | |||
get_inbox_selector_html: function() { | |||
email_account_map = {} | |||
$.each(frappe.boot.email_accounts, function(idx, account){ | |||
email_account_map[account.email_id] = account.email_account | |||
}); | |||
html = frappe.render_template("select_email_inbox", { | |||
email_accounts: email_account_map, | |||
current_email_account: this.current_email_account, | |||
is_system_manager: has_common(["System Manager", "Administrator"], roles), | |||
is_inbox_configured: Object.keys(email_account_map).length | |||
}) | |||
if(!Object.keys(email_account_map).length) | |||
this.no_result_doctype = "Email Account" | |||
return html | |||
}, | |||
bind_email_inbox_selector: function() { | |||
// bind email_account on_change event | |||
var me = this; | |||
this.container.find('select[data-fieldname="email_inbox"]').on("change", function(event) { | |||
inbox = $(event.target).val(); | |||
frappe.set_route("List", "Communication", "Inbox", inbox) | |||
}) | |||
}, | |||
make_new_doc: function() { | |||
if (this.no_result_doctype == "Communication") { | |||
new frappe.views.CommunicationComposer({ | |||
doc: {} | |||
}) | |||
} else { | |||
frappe.new_doc(this.no_result_doctype) | |||
} | |||
} | |||
}); |
@@ -21,10 +21,25 @@ | |||
</div> | |||
<div class="col-sm-2 col-xs-2 text-right list-row-right" style="padding-left:0px"> | |||
<div class="visible-xs"> | |||
{%= data.attachment_html %} | |||
<span class="text-muted inbox-attachment"> | |||
{% if(data.has_attachment) { %} | |||
<i class="fa fa-paperclip fa-large"></i> | |||
{% } %} | |||
</span> | |||
</div> | |||
<div class="hidden-xs"> | |||
{%= data.attachment_html %} | |||
<span class="text-muted inbox-attachment"> | |||
{% if(data.reference_doctype && data.reference_name) { %} | |||
<a class="text-muted grey" href="#Form/{%= data.reference_doctype %}/{%= data.reference_name %}"> | |||
<i class="fa fa-link fa-large"></i> | |||
</a> | |||
{% } %} | |||
</span> | |||
<span class="text-muted inbox-attachment"> | |||
{% if(data.has_attachment) { %} | |||
<i class="fa fa-paperclip fa-large"></i> | |||
{% } %} | |||
</span> | |||
<span class="list-row-modified text-muted"> | |||
{%= comment_when(data.modified, true) %} | |||
</span> | |||
@@ -0,0 +1,32 @@ | |||
<div class="no-result"> | |||
{% if(is_inbox_configured) { %} | |||
<div class="select-inbox no-border"> | |||
<div class="form-group"> | |||
<div class="clearfix"> | |||
<label class="control-label" style="padding-right: 0px;">Email Inbox</label> | |||
</div> | |||
<div class="control-input-wrapper"> | |||
<div class="control-input"> | |||
<select type="text" autocomplete="off" class="input-with-feedback form-control bold" maxlength="140" data-fieldtype="Select" data-fieldname="email_inbox" placeholder=""> | |||
<option value=""></option> | |||
{% for(var email_id in email_accounts) { %} | |||
<option value="{%= email_accounts[email_id] %}">{%= email_id %}</option> | |||
{% } %} | |||
</select> | |||
</div> | |||
<p class="help-box small text-muted hidden-xs">Please Select Email Inbox</p> | |||
</div> | |||
</div> | |||
</div> | |||
{% } else if(!is_system_manager) { %} | |||
<div class="msg-box no-border"> | |||
<p>Email Account is not assigned, Please contact System Manager</p> | |||
</div> | |||
{% } else { %} | |||
{%= frappe.render_template("inbox_no_result", { | |||
doctype: "Email Account", | |||
label: "Email Account", | |||
action: `frappe.new_doc("Email Account")` | |||
}) %} | |||
{% } %} | |||
</div> |
@@ -767,3 +767,7 @@ select.form-control { | |||
// } | |||
} | |||
} | |||
body[data-route^="Form/Communication"] textarea[data-fieldname="subject"] { | |||
height: 80px !important; | |||
} |
@@ -455,4 +455,12 @@ | |||
display: block; | |||
} | |||
} | |||
} | |||
.inbox-attachment, .inbox-link { | |||
margin-right: 7px; | |||
} | |||
.select-inbox { | |||
padding: 30px 30px; | |||
} |