diff --git a/frappe/__init__.py b/frappe/__init__.py
index 0413efed9f..507f0b7283 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json
from .exceptions import *
from .utils.jinja import get_jenv, get_template, render_template
-__version__ = '7.2.8'
+__version__ = '7.2.9'
__title__ = "Frappe Framework"
local = Local()
@@ -372,7 +372,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message
unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
attachments=None, content=None, doctype=None, name=None, reply_to=None,
cc=[], message_id=None, in_reply_to=None, send_after=None, expose_recipients=None,
- send_priority=1, communication=None, retry=1, now=None):
+ send_priority=1, communication=None, retry=1, now=None, read_receipt=None):
"""Send email using user's default **Email Account** or global default **Email Account**.
@@ -411,7 +411,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message
unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message,
attachments=attachments, reply_to=reply_to, cc=cc, message_id=message_id, in_reply_to=in_reply_to,
send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority,
- communication=communication, now=now)
+ communication=communication, now=now, read_receipt=read_receipt)
whitelisted = []
guest_methods = []
diff --git a/frappe/config/desktop.py b/frappe/config/desktop.py
index 4859802f49..97c4edefdb 100644
--- a/frappe/config/desktop.py
+++ b/frappe/config/desktop.py
@@ -3,6 +3,14 @@ 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"),
diff --git a/frappe/config/setup.py b/frappe/config/setup.py
index 7e39a79675..45c3667a11 100644
--- a/frappe/config/setup.py
+++ b/frappe/config/setup.py
@@ -134,6 +134,11 @@ def get_data():
"name": "Email Account",
"description": _("Add / Manage Email Accounts.")
},
+ {
+ "type": "doctype",
+ "name": "Email Domain",
+ "description": _("Add / Manage Email Domains.")
+ },
{
"type": "doctype",
"name": "Email Alert",
diff --git a/frappe/core/doctype/communication/communication.json b/frappe/core/doctype/communication/communication.json
index 4886b6ad18..84a491be86 100644
--- a/frappe/core/doctype/communication/communication.json
+++ b/frappe/core/doctype/communication/communication.json
@@ -1,1167 +1,1465 @@
{
- "allow_copy": 0,
- "allow_import": 1,
- "allow_rename": 0,
- "autoname": "",
- "beta": 0,
- "creation": "2013-01-29 10:47:14",
- "custom": 0,
- "description": "Keep a track of all communications",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "allow_copy": 0,
+ "allow_import": 1,
+ "allow_rename": 0,
+ "autoname": "",
+ "beta": 0,
+ "creation": "2013-01-29 10:47:14",
+ "custom": 0,
+ "description": "Keep a track of all communications",
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "subject",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Subject",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "subject",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Subject",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": "eval:doc.communication_type==='Communication'",
- "columns": 0,
- "fieldname": "section_break_10",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": "eval:doc.communication_type==='Communication'",
+ "columns": 0,
+ "fieldname": "section_break_10",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "depends_on": "eval:doc.communication_type===\"Communication\"",
- "fieldname": "communication_medium",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Type",
- "length": 0,
- "no_copy": 0,
- "options": "\nEmail\nChat\nPhone\nSMS\nVisit\nOther",
- "permlevel": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "",
+ "depends_on": "eval:doc.communication_type===\"Communication\"",
+ "fieldname": "communication_medium",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nEmail\nChat\nPhone\nSMS\nVisit\nOther",
+ "permlevel": 0,
+ "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,
- "depends_on": "eval:doc.communication_medium===\"Email\"",
- "fieldname": "sender",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "From",
- "length": 0,
- "no_copy": 0,
- "options": "Email",
- "permlevel": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.communication_medium===\"Email\"",
+ "fieldname": "sender",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From",
+ "length": 255,
+ "no_copy": 0,
+ "options": "Email",
+ "permlevel": 0,
+ "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": "column_break_4",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "recipients",
- "fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "To",
- "length": 0,
- "no_copy": 0,
- "options": "Email",
- "permlevel": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "recipients",
+ "fieldtype": "Code",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Email",
+ "permlevel": 0,
+ "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,
- "depends_on": "eval:doc.communication_medium===\"Email\"",
- "fieldname": "cc",
- "fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "CC",
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.communication_medium===\"Email\"",
+ "fieldname": "cc",
+ "fieldtype": "Code",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "CC",
+ "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,
- "depends_on": "eval:in_list([\"Phone\", \"SMS\"], doc.communication_medium)",
- "fieldname": "phone_no",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Phone No.",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:in_list([\"Phone\", \"SMS\"], doc.communication_medium)",
+ "fieldname": "phone_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Phone No.",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
- "description": "Integrations can use this field to set email delivery status",
- "fieldname": "delivery_status",
- "fieldtype": "Select",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Delivery Status",
- "length": 0,
- "no_copy": 0,
- "options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed\nError\nExpired\nSending",
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "Integrations can use this field to set email delivery status",
+ "fieldname": "delivery_status",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Delivery Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed\nError\nExpired\nSending",
+ "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": "section_break_8",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "content",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Message",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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": "content",
+ "fieldtype": "Text Editor",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Message",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
"width": "400"
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 1,
- "columns": 0,
- "fieldname": "status_section",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Status",
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "status_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Status",
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "text_content",
- "fieldtype": "Code",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Text Content",
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "text_content",
+ "fieldtype": "Code",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Text Content",
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Communication",
- "fieldname": "communication_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Communication Type",
- "length": 0,
- "no_copy": 0,
- "options": "Communication\nComment\nChat\nBot\nNotification",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Communication",
+ "fieldname": "communication_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Communication Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Communication\nComment\nChat\nBot\nNotification",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "comment_type",
- "fieldtype": "Select",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Comment Type",
- "length": 0,
- "no_copy": 0,
- "options": "\nComment\nLike\nInfo\nLabel\nWorkflow\nCreated\nUpdated\nSubmitted\nCancelled\nDeleted\nAssigned\nAssignment Completed\nAttachment\nAttachment Removed\nShared\nUnshared\nBot",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "comment_type",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Comment Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nComment\nLike\nInfo\nLabel\nWorkflow\nCreated\nUpdated\nSubmitted\nCancelled\nDeleted\nAssigned\nAssignment Completed\nAttachment\nAttachment Removed\nShared\nUnshared\nBot\nRelinked",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "column_break_5",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.communication_type===\"Communication\"",
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Status",
- "length": 0,
- "no_copy": 0,
- "options": "Open\nReplied\nClosed\nLinked",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.communication_type===\"Communication\"",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Open\nReplied\nClosed\nLinked",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.communication_type===\"Communication\"",
- "fieldname": "sent_or_received",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Sent or Received",
- "length": 0,
- "no_copy": 0,
- "options": "Sent\nReceived",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.communication_type===\"Communication\"",
+ "fieldname": "sent_or_received",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Sent or Received",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Sent\nReceived",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 1,
- "columns": 0,
- "fieldname": "additional_info",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "More Information",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "additional_info",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "More Information",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
- "default": "Today",
- "fieldname": "communication_date",
- "fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Now",
+ "fieldname": "communication_date",
+ "fieldtype": "Datetime",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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": "column_break_14",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "read_receipt",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_standard_filter": 0,
+ "in_list_view": 0,
+ "label": "Sent Read Receipt",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "sender_full_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "From Full Name",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_14",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 1,
- "columns": 0,
- "fieldname": "reference_section",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reference",
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sender_full_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From Full Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "reference_doctype",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reference DocType",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "reference_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference",
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reference_name",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reference Name",
- "length": 0,
- "no_copy": 0,
- "options": "reference_doctype",
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference DocType",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "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": "reference_owner",
- "fieldtype": "Read Only",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reference Owner",
- "length": 0,
- "no_copy": 0,
- "options": "reference_name.owner",
- "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": 1,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "reference_doctype",
+ "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,
- "depends_on": "eval:doc.communication_medium===\"Email\"",
- "fieldname": "email_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Email Account",
- "length": 0,
- "no_copy": 0,
- "options": "Email Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_owner",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference Owner",
+ "length": 0,
+ "no_copy": 0,
+ "options": "reference_name.owner",
+ "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": 1,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "in_reply_to",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "In Reply To",
- "length": 0,
- "no_copy": 0,
- "options": "Communication",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.communication_medium===\"Email\"",
+ "fieldname": "email_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Email Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Email Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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,
- "default": "__user",
- "fieldname": "user",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 1,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "User",
- "length": 0,
- "no_copy": 0,
- "options": "User",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "in_reply_to",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "In Reply To",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Communication",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "column_break_27",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "__user",
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 1,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "User",
+ "length": 0,
+ "no_copy": 0,
+ "options": "User",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "link_doctype",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Link DocType",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_27",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "link_name",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Link Name",
- "length": 0,
- "no_copy": 0,
- "options": "link_doctype",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "link_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Link DocType",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "timeline_doctype",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Timeline DocType",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "link_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Link Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "link_doctype",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "timeline_name",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Timeline Name",
- "length": 0,
- "no_copy": 0,
- "options": "timeline_doctype",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "timeline_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Timeline DocType",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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,
- "default": "0",
- "fieldname": "unread_notification_sent",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Unread Notification Sent",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "timeline_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Timeline Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "timeline_doctype",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "seen",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Seen",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "timeline_label",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Timeline field Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 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": "message_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 1,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Message ID",
- "length": 995,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "default": "0",
+ "fieldname": "unread_notification_sent",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Unread Notification Sent",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "_user_tags",
- "fieldtype": "Data",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "User Tags",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "print_hide": 1,
- "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "seen",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Seen",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "message_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 1,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Message ID",
+ "length": 995,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "_user_tags",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "User Tags",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "print_hide": 1,
+ "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": 1,
+ "fieldname": "email_inbox",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Email Inbox",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "uid",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "UID",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "message_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 1,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Message ID",
+ "length": 995,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "unique_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Unique id",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "deleted",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Deleted",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "actualdate",
+ "fieldtype": "Datetime",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "ActualDate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "nomatch",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "No Match",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "has_attachment",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Has Attachment",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "timeline_hide",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Hidden in Timeline",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-comment",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "in_dialog": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2016-12-29 14:39:53.460122",
- "modified_by": "Administrator",
- "module": "Core",
- "name": "Communication",
- "owner": "Administrator",
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "fa fa-comment",
+ "idx": 1,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2016-12-29 14:39:53.460123",
+ "modified_by": "Administrator",
+ "module": "Core",
+ "name": "Communication",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "is_custom": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 0,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
"write": 1
- },
+ },
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 0,
- "export": 0,
- "if_owner": 1,
- "import": 0,
- "is_custom": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "All",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 0
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 1,
+ "cancel": 0,
+ "create": 0,
+ "delete": 0,
+ "email": 0,
+ "export": 0,
+ "if_owner": 1,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 0,
+ "role": "All",
+ "set_user_permissions": 0,
+ "share": 0,
+ "submit": 0,
+ "user_permission_doctypes": "[\"Email Account\"]",
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "search_fields": "subject",
- "sort_order": "DESC",
- "title_field": "subject",
- "track_changes": 1,
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "search_fields": "subject",
+ "sort_order": "DESC",
+ "title_field": "subject",
+ "track_changes": 1,
"track_seen": 0
-}
\ No newline at end of file
+}
diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py
index 71470457a4..34f3c6af83 100644
--- a/frappe/core/doctype/communication/communication.py
+++ b/frappe/core/doctype/communication/communication.py
@@ -48,25 +48,25 @@ class Communication(Document):
def after_insert(self):
if not (self.reference_doctype and self.reference_name):
return
+ if not self.timeline_hide:
+ if self.communication_type in ("Communication", "Comment"):
+ # send new comment to listening clients
+ frappe.publish_realtime('new_communication', self.as_dict(),
+ doctype=self.reference_doctype, docname=self.reference_name,
+ after_commit=True)
- if self.communication_type in ("Communication", "Comment"):
- # send new comment to listening clients
- frappe.publish_realtime('new_communication', self.as_dict(),
- doctype= self.reference_doctype, docname = self.reference_name,
- after_commit=True)
+ if self.communication_type == "Comment":
+ notify_mentions(self)
- if self.communication_type == "Comment":
- notify_mentions(self)
-
- elif self.communication_type in ("Chat", "Notification", "Bot"):
- if self.reference_name == frappe.session.user:
- message = self.as_dict()
- message['broadcast'] = True
- frappe.publish_realtime('new_message', message, after_commit=True)
- else:
- # reference_name contains the user who is addressed in the messages' page comment
- frappe.publish_realtime('new_message', self.as_dict(),
- user=self.reference_name, after_commit=True)
+ elif self.communication_type in ("Chat", "Notification", "Bot"):
+ if self.reference_name == frappe.session.user:
+ message = self.as_dict()
+ message['broadcast'] = True
+ frappe.publish_realtime('new_message', message, after_commit=True)
+ else:
+ # reference_name contains the user who is addressed in the messages' page comment
+ frappe.publish_realtime('new_message', self.as_dict(),
+ user=self.reference_name, after_commit=True)
def on_update(self):
"""Update parent status as `Open` or `Replied`."""
@@ -118,7 +118,7 @@ class Communication(Document):
sender_name = None
self.sender = sender_email
- self.sender_full_name = sender_name or get_fullname(frappe.session.user)
+ self.sender_full_name = sender_name or get_fullname(frappe.session.user) if frappe.session.user!='Administrator' else None
def get_parent_doc(self):
"""Returns document of `reference_doctype`, `reference_doctype`"""
@@ -227,8 +227,7 @@ def on_doctype_update():
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"])
- frappe.db.add_index("Communication", ["creation"])
- frappe.db.add_index("Communication", ["modified"])
+ frappe.db.add_index("Communication", ["communication_date"])
frappe.db.add_index("Communication", ["message_id(200)"])
def has_permission(doc, ptype, user):
diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py
index 7f713284c0..eb4bf2a9c2 100755
--- a/frappe/core/doctype/communication/email.py
+++ b/frappe/core/doctype/communication/email.py
@@ -16,11 +16,12 @@ import MySQLdb
import time
from frappe import _
from frappe.utils.background_jobs import enqueue
+import email.utils
@frappe.whitelist()
def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent",
- sender=None, recipients=None, communication_medium="Email", send_email=False,
- print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, flags=None):
+ sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False,
+ print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, flags=None,read_receipt=None):
"""Make a new communication.
:param doctype: Reference DocType.
@@ -53,13 +54,15 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
"subject": subject,
"content": content,
"sender": sender,
+ "sender_full_name":sender_full_name,
"recipients": recipients,
"cc": cc or None,
"communication_medium": communication_medium,
"sent_or_received": sent_or_received,
"reference_doctype": doctype,
"reference_name": name,
- "message_id":get_message_id().strip(" <>")
+ "message_id":get_message_id().strip(" <>"),
+ "read_receipt":read_receipt
})
comm.insert(ignore_permissions=True)
@@ -151,7 +154,8 @@ def _notify(doc, print_html=None, print_format=None, attachments=None,
message_id=doc.message_id,
unsubscribe_message=unsubscribe_message,
delayed=True,
- communication=doc.name
+ communication=doc.name,
+ read_receipt = doc.read_receipt
)
def update_parent_status(doc):
@@ -196,8 +200,9 @@ def get_recipients_and_cc(doc, recipients, cc, fetched_from_email_account=False)
original_recipients, recipients = recipients, []
# send email to the sender of the previous email in the thread which this email is a reply to
- if doc.previous_email_sender:
- recipients.append(doc.previous_email_sender)
+ #provides erratic results and can send external
+ #if doc.previous_email_sender:
+ # recipients.append(doc.previous_email_sender)
# cc that was received in the email
original_cc = split_emails(doc.cc)
@@ -254,9 +259,13 @@ def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None)
def set_incoming_outgoing_accounts(doc):
doc.incoming_email_account = doc.outgoing_email_account = None
- if doc.reference_doctype:
+ if not doc.incoming_email_account and doc.sender:
doc.incoming_email_account = frappe.db.get_value("Email Account",
- {"append_to": doc.reference_doctype, "enable_incoming": 1}, "email_id")
+ {"email_id": doc.sender, "enable_incoming": 1}, "email_id")
+
+ if not doc.incoming_email_account and doc.reference_doctype:
+ doc.incoming_email_account = frappe.db.get_value("Email Account",
+ {"append_to": doc.reference_doctype, }, "email_id")
doc.outgoing_email_account = frappe.db.get_value("Email Account",
{"append_to": doc.reference_doctype, "enable_outgoing": 1},
@@ -276,20 +285,13 @@ def get_recipients(doc, fetched_from_email_account=False):
# [EDGE CASE] doc.recipients can be None when an email is sent as BCC
recipients = split_emails(doc.recipients)
- if fetched_from_email_account and doc.in_reply_to:
+ #if fetched_from_email_account and doc.in_reply_to:
# add sender of previous reply
- doc.previous_email_sender = frappe.db.get_value("Communication", doc.in_reply_to, "sender")
- recipients.append(doc.previous_email_sender)
+ #doc.previous_email_sender = frappe.db.get_value("Communication", doc.in_reply_to, "sender")
+ #recipients.append(doc.previous_email_sender)
if recipients:
- # exclude email accounts
- exclude = [d[0] for d in
- frappe.db.get_all("Email Account", ["email_id"], {"enable_incoming": 1}, as_list=True)]
- exclude += [d[0] for d in
- frappe.db.get_all("Email Account", ["login_id"], {"enable_incoming": 1}, as_list=True)
- if d[0]]
-
- recipients = filter_email_list(doc, recipients, exclude)
+ recipients = filter_email_list(doc, recipients, [])
return recipients
@@ -308,12 +310,8 @@ def get_cc(doc, recipients=None, fetched_from_email_account=False):
cc.append(doc.sender)
if cc:
- # exclude email accounts, unfollows, recipients and unsubscribes
- exclude = [d[0] for d in
- frappe.db.get_all("Email Account", ["email_id"], {"enable_incoming": 1}, as_list=True)]
- exclude += [d[0] for d in
- frappe.db.get_all("Email Account", ["login_id"], {"enable_incoming": 1}, as_list=True)
- if d[0]]
+ # exclude unfollows, recipients and unsubscribes
+ exclude = [] #added to remove account check
exclude += [d[0] for d in frappe.db.get_all("User", ["name"], {"thread_notify": 0}, as_list=True)]
exclude += [(parseaddr(email)[1] or "").lower() for email in recipients]
diff --git a/frappe/core/doctype/dynamic_link/__init__.py b/frappe/core/doctype/dynamic_link/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/core/doctype/dynamic_link/dynamic_link.json b/frappe/core/doctype/dynamic_link/dynamic_link.json
new file mode 100644
index 0000000000..cde3702896
--- /dev/null
+++ b/frappe/core/doctype/dynamic_link/dynamic_link.json
@@ -0,0 +1,97 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2017-01-13 04:55:18.835023",
+ "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": "link_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Link DocType",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "link_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Link Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "link_doctype",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-01-13 04:55:18.835023",
+ "modified_by": "Administrator",
+ "module": "Core",
+ "name": "Dynamic Link",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/frappe/core/doctype/dynamic_link/dynamic_link.py b/frappe/core/doctype/dynamic_link/dynamic_link.py
new file mode 100644
index 0000000000..21f82f522a
--- /dev/null
+++ b/frappe/core/doctype/dynamic_link/dynamic_link.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, 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 DynamicLink(Document):
+ pass
+
+def on_doctype_update():
+ frappe.db.add_index("Dynamic Link", ["link_doctype", "link_name"])
diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js
index ad660a8b59..05de1d2e06 100644
--- a/frappe/core/doctype/user/user.js
+++ b/frappe/core/doctype/user/user.js
@@ -76,6 +76,26 @@ frappe.ui.form.on('User', {
}
}
}
+ if (frm.doc.user_emails){
+ var found =0;
+ for (var i = 0;i {0}'.format(after)
+ conditions+= ' and communication_date > {0}'.format(after)
communications = frappe.db.sql("""select {fields}
from tabCommunication
diff --git a/frappe/desk/page/activity/activity_row.html b/frappe/desk/page/activity/activity_row.html
index ea02365220..2a1e5a7b48 100644
--- a/frappe/desk/page/activity/activity_row.html
+++ b/frappe/desk/page/activity/activity_row.html
@@ -30,6 +30,8 @@
{%= __("{0} {1}", ["" + __(feed_type) + " ", feed_type==="Deleted" ? subject : link ]) %}
{% } else if (feed_type==="Updated") { %}
{%= __("Updated {0}: {1}", [link, "" + subject + " "]) %}
+ {% } else if (feed_type==="Relinked") { %}
+ {%= __("{0} {1} to {2}", [by, content,link]) %}
{% } else if (reference_doctype && reference_name) { %}
{%= __("{0}: {1}", [link, "" + content + " "]) %}
{% } else { %}
diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py
index 80b50ac069..725f495361 100644
--- a/frappe/desk/reportview.py
+++ b/frappe/desk/reportview.py
@@ -12,7 +12,6 @@ from frappe import _
@frappe.whitelist()
def get():
args = get_form_params()
- args.save_list_settings = True
data = compress(execute(**args), args = args)
@@ -33,6 +32,10 @@ def get_form_params():
data["fields"] = json.loads(data["fields"])
if isinstance(data.get("docstatus"), basestring):
data["docstatus"] = json.loads(data["docstatus"])
+ if isinstance(data.get("save_list_settings"), basestring):
+ data["save_list_settings"] = json.loads(data["save_list_settings"])
+ else:
+ data["save_list_settings"] = True
# queries must always be server side
@@ -279,3 +282,27 @@ def build_match_conditions(doctype, as_condition=True):
return match_conditions.replace("%", "%%")
else:
return match_conditions
+
+def get_filters_cond(doctype, filters, conditions):
+ if filters:
+ flt = filters
+ if isinstance(filters, dict):
+ filters = filters.items()
+ flt = []
+ for f in filters:
+ if isinstance(f[1], basestring) and f[1][0] == '!':
+ flt.append([doctype, f[0], '!=', f[1][1:]])
+ else:
+ value = frappe.db.escape(f[1]) if isinstance(f[1], basestring) else f[1]
+ flt.append([doctype, f[0], '=', value])
+
+ query = DatabaseQuery(doctype)
+ query.filters = flt
+ query.conditions = conditions
+ query.build_filter_conditions(flt, conditions)
+
+ cond = ' and ' + ' and '.join(query.conditions)
+ else:
+ cond = ''
+ return cond
+
diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py
index 6b0cef4ec3..ac35841d4c 100644
--- a/frappe/desk/treeview.py
+++ b/frappe/desk/treeview.py
@@ -46,17 +46,24 @@ def get_children():
@frappe.whitelist()
def add_node():
- doctype = frappe.form_dict.get('doctype')
- parent_field = 'parent_' + doctype.lower().replace(' ', '_')
- name_field = doctype.lower().replace(' ', '_') + '_name'
+ args = make_tree_args(**frappe.form_dict)
+ doc = frappe.get_doc(args)
- doc = frappe.new_doc(doctype)
- doc.update({
- name_field: frappe.form_dict[name_field],
- parent_field: frappe.form_dict['parent'],
- "is_group": frappe.form_dict['is_group']
- })
- if doctype == "Sales Person":
+ if args.doctype == "Sales Person":
doc.employee = frappe.form_dict.get('employee')
- doc.save()
\ No newline at end of file
+ doc.save()
+
+def make_tree_args(**kwarg):
+ del kwarg['cmd']
+
+ doctype = kwarg['doctype']
+ parent_field = 'parent_' + doctype.lower().replace(' ', '_')
+ name_field = doctype.lower().replace(' ', '_') + '_name'
+
+ kwarg.update({
+ name_field: kwarg[name_field],
+ parent_field: kwarg["parent"]
+ })
+
+ return frappe._dict(kwarg)
diff --git a/frappe/email/__init__.py b/frappe/email/__init__.py
index eeb7823172..626efd258a 100644
--- a/frappe/email/__init__.py
+++ b/frappe/email/__init__.py
@@ -34,3 +34,73 @@ def get_system_managers():
WHERE role='System Manager'
AND parent!='Administrator'
AND parent IN (SELECT email FROM tabUser WHERE enabled=1)""")
+
+@frappe.whitelist()
+def relink(name,reference_doctype=None,reference_name=None):
+ dt = reference_doctype
+ dn = reference_name
+ origin = frappe.db.get_value("Communication", name, ["reference_doctype", "reference_name", "communication_medium", "subject"], as_dict=1)
+
+ subject_link = '' + origin.subject
+ content= 'Relinked ' + origin.communication_medium + ' ' + subject_link + ' '
+
+ if origin.reference_doctype:
+ from_link = ''
+ content += ' from ' + from_link + origin.reference_doctype +' '+ origin.reference_name +' '
+
+ frappe.db.sql("""UPDATE `tabCommunication`
+ SET reference_doctype = %(ref_doc)s, reference_name = %(ref_name)s, STATUS = "Linked"
+ WHERE communication_type = "Communication" and name = %(name)s OR timeline_hide = %(name)s""",
+ {'ref_doc': dt,
+ 'ref_name': dn, 'name': name})
+
+ dup_list = [{"name":name, "timeline_label":False}] + frappe.db.get_values("Communication", {"timeline_hide": name, "communication_type":"Communication"}, ["name", "timeline_label"], as_dict=1)
+ for comm in dup_list:
+ if not comm["timeline_label"]:
+ doc = frappe.get_doc("Communication", comm["name"])
+ if not doc.timeline_label:
+ doc.timeline_doctype = None
+ doc.timeline_name = None
+ doc.set_timeline_doc()
+ if comm["name"] == dup_list[0]["name"]:
+ doc.save(ignore_permissions=True)
+ else:
+ doc.db_update()
+
+ frappe.get_doc({
+ "doctype": "Communication",
+ "communication_type": "Comment",
+ "comment_type": "Relinked",
+ "reference_doctype": dt,
+ "reference_name": dn,
+ "subject": origin.subject,
+ "communication_medium": frappe.db.get_value("Communication",name,"communication_medium"),
+ "reference_owner": frappe.db.get_value(dt, dn, "owner"),
+ "content": content,
+ "sender":frappe.session.user
+ }).insert(ignore_permissions=True)
+
+def get_communication_doctype(doctype, txt, searchfield, start, page_len, filters):
+ user_perms = frappe.utils.user.UserPermissions(frappe.session.user)
+ user_perms.build_permissions()
+ can_read = user_perms.can_read
+ from frappe.modules import load_doctype_module
+ com_doctypes = []
+ if len(txt)<2:
+
+ for name in ["Customer", "Supplier"]:
+ try:
+ module = load_doctype_module(name, suffix='_dashboard')
+ if hasattr(module, 'get_data'):
+ for i in module.get_data()['transactions']:
+ com_doctypes += i["items"]
+ except ImportError:
+ pass
+ else:
+ com_doctypes = [d[0] for d in frappe.db.get_values("DocType", {"issingle": 0, "istable": 0, "hide_toolbar": 0})]
+
+ out = []
+ for dt in com_doctypes:
+ if txt.lower().replace("%", "") in dt.lower() and dt in can_read:
+ out.append([dt])
+ return out
diff --git a/frappe/email/doctype/contact/__init__.py b/frappe/email/doctype/contact/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/email/doctype/contact/contact.js b/frappe/email/doctype/contact/contact.js
new file mode 100644
index 0000000000..149506a761
--- /dev/null
+++ b/frappe/email/doctype/contact/contact.js
@@ -0,0 +1,41 @@
+// Copyright (c) 2016, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+
+cur_frm.email_field = "email_id";
+frappe.ui.form.on("Contact", {
+ refresh: function(frm) {
+ if(frm.doc.__islocal) {
+ var last_route = frappe.route_history.slice(-2, -1)[0];
+ if(frappe.contact_link && frappe.contact_link.doc
+ && frappe.contact_link.doc.name==last_route[2]) {
+ frm.add_child('links', {
+ link_doctype: frappe.contact_link.doctype,
+ link_name: frappe.contact_link.doc[frappe.contact_link.fieldname]
+ });
+ }
+ }
+
+ if(!frm.doc.user && !frm.is_new() && frm.perm[0].write) {
+ frm.add_custom_button(__("Invite as User"), function() {
+ frappe.call({
+ method: "erpnext.utilities.doctype.contact.contact.invite_user",
+ args: {
+ contact: frm.doc.name
+ },
+ callback: function(r) {
+ frm.set_value("user", r.message);
+ }
+ });
+ });
+ }
+ },
+ validate: function(frm) {
+ // clear linked customer / supplier / sales partner on saving...
+ if(frm.doc.links) {
+ frm.doc.links.forEach(function(d) {
+ frappe.model.remove_from_locals(d.link_doctype, d.link_name);
+ });
+ }
+ }
+});
\ No newline at end of file
diff --git a/frappe/email/doctype/contact/contact.json b/frappe/email/doctype/contact/contact.json
new file mode 100644
index 0000000000..ae247f8ce4
--- /dev/null
+++ b/frappe/email/doctype/contact/contact.json
@@ -0,0 +1,805 @@
+{
+ "allow_copy": 0,
+ "allow_import": 1,
+ "allow_rename": 1,
+ "beta": 0,
+ "creation": "2013-01-10 16:34:32",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 0,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "contact_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "options": "fa fa-user",
+ "permlevel": 0,
+ "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": "first_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "First Name",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "first_name",
+ "oldfieldtype": "Data",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "last_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Last Name",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "last_name",
+ "oldfieldtype": "Data",
+ "permlevel": 0,
+ "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": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "email_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Email Address",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "email_id",
+ "oldfieldtype": "Data",
+ "options": "Email",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 1,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "User Id",
+ "length": 0,
+ "no_copy": 0,
+ "options": "User",
+ "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": "cb00",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
+ "default": "Passive",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Passive\nOpen\nReplied",
+ "permlevel": 0,
+ "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": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "phone",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Phone",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "contact_no",
+ "oldfieldtype": "Data",
+ "permlevel": 0,
+ "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": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "mobile_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Mobile No",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "mobile_no",
+ "oldfieldtype": "Data",
+ "permlevel": 0,
+ "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": "image",
+ "fieldtype": "Attach Image",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Image",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "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": "contact_details",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference",
+ "length": 0,
+ "no_copy": 0,
+ "options": "fa fa-pushpin",
+ "permlevel": 0,
+ "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,
+ "default": "0",
+ "depends_on": "",
+ "fieldname": "is_primary_contact",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Primary Contact",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "is_primary_contact",
+ "oldfieldtype": "Select",
+ "permlevel": 0,
+ "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": "links",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Links",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Dynamic Link",
+ "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": "more_info",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "More Information",
+ "length": 0,
+ "no_copy": 0,
+ "options": "fa fa-file-text",
+ "permlevel": 0,
+ "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,
+ "description": "",
+ "fieldname": "department",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Department",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
+ "description": "",
+ "fieldname": "designation",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Designation",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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": "column_break_17",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "unsubscribed",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Unsubscribed",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "fa fa-user",
+ "idx": 1,
+ "image_field": "image",
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-01-13 06:59:06.417300",
+ "modified_by": "Administrator",
+ "module": "Email",
+ "name": "Contact",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Sales Master Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Purchase Master Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Sales Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Purchase Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Maintenance Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Sales User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Purchase User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Maintenance User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 0,
+ "delete": 0,
+ "email": 0,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "match": "",
+ "permlevel": 1,
+ "print": 0,
+ "read": 1,
+ "report": 1,
+ "role": "All",
+ "set_user_permissions": 0,
+ "share": 0,
+ "submit": 0,
+ "write": 0
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_changes": 0,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/frappe/email/doctype/contact/contact.py b/frappe/email/doctype/contact/contact.py
new file mode 100644
index 0000000000..ac878c74cd
--- /dev/null
+++ b/frappe/email/doctype/contact/contact.py
@@ -0,0 +1,130 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import cstr, has_gravatar
+from frappe import _
+from frappe.model.document import Document
+
+class Contact(Document):
+ def autoname(self):
+ # concat first and last name
+ self.name = " ".join(filter(None,
+ [cstr(self.get(f)).strip() for f in ["first_name", "last_name"]]))
+
+ # concat party name if reqd
+ for link in self.links:
+ self.name = self.name + '-' + link.link_name.strip()
+ break
+
+ def validate(self):
+ self.set_user()
+ if self.email_id:
+ self.image = has_gravatar(self.email_id)
+
+ def set_user(self):
+ if not self.user and self.email_id:
+ self.user = frappe.db.get_value("User", {"email": self.email_id})
+
+ def on_trash(self):
+ frappe.db.sql("""update `tabIssue` set contact='' where contact=%s""",
+ self.name)
+
+ def has_common_link(self, doc):
+ reference_links = [(link.link_doctype, link.link_name) for link in doc.links]
+ for link in self.links:
+ if (link.link_doctype, link.link_name) in reference_links:
+ return True
+
+
+def get_default_contact(doctype, name):
+ '''Returns default contact for the given doctype, name'''
+ out = frappe.db.sql('''select contact.name
+ from
+ tabContact contact, `tabDynamic Link` dl
+ where
+ dl.parent = contact.name and
+ dl.link_doctype=%s and
+ dl.link_name=%s and
+ dl.parenttype = "Contact"
+ order by
+ contact.is_primary_contact desc, name
+ limit 1''', (doctype, name), debug=1)
+
+ print out
+ return out and out[0][0] or None
+
+@frappe.whitelist()
+def invite_user(contact):
+ contact = frappe.get_doc("Contact", contact)
+
+ if not contact.email_id:
+ frappe.throw(_("Please set Email Address"))
+
+ if contact.has_permission("write"):
+ user = frappe.get_doc({
+ "doctype": "User",
+ "first_name": contact.first_name,
+ "last_name": contact.last_name,
+ "email": contact.email_id,
+ "user_type": "Website User",
+ "send_welcome_email": 1
+ }).insert(ignore_permissions = True)
+
+ return user.name
+
+@frappe.whitelist()
+def get_contact_details(contact):
+ contact = frappe.get_doc("Contact", contact)
+ out = {
+ "contact_person": contact.get("name"),
+ "contact_display": " ".join(filter(None,
+ [contact.get("first_name"), contact.get("last_name")])),
+ "contact_email": contact.get("email_id"),
+ "contact_mobile": contact.get("mobile_no"),
+ "contact_phone": contact.get("phone"),
+ "contact_designation": contact.get("designation"),
+ "contact_department": contact.get("department")
+ }
+ return out
+
+def update_contact(doc, method):
+ '''Update contact when user is updated, if contact is found. Called via hooks'''
+ contact_name = frappe.db.get_value("Contact", {"email_id": doc.name})
+ if contact_name:
+ contact = frappe.get_doc("Contact", contact_name)
+ for key in ("first_name", "last_name", "phone"):
+ if doc.get(key):
+ contact.set(key, doc.get(key))
+ contact.flags.ignore_mandatory = True
+ contact.save(ignore_permissions=True)
+
+def contact_query(doctype, txt, searchfield, start, page_len, filters):
+ from frappe.desk.reportview import get_match_cond
+
+ return frappe.db.sql("""select
+ contact.name, contact.first_name, contact.last_name
+ from
+ tabContact as contact, `tabDynamic Link` as dl
+ where
+ dl.parent = contact.name and
+ dl.parenttype = 'Contact' and
+ dl.link_doctype = %(link_doctype)s and
+ dl.link_name = %(link_name)s and
+ contact.`{key}` like %(txt)s
+ {mcond}
+ order by
+ if(locate(%(_txt)s, contact.name), locate(%(_txt)s, contact.name), 99999),
+ contact.idx desc, contact.name
+ limit %(start)s, %(page_len)s """.format(
+ mcond=get_match_cond(doctype),
+ key=frappe.db.escape(searchfield)),
+ {
+ 'txt': "%%%s%%" % frappe.db.escape(txt),
+ '_txt': txt.replace("%", ""),
+ 'start': start,
+ 'page_len': page_len,
+ 'link_doctype': filters.get('link_doctype'),
+ 'link_name': filters.get('link_name')
+ })
diff --git a/frappe/email/doctype/contact/test_contact.py b/frappe/email/doctype/contact/test_contact.py
new file mode 100644
index 0000000000..99b6581a73
--- /dev/null
+++ b/frappe/email/doctype/contact/test_contact.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+test_records = frappe.get_test_records('Contact')
+
+class TestContact(unittest.TestCase):
+ pass
diff --git a/frappe/email/doctype/contact/test_records.json b/frappe/email/doctype/contact/test_records.json
new file mode 100644
index 0000000000..e319745385
--- /dev/null
+++ b/frappe/email/doctype/contact/test_records.json
@@ -0,0 +1,22 @@
+[
+ {
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "doctype": "Contact",
+ "email_id": "test_contact_customer@example.com",
+ "first_name": "_Test Contact For _Test Customer",
+ "is_primary_contact": 1,
+ "phone": "+91 0000000000",
+ "status": "Open"
+ },
+ {
+ "doctype": "Contact",
+ "email_id": "test_contact_supplier@example.com",
+ "first_name": "_Test Contact For _Test Supplier",
+ "is_primary_contact": 1,
+ "phone": "+91 0000000000",
+ "status": "Open",
+ "supplier": "_Test Supplier",
+ "supplier_name": "_Test Supplier"
+ }
+]
\ No newline at end of file
diff --git a/frappe/email/doctype/email_account/email_account.js b/frappe/email/doctype/email_account/email_account.js
index d370144b66..d639fa97fb 100644
--- a/frappe/email/doctype/email_account/email_account.js
+++ b/frappe/email/doctype/email_account/email_account.js
@@ -8,10 +8,10 @@ frappe.email_defaults = {
"use_tls": 1
},
"Outlook.com": {
- "email_server": "pop3.live.com",
+ "email_server": "pop3-mail.outlook.com",
"use_ssl": 1,
"enable_outgoing": 1,
- "smtp_server": "smtp.live.com",
+ "smtp_server": "smtp-mail.outlook.com",
"smtp_port": 587,
"use_tls": 1
},
@@ -52,7 +52,7 @@ frappe.email_defaults_imap = {
"email_server": "imap.gmail.com"
},
"Outlook.com": {
- "email_server": "imap.live.com"
+ "email_server": "imap-mail.outlook.com"
},
"Yahoo Mail": {
"email_server": "imap.mail.yahoo.com"
@@ -88,15 +88,9 @@ frappe.ui.form.on("Email Account", {
});
}
},
- email_id: function(frm) {
- if(!frm.doc.email_account_name) {
- frm.set_value("email_account_name",
- (frm.doc.service ? frm.doc.service + " " : "")
- + toTitle(frm.doc.email_id.split("@")[0].replace(/[._]/g, " ")));
- }
- },
enable_incoming: function(frm) {
- frm.set_df_property("append_to", "reqd", frm.doc.enable_incoming);
+ frm.doc.no_remaining = null //perform full sync
+ //frm.set_df_property("append_to", "reqd", frm.doc.enable_incoming);
},
notify_if_unreplied: function(frm) {
frm.set_df_property("send_notification_to", "reqd", frm.doc.notify_if_unreplied);
@@ -105,10 +99,30 @@ frappe.ui.form.on("Email Account", {
frm.set_df_property("append_to", "only_select", true);
frm.set_query("append_to", "frappe.email.doctype.email_account.email_account.get_append_to");
},
+ validate:function(frm){
+ frm.events.update_domain(frm,true);
+ },
refresh: function(frm) {
+ frm.events.update_domain(frm,true);
frm.events.enable_incoming(frm);
frm.events.notify_if_unreplied(frm);
frm.events.show_gmail_message_for_less_secure_apps(frm);
+ if (frm.doc.__islocal != 1) {
+ if (frappe.route_titles["create user account"]) {
+ var user =frappe.route_titles["create user account"];
+ delete frappe.route_titles["create user account"];
+ var userdoc = frappe.get_doc("User",user);
+ frappe.model.with_doc("User", user, function (doc) {
+ var new_row = frappe.model.add_child(userdoc, "User Email", "user_emails");
+ new_row.email_account = cur_frm.doc.name;
+ new_row.awaiting_password = cur_frm.doc.awaiting_password;
+ new_row.email_id = cur_frm.doc.email_id;
+ new_row.idx = 0;
+ frappe.route_titles["unsaved"] = 1;
+ frappe.set_route("Form", "User",user);
+ });
+ }
+ }
},
show_gmail_message_for_less_secure_apps: function(frm) {
if(frm.doc.service==="Gmail") {
@@ -117,4 +131,49 @@ frappe.ui.form.on("Email Account", {
href="https://support.google.com/accounts/answer/6010255?hl=en">Read this for details');
}
},
-});
+ email_id:function(frm){
+ //pull domain and if no matching domain go create one
+ frm.events.update_domain(frm,false);
+ },
+ update_domain:function(frm,norefresh){
+ if (cur_frm.doc.email_id && !cur_frm.doc.service) {
+ frappe.call({
+ method: 'get_domain',
+ doc: cur_frm.doc,
+ async:false,
+ args: {
+ "email_id": cur_frm.doc.email_id
+ },
+ callback: function (frm) {
+ try {
+ if (cur_frm.doc.domain !=frm["message"][0]["name"]) {
+ cur_frm.doc.domain = frm["message"][0]["name"]
+ cur_frm.doc.email_server= frm["message"][0]["email_server"];
+ cur_frm.doc.use_imap= frm["message"][0]["use_imap"];
+ cur_frm.doc.smtp_server= frm["message"][0]["smtp_server"];
+ cur_frm.doc.use_ssl= frm["message"][0]["use_ssl"];
+ cur_frm.doc.use_tls= frm["message"][0]["use_tls"];
+ cur_frm.doc.smtp_port = frm["message"][0]["smtp_port"];
+ if (!norefresh) {
+ cur_frm.refresh();
+ }
+ }
+ }
+ catch (Exception) {
+ frappe.confirm(
+ 'Email Domain not configured for this account\nCreate one?',
+ function () {
+ frappe.model.with_doctype("Email Domain", function() {
+ frappe.route_options = {email_id: cur_frm.doc.email_id};
+ frappe.route_titles["return to email_account"] = 1
+ var doc = frappe.model.get_new_doc("Email Domain");
+ frappe.set_route("Form", "Email Domain", doc.name);
+ })
+ }
+ )
+ }
+ }
+ });
+ }
+ }
+});
\ No newline at end of file
diff --git a/frappe/email/doctype/email_account/email_account.json b/frappe/email/doctype/email_account/email_account.json
index ad6d0641f9..146b682c0d 100644
--- a/frappe/email/doctype/email_account/email_account.json
+++ b/frappe/email/doctype/email_account/email_account.json
@@ -12,63 +12,6 @@
"editable_grid": 0,
"engine": "InnoDB",
"fields": [
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "email_settings",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "",
- "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
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "service",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Service",
- "length": 0,
- "no_copy": 0,
- "options": "\nGMail\nSendgrid\nSparkPost\nYahoo Mail\nOutlook.com\nYandex.Mail",
- "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,
@@ -82,10 +25,10 @@
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Email Address",
+ "label": "Email Id",
"length": 0,
"no_copy": 0,
- "options": "Email",
+ "options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -140,9 +83,10 @@
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Login Id",
+ "label": "Email Address",
"length": 0,
"no_copy": 0,
+ "options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -183,6 +127,34 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "awaiting_password",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Awaiting password",
+ "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
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -212,6 +184,92 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "email_settings",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "domain",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Domain",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Email Domain",
+ "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": "service",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Service",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nGMail\nSendgrid\nSparkPost\nYahoo Mail\nOutlook.com\nYandex.Mail",
+ "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,
@@ -275,7 +333,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "",
+ "depends_on": "eval: !doc.domain && doc.enable_incoming",
"fieldname": "use_imap",
"fieldtype": "Check",
"hidden": 0,
@@ -287,6 +345,7 @@
"label": "Use IMAP",
"length": 0,
"no_copy": 0,
+ "options": "domain.use_imap",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -304,7 +363,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "enable_incoming",
+ "depends_on": "eval:!doc.domain && doc.enable_incoming",
"description": "e.g. pop.gmail.com / imap.gmail.com",
"fieldname": "email_server",
"fieldtype": "Data",
@@ -317,6 +376,7 @@
"label": "Email Server",
"length": 0,
"no_copy": 0,
+ "options": "domain.email_server",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -334,7 +394,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "enable_incoming",
+ "depends_on": "eval:!doc.domain && doc.enable_incoming",
"fieldname": "use_ssl",
"fieldtype": "Check",
"hidden": 0,
@@ -346,6 +406,7 @@
"label": "Use SSL",
"length": 0,
"no_copy": 0,
+ "options": "domain.use_ssl",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -364,7 +425,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
- "depends_on": "enable_incoming",
+ "depends_on": "eval:!doc.domain && doc.enable_incoming",
"description": "Ignore attachments over this size",
"fieldname": "attachment_limit",
"fieldtype": "Int",
@@ -377,6 +438,7 @@
"label": "Attachment Limit (MB)",
"length": 0,
"no_copy": 0,
+ "options": "domain.attachment_limit",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -394,6 +456,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "default": "",
"depends_on": "enable_incoming",
"description": "Append as communication against this DocType (must have fields, \"Status\", \"Subject\")",
"fieldname": "append_to",
@@ -402,7 +465,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
+ "in_list_view": 0,
"in_standard_filter": 1,
"label": "Append To",
"length": 0,
@@ -629,7 +692,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "enable_outgoing",
+ "depends_on": "eval:!doc.domain && doc.enable_outgoing",
"description": "e.g. smtp.gmail.com",
"fieldname": "smtp_server",
"fieldtype": "Data",
@@ -642,6 +705,7 @@
"label": "SMTP Server",
"length": 0,
"no_copy": 0,
+ "options": "domain.smtp_server",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -659,7 +723,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "enable_outgoing",
+ "depends_on": "eval:!doc.domain && doc.enable_outgoing",
"fieldname": "use_tls",
"fieldtype": "Check",
"hidden": 0,
@@ -671,6 +735,7 @@
"label": "Use TLS",
"length": 0,
"no_copy": 0,
+ "options": "domain.use_tls",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -688,7 +753,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "enable_outgoing",
+ "depends_on": "eval:!doc.domain && doc.enable_outgoing",
"description": "If non standard port (e.g. 587)",
"fieldname": "smtp_port",
"fieldtype": "Data",
@@ -701,6 +766,7 @@
"label": "Port",
"length": 0,
"no_copy": 0,
+ "options": "domain.smtp_port",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -1029,6 +1095,118 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "uid_validity",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "uid validity",
+ "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": "uidnext",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "uidnext",
+ "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": "no_remaining",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "No of emails remaining to be synced",
+ "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": "no_failed",
+ "fieldtype": "Int",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "no failed attempts",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
}
],
"hide_heading": 0,
@@ -1042,7 +1220,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-12-29 14:39:56.456917",
+ "modified": "2017-01-06 13:57:39.516766",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Account",
@@ -1066,7 +1244,7 @@
"report": 0,
"role": "System Manager",
"set_user_permissions": 1,
- "share": 1,
+ "share": 0,
"submit": 0,
"write": 1
}
diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py
index e6915cfb18..90f632c8a3 100755
--- a/frappe/email/doctype/email_account/email_account.py
+++ b/frappe/email/doctype/email_account/email_account.py
@@ -12,7 +12,7 @@ from frappe.utils import validate_email_add, cint, get_datetime, DATE_FORMAT, st
from frappe.utils.user import is_system_user
from frappe.utils.jinja import render_template
from frappe.email.smtp import SMTPServer
-from frappe.email.receive import EmailServer, Email
+from frappe.email.receive import EmailServer, Email, get_unique_id
from poplib import error_proto
from dateutil.relativedelta import relativedelta
from datetime import datetime, timedelta
@@ -31,9 +31,6 @@ class EmailAccount(Document):
self.email_account_name = self.email_id.split("@", 1)[0]\
.replace("_", " ").replace(".", " ").replace("-", " ").title()
- if self.service:
- self.email_account_name = self.email_account_name + " " + self.service
-
self.name = self.email_account_name
def validate(self):
@@ -50,15 +47,21 @@ class EmailAccount(Document):
if frappe.local.flags.in_patch or frappe.local.flags.in_test:
return
- if self.enable_incoming and not self.append_to:
- frappe.throw(_("Append To is mandatory for incoming mails"))
+ #if self.enable_incoming and not self.append_to:
+ # frappe.throw(_("Append To is mandatory for incoming mails"))
- if not frappe.local.flags.in_install and not frappe.local.flags.in_patch:
- if self.enable_incoming:
- self.get_incoming_server()
-
- if self.enable_outgoing:
- self.check_smtp()
+ if not self.awaiting_password and not frappe.local.flags.in_install and not frappe.local.flags.in_patch:
+ if self.password:
+ if self.enable_incoming:
+ self.get_incoming_server()
+ self.no_failed = 0
+
+
+ if self.enable_outgoing:
+ self.check_smtp()
+ else:
+ if self.enable_incoming or self.enable_outgoing:
+ frappe.throw(_("Password is required or select Awaiting Password"))
if self.notify_if_unreplied:
if not self.send_notification_to:
@@ -71,9 +74,20 @@ class EmailAccount(Document):
if self.append_to not in valid_doctypes:
frappe.throw(_("Append To can be one of {0}").format(comma_or(valid_doctypes)))
+
+
def on_update(self):
"""Check there is only one default of each type."""
self.there_must_be_only_one_default()
+ if self.awaiting_password:
+ # push values to user_emails
+ frappe.db.sql("""UPDATE `tabUser Email` SET awaiting_password = 1
+ WHERE email_account = %(account)s""", {"account": self.name})
+ else:
+ frappe.db.sql("""UPDATE `tabUser Email` SET awaiting_password = 0
+ WHERE email_account = %(account)s""", {"account": self.name})
+ from frappe.core.doctype.user.user import ask_pass_update
+ ask_pass_update()
def there_must_be_only_one_default(self):
"""If current Email Account is default, un-default all other accounts."""
@@ -88,6 +102,18 @@ class EmailAccount(Document):
email_account.set(fn, 0)
email_account.save()
+ @frappe.whitelist()
+ def get_domain(self,email_id):
+ """look-up the domain and then full"""
+ try:
+ domain = email_id.split("@")
+ return frappe.db.sql("""select name,use_imap,email_server,use_ssl,smtp_server,use_tls,smtp_port
+ from `tabEmail Domain`
+ where name = %s
+ """,domain[1],as_dict=1)
+ except Exception:
+ pass
+
def check_smtp(self):
"""Checks SMTP settings."""
if self.enable_outgoing:
@@ -106,12 +132,18 @@ class EmailAccount(Document):
def get_incoming_server(self, in_receive=False):
"""Returns logged in POP3/IMAP connection object."""
+ if frappe.cache().get_value("workers:no-internet") == True:
+ return None
args = frappe._dict({
+ "email_account":self.name,
"host": self.email_server,
"use_ssl": self.use_ssl,
"username": getattr(self, "login_id", None) or self.email_id,
- "use_imap": self.use_imap
+ "use_imap": self.use_imap,
+ "uid_validity":self.uid_validity,
+ "uidnext":self.uidnext,
+ "no_remaining":self.no_remaining
})
if self.password:
args.password = self.get_password()
@@ -139,38 +171,46 @@ class EmailAccount(Document):
if in_receive:
# timeout while connecting, see receive.py connect method
description = frappe.message_log.pop() if frappe.message_log else "Socket Error"
- self.handle_incoming_connect_error(description=description)
-
+ if test_internet():
+ self.db_set("no_failed", self.no_failed + 1)
+ if self.no_failed > 2:
+ self.handle_incoming_connect_error(description=description)
+ else:
+ frappe.cache().set_value("workers:no-internet", True)
return None
else:
raise
-
+ if not in_receive:
+ if self.use_imap:
+ email_server.imap.logout()
# reset failed attempts count
self.set_failed_attempts_count(0)
return email_server
def handle_incoming_connect_error(self, description):
- '''Disable email account if 3 failed attempts found'''
- if self.get_failed_attempts_count() == 3:
- self.db_set("enable_incoming", 0)
-
- for user in get_system_managers(only_name=True):
- try:
- assign_to.add({
- 'assign_to': user,
- 'doctype': self.doctype,
- 'name': self.name,
- 'description': description,
- 'priority': 'High',
- 'notify': 1
- })
- except assign_to.DuplicateToDoError:
- frappe.message_log.pop()
- pass
+ if test_internet():
+ if self.get_failed_attempts_count() > 2:
+ self.db_set("enable_incoming", 0)
+
+ for user in get_system_managers(only_name=True):
+ try:
+ assign_to.add({
+ 'assign_to': user,
+ 'doctype': self.doctype,
+ 'name': self.name,
+ 'description': description,
+ 'priority': 'High',
+ 'notify': 1
+ })
+ except assign_to.DuplicateToDoError:
+ frappe.message_log.pop()
+ pass
+ else:
+ self.set_failed_attempts_count(self.get_failed_attempts_count() + 1)
else:
- self.set_failed_attempts_count(self.get_failed_attempts_count() + 1)
+ frappe.cache().set_value("workers:no-internet", True)
def set_failed_attempts_count(self, value):
frappe.cache().set('{0}:email-account-failed-attempts'.format(self.name), value)
@@ -181,46 +221,96 @@ class EmailAccount(Document):
def receive(self, test_mails=None):
"""Called by scheduler to receive emails from this EMail account using POP3/IMAP."""
if self.enable_incoming:
+ exceptions = []
if frappe.local.flags.in_test:
incoming_mails = test_mails
else:
email_server = self.get_incoming_server(in_receive=True)
if not email_server:
return
-
- incoming_mails = email_server.get_messages()
-
- exceptions = []
- for raw in incoming_mails:
try:
- communication = self.insert_communication(raw)
+ incoming_mails = email_server.get_messages()
+ except Exception as e:
+ frappe.db.sql("update `tabEmail Account` set no_remaining = NULL where name = %s",(email_server.settings.email_account), auto_commit=1)
+ incoming_mails = []
- except SentEmailInInbox:
+ for msg in incoming_mails:
+ try:
+
+ communication = self.insert_communication(msg)
+ #self.notify_update()
+
+ except SentEmailInInbox as e:
frappe.db.rollback()
+ if self.use_imap:
+ self.handle_bad_emails(email_server, msg[1], msg[0], "sent email in inbox")
- except Exception:
+
+ except Exception as e:
frappe.db.rollback()
log('email_account.receive')
+ if self.use_imap:
+ self.handle_bad_emails(email_server, msg[1], msg[0], frappe.get_traceback())
exceptions.append(frappe.get_traceback())
else:
frappe.db.commit()
attachments = [d.file_name for d in communication._attachments]
- # TODO fix bug where it sends emails to 'Adminsitrator' during testing
- communication.notify(attachments=attachments, fetched_from_email_account=True)
+ if communication.message_id and not communication.timeline_hide:
+ first = frappe.db.get_value("Communication", {"message_id": communication.message_id},["name"],order_by="creation",as_dict=1)
+ if first:
+ if first.name != communication.name:
+ frappe.db.sql("""update tabCommunication set timeline_hide =%s where name = %s""",(first.name,communication.name),auto_commit=1)
+
+ if self.no_remaining == '0' and not frappe.local.flags.in_test:
+ if communication.reference_doctype :
+ if not communication.timeline_hide and not communication.unread_notification_sent:
+ communication.notify(attachments=attachments, fetched_from_email_account=True)
+
+ #notify if user is linked to account
+ if len(incoming_mails)>0 and not frappe.local.flags.in_test:
+ frappe.publish_realtime('new_email', {"account":self.email_account_name,"number":len(incoming_mails)})
if exceptions:
raise Exception, frappe.as_json(exceptions)
- def insert_communication(self, raw):
+ def handle_bad_emails(self,email_server,uid,raw,reason):
+ if cint(email_server.settings.use_imap):
+ import email
+ try:
+ mail = email.message_from_string(raw)
+
+ unique_id = get_unique_id(mail)
+ message_id = mail.get('Message-ID')
+ except Exception:
+ message_id = "can't be parsed"
+
+ unhandled_email = frappe.get_doc({
+ "doctype": "Unhandled Email",
+ "email_account": email_server.settings.email_account,
+ "uid": uid,
+ "message_id": message_id,
+ "unique_id":unique_id,
+ "reason":reason
+ })
+ unhandled_email.save()
+ frappe.db.commit()
+
+ def insert_communication(self, msg):
+ if isinstance(msg,list):
+ raw, uid, seen = msg
+ else:
+ raw = msg
+ seen = uid = None
email = Email(raw)
- if email.from_email == self.email_id:
+ if email.from_email == self.email_id and not email.mail.get("Reply-To"):
# gmail shows sent emails in inbox
# and we don't want emails sent by us to be pulled back into the system again
+ # dont count emails sent by the system get those
raise SentEmailInInbox
-
+
communication = frappe.get_doc({
"doctype": "Communication",
"subject": email.subject,
@@ -229,14 +319,23 @@ class EmailAccount(Document):
"sent_or_received": "Received",
"sender_full_name": email.from_real_name,
"sender": email.from_email,
- "recipients": email.mail.get("To"),
- "cc": email.mail.get("CC"),
+ "recipients": email.To,
+ "cc": email.CC,
"email_account": self.name,
- "communication_medium": "Email"
+ "communication_medium": "Email",
+ "uid":uid,
+ "message_id":email.message_id,
+ "communication_date":email.date,
+ "has_attachment": 1 if email.attachments else 0,
+ "seen":seen,
+ "unique_id":email.unique_id
})
self.set_thread(communication, email)
+ if not self.no_remaining == '0':
+ communication.unread_notification_sent = 1
+
communication.flags.in_receive = True
communication.insert(ignore_permissions = 1)
@@ -275,7 +374,7 @@ class EmailAccount(Document):
parent = self.find_parent_from_in_reply_to(communication, email)
- if not parent:
+ if not parent and self.append_to:
self.set_sender_field_and_subject_field()
if not parent and self.append_to:
@@ -335,7 +434,7 @@ class EmailAccount(Document):
}, fields="name")
if parent:
- parent = frappe.get_doc(self.append_to, parent[0].name)
+ parent = frappe._dict(doctype=self.append_to, name=parent[0].name)
return parent
@@ -378,24 +477,34 @@ class EmailAccount(Document):
if in_reply_to and "@{0}".format(frappe.local.site) in in_reply_to:
# reply to a communication sent from the system
- email_queue = frappe.db.get_value('Email Queue', dict(message_id=in_reply_to), ['reference_doctype', 'reference_name'])
+ email_queue = frappe.db.get_value('Email Queue', dict(message_id=in_reply_to), ['communication','reference_doctype', 'reference_name'])
if email_queue:
- parent_doctype, parent_name = email_queue
+ parent_communication, parent_doctype, parent_name = email_queue
+ if parent_communication:
+ communication.in_reply_to = parent_communication
else:
reference, domain = in_reply_to.split("@", 1)
parent_doctype, parent_name = 'Communication', reference
if frappe.db.exists(parent_doctype, parent_name):
- parent = frappe.get_doc(parent_doctype, parent_name)
+ parent = frappe._dict(doctype=parent_doctype, name=parent_name)
# set in_reply_to of current communication
if parent_doctype=='Communication':
- communication.in_reply_to = parent_name
+ # communication.in_reply_to = email_queue.communication
if parent.reference_name:
# the true parent is the communication parent
parent = frappe.get_doc(parent.reference_doctype,
parent.reference_name)
+ if email.message_id:
+ first = frappe.db.get_value("Communication", {"message_id": email.message_id},["name", "reference_doctype", "reference_name"], order_by="creation", as_dict=1)
+
+ if first:
+ # set timeline hide to parent doc so are linked
+ communication.timeline_hide = first.name
+ if frappe.db.exists(first.reference_doctype, first.reference_name):
+ parent = frappe._dict(doctype=first.reference_doctype, name=first.reference_name)
return parent
@@ -438,6 +547,20 @@ def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len
if not txt: txt = ""
return [[d] for d in frappe.get_hooks("email_append_to") if txt in d]
+def test_internet(host="8.8.8.8", port=53, timeout=3):
+ """
+ Host: 8.8.8.8 (google-public-dns-a.google.com)
+ OpenPort: 53/tcp
+ Service: domain (DNS/TCP)
+ """
+ try:
+ socket.setdefaulttimeout(timeout)
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
+ return True
+ except Exception as ex:
+ print ex.message
+ return False
+
def notify_unreplied():
"""Sends email notifications if there are unreplied Communications
and `notify_if_unreplied` is set as true."""
@@ -451,6 +574,7 @@ def notify_unreplied():
"sent_or_received": "Received",
"reference_doctype": email_account.append_to,
"unread_notification_sent": 0,
+ "email_account":email_account.name,
"creation": ("<", datetime.now() - timedelta(seconds = (email_account.unreplied_for_mins or 30) * 60)),
"creation": (">", datetime.now() - timedelta(seconds = (email_account.unreplied_for_mins or 30) * 60 * 3))
}):
@@ -467,9 +591,13 @@ def notify_unreplied():
def pull(now=False):
"""Will be called via scheduler, pull emails from all enabled Email accounts."""
+ if frappe.cache().get_value("workers:no-internet") == True:
+ if test_internet():
+ frappe.cache().set_value("workers:no-internet", False)
+ else:
+ return
queued_jobs = get_jobs(site=frappe.local.site, key='job_name')[frappe.local.site]
-
- for email_account in frappe.get_list("Email Account", filters={"enable_incoming": 1}):
+ for email_account in frappe.get_list("Email Account",["name", "no_remaining"], filters={"enable_incoming": 1, "awaiting_password": 0}):
if now:
pull_from_email_account(email_account.name)
@@ -478,8 +606,12 @@ def pull(now=False):
job_name = 'pull_from_email_account|{0}'.format(email_account.name)
if job_name not in queued_jobs:
- enqueue(pull_from_email_account, 'short', event='all', job_name=job_name,
- email_account=email_account.name)
+ if email_account.no_remaining == '0':
+ enqueue(pull_from_email_account, 'short', event='all', job_name=job_name,
+ email_account=email_account.name)
+ else:
+ enqueue(pull_from_email_account, 'long', event='all', job_name=job_name,
+ email_account=email_account.name)
def pull_from_email_account(email_account):
'''Runs within a worker process'''
diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py
index 5513c7dc42..549d721e64 100644
--- a/frappe/email/doctype/email_account/test_email_account.py
+++ b/frappe/email/doctype/email_account/test_email_account.py
@@ -129,7 +129,7 @@ class TestEmailAccount(unittest.TestCase):
# send
sent_name = make(subject = "Test", content="test content",
- recipients="test_receiver@example.com", sender="test@example.com",
+ recipients="test_receiver@example.com", sender="test@example.com",doctype="ToDo",name=frappe.get_last_doc("ToDo").name,
send_email=True)["name"]
sent_mail = email.message_from_string(frappe.get_last_doc("Email Queue").message)
@@ -146,8 +146,8 @@ class TestEmailAccount(unittest.TestCase):
sent = frappe.get_doc("Communication", sent_name)
comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"})
- self.assertEquals(comm.reference_doctype, sent.doctype)
- self.assertEquals(comm.reference_name, sent.name)
+ self.assertEquals(comm.reference_doctype, sent.reference_doctype)
+ self.assertEquals(comm.reference_name, sent.reference_name)
def test_threading_by_subject(self):
frappe.db.sql("""delete from tabCommunication
diff --git a/frappe/email/doctype/email_account/test_records.json b/frappe/email/doctype/email_account/test_records.json
index 70e8b2f927..fbe7d9c281 100644
--- a/frappe/email/doctype/email_account/test_records.json
+++ b/frappe/email/doctype/email_account/test_records.json
@@ -3,6 +3,7 @@
"is_default": 1,
"is_global": 1,
"doctype": "Email Account",
+ "domain":"example.com",
"append_to": "ToDo",
"email_account_name": "_Test Email Account 1",
"enable_outgoing": 1,
@@ -17,6 +18,11 @@
"notify_if_unreplied": 1,
"unreplied_for_mins": 20,
"send_notification_to": "test_unreplied@example.com",
- "pop3_server": "pop.test.example.com"
+ "pop3_server": "pop.test.example.com",
+ "no_remaining":"0"
+ },
+ {
+ "doctype": "ToDo",
+ "description":"test doctype"
}
]
diff --git a/frappe/email/doctype/email_domain/__init__.py b/frappe/email/doctype/email_domain/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/email/doctype/email_domain/email_domain.js b/frappe/email/doctype/email_domain/email_domain.js
new file mode 100644
index 0000000000..3c3c47ff78
--- /dev/null
+++ b/frappe/email/doctype/email_domain/email_domain.js
@@ -0,0 +1,16 @@
+
+frappe.ui.form.on("Email Domain", {
+ email_id:function(frm){
+ frm.set_value("domain_name",frm.doc.email_id.split("@")[1])
+ },
+ refresh:function(frm){
+ if (frm.doc.email_id){frm.set_value("domain_name",frm.doc.email_id.split("@")[1])}
+ if (frm.doc.__islocal != 1) {
+ route = frappe.get_prev_route()
+ if (frappe.route_titles["return to email_account"]){
+ delete frappe.route_titles["return to email_account"];
+ frappe.set_route(route);
+ }
+ }
+ }
+})
diff --git a/frappe/email/doctype/email_domain/email_domain.json b/frappe/email/doctype/email_domain/email_domain.json
new file mode 100644
index 0000000000..b2a2cd69a6
--- /dev/null
+++ b/frappe/email/doctype/email_domain/email_domain.json
@@ -0,0 +1,443 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:domain_name",
+ "beta": 0,
+ "creation": "2016-03-29 10:50:48.848239",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 0,
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "email_settings",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "",
+ "fieldname": "domain_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "domain name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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,
+ "description": "",
+ "fieldname": "email_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Example Email Address",
+ "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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "mailbox_settings",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "description": "e.g. pop.gmail.com / imap.gmail.com",
+ "fieldname": "email_server",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Email Server",
+ "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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "use_imap",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Use IMAP",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "use_ssl",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Use SSL",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "1",
+ "depends_on": "",
+ "description": "Ignore attachments over this size",
+ "fieldname": "attachment_limit",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Attachment Limit (MB)",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "description": "Append as communication against this DocType (must have fields, \"Status\", \"Subject\")",
+ "fieldname": "append_to",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Append To",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "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": "outgoing_mail_settings",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "description": "e.g. smtp.gmail.com",
+ "fieldname": "smtp_server",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "SMTP Server",
+ "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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "use_tls",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Use TLS",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "description": "If non standard port (e.g. 587)",
+ "fieldname": "smtp_port",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Port",
+ "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
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "icon-inbox",
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2016-12-23 13:31:58.408528",
+ "modified_by": "Administrator",
+ "module": "Email",
+ "name": "Email Domain",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 0,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 0,
+ "role": "System Manager",
+ "set_user_permissions": 1,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/frappe/email/doctype/email_domain/email_domain.py b/frappe/email/doctype/email_domain/email_domain.py
new file mode 100644
index 0000000000..baca02ade9
--- /dev/null
+++ b/frappe/email/doctype/email_domain/email_domain.py
@@ -0,0 +1,80 @@
+# -*- 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
+from frappe.model.document import Document
+from frappe.utils import validate_email_add ,cint
+import imaplib,poplib,smtplib
+
+class EmailDomain(Document):
+ def autoname(self):
+ if self.domain_name:
+ self.name = self.domain_name
+
+
+ def validate(self):
+ """Validate email id and check POP3/IMAP and SMTP connections is enabled."""
+ if self.email_id:
+ validate_email_add(self.email_id, True)
+
+ if frappe.local.flags.in_patch or frappe.local.flags.in_test:
+ return
+
+ if not frappe.local.flags.in_install and not frappe.local.flags.in_patch:
+ try:
+ if self.use_imap:
+ if self.use_ssl:
+ test = imaplib.IMAP4_SSL(self.email_server)
+ else:
+ test = imaplib.IMAP4(self.email_server)
+
+ else:
+ if self.use_ssl:
+ test = poplib.POP3_SSL(self.email_server)
+ else:
+ test = poplib.POP3(self.email_server)
+
+ except Exception:
+ frappe.throw("Incoming email account not correct")
+ return None
+ finally:
+ try:
+ if self.use_imap:
+ test.logout()
+ else:
+ test.quit()
+ except Exception:
+ pass
+ try:
+ if self.use_tls and not self.smtp_port:
+ self.port = 587
+ sess = smtplib.SMTP((self.smtp_server or "").encode('utf-8'), cint(self.smtp_port) or None)
+ sess.quit()
+ except Exception as e:
+ frappe.throw("Outgoing email account not correct")
+ return None
+ return
+
+ def on_update(self):
+ """update all email accounts using this domain"""
+ for email_account in frappe.get_all("Email Account",
+ filters={"domain": self.name}):
+
+ try:
+ email_account = frappe.get_doc("Email Account",
+ email_account.name)
+ email_account.set("email_server",self.email_server)
+ email_account.set("use_imap",self.use_imap)
+ email_account.set("use_ssl",self.use_ssl)
+ email_account.set("use_tls",self.use_tls)
+ email_account.set("attachment_limit",self.attachment_limit)
+ email_account.set("smtp_server",self.smtp_server)
+ email_account.set("smtp_port",self.smtp_port)
+ email_account.save()
+ except Exception, e:
+ frappe.msgprint(email_account.name)
+ frappe.throw(e)
+ return None
+
diff --git a/frappe/email/doctype/email_domain/test_email_domain.py b/frappe/email/doctype/email_domain/test_email_domain.py
new file mode 100644
index 0000000000..0050b3250a
--- /dev/null
+++ b/frappe/email/doctype/email_domain/test_email_domain.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+# test_records = frappe.get_test_records('Domain')
+
+class TestDomain(unittest.TestCase):
+ pass
diff --git a/frappe/email/doctype/email_flag_queue/__init__.py b/frappe/email/doctype/email_flag_queue/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/email/doctype/email_flag_queue/email_flag_queue.js b/frappe/email/doctype/email_flag_queue/email_flag_queue.js
new file mode 100644
index 0000000000..19c4d4b0c1
--- /dev/null
+++ b/frappe/email/doctype/email_flag_queue/email_flag_queue.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Email Flag Queue', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/frappe/email/doctype/email_flag_queue/email_flag_queue.json b/frappe/email/doctype/email_flag_queue/email_flag_queue.json
new file mode 100644
index 0000000000..64932b069d
--- /dev/null
+++ b/frappe/email/doctype/email_flag_queue/email_flag_queue.json
@@ -0,0 +1,144 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-04-20 15:29:39.785172",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 0,
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "comm_name",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "comm_name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Communication",
+ "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": "action",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "action",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "flag",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "flag",
+ "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
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2016-12-22 10:37:28.738451",
+ "modified_by": "Administrator",
+ "module": "Email",
+ "name": "Email Flag Queue",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/frappe/email/doctype/email_flag_queue/email_flag_queue.py b/frappe/email/doctype/email_flag_queue/email_flag_queue.py
new file mode 100644
index 0000000000..487ef7db50
--- /dev/null
+++ b/frappe/email/doctype/email_flag_queue/email_flag_queue.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, 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 EmailFlagQueue(Document):
+ pass
diff --git a/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py b/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py
new file mode 100644
index 0000000000..644a2a8ff7
--- /dev/null
+++ b/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+# test_records = frappe.get_test_records('Email Flag Queue')
+
+class TestEmailFlagQueue(unittest.TestCase):
+ pass
diff --git a/frappe/email/doctype/email_queue/email_queue_list.js b/frappe/email/doctype/email_queue/email_queue_list.js
index 1249183467..5ea247dbb9 100644
--- a/frappe/email/doctype/email_queue/email_queue_list.js
+++ b/frappe/email/doctype/email_queue/email_queue_list.js
@@ -2,5 +2,22 @@ frappe.listview_settings['Email Queue'] = {
get_indicator: function(doc) {
colour = {'Sent': 'green', 'Sending': 'blue', 'Not Sent': 'grey', 'Error': 'red', 'Expired': 'orange'};
return [__(doc.status), colour[doc.status], "status,=," + doc.status];
+ },
+ refresh: function(doclist){
+ if (has_common(user_roles, ["Administrator", "System Manager"])){
+ if (cint(frappe.defaults.get_default("hold_queue"))){
+ doclist.page.clear_inner_toolbar()
+ doclist.page.add_inner_button(__("Resume Sending"), function() {
+ frappe.defaults.set_default("hold_queue", 0);
+ cur_list.refresh();
+ })
+ } else {
+ doclist.page.clear_inner_toolbar()
+ doclist.page.add_inner_button(__("Suspend Sending"), function() {
+ frappe.defaults.set_default("hold_queue", 1)
+ cur_list.refresh();
+ })
+ }
+ }
}
}
diff --git a/frappe/email/doctype/unhandled_email/__init__.py b/frappe/email/doctype/unhandled_email/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/email/doctype/unhandled_email/test_unhandled_email.py b/frappe/email/doctype/unhandled_email/test_unhandled_email.py
new file mode 100644
index 0000000000..6cabcf6ec2
--- /dev/null
+++ b/frappe/email/doctype/unhandled_email/test_unhandled_email.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+# test_records = frappe.get_test_records('Unhandled Emails')
+
+class TestUnhandledEmail(unittest.TestCase):
+ pass
diff --git a/frappe/email/doctype/unhandled_email/unhandled_email.json b/frappe/email/doctype/unhandled_email/unhandled_email.json
new file mode 100644
index 0000000000..ab1972bd71
--- /dev/null
+++ b/frappe/email/doctype/unhandled_email/unhandled_email.json
@@ -0,0 +1,204 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "creation": "2016-04-14 09:41:45.892975",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "email_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Email Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Email Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "uid",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "uid",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reason",
+ "fieldtype": "Long Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Reason",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "message_id",
+ "fieldtype": "Code",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Message-id",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "unique_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Unique id",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "raw",
+ "fieldtype": "Code",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Raw Email",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2016-07-21 00:35:02.752145",
+ "modified_by": "Administrator",
+ "module": "Email",
+ "name": "Unhandled Email",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 0,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 0,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 0,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/frappe/email/doctype/unhandled_email/unhandled_email.py b/frappe/email/doctype/unhandled_email/unhandled_email.py
new file mode 100644
index 0000000000..edbb410106
--- /dev/null
+++ b/frappe/email/doctype/unhandled_email/unhandled_email.py
@@ -0,0 +1,11 @@
+# -*- 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
+from frappe.model.document import Document
+
+class UnhandledEmail(Document):
+ def on_trash(self):
+ frappe.db.set_value("Email Account",self.email_account,"no_remaining",None)
diff --git a/frappe/email/page/__init__.py b/frappe/email/page/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/email/page/email_inbox/__init__.py b/frappe/email/page/email_inbox/__init__.py
new file mode 100644
index 0000000000..4e141cbed1
--- /dev/null
+++ b/frappe/email/page/email_inbox/__init__.py
@@ -0,0 +1,119 @@
+# -*- 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_list(email_account,start,page_length):
+ inbox_list = []
+ communications = frappe.db.sql("""select name, sender, sender_full_name, actualdate, recipients, communication_medium as comment_type, subject, status ,reference_doctype,reference_name,timeline_doctype,timeline_name,timeline_label,sent_or_received,uid,message_id, seen,nomatch,has_attachment
+ from tabCommunication
+ where email_account = %(email_account)s and deleted = 0
+ ORDER BY actualdate DESC
+ LIMIT %(page_length)s OFFSET %(start)s""",{"email_account":email_account,"start":int(start),"page_length":int(page_length)},as_dict=1)
+ for c in communications:
+ comm = {}
+
+ comm["name"] = c.get('name')
+ comm["reference_doctype"] = c.get('reference_doctype')
+ comm["reference_name"] = c.get('reference_name')
+ if c.get('recipients') != None:
+ comm["recipients"] = c.get('recipients').replace('"',"").strip("<>")
+ comm["sender"] = c.get('sender')
+ comm["sender_full_name"] = c.get('sender_full_name')
+ comm["actualdate"] = c.get('actualdate')
+ comm["subject"] = c.get('subject')
+ comm["status"] = c.get('status')
+ comm["content"] = c.get('content')
+ comm["timeline_doctype"] = c.get('timeline_doctype')
+ comm["timeline_name"] = c.get('timeline_name')
+ comm["timeline_label"] = c.get('timeline_label')
+ comm["sent_or_received"] = c.get('sent_or_received')
+ comm["uid"]= c.get('uid')
+ comm["message_id"]=c.get("message_id")
+ comm["seen"] = c.get('seen')
+ comm["nomatch"] =c.get('nomatch')
+ comm["has_attachment"]=c.get('has_attachment')
+ inbox_list.append(comm)
+ return inbox_list
+
+@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,field):
+ names = json.loads(names)
+ class Found(Exception):
+ pass
+
+ for item in names:
+ if item["uid"]:
+ state = frappe.db.get_value("Communication", item["name"], field)
+ if (action =='+FLAGS' and state ==0) or (action =='-FLAGS' and state ==1): #check states are correct
+ try:
+ queue = frappe.db.sql("""select name,action,flag from `tabEmail Flag Queue`
+ where comm_name = %(name)s""",{"name":item["name"]},as_dict=1)
+ for q in queue:
+ if q.flag==flag:#is same email with same flag
+ if q.action!=action:#to prevent flag local and server states being out of sync
+ frappe.delete_doc("Email Flag Queue", q.name)
+ raise Found
+
+ flag_queue = frappe.get_doc({
+ "doctype": "Email Flag Queue",
+ "comm_name": str(item["name"]),
+ "action":action,
+ "flag":flag
+ })
+ flag_queue.save(ignore_permissions=True);
+ except Found:
+ pass
+
+@frappe.whitelist()
+def setnomatch(name):
+ frappe.db.set_value("Communication", str(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", str(d["name"]), field, val,update_modified=False)
+
+@frappe.whitelist()
+def get_length(email_account):
+ try:
+ return frappe.db.sql("""select count(name)
+ from tabCommunication
+ where deleted = 0 and email_account= %(email_account)s""",{"email_account":email_account})
+ except:
+ return 0
+
+@frappe.whitelist()
+def get_accounts(user):
+ try:
+ return frappe.db.sql("""select email_account,email_id
+ from `tabUser Email`
+ where parent = %(user)s
+ order by idx""",{"user":user},as_dict=1)
+ except:
+ return
+
+# for the selection/deletion of multiple items
+def set_multiple_status(names, status):
+ names = json.loads(names)
+ for name in names:
+ set_status(name, status)
+
+def set_status(name, status):
+ st = frappe.get_doc("Issue", name)
+ st.status = status
+ st.save()
diff --git a/frappe/email/page/email_inbox/email_inbox.js b/frappe/email/page/email_inbox/email_inbox.js
new file mode 100644
index 0000000000..36989bd561
--- /dev/null
+++ b/frappe/email/page/email_inbox/email_inbox.js
@@ -0,0 +1,636 @@
+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.Inbox({
+ 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.Inbox = frappe.ui.Listing.extend({
+ init: function(opts) {
+ $.extend(this, opts);
+ wrap = this;
+ this.wrapper = opts.wrapper;
+ this.filters = {};
+ this.page_length = 20;
+ this.start = 0;
+ this.cur_page = 1;
+ this.no_result_message = 'No Emails to Display';
+
+ this.render_sidemenu();
+ if (this.account) {
+ 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.filter_list.add_filter("Communication", "deleted", "=", "No");
+ this.render_headers();
+ this.render_footer();
+ this.run();
+ this.render_buttons();
+ this.init_select_all();
+ var me = this;
+ frappe.realtime.on("new_email", function(data) {
+ for(var i =0; i ';
+ 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 += '';
+ buttons += rows;
+ buttons += '';
+ 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-delete").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();
+ me.filter_list.add_filter("Communication", "deleted", "=", "No");
+ if (me.filter_list.reload_stats){me.filter_list.reload_stats()}
+ me.refresh();
+ });
+ }
+ }
+ })
+ },
+ 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", "nomatch", "has_attachment", "timeline_hide"],
+ filters: this.filter_list.get_filters(),
+ order_by: 'communication_date desc',
+ save_list_settings: false
+ };
+
+ args.filters = args.filters.concat(this.filter_list.default_filters)
+
+ return args;
+ },
+ render_list:function(data){
+ var me = this
+ $(me.wrapper).find(".result-list").html("");
+ for (var i = 0; i < data.length; i++)
+ {
+ this.prepare_row(data[i]);
+ $(frappe.render_template("inbox_list", {data: data[i]})).data("data", data[i]).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);
+ });
+ },
+ prepare_row:function(row){
+ row.hascompany =(row.customer || row.supplier) ? true : false;
+ row.seen = this.account!="Sent" ? row.seen : 1;
+ },
+ render_footer:function(){
+ var me = this;
+ me.footer = $(me.wrapper).append(frappe.render_template("inbox_footer","")).find(".foot-con");
+ frappe.require('assets/frappe/js/lib/bootstrap-paginator.min.js',function(){
+ me.footer.bootstrapPaginator({
+ currentPage: 1,
+ totalPages: 10,
+ bootstrapMajorVersion:3,
+ onPageClicked: function(e,originalEvent,type,page){
+ me.cur_page = page;
+ $('.footer-numbers').html('showing: ' + (me.cur_page - 1) * me.page_length + ' to ' + (
+ (me.data_length > (me.cur_page * me.page_length))?(me.cur_page * me.page_length):me.data_length) + ' of ' + me.data_length);
+ me.run(true,true);
+ }
+ });
+ });
+ $(me.wrapper).find('.list-paging-area').addClass('hide');
+ },
+ update_footer:function(){
+ var me = this;
+ //default filter used for filters
+ var filters = me.filter_list.get_filters();
+ if (me.filter_list.default_filters){filters = filters.concat(me.filter_list.default_filters)}
+ return frappe.call({
+ method: me.method || 'frappe.desk.query_builder.runquery',
+ type: "GET",
+ freeze: (me.freeze != undefined ? me.freeze : true),
+ args: {
+ doctype: me.doctype,
+ fields: ["count(*) as number"],
+ filters: filters,
+ save_list_settings: false
+ },
+ callback: function (r) {
+ r.values = me.get_values_from_response(r.message);
+ me.data_length = r.values[0]["number"]
+ if (me.data_length != 0) {
+ me.footer.show();
+ me.last_page = Math.ceil(me.data_length / me.page_length);
+ frappe.require('assets/frappe/js/lib/bootstrap-paginator.min.js',function(){
+ me.footer.bootstrapPaginator({currentPage: 1, totalPages: me.last_page})
+ });
+ } else {
+ me.footer.hide();
+ }
+ $('.footer-numbers').html('showing: ' + (me.cur_page - 1) * me.page_length + ' to ' + (
+ (me.data_length > (me.cur_page * me.page_length)) ? (me.cur_page * me.page_length) : me.data_length) + ' of ' + me.data_length);
+ },
+ no_spinner: this.no_loading
+ });
+ },
+ 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_titles["update_contact"];
+ frappe.route_titles["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")[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$2 ");
+ }
+
+ return c
+ },
+ init_select_all: function () {
+ var me = this;
+
+ $(".list-select-all").on("click", function () {
+ $(me.wrapper).find('.list-delete').prop("checked", $(this).prop("checked"));
+ me.toggle_actions();
+ });
+
+ $(me.wrapper).on("click", ".list-delete", 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-delete:checked").last().parents(".list-row");
+ if ($start_row) {
+ $start_row.nextUntil($end_row).find(".list-delete").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
+
+
+
+ {% if(data.attachments && data.attachments.length) { %}
+
+ {% $.each(data.attachments, function(i, a) { %}
+
+ {% }); %}
+
+ {% } %}
+
+
+
+ {%= data.comment_html %}
+
+
+
+
diff --git a/frappe/email/page/email_inbox/inbox_email_actions.html b/frappe/email/page/email_inbox/inbox_email_actions.html
new file mode 100644
index 0000000000..5247f7709b
--- /dev/null
+++ b/frappe/email/page/email_inbox/inbox_email_actions.html
@@ -0,0 +1,11 @@
+ Actions
+
+
+
\ No newline at end of file
diff --git a/frappe/email/page/email_inbox/inbox_filter.html b/frappe/email/page/email_inbox/inbox_filter.html
new file mode 100644
index 0000000000..e31fa10359
--- /dev/null
+++ b/frappe/email/page/email_inbox/inbox_filter.html
@@ -0,0 +1,8 @@
+
+
+
+
+ {%= __("Show Filter Dashboard") %}
+
+
+
\ No newline at end of file
diff --git a/frappe/email/page/email_inbox/inbox_footer.html b/frappe/email/page/email_inbox/inbox_footer.html
new file mode 100644
index 0000000000..74e189e329
--- /dev/null
+++ b/frappe/email/page/email_inbox/inbox_footer.html
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/frappe/email/page/email_inbox/inbox_headers.html b/frappe/email/page/email_inbox/inbox_headers.html
new file mode 100644
index 0000000000..55d16d8f8c
--- /dev/null
+++ b/frappe/email/page/email_inbox/inbox_headers.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ Sender
+
+
+ Subject
+
+
+ Company/User
+
+
+ Linked Document
+
+
+
+
+
+ Sent
+
+
+
+
+
diff --git a/frappe/email/page/email_inbox/inbox_list.html b/frappe/email/page/email_inbox/inbox_list.html
new file mode 100644
index 0000000000..b438f7dbfb
--- /dev/null
+++ b/frappe/email/page/email_inbox/inbox_list.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if(data.has_attachment){ %}
+
+ {% } %}
+
+
+
+
+
+ {%= comment_when(data.communication_date,true) %}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frappe/email/queue.py b/frappe/email/queue.py
index afbb656dc0..01e7d8b409 100755
--- a/frappe/email/queue.py
+++ b/frappe/email/queue.py
@@ -10,7 +10,7 @@ from frappe.email.smtp import SMTPServer, get_outgoing_email_account
from frappe.email.email_body import get_email, get_formatted_html
from frappe.utils.verified_command import get_signed_params, verify_request
from html2text import html2text
-from frappe.utils import get_url, nowdate, encode, now_datetime, add_days, split_emails, cstr
+from frappe.utils import get_url, nowdate, encode, now_datetime, add_days, split_emails, cstr, cint
from rq.timeouts import JobTimeoutException
from frappe.utils.scheduler import log
@@ -19,7 +19,7 @@ class EmailLimitCrossedError(frappe.ValidationError): pass
def send(recipients=None, sender=None, subject=None, message=None, reference_doctype=None,
reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
attachments=None, reply_to=None, cc=[], message_id=None, in_reply_to=None, send_after=None,
- expose_recipients=None, send_priority=1, communication=None, now=False):
+ expose_recipients=None, send_priority=1, communication=None, now=False, read_receipt=None):
"""Add email to sending queue (Email Queue)
:param recipients: List of recipients.
@@ -85,7 +85,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
# add to queue
email_queue = add(recipients, sender, subject, email_content, email_text_context, reference_doctype,
reference_name, attachments, reply_to, cc, message_id, in_reply_to, send_after, send_priority, email_account=email_account, communication=communication,
- unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, expose_recipients=expose_recipients)
+ unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, expose_recipients=expose_recipients, read_receipt=read_receipt)
if now:
send_one(email_queue.name, now=True)
@@ -93,7 +93,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
def add(recipients, sender, subject, formatted, text_content=None,
reference_doctype=None, reference_name=None, attachments=None, reply_to=None,
cc=[], message_id=None, in_reply_to=None, send_after=None, send_priority=1, email_account=None,
- communication=None, unsubscribe_method=None, unsubscribe_params=None, expose_recipients=None):
+ communication=None, unsubscribe_method=None, unsubscribe_params=None, expose_recipients=None, read_receipt=None):
"""Add to Email Queue"""
e = frappe.new_doc('Email Queue')
e.priority = send_priority
@@ -104,6 +104,8 @@ def add(recipients, sender, subject, formatted, text_content=None,
cc=cc, email_account=email_account, expose_recipients=expose_recipients)
mail.set_message_id(message_id)
+ if read_receipt:
+ mail.msg_root["Disposition-Notification-To"] = sender
if in_reply_to:
mail.set_in_reply_to(in_reply_to)
@@ -240,6 +242,9 @@ def flush(from_test=False):
for i in xrange(cache.llen('cache_email_queue')):
email = cache.lpop('cache_email_queue')
+ if cint(frappe.defaults.get_defaults().get("hold_queue"))==1:
+ break
+
if email:
send_one(email, smtpserver, auto_commit, from_test=from_test)
@@ -272,6 +277,8 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals
if frappe.are_emails_muted():
frappe.msgprint(_("Emails are muted"))
return
+ if cint(frappe.defaults.get_defaults().get("hold_queue"))==1 :
+ return
if email.status not in ('Not Sent','Partially Sent') :
# rollback to release lock and return
diff --git a/frappe/email/receive.py b/frappe/email/receive.py
index 7bceaab1be..f6898de432 100644
--- a/frappe/email/receive.py
+++ b/frappe/email/receive.py
@@ -2,7 +2,7 @@
# MIT License. See license.txt
from __future__ import unicode_literals
-import time, _socket, poplib, imaplib, email, email.utils, datetime, chardet, re
+import time, _socket, poplib, imaplib, email, email.utils, datetime, chardet, re, hashlib
from email_reply_parser import EmailReplyParser
from email.header import decode_header
import frappe
@@ -11,6 +11,9 @@ from frappe.utils import (extract_email_id, convert_utc_to_user_timezone, now,
cint, cstr, strip, markdown)
from frappe.utils.scheduler import log
from frappe.utils.file_manager import get_random_filename, save_file, MaxFileSizeReachedError
+from email_reply_parser import EmailReplyParser
+from email.header import decode_header
+from frappe.utils.file_manager import get_random_filename
class EmailSizeExceededError(frappe.ValidationError): pass
class EmailTimeoutError(frappe.ValidationError): pass
@@ -46,6 +49,7 @@ class EmailServer:
try:
if cint(self.settings.use_ssl):
self.imap = Timed_IMAP4_SSL(self.settings.host, timeout=frappe.conf.get("pop_timeout"))
+ #self.imap = imaplib.IMAP4_SSL(self.settings.host)
else:
self.imap = Timed_IMAP4(self.settings.host, timeout=frappe.conf.get("pop_timeout"))
self.imap.login(self.settings.username, self.settings.password)
@@ -98,42 +102,56 @@ class EmailServer:
frappe.db.commit()
- if not self.connect():
- return []
-
try:
# track if errors arised
self.errors = False
self.latest_messages = []
+ if cint(self.settings.use_imap):
+ uid_validity = self.get_status()
+ else:
+ email_list = self.get_new_mails()
- email_list = self.get_new_mails()
- num = num_copy = len(email_list)
-
- # WARNING: Hard coded max no. of messages to be popped
- if num > 20: num = 20
# size limits
self.total_size = 0
self.max_email_size = cint(frappe.local.conf.get("max_email_size"))
self.max_total_size = 5 * self.max_email_size
+ if cint(self.settings.use_imap):
+ #try:
+ if self.check_uid_validity(uid_validity):
+ email_list = self.get_new_mails()
+ if email_list:
+ self.get_imap_messages(email_list)
+ self.sync_flags()
+ self.get_seen()
+ self.push_deleted()
- for i, message_meta in enumerate(email_list):
- # do not pull more than NUM emails
- if (i+1) > num:
- break
+ else:
+ pass
- try:
- self.retrieve_message(message_meta, i+1)
- except (TotalSizeExceededError, EmailTimeoutError, LoginLimitExceeded):
- break
+ else:
+ num = num_copy = len(email_list)
- # WARNING: Mark as read - message number 101 onwards from the pop list
- # This is to avoid having too many messages entering the system
- num = num_copy
- if not cint(self.settings.use_imap):
- if num > 100 and not self.errors:
- for m in xrange(101, num+1):
- self.pop.dele(m)
+ # WARNING: Hard coded max no. of messages to be popped
+ if num > 20: num = 20 #20
+
+ for i, message_meta in enumerate(email_list):
+ # do not pull more than NUM emails
+ if (i+1) > num:
+ break
+
+ try:
+ self.retrieve_message(message_meta, i+1)
+ except (TotalSizeExceededError, EmailTimeoutError, LoginLimitExceeded):
+ break
+
+ # WARNING: Mark as read - message number 101 onwards from the pop list
+ # This is to avoid having too many messages entering the system
+ num = num_copy
+ if not cint(self.settings.use_imap):
+ if num > 100 and not self.errors:
+ for m in xrange(101, num+1):
+ self.pop.dele(m)
except Exception, e:
if self.has_login_limit_exceeded(e):
@@ -151,17 +169,316 @@ class EmailServer:
return self.latest_messages
+ def get_status(self):
+ passed, status = self.imap.status("Inbox", "(UIDNEXT UIDVALIDITY)")
+ match = re.search(r"(?<=UIDVALIDITY )[0-9]*", status[0], re.U | re.I)
+ if match:
+ uid_validity = match.group(0)
+ match = re.search(r"(?<=UIDNEXT )[0-9]*", status[0], re.U | re.I)
+ if match:
+ uidnext = match.group(0)
+ frappe.db.sql("update `tabEmail Account` set uidnext = %s where name = %s",(uidnext, self.settings.email_account), auto_commit=1)
+ self.settings.newuidnext = uidnext
+ return uid_validity
+
def get_new_mails(self):
"""Return list of new mails"""
if cint(self.settings.use_imap):
self.imap.select("Inbox")
- response, message = self.imap.uid('search', None, "UNSEEN")
+ if self.settings.no_remaining == '0' and self.settings.uidnext:
+ if self.settings.uidnext == self.settings.newuidnext:
+ return False
+ else:
+ #request all messages between last uidnext and new
+ return True
+ else:
+ response, message = self.imap.uid('search', None, "ALL")
email_list = message[0].split()
else:
email_list = self.pop.list()[1]
return email_list
+ def check_uid_validity(self, uid_validity):
+ if self.settings.uid_validity:
+ if self.settings.uid_validity == uid_validity:
+ return True
+ else:
+ #validity changed
+ self.settings.no_remaining = None
+ self.rebuild_uid(uid_validity)
+ return True
+
+ else:#if email account settings is blank
+ uid_list = frappe.db.sql("""select uid
+ from tabCommunication
+ where email_account = %(email_account)s and uid is not Null
+ order by uid
+ """,{"email_account":self.settings.email_account}, as_list=1)
+ new_uid_list = []
+ for i in uid_list:
+ new_uid_list.append(i[0])
+
+ if new_uid_list:#if email account
+ self.rebuild_uid(uid_validity)
+ return True
+ else:# if no uid and no emails with uid
+ frappe.db.set_value("Email Account", self.settings.email_account, "uid_validity", uid_validity)
+ frappe.db.commit()
+ return True
+
+ def rebuild_uid(self,uid_validity):
+ uid_list = frappe.db.sql("""select name,uid ,unique_id
+ from `tabCommunication`
+ where email_account = %(email_account)s and unique_id is not Null and sent_or_received = 'Received'
+ #order by uid
+ """, {"email_account": self.settings.email_account}, as_dict=1)
+
+ unhandled_uid_list = frappe.db.sql("""select name,uid ,unique_id
+ from `tabUnhandled Email`
+ where email_account = %(email_account)s and unique_id is not Null
+ #order by uid
+ """, {"email_account": self.settings.email_account}, as_dict=1)
+
+
+ message_list = []
+ #get message-id's to link new uid's to
+ import email
+ self.imap.select("Inbox")
+ #messages = self.imap.uid('fetch', "1:*", '(BODY.PEEK[HEADER.FIELDS (FROM TO ENVELOPE-TO DATE RECEIVED)])')
+ messages = self.imap.uid('fetch', "1:*", '(BODY.PEEK[HEADER])')
+ for i, item in enumerate(messages[1]):
+ if isinstance(item, tuple):
+ # check for uid appended to the end
+ uid = re.search(r'UID [0-9]*', messages[1][i + 1], re.U | re.I)
+ if uid:
+ uid = uid.group()[4:]
+ else:
+ uid = ""
+
+ # check for uid at start
+ if not uid:
+ # for m in item:
+ uid = re.search(r'UID [0-9]*', messages[1][i][0], re.U | re.I)
+ if uid:
+ uid = uid.group()[4:]
+ else:
+ uid = ""
+ continue
+ mail = email.message_from_string(item[1])
+ unique_id = get_unique_id(mail)
+ message_list.append([uid, unique_id])
+ # clear out
+ frappe.db.sql("""update `tabCommunication`
+ set uid = NULL
+ where email_account = %(email_account)s
+ """, {"email_account": self.settings.email_account})
+ frappe.db.sql("""update `tabUnhandled Email`
+ set uid = NULL
+ where email_account = %(email_account)s
+ """, {"email_account": self.settings.email_account})
+
+ # write new uid
+ new_uid = []
+ for old in uid_list:
+ for new in message_list:
+ if old["unique_id"] == new[1]:
+ frappe.db.sql("""update `tabCommunication`
+ set uid = %(uid)s
+ where name = %(name)s
+ """, {"name": old["name"],
+ "uid": new[0]})
+ break
+ for old in unhandled_uid_list:
+ for new in message_list:
+ if old["unique_id"] == new[1]:
+ frappe.db.sql("""update `tabUnhandled Email`
+ set uid = %(uid)s
+ where name = %(name)s
+ """, {"name": old["name"],
+ "uid": new[0]})
+ break
+
+ frappe.db.set_value("Email Account", self.settings.email_account, "uid_validity", uid_validity)
+ frappe.db.set_value("Email Account", self.settings.email_account, "no_remaining", None)
+ frappe.db.commit()
+
+
+
+ def get_imap_messages(self,email_list):
+ if self.settings.no_remaining == '0' and self.settings.uidnext:
+ download_list = range(int(self.settings.uidnext), int(self.settings.newuidnext))
+ else:
+ #compare stored uid to new uid list to dl any missing messages
+ uid_list = frappe.db.sql("""select uid
+ from tabCommunication
+ where email_account = %(email_account)s and uid is not Null
+ order by uid
+ """,{"email_account":self.settings.email_account},as_list=1)
+ uid_list = uid_list + (frappe.db.sql("""select uid
+ from `tabUnhandled Email`
+ where email_account = %(email_account)s and uid is not Null
+ order by uid
+ """,{"email_account":self.settings.email_account},as_list=1))
+ new_uid_list = []
+ for i in uid_list:
+ new_uid_list.append(i[0])
+
+ download_list = []
+ for new in email_list:
+ if new not in new_uid_list:
+ download_list.append(cint(new))
+
+ from itertools import count, groupby
+ num = 50
+
+ # set number of email remaining to be synced
+ dl_length = len(download_list)
+
+ lcount =1
+ while len(download_list)>0:
+ # trim list to specified num emails to dl at a time
+
+ dlength = len(download_list)
+ cur_download_list = download_list[dlength - num:dlength]
+ if cur_download_list:
+ download_list = download_list[:dlength - num]
+
+ if lcount>=4:
+ download_list = []
+
+ # compress download list into ranges
+ G=(list(x) for _,x in groupby(cur_download_list, lambda x,c=count(): next(c)-x))
+ message_meta = ",".join(":".join(map(str,(g[0],g[-1])[:len(g)])) for g in G)
+
+ messages =[]
+
+ try:
+ messages = self.imap.uid('fetch', message_meta, '(BODY.PEEK[])')
+
+ except (TotalSizeExceededError, EmailTimeoutError), e:
+ print("timeout or size exceed")
+ pass
+ except (imaplib.IMAP4.error),e:
+
+ print (e)
+ pass
+
+ if messages and messages[0]=='OK':
+ for i, item in enumerate(messages[1]):
+ if isinstance(item, tuple):
+ #check for uid appended to the end
+ uid = re.search(r'UID [0-9]*', messages[1][i + 1], re.U|re.I)
+ if uid:
+ uid = uid.group()[4:]
+ else:
+ uid = ""
+
+
+ #check for uid at start
+ if not uid:
+ #for m in item:
+ uid = re.search(r'UID [0-9]*', messages[1][i][0], re.U|re.I)
+ if uid:
+ uid = uid.group()[4:]
+ else:
+ uid = ""
+ continue
+
+
+ if uid:
+ self.latest_messages.append([item[1],uid,1])#message,uid,seen
+
+ frappe.db.sql("update `tabEmail Account` set no_remaining = %s where name = %s",
+ (dl_length-len(self.latest_messages), self.settings.email_account), auto_commit=1)
+ lcount = lcount +1
+
+ def sync_flags(self):
+ #get flags from email flag queue + join them to the matching email account and uid
+ queue = frappe.db.sql("""select que.name,comm.uid,que.action,que.flag
+ from tabCommunication as comm,`tabEmail Flag Queue` as que
+ where comm.name = que.comm_name and comm.uid is not null and comm.email_account=%(email_account)s""",
+ {"email_account":self.settings.email_account}, as_dict=1)
+ #loop though flags
+
+ for item in queue:
+ try:
+ self.imap.uid('STORE', item.uid, item.action, item.flag)
+ #delete flag matching email account
+ frappe.delete_doc("Email Flag Queue", item["name"])
+ except Exception,e:
+ #need to do
+ pass
+
+ def get_seen(self):
+ comm_list = frappe.db.sql("""select name,uid,seen from `tabCommunication`
+ where email_account = %(email_account)s and uid is not null""",
+ {"email_account":self.settings.email_account}, as_dict=1)
+
+ try:
+ #response, messages = self.imap.uid('fetch', '1:*', '(FLAGS)')
+ response, seen_list = self.imap.uid('search', None, "SEEN")
+ response, unseen_list = self.imap.uid('search', None, "UNSEEN")
+ except Exception,e:
+ print("failed get seen sync download")
+ return
+ unseen_list = unseen_list[0].split()
+ for unseen in unseen_list:
+ for msg in self.latest_messages:
+ if unseen == msg[1]:
+ msg[2] = 0
+
+ for comm in comm_list:
+ if comm.uid == unseen:
+ if comm.seen:
+ frappe.db.sql("update `tabCommunication` set seen=%s where name = %s",(0, comm.name))
+ comm_list.remove(comm)
+ break
+ seen_list = seen_list[0].split()
+ for seen in seen_list:
+ for msg in self.latest_messages:
+ if seen == msg[1]:
+ msg[2] = 1
+
+ for comm in comm_list:
+ if comm.uid == seen:
+ if not comm.seen:
+ frappe.db.sql("update `tabCommunication` set seen=%s where name = %s", (1, comm.name))
+ comm_list.remove(comm)
+ break
+ '''
+ for item in messages:
+ uid = re.search(r'UID [0-9]*', item, re.U | re.I)
+ if uid:
+ uid = uid.group()[4:]
+ else:
+ uid = ""
+
+ # flag = re.search(r"(?<=FLAGS \()(.*?)(?=\))", item, re.U | re.I)
+ flag = re.search(r"\\Seen", item, re.U | re.I)
+
+ for msg in self.latest_messages:
+ if uid == msg[1]:
+ if flag:
+ msg[2]=0
+
+ for comm in comm_list:
+ if comm.uid==uid:
+ if flag:
+ if not comm.email_seen:
+ frappe.db.set_value('Communication',comm.name,'email_seen','1',update_modified=False)
+ else:
+ if comm.email_seen:
+ frappe.db.set_value('Communication', comm.name, 'email_seen', '0', update_modified=False)
+ comm_list.remove(comm)
+ break
+ '''
+ frappe.db.commit()
+
+
+ def push_deleted(self):
+ pass
+
def retrieve_message(self, message_meta, msg_num=None):
incoming_mail = None
try:
@@ -194,13 +511,15 @@ class EmailServer:
self.pop.dele(msg_num)
else:
# mark as seen
- self.imap.uid('STORE', message_meta, '+FLAGS', '(\\SEEN)')
+ #self.imap.uid('STORE', message_meta, '+FLAGS', '(\\SEEN)')
+ pass
else:
if not cint(self.settings.use_imap):
self.pop.dele(msg_num)
else:
# mark as seen
- self.imap.uid('STORE', message_meta, '+FLAGS', '(\\SEEN)')
+ #self.imap.uid('STORE', message_meta, '+FLAGS', '(\\SEEN)')
+ pass
def has_login_limit_exceeded(self, e):
return "-ERR Exceeded the login limit" in strip(cstr(e.message))
@@ -245,6 +564,23 @@ class EmailServer:
return error_msg
+
+def get_unique_id(mail):
+ hash = hashlib.sha1()
+ # loop though headers to make unique id looping used to resolve encoding issue of adding together
+ for h in mail._headers:
+ if h[0] != 'Content-Type': # skip variable boundaries
+ try:
+ temp = decode_header(h[1])
+ decoded = ''.join(
+ [d[0].decode(d[1]).encode('ascii', 'ignore') if d[1] is not None else d[0] for d in temp])
+ cleaned = re.sub(r"\s+", u"", decoded,
+ flags=re.UNICODE) # gmail fix as returns different whitespace if download only headers
+ hash.update(cleaned)
+ except:
+ pass
+ return hash.hexdigest()
+
class Email:
"""Wrapper for an email."""
def __init__(self, content):
@@ -264,10 +600,52 @@ class Email:
self.set_from()
self.message_id = (self.mail.get('Message-ID') or "").strip(" <>")
+
+ self.unique_id = get_unique_id(self.mail)
+
+ # gmail mailing-list compatibility
+ # use X-Original-Sender if available, as gmail sometimes modifies the 'From'
+ # _from_email = self.mail.get("X-Original-From") or self.mail["From"]
+ #
+ # self.from_email = extract_email_id(_from_email)
+ # if self.from_email:
+ # self.from_email = self.from_email.lower()
+ #
+ # #self.from_real_name = email.utils.parseaddr(_from_email)[0]
+ #
+ # _from_real_name = decode_header(email.utils.parseaddr(_from_email)[0])
+ # self.from_real_name = decode_header(email.utils.parseaddr(_from_email)[0])[0][0] or ""
+ #
+ # try:
+ # if _from_real_name[0][1]:
+ # self.from_real_name = self.from_real_name.decode(_from_real_name[0][1])
+ # else:
+ # # assume that the encoding is utf-8
+ # self.from_real_name = self.from_real_name.decode("utf-8")
+ # except UnicodeDecodeError,e:
+ # print e
+ # pass
+
+ #self.from_real_name = email.Header.decode_header(email.utils.parseaddr(_from_email)[0])[0][0]
+ self.To = self.mail.get("To")
+ if self.To:
+ to = u""
+ for name, encoding in decode_header(self.To):
+ if encoding:
+ to += name.decode(encoding)
+ else:
+ to += name
+ self.To = to.lower()
+ self.CC = self.mail.get("CC")
+ if self.CC:
+ self.CC = self.CC.lower()
if self.mail["Date"]:
- utc = email.utils.mktime_tz(email.utils.parsedate_tz(self.mail["Date"]))
- utc_dt = datetime.datetime.utcfromtimestamp(utc)
- self.date = convert_utc_to_user_timezone(utc_dt).strftime('%Y-%m-%d %H:%M:%S')
+ try:
+ utc = email.utils.mktime_tz(email.utils.parsedate_tz(self.mail["Date"]))
+ utc_dt = datetime.datetime.utcfromtimestamp(utc)
+ self.date = convert_utc_to_user_timezone(utc_dt).strftime('%Y-%m-%d %H:%M:%S')
+ except:
+ self.date = now()
else:
self.date = now()
@@ -278,13 +656,32 @@ class Email:
def set_subject(self):
"""Parse and decode `Subject` header."""
- _subject = decode_header(self.mail.get("Subject", "No Subject"))
- self.subject = _subject[0][0] or ""
- if _subject[0][1]:
- self.subject = self.subject.decode(_subject[0][1])
- else:
- # assume that the encoding is utf-8
- self.subject = self.subject.decode("utf-8")[:140]
+ from email.errors import HeaderParseError
+ try:
+ _subject = decode_header(self.mail.get("Subject", "No Subject"))
+ self.subject = _subject[0][0] or ""
+
+ if _subject[0][1]:
+ self.subject = self.subject.decode(_subject[0][1])
+ else:
+ # assume that the encoding is utf-8
+ self.subject = self.subject.decode("utf-8")[:140]
+ except (UnicodeDecodeError, HeaderParseError):
+ #try:
+ # self.subject = self.subject.decode("gb18030")
+ #except UnicodeDecodeError:
+ self.subject = u'Error Decoding Subject'
+ #if self.subject and len(self.subject)>140:
+ # self.subject = self.subject[:135]
+ import re
+
+ emoji_pattern = re.compile("["
+ u"\U0001F600-\U0001F64F" # emoticons
+ u"\U0001F300-\U0001F5FF" # symbols & pictographs
+ u"\U0001F680-\U0001F6FF" # transport & map symbols
+ u"\U0001F1E0-\U0001F1FF" # flags (iOS)
+ "]+", flags=re.UNICODE)
+ self.subject = emoji_pattern.sub(r'', self.subject)
if not self.subject:
self.subject = "No Subject"
@@ -294,14 +691,27 @@ class Email:
# use X-Original-Sender if available, as gmail sometimes modifies the 'From'
_from_email = self.mail.get("X-Original-From") or self.mail["From"]
_from_email, encoding = decode_header(_from_email)[0]
+ _reply_to, _reply_to_encoding = decode_header(self.mail.get("Reply-To"))[0]
if encoding:
_from_email = _from_email.decode(encoding)
else:
_from_email = _from_email.decode('utf-8')
+
+ if _reply_to_encoding:
+ _reply_to = _from_email.decode(encoding)
+ else:
+ _reply_to = _from_email.decode('utf-8')
- self.from_email = extract_email_id(_from_email)
- self.from_real_name = email.utils.parseaddr(_from_email)[0]
+ if _reply_to and not frappe.db.get_value('Email Account', {"email_id":_reply_to}, 'email_id'):
+ self.from_email = extract_email_id(_reply_to)
+ else:
+ self.from_email = extract_email_id(_from_email)
+
+ if self.from_email:
+ self.from_email = self.from_email.lower()
+
+ self.from_real_name = email.utils.parseaddr(_from_email)[0] if "@" in _from_email else _from_email
def set_content_and_type(self):
self.content, self.content_type = '[Blank Email]', 'text/plain'
diff --git a/frappe/geo/doctype/address/__init__.py b/frappe/geo/doctype/address/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/geo/doctype/address/address.js b/frappe/geo/doctype/address/address.js
new file mode 100644
index 0000000000..7305e5061d
--- /dev/null
+++ b/frappe/geo/doctype/address/address.js
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Address", {
+ refresh: function(frm) {
+ if(frm.doc.__islocal) {
+ var last_route = frappe.route_history.slice(-2, -1)[0];
+ if(frappe.contact_link && frappe.contact_link.doc
+ && frappe.contact_link.doc.name==last_route[2]) {
+ frm.add_child('links', {
+ link_doctype: frappe.contact_link.doctype,
+ link_name: frappe.contact_link.doc[frappe.contact_link.fieldname]
+ });
+ }
+ }
+ },
+ validate: function(frm) {
+ // clear linked customer / supplier / sales partner on saving...
+ if(frm.doc.links) {
+ frm.doc.links.forEach(function(d) {
+ frappe.model.remove_from_locals(d.link_doctype, d.link_name);
+ });
+ }
+ }
+});
diff --git a/frappe/geo/doctype/address/address.json b/frappe/geo/doctype/address/address.json
new file mode 100644
index 0000000000..969f9b0f69
--- /dev/null
+++ b/frappe/geo/doctype/address/address.json
@@ -0,0 +1,651 @@
+{
+ "allow_copy": 0,
+ "allow_import": 1,
+ "allow_rename": 1,
+ "beta": 0,
+ "creation": "2013-01-10 16:34:32",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 0,
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "address_details",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "options": "fa fa-map-marker",
+ "permlevel": 0,
+ "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,
+ "description": "Name of person or organization that this address belongs to.",
+ "fieldname": "address_title",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Address Title",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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": "address_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Address Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "address_line1",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Address Line 1",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "address_line2",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Address Line 2",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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": "city",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "City/Town",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 1,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "county",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "County",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "state",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "State",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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": "country",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 0,
+ "in_standard_filter": 1,
+ "label": "Country",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Country",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 1,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "pincode",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Postal Code",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 1,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break0",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
+ "width": "50%"
+ },
+ {
+ "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_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Email Address",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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": "phone",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Phone",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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": "fax",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Fax",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
+ "default": "0",
+ "description": "",
+ "fieldname": "is_primary_address",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Preferred Billing Address",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
+ "default": "0",
+ "description": "",
+ "fieldname": "is_shipping_address",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Preferred Shipping Address",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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": "linked_with",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference",
+ "length": 0,
+ "no_copy": 0,
+ "options": "fa fa-pushpin",
+ "permlevel": 0,
+ "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,
+ "default": "0",
+ "fieldname": "is_your_company_address",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Your Company Address",
+ "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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "links",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Links",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Dynamic Link",
+ "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
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "fa fa-map-marker",
+ "idx": 5,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-01-13 05:01:15.084023",
+ "modified_by": "Administrator",
+ "module": "Geo",
+ "name": "Address",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Sales User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Purchase User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Maintenance User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "search_fields": "country, state",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 0,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/frappe/geo/doctype/address/address.py b/frappe/geo/doctype/address/address.py
new file mode 100644
index 0000000000..2090dc3aee
--- /dev/null
+++ b/frappe/geo/doctype/address/address.py
@@ -0,0 +1,203 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+from frappe import throw, _
+from frappe.utils import cstr
+
+from frappe.model.document import Document
+from jinja2 import TemplateSyntaxError
+from frappe.utils.user import is_website_user
+from frappe.model.naming import make_autoname
+
+class Address(Document):
+ def __setup__(self):
+ self.flags.linked = False
+
+ def autoname(self):
+ if not self.address_title:
+ if self.links:
+ self.address_title = self.links[0].link_name
+
+ if self.address_title:
+ self.name = (cstr(self.address_title).strip() + "-" + cstr(self.address_type).strip())
+ if frappe.db.exists("Address", self.name):
+ self.name = make_autoname(cstr(self.address_title).strip() + "-" +
+ cstr(self.address_type).strip() + "-.#")
+ else:
+ throw(_("Address Title is mandatory."))
+
+ def validate(self):
+ self.link_address()
+ self.validate_reference()
+
+ def link_address(self):
+ """Link address based on owner"""
+ if not self.links and not self.is_your_company_address:
+ contact_name = frappe.db.get_value("Contact", {"email_id": self.owner})
+ if contact_name:
+ contact = frappe.get_doc('Contact', contact_name)
+ for link in contact.links:
+ self.append('links', dict(link_doctype=link.link_doctype, link_name=link.link_name))
+ return True
+
+ return False
+
+ def validate_reference(self):
+ if self.is_your_company_address:
+ if not self.company:
+ frappe.throw(_("Company is mandatory, as it is your company address"))
+ if self.links:
+ self.links = []
+
+ def get_display(self):
+ return get_address_display(self.as_dict())
+
+ def has_link(self, doctype, name):
+ for link in self.links:
+ if link.link_doctype==doctype and link.link_name== name:
+ return True
+
+ def has_common_link(self, doc):
+ reference_links = [(link.link_doctype, link.link_name) for link in doc.links]
+ for link in self.links:
+ if (link.link_doctype, link.link_name) in reference_links:
+ return True
+
+ return False
+
+def get_default_address(doctype, name, sort_key='is_primary_address'):
+ '''Returns default Address name for the given doctype, name'''
+ out = frappe.db.sql('''select address.name
+ from
+ tabAddress address, `tabDynamic Link` dl
+ where
+ dl.link_doctype=%s and
+ dl.link_name=%s and
+ dl.parent = address.name and
+ dl.parenttype = "Address"
+ order by
+ address.`{0}` desc, name
+ limit 1'''.format(sort_key), (doctype, name))
+ return out and out[0][0] or None
+
+
+@frappe.whitelist()
+def get_address_display(address_dict):
+ if not address_dict:
+ return
+
+ if not isinstance(address_dict, dict):
+ address_dict = frappe.db.get_value("Address", address_dict, "*", as_dict=True) or {}
+
+ name, template = get_address_templates(address_dict)
+
+ try:
+ return frappe.render_template(template, address_dict)
+ except TemplateSyntaxError:
+ frappe.throw(_("There is an error in your Address Template {0}").format(name))
+
+
+def get_territory_from_address(address):
+ """Tries to match city, state and country of address to existing territory"""
+ if not address:
+ return
+
+ if isinstance(address, basestring):
+ address = frappe.get_doc("Address", address)
+
+ territory = None
+ for fieldname in ("city", "state", "country"):
+ territory = frappe.db.get_value("Territory", address.get(fieldname))
+ if territory:
+ break
+
+ return territory
+
+def get_list_context(context=None):
+ return {
+ "title": _("Addresses"),
+ "get_list": get_address_list,
+ "row_template": "templates/includes/address_row.html",
+ 'no_breadcrumbs': True,
+ }
+
+def get_address_list(doctype, txt, filters, limit_start, limit_page_length=20):
+ from frappe.www.list import get_list
+ user = frappe.session.user
+ ignore_permissions = False
+ if is_website_user():
+ if not filters: filters = []
+ filters.append(("Address", "owner", "=", user))
+ ignore_permissions = True
+
+ return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=ignore_permissions)
+
+def has_website_permission(doc, ptype, user, verbose=False):
+ """Returns true if there is a related lead or contact related to this document"""
+ contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user})
+ if contact_name:
+ contact = frappe.get_doc('Contact', contact_name)
+ return contact.has_common_link(doc)
+
+ lead_name = frappe.db.get_value("Lead", {"email_id": frappe.session.user})
+ if lead_name:
+ return doc.has_link('Lead', lead_name)
+
+ return False
+
+def get_address_templates(address):
+ result = frappe.db.get_value("Address Template", \
+ {"country": address.get("country")}, ["name", "template"])
+
+ if not result:
+ result = frappe.db.get_value("Address Template", \
+ {"is_default": 1}, ["name", "template"])
+
+ if not result:
+ frappe.throw(_("No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template."))
+ else:
+ return result
+
+@frappe.whitelist()
+def get_shipping_address(company):
+ filters = {"company": company, "is_your_company_address":1}
+ fieldname = ["name", "address_line1", "address_line2", "city", "state", "country"]
+
+ address_as_dict = frappe.db.get_value("Address", filters=filters, fieldname=fieldname, as_dict=True)
+
+ if address_as_dict:
+ name, address_template = get_address_templates(address_as_dict)
+ return address_as_dict.get("name"), frappe.render_template(address_template, address_as_dict)
+
+def contact_query(doctype, txt, searchfield, start, page_len, filters):
+ from frappe.desk.reportview import get_match_cond
+
+ return frappe.db.sql("""select
+ address.name, address.city, address.country
+ from
+ tabAddress as address, `tabDynamic Link` as dl
+ where
+ dl.parent = address.name and
+ dl.parenttype = 'Address' and
+ dl.link_doctype = %(link_doctype)s and
+ dl.link_name = %(link_name)s and
+ address.`{key}` like %(txt)s
+ {mcond}
+ order by
+ if(locate(%(_txt)s, address.name), locate(%(_txt)s, address.name), 99999),
+ address.idx desc, address.name
+ limit %(start)s, %(page_len)s """.format(
+ mcond=get_match_cond(doctype),
+ key=frappe.db.escape(searchfield)),
+ {
+ 'txt': "%%%s%%" % frappe.db.escape(txt),
+ '_txt': txt.replace("%", ""),
+ 'start': start,
+ 'page_len': page_len,
+ 'link_doctype': filters.get('link_doctype'),
+ 'link_name': filters.get('link_name')
+ })
diff --git a/frappe/geo/doctype/address/test_address.py b/frappe/geo/doctype/address/test_address.py
new file mode 100644
index 0000000000..2c067799f9
--- /dev/null
+++ b/frappe/geo/doctype/address/test_address.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe, unittest
+test_records = frappe.get_test_records('Address')
+
+from frappe.geo.doctype.address.address import get_address_display
+
+class TestAddress(unittest.TestCase):
+ def test_template_works(self):
+ address = frappe.get_list("Address")[0].name
+ display = get_address_display(frappe.get_doc("Address", address).as_dict())
+ self.assertTrue(display)
+
+
+test_dependencies = ["Address Template"]
diff --git a/frappe/geo/doctype/address/test_records.json b/frappe/geo/doctype/address/test_records.json
new file mode 100644
index 0000000000..a7bde9a814
--- /dev/null
+++ b/frappe/geo/doctype/address/test_records.json
@@ -0,0 +1,15 @@
+[
+ {
+ "address_line1": "_Test Address Line 1",
+ "address_title": "_Test Address",
+ "address_type": "Office",
+ "city": "_Test City",
+ "state": "Test State",
+ "country": "India",
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "doctype": "Address",
+ "is_primary_address": 1,
+ "phone": "+91 0000000000"
+ }
+]
\ No newline at end of file
diff --git a/frappe/geo/doctype/address_template/__init__.py b/frappe/geo/doctype/address_template/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/geo/doctype/address_template/address_template.js b/frappe/geo/doctype/address_template/address_template.js
new file mode 100644
index 0000000000..db3c68c220
--- /dev/null
+++ b/frappe/geo/doctype/address_template/address_template.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Address Template', {
+ refresh: function(frm) {
+ if(frm.is_new() && !frm.doc.template) {
+ // set default template via js so that it is translated
+ frappe.call({
+ method: 'frappe.geo.doctype.address_template.address_template.get_default_address_template',
+ callback: function(r) {
+ frm.set_value('template', r.message);
+ }
+ });
+ }
+ }
+});
diff --git a/frappe/geo/doctype/address_template/address_template.json b/frappe/geo/doctype/address_template/address_template.json
new file mode 100644
index 0000000000..8bc0c91367
--- /dev/null
+++ b/frappe/geo/doctype/address_template/address_template.json
@@ -0,0 +1,148 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 1,
+ "autoname": "field:country",
+ "beta": 0,
+ "creation": "2014-06-05 02:22:36.029850",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 0,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "country",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Country",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Country",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 1,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "This format is used if country specific format is not found",
+ "fieldname": "is_default",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Is Default",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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,
+ "default": "",
+ "description": "Default Template \nUses Jinja Templating and all the fields of Address (including Custom Fields if any) will be available
\n{{ address_line1 }}<br>\n{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}\n{{ city }}<br>\n{% if state %}{{ state }}<br>{% endif -%}\n{% if pincode %} PIN: {{ pincode }}<br>{% endif -%}\n{{ country }}<br>\n{% if phone %}Phone: {{ phone }}<br>{% endif -%}\n{% if fax %}Fax: {{ fax }}<br>{% endif -%}\n{% if email_id %}Email: {{ email_id }}<br>{% endif -%}\n ",
+ "fieldname": "template",
+ "fieldtype": "Code",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Template",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "fa fa-map-marker",
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-01-13 05:11:37.499528",
+ "modified_by": "Administrator",
+ "module": "Geo",
+ "name": "Address Template",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 0,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 1,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 0,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/frappe/geo/doctype/address_template/address_template.py b/frappe/geo/doctype/address_template/address_template.py
new file mode 100644
index 0000000000..9336f75027
--- /dev/null
+++ b/frappe/geo/doctype/address_template/address_template.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe.utils.jinja import validate_template
+from frappe import _
+
+class AddressTemplate(Document):
+ def validate(self):
+ if not self.template:
+ self.template = get_default_address_template()
+
+ self.defaults = frappe.db.get_values("Address Template", {"is_default":1, "name":("!=", self.name)})
+ if not self.is_default:
+ if not self.defaults:
+ self.is_default = 1
+ frappe.msgprint(_("Setting this Address Template as default as there is no other default"))
+
+ validate_template(self.template)
+
+ def on_update(self):
+ if self.is_default and self.defaults:
+ for d in self.defaults:
+ frappe.db.set_value("Address Template", d[0], "is_default", 0)
+
+ def on_trash(self):
+ if self.is_default:
+ frappe.throw(_("Default Address Template cannot be deleted"))
+
+@frappe.whitelist()
+def get_default_address_template():
+ '''Get default address template (translated)'''
+ return '''{{ address_line1 }} {% if address_line2 %}{{ address_line2 }} {% endif -%}\
+{{ city }}
+{% if state %}{{ state }} {% endif -%}
+{% if pincode %}{{ pincode }} {% endif -%}
+{{ country }}
+{% if phone %}'''+_('Phone')+''': {{ phone }} {% endif -%}
+{% if fax %}'''+_('Fax')+''': {{ fax }} {% endif -%}
+{% if email_id %}'''+_('Email')+''': {{ email_id }} {% endif -%}'''
diff --git a/frappe/geo/doctype/address_template/test_address_template.py b/frappe/geo/doctype/address_template/test_address_template.py
new file mode 100644
index 0000000000..8e300ac457
--- /dev/null
+++ b/frappe/geo/doctype/address_template/test_address_template.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe, unittest
+test_records = frappe.get_test_records('Address Template')
+
+class TestAddressTemplate(unittest.TestCase):
+ def test_default_is_unset(self):
+ a = frappe.get_doc("Address Template", "India")
+ a.is_default = 1
+ a.save()
+
+ b = frappe.get_doc("Address Template", "Brazil")
+ b.is_default = 1
+ b.save()
+
+ self.assertEqual(frappe.db.get_value("Address Template", "India", "is_default"), 0)
+
+ def tearDown(self):
+ a = frappe.get_doc("Address Template", "India")
+ a.is_default = 1
+ a.save()
diff --git a/frappe/geo/doctype/address_template/test_records.json b/frappe/geo/doctype/address_template/test_records.json
new file mode 100644
index 0000000000..412c9e745b
--- /dev/null
+++ b/frappe/geo/doctype/address_template/test_records.json
@@ -0,0 +1,13 @@
+[
+ {
+ "country": "India",
+ "is_default": 1,
+ "template": "{{ address_title }} \n{{ address_line1 }} \n{% if address_line2 %}{{ address_line2 }} {% endif %}\n{{ city }} \n{% if state %}{{ state }} {% endif %}\n{% if pincode %} PIN / ZIP: {{ pincode }} {% endif %}\n{{ country }} \n{% if phone %}Phone: {{ phone }} {% endif %}\n{% if fax %}Fax: {{ fax }} {% endif %}\n{% if email_id %}Email: {{ email_id }} {% endif %}\n"
+ },
+ {
+ "country": "Brazil",
+ "is_default": 0,
+ "template": "{{ address_title }} \n{{ address_line1 }} \n{% if address_line2 %}{{ address_line2 }} {% endif %}\n{{ city }} \n{% if state %}{{ state }} {% endif %}\n{% if pincode %} PIN / ZIP: {{ pincode }} {% endif %}\n{{ country }} \n{% if phone %}Phone: {{ phone }} {% endif %}\n{% if fax %}Fax: {{ fax }} {% endif %}\n{% if email_id %}Email: {{ email_id }} {% endif %}\n"
+ }
+]
+
diff --git a/frappe/hooks.py b/frappe/hooks.py
index a79e377763..11feb9c0a8 100755
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -91,6 +91,10 @@ has_permission = {
"Communication": "frappe.core.doctype.communication.communication.has_permission"
}
+has_website_permission = {
+ "Address": "erpnext.utilities.doctype.address.address.has_website_permission"
+}
+
standard_queries = {
"User": "frappe.core.doctype.user.user.user_query"
}
diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py
index 92aa639f7a..55c20c45fb 100644
--- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py
+++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py
@@ -131,6 +131,7 @@ def get_dropbox_authorize_url():
@frappe.whitelist(allow_guest=True)
def dropbox_callback(oauth_token=None, not_approved=False):
doc = frappe.get_doc("Dropbox Settings")
+ close = '' + _('Please close this window') + '
'
if not not_approved:
if doc.get_password(fieldname="dropbox_access_key", raise_exception=False)==oauth_token:
@@ -145,7 +146,6 @@ def dropbox_callback(oauth_token=None, not_approved=False):
frappe.db.commit()
else:
- close = '' + _('Please close this window') + '
'
frappe.respond_as_web_page(_("Dropbox Setup"),
_("Illegal Access Token. Please try again") + close,
indicator_color='red',
diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py
index 34bc65e5a9..3e7af7ed8b 100644
--- a/frappe/model/base_document.py
+++ b/frappe/model/base_document.py
@@ -608,7 +608,7 @@ class BaseDocument(object):
if not value or not isinstance(value, basestring):
continue
- elif ("<" not in value and ">" not in value):
+ elif (u"<" not in value and u">" not in value):
# doesn't look like html so no need
continue
diff --git a/frappe/patches.txt b/frappe/patches.txt
index 10800fbbd3..142fb8bf1e 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -24,6 +24,7 @@ execute:frappe.reload_doc('core', 'doctype', 'patch_log') #2016-10-31
execute:frappe.reload_doctype("File") # 2015-10-19
execute:frappe.reload_doc('core', 'doctype', 'error_snapshot')
execute:frappe.clear_cache()
+frappe.patches.v7_1.rename_scheduler_log_to_error_log
frappe.patches.v7_1.sync_language_doctype
frappe.patches.v7_0.rename_bulk_email_to_email_queue
frappe.patches.v7_1.rename_chinese_language_codes
diff --git a/frappe/patches/v6_24/imap_get_unique.py b/frappe/patches/v6_24/imap_get_unique.py
new file mode 100644
index 0000000000..4e07947fd4
--- /dev/null
+++ b/frappe/patches/v6_24/imap_get_unique.py
@@ -0,0 +1,82 @@
+import frappe
+import imaplib
+import hashlib
+import re
+import email, email.utils,email.header
+from email.header import decode_header
+from frappe.email.receive import get_unique_id
+
+def execute():
+ frappe.reload_doctype("Communication")
+ frappe.reload_doctype("Unhandled Email")
+ for email_account in frappe.get_list("Email Account", filters={"awaiting_password": 0}):
+ email_acc = frappe.get_doc("Email Account", email_account)
+ try:
+ email_server = email_acc.get_server(in_receive=True)
+ email_server.imap.select("Inbox")
+ #messages =email_server.imap.uid('fetch', "1:*", '(BODY.PEEK[HEADER.FIELDS (FROM TO ENVELOPE-TO DATE RECEIVED)])')
+ messages = email_server.imap.uid('fetch', "1:*",'(BODY.PEEK[HEADER])')
+ comms = frappe.db.sql("""select uid,name from `tabCommunication`
+ where email_account=%(email_account)s""",{"email_account":email_account.name},as_dict=1 )
+ unhandled = frappe.db.sql("""select uid,name from `tabUnhandled Email`
+ where email_account=%(email_account)s""",{"email_account":email_account.name},as_dict=1 )
+ count =0;
+ for i, item in enumerate(messages[1]):
+ if isinstance(item, tuple):
+
+ # check for uid appended to the end
+ uid = re.search(r'UID [0-9]*', messages[1][i + 1], re.U | re.I)
+ if uid:
+ uid = uid.group()[4:]
+ else:
+ uid = ""
+
+ # check for uid at start
+ if not uid:
+ # for m in item:
+ uid = re.search(r'UID [0-9]*', messages[1][i][0], re.U | re.I)
+ if uid:
+ uid = uid.group()[4:]
+ else:
+ uid = ""
+ continue
+ mail = email.message_from_string(item[1])
+ unique_id = get_unique_id(mail)
+
+ #unique_id = hashlib.md5((mail.get("X-Original-From") or mail["From"])+(mail.get("To") or mail.get("Envelope-to"))+(mail.get("Received") or mail["Date"])).hexdigest()
+ found =False
+ for comm in comms:
+ if comm.uid == uid:
+ found =True
+ frappe.db.sql("""update `tabCommunication`
+ set unique_id = %(unique_id)s
+ where name = %(name)s
+ """, {"unique_id": unique_id,
+ "name":comm.name})
+
+ if not found:
+ for comm in unhandled:
+ if comm.uid == uid:
+ found = True
+ frappe.db.sql("""update `tabUnhandled Email`
+ set unique_id = %(unique_id)s
+ where name = %(name)s
+ """, {"unique_id": unique_id,
+ "name": comm.name})
+ if found:
+ count += 1
+
+ #frappe.db.sql("""update `tabCommunication`
+ # set unique_id = %(unique_id)s
+ # where email_account= %(email_account)s and uid = %(uid)s
+ # """, {"unique_id": h,
+ # "email_account":email_account.name,
+ # "uid": uid})
+ print email_account.name,count
+ except Exception, e:
+ print e
+ finally:
+ try:
+ email_server.imap.logout()
+ except:
+ pass
diff --git a/frappe/patches/v7_2/set_in_standard_filter_property.py b/frappe/patches/v7_2/set_in_standard_filter_property.py
index 2b8f3be38e..421861cd69 100644
--- a/frappe/patches/v7_2/set_in_standard_filter_property.py
+++ b/frappe/patches/v7_2/set_in_standard_filter_property.py
@@ -8,7 +8,7 @@ def execute():
except Exception, e:
if e.args[0]!=1054: raise e
- for doctype in frappe.get_all("DocType", {"istable": 0, "issingle": 0}):
+ for doctype in frappe.get_all("DocType", {"istable": 0, "issingle": 0, "custom": 0}):
try:
frappe.reload_doctype(doctype.name, force=True)
except KeyError:
diff --git a/frappe/print/doctype/print_settings/print_settings.json b/frappe/print/doctype/print_settings/print_settings.json
index 0b59254abc..7415dc7947 100644
--- a/frappe/print/doctype/print_settings/print_settings.json
+++ b/frappe/print/doctype/print_settings/print_settings.json
@@ -465,6 +465,34 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "allow_page_break_inside_tables",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Allow page break inside tables",
+ "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
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -530,7 +558,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-12-29 14:40:32.410139",
+ "modified": "2017-01-10 16:48:51.244664",
"modified_by": "Administrator",
"module": "Print",
"name": "Print Settings",
diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js
index 26c09186c0..13fb7d081c 100644
--- a/frappe/public/js/frappe/desk.js
+++ b/frappe/public/js/frappe/desk.js
@@ -84,8 +84,93 @@ frappe.Application = Class.extend({
});
dialog.get_close_btn().toggle(false);
});
+ if (sys_defaults.email_user_password){
+ var email_list = sys_defaults.email_user_password.split(',');
+ for (u in email_list) {
+ if (email_list[u]===frappe.user.name){
+ this.set_password(email_list[u])
+ }
+ }
+ }
+
+ },
+ set_password: function (user) {
+ var me=this
+ frappe.call({
+ method: 'frappe.core.doctype.user.user.get_email_awaiting',
+ args: {
+ "user": user
+ },
+ callback: function (email_account) {
+ email_account = email_account["message"];
+ if (email_account) {
+ var i = 0;
+ if (i < email_account.length) {
+ me.email_password_prompt( email_account, user, i);
+ }
+ }
+ }
+ });
},
+ email_password_prompt: function(email_account,user,i) {
+ var me = this
+ var d = new frappe.ui.Dialog({
+ title: __('Email Account setup please enter your password for: '+email_account[i]["email_id"]),
+ fields: [
+ { 'fieldname': 'password',
+ 'fieldtype': 'Password',
+ 'label': 'Email Account Password',
+ 'reqd': 1
+ },
+ {
+ "fieldtype": "Button",
+ "label": __("Submit")
+ }
+ ]
+ });
+ d.get_input("submit").on("click", function() {
+ //setup spinner
+ d.hide();
+ var s = new frappe.ui.Dialog({
+ title: __("Checking one moment"),
+ fields: [{
+ "fieldtype": "HTML",
+ "fieldname": "checking"
+ }]
+ });
+ s.fields_dict.checking.$wrapper.html(' ')
+ s.show();
+ frappe.call({
+ method: 'frappe.core.doctype.user.user.set_email_password',
+ args: {
+ "email_account": email_account[i]["email_account"],
+ "user": user,
+ "password": d.get_value("password")
+ },
+ callback: function (passed)
+ {
+ s.hide();
+ d.hide();//hide waiting indication
+ if (!passed["message"])
+ {
+ show_alert("Login Failed please try again", 5);
+ me.email_password_prompt(email_account, user, i)
+ }
+ else
+ {
+ if (i + 1 < email_account.length)
+ {
+ i = i + 1;
+ me.email_password_prompt(email_account, user, i)
+ }
+ }
+
+ }
+ });
+ });
+ d.show();
+ },
load_bootinfo: function() {
if(frappe.boot) {
frappe.modules = {};
diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js
index 78ef5c0ceb..b0f79564ef 100644
--- a/frappe/public/js/frappe/form/control.js
+++ b/frappe/public/js/frappe/form/control.js
@@ -735,11 +735,11 @@ frappe.ui.form.ControlDateRange = frappe.ui.form.ControlData.extend({
this.set_mandatory && this.set_mandatory(value);
},
parse: function(value) {
- if(value) {
- vals = value.split(",")
- value = dateutil.user_to_obj(vals[0]);
- value2 = dateutil.user_to_obj(vals[vals.length-1]);
- return [value,value2];
+ if(value && (value.indexOf(',') !== -1 || value.indexOf('to') !== -1)) {
+ vals = value.split(/[( to )(,)]/)
+ from_date = moment(dateutil.user_to_obj(vals[0])).format('YYYY-MM-DD');
+ to_date = moment(dateutil.user_to_obj(vals[vals.length-1])).format('YYYY-MM-DD');
+ return [from_date, to_date];
}
},
format_for_input: function(value,value2) {
diff --git a/frappe/public/js/frappe/form/footer/assign_to.js b/frappe/public/js/frappe/form/footer/assign_to.js
index 60b3627550..a4dca37538 100644
--- a/frappe/public/js/frappe/form/footer/assign_to.js
+++ b/frappe/public/js/frappe/form/footer/assign_to.js
@@ -155,7 +155,7 @@ frappe.ui.form.AssignToDialog = Class.extend({
primary_action: function() {
var assign_to = opts.obj.dialog.fields_dict.assign_to.get_value();
var args = opts.obj.dialog.get_values();
- frappe.ui.add_assignment(assign_to, args, opts, dialog);
+ frappe.ui.add_assignment(assign_to, args, opts, opts.obj.dialog);
},
primary_action_label: __("Add")
}));
diff --git a/frappe/public/js/frappe/form/footer/timeline.js b/frappe/public/js/frappe/form/footer/timeline.js
index 949d62c3ae..2f5b858a22 100644
--- a/frappe/public/js/frappe/form/footer/timeline.js
+++ b/frappe/public/js/frappe/form/footer/timeline.js
@@ -34,7 +34,13 @@ frappe.ui.form.Timeline = Class.extend({
}
}
});
-
+ this.list.on("click",".comment-header",function(e) {
+ if (!inList(["A","BUTTON"],e.target.tagName)) {
+ $(this).parent().find(".timeline-content-show").toggleClass("hide");
+ $(this).find(".expand-icon").toggleClass("octicon-chevron-down octicon-chevron-up")
+ }
+ })
+
this.email_button = this.wrapper.find(".btn-new-email")
.on("click", function() {
new frappe.views.CommunicationComposer({
@@ -96,7 +102,7 @@ frappe.ui.form.Timeline = Class.extend({
var communications = this.get_communications(true);
- $.each(communications.sort(function(a, b) { return a.creation > b.creation ? -1 : 1 }),
+ $.each(communications.sort(function(a, b) { return a.communication_date > b.communication_date ? -1 : 1 }),
function(i, c) {
if(c.content) {
c.frm = me.frm;
@@ -121,7 +127,7 @@ frappe.ui.form.Timeline = Class.extend({
comment_type: "Created",
communication_type: "Comment",
sender: this.frm.doc.owner,
- creation: this.frm.doc.creation,
+ communication_date: this.frm.doc.creation,
frm: this.frm
});
@@ -130,6 +136,8 @@ frappe.ui.form.Timeline = Class.extend({
this.frm.sidebar.refresh_comments();
this.frm.trigger('timeline_refresh');
+ $(this.list.find(".comment-header")[0]).parent().find(".timeline-content-show").toggleClass("hide")
+ $(this.list.find(".comment-header")[0]).find(".expand-icon").toggleClass("octicon-chevron-down octicon-chevron-up")
},
render_timeline_item: function(c) {
@@ -187,6 +195,7 @@ frappe.ui.form.Timeline = Class.extend({
if(c.communication_type=="Communication" && c.communication_medium==="Email") {
this.last_type = c.communication_medium;
this.add_reply_btn_event($timeline_item, c);
+ this.add_relink_btn_event($timeline_item, c);
}
},
@@ -215,6 +224,18 @@ frappe.ui.form.Timeline = Class.extend({
});
},
+ add_relink_btn_event: function($timeline_item, c) {
+ var me = this;
+ $timeline_item.find(".relink-link").on("click", function() {
+ var name = $(this).attr("data-name");
+ callback= function (frm) {
+ $timeline_item.hide()
+ }
+ frappe.timeline.relink_dialog(name, cur_frm.doctype, cur_frm.name, callback)
+ });
+
+ },
+
prepare_timeline_item: function(c) {
if(!c.sender) c.sender = this.frm.doc.owner;
@@ -240,7 +261,7 @@ frappe.ui.form.Timeline = Class.extend({
}
}
- c.comment_on = comment_when(c.creation);
+ c.comment_on = comment_when(c.communication_date || c.creation);
c.fullname = c.sender_full_name || frappe.user.full_name(c.sender);
if(c.attachments && typeof c.attachments==="string")
@@ -272,6 +293,7 @@ frappe.ui.form.Timeline = Class.extend({
} else {
c.content_html = c.content;
c.content_html = frappe.utils.strip_whitespace(c.content_html);
+ c.content_html = c.content_html.replace(/</g,"<").replace(/>/g,">")
}
// bold @mentions
@@ -294,7 +316,7 @@ frappe.ui.form.Timeline = Class.extend({
},
is_communication_or_comment: function(c) {
- return c.communication_type==="Communication" || (c.communication_type==="Comment" && c.comment_type==="Comment");
+ return c.communication_type==="Communication" || (c.communication_type==="Comment" && (c.comment_type==="Comment"||c.comment_type==="Relinked"));
},
set_icon_and_color: function(c) {
@@ -316,7 +338,8 @@ frappe.ui.form.Timeline = Class.extend({
"Shared": "octicon octicon-eye",
"Unshared": "octicon octicon-circle-slash",
"Like": "octicon octicon-heart",
- "Edit": "octicon octicon-pencil"
+ "Edit": "octicon octicon-pencil",
+ "Relinked": "octicon octicon-check"
}[c.comment_type || c.communication_medium]
c.color = {
@@ -333,7 +356,8 @@ frappe.ui.form.Timeline = Class.extend({
"Workflow": "#2c3e50",
"Label": "#2c3e50",
"Attachment": "#7f8c8d",
- "Attachment Removed": "#eee"
+ "Attachment Removed": "#eee",
+ "Relinked": "#16a085"
}[c.comment_type || c.communication_medium];
c.icon_fg = {
@@ -547,7 +571,7 @@ frappe.ui.form.Timeline = Class.extend({
communications = this.frm.get_docinfo().communications,
email = this.get_recipient();
- $.each(communications.sort(function(a, b) { return a.creation > b.creation ? -1 : 1 }), function(i, c) {
+ $.each(communications.sort(function(a, b) { return a.communication_date > b.communication_date ? -1 : 1 }), function(i, c) {
if(c.communication_type=='Communication' && c.communication_medium=="Email") {
if(from_recipient) {
if(c.sender.indexOf(email)!==-1) {
@@ -668,4 +692,52 @@ $.extend(frappe.timeline, {
return index;
},
+ relink_dialog: function(name, reference_doctype, reference_name, callback, timeline_hide){
+ var lib = "frappe.email";
+ var d = new frappe.ui.Dialog ({
+ title: __("Relink Communication"),
+ fields: [{
+ "fieldtype": "Link",
+ "options": "DocType",
+ "label": __("Reference Doctype"),
+ "fieldname": "reference_doctype",
+ "get_query": function() {return {"query": lib +".get_communication_doctype"}}
+ },
+ {
+ "fieldtype": "Dynamic Link",
+ "options": "reference_doctype",
+ "label": __("Reference Name"),
+ "fieldname": "reference_name"
+ }]
+ });
+ d.set_value("reference_doctype", reference_doctype);
+ d.set_value("reference_name", reference_name);
+ d.set_primary_action(__("Relink"), function (frm) {
+ values = d.get_values();
+ if (values) {
+ frappe.confirm(
+ 'Are you sure you want to relink this communication to ' + values["reference_name"] + '?',
+ function () {
+ d.hide();
+ frappe.call
+ ({
+ method: lib + ".relink",
+ args: {
+ "name": timeline_hide ? timeline_hide: name,
+ "reference_doctype": values["reference_doctype"],
+ "reference_name": values["reference_name"]
+ },
+ callback: function (frm) {
+ callback(frm)
+ }
+ })
+ },
+ function () {
+ show_alert('Document not Relinked')
+ }
+ )
+ }
+ });
+ d.show();
+ }
})
diff --git a/frappe/public/js/frappe/form/footer/timeline_item.html b/frappe/public/js/frappe/form/footer/timeline_item.html
index 1bc5df9081..f15cf0aed8 100755
--- a/frappe/public/js/frappe/form/footer/timeline_item.html
+++ b/frappe/public/js/frappe/form/footer/timeline_item.html
@@ -25,9 +25,9 @@
{% if(data.communication_type==="Communication"
|| (data.communication_type==="Comment"
&& data.comment_type==="Comment")) { %}
-
-
diff --git a/frappe/public/js/frappe/list/doclistview.js b/frappe/public/js/frappe/list/doclistview.js
index 04470dbe1b..5c548826c3 100644
--- a/frappe/public/js/frappe/list/doclistview.js
+++ b/frappe/public/js/frappe/list/doclistview.js
@@ -723,7 +723,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
})
if(docname.length >= 1){
- me.dialog = new frappe.ui.AssignToDialog({
+ me.dialog = new frappe.ui.form.AssignToDialog({
obj: me,
method: 'frappe.desk.form.assign_to.add_multiple',
doctype: me.doctype,
diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js
index 12dedfddb7..638e28b6f9 100755
--- a/frappe/public/js/frappe/views/communication.js
+++ b/frappe/public/js/frappe/views/communication.js
@@ -47,15 +47,18 @@ frappe.views.CommunicationComposer = Class.extend({
},
get_fields: function() {
- return [
- {label:__("To"), fieldtype:"Data", reqd: 0, fieldname:"recipients"},
- {fieldtype: "Section Break", collapsible: 1, label: __("CC & Standard Reply")},
- {label:__("CC"), fieldtype:"Data", fieldname:"cc"},
+ this.from = {};
+ this.get_from();
+ var fields= [
+ this.from ,
+ {label:__("To"), fieldtype:"Data", reqd: 0, fieldname:"recipients",length:524288},
+ {fieldtype: "Section Break", collapsible: 1, label: "CC & Standard Reply"},
+ {label:__("CC"), fieldtype:"Data", fieldname:"cc",length:524288},
{label:__("Standard Reply"), fieldtype:"Link", options:"Standard Reply",
fieldname:"standard_reply"},
{fieldtype: "Section Break"},
{label:__("Subject"), fieldtype:"Data", reqd: 1,
- fieldname:"subject"},
+ fieldname:"subject",length:524288},
{fieldtype: "Section Break"},
{label:__("Message"), fieldtype:"Text Editor", reqd: 1,
fieldname:"content"},
@@ -65,6 +68,8 @@ frappe.views.CommunicationComposer = Class.extend({
fieldname:"send_email"},
{label:__("Send me a copy"), fieldtype:"Check",
fieldname:"send_me_a_copy"},
+ {label:__("Send Read Receipt"), fieldtype:"Check",
+ fieldname:"send_read_receipt"},
{label:__("Communication Medium"), fieldtype:"Select",
options: ["Phone", "Chat", "Email", "SMS", "Visit", "Other"],
fieldname:"communication_medium"},
@@ -81,8 +86,33 @@ frappe.views.CommunicationComposer = Class.extend({
{label:__("Select Attachments"), fieldtype:"HTML",
fieldname:"select_attachments"}
];
+ if(!fields[1]){//removes from if doesnt have assigned email
+ fields.splice(1,1)
+ }
+ return fields
},
+ get_from:function(){
+ var me = this;
+ frappe.call({
+ method: 'frappe.email.page.email_inbox.get_accounts',
+ args: {user: frappe.user["name"]},
+ async: false,
+ callback: function (list) {
+ if (list["message"]) {
+ var accounts = [];
+ for (var i =0;i2&&!this.$element.is("ul"))throw"in Bootstrap version 3 the pagination root item must be an ul element.";this.currentPage=1,this.lastPage=1,this.setOptions(options),this.initialized=!0},setOptions:function(options){this.options=$.extend({},this.options||$.fn.bootstrapPaginator.defaults,options),this.totalPages=parseInt(this.options.totalPages,10),this.numberOfPages=parseInt(this.options.numberOfPages,10),options&&"undefined"!=typeof options.currentPage&&this.setCurrentPage(options.currentPage),this.listen(),this.render(),this.initialized||this.lastPage===this.currentPage||this.$element.trigger("page-changed",[this.lastPage,this.currentPage])},listen:function(){this.$element.off("page-clicked"),this.$element.off("page-changed"),"function"==typeof this.options.onPageClicked&&this.$element.bind("page-clicked",this.options.onPageClicked),"function"==typeof this.options.onPageChanged&&this.$element.on("page-changed",this.options.onPageChanged),this.$element.bind("page-clicked",this.onPageClicked)},destroy:function(){this.$element.off("page-clicked"),this.$element.off("page-changed"),this.$element.removeData("bootstrapPaginator"),this.$element.empty()},show:function(page){this.setCurrentPage(page),this.render(),this.lastPage!==this.currentPage&&this.$element.trigger("page-changed",[this.lastPage,this.currentPage])},showNext:function(){var pages=this.getPages();pages.next&&this.show(pages.next)},showPrevious:function(){var pages=this.getPages();pages.prev&&this.show(pages.prev)},showFirst:function(){var pages=this.getPages();pages.first&&this.show(pages.first)},showLast:function(){var pages=this.getPages();pages.last&&this.show(pages.last)},onPageItemClicked:function(event){var type=event.data.type,page=event.data.page;this.$element.trigger("page-clicked",[event,type,page])},onPageClicked:function(event,originalEvent,type,page){var currentTarget=$(event.currentTarget);switch(type){case"first":currentTarget.bootstrapPaginator("showFirst");break;case"prev":currentTarget.bootstrapPaginator("showPrevious");break;case"next":currentTarget.bootstrapPaginator("showNext");break;case"last":currentTarget.bootstrapPaginator("showLast");break;case"page":currentTarget.bootstrapPaginator("show",page)}},render:function(){var containerClass=this.getValueFromOption(this.options.containerClass,this.$element),size=this.options.size||"normal",alignment=this.options.alignment||"left",pages=this.getPages(),listContainer=2===this.options.bootstrapMajorVersion?$(""):this.$element,listContainerClass=2===this.options.bootstrapMajorVersion?this.getValueFromOption(this.options.listContainerClass,listContainer):null,first=null,prev=null,next=null,last=null,p=null,i=0;switch(this.$element.prop("class",""),this.$element.addClass("pagination"),size.toLowerCase()){case"large":case"small":case"mini":this.$element.addClass($.fn.bootstrapPaginator.sizeArray[this.options.bootstrapMajorVersion][size.toLowerCase()])}if(2===this.options.bootstrapMajorVersion)switch(alignment.toLowerCase()){case"center":this.$element.addClass("pagination-centered");break;case"right":this.$element.addClass("pagination-right")}for(this.$element.addClass(containerClass),this.$element.empty(),2===this.options.bootstrapMajorVersion&&(this.$element.append(listContainer),listContainer.addClass(listContainerClass)),this.pageRef=[],pages.first&&(first=this.buildPageItem("first",pages.first),first&&listContainer.append(first)),pages.prev&&(prev=this.buildPageItem("prev",pages.prev),prev&&listContainer.append(prev)),i=0;i"),itemContent=$(" "),text="",title="",itemContainerClass=this.options.itemContainerClass(type,page,this.currentPage),itemContentClass=this.getValueFromOption(this.options.itemContentClass,type,page,this.currentPage),tooltipOpts=null;switch(type){case"first":if(!this.getValueFromOption(this.options.shouldShowPage,type,page,this.currentPage))return;text=this.options.itemTexts(type,page,this.currentPage),title=this.options.tooltipTitles(type,page,this.currentPage);break;case"last":if(!this.getValueFromOption(this.options.shouldShowPage,type,page,this.currentPage))return;text=this.options.itemTexts(type,page,this.currentPage),title=this.options.tooltipTitles(type,page,this.currentPage);break;case"prev":if(!this.getValueFromOption(this.options.shouldShowPage,type,page,this.currentPage))return;text=this.options.itemTexts(type,page,this.currentPage),title=this.options.tooltipTitles(type,page,this.currentPage);break;case"next":if(!this.getValueFromOption(this.options.shouldShowPage,type,page,this.currentPage))return;text=this.options.itemTexts(type,page,this.currentPage),title=this.options.tooltipTitles(type,page,this.currentPage);break;case"page":if(!this.getValueFromOption(this.options.shouldShowPage,type,page,this.currentPage))return;text=this.options.itemTexts(type,page,this.currentPage),title=this.options.tooltipTitles(type,page,this.currentPage)}return itemContainer.addClass(itemContainerClass).append(itemContent),itemContent.addClass(itemContentClass).html(text).on("click",null,{type:type,page:page},$.proxy(this.onPageItemClicked,this)),this.options.pageUrl&&itemContent.attr("href",this.getValueFromOption(this.options.pageUrl,type,page,this.currentPage)),this.options.useBootstrapTooltip?(tooltipOpts=$.extend({},this.options.bootstrapTooltipOptions,{title:title}),itemContent.tooltip(tooltipOpts)):itemContent.attr("title",title),itemContainer},setCurrentPage:function(page){if(page>this.totalPages||1>page)throw"Page out of range";this.lastPage=this.currentPage,this.currentPage=parseInt(page,10)},getPages:function(){var totalPages=this.totalPages,pageStart=0===this.currentPage%this.numberOfPages?(parseInt(this.currentPage/this.numberOfPages,10)-1)*this.numberOfPages+1:parseInt(this.currentPage/this.numberOfPages,10)*this.numberOfPages+1,output=[],i=0,counter=0;for(pageStart=1>pageStart?1:pageStart,i=pageStart,counter=0;counter=i;i+=1,counter+=1)output.push(i);return output.first=1,output.prev=this.currentPage>1?this.currentPage-1:1,output.next=this.currentPage
- {{ _("Dont have an account? Sign up") }}
+ {{ _("Don't have an account? Sign up") }}
{%- endif -%}