Skip to content

Commit

Permalink
feat(Communication): Move Timeline DocType and Name to Dynamic Links (#…
Browse files Browse the repository at this point in the history
…7467)

feat(Communication): Move Timeline DocType and Name to Dynamic Links
  • Loading branch information
netchampfaris committed May 20, 2019
2 parents a873340 + 4946c15 commit cf8a53e
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 228 deletions.
2 changes: 1 addition & 1 deletion frappe/core/doctype/activity_log/activity_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from frappe import _
from frappe.utils import get_fullname, now
from frappe.model.document import Document
from frappe.core.utils import get_parent_doc, set_timeline_doc
from frappe.core.utils import set_timeline_doc
import frappe

class ActivityLog(Document):
Expand Down
74 changes: 34 additions & 40 deletions frappe/core/doctype/communication/communication.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"allow_import": 1,
"creation": "2013-01-29 10:47:14",
"description": "Keep a track of all communications",
"description": "Keeps track of all communications",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
Expand Down Expand Up @@ -41,14 +41,11 @@
"user",
"column_break_27",
"email_template",
"link_doctype",
"link_name",
"timeline_doctype",
"timeline_name",
"timeline_label",
"unread_notification_sent",
"seen",
"_user_tags",
"timeline_links_sections",
"timeline_links",
"email_inbox",
"message_id",
"uid",
Expand Down Expand Up @@ -204,6 +201,7 @@
"label": "Date"
},
{
"default": "0",
"fieldname": "read_receipt",
"fieldtype": "Check",
"label": "Sent Read Receipt",
Expand All @@ -220,6 +218,7 @@
"read_only": 1
},
{
"default": "0",
"fieldname": "read_by_recipient",
"fieldtype": "Check",
"label": "Read by Recipient",
Expand Down Expand Up @@ -284,39 +283,6 @@
"fieldname": "column_break_27",
"fieldtype": "Column Break"
},
{
"fieldname": "link_doctype",
"fieldtype": "Link",
"label": "Link DocType",
"options": "DocType",
"read_only": 1
},
{
"fieldname": "link_name",
"fieldtype": "Dynamic Link",
"label": "Link Name",
"options": "link_doctype",
"read_only": 1
},
{
"fieldname": "timeline_doctype",
"fieldtype": "Link",
"label": "Timeline DocType",
"options": "DocType",
"read_only": 1
},
{
"fieldname": "timeline_name",
"fieldtype": "Dynamic Link",
"label": "Timeline Name",
"options": "timeline_doctype",
"read_only": 1
},
{
"fieldname": "timeline_label",
"fieldtype": "Data",
"label": "Timeline field Name"
},
{
"default": "0",
"fieldname": "unread_notification_sent",
Expand All @@ -325,6 +291,7 @@
"read_only": 1
},
{
"default": "0",
"fieldname": "seen",
"fieldtype": "Check",
"label": "Seen",
Expand Down Expand Up @@ -368,6 +335,7 @@
"options": "Open\nSpam\nTrash"
},
{
"default": "0",
"fieldname": "has_attachment",
"fieldtype": "Check",
"hidden": 1,
Expand Down Expand Up @@ -398,11 +366,24 @@
"label": "Email Template",
"options": "Email Template",
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "timeline_links_sections",
"fieldtype": "Section Break",
"label": "Timeline Links"
},
{
"fieldname": "timeline_links",
"fieldtype": "Table",
"label": "Timeline Links",
"options": "Dynamic Link",
"permlevel": 2
}
],
"icon": "fa fa-comment",
"idx": 1,
"modified": "2019-05-04 15:36:35.818714",
"modified": "2019-05-20 14:14:01.514493",
"modified_by": "Administrator",
"module": "Core",
"name": "Communication",
Expand All @@ -428,6 +409,18 @@
"role": "System Manager",
"share": 1
},
{
"delete": 1,
"email": 1,
"export": 1,
"permlevel": 2,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"delete": 1,
"email": 1,
Expand All @@ -437,6 +430,7 @@
}
],
"search_fields": "subject",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "subject",
"track_changes": 1,
Expand Down
102 changes: 91 additions & 11 deletions frappe/core/doctype/communication/communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
from frappe.utils import validate_email_address, get_fullname, strip_html, cstr
from frappe.core.doctype.communication.email import (validate_email,
notify, _notify, update_parent_mins_to_first_response)
from frappe.core.utils import get_parent_doc, set_timeline_doc
from frappe.core.utils import get_parent_doc
from frappe.utils.bot import BotReply
from frappe.utils import parse_addr
from frappe.core.doctype.comment.comment import update_comment_in_doc

from email.utils import parseaddr
from collections import Counter

exclude_from_linked_with = True
Expand Down Expand Up @@ -58,7 +58,10 @@ def validate(self):
self.set_sender_full_name()

validate_email(self)
set_timeline_doc(self)

if self.communication_medium == "Email":
self.set_timeline_links()
self.deduplicate_timeline_links()

def validate_reference(self):
if self.reference_doctype and self.reference_name:
Expand All @@ -79,6 +82,7 @@ def validate_reference(self):
circular_linking = True
break
doc = get_parent_doc(doc)

if circular_linking:
frappe.throw(_("Please make sure the Reference Communication Docs are not circularly linked."), frappe.CircularLinkingError)

Expand Down Expand Up @@ -231,26 +235,66 @@ def set_delivery_status(self, commit=False):
if commit:
frappe.db.commit()

# Timeline Links
def set_timeline_links(self):
contacts = get_contacts([self.sender, self.recipients, self.cc, self.bcc])
for contact_name in contacts:
self.add_link('Contact', contact_name)

#link contact's dynamic links to communication
add_contact_links_to_communication(self, contact_name)

def deduplicate_timeline_links(self):
if self.timeline_links:
links, duplicate = [], False

for l in self.timeline_links:
t = (l.link_doctype, l.link_name)
if not t in links:
links.append(t)
else:
duplicate = True

if duplicate:
del self.timeline_links[:] # make it python 2 compatible as list.clear() is python 3 only
for l in links:
self.add_link(link_doctype=l[0], link_name=l[1])

def add_link(self, link_doctype, link_name, autosave=False):
self.append("timeline_links",
{
"link_doctype": link_doctype,
"link_name": link_name
}
)

if autosave:
self.save(ignore_permissions=True)

def get_links(self):
return self.timeline_links

def remove_link(self, link_doctype, link_name, autosave=False, ignore_permissions=True):
for l in self.timeline_links:
if l.link_doctype == link_doctype and l.link_name == link_name:
self.timeline_links.remove(l)

if autosave:
self.save(ignore_permissions=ignore_permissions)

def on_doctype_update():
"""Add indexes in `tabCommunication`"""
frappe.db.add_index("Communication", ["reference_doctype", "reference_name"])
frappe.db.add_index("Communication", ["timeline_doctype", "timeline_name"])
frappe.db.add_index("Communication", ["link_doctype", "link_name"])
frappe.db.add_index("Communication", ["status", "communication_type"])

def has_permission(doc, ptype, user):
if ptype=="read":
if (doc.reference_doctype == "Communication" and doc.reference_name == doc.name) \
or (doc.timeline_doctype == "Communication" and doc.timeline_name == doc.name):
return
if doc.reference_doctype == "Communication" and doc.reference_name == doc.name:
return

if doc.reference_doctype and doc.reference_name:
if frappe.has_permission(doc.reference_doctype, ptype="read", doc=doc.reference_name):
return True
if doc.timeline_doctype and doc.timeline_name:
if frappe.has_permission(doc.timeline_doctype, ptype="read", doc=doc.timeline_name):
return True

def get_permission_query_conditions_for_communication(user):
if not user: user = frappe.session.user
Expand All @@ -270,3 +314,39 @@ def get_permission_query_conditions_for_communication(user):
email_accounts = [ '"%s"'%account.get("email_account") for account in accounts ]
return """tabCommunication.email_account in ({email_accounts})"""\
.format(email_accounts=','.join(email_accounts))

def get_contacts(email_strings):
email_addrs = []

for email_string in email_strings:
if email_string:
for email in email_string.split(","):
parsed_email = parseaddr(email)[1]
if parsed_email:
email_addrs.append(parsed_email)

contacts = []
for email in email_addrs:
contact_name = frappe.db.get_value('Contact', {'email_id': email})

if not contact_name:
contact = frappe.get_doc({
"doctype": "Contact",
"first_name": frappe.unscrub(email.split("@")[0]),
"email_id": email
}).insert(ignore_permissions=True)
contact_name = contact.name

contacts.append(contact_name)

return contacts

def add_contact_links_to_communication(communication, contact_name):
contact_links = frappe.get_list("Dynamic Link", filters={
"parenttype": "Contact",
"parent": contact_name
}, fields=["link_doctype", "link_name"])

if contact_links:
for contact_link in contact_links:
communication.add_link(contact_link.link_doctype, contact_link.link_name)
10 changes: 3 additions & 7 deletions frappe/core/doctype/communication/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,9 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
"message_id":get_message_id().strip(" <>"),
"read_receipt":read_receipt,
"has_attachment": 1 if attachments else 0
})
comm.insert(ignore_permissions=True)
}).insert(ignore_permissions=True)

if not doctype:
# if no reference given, then send it against the communication
comm.db_set(dict(reference_doctype='Communication', reference_name=comm.name))
comm.save(ignore_permissions=True)

if isinstance(attachments, string_types):
attachments = json.loads(attachments)
Expand Down Expand Up @@ -557,5 +554,4 @@ def mark_email_as_seen(name=None):

frappe.response["type"] = 'binary'
frappe.response["filename"] = "imaginary_pixel.png"
frappe.response["filecontent"] = buffered_obj.getvalue()

frappe.response["filecontent"] = buffered_obj.getvalue()

0 comments on commit cf8a53e

Please sign in to comment.