浏览代码

Merge pull request #2869 from mbauskar/inbox-view

[enhance] communication inbox view
version-14
Nabin Hait 8 年前
committed by GitHub
父节点
当前提交
d45e5a6154
共有 38 个文件被更改,包括 715 次插入920 次删除
  1. +0
    -8
      frappe/config/desktop.py
  2. +64
    -6
      frappe/core/doctype/communication/communication.js
  3. +69
    -9
      frappe/core/doctype/communication/communication.json
  4. +27
    -13
      frappe/core/doctype/communication/communication.py
  5. +1
    -1
      frappe/core/doctype/communication/communication_list.js
  6. +17
    -0
      frappe/core/notifications.py
  7. +13
    -1
      frappe/core/page/desktop/desktop.js
  8. +0
    -1
      frappe/email/doctype/email_account/email_account.py
  9. +5
    -4
      frappe/email/doctype/email_flag_queue/email_flag_queue.json
  10. +0
    -0
      frappe/email/doctype/email_rule/__init__.py
  11. +8
    -0
      frappe/email/doctype/email_rule/email_rule.js
  12. +122
    -0
      frappe/email/doctype/email_rule/email_rule.json
  13. +10
    -0
      frappe/email/doctype/email_rule/email_rule.py
  14. +10
    -0
      frappe/email/doctype/email_rule/test_email_rule.py
  15. +96
    -6
      frappe/email/inbox.py
  16. +0
    -84
      frappe/email/page/email_inbox/__init__.py
  17. +0
    -665
      frappe/email/page/email_inbox/email_inbox.js
  18. +0
    -19
      frappe/email/page/email_inbox/email_inbox_sidebar.html
  19. +0
    -45
      frappe/email/page/email_inbox/inbox_list.html
  20. +1
    -0
      frappe/patches.txt
  21. +22
    -0
      frappe/patches/v8_0/setup_email_inbox.py
  22. +2
    -0
      frappe/public/build.json
  23. +3
    -0
      frappe/public/css/form.css
  24. +7
    -0
      frappe/public/css/list.css
  25. +7
    -1
      frappe/public/js/frappe/form/footer/timeline.html
  26. +29
    -10
      frappe/public/js/frappe/form/footer/timeline.js
  27. +16
    -1
      frappe/public/js/frappe/list/list_renderer.js
  28. +0
    -1
      frappe/public/js/frappe/list/list_sidebar.html
  29. +6
    -0
      frappe/public/js/frappe/list/list_sidebar.js
  30. +8
    -19
      frappe/public/js/frappe/list/list_view.js
  31. +1
    -0
      frappe/public/js/frappe/ui/toolbar/notifications.js
  32. +2
    -3
      frappe/public/js/frappe/views/communication.js
  33. +8
    -0
      frappe/public/js/frappe/views/inbox/inbox_no_result.html
  34. +100
    -21
      frappe/public/js/frappe/views/inbox/inbox_view.js
  35. +17
    -2
      frappe/public/js/frappe/views/inbox/inbox_view_item_row.html
  36. +32
    -0
      frappe/public/js/frappe/views/inbox/select_email_inbox.html
  37. +4
    -0
      frappe/public/less/form.less
  38. +8
    -0
      frappe/public/less/list.less

+ 0
- 8
frappe/config/desktop.py 查看文件

@@ -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"),


+ 64
- 6
frappe/core/doctype/communication/communication.js 查看文件

@@ -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")
}
})
}
});

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

+ 27
- 13
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

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":


+ 1
- 1
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"]],


+ 17
- 0
frappe/core/notifications.py 查看文件

@@ -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]

+ 13
- 1
frappe/core/page/desktop/desktop.js 查看文件

@@ -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,


+ 0
- 1
frappe/email/doctype/email_account/email_account.py 查看文件

@@ -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"


+ 5
- 4
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",


+ 0
- 0
frappe/email/doctype/email_rule/__init__.py 查看文件


+ 8
- 0
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) {

}
});

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

+ 10
- 0
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

+ 10
- 0
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

+ 96
- 6
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

+ 0
- 84
frappe/email/page/email_inbox/__init__.py 查看文件

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

+ 0
- 665
frappe/email/page/email_inbox/email_inbox.js 查看文件

@@ -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(/&lt;/g,"<").replace(/&gt;/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);
});
},
});

+ 0
- 19
frappe/email/page/email_inbox/email_inbox_sidebar.html 查看文件

@@ -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>

+ 0
- 45
frappe/email/page/email_inbox/inbox_list.html 查看文件

@@ -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>

+ 1
- 0
frappe/patches.txt 查看文件

@@ -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

+ 22
- 0
frappe/patches/v8_0/setup_email_inbox.py 查看文件

@@ -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'""")

+ 2
- 0
frappe/public/build.json 查看文件

@@ -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",


+ 3
- 0
frappe/public/css/form.css 查看文件

@@ -595,3 +595,6 @@ select.form-control {
box-shadow: none;
}
}
body[data-route^="Form/Communication"] textarea[data-fieldname="subject"] {
height: 80px !important;
}

+ 7
- 0
frappe/public/css/list.css 查看文件

@@ -374,3 +374,10 @@
.gantt .details-container .standard-image {
display: block;
}
.inbox-attachment,
.inbox-link {
margin-right: 7px;
}
.select-inbox {
padding: 30px 30px;
}

+ 7
- 1
frappe/public/js/frappe/form/footer/timeline.html 查看文件

@@ -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">



+ 29
- 10
frappe/public/js/frappe/form/footer/timeline.js 查看文件

@@ -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;



+ 16
- 1
frappe/public/js/frappe/list/list_renderer.js 查看文件

@@ -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;
},
});

+ 0
- 1
frappe/public/js/frappe/list/list_sidebar.html 查看文件

@@ -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>


+ 6
- 0
frappe/public/js/frappe/list/list_sidebar.js 查看文件

@@ -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) {


+ 8
- 19
frappe/public/js/frappe/list/list_view.js 查看文件

@@ -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,


+ 1
- 0
frappe/public/js/frappe/ui/toolbar/notifications.js 查看文件

@@ -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() {


+ 2
- 3
frappe/public/js/frappe/views/communication.js 查看文件

@@ -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);
}



+ 8
- 0
frappe/public/js/frappe/views/inbox/inbox_no_result.html 查看文件

@@ -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>

+ 100
- 21
frappe/public/js/frappe/views/inbox/inbox_view.js 查看文件

@@ -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)
}
}
});

+ 17
- 2
frappe/public/js/frappe/views/inbox/inbox_view_item_row.html 查看文件

@@ -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>


+ 32
- 0
frappe/public/js/frappe/views/inbox/select_email_inbox.html 查看文件

@@ -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>

+ 4
- 0
frappe/public/less/form.less 查看文件

@@ -767,3 +767,7 @@ select.form-control {
// }
}
}

body[data-route^="Form/Communication"] textarea[data-fieldname="subject"] {
height: 80px !important;
}

+ 8
- 0
frappe/public/less/list.less 查看文件

@@ -455,4 +455,12 @@
display: block;
}
}
}

.inbox-attachment, .inbox-link {
margin-right: 7px;
}

.select-inbox {
padding: 30px 30px;
}

正在加载...
取消
保存