Conflicts: frappe/__version__.py frappe/core/doctype/communication/communication.json frappe/hooks.py frappe/model/document.py frappe/public/css/form.css frappe/public/less/form.less requirements.txt setup.pyversion-14
@@ -241,7 +241,7 @@ def msgprint(msg, small=0, raise_exception=0, as_table=False): | |||||
msg = '<table border="1px" style="border-collapse: collapse" cellpadding="2px">' + ''.join(['<tr>'+''.join(['<td>%s</td>' % c for c in r])+'</tr>' for r in msg]) + '</table>' | msg = '<table border="1px" style="border-collapse: collapse" cellpadding="2px">' + ''.join(['<tr>'+''.join(['<td>%s</td>' % c for c in r])+'</tr>' for r in msg]) + '</table>' | ||||
if flags.print_messages: | if flags.print_messages: | ||||
print "Message: " + repr(msg) | |||||
print "Message: " + repr(msg).encode("utf-8") | |||||
message_log.append((small and '__small:' or '')+cstr(msg or '')) | message_log.append((small and '__small:' or '')+cstr(msg or '')) | ||||
_raise_exception() | _raise_exception() | ||||
@@ -430,6 +430,9 @@ def has_website_permission(doctype, ptype="read", doc=None, user=None, verbose=F | |||||
hooks = (get_hooks("has_website_permission") or {}).get(doctype, []) | hooks = (get_hooks("has_website_permission") or {}).get(doctype, []) | ||||
if hooks: | if hooks: | ||||
if isinstance(doc, basestring): | |||||
doc = get_doc(doctype, doc) | |||||
for method in hooks: | for method in hooks: | ||||
result = call(get_attr(method), doc=doc, ptype=ptype, user=user, verbose=verbose) | result = call(get_attr(method), doc=doc, ptype=ptype, user=user, verbose=verbose) | ||||
# if even a single permission check is Falsy | # if even a single permission check is Falsy | ||||
@@ -1,2 +1,2 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
__version__ = "6.0.0-wip" | |||||
__version__ = "6.0.0" |
@@ -23,7 +23,7 @@ class HTTPRequest: | |||||
self.domain = self.domain[4:] | self.domain = self.domain[4:] | ||||
if frappe.get_request_header('X-Forwarded-For'): | if frappe.get_request_header('X-Forwarded-For'): | ||||
frappe.local.request_ip = frappe.get_request_header('X-Forwarded-For') | |||||
frappe.local.request_ip = (frappe.get_request_header('X-Forwarded-For').split(",")[0]).strip() | |||||
elif frappe.get_request_header('REMOTE_ADDR'): | elif frappe.get_request_header('REMOTE_ADDR'): | ||||
frappe.local.request_ip = frappe.get_request_header('REMOTE_ADDR') | frappe.local.request_ip = frappe.get_request_header('REMOTE_ADDR') | ||||
@@ -0,0 +1 @@ | |||||
- Moved Backup Manager and Social Login Keys to the new **Integrations** module |
@@ -72,4 +72,10 @@ def get_data(): | |||||
"type": "module", | "type": "module", | ||||
"system_manager": 1 | "system_manager": 1 | ||||
}, | }, | ||||
"Integrations": { | |||||
"color": "#36414C", | |||||
"icon": "octicon octicon-plug", | |||||
"type": "module", | |||||
"system_manager": 1 | |||||
} | |||||
} | } |
@@ -0,0 +1,23 @@ | |||||
from __future__ import unicode_literals | |||||
from frappe import _ | |||||
def get_data(): | |||||
return [ | |||||
{ | |||||
"label": _("Documents"), | |||||
"icon": "icon-star", | |||||
"items": [ | |||||
{ | |||||
"type": "doctype", | |||||
"name": "Social Login Keys", | |||||
"description": _("Enter keys to enable login via Facebook, Google, GitHub."), | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "Backup Manager", | |||||
"description": _("Manage cloud backups on Dropbox"), | |||||
"hide_count": True | |||||
} | |||||
] | |||||
} | |||||
] |
@@ -208,12 +208,6 @@ def get_data(): | |||||
"description": _("Install Applications."), | "description": _("Install Applications."), | ||||
"icon": "icon-download" | "icon": "icon-download" | ||||
}, | }, | ||||
{ | |||||
"type": "doctype", | |||||
"name": "Backup Manager", | |||||
"description": _("Manage cloud backups on Dropbox"), | |||||
"hide_count": True | |||||
}, | |||||
{ | { | ||||
"type": "doctype", | "type": "doctype", | ||||
"name": "Scheduler Log", | "name": "Scheduler Log", | ||||
@@ -77,11 +77,6 @@ def get_data(): | |||||
"type": "doctype", | "type": "doctype", | ||||
"name": "Website Theme", | "name": "Website Theme", | ||||
"description": _("List of themes for Website."), | "description": _("List of themes for Website."), | ||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "Social Login Keys", | |||||
"description": _("Enter keys to enable login via Facebook, Google, GitHub."), | |||||
} | } | ||||
] | ] | ||||
}, | }, | ||||
@@ -1,251 +1,267 @@ | |||||
{ | { | ||||
"allow_import": 1, | |||||
"autoname": "naming_series:", | |||||
"creation": "2013-01-29 10:47:14", | |||||
"description": "Keep a track of all communications", | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "Setup", | |||||
"allow_import": 1, | |||||
"autoname": "naming_series:", | |||||
"creation": "2013-01-29 10:47:14", | |||||
"description": "Keep a track of all communications", | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "Setup", | |||||
"fields": [ | "fields": [ | ||||
{ | { | ||||
"default": "COMM-", | |||||
"fieldname": "naming_series", | |||||
"fieldtype": "Select", | |||||
"hidden": 1, | |||||
"label": "Series", | |||||
"options": "COMM-", | |||||
"default": "COMM-", | |||||
"fieldname": "naming_series", | |||||
"fieldtype": "Select", | |||||
"hidden": 1, | |||||
"label": "Series", | |||||
"options": "COMM-", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
{ | |||||
"fieldname": "sent_or_received", | |||||
"fieldtype": "Select", | |||||
"in_list_view": 1, | |||||
"label": "Sent or Received", | |||||
"options": "Sent\nReceived", | |||||
"permlevel": 0, | |||||
}, | |||||
{ | |||||
"fieldname": "sent_or_received", | |||||
"fieldtype": "Select", | |||||
"in_list_view": 1, | |||||
"label": "Sent or Received", | |||||
"options": "Sent\nReceived", | |||||
"permlevel": 0, | |||||
"reqd": 1 | "reqd": 1 | ||||
}, | |||||
}, | |||||
{ | |||||
"fieldname": "status", | |||||
"fieldtype": "Select", | |||||
"label": "Status", | |||||
"options": "Open\nReplied\nArchived", | |||||
"permlevel": 0, | |||||
"precision": "" | |||||
}, | |||||
{ | { | ||||
"fieldname": "status", | |||||
"fieldtype": "Select", | |||||
"label": "Status", | |||||
"options": "Open\nReplied\nArchived", | |||||
"permlevel": 0, | |||||
"description": "Integrations can use this field to set email delivery status", | |||||
"fieldname": "delivery_status", | |||||
"fieldtype": "Select", | |||||
"hidden": 1, | |||||
"label": "Delivery Status", | |||||
"options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed", | |||||
"permlevel": 0, | |||||
"precision": "" | "precision": "" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "subject", | |||||
"fieldtype": "Data", | |||||
"in_list_view": 0, | |||||
"label": "Subject", | |||||
"permlevel": 0, | |||||
"fieldname": "subject", | |||||
"fieldtype": "Data", | |||||
"in_list_view": 0, | |||||
"label": "Subject", | |||||
"permlevel": 0, | |||||
"reqd": 1 | "reqd": 1 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "column_break_5", | |||||
"fieldtype": "Column Break", | |||||
"permlevel": 0, | |||||
"fieldname": "column_break_5", | |||||
"fieldtype": "Column Break", | |||||
"permlevel": 0, | |||||
"precision": "" | "precision": "" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "reference_doctype", | |||||
"fieldtype": "Link", | |||||
"label": "Reference DocType", | |||||
"options": "DocType", | |||||
"permlevel": 0, | |||||
"fieldname": "reference_doctype", | |||||
"fieldtype": "Link", | |||||
"label": "Reference DocType", | |||||
"options": "DocType", | |||||
"permlevel": 0, | |||||
"precision": "" | "precision": "" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "reference_name", | |||||
"fieldtype": "Dynamic Link", | |||||
"label": "Reference Name", | |||||
"options": "reference_doctype", | |||||
"permlevel": 0, | |||||
"fieldname": "reference_name", | |||||
"fieldtype": "Dynamic Link", | |||||
"label": "Reference Name", | |||||
"options": "reference_doctype", | |||||
"permlevel": 0, | |||||
"precision": "" | "precision": "" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "section_break_8", | |||||
"fieldtype": "Section Break", | |||||
"permlevel": 0, | |||||
"fieldname": "section_break_8", | |||||
"fieldtype": "Section Break", | |||||
"permlevel": 0, | |||||
"precision": "" | "precision": "" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "content", | |||||
"fieldtype": "Text Editor", | |||||
"label": "Content", | |||||
"permlevel": 0, | |||||
"reqd": 0, | |||||
"fieldname": "content", | |||||
"fieldtype": "Text Editor", | |||||
"label": "Content", | |||||
"permlevel": 0, | |||||
"reqd": 0, | |||||
"width": "400" | "width": "400" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "additional_info", | |||||
"fieldtype": "Section Break", | |||||
"label": "Additional Info", | |||||
"fieldname": "additional_info", | |||||
"fieldtype": "Section Break", | |||||
"label": "Additional Info", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "recipients", | |||||
"fieldtype": "Data", | |||||
"label": "Recipients", | |||||
"fieldname": "recipients", | |||||
"fieldtype": "Data", | |||||
"label": "Recipients", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "sender", | |||||
"fieldtype": "Data", | |||||
"label": "Sender", | |||||
"fieldname": "phone_no", | |||||
"fieldtype": "Data", | |||||
"label": "Phone No.", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "sender_full_name", | |||||
"fieldtype": "Data", | |||||
"label": "Sender Full Name", | |||||
"permlevel": 0, | |||||
"fieldname": "communication_medium", | |||||
"fieldtype": "Select", | |||||
"in_list_view": 1, | |||||
"label": "Communication Medium", | |||||
"options": "\nChat\nPhone\nEmail\nSMS\nVisit\nOther", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "column_break_14", | |||||
"fieldtype": "Column Break", | |||||
"permlevel": 0, | |||||
"precision": "" | "precision": "" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "communication_medium", | |||||
"fieldtype": "Select", | |||||
"in_list_view": 1, | |||||
"label": "Communication Medium", | |||||
"options": "\nChat\nPhone\nEmail\nSMS\nVisit\nOther", | |||||
"fieldname": "sender", | |||||
"fieldtype": "Data", | |||||
"label": "Sender", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "phone_no", | |||||
"fieldtype": "Data", | |||||
"label": "Phone No.", | |||||
"permlevel": 0 | |||||
}, | |||||
"fieldname": "sender_full_name", | |||||
"fieldtype": "Data", | |||||
"label": "Sender Full Name", | |||||
"permlevel": 0, | |||||
"precision": "" | |||||
}, | |||||
{ | { | ||||
"fieldname": "section_break2", | |||||
"fieldtype": "Section Break", | |||||
"options": "simple", | |||||
"fieldname": "section_break2", | |||||
"fieldtype": "Section Break", | |||||
"options": "simple", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "column_break4", | |||||
"fieldtype": "Column Break", | |||||
"label": "By", | |||||
"fieldname": "column_break4", | |||||
"fieldtype": "Column Break", | |||||
"label": "By", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "email_account", | |||||
"fieldtype": "Link", | |||||
"label": "Email Account", | |||||
"options": "Email Account", | |||||
"permlevel": 0, | |||||
"fieldname": "email_account", | |||||
"fieldtype": "Link", | |||||
"label": "Email Account", | |||||
"options": "Email Account", | |||||
"permlevel": 0, | |||||
"precision": "" | "precision": "" | ||||
}, | |||||
{ | |||||
"default": "__user", | |||||
"fieldname": "user", | |||||
"fieldtype": "Link", | |||||
"ignore_user_permissions": 1, | |||||
"label": "User", | |||||
"options": "User", | |||||
"permlevel": 0, | |||||
}, | |||||
{ | |||||
"default": "__user", | |||||
"fieldname": "user", | |||||
"fieldtype": "Link", | |||||
"ignore_user_permissions": 1, | |||||
"label": "User", | |||||
"options": "User", | |||||
"permlevel": 0, | |||||
"read_only": 1 | "read_only": 1 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "column_break5", | |||||
"fieldtype": "Column Break", | |||||
"label": "On", | |||||
"fieldname": "column_break5", | |||||
"fieldtype": "Column Break", | |||||
"label": "On", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"default": "Today", | |||||
"fieldname": "communication_date", | |||||
"fieldtype": "Datetime", | |||||
"label": "Date", | |||||
"default": "Today", | |||||
"fieldname": "communication_date", | |||||
"fieldtype": "Datetime", | |||||
"label": "Date", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
{ | |||||
"fieldname": "_user_tags", | |||||
"fieldtype": "Data", | |||||
"hidden": 1, | |||||
"label": "User Tags", | |||||
"no_copy": 1, | |||||
"permlevel": 0, | |||||
}, | |||||
{ | |||||
"fieldname": "_user_tags", | |||||
"fieldtype": "Data", | |||||
"hidden": 1, | |||||
"label": "User Tags", | |||||
"no_copy": 1, | |||||
"permlevel": 0, | |||||
"print_hide": 1 | "print_hide": 1 | ||||
}, | |||||
{ | |||||
"default": "0", | |||||
"fieldname": "unread_notification_sent", | |||||
"fieldtype": "Check", | |||||
"label": "Unread Notification Sent", | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
}, | |||||
{ | |||||
"default": "0", | |||||
"fieldname": "unread_notification_sent", | |||||
"fieldtype": "Check", | |||||
"label": "Unread Notification Sent", | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"read_only": 1 | "read_only": 1 | ||||
} | } | ||||
], | |||||
"icon": "icon-comment", | |||||
"idx": 1, | |||||
"in_dialog": 0, | |||||
"issingle": 0, | |||||
"modified": "2015-07-28 16:18:11.664740", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "Communication", | |||||
"owner": "Administrator", | |||||
], | |||||
"icon": "icon-comment", | |||||
"idx": 1, | |||||
"in_dialog": 0, | |||||
"issingle": 0, | |||||
"modified": "2015-07-28 17:18:11.664740", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "Communication", | |||||
"owner": "Administrator", | |||||
"permissions": [ | "permissions": [ | ||||
{ | { | ||||
"amend": 0, | |||||
"apply_user_permissions": 1, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Support Team", | |||||
"share": 1, | |||||
"submit": 0, | |||||
"amend": 0, | |||||
"apply_user_permissions": 1, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Support Team", | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 1 | "write": 1 | ||||
}, | |||||
{ | |||||
"amend": 0, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Sales Manager", | |||||
"share": 1, | |||||
"submit": 0, | |||||
}, | |||||
{ | |||||
"amend": 0, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Sales Manager", | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 1 | "write": 1 | ||||
}, | |||||
{ | |||||
"amend": 0, | |||||
"apply_user_permissions": 1, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Sales User", | |||||
"share": 1, | |||||
"submit": 0, | |||||
}, | |||||
{ | |||||
"amend": 0, | |||||
"apply_user_permissions": 1, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Sales User", | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 1 | "write": 1 | ||||
}, | |||||
{ | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "System Manager", | |||||
"share": 1, | |||||
"submit": 0, | |||||
}, | |||||
{ | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "System Manager", | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 1 | "write": 1 | ||||
} | } | ||||
], | |||||
"search_fields": "subject", | |||||
], | |||||
"search_fields": "subject", | |||||
"title_field": "subject" | "title_field": "subject" | ||||
} | |||||
} |
@@ -135,6 +135,9 @@ class CustomizeForm(Document): | |||||
and cint(df.get("precision")) > cint(meta_df[0].get("precision")): | and cint(df.get("precision")) > cint(meta_df[0].get("precision")): | ||||
update_db = True | update_db = True | ||||
elif property == "unique": | |||||
update_db = True | |||||
self.make_property_setter(property=property, value=df.get(property), | self.make_property_setter(property=property, value=df.get(property), | ||||
property_type=self.docfield_properties[property], fieldname=df.fieldname) | property_type=self.docfield_properties[property], fieldname=df.fieldname) | ||||
@@ -113,7 +113,7 @@ def get_comments(dt, dn, limit=100): | |||||
communications = frappe.db.sql("""select name, | communications = frappe.db.sql("""select name, | ||||
content as comment, sender as comment_by, creation, | content as comment, sender as comment_by, creation, | ||||
communication_medium as comment_type, subject, | |||||
communication_medium as comment_type, subject, delivery_status, | |||||
"Communication" as doctype | "Communication" as doctype | ||||
from tabCommunication | from tabCommunication | ||||
where reference_doctype=%s and reference_name=%s | where reference_doctype=%s and reference_name=%s | ||||
@@ -95,8 +95,7 @@ def clear_notifications(user="*"): | |||||
frappe.cache().delete_keys("notification_count:") | frappe.cache().delete_keys("notification_count:") | ||||
else: | else: | ||||
# delete count for user | # delete count for user | ||||
for key in frappe.cache().get_keys("notification_count:"): | |||||
frappe.cache().hdel(key, user) | |||||
frappe.cache().hdel_keys("notification_count:", user) | |||||
def delete_notification_count_for(doctype): | def delete_notification_count_for(doctype): | ||||
frappe.cache().delete_key("notification_count:" + doctype) | frappe.cache().delete_key("notification_count:" + doctype) | ||||
@@ -60,6 +60,9 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc | |||||
if reference_doctype and reference_name: | if reference_doctype and reference_name: | ||||
unsubscribed = [d.email for d in frappe.db.get_all("Email Unsubscribe", "email", | unsubscribed = [d.email for d in frappe.db.get_all("Email Unsubscribe", "email", | ||||
{"reference_doctype": reference_doctype, "reference_name": reference_name})] | {"reference_doctype": reference_doctype, "reference_name": reference_name})] | ||||
unsubscribed += [d.email for d in frappe.db.get_all("Email Unsubscribe", "email", | |||||
{"global_unsubscribe": 1})] | |||||
else: | else: | ||||
unsubscribed = [] | unsubscribed = [] | ||||
@@ -159,14 +162,19 @@ def unsubscribe(doctype, name, email): | |||||
if not verify_request(): | if not verify_request(): | ||||
return | return | ||||
frappe.get_doc({ | |||||
"doctype": "Email Unsubscribe", | |||||
"email": email, | |||||
"reference_doctype": doctype, | |||||
"reference_name": name | |||||
}).insert(ignore_permissions=True) | |||||
try: | |||||
frappe.get_doc({ | |||||
"doctype": "Email Unsubscribe", | |||||
"email": email, | |||||
"reference_doctype": doctype, | |||||
"reference_name": name | |||||
}).insert(ignore_permissions=True) | |||||
except frappe.DuplicateEntryError: | |||||
frappe.db.rollback() | |||||
frappe.db.commit() | |||||
else: | |||||
frappe.db.commit() | |||||
return_unsubscribed_page(email, doctype, name) | return_unsubscribed_page(email, doctype, name) | ||||
@@ -25,7 +25,8 @@ | |||||
"report_hide": 0, | "report_hide": 0, | ||||
"reqd": 1, | "reqd": 1, | ||||
"search_index": 0, | "search_index": 0, | ||||
"set_only_once": 0 | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | }, | ||||
{ | { | ||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
@@ -43,9 +44,10 @@ | |||||
"print_hide": 0, | "print_hide": 0, | ||||
"read_only": 0, | "read_only": 0, | ||||
"report_hide": 0, | "report_hide": 0, | ||||
"reqd": 1, | |||||
"reqd": 0, | |||||
"search_index": 0, | "search_index": 0, | ||||
"set_only_once": 0 | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | }, | ||||
{ | { | ||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
@@ -63,9 +65,30 @@ | |||||
"print_hide": 0, | "print_hide": 0, | ||||
"read_only": 0, | "read_only": 0, | ||||
"report_hide": 0, | "report_hide": 0, | ||||
"reqd": 1, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "global_unsubscribe", | |||||
"fieldtype": "Check", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 1, | |||||
"label": "Global Unsubscribe", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | "search_index": 0, | ||||
"set_only_once": 0 | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
} | } | ||||
], | ], | ||||
"hide_heading": 0, | "hide_heading": 0, | ||||
@@ -75,7 +98,7 @@ | |||||
"is_submittable": 0, | "is_submittable": 0, | ||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"modified": "2015-03-18 09:41:20.216319", | |||||
"modified": "2015-08-05 06:02:12.805282", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Email", | "module": "Email", | ||||
"name": "Email Unsubscribe", | "name": "Email Unsubscribe", | ||||
@@ -90,6 +113,7 @@ | |||||
"delete": 1, | "delete": 1, | ||||
"email": 1, | "email": 1, | ||||
"export": 1, | "export": 1, | ||||
"if_owner": 0, | |||||
"import": 0, | "import": 0, | ||||
"permlevel": 0, | "permlevel": 0, | ||||
"print": 1, | "print": 1, | ||||
@@ -8,6 +8,32 @@ from frappe.model.document import Document | |||||
from frappe import _ | from frappe import _ | ||||
class EmailUnsubscribe(Document): | class EmailUnsubscribe(Document): | ||||
def validate(self): | |||||
if not self.global_unsubscribe and not (self.reference_doctype and self.reference_name): | |||||
frappe.throw(_("Reference DocType and Reference Name are required"), frappe.MandatoryError) | |||||
if not self.global_unsubscribe and frappe.db.get_value(self.doctype, self.name, "global_unsubscribe"): | |||||
frappe.throw(_("Delete this record to allow sending to this email address")) | |||||
if self.global_unsubscribe: | |||||
if frappe.get_all("Email Unsubscribe", | |||||
filters={"email": self.email, "global_unsubscribe": 1, "name": ["!=", self.name]}): | |||||
frappe.throw(_("{0} already unsubscribed").format(self.email), frappe.DuplicateEntryError) | |||||
else: | |||||
if frappe.get_all("Email Unsubscribe", | |||||
filters={ | |||||
"email": self.email, | |||||
"reference_doctype": self.reference_doctype, | |||||
"reference_name": self.reference_name, | |||||
"name": ["!=", self.name] | |||||
}): | |||||
frappe.throw(_("{0} already unsubscribed for {1} {2}").format( | |||||
self.email, self.reference_doctype, self.reference_name), | |||||
frappe.DuplicateEntryError) | |||||
def on_update(self): | def on_update(self): | ||||
doc = frappe.get_doc(self.reference_doctype, self.reference_name) | |||||
doc.add_comment("Label", _("Left this conversation"), comment_by=self.email) | |||||
if self.reference_doctype and self.reference_name: | |||||
doc = frappe.get_doc(self.reference_doctype, self.reference_name) | |||||
doc.add_comment("Label", _("Left this conversation"), comment_by=self.email) | |||||
@@ -192,7 +192,7 @@ class EMail: | |||||
"Date": email.utils.formatdate(), | "Date": email.utils.formatdate(), | ||||
"Reply-To": self.reply_to.encode("utf-8") if self.reply_to else None, | "Reply-To": self.reply_to.encode("utf-8") if self.reply_to else None, | ||||
"CC": ', '.join(self.cc).encode("utf-8") if self.cc else None, | "CC": ', '.join(self.cc).encode("utf-8") if self.cc else None, | ||||
b'X-Frappe-Site': get_url().encode('utf-8') | |||||
b'X-Frappe-Site': get_url().encode('utf-8'), | |||||
} | } | ||||
# reset headers as values may be changed. | # reset headers as values may be changed. | ||||
@@ -201,6 +201,10 @@ class EMail: | |||||
del self.msg_root[key] | del self.msg_root[key] | ||||
self.msg_root[key] = val | self.msg_root[key] = val | ||||
# call hook to enable apps to modify msg_root before sending | |||||
for hook in frappe.get_hooks("make_email_body_message"): | |||||
frappe.get_attr(hook)(self) | |||||
def as_string(self): | def as_string(self): | ||||
"""validate, build message and convert to string""" | """validate, build message and convert to string""" | ||||
self.validate() | self.validate() | ||||
@@ -90,11 +90,11 @@ def execute_cmd(cmd, async=False): | |||||
if frappe.session['user'] == 'Guest': | if frappe.session['user'] == 'Guest': | ||||
if (method not in frappe.guest_methods): | if (method not in frappe.guest_methods): | ||||
frappe.msgprint(_("Not permitted")) | frappe.msgprint(_("Not permitted")) | ||||
raise frappe.PermissionError('Not Allowed, %s' % str(method)) | |||||
raise frappe.PermissionError('Not Allowed, {0}'.format(method)) | |||||
else: | else: | ||||
if not method in frappe.whitelisted: | if not method in frappe.whitelisted: | ||||
frappe.msgprint(_("Not permitted")) | frappe.msgprint(_("Not permitted")) | ||||
raise frappe.PermissionError('Not Allowed, %s' % str(method)) | |||||
raise frappe.PermissionError('Not Allowed, {0}'.format(method)) | |||||
ret = frappe.call(method, **frappe.form_dict) | ret = frappe.call(method, **frappe.form_dict) | ||||
@@ -26,7 +26,7 @@ to ERPNext. | |||||
""" | """ | ||||
app_icon = "octicon octicon-circuit-board" | app_icon = "octicon octicon-circuit-board" | ||||
app_version = "6.0.0-wip" | |||||
app_version = "6.0.0" | |||||
app_color = "orange" | app_color = "orange" | ||||
github_link = "https://github.com/frappe/frappe" | github_link = "https://github.com/frappe/frappe" | ||||
@@ -0,0 +1,221 @@ | |||||
{ | |||||
"allow_copy": 0, | |||||
"allow_import": 0, | |||||
"allow_rename": 0, | |||||
"creation": "2014-03-04 08:29:52", | |||||
"custom": 0, | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "System", | |||||
"fields": [ | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "facebook", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Facebook", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "facebook_client_id", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Facebook Client ID", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "facebook_client_secret", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Facebook Client Secret", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "google", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Google", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "google_client_id", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Google Client ID", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "google_client_secret", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Google Client Secret", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "github", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "GitHub", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "github_client_id", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "GitHub Client ID", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"fieldname": "github_client_secret", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "GitHub Client Secret", | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"print_hide": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
} | |||||
], | |||||
"hide_heading": 0, | |||||
"hide_toolbar": 0, | |||||
"icon": "icon-signin", | |||||
"idx": 1, | |||||
"in_create": 0, | |||||
"in_dialog": 0, | |||||
"is_submittable": 0, | |||||
"issingle": 1, | |||||
"istable": 0, | |||||
"modified": "2015-08-05 08:14:52.667728", | |||||
"modified_by": "Administrator", | |||||
"module": "Integrations", | |||||
"name": "Social Login Keys", | |||||
"owner": "Administrator", | |||||
"permissions": [ | |||||
{ | |||||
"amend": 0, | |||||
"apply_user_permissions": 0, | |||||
"cancel": 0, | |||||
"create": 1, | |||||
"delete": 0, | |||||
"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": 1, | |||||
"submit": 0, | |||||
"write": 1 | |||||
} | |||||
], | |||||
"read_only": 0, | |||||
"read_only_onload": 0 | |||||
} |
@@ -140,6 +140,7 @@ class DbTable: | |||||
""" | """ | ||||
fl = frappe.db.sql("SELECT * FROM tabDocField WHERE parent = %s", self.doctype, as_dict = 1) | fl = frappe.db.sql("SELECT * FROM tabDocField WHERE parent = %s", self.doctype, as_dict = 1) | ||||
precisions = {} | precisions = {} | ||||
uniques = {} | |||||
if not frappe.flags.in_install: | if not frappe.flags.in_install: | ||||
custom_fl = frappe.db.sql("""\ | custom_fl = frappe.db.sql("""\ | ||||
@@ -152,10 +153,15 @@ class DbTable: | |||||
filters={"doc_type": self.doctype, "doctype_or_field": "DocField", "property": "precision"}): | filters={"doc_type": self.doctype, "doctype_or_field": "DocField", "property": "precision"}): | ||||
precisions[ps.field_name] = ps.value | precisions[ps.field_name] = ps.value | ||||
# apply unique from property setters | |||||
for ps in frappe.get_all("Property Setter", fields=["field_name", "value"], | |||||
filters={"doc_type": self.doctype, "doctype_or_field": "DocField", "property": "unique"}): | |||||
uniques[ps.field_name] = cint(ps.value) | |||||
for f in fl: | for f in fl: | ||||
self.columns[f['fieldname']] = DbColumn(self, f['fieldname'], | self.columns[f['fieldname']] = DbColumn(self, f['fieldname'], | ||||
f['fieldtype'], f.get('length'), f.get('default'), f.get('search_index'), | f['fieldtype'], f.get('length'), f.get('default'), f.get('search_index'), | ||||
f.get('options'), f.get('unique'), precisions.get(f['fieldname']) or f.get('precision')) | |||||
f.get('options'), uniques.get(f["fieldname"], f.get('unique')), precisions.get(f['fieldname']) or f.get('precision')) | |||||
def get_columns_from_db(self): | def get_columns_from_db(self): | ||||
self.show_columns = frappe.db.sql("desc `%s`" % self.name) | self.show_columns = frappe.db.sql("desc `%s`" % self.name) | ||||
@@ -294,18 +300,11 @@ class DbColumn: | |||||
return | return | ||||
# type | # type | ||||
if (current_def['type'] != column_def) or (self.unique and not current_def['unique'] \ | |||||
and column_def not in ('text', 'longtext')): | |||||
if (current_def['type'] != column_def) or \ | |||||
((self.unique and not current_def['unique']) and column_def not in ('text', 'longtext')): | |||||
self.table.change_type.append(self) | self.table.change_type.append(self) | ||||
else: | else: | ||||
# index | |||||
if current_def['index'] and not self.set_index and not self.unique: | |||||
self.table.drop_index.append(self) | |||||
if (not current_def['index'] and self.set_index) and not (column_def in ('text', 'longtext')): | |||||
self.table.add_index.append(self) | |||||
# default | # default | ||||
if (self.default_changed(current_def) \ | if (self.default_changed(current_def) \ | ||||
and (self.default not in default_shortcuts) \ | and (self.default not in default_shortcuts) \ | ||||
@@ -313,6 +312,15 @@ class DbColumn: | |||||
and not (column_def in ['text','longtext'])): | and not (column_def in ['text','longtext'])): | ||||
self.table.set_default.append(self) | self.table.set_default.append(self) | ||||
# index should be applied or dropped irrespective of type change | |||||
if ( (current_def['index'] and not self.set_index and not self.unique) | |||||
or (current_def['unique'] and not self.unique) ): | |||||
# to drop unique you have to drop index | |||||
self.table.drop_index.append(self) | |||||
elif (not current_def['index'] and self.set_index) and not (column_def in ('text', 'longtext')): | |||||
self.table.add_index.append(self) | |||||
def default_changed(self, current_def): | def default_changed(self, current_def): | ||||
if "decimal" in current_def['type']: | if "decimal" in current_def['type']: | ||||
return self.default_changed_for_decimal(current_def) | return self.default_changed_for_decimal(current_def) | ||||
@@ -150,27 +150,41 @@ def check_if_doc_is_linked(doc, method="Delete"): | |||||
if item and item.parent != doc.name and ((method=="Delete" and item.docstatus<2) or | if item and item.parent != doc.name and ((method=="Delete" and item.docstatus<2) or | ||||
(method=="Cancel" and item.docstatus==1)): | (method=="Cancel" and item.docstatus==1)): | ||||
# raise exception only if | |||||
# linked to an non-cancelled doc when deleting | |||||
# or linked to a submitted doc when cancelling | |||||
frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype, | frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype, | ||||
doc.name, item.parenttype if item.parent else link_dt, item.parent or item.name), | doc.name, item.parenttype if item.parent else link_dt, item.parent or item.name), | ||||
frappe.LinkExistsError) | frappe.LinkExistsError) | ||||
def check_if_doc_is_dynamically_linked(doc): | |||||
def check_if_doc_is_dynamically_linked(doc, method="Delete"): | |||||
for query in dynamic_link_queries: | for query in dynamic_link_queries: | ||||
for df in frappe.db.sql(query, as_dict=True): | for df in frappe.db.sql(query, as_dict=True): | ||||
if frappe.get_meta(df.parent).issingle: | if frappe.get_meta(df.parent).issingle: | ||||
# dynamic link in single doc | # dynamic link in single doc | ||||
refdoc = frappe.db.get_singles_dict(df.parent) | refdoc = frappe.db.get_singles_dict(df.parent) | ||||
if refdoc.get(df.options)==doc.doctype and refdoc.get(df.fieldname)==doc.name: | |||||
if (refdoc.get(df.options)==doc.doctype | |||||
and refdoc.get(df.fieldname)==doc.name | |||||
and ((method=="Delete" and refdoc.docstatus < 2) | |||||
or (method=="Cancel" and refdoc.docstatus==1)) | |||||
): | |||||
# raise exception only if | |||||
# linked to an non-cancelled doc when deleting | |||||
# or linked to a submitted doc when cancelling | |||||
frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype, | frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype, | ||||
doc.name, df.parent, ""), frappe.LinkExistsError) | doc.name, df.parent, ""), frappe.LinkExistsError) | ||||
else: | else: | ||||
# dynamic link in table | # dynamic link in table | ||||
for name in frappe.db.sql_list("""select name from `tab{parent}` where | |||||
{options}=%s and {fieldname}=%s""".format(**df), (doc.doctype, doc.name)): | |||||
frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype, | |||||
doc.name, df.parent, name), frappe.LinkExistsError) | |||||
for refdoc in frappe.db.sql("""select name, docstatus from `tab{parent}` where | |||||
{options}=%s and {fieldname}=%s""".format(**df), (doc.doctype, doc.name), as_dict=True): | |||||
if ((method=="Delete" and refdoc.docstatus < 2) or (method=="Cancel" and refdoc.docstatus==1)): | |||||
# raise exception only if | |||||
# linked to an non-cancelled doc when deleting | |||||
# or linked to a submitted doc when cancelling | |||||
frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}")\ | |||||
.format(doc.doctype, doc.name, df.parent, refdoc.name), frappe.LinkExistsError) | |||||
def delete_linked_todos(doc): | def delete_linked_todos(doc): | ||||
delete_doc("ToDo", frappe.db.sql_list("""select name from `tabToDo` | delete_doc("ToDo", frappe.db.sql_list("""select name from `tabToDo` | ||||
@@ -457,7 +457,7 @@ class Document(BaseDocument): | |||||
msgprint(msg) | msgprint(msg) | ||||
if frappe.flags.print_messages: | if frappe.flags.print_messages: | ||||
print self.as_dict() | |||||
print self.as_json().encode("utf-8") | |||||
raise frappe.MandatoryError(", ".join((each[0] for each in missing))) | raise frappe.MandatoryError(", ".join((each[0] for each in missing))) | ||||
@@ -581,7 +581,7 @@ class Document(BaseDocument): | |||||
from frappe.model.delete_doc import check_if_doc_is_linked, check_if_doc_is_dynamically_linked | from frappe.model.delete_doc import check_if_doc_is_linked, check_if_doc_is_dynamically_linked | ||||
if not self.flags.ignore_links: | if not self.flags.ignore_links: | ||||
check_if_doc_is_linked(self, method="Cancel") | check_if_doc_is_linked(self, method="Cancel") | ||||
check_if_doc_is_dynamically_linked(self) | |||||
check_if_doc_is_dynamically_linked(self, method="Cancel") | |||||
@staticmethod | @staticmethod | ||||
def whitelist(f): | def whitelist(f): | ||||
@@ -6,3 +6,4 @@ Custom | |||||
Geo | Geo | ||||
Desk | Desk | ||||
Integrations |
@@ -161,3 +161,15 @@ select.form-control { | |||||
font-weight: bold; | font-weight: bold; | ||||
background-color: #fffce7; | background-color: #fffce7; | ||||
} | } | ||||
.form-headline .alert { | |||||
font-size: 12px; | |||||
border-color: #d1d8dd; | |||||
margin-bottom: 0px; | |||||
} | |||||
.delivery-status-indicator { | |||||
display: inline-block; | |||||
margin-top: -3px; | |||||
margin-left: 1px; | |||||
font-weight: 500; | |||||
color: #8d99a6; | |||||
} |
@@ -119,6 +119,16 @@ frappe.ui.form.ControlHTML = frappe.ui.form.Control.extend({ | |||||
}, | }, | ||||
html: function(html) { | html: function(html) { | ||||
this.$wrapper.html(html || this.get_content()); | this.$wrapper.html(html || this.get_content()); | ||||
}, | |||||
set_value: function(html) { | |||||
if(html.appendTo) { | |||||
// jquery object | |||||
html.appendTo(this.$wrapper.empty()); | |||||
} else { | |||||
// html | |||||
this.df.options = html; | |||||
this.html(html); | |||||
} | |||||
} | } | ||||
}); | }); | ||||
@@ -24,7 +24,7 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
}, | }, | ||||
set_headline_alert: function(text, alert_class, icon) { | set_headline_alert: function(text, alert_class, icon) { | ||||
this.set_headline(repl('<div class="alert %(alert_class)s">%(icon)s%(text)s</div>', { | this.set_headline(repl('<div class="alert %(alert_class)s">%(icon)s%(text)s</div>', { | ||||
"alert_class": alert_class || "alert-info", | |||||
"alert_class": alert_class || "", | |||||
"icon": icon ? '<i class="'+icon+'" /> ' : "", | "icon": icon ? '<i class="'+icon+'" /> ' : "", | ||||
"text": text | "text": text | ||||
})); | })); | ||||
@@ -10,15 +10,28 @@ | |||||
</span> | </span> | ||||
</div> | </div> | ||||
{% if(data.doctype=="Communication" || data.comment_type=="Comment") { %} | {% if(data.doctype=="Communication" || data.comment_type=="Comment") { %} | ||||
<h6> | |||||
<h6> | |||||
<i class="{%= data.icon %} icon-fixed-width"></i> | <i class="{%= data.icon %} icon-fixed-width"></i> | ||||
<span title="{%= data.comment_by %}">{%= data.fullname %}</span> | <span title="{%= data.comment_by %}">{%= data.fullname %}</span> | ||||
<span class="text-muted" style="font-weight: normal;"> | <span class="text-muted" style="font-weight: normal;"> | ||||
– {%= data.comment_on %}</span> | – {%= data.comment_on %}</span> | ||||
{% if(data.doctype=="Communication") { %} | {% if(data.doctype=="Communication") { %} | ||||
<span class="text-muted">–</span> | |||||
<a href="#Form/{%= data.doctype %}/{%= data.name %}" | |||||
class="text-muted">{%= __("Details") %}</a> | |||||
<span class="text-muted">–</span> | |||||
<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="indicator-right {%= indicator_class %} delivery-status-indicator" | |||||
title="{%= data.delivery_status %}"> | |||||
{%= data.delivery_status %}</span> | |||||
{% } else { %} {%= __("Details") %} {% } %} | |||||
</a> | |||||
<a class="text-muted reply-link pull-right" | <a class="text-muted reply-link pull-right" | ||||
data-name="{%= data.name %}">{%= __("Reply") %}</a> | data-name="{%= data.name %}">{%= __("Reply") %}</a> | ||||
{% } %} | {% } %} | ||||
@@ -53,6 +53,14 @@ frappe.ui.form.Grid = Class.extend({ | |||||
var me = this, | var me = this, | ||||
$rows = $(me.parent).find(".rows"), | $rows = $(me.parent).find(".rows"), | ||||
data = this.get_data(); | data = this.get_data(); | ||||
if (this.frm && this.frm.docname) { | |||||
// use doc specific docfield object | |||||
this.df = frappe.meta.get_docfield(this.frm.doctype, this.df.fieldname, this.frm.docname); | |||||
} else { | |||||
// use non-doc specific docfield | |||||
this.df = frappe.meta.get_docfield(this.df.options, this.df.fieldname); | |||||
} | |||||
this.docfields = frappe.meta.get_docfields(this.doctype, this.frm.docname); | this.docfields = frappe.meta.get_docfields(this.doctype, this.frm.docname); | ||||
this.display_status = frappe.perm.get_field_display_status(this.df, this.frm.doc, | this.display_status = frappe.perm.get_field_display_status(this.df, this.frm.doc, | ||||
@@ -10,7 +10,7 @@ | |||||
</div> | </div> | ||||
<div class="text-right col-sm-5 col-xs-6 page-actions"> | <div class="text-right col-sm-5 col-xs-6 page-actions"> | ||||
<!-- ID and icon buttons --> | <!-- ID and icon buttons --> | ||||
<h6 class="text-ellipsis sub-heading rtl hide text-muted"></h6> | |||||
<h6 class="text-ellipsis sub-heading hide text-muted"></h6> | |||||
<span class="page-icon-group hide hidden-xs hidden-sm"></span> | <span class="page-icon-group hide hidden-xs hidden-sm"></span> | ||||
<!-- buttons --> | <!-- buttons --> | ||||
@@ -209,3 +209,18 @@ select.form-control { | |||||
font-weight: bold; | font-weight: bold; | ||||
background-color: @light-yellow; | background-color: @light-yellow; | ||||
} | } | ||||
.form-headline .alert { | |||||
font-size: @text-medium; | |||||
border-color: @border-color; | |||||
// background-color: @light-bg; | |||||
margin-bottom: 0px; | |||||
} | |||||
.delivery-status-indicator { | |||||
display: inline-block; | |||||
margin-top: -3px; | |||||
margin-left: 1px; | |||||
font-weight: 500; | |||||
color: @text-muted; | |||||
} |
@@ -10,6 +10,7 @@ | |||||
@modal-backdrop-bg: #334143; | @modal-backdrop-bg: #334143; | ||||
@light-yellow: #fffce7; | @light-yellow: #fffce7; | ||||
@text-extra-muted: @border-color; | @text-extra-muted: @border-color; | ||||
@text-regular: 14px; | |||||
@text-medium: 12px; | @text-medium: 12px; | ||||
@text-small: 10px; | @text-small: 10px; | ||||
@@ -32,7 +32,7 @@ def clear_cache(user=None): | |||||
cache = frappe.cache() | cache = frappe.cache() | ||||
groups = ("bootinfo", "user_recent", "user_roles", "user_doc", "lang", | groups = ("bootinfo", "user_recent", "user_roles", "user_doc", "lang", | ||||
"defaults", "user_permissions", "roles") | |||||
"defaults", "user_permissions", "roles", "home_page") | |||||
if user: | if user: | ||||
for name in groups: | for name in groups: | ||||
@@ -82,7 +82,7 @@ def make_boilerplate(dest, app_name): | |||||
with open(os.path.join(dest, hooks.app_name, hooks.app_name, "config", "desktop.py"), "w") as f: | with open(os.path.join(dest, hooks.app_name, hooks.app_name, "config", "desktop.py"), "w") as f: | ||||
f.write(encode(desktop_template.format(**hooks))) | f.write(encode(desktop_template.format(**hooks))) | ||||
print "'{app}' created at {path}".format(app=app_name, path=os.path.join(dest, app_name)) | |||||
manifest_template = """include MANIFEST.in | manifest_template = """include MANIFEST.in | ||||
@@ -136,8 +136,15 @@ class RedisWrapper(redis.Redis): | |||||
except redis.exceptions.ConnectionError: | except redis.exceptions.ConnectionError: | ||||
pass | pass | ||||
def hdel_keys(self, name_starts_with, key): | |||||
"""Delete hash names with wildcard `*` and key""" | |||||
for name in frappe.cache().get_keys(name_starts_with): | |||||
name = name.split("|", 1)[1] | |||||
self.hdel(name, key) | |||||
def hkeys(self, name): | def hkeys(self, name): | ||||
try: | try: | ||||
return super(redis.Redis, self).hkeys(self.make_key(name)) | return super(redis.Redis, self).hkeys(self.make_key(name)) | ||||
except redis.exceptions.ConnectionError: | except redis.exceptions.ConnectionError: | ||||
return [] | return [] | ||||
@@ -1,80 +0,0 @@ | |||||
{ | |||||
"creation": "2014-03-04 08:29:52", | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "System", | |||||
"fields": [ | |||||
{ | |||||
"fieldname": "facebook", | |||||
"fieldtype": "Section Break", | |||||
"label": "Facebook", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "facebook_client_id", | |||||
"fieldtype": "Data", | |||||
"label": "Facebook Client ID", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "facebook_client_secret", | |||||
"fieldtype": "Data", | |||||
"label": "Facebook Client Secret", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "google", | |||||
"fieldtype": "Section Break", | |||||
"label": "Google", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "google_client_id", | |||||
"fieldtype": "Data", | |||||
"label": "Google Client ID", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "google_client_secret", | |||||
"fieldtype": "Data", | |||||
"label": "Google Client Secret", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "github", | |||||
"fieldtype": "Section Break", | |||||
"label": "GitHub", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "github_client_id", | |||||
"fieldtype": "Data", | |||||
"label": "GitHub Client ID", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "github_client_secret", | |||||
"fieldtype": "Data", | |||||
"label": "GitHub Client Secret", | |||||
"permlevel": 0 | |||||
} | |||||
], | |||||
"icon": "icon-signin", | |||||
"idx": 1, | |||||
"issingle": 1, | |||||
"modified": "2015-02-05 05:11:46.875246", | |||||
"modified_by": "Administrator", | |||||
"module": "Website", | |||||
"name": "Social Login Keys", | |||||
"owner": "Administrator", | |||||
"permissions": [ | |||||
{ | |||||
"create": 1, | |||||
"permlevel": 0, | |||||
"read": 1, | |||||
"role": "System Manager", | |||||
"share": 1, | |||||
"write": 1 | |||||
} | |||||
] | |||||
} |
@@ -41,6 +41,9 @@ class WebForm(WebsiteGenerator): | |||||
name = frappe.db.get_value(self.doc_type, {"owner": frappe.session.user}, "name") | name = frappe.db.get_value(self.doc_type, {"owner": frappe.session.user}, "name") | ||||
if name: | if name: | ||||
frappe.form_dict.name = name | frappe.form_dict.name = name | ||||
else: | |||||
# only a single doc allowed and no existing doc, hence new | |||||
frappe.form_dict.new = 1 | |||||
# always render new form if login is not required or doesn't allow editing existing ones | # always render new form if login is not required or doesn't allow editing existing ones | ||||
if not self.login_required or not self.allow_edit: | if not self.login_required or not self.allow_edit: | ||||
@@ -217,6 +217,7 @@ def clear_cache(path=None): | |||||
clear_sitemap() | clear_sitemap() | ||||
frappe.clear_cache("Guest") | frappe.clear_cache("Guest") | ||||
frappe.cache().delete_value("_website_pages") | frappe.cache().delete_value("_website_pages") | ||||
frappe.cache().delete_value("home_page") | |||||
for method in frappe.get_hooks("website_clear_cache"): | for method in frappe.get_hooks("website_clear_cache"): | ||||
frappe.get_attr(method)(path) | frappe.get_attr(method)(path) |
@@ -59,8 +59,10 @@ def set_breadcrumbs(out, context): | |||||
"""Build breadcrumbs template (deprecated)""" | """Build breadcrumbs template (deprecated)""" | ||||
out["no_breadcrumbs"] = context.get("no_breadcrumbs", 0) or ("<!-- no-breadcrumbs -->" in out.get("content", "")) | out["no_breadcrumbs"] = context.get("no_breadcrumbs", 0) or ("<!-- no-breadcrumbs -->" in out.get("content", "")) | ||||
# breadcrumbs | |||||
if not out["no_breadcrumbs"] and "breadcrumbs" not in out: | |||||
if out["no_breadcrumbs"]: | |||||
out["breadcrumbs"] = "" | |||||
elif "breadcrumbs" not in out: | |||||
out["breadcrumbs"] = frappe.get_template("templates/includes/breadcrumbs.html").render(context) | out["breadcrumbs"] = frappe.get_template("templates/includes/breadcrumbs.html").render(context) | ||||
def set_title_and_header(out, context): | def set_title_and_header(out, context): | ||||
@@ -29,3 +29,4 @@ email_reply_parser | |||||
click | click | ||||
num2words | num2words | ||||
gevent-socketio | gevent-socketio | ||||
watchdog==0.8.0 |
@@ -1,6 +1,6 @@ | |||||
from setuptools import setup, find_packages | from setuptools import setup, find_packages | ||||
version = "6.0.0-wip" | |||||
version = "6.0.0" | |||||
with open("requirements.txt", "r") as f: | with open("requirements.txt", "r") as f: | ||||
install_requires = f.readlines() | install_requires = f.readlines() | ||||