Merge branch 'develop' into custom-perms-1
This commit is contained in:
commit
e223c5b740
93 changed files with 7461 additions and 1411 deletions
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
0
frappe/core/doctype/dynamic_link/__init__.py
Normal file
0
frappe/core/doctype/dynamic_link/__init__.py
Normal file
97
frappe/core/doctype/dynamic_link/dynamic_link.json
Normal file
97
frappe/core/doctype/dynamic_link/dynamic_link.json
Normal file
|
|
@ -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
|
||||
}
|
||||
13
frappe/core/doctype/dynamic_link/dynamic_link.py
Normal file
13
frappe/core/doctype/dynamic_link/dynamic_link.py
Normal file
|
|
@ -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"])
|
||||
|
|
@ -76,6 +76,26 @@ frappe.ui.form.on('User', {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (frm.doc.user_emails){
|
||||
var found =0;
|
||||
for (var i = 0;i<frm.doc.user_emails.length;i++){
|
||||
if (frm.doc.email==frm.doc.user_emails[i].email_id){
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found){
|
||||
frm.add_custom_button("Create User Email",frm.events.create_user_email)
|
||||
}
|
||||
}
|
||||
|
||||
if (frappe.route_titles["unsaved"]===1){
|
||||
delete frappe.route_titles["unsaved"];
|
||||
for ( var i=0;i<frm.doc.user_emails.length;i++){
|
||||
frm.doc.user_emails[i].idx=frm.doc.user_emails[i].idx+1;
|
||||
}
|
||||
frm.doc.email_account
|
||||
cur_frm.dirty();
|
||||
}
|
||||
},
|
||||
validate: function(frm) {
|
||||
if(frm.roles_editor) {
|
||||
|
|
@ -92,6 +112,30 @@ frappe.ui.form.on('User', {
|
|||
if(user!="Administrator") {
|
||||
frm.toggle_enable('email', doc.__islocal);
|
||||
}
|
||||
},
|
||||
create_user_email:function(frm) {
|
||||
frappe.call({
|
||||
method: 'frappe.core.doctype.user.user.has_email_account',
|
||||
args: {email:cur_frm.doc.email},
|
||||
callback: function(r) {
|
||||
if (r["message"]== undefined){
|
||||
frappe.route_options = {
|
||||
"email_id": cur_frm.doc.email,
|
||||
"awaiting_password":1,
|
||||
"enable_incoming":1,
|
||||
"append_to":"Communication"
|
||||
};
|
||||
frappe.model.with_doctype("Email Account", function (doc) {
|
||||
var doc = frappe.model.get_new_doc("Email Account");
|
||||
frappe.route_titles["create user account"]=cur_frm.doc.name;
|
||||
frappe.set_route("Form", "Email Account", doc.name);
|
||||
})
|
||||
}else{
|
||||
frappe.route_titles["create user account"]=cur_frm.doc.name;
|
||||
frappe.set_route("Form", "Email Account", r["message"][0]["name"]);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -888,6 +888,64 @@
|
|||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "email_inbox",
|
||||
"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": "Email Inbox",
|
||||
"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": "user_emails",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "User Emails",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User Email",
|
||||
"permlevel": 1,
|
||||
"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,
|
||||
|
|
@ -1755,7 +1813,7 @@
|
|||
"istable": 0,
|
||||
"max_attachments": 5,
|
||||
"menu_index": 0,
|
||||
"modified": "2016-12-29 14:40:03.428429",
|
||||
"modified": "2017-01-13 07:10:40.266109",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "User",
|
||||
|
|
|
|||
|
|
@ -44,6 +44,29 @@ class User(Document):
|
|||
def after_insert(self):
|
||||
self.set_default_roles()
|
||||
|
||||
def force_user_email_update(self):
|
||||
for user_email in self.user_emails:
|
||||
if not user_email.email_id:
|
||||
user_email.email_id = frappe.db.get_value("Email Account", {"name": user_email.email_account},
|
||||
"email_id")
|
||||
|
||||
def user_emails_to_permissions(self):
|
||||
if frappe.session.user == "Administrator" or "System Manager" in frappe.get_roles():
|
||||
from frappe.core.page.user_permissions.user_permissions import get_permissions
|
||||
|
||||
permissions = set([x.defvalue for x in get_permissions(self.name, "Email Account")])
|
||||
user_emails = set([x.email_account for x in self.user_emails])
|
||||
|
||||
# compare vs user email
|
||||
add = user_emails - permissions
|
||||
remove = permissions - user_emails
|
||||
|
||||
# set the difference
|
||||
for r in remove:
|
||||
frappe.permissions.remove_user_permission("Email Account", r, self.name)
|
||||
for a in add:
|
||||
frappe.permissions.add_user_permission("Email Account", a, self.name, with_message=True)
|
||||
|
||||
def validate(self):
|
||||
self.check_demo()
|
||||
|
||||
|
|
@ -63,6 +86,9 @@ class User(Document):
|
|||
self.remove_all_roles_for_guest()
|
||||
self.validate_username()
|
||||
self.remove_disabled_roles()
|
||||
self.force_user_email_update()
|
||||
self.user_emails_to_permissions()
|
||||
ask_pass_update()
|
||||
|
||||
if self.language == "Loading...":
|
||||
self.language = None
|
||||
|
|
@ -539,6 +565,55 @@ def test_password_strength(new_password, key=None, old_password=None):
|
|||
if new_password:
|
||||
return _test_password_strength(new_password, user_inputs=user_data)
|
||||
|
||||
#for login
|
||||
@frappe.whitelist()
|
||||
def has_email_account(email):
|
||||
return frappe.get_list("Email Account", filters={"email_id": email})
|
||||
|
||||
@frappe.whitelist(allow_guest=False)
|
||||
def get_email_awaiting(user):
|
||||
waiting = frappe.db.sql("""select email_account,email_id
|
||||
from `tabUser Email`
|
||||
where awaiting_password = 1
|
||||
and parent = %(user)s""", {"user":user}, as_dict=1)
|
||||
if waiting:
|
||||
return waiting
|
||||
else:
|
||||
frappe.db.sql("""update `tabUser Email`
|
||||
set awaiting_password =0
|
||||
where parent = %(user)s""",{"user":user})
|
||||
return False
|
||||
|
||||
@frappe.whitelist(allow_guest=False)
|
||||
def set_email_password(email_account, user, password):
|
||||
account = frappe.get_doc("Email Account",
|
||||
email_account)
|
||||
if account.awaiting_password:
|
||||
account.set("awaiting_password",0)
|
||||
account.set("password",password)
|
||||
try:
|
||||
validate = account.validate()
|
||||
save= account.save(ignore_permissions=True)
|
||||
frappe.db.sql("""update `tabUser Email` set awaiting_password = 0
|
||||
where email_account = %(account)s""",{"account": email_account})
|
||||
ask_pass_update()
|
||||
except Exception, e:
|
||||
frappe.db.rollback()
|
||||
return False
|
||||
return True
|
||||
|
||||
def ask_pass_update():
|
||||
# update the sys defaults as to awaiting users
|
||||
from frappe.utils import set_default
|
||||
users = frappe.db.sql("""SELECT DISTINCT(parent)
|
||||
FROM `tabUser Email`
|
||||
WHERE awaiting_password = 1""", as_list=1)
|
||||
|
||||
password_list = []
|
||||
for u in users:
|
||||
password_list.append(u[0])
|
||||
set_default("email_user_password", u','.join(password_list))
|
||||
|
||||
def _get_user_for_update_password(key, old_password):
|
||||
# verify old password
|
||||
if key:
|
||||
|
|
|
|||
0
frappe/core/doctype/user_email/__init__.py
Normal file
0
frappe/core/doctype/user_email/__init__.py
Normal file
125
frappe/core/doctype/user_email/user_email.json
Normal file
125
frappe/core/doctype/user_email/user_email.json
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2016-03-30 10:04:25.828742",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "email_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"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": 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": "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,
|
||||
"options": "email_account.awaiting_password",
|
||||
"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": "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 ID",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "email_account.email_id",
|
||||
"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,
|
||||
"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 07:07:40.049130",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "User Email",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
||||
10
frappe/core/doctype/user_email/user_email.py
Normal file
10
frappe/core/doctype/user_email/user_email.py
Normal file
|
|
@ -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 UserEmail(Document):
|
||||
pass
|
||||
|
|
@ -148,13 +148,14 @@ def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=
|
|||
if not fields:
|
||||
fields = '''name, communication_type,
|
||||
communication_medium, comment_type,
|
||||
content, sender, sender_full_name, creation, subject, delivery_status, _liked_by,
|
||||
content, sender, sender_full_name, communication_date, subject, delivery_status, _liked_by,
|
||||
timeline_doctype, timeline_name,
|
||||
reference_doctype, reference_name,
|
||||
link_doctype, link_name,
|
||||
"Communication" as doctype'''
|
||||
|
||||
conditions = '''communication_type in ("Communication", "Comment")
|
||||
and timeline_hide is null
|
||||
and (
|
||||
(reference_doctype=%(doctype)s and reference_name=%(name)s)
|
||||
or (
|
||||
|
|
@ -170,7 +171,7 @@ def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=
|
|||
|
||||
if after:
|
||||
# find after a particular date
|
||||
conditions+= ' and creation > {0}'.format(after)
|
||||
conditions+= ' and communication_date > {0}'.format(after)
|
||||
|
||||
communications = frappe.db.sql("""select {fields}
|
||||
from tabCommunication
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
{%= __("{0} {1}", ["<strong>" + __(feed_type) + "</strong>", feed_type==="Deleted" ? subject : link ]) %}
|
||||
{% } else if (feed_type==="Updated") { %}
|
||||
{%= __("Updated {0}: {1}", [link, "<strong>" + subject + "</strong>"]) %}
|
||||
{% } else if (feed_type==="Relinked") { %}
|
||||
{%= __("{0} {1} to {2}", [by, content,link]) %}
|
||||
{% } else if (reference_doctype && reference_name) { %}
|
||||
{%= __("{0}: {1}", [link, "<strong>" + content + "</strong>"]) %}
|
||||
{% } else { %}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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 = '<a href="/desk#Form/Communication/' + name +'" target="_blank">' + origin.subject
|
||||
content= 'Relinked ' + origin.communication_medium + ' ' + subject_link + '</a>'
|
||||
|
||||
if origin.reference_doctype:
|
||||
from_link = '<a href="/desk#Form/' + origin.reference_doctype +'/'+ origin.reference_name +'" target="_blank">'
|
||||
content += ' from ' + from_link + origin.reference_doctype +' '+ origin.reference_name +'</a>'
|
||||
|
||||
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
|
||||
|
|
|
|||
0
frappe/email/doctype/contact/__init__.py
Normal file
0
frappe/email/doctype/contact/__init__.py
Normal file
41
frappe/email/doctype/contact/contact.js
Normal file
41
frappe/email/doctype/contact/contact.js
Normal file
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
805
frappe/email/doctype/contact/contact.json
Normal file
805
frappe/email/doctype/contact/contact.json
Normal file
|
|
@ -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
|
||||
}
|
||||
130
frappe/email/doctype/contact/contact.py
Normal file
130
frappe/email/doctype/contact/contact.py
Normal file
|
|
@ -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')
|
||||
})
|
||||
12
frappe/email/doctype/contact/test_contact.py
Normal file
12
frappe/email/doctype/contact/test_contact.py
Normal file
|
|
@ -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
|
||||
22
frappe/email/doctype/contact/test_records.json
Normal file
22
frappe/email/doctype/contact/test_records.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
@ -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</a>');
|
||||
}
|
||||
},
|
||||
});
|
||||
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);
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'''
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
0
frappe/email/doctype/email_domain/__init__.py
Normal file
0
frappe/email/doctype/email_domain/__init__.py
Normal file
16
frappe/email/doctype/email_domain/email_domain.js
Normal file
16
frappe/email/doctype/email_domain/email_domain.js
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
443
frappe/email/doctype/email_domain/email_domain.json
Normal file
443
frappe/email/doctype/email_domain/email_domain.json
Normal file
|
|
@ -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
|
||||
}
|
||||
80
frappe/email/doctype/email_domain/email_domain.py
Normal file
80
frappe/email/doctype/email_domain/email_domain.py
Normal file
|
|
@ -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
|
||||
|
||||
12
frappe/email/doctype/email_domain/test_email_domain.py
Normal file
12
frappe/email/doctype/email_domain/test_email_domain.py
Normal file
|
|
@ -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
|
||||
0
frappe/email/doctype/email_flag_queue/__init__.py
Normal file
0
frappe/email/doctype/email_flag_queue/__init__.py
Normal file
|
|
@ -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) {
|
||||
|
||||
}
|
||||
});
|
||||
144
frappe/email/doctype/email_flag_queue/email_flag_queue.json
Normal file
144
frappe/email/doctype/email_flag_queue/email_flag_queue.json
Normal file
|
|
@ -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
|
||||
}
|
||||
10
frappe/email/doctype/email_flag_queue/email_flag_queue.py
Normal file
10
frappe/email/doctype/email_flag_queue/email_flag_queue.py
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
0
frappe/email/doctype/unhandled_email/__init__.py
Normal file
0
frappe/email/doctype/unhandled_email/__init__.py
Normal file
12
frappe/email/doctype/unhandled_email/test_unhandled_email.py
Normal file
12
frappe/email/doctype/unhandled_email/test_unhandled_email.py
Normal file
|
|
@ -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
|
||||
204
frappe/email/doctype/unhandled_email/unhandled_email.json
Normal file
204
frappe/email/doctype/unhandled_email/unhandled_email.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
11
frappe/email/doctype/unhandled_email/unhandled_email.py
Normal file
11
frappe/email/doctype/unhandled_email/unhandled_email.py
Normal file
|
|
@ -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)
|
||||
0
frappe/email/page/__init__.py
Normal file
0
frappe/email/page/__init__.py
Normal file
119
frappe/email/page/email_inbox/__init__.py
Normal file
119
frappe/email/page/email_inbox/__init__.py
Normal file
|
|
@ -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()
|
||||
636
frappe/email/page/email_inbox/email_inbox.js
Normal file
636
frappe/email/page/email_inbox/email_inbox.js
Normal file
|
|
@ -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.length; i++) {
|
||||
if (data.account == me.accounts[i].name) {
|
||||
frappe.utils.notify(data.account, "you have "+data.number+" new emails", {}, function () {
|
||||
window.focus();
|
||||
me.account = data.account;
|
||||
$(me.page.sidebar).find(".list-row").removeClass("list-row-head").css("font-weight","normal");
|
||||
$('.inbox-item[data-account="' + data.account + '" ]').closest(".list-row").addClass("list-row-head").css("font-weight","bold");
|
||||
me.refresh();
|
||||
});
|
||||
if(!me.fresh &&(data.account == me.account || me.account == me.allaccounts)) {
|
||||
me.fresh = true;
|
||||
me.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
me.fresh = false
|
||||
});
|
||||
} else {
|
||||
frappe.msgprint(__("No Email Account assigned to you. Please contact your System Administrator"));
|
||||
|
||||
setTimeout(function() {
|
||||
if (frappe.session.user==="Administrator") {
|
||||
frappe.set_route("List", "User");
|
||||
} else {
|
||||
frappe.set_route('');
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
},
|
||||
refresh:function(){
|
||||
delete frappe.route_titles["create_contact"];
|
||||
delete frappe.route_titles["update_contact"];
|
||||
this.run();
|
||||
},
|
||||
render_headers: function(){
|
||||
$(".layout-main-section-wrapper").css("padding-left","0px").css("padding-right","0px");
|
||||
var data = {"start":this.start,"page_length":this.page_length.toString()};
|
||||
this.list_header = $(frappe.render_template("inbox_headers", data)).appendTo(this.page.main.find(".list-headers"));
|
||||
},
|
||||
render_sidemenu: function () {
|
||||
var me = this;
|
||||
frappe.call({
|
||||
method: 'frappe.email.page.email_inbox.get_accounts',
|
||||
args:{user:frappe.user["name"]},
|
||||
async:false,
|
||||
callback:function(list){
|
||||
var buttons = '<div class="layout-main-section overlay-sidebar">';
|
||||
if (list["message"]){
|
||||
me.accounts = [];
|
||||
var rows = "";
|
||||
|
||||
for (var i = 0;i<list["message"].length;i++)
|
||||
{
|
||||
rows += '<div class="list-row inbox-select"> <div class="row"><a class="inbox-item ellipsis col-md-12" title ="'+list["message"][i]["email_id"]+'" data-account="'+list["message"][i]["email_account"]+'" style="margin-left: 10px;">'+list["message"][i]["email_id"]+'</a> </div></div>';
|
||||
me.accounts.push({name:list["message"][i]["email_account"],email:list["message"][i]["email_id"]})
|
||||
}
|
||||
me.allaccounts = $.map(me.accounts,function(v){return v.name}).join(",");
|
||||
buttons += '<div class="list-row inbox-select list-row-head" style="font-weight:bold"> <div class="row"><a class="inbox-item ellipsis col-md-12 " title ="All Accounts" data-account="'+me.allaccounts+'" style="margin-left: 10px;">All Accounts</a> </div></div>';
|
||||
buttons += rows;
|
||||
buttons += '<div class="list-row inbox-select"> <div class="row"><a class="inbox-item ellipsis col-md-12 " title ="Sent" data-account="Sent" style="margin-left: 10px;">Sent</a> </div></div>';
|
||||
me.account = me.allaccounts;
|
||||
me.default_filters=[
|
||||
["Communication", "communication_type", "=", "Communication"],
|
||||
["Communication", "email_account", "in", me.account],
|
||||
["Communication", "sent_or_received", "=", "Received"]]
|
||||
|
||||
me.page.sidebar.empty().append(buttons);
|
||||
$(".inbox-select").on("click",function(btn){
|
||||
me.account = $(btn.currentTarget).find(".inbox-item").data("account");
|
||||
$(me.page.sidebar).find(".list-row").removeClass("list-row-head").css("font-weight","normal");
|
||||
$(btn.currentTarget).closest(".list-row").addClass("list-row-head").css("font-weight","bold");
|
||||
me.cur_page = 1;
|
||||
$(me.page.main).find(".list-select-all,.list-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<me.accounts.length;i++){
|
||||
if(me.accounts[i].name===me.account){
|
||||
sender = me.accounts[i].email;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new frappe.views.CommunicationComposer({
|
||||
doc: {
|
||||
doctype: c.reference_doctype,
|
||||
name: c.reference_name
|
||||
},
|
||||
sender:sender,
|
||||
subject: "Re: " + c.subject,
|
||||
recipients: c.sender,
|
||||
last_email: c,
|
||||
attachments:c.attachments
|
||||
});
|
||||
});
|
||||
//reply-all
|
||||
$(emailitem.$wrapper).find(".reply-all-link").on("click", function () {
|
||||
var sender = "";
|
||||
for (var i=0;i<me.accounts.length;i++){
|
||||
if(me.accounts[i].name===me.account){
|
||||
sender = me.accounts[i].email;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new frappe.views.CommunicationComposer({
|
||||
doc: {
|
||||
doctype: c.reference_doctype,
|
||||
name: c.reference_name
|
||||
},
|
||||
sender:sender,
|
||||
subject: "Re: " + c.subject,
|
||||
recipients: (c.sender + (c.recipients ? ", "+c.recipients:"") + (c.cc ? ", "+c.cc:"")).replace(sender,""),
|
||||
last_email: c,
|
||||
attachments:c.attachments
|
||||
});
|
||||
});
|
||||
//forward
|
||||
$(emailitem.$wrapper).find(".forward-link").on("click", function () {
|
||||
var sender = "";
|
||||
for (var i=0;i<me.accounts.length;i++){
|
||||
if(me.accounts[i].name===me.account){
|
||||
sender = me.accounts[i].email;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var communication = new frappe.views.CommunicationComposer({
|
||||
doc: {
|
||||
doctype: c.reference_doctype,
|
||||
name: c.reference_name
|
||||
},
|
||||
sender:sender,
|
||||
subject: "FW: " + c.subject,
|
||||
last_email: c,
|
||||
forward:true,
|
||||
attachments:c.attachments
|
||||
});
|
||||
|
||||
$(communication.dialog.fields_dict.select_attachments.wrapper).find("input[type=checkbox]").prop("checked",true)
|
||||
});
|
||||
},
|
||||
relink:function(row){
|
||||
var me = this;
|
||||
var callback = function(frm){
|
||||
$(me.wrapper).find(".row-named[data-name="+row.name+"]").find(".reference-document")
|
||||
.html(values["reference_name"])
|
||||
.attr("href",'#Form/'+values["reference_doctype"]+ '/'+values["reference_name"])
|
||||
.attr("title","Linked Doctype: "+values["reference_doctype"]);
|
||||
row.reference_doctype = values["reference_doctype"];
|
||||
row.reference_name = values["reference_name"];
|
||||
}
|
||||
frappe.timeline.relink_dialog(row.name, row.reference_doctype, row.reference_name, callback, row.timeline_hide);
|
||||
},
|
||||
run:function(more,footer) {
|
||||
var me = this;
|
||||
me.start = (me.cur_page-1) * me.page_length;
|
||||
if (!footer) {
|
||||
this.update_footer()
|
||||
}
|
||||
this._super(more)
|
||||
},
|
||||
prepare_email:function(c){
|
||||
var me = this;
|
||||
frappe.call({
|
||||
method:'frappe.email.page.email_inbox.get_email_content',
|
||||
args:{
|
||||
doctype:"Communication",
|
||||
name:c.name
|
||||
},
|
||||
async:false,
|
||||
callback:function(r){
|
||||
c.attachments =r["message"][0];
|
||||
c.content = r["message"][1];
|
||||
}
|
||||
});
|
||||
c.doctype ="Communication";
|
||||
|
||||
c.comment_on = comment_when(c.communication_date);
|
||||
|
||||
|
||||
if (c.attachments && typeof c.attachments === "string")
|
||||
c.attachments = JSON.parse(c.attachments);
|
||||
|
||||
if (!c.comment_type)
|
||||
c.comment_type = "Email";
|
||||
|
||||
c.comment = c.content;
|
||||
if (c.comment_type == "Email") {
|
||||
c.comment = c.comment.split("<!-- original-reply -->")[0];
|
||||
c.comment = frappe.utils.strip_original_content(c.comment);
|
||||
c.comment = frappe.dom.remove_script_and_style(c.comment);
|
||||
|
||||
c.original_comment = c.comment;
|
||||
c.comment = frappe.utils.toggle_blockquote(c.comment);
|
||||
}
|
||||
|
||||
|
||||
if (!frappe.utils.is_html(c.comment)) {
|
||||
c.comment_html = frappe.markdown(__(c.comment));
|
||||
} else {
|
||||
c.comment_html = c.comment;
|
||||
c.comment_html = frappe.utils.strip_whitespace(c.comment_html);
|
||||
c.comment_html = c.comment_html.replace(/</g,"<").replace(/>/g,">")
|
||||
}
|
||||
|
||||
|
||||
|
||||
// bold @mentions
|
||||
if (c.comment_type === "Comment") {
|
||||
c.comment_html = c.comment_html.replace(/(^|\W)(@\w+)/g, "$1<b>$2</b>");
|
||||
}
|
||||
|
||||
return c
|
||||
},
|
||||
init_select_all: function () {
|
||||
var me = this;
|
||||
|
||||
$(".list-select-all").on("click", function () {
|
||||
$(me.wrapper).find('.list-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<me.accounts.length;i++){
|
||||
if(me.accounts[i].name===me.account){
|
||||
sender = me.accounts[i].email
|
||||
break;
|
||||
}
|
||||
}
|
||||
new frappe.views.CommunicationComposer({
|
||||
doc: {},
|
||||
sender: sender
|
||||
});
|
||||
},"fa-plus","New Email");
|
||||
},
|
||||
toggle_actions: function () {
|
||||
var me = this;
|
||||
if (me.page.main.find(".list-delete:checked").length) {
|
||||
//show buttons
|
||||
$(me.page.actions_btn_group).show();
|
||||
$(me.page.btn_primary).hide()
|
||||
} else {
|
||||
//hide button
|
||||
$(me.page.actions_btn_group).hide();
|
||||
$(me.page.btn_primary).show()
|
||||
}
|
||||
},
|
||||
delete_email:function(data){
|
||||
var me = this;
|
||||
if (!data) {
|
||||
var names = $.map(me.action_checked_items('.data("data")'),function(v){return {name:v.name, uid:v.uid}});
|
||||
me.action_checked_items('.remove()')
|
||||
} else {
|
||||
var names = [{name:data.name, uid:data.uid}]
|
||||
}
|
||||
//could add flag to sync deletes but not going to as keeps history
|
||||
|
||||
me.update_local_flags(names, "deleted", "1")
|
||||
},
|
||||
mark_unread:function(){
|
||||
var me = this;
|
||||
var names = $.map(me.action_checked_items('.data("data")'), function(v){return {name:v.name, uid:v.uid}});
|
||||
me.create_flag_queue(names,"-FLAGS","(\\SEEN)","seen");
|
||||
me.action_checked_items('.css("font-weight", "BOLD")');
|
||||
me.update_local_flags(names,"seen","0")
|
||||
},
|
||||
mark_read:function(data){
|
||||
var me = this;
|
||||
if (!data) {
|
||||
var names = $.map(me.action_checked_items('.data("data")'), function(v){return {name:v.name, uid:v.uid}});
|
||||
me.action_checked_items('.css("font-weight", "normal")')
|
||||
} else{
|
||||
var names = [{name:data.name, uid:data.uid}];
|
||||
$(".row-named").filter("[data-name=" + data.name + "]").css("font-weight", "normal")
|
||||
}
|
||||
me.create_flag_queue(names,"+FLAGS","(\\SEEN)","seen");
|
||||
me.update_local_flags(names,"seen","1")
|
||||
|
||||
},
|
||||
create_flag_queue:function(names,action,flag,field){
|
||||
frappe.call({
|
||||
method: 'frappe.email.page.email_inbox.create_flag_queue',
|
||||
args:{
|
||||
names:JSON.stringify(names),
|
||||
action:action,
|
||||
flag:flag,
|
||||
field:field
|
||||
}
|
||||
})
|
||||
},
|
||||
update_local_flags:function(names,field,val){
|
||||
frappe.call({
|
||||
method: 'frappe.email.page.email_inbox.update_local_flags',
|
||||
args:{
|
||||
names:JSON.stringify(names),
|
||||
field:field,
|
||||
val:val
|
||||
}
|
||||
})
|
||||
$('.list-delete:checked').prop( "checked", false );
|
||||
},
|
||||
action_checked_items: function(action) {
|
||||
return $.map(this.page.main.find('.list-delete:checked'), function(e) {
|
||||
return eval('$(e).closest(".row-named")'+action);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
23
frappe/email/page/email_inbox/email_inbox.json
Normal file
23
frappe/email/page/email_inbox/email_inbox.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"content": null,
|
||||
"creation": "2016-04-14 10:22:57.308665",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"icon": "fa fa-inbox",
|
||||
"idx": 0,
|
||||
"modified": "2016-12-22 17:19:55.284608",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "email_inbox",
|
||||
"owner": "Administrator",
|
||||
"page_name": "email_inbox",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Email User"
|
||||
}
|
||||
],
|
||||
"script": null,
|
||||
"standard": "Yes",
|
||||
"style": null,
|
||||
"title": "Email Inbox"
|
||||
}
|
||||
60
frappe/email/page/email_inbox/inbox_email.html
Normal file
60
frappe/email/page/email_inbox/inbox_email.html
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<div class="pull-left media-body" style="max-width: 100%; padding-right: 0px;min-height: 200px;">
|
||||
<div class="media-content-wrapper">
|
||||
<div class="comment-header small" >
|
||||
<span title="{%= data.sender %}">{%= data.sender_full_name?data.sender_full_name:data.sender %}</span>
|
||||
<span class="text-muted" style="font-weight: normal;">
|
||||
– {%= data.comment_on %}</span>
|
||||
{% if(data.doctype=="Communication") { %}
|
||||
{% if (frappe.model.can_read(\'Communication\')) { %}
|
||||
<a href="#Form/{%= data.doctype %}/{%= data.name %}"
|
||||
class="text-muted">
|
||||
{% } %}
|
||||
|
||||
{% if (data.delivery_status) {
|
||||
if (in_list(["Sent", "Opened", "Clicked"], data.delivery_status)) {
|
||||
var indicator_class = "green";
|
||||
} else {
|
||||
var indicator_class = "red";
|
||||
}
|
||||
%}
|
||||
<span class="text-muted">–</span>
|
||||
<span class="indicator-right {%= indicator_class %} delivery-status-indicator"
|
||||
title="{%= data.delivery_status %}">
|
||||
{%= data.delivery_status %}</span>
|
||||
|
||||
{% } else { %}
|
||||
{% if (frappe.model.can_read(\'Communication\')) { %}
|
||||
<span class="text-muted">–</span>
|
||||
{%= __("Details") %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
|
||||
{% if (frappe.model.can_read(\'Communication\')) { %}
|
||||
</a>
|
||||
{% } %}
|
||||
{% } %}
|
||||
</div>
|
||||
|
||||
{% if(data.attachments && data.attachments.length) { %}
|
||||
<div style="margin: 10px 0px">
|
||||
{% $.each(data.attachments, function(i, a) { %}
|
||||
<div class="ellipsis">
|
||||
<a href="{%= a.file_url.replace(/#/g, \'%23\') %}" class=" small" target="_blank">
|
||||
<i class="fa fa-paperclip"></i>
|
||||
{%= a.file_url.split("/").slice(-1)[0] %}
|
||||
{% if (a.is_private) { %}
|
||||
<i class="fa fa-lock text-warning"></i>
|
||||
{% } %}
|
||||
</a>
|
||||
</div>
|
||||
{% }); %}
|
||||
</div>
|
||||
{% } %}
|
||||
|
||||
<div class="reply">
|
||||
<div>
|
||||
{%= data.comment_html %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
11
frappe/email/page/email_inbox/inbox_email_actions.html
Normal file
11
frappe/email/page/email_inbox/inbox_email_actions.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> Actions
|
||||
<span class="caret"/>
|
||||
</button>
|
||||
<ul class="dropdown-menu" style="min-width: 100px">
|
||||
<li class="user-action reply-link"><a>Reply</a></li>
|
||||
<li class="user-action reply-all-link"><a>Reply-All</a></li>
|
||||
<li class="user-action forward-link"><a>Forward</a></li>
|
||||
<li class="user-action delete-link"><a>Delete</a></li>
|
||||
<li class="user-action company-link"><a>{%= data.timeline_label ? "Rematch" : "Match" %}</a></li>
|
||||
<li class="user-action relink-link"><a>{%= data.reference_name ? "Relink": "Link" %} </a></li>
|
||||
</ul>
|
||||
8
frappe/email/page/email_inbox/inbox_filter.html
Normal file
8
frappe/email/page/email_inbox/inbox_filter.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<div class="list-filters" style="display: none;">
|
||||
<div class="show_filters">
|
||||
<div class="set-filters">
|
||||
<button class="btn btn-default btn-xs show-filter-dashboard text-muted" style="margin-right: 10px;">
|
||||
{%= __("Show Filter Dashboard") %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
9
frappe/email/page/email_inbox/inbox_footer.html
Normal file
9
frappe/email/page/email_inbox/inbox_footer.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<footer class="footer hidden-xs" style="position: fixed;left: 0;bottom: 0;width: 100%;height: 60px;background-color: #f5f5f5;">
|
||||
<div class="container" >
|
||||
<div class="col-sm-2 hidden-sm"/>
|
||||
<div class="col-sm-7">
|
||||
<ul class="foot-con"/>
|
||||
<div class="footer-numbers" style="vertical-align: middle;float:right;margin: 20px 0"/>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
27
frappe/email/page/email_inbox/inbox_headers.html
Normal file
27
frappe/email/page/email_inbox/inbox_headers.html
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<div class="list-row list-row-head">
|
||||
<div class="row">
|
||||
<div class="col-mg-5 col-sm-12 list-row-left">
|
||||
<div class="row">
|
||||
<div class="col-sm-2 list-col ellipsis text-muted h6 col-xs-12">
|
||||
<input class="list-select-all" type="checkbox" style="margin: 0 7px 0 0;" title="Select All">
|
||||
<span class="list-value" style="vertical-align: top;">Sender</span>
|
||||
</div>
|
||||
<div class="col-sm-4 list-col ellipsis h6 text-muted col-xs-12">
|
||||
<span class="list-value">Subject</span>
|
||||
</div>
|
||||
<div class="col-sm-2 list-col ellipsis h6 text-muted hidden-xs" title="Company/user this sender will be match to for history">
|
||||
<span class="list-value">Company/User</span>
|
||||
</div>
|
||||
<div class="col-sm-2 list-col ellipsis h6 text-muted hidden-xs" title="Document this email will display on">
|
||||
<span class="list-value">Linked Document</span>
|
||||
</div>
|
||||
<div class="col-sm-1 list-col ellipsis h6 text-muted hidden-xs" title="Does the e-mail contain attachment/s">
|
||||
<span class="list-value"><i class="fa fa-paperclip fa-large"></i></span>
|
||||
</div>
|
||||
<div class="col-sm-1 list-col ellipsis h6 text-muted hidden-xs" title="Time the e-mail was sent">
|
||||
<span class="list-value">Sent</i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
46
frappe/email/page/email_inbox/inbox_list.html
Normal file
46
frappe/email/page/email_inbox/inbox_list.html
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<div class="list-row row-named" data-name="{%= data.name %}" style=" {%= data.seen ? "font-weight:normal" : "font-weight:bold" %}">
|
||||
<div class="row doclist-row has-checkbox">
|
||||
<div class="col-mg-5 col-sm-12 list-row-left">
|
||||
<div class="row">
|
||||
<div class="col-sm-2 list-col ellipsis col-xs-12">
|
||||
<span class="list-value">
|
||||
<input class="list-delete noclick" type="checkbox" style="margin: 0 7px 0 0; vertical-align: middle;">
|
||||
{% if (data.sender_full_name){var sender = data.sender_full_name} else {var sender = data.sender} %}
|
||||
<a class="grey" title="{%= sender %}">{%= sender %}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-4 list-col ellipsis col-xs-12">
|
||||
<span class="list-value">
|
||||
<a class="grey" title="{%= data.name %}">{%= data.subject %}</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2 list-col ellipsis hidden-xs">
|
||||
<span class="list-value">
|
||||
<a class="h6 text-muted grey noclick" data-filter="timeline_doctype,=,{%= data.timeline_doctype %}" title="Linked Doctype: {%= data.reference_doctype %}" href="#Form/{%= data.timeline_doctype %}/{%= data.timeline_name %}">{%= data.timeline_label%}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-2 list-col ellipsis hidden-xs">
|
||||
<span class="list-value">
|
||||
<i class="h6 fa fa-link text-muted relink-link pull-right" style="padding: 0px 0px; margin-right: 10px; font-size: 1.1em;"></i>
|
||||
<a class="text-muted grey noclick reference-document" title="Linked Doctype: {%= data.reference_doctype %}" href="#Form/{%= data.reference_doctype %}/{%= data.reference_name %}">{%= data.reference_name %}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-1 list-col ellipsis hidden-xs">
|
||||
<span class="list-value">
|
||||
<span>
|
||||
{% if(data.has_attachment){ %}
|
||||
<i class="fa fa-paperclip fa-large"></i>
|
||||
{% } %}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-1 list-col ellipsis hidden-xs" >
|
||||
<span class="list-value">
|
||||
{%= comment_when(data.communication_date,true) %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
0
frappe/geo/doctype/address/__init__.py
Normal file
0
frappe/geo/doctype/address/__init__.py
Normal file
25
frappe/geo/doctype/address/address.js
Normal file
25
frappe/geo/doctype/address/address.js
Normal file
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
651
frappe/geo/doctype/address/address.json
Normal file
651
frappe/geo/doctype/address/address.json
Normal file
|
|
@ -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
|
||||
}
|
||||
203
frappe/geo/doctype/address/address.py
Normal file
203
frappe/geo/doctype/address/address.py
Normal file
|
|
@ -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')
|
||||
})
|
||||
18
frappe/geo/doctype/address/test_address.py
Normal file
18
frappe/geo/doctype/address/test_address.py
Normal file
|
|
@ -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"]
|
||||
15
frappe/geo/doctype/address/test_records.json
Normal file
15
frappe/geo/doctype/address/test_records.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
0
frappe/geo/doctype/address_template/__init__.py
Normal file
0
frappe/geo/doctype/address_template/__init__.py
Normal file
16
frappe/geo/doctype/address_template/address_template.js
Normal file
16
frappe/geo/doctype/address_template/address_template.js
Normal file
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
148
frappe/geo/doctype/address_template/address_template.json
Normal file
148
frappe/geo/doctype/address_template/address_template.json
Normal file
|
|
@ -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": "<h4>Default Template</h4>\n<p>Uses <a href=\"http://jinja.pocoo.org/docs/templates/\">Jinja Templating</a> and all the fields of Address (including Custom Fields if any) will be available</p>\n<pre><code>{{ 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</code></pre>",
|
||||
"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
|
||||
}
|
||||
43
frappe/geo/doctype/address_template/address_template.py
Normal file
43
frappe/geo/doctype/address_template/address_template.py
Normal file
|
|
@ -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 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}\
|
||||
{{ city }}<br>
|
||||
{% if state %}{{ state }}<br>{% endif -%}
|
||||
{% if pincode %}{{ pincode }}<br>{% endif -%}
|
||||
{{ country }}<br>
|
||||
{% if phone %}'''+_('Phone')+''': {{ phone }}<br>{% endif -%}
|
||||
{% if fax %}'''+_('Fax')+''': {{ fax }}<br>{% endif -%}
|
||||
{% if email_id %}'''+_('Email')+''': {{ email_id }}<br>{% endif -%}'''
|
||||
24
frappe/geo/doctype/address_template/test_address_template.py
Normal file
24
frappe/geo/doctype/address_template/test_address_template.py
Normal file
|
|
@ -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()
|
||||
13
frappe/geo/doctype/address_template/test_records.json
Normal file
13
frappe/geo/doctype/address_template/test_records.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[
|
||||
{
|
||||
"country": "India",
|
||||
"is_default": 1,
|
||||
"template": "{{ address_title }}<br>\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 / ZIP: {{ 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"
|
||||
},
|
||||
{
|
||||
"country": "Brazil",
|
||||
"is_default": 0,
|
||||
"template": "{{ address_title }}<br>\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 / ZIP: {{ 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"
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = '<p class="text-muted">' + _('Please close this window') + '</p>'
|
||||
|
||||
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 = '<p class="text-muted">' + _('Please close this window') + '</p>'
|
||||
frappe.respond_as_web_page(_("Dropbox Setup"),
|
||||
_("Illegal Access Token. Please try again") + close,
|
||||
indicator_color='red',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
82
frappe/patches/v6_24/imap_get_unique.py
Normal file
82
frappe/patches/v6_24/imap_get_unique.py
Normal file
|
|
@ -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
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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('<i class="fa fa-spinner fa-spin fa-4x"></i>')
|
||||
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 = {};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@
|
|||
{% if(data.communication_type==="Communication"
|
||||
|| (data.communication_type==="Comment"
|
||||
&& data.comment_type==="Comment")) { %}
|
||||
|
||||
<div class="comment-header small">
|
||||
<div class="comment-header small" style="cursor: pointer;">
|
||||
<i class="{%= data.icon %} fa-fw"></i>
|
||||
<i class="expand-icon octicon octicon-chevron-down"></i>
|
||||
<span title="{%= data.comment_by %}">{%= data.fullname %}</span>
|
||||
<span>
|
||||
{% if (data.timeline_doctype===data.frm.doc.doctype
|
||||
|
|
@ -39,6 +39,15 @@
|
|||
</a>
|
||||
{% } %}
|
||||
</span>
|
||||
{% if(data.communication_type==="Communication") { %}
|
||||
<span title="{%=data.subject%}" class="hidden-xs">
|
||||
–
|
||||
{% if (data.subject.length > 40){
|
||||
data.subject = data.subject.substr(0,37)+"...";
|
||||
} %}
|
||||
{%= data.subject %}
|
||||
</span>
|
||||
{% } %}
|
||||
<span class="text-muted" style="font-weight: normal;">
|
||||
– {%= data.comment_on %}</span>
|
||||
{% if(data.communication_type==="Communication") { %}
|
||||
|
|
@ -72,9 +81,14 @@
|
|||
{% if (frappe.model.can_read(\'Communication\')) { %}
|
||||
</a>
|
||||
{% } %}
|
||||
|
||||
{% if (data.communication_medium === "Email") { %}
|
||||
<a class="text-muted relink-link timeline-content-show hide"
|
||||
data-name="{%= data.name %}"> – {%= __("Relink") %}</a>
|
||||
{% } %}
|
||||
|
||||
{% if (data.communication_medium === "Email") { %}
|
||||
<a class="text-muted reply-link pull-right"
|
||||
<a class="text-muted reply-link pull-right timeline-content-show hide"
|
||||
data-name="{%= data.name %}">{%= __("Reply") %}</a>
|
||||
{% } %}
|
||||
{% } %}
|
||||
|
|
@ -90,10 +104,26 @@
|
|||
{{ (data._liked_by || []).length }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="reply" style="overflow-x: auto">
|
||||
<div class="reply timeline-content-show hide" style="overflow-x: auto">
|
||||
<div class="timeline-item-content">
|
||||
{%= data.content_html %}
|
||||
</div>
|
||||
{% if(data.attachments && data.attachments.length) { %}
|
||||
<div style="margin: 10px 0px">
|
||||
{% $.each(data.attachments, function(i, a) { %}
|
||||
<div class="ellipsis">
|
||||
<a href="{%= encodeURI(a.file_url).replace(/#/g, \'%23\') %}"
|
||||
class="text-muted small" target="_blank">
|
||||
<i class="fa fa-paperclip"></i>
|
||||
{%= a.file_url.split("/").slice(-1)[0] %}
|
||||
{% if (a.is_private) { %}
|
||||
<i class="fa fa-lock text-warning"></i>
|
||||
{% } %}
|
||||
</a>
|
||||
</div>
|
||||
{% }); %}
|
||||
</div>
|
||||
{% } %}
|
||||
</div>
|
||||
|
||||
{% } else if(in_list(["Assignment Completed", "Assigned", "Shared",
|
||||
|
|
@ -148,22 +178,6 @@
|
|||
– {%= data.comment_on %}</span>
|
||||
</div>
|
||||
{% } %}
|
||||
{% if(data.attachments && data.attachments.length) { %}
|
||||
<div style="margin: 10px 0px">
|
||||
{% $.each(data.attachments, function(i, a) { %}
|
||||
<div class="ellipsis">
|
||||
<a href="{%= encodeURI(a.file_url).replace(/#/g, \'%23\') %}"
|
||||
class="text-muted small" target="_blank">
|
||||
<i class="fa fa-paperclip"></i>
|
||||
{%= a.file_url.split("/").slice(-1)[0] %}
|
||||
{% if (a.is_private) { %}
|
||||
<i class="fa fa-lock text-warning"></i>
|
||||
{% } %}
|
||||
</a>
|
||||
</div>
|
||||
{% }); %}
|
||||
</div>
|
||||
{% } %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;i<list["message"].length;i++){
|
||||
accounts.push(list["message"][i]["email_id"])
|
||||
}
|
||||
me.from = {label: __("From"), fieldtype: "Select", reqd: 1, fieldname: "sender", options: accounts}
|
||||
|
||||
} else {
|
||||
me.from = false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
prepare: function() {
|
||||
this.setup_subject_and_recipients();
|
||||
this.setup_print_language()
|
||||
|
|
@ -93,6 +123,9 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
this.setup_last_edited_communication();
|
||||
this.setup_standard_reply();
|
||||
$(this.dialog.fields_dict.recipients.input).val(this.recipients || "").change();
|
||||
if(this.dialog.fields_dict.sender) {
|
||||
$(this.dialog.fields_dict.sender.input).val(this.sender || "").change();
|
||||
}
|
||||
$(this.dialog.fields_dict.subject.input).val(this.subject || "").change();
|
||||
this.setup_earlier_reply();
|
||||
},
|
||||
|
|
@ -100,11 +133,11 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
setup_subject_and_recipients: function() {
|
||||
this.subject = this.subject || "";
|
||||
|
||||
if(!this.recipients && this.last_email) {
|
||||
if(!this.forward && !this.recipients && this.last_email) {
|
||||
this.recipients = this.last_email.sender;
|
||||
}
|
||||
|
||||
if(!this.recipients) {
|
||||
if(!this.forward && !this.recipients) {
|
||||
this.recipients = this.frm && this.frm.timeline.get_recipient();
|
||||
}
|
||||
|
||||
|
|
@ -177,49 +210,64 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
|
||||
setup_last_edited_communication: function() {
|
||||
var me = this;
|
||||
this.dialog.onhide = function() {
|
||||
if(cur_frm && cur_frm.docname) {
|
||||
var last_edited_communication = me.get_last_edited_communication();
|
||||
$.extend(last_edited_communication, {
|
||||
recipients: me.dialog.get_value("recipients"),
|
||||
subject: me.dialog.get_value("subject"),
|
||||
content: me.dialog.get_value("content"),
|
||||
});
|
||||
if (!this.doc){
|
||||
if (cur_frm){
|
||||
this.doc = cur_frm.doctype;
|
||||
}else{
|
||||
this.doc = "Inbox";
|
||||
}
|
||||
}
|
||||
if (cur_frm && cur_frm.docname) {
|
||||
this.key = cur_frm.docname;
|
||||
} else {
|
||||
this.key = "Inbox";
|
||||
}
|
||||
if(this.last_email) {
|
||||
this.key = this.key + ":" + this.last_email.name;
|
||||
}
|
||||
if(this.subject){
|
||||
this.key = this.key + ":" + this.subject;
|
||||
}
|
||||
this.dialog.onhide = function() {
|
||||
var last_edited_communication = me.get_last_edited_communication();
|
||||
$.extend(last_edited_communication, {
|
||||
sender: me.dialog.get_value("sender"),
|
||||
recipients: me.dialog.get_value("recipients"),
|
||||
subject: me.dialog.get_value("subject"),
|
||||
content: me.dialog.get_value("content"),
|
||||
});
|
||||
}
|
||||
|
||||
this.dialog.on_page_show = function() {
|
||||
if (cur_frm && cur_frm.docname && !me.txt) {
|
||||
if (!me.txt) {
|
||||
var last_edited_communication = me.get_last_edited_communication();
|
||||
if(last_edited_communication.content) {
|
||||
me.dialog.set_value("sender", last_edited_communication.sender || "");
|
||||
me.dialog.set_value("subject", last_edited_communication.subject || "");
|
||||
me.dialog.set_value("recipients", last_edited_communication.recipients || "");
|
||||
me.dialog.set_value("content", last_edited_communication.content || "");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
get_last_edited_communication: function() {
|
||||
var key = cur_frm.docname;
|
||||
if(this.last_email) {
|
||||
key = key + ":" + this.last_email.name;
|
||||
}
|
||||
if (!frappe.last_edited_communication[cur_frm.doctype]) {
|
||||
frappe.last_edited_communication[cur_frm.doctype] = {};
|
||||
if (!frappe.last_edited_communication[this.doc]) {
|
||||
frappe.last_edited_communication[this.doc] = {};
|
||||
}
|
||||
|
||||
if(!frappe.last_edited_communication[cur_frm.doctype][key]) {
|
||||
frappe.last_edited_communication[cur_frm.doctype][key] = {};
|
||||
if(!frappe.last_edited_communication[this.doc][this.key]) {
|
||||
frappe.last_edited_communication[this.doc][this.key] = {};
|
||||
}
|
||||
|
||||
return frappe.last_edited_communication[cur_frm.doctype][key];
|
||||
return frappe.last_edited_communication[this.doc][this.key];
|
||||
},
|
||||
|
||||
setup_print_language: function() {
|
||||
var me = this;
|
||||
var doc = cur_frm.doc;
|
||||
var doc = this.doc || cur_frm.doc;
|
||||
var fields = this.dialog.fields_dict;
|
||||
|
||||
//Load default print language from doctype
|
||||
|
|
@ -264,7 +312,9 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
var attach = $(fields.select_attachments.wrapper);
|
||||
|
||||
var me = this
|
||||
me.attachments = []
|
||||
if (!me.attachments){
|
||||
me.attachments = []
|
||||
}
|
||||
|
||||
var args = {
|
||||
args: {
|
||||
|
|
@ -340,6 +390,7 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
$(fields.send_email.input).click(function() {
|
||||
$(fields.communication_medium.wrapper).toggle(!!!$(this).prop("checked"));
|
||||
$(fields.sent_or_received.wrapper).toggle(!!!$(this).prop("checked"));
|
||||
$(fields.send_read_receipt.wrapper).toggle($(this).prop("checked"));
|
||||
me.dialog.get_primary_btn().html($(this).prop("checked") ? "Send" : "Add Communication");
|
||||
});
|
||||
|
||||
|
|
@ -436,8 +487,11 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
print_format: print_format,
|
||||
communication_medium: form_values.communication_medium,
|
||||
sent_or_received: form_values.sent_or_received,
|
||||
sender: form_values.sender,
|
||||
sender_full_name: form_values.sender?frappe.user.full_name():undefined,
|
||||
attachments: selected_attachments,
|
||||
_lang : me.lang_code
|
||||
_lang : me.lang_code,
|
||||
read_receipt:form_values.send_read_receipt
|
||||
},
|
||||
btn: btn,
|
||||
callback: function(r) {
|
||||
|
|
@ -451,10 +505,10 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
|
||||
me.dialog.hide();
|
||||
|
||||
if ((frappe.last_edited_communication[me.doc] || {})[me.key]) {
|
||||
delete frappe.last_edited_communication[me.doc][me.key];
|
||||
}
|
||||
if (cur_frm) {
|
||||
if (cur_frm.docname && (frappe.last_edited_communication[cur_frm.doctype] || {})[cur_frm.docname]) {
|
||||
delete frappe.last_edited_communication[cur_frm.doctype][cur_frm.docname];
|
||||
}
|
||||
// clear input
|
||||
cur_frm.timeline.input.val("");
|
||||
cur_frm.reload_doc();
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
if(opts.sort_by_next) this.sort_by_next_select.val(opts.sort_by_next);
|
||||
if(opts.sort_order_next) this.sort_order_next_select.val(opts.sort_order_next);
|
||||
|
||||
this.add_totals_row = opts.add_totals_row;
|
||||
this.add_totals_row = cint(opts.add_totals_row);
|
||||
},
|
||||
|
||||
set_route_filters: function(first_load) {
|
||||
|
|
@ -711,6 +711,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
sort_order: me.sort_order_select.val(),
|
||||
sort_by_next: me.sort_by_next_select.val(),
|
||||
sort_order_next: me.sort_order_next_select.val(),
|
||||
add_totals_row: me.add_totals_row
|
||||
})
|
||||
},
|
||||
callback: function(r) {
|
||||
|
|
|
|||
|
|
@ -214,7 +214,6 @@ frappe.views.TreeView = Class.extend({
|
|||
}
|
||||
|
||||
$.extend(args, v)
|
||||
|
||||
return frappe.call({
|
||||
method: me.opts.add_tree_node || "frappe.desk.treeview.add_node",
|
||||
args: args,
|
||||
|
|
|
|||
|
|
@ -248,7 +248,9 @@ _f.Frm.prototype.set_query = function(fieldname, opt1, opt2) {
|
|||
} else {
|
||||
// on parent table
|
||||
// set_query(fieldname, query)
|
||||
this.fields_dict[fieldname].get_query = opt1;
|
||||
if(this.fields_dict[fieldname]) {
|
||||
this.fields_dict[fieldname].get_query = opt1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
1
frappe/public/js/lib/bootstrap-paginator.min.js
vendored
Normal file
1
frappe/public/js/lib/bootstrap-paginator.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -34,6 +34,7 @@ class TestPassword(unittest.TestCase):
|
|||
if not frappe.db.exists('Email Account', name):
|
||||
return frappe.get_doc({
|
||||
'doctype': 'Email Account',
|
||||
'domain': 'example.com',
|
||||
'email_account_name': name,
|
||||
'append_to': 'Communication',
|
||||
'smtp_server': 'test.example.com',
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ def validate_email_add(email_str, throw=False):
|
|||
|
||||
if not email_str:
|
||||
valid = False
|
||||
if 'undisclosed-recipient' in email_str:
|
||||
return False
|
||||
|
||||
elif " " in email_str and "<" not in email_str:
|
||||
# example: "test@example.com test2@example.com" will return "test@example.comtest2" after parseaddr!!!
|
||||
|
|
@ -111,7 +113,8 @@ def split_emails(txt):
|
|||
email_list = []
|
||||
|
||||
# emails can be separated by comma or newline
|
||||
for email in re.split('''[,\\n](?=(?:[^"]|"[^"]*")*$)''', cstr(txt)):
|
||||
s = re.sub(r'[\t\n\r]', ' ', cstr(txt))
|
||||
for email in re.split('''[,\\n](?=(?:[^"]|"[^"]*")*$)''', s):
|
||||
email = strip(cstr(email))
|
||||
if email:
|
||||
email_list.append(email)
|
||||
|
|
|
|||
|
|
@ -62,8 +62,9 @@ def install_basic_docs():
|
|||
{'doctype': "Workflow Action", "workflow_action_name": "Approve"},
|
||||
{'doctype': "Workflow Action", "workflow_action_name": "Reject"},
|
||||
{'doctype': "Workflow Action", "workflow_action_name": "Review"},
|
||||
{'doctype': "Email Account", "email_id": "notifications@example.com", "default_outgoing": 1},
|
||||
{'doctype': "Email Account", "email_id": "replies@example.com", "default_incoming": 1}
|
||||
{'doctype': "Email Domain", "domain_name":"example.com", "email_id": "account@example.com", "password": "pass", "email_server": "imap.example.com","use_imap": 1, "smtp_server": "smtp.example.com"},
|
||||
{'doctype': "Email Account", "domain":"example.com", "email_id": "notifications@example.com", "default_outgoing": 1},
|
||||
{'doctype': "Email Account", "domain":"example.com", "email_id": "replies@example.com", "default_incoming": 1}
|
||||
]
|
||||
|
||||
for d in install_docs:
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ class HelpArticle(WebsiteGenerator):
|
|||
def on_update(self):
|
||||
self.update_category()
|
||||
clear_cache()
|
||||
super(HelpArticle, self).on_update()
|
||||
|
||||
def update_category(self):
|
||||
cnt = frappe.db.sql("""select count(*) from `tabHelp Article`
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
{%- if not disable_signup -%}
|
||||
<p class="text-center">
|
||||
<a href="#signup" style="margin-top: -2px;">{{ _("Dont have an account? Sign up") }}</a>
|
||||
<a href="#signup" style="margin-top: -2px;">{{ _("Don't have an account? Sign up") }}</a>
|
||||
</p>
|
||||
{%- endif -%}
|
||||
<p class="text-center">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue