From 788887367203e5074af3a41798dec4ee7e7aee58 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 15 Oct 2019 14:54:01 +0530 Subject: [PATCH 001/157] feat: allow email append to for other doctypes --- .../doctype/customize_form/customize_form.json | 9 ++++++++- .../custom/doctype/customize_form/customize_form.py | 3 ++- frappe/email/doctype/email_account/email_account.py | 12 +++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 0b1df62f9d..4778bf524f 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -20,6 +20,7 @@ "allow_auto_repeat", "allow_import", "image_view", + "allow_in_email_append_to", "column_break_5", "title_field", "image_field", @@ -174,13 +175,19 @@ "fieldname": "allow_import", "fieldtype": "Check", "label": "Allow Import (via Data Import Tool)" + }, + { + "default": "0", + "fieldname": "allow_in_email_append_to", + "fieldtype": "Check", + "label": "Allow in Email Append To" } ], "hide_toolbar": 1, "icon": "fa fa-glass", "idx": 1, "issingle": 1, - "modified": "2019-10-08 11:16:36.698006", + "modified": "2019-10-15 14:20:51.272870", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index b851d40b83..190d4cb567 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -31,7 +31,8 @@ doctype_properties = { 'track_changes': 'Check', 'track_views': 'Check', 'allow_auto_repeat': 'Check', - 'allow_import': 'Check' + 'allow_import': 'Check', + 'allow_in_email_append_to': 'Check' } docfield_properties = { diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index f10f08664c..73e85659b3 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -652,7 +652,17 @@ class EmailAccount(Document): @frappe.whitelist() def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None): if not txt: txt = "" - return [[d] for d in frappe.get_hooks("email_append_to") if txt in d] + + email_append_to_list = frappe.get_hooks("email_append_to") + custom_email_append_to_list = frappe.get_list("Property Setter", filters={ + "property": "allow_in_email_append_to", + "value": 1 + }, fields=["doc_type"]) + + for doctype in custom_email_append_to_list: + email_append_to_list.append(doctype.doc_type) + + return [[d] for d in set(email_append_to_list) if txt in d] def test_internet(host="8.8.8.8", port=53, timeout=3): """Returns True if internet is connected From f207d98d784ac7ab2f2b2882fbb5640e273c4120 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 15 Oct 2019 15:21:42 +0530 Subject: [PATCH 002/157] fix: check if subject and status exists --- frappe/custom/doctype/customize_form/customize_form.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 190d4cb567..57d0251f72 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -165,6 +165,7 @@ class CustomizeForm(Document): self.flags.update_db = False self.flags.rebuild_doctype_for_global_search = False + self.check_email_append_to() self.set_property_setters() self.update_custom_fields() self.set_name_translation() @@ -249,6 +250,13 @@ class CustomizeForm(Document): self.make_property_setter(property=property, value=df.get(property), property_type=docfield_properties[property], fieldname=df.fieldname) + def check_email_append_to(self): + meta = frappe.get_meta(self.doc_type) + + if self.allow_in_email_append_to and not (meta.has_field("subject") and meta.has_field("status")): + frappe.throw(_("Add custom fields for Subject and Status in Document Type \ + {0} to enable Email Append To").format(self.doc_type)) + def update_custom_fields(self): for i, df in enumerate(self.get("fields")): if df.get("is_custom_field"): From 93e64e4ce659c5c0919694b8ff7927bd6486da5c Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sat, 26 Oct 2019 18:14:47 +0530 Subject: [PATCH 003/157] fix: check if meta has subject and status --- .../doctype/email_account/email_account.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 73e85659b3..703835e87b 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -653,16 +653,20 @@ class EmailAccount(Document): def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None): if not txt: txt = "" + if frappe.cache().hget("email_append_to", "email_append_to_dts"): + return frappe.cache().hget("email_append_to", "email_append_to_dts") + email_append_to_list = frappe.get_hooks("email_append_to") - custom_email_append_to_list = frappe.get_list("Property Setter", filters={ - "property": "allow_in_email_append_to", - "value": 1 - }, fields=["doc_type"]) - for doctype in custom_email_append_to_list: - email_append_to_list.append(doctype.doc_type) + for dt in frappe.get_list("DocType", filters={"istable": 0, "issingle": 0}): + meta = frappe.get_meta(dt.name) + if meta.allow_in_email_append_to and meta.has_field("subject") and meta.has_field("status"): + email_append_to_list.append(dt.name) - return [[d] for d in set(email_append_to_list) if txt in d] + email_append = [[d] for d in set(email_append_to_list) if txt in d] + frappe.cache().hset("email_append_to", "email_append_to_dts", email_append) + + return email_append def test_internet(host="8.8.8.8", port=53, timeout=3): """Returns True if internet is connected From 7e7c14e2e72652a2d6641596543848ecf59bc7ce Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 21 Nov 2019 12:21:30 +0530 Subject: [PATCH 004/157] fix: statushsould have open and closed in options --- .../doctype/customize_form/customize_form.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 57d0251f72..7f853324b5 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -253,9 +253,17 @@ class CustomizeForm(Document): def check_email_append_to(self): meta = frappe.get_meta(self.doc_type) - if self.allow_in_email_append_to and not (meta.has_field("subject") and meta.has_field("status")): - frappe.throw(_("Add custom fields for Subject and Status in Document Type \ - {0} to enable Email Append To").format(self.doc_type)) + if self.allow_in_email_append_to: + if not meta.has_field("subject"): + frappe.throw(_("Add custom fields for Subject in Document Type {0} to enable Email Append To").format(self.doc_type)) + + if not meta.has_field("status"): + frappe.throw(_("Add custom fields for Status in Document Type {0} to enable Email Append To").format(self.doc_type)) + else: + status = meta.get_field("status") + for option in ["Open", "Closed"]: + if not option in status.options: + frappe.throw(_("Status field should have status {0} in options").format(option)) def update_custom_fields(self): for i, df in enumerate(self.get("fields")): From 12c1d6cc57533107d27df3937b230f349c9f8aa3 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 21 Nov 2019 13:55:11 +0530 Subject: [PATCH 005/157] fix: check if property exists --- frappe/email/doctype/email_account/email_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 703835e87b..e1f5a2adeb 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -660,7 +660,7 @@ def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len for dt in frappe.get_list("DocType", filters={"istable": 0, "issingle": 0}): meta = frappe.get_meta(dt.name) - if meta.allow_in_email_append_to and meta.has_field("subject") and meta.has_field("status"): + if meta.get("allow_in_email_append_to") and meta.allow_in_email_append_to: email_append_to_list.append(dt.name) email_append = [[d] for d in set(email_append_to_list) if txt in d] From 87eb7243fd61175b9f63e320ab31130a6612b0c0 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 21 Nov 2019 15:31:30 +0530 Subject: [PATCH 006/157] fix: allow custom doctypes --- frappe/email/doctype/email_account/email_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index e1f5a2adeb..c45dab110d 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -660,7 +660,7 @@ def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len for dt in frappe.get_list("DocType", filters={"istable": 0, "issingle": 0}): meta = frappe.get_meta(dt.name) - if meta.get("allow_in_email_append_to") and meta.allow_in_email_append_to: + if meta.custom or (meta.get("allow_in_email_append_to") and meta.allow_in_email_append_to): email_append_to_list.append(dt.name) email_append = [[d] for d in set(email_append_to_list) if txt in d] From 8c9918bc96ef630a9d75589474c6e8f8e66d5814 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 12 Dec 2019 12:46:21 +0530 Subject: [PATCH 007/157] feat: allow document creation via email --- frappe/core/doctype/doctype/doctype.json | 28 ++++++++++- frappe/core/doctype/doctype/doctype.py | 46 +++++++++++++++++++ .../customize_form/customize_form.json | 28 +++++++++-- .../doctype/customize_form/customize_form.py | 24 +++------- frappe/database/mariadb/framework_mariadb.sql | 4 ++ .../database/postgres/framework_postgres.sql | 4 ++ .../doctype/email_account/email_account.py | 26 +++-------- 7 files changed, 118 insertions(+), 42 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 4e3f2fd84a..0fa90ac5df 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -35,6 +35,9 @@ "timeline_field", "nsm_parent_field", "max_attachments", + "subject_field", + "sender_field", + "status_field", "column_break_23", "hide_toolbar", "allow_copy", @@ -42,6 +45,7 @@ "allow_import", "allow_events_in_timeline", "allow_auto_repeat", + "email_append_to", "view_settings", "title_field", "search_fields", @@ -488,11 +492,33 @@ "fieldtype": "Table", "label": "Links", "options": "DocType Link" + }, + { + "fieldname": "subject_field", + "fieldtype": "Data", + "label": "Subject Field" + }, + { + "fieldname": "sender_field", + "fieldtype": "Data", + "label": "Sender Field" + }, + { + "default": "0", + "fieldname": "email_append_to", + "fieldtype": "Check", + "label": "Allow document creation via Email" + }, + { + "fieldname": "status_field", + "fieldtype": "Data", + "label": "Status Field" } ], "icon": "fa fa-bolt", "idx": 6, - "modified": "2019-11-25 17:24:03.690192", + "links": [], + "modified": "2019-12-12 12:05:39.317311", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 1223d50878..35cbdfa992 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -93,6 +93,8 @@ class DocType(Document): if not self.is_new(): self.setup_fields_to_fetch() + check_email_append_to(self) + if self.default_print_format and not self.custom: frappe.throw(_('Standard DocType cannot have default print format, use Customize Form')) @@ -1144,3 +1146,47 @@ def check_if_fieldname_conflicts_with_methods(doctype, fieldname): def clear_linked_doctype_cache(): frappe.cache().delete_value('linked_doctypes_without_ignore_user_permissions_enabled') + +def check_email_append_to(doc): + if not doc.email_append_to: + return + + # Subject Field + doc.subject_field = doc.subject_field.strip() if doc.subject_field else None + subject_field = get_field(doc, doc.subject_field) + + if not (doc.subject_field or subject_field): + frappe.throw(_("Add Subject Field for creating documents from Email")) + + if subject_field.fieldtype not in ["Data", "Text", "Long Text", "Small Text", "Text Editor"]: + frappe.throw(_("Subject Field type should be Data, Text, Long Text, Small Text, Text Editor")) + + # Sender Field + doc.sender_field = doc.sender_field.strip() if doc.sender_field else None + sender_field = get_field(doc, doc.sender_field) + + if not (doc.sender_field or sender_field): + frappe.throw(_("Add Sender Field for creating documents from Email")) + + # Status Field + doc.status_field = doc.status_field.strip() if doc.status_field else None + status_field = get_field(doc, doc.status_field) + + if not (doc.status_field or status_field): + frappe.throw(_("Add Status Field for creating documents from Email")) + + if status_field.fieldtype != "Select": + frappe.throw(_("Status Field type should be Select")) + + for option in ["Open", "Closed"]: + if not option in status_field.options: + frappe.throw(_("Status field should have status Open and Closed in options")) + + +def get_field(doc, fieldname): + if not (doc or fieldname): + return + + for field in doc.fields: + if field.fieldname == fieldname: + return field diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 4778bf524f..3e547fb22f 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "DL.####", "creation": "2013-01-29 17:55:08", "doctype": "DocType", @@ -20,11 +21,14 @@ "allow_auto_repeat", "allow_import", "image_view", - "allow_in_email_append_to", + "email_append_to", "column_break_5", "title_field", "image_field", "search_fields", + "subject_field", + "sender_field", + "status_field", "section_break_8", "sort_field", "column_break_10", @@ -176,18 +180,34 @@ "fieldtype": "Check", "label": "Allow Import (via Data Import Tool)" }, + { + "fieldname": "subject_field", + "fieldtype": "Data", + "label": "Subject Field" + }, + { + "fieldname": "sender_field", + "fieldtype": "Data", + "label": "Sender Field" + }, { "default": "0", - "fieldname": "allow_in_email_append_to", + "fieldname": "email_append_to", "fieldtype": "Check", - "label": "Allow in Email Append To" + "label": "Allow document creation via Email" + }, + { + "fieldname": "status_field", + "fieldtype": "Data", + "label": "Status Field" } ], "hide_toolbar": 1, "icon": "fa fa-glass", "idx": 1, "issingle": 1, - "modified": "2019-10-15 14:20:51.272870", + "links": [], + "modified": "2019-12-12 12:44:02.674456", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 7f853324b5..5e5a9f0c9f 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -12,7 +12,7 @@ from frappe import _ from frappe.utils import cint from frappe.model.document import Document from frappe.model import no_value_fields, core_doctypes_list -from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype +from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype, check_email_append_to from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.model.docfield import supports_translation @@ -32,7 +32,10 @@ doctype_properties = { 'track_views': 'Check', 'allow_auto_repeat': 'Check', 'allow_import': 'Check', - 'allow_in_email_append_to': 'Check' + 'email_append_to': 'Check', + 'subject_field': 'Data', + 'sender_field': 'Data', + 'status_field': 'Data' } docfield_properties = { @@ -165,7 +168,7 @@ class CustomizeForm(Document): self.flags.update_db = False self.flags.rebuild_doctype_for_global_search = False - self.check_email_append_to() + check_email_append_to(self) self.set_property_setters() self.update_custom_fields() self.set_name_translation() @@ -250,21 +253,6 @@ class CustomizeForm(Document): self.make_property_setter(property=property, value=df.get(property), property_type=docfield_properties[property], fieldname=df.fieldname) - def check_email_append_to(self): - meta = frappe.get_meta(self.doc_type) - - if self.allow_in_email_append_to: - if not meta.has_field("subject"): - frappe.throw(_("Add custom fields for Subject in Document Type {0} to enable Email Append To").format(self.doc_type)) - - if not meta.has_field("status"): - frappe.throw(_("Add custom fields for Status in Document Type {0} to enable Email Append To").format(self.doc_type)) - else: - status = meta.get_field("status") - for option in ["Open", "Closed"]: - if not option in status.options: - frappe.throw(_("Status field should have status {0} in options").format(option)) - def update_custom_fields(self): for i, df in enumerate(self.get("fields")): if df.get("is_custom_field"): diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql index b1a769b189..10e2ef7e7d 100644 --- a/frappe/database/mariadb/framework_mariadb.sql +++ b/frappe/database/mariadb/framework_mariadb.sql @@ -215,6 +215,10 @@ CREATE TABLE `tabDocType` ( `allow_guest_to_view` int(1) NOT NULL DEFAULT 0, `route` varchar(255) DEFAULT NULL, `is_published_field` varchar(255) DEFAULT NULL, + `email_append_to` int(1) NOT NULL DEFAULT 0, + `subject_field` varchar(255) DEFAULT NULL, + `sender_field` varchar(255) DEFAULT NULL, + `status_field` varchar(255) DEFAULT NULL, PRIMARY KEY (`name`), KEY `parent` (`parent`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql index cd2f02d8e4..c89c9a8b16 100644 --- a/frappe/database/postgres/framework_postgres.sql +++ b/frappe/database/postgres/framework_postgres.sql @@ -220,6 +220,10 @@ CREATE TABLE "tabDocType" ( "allow_guest_to_view" smallint NOT NULL DEFAULT 0, "route" varchar(255) DEFAULT NULL, "is_published_field" varchar(255) DEFAULT NULL, + `email_append_to` smallint NOT NULL DEFAULT 0, + `subject_field` varchar(255) DEFAULT NULL, + `sender_field` varchar(255) DEFAULT NULL, + `status_field` varchar(255) DEFAULT NULL, PRIMARY KEY ("name") ) ; diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 4dde4826bf..b520c92399 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -419,42 +419,30 @@ class EmailAccount(Document): If no thread id is found and `append_to` is set for the email account, it will create a new parent transaction (e.g. Issue)""" - parent = None - - parent = self.find_parent_from_in_reply_to(communication, email) + parent = self.find_parent_from_in_reply_to(communication, email) or None if not parent and self.append_to: self.set_sender_field_and_subject_field() - - if not parent and self.append_to: parent = self.find_parent_based_on_subject_and_sender(communication, email) - if not parent and self.append_to and self.append_to!="Communication": - parent = self.create_new_parent(communication, email) + if self.append_to!="Communication": + parent = self.create_new_parent(communication, email) if parent: communication.reference_doctype = parent.doctype communication.reference_name = parent.name # check if message is notification and disable notifications for this message - isnotification = email.mail.get("isnotification") - if isnotification: - if "notification" in isnotification: - communication.unread_notification_sent = 1 + if email.mail.get("isnotification") and "notification" in email.mail.get("isnotification"): + communication.unread_notification_sent = 1 def set_sender_field_and_subject_field(self): '''Identify the sender and subject fields from the `append_to` DocType''' # set subject_field and sender_field - meta_module = frappe.get_meta_module(self.append_to) meta = frappe.get_meta(self.append_to) - self.subject_field = getattr(meta_module, "subject_field", "subject") - if not meta.get_field(self.subject_field): - self.subject_field = None - - self.sender_field = getattr(meta_module, "sender_field", "sender") - if not meta.get_field(self.sender_field): - self.sender_field = None + self.subject_field = meta.subject_field if meta.subject_field else None + self.sender_field = meta.sender_field if meta.sender_field else None def find_parent_based_on_subject_and_sender(self, communication, email): '''Find parent document based on subject and sender match''' From ec89d275a2bad2de14c7d4249cd17dc7b9a69dd4 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 12 Dec 2019 15:04:56 +0530 Subject: [PATCH 008/157] feat: remove hooks for email_append_to --- .../doctype/communication/communication.json | 8 ++++++- frappe/desk/doctype/event/event.json | 15 +++++++++++- frappe/desk/doctype/todo/todo.json | 10 ++++++-- frappe/desk/doctype/todo/todo.py | 2 -- .../doctype/email_account/email_account.py | 23 +++++++++++-------- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/frappe/core/doctype/communication/communication.json b/frappe/core/doctype/communication/communication.json index 5e34804b93..3418fc073b 100644 --- a/frappe/core/doctype/communication/communication.json +++ b/frappe/core/doctype/communication/communication.json @@ -1,9 +1,11 @@ { + "actions": [], "allow_import": 1, "creation": "2013-01-29 10:47:14", "description": "Keeps track of all communications", "doctype": "DocType", "document_type": "Setup", + "email_append_to": 1, "engine": "InnoDB", "field_order": [ "subject", @@ -383,7 +385,8 @@ ], "icon": "fa fa-comment", "idx": 1, - "modified": "2019-10-09 14:22:27.664645", + "links": [], + "modified": "2019-12-12 13:01:23.716052", "modified_by": "Administrator", "module": "Core", "name": "Communication", @@ -430,8 +433,11 @@ } ], "search_fields": "subject", + "sender_field": "sender", "sort_field": "modified", "sort_order": "DESC", + "status_field": "status", + "subject_field": "subject", "title_field": "subject", "track_changes": 1, "track_seen": 1 diff --git a/frappe/desk/doctype/event/event.json b/frappe/desk/doctype/event/event.json index 032030ddef..8a0372b13f 100644 --- a/frappe/desk/doctype/event/event.json +++ b/frappe/desk/doctype/event/event.json @@ -1,9 +1,11 @@ { + "actions": [], "allow_import": 1, "autoname": "EV.#####", "creation": "2013-06-10 13:17:47", "doctype": "DocType", "document_type": "Document", + "email_append_to": 1, "engine": "InnoDB", "field_order": [ "details", @@ -17,6 +19,7 @@ "starts_on", "ends_on", "status", + "sender", "all_day", "sync_with_google_calendar", "sb_00", @@ -262,11 +265,18 @@ "fieldtype": "Check", "label": "Pulled from Google Calendar", "read_only": 1 + }, + { + "fieldname": "sender", + "fieldtype": "Data", + "label": "Sender", + "read_only": 1 } ], "icon": "fa fa-calendar", "idx": 1, - "modified": "2019-08-08 16:01:19.489396", + "links": [], + "modified": "2019-12-12 12:58:36.621861", "modified_by": "Administrator", "module": "Desk", "name": "Event", @@ -297,8 +307,11 @@ } ], "read_only": 1, + "sender_field": "sender", "sort_field": "modified", "sort_order": "DESC", + "status_field": "status", + "subject_field": "subject", "title_field": "subject", "track_changes": 1, "track_seen": 1, diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json index 508720a488..f80a785ccd 100644 --- a/frappe/desk/doctype/todo/todo.json +++ b/frappe/desk/doctype/todo/todo.json @@ -1,8 +1,10 @@ { + "actions": [], "autoname": "hash", "creation": "2012-07-03 13:30:35", "doctype": "DocType", "document_type": "Setup", + "email_append_to": 1, "engine": "InnoDB", "field_order": [ "description_and_status", @@ -154,7 +156,8 @@ ], "icon": "fa fa-check", "idx": 2, - "modified": "2019-09-10 14:34:59.161750", + "links": [], + "modified": "2019-12-12 12:53:17.565139", "modified_by": "Administrator", "module": "Desk", "name": "ToDo", @@ -185,9 +188,12 @@ ], "quick_entry": 1, "search_fields": "description, reference_type, reference_name", + "sender_field": "sender", "sort_field": "modified", "sort_order": "DESC", + "status_field": "status", + "subject_field": "description", "title_field": "description", "track_changes": 1, "track_seen": 1 -} +} \ No newline at end of file diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index 5d04f412c0..034afd3184 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -8,8 +8,6 @@ import json from frappe.model.document import Document from frappe.utils import get_fullname -subject_field = "description" -sender_field = "sender" exclude_from_linked_with = True class ToDo(Document): diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index b520c92399..626ecbf378 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -637,21 +637,24 @@ class EmailAccount(Document): frappe.throw(_("Automatic Linking can be activated only for one Email Account.")) @frappe.whitelist() -def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None): - if not txt: txt = "" +def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None, reset_cache=False): - if frappe.cache().hget("email_append_to", "email_append_to_dts"): - return frappe.cache().hget("email_append_to", "email_append_to_dts") + if frappe.cache().hget("email_append_to", "email_append_to_doctypes") and not reset_cache: + return frappe.cache().hget("email_append_to", "email_append_to_doctypes") - email_append_to_list = frappe.get_hooks("email_append_to") + txt = txt if txt else "" + email_append_to_list = [] - for dt in frappe.get_list("DocType", filters={"istable": 0, "issingle": 0}): - meta = frappe.get_meta(dt.name) - if meta.custom or (meta.get("allow_in_email_append_to") and meta.allow_in_email_append_to): + for dt in frappe.get_all("DocType", filters={"istable": 0, "issingle": 0}, fields=["name", "email_append_to"]): + if dt.get("email_append_to") and dt.email_append_to: email_append_to_list.append(dt.name) + else: + meta = frappe.get_meta(dt.name) + if meta.get("email_append_to") and meta.email_append_to: + email_append_to_list.append(dt.name) - email_append = [[d] for d in set(email_append_to_list) if txt in d] - frappe.cache().hset("email_append_to", "email_append_to_dts", email_append) + email_append_to = [[d] for d in set(email_append_to_list) if txt in d] + frappe.cache().hset("email_append_to", "email_append_to_doctypes", email_append_to) return email_append From b98ea6415016d5f4f08c6d15ec57f5b65506eab0 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 12 Dec 2019 19:23:22 +0530 Subject: [PATCH 009/157] fix: remove status field --- frappe/core/doctype/doctype/doctype.json | 8 +------- frappe/core/doctype/doctype/doctype.py | 14 -------------- .../doctype/customize_form/customize_form.json | 8 +------- .../doctype/customize_form/customize_form.py | 3 +-- 4 files changed, 3 insertions(+), 30 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 0fa90ac5df..9a8177d3a3 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -37,7 +37,6 @@ "max_attachments", "subject_field", "sender_field", - "status_field", "column_break_23", "hide_toolbar", "allow_copy", @@ -508,17 +507,12 @@ "fieldname": "email_append_to", "fieldtype": "Check", "label": "Allow document creation via Email" - }, - { - "fieldname": "status_field", - "fieldtype": "Data", - "label": "Status Field" } ], "icon": "fa fa-bolt", "idx": 6, "links": [], - "modified": "2019-12-12 12:05:39.317311", + "modified": "2019-12-12 19:16:50.130503", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 35cbdfa992..5610fa4f89 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1168,20 +1168,6 @@ def check_email_append_to(doc): if not (doc.sender_field or sender_field): frappe.throw(_("Add Sender Field for creating documents from Email")) - # Status Field - doc.status_field = doc.status_field.strip() if doc.status_field else None - status_field = get_field(doc, doc.status_field) - - if not (doc.status_field or status_field): - frappe.throw(_("Add Status Field for creating documents from Email")) - - if status_field.fieldtype != "Select": - frappe.throw(_("Status Field type should be Select")) - - for option in ["Open", "Closed"]: - if not option in status_field.options: - frappe.throw(_("Status field should have status Open and Closed in options")) - def get_field(doc, fieldname): if not (doc or fieldname): diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 3e547fb22f..49e8dee366 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -28,7 +28,6 @@ "search_fields", "subject_field", "sender_field", - "status_field", "section_break_8", "sort_field", "column_break_10", @@ -195,11 +194,6 @@ "fieldname": "email_append_to", "fieldtype": "Check", "label": "Allow document creation via Email" - }, - { - "fieldname": "status_field", - "fieldtype": "Data", - "label": "Status Field" } ], "hide_toolbar": 1, @@ -207,7 +201,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2019-12-12 12:44:02.674456", + "modified": "2019-12-12 19:17:08.426469", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 5e5a9f0c9f..4c5fe76bed 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -34,8 +34,7 @@ doctype_properties = { 'allow_import': 'Check', 'email_append_to': 'Check', 'subject_field': 'Data', - 'sender_field': 'Data', - 'status_field': 'Data' + 'sender_field': 'Data' } docfield_properties = { From 78f506226a12aa4cf32d769293cdb41d1ec994b4 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 12 Dec 2019 21:14:26 +0530 Subject: [PATCH 010/157] fix: return correct variable --- frappe/email/doctype/email_account/email_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 626ecbf378..7d9c2bb8fb 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -656,7 +656,7 @@ def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len email_append_to = [[d] for d in set(email_append_to_list) if txt in d] frappe.cache().hset("email_append_to", "email_append_to_doctypes", email_append_to) - return email_append + return email_append_to def test_internet(host="8.8.8.8", port=53, timeout=3): """Returns True if internet is connected From 2fe3430e743a2d4311018b02bc5d8737fc2b9ecc Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 27 Dec 2019 14:30:24 +0530 Subject: [PATCH 011/157] fix: create a new section for email append to --- frappe/core/doctype/doctype/doctype.js | 12 +++++++++++- frappe/core/doctype/doctype/doctype.json | 17 +++++++++++++---- .../doctype/customize_form/customize_form.js | 9 +++++++++ .../doctype/customize_form/customize_form.json | 17 ++++++++++++++--- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index 9a19185cfc..379f7cdd9e 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -53,7 +53,17 @@ frappe.ui.form.on('DocType', { frm.events.autoname(frm); }, - autoname(frm) { + email_append_to: function (frm) { + frm.set_df_property("subject_field", "reqd", 0); + frm.set_df_property("sender_field", "reqd", 0); + + if (frm.doc.email_append_to) { + frm.set_df_property("subject_field", "reqd", 1); + frm.set_df_property("sender_field", "reqd", 1); + } + }, + + autoname: function(frm) { frm.set_df_property('fields', 'reqd', frm.doc.autoname !== 'Prompt'); } }) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 9a8177d3a3..59fd55c402 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -35,8 +35,6 @@ "timeline_field", "nsm_parent_field", "max_attachments", - "subject_field", - "sender_field", "column_break_23", "hide_toolbar", "allow_copy", @@ -44,7 +42,6 @@ "allow_import", "allow_events_in_timeline", "allow_auto_repeat", - "email_append_to", "view_settings", "title_field", "search_fields", @@ -57,6 +54,10 @@ "color", "show_preview_popup", "show_name_in_global_search", + "email_settings_sb", + "email_append_to", + "subject_field", + "sender_field", "sb2", "permissions", "restrict_to_domain", @@ -493,11 +494,13 @@ "options": "DocType Link" }, { + "depends_on": "email_append_to", "fieldname": "subject_field", "fieldtype": "Data", "label": "Subject Field" }, { + "depends_on": "email_append_to", "fieldname": "sender_field", "fieldtype": "Data", "label": "Sender Field" @@ -507,12 +510,18 @@ "fieldname": "email_append_to", "fieldtype": "Check", "label": "Allow document creation via Email" + }, + { + "collapsible": 1, + "fieldname": "email_settings_sb", + "fieldtype": "Section Break", + "label": "Email Settings" } ], "icon": "fa fa-bolt", "idx": 6, "links": [], - "modified": "2019-12-12 19:16:50.130503", + "modified": "2019-12-27 13:28:06.201814", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js index f1eadaaf2e..4ade51ae5b 100644 --- a/frappe/custom/doctype/customize_form/customize_form.js +++ b/frappe/custom/doctype/customize_form/customize_form.js @@ -132,6 +132,15 @@ frappe.ui.form.on("Customize Form", { }, + email_append_to: function(frm) { + frm.set_df_property("subject_field", "reqd", 0); + frm.set_df_property("sender_field", "reqd", 0); + + if (frm.doc.email_append_to) { + frm.set_df_property("subject_field", "reqd", 1); + frm.set_df_property("sender_field", "reqd", 1); + } + } }); frappe.ui.form.on("Customize Form Field", { diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 49e8dee366..f719add96c 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -26,12 +26,14 @@ "title_field", "image_field", "search_fields", - "subject_field", - "sender_field", "section_break_8", "sort_field", "column_break_10", "sort_order", + "section_break_23", + "subject_field", + "cb_01", + "sender_field", "fields_section_break", "fields" ], @@ -194,6 +196,15 @@ "fieldname": "email_append_to", "fieldtype": "Check", "label": "Allow document creation via Email" + }, + { + "depends_on": "doc_type", + "fieldname": "section_break_23", + "fieldtype": "Section Break" + }, + { + "fieldname": "cb_01", + "fieldtype": "Column Break" } ], "hide_toolbar": 1, @@ -201,7 +212,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2019-12-12 19:17:08.426469", + "modified": "2019-12-27 14:29:49.097980", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", From 9543b7d4afca27c4e7f618d3f65b3e4fe6541c52 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 27 Dec 2019 14:48:46 +0530 Subject: [PATCH 012/157] fix: remove status field --- .../doctype/communication/communication.json | 3 +-- frappe/database/mariadb/framework_mariadb.sql | 1 - frappe/database/postgres/framework_postgres.sql | 1 - frappe/desk/doctype/event/event.json | 3 +-- frappe/desk/doctype/todo/todo.json | 3 +-- .../email/doctype/email_account/email_account.py | 16 ++++++---------- 6 files changed, 9 insertions(+), 18 deletions(-) diff --git a/frappe/core/doctype/communication/communication.json b/frappe/core/doctype/communication/communication.json index 3418fc073b..ae6fb164ec 100644 --- a/frappe/core/doctype/communication/communication.json +++ b/frappe/core/doctype/communication/communication.json @@ -386,7 +386,7 @@ "icon": "fa fa-comment", "idx": 1, "links": [], - "modified": "2019-12-12 13:01:23.716052", + "modified": "2019-12-27 14:44:04.880373", "modified_by": "Administrator", "module": "Core", "name": "Communication", @@ -436,7 +436,6 @@ "sender_field": "sender", "sort_field": "modified", "sort_order": "DESC", - "status_field": "status", "subject_field": "subject", "title_field": "subject", "track_changes": 1, diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql index 10e2ef7e7d..bf9e0e33c8 100644 --- a/frappe/database/mariadb/framework_mariadb.sql +++ b/frappe/database/mariadb/framework_mariadb.sql @@ -218,7 +218,6 @@ CREATE TABLE `tabDocType` ( `email_append_to` int(1) NOT NULL DEFAULT 0, `subject_field` varchar(255) DEFAULT NULL, `sender_field` varchar(255) DEFAULT NULL, - `status_field` varchar(255) DEFAULT NULL, PRIMARY KEY (`name`), KEY `parent` (`parent`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql index c89c9a8b16..6bd20d241b 100644 --- a/frappe/database/postgres/framework_postgres.sql +++ b/frappe/database/postgres/framework_postgres.sql @@ -223,7 +223,6 @@ CREATE TABLE "tabDocType" ( `email_append_to` smallint NOT NULL DEFAULT 0, `subject_field` varchar(255) DEFAULT NULL, `sender_field` varchar(255) DEFAULT NULL, - `status_field` varchar(255) DEFAULT NULL, PRIMARY KEY ("name") ) ; diff --git a/frappe/desk/doctype/event/event.json b/frappe/desk/doctype/event/event.json index 8a0372b13f..59ba7689db 100644 --- a/frappe/desk/doctype/event/event.json +++ b/frappe/desk/doctype/event/event.json @@ -276,7 +276,7 @@ "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2019-12-12 12:58:36.621861", + "modified": "2019-12-27 14:45:51.332025", "modified_by": "Administrator", "module": "Desk", "name": "Event", @@ -310,7 +310,6 @@ "sender_field": "sender", "sort_field": "modified", "sort_order": "DESC", - "status_field": "status", "subject_field": "subject", "title_field": "subject", "track_changes": 1, diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json index f80a785ccd..cb6dc2fc03 100644 --- a/frappe/desk/doctype/todo/todo.json +++ b/frappe/desk/doctype/todo/todo.json @@ -157,7 +157,7 @@ "icon": "fa fa-check", "idx": 2, "links": [], - "modified": "2019-12-12 12:53:17.565139", + "modified": "2019-12-27 14:45:25.161055", "modified_by": "Administrator", "module": "Desk", "name": "ToDo", @@ -191,7 +191,6 @@ "sender_field": "sender", "sort_field": "modified", "sort_order": "DESC", - "status_field": "status", "subject_field": "description", "title_field": "description", "track_changes": 1, diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 0bd4beed0b..74285e2182 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -637,24 +637,20 @@ class EmailAccount(Document): frappe.throw(_("Automatic Linking can be activated only for one Email Account.")) @frappe.whitelist() -def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None, reset_cache=False): - - if frappe.cache().hget("email_append_to", "email_append_to_doctypes") and not reset_cache: - return frappe.cache().hget("email_append_to", "email_append_to_doctypes") - +def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None): txt = txt if txt else "" email_append_to_list = [] + # Set Email Append To DocTypes via DocType for dt in frappe.get_all("DocType", filters={"istable": 0, "issingle": 0}, fields=["name", "email_append_to"]): if dt.get("email_append_to") and dt.email_append_to: email_append_to_list.append(dt.name) - else: - meta = frappe.get_meta(dt.name) - if meta.get("email_append_to") and meta.email_append_to: - email_append_to_list.append(dt.name) + + # Set Email Append To DocTypes set via Customize Form + for dt in frappe.get_list("Property Setter", filters={"property": "email_append_to", "value": 1}, fields=["doc_type"]): + email_append_to_list.append(dt.doc_type) email_append_to = [[d] for d in set(email_append_to_list) if txt in d] - frappe.cache().hset("email_append_to", "email_append_to_doctypes", email_append_to) return email_append_to From 3b517023dfd767f7ac96c7a89bcd59bf7e8f8bfc Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Mon, 13 Jan 2020 17:10:45 +0530 Subject: [PATCH 013/157] feat: make sender_field mandatory --- frappe/core/doctype/doctype/doctype.js | 2 -- frappe/core/doctype/doctype/doctype.py | 15 +++++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index 379f7cdd9e..844d49ed21 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -54,11 +54,9 @@ frappe.ui.form.on('DocType', { }, email_append_to: function (frm) { - frm.set_df_property("subject_field", "reqd", 0); frm.set_df_property("sender_field", "reqd", 0); if (frm.doc.email_append_to) { - frm.set_df_property("subject_field", "reqd", 1); frm.set_df_property("sender_field", "reqd", 1); } }, diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index b8e0bbbe7b..9a4b09b24b 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1155,18 +1155,21 @@ def check_email_append_to(doc): doc.subject_field = doc.subject_field.strip() if doc.subject_field else None subject_field = get_field(doc, doc.subject_field) - if not (doc.subject_field or subject_field): - frappe.throw(_("Add Subject Field for creating documents from Email")) + if doc.subject_field and not subject_field: + frappe.throw(_("Select a valid Subject field for creating documents from Email")) - if subject_field.fieldtype not in ["Data", "Text", "Long Text", "Small Text", "Text Editor"]: + if subject_field and subject_field.fieldtype not in ["Data", "Text", "Long Text", "Small Text", "Text Editor"]: frappe.throw(_("Subject Field type should be Data, Text, Long Text, Small Text, Text Editor")) - # Sender Field + # Sender Field is mandatory doc.sender_field = doc.sender_field.strip() if doc.sender_field else None sender_field = get_field(doc, doc.sender_field) - if not (doc.sender_field or sender_field): - frappe.throw(_("Add Sender Field for creating documents from Email")) + if doc.sender_field and not sender_field: + frappe.throw(_("Select a valid Sender Field for creating documents from Email")) + + if not sender_field.options == "Email": + frappe.throw(_("Sender Field should have Email in options")) def get_field(doc, fieldname): From f62f80f33c0ceced29e9c245ba37ad74245b4c6e Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Mon, 13 Jan 2020 23:54:47 +0530 Subject: [PATCH 014/157] fix: check if meta hasattr for subject and email --- frappe/email/doctype/email_account/email_account.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index ef8fb0ab28..ad11a50b29 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -432,7 +432,7 @@ class EmailAccount(Document): self.set_sender_field_and_subject_field() parent = self.find_parent_based_on_subject_and_sender(communication, email) - if self.append_to!="Communication": + if not self.append_to == "Communication": parent = self.create_new_parent(communication, email) if parent: @@ -447,9 +447,14 @@ class EmailAccount(Document): '''Identify the sender and subject fields from the `append_to` DocType''' # set subject_field and sender_field meta = frappe.get_meta(self.append_to) + self.subject_field = None + self.sender_field = None - self.subject_field = meta.subject_field if meta.subject_field else None - self.sender_field = meta.sender_field if meta.sender_field else None + if hasattr(meta, "subject_field"): + self.subject_field = meta.subject_field + + if hasattr(meta, "sender_field"): + self.sender_field = meta.sender_field def find_parent_based_on_subject_and_sender(self, communication, email): '''Find parent document based on subject and sender match''' From 84d740af100723bdff63099c509b09d6759b0705 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 14 Jan 2020 17:18:29 +0530 Subject: [PATCH 015/157] fix: add Email in options --- frappe/custom/doctype/customize_form/test_customize_form.py | 2 +- frappe/desk/doctype/todo/todo.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/custom/doctype/customize_form/test_customize_form.py b/frappe/custom/doctype/customize_form/test_customize_form.py index 1cd71ea05d..cace25a03d 100644 --- a/frappe/custom/doctype/customize_form/test_customize_form.py +++ b/frappe/custom/doctype/customize_form/test_customize_form.py @@ -46,7 +46,7 @@ class TestCustomizeForm(unittest.TestCase): d = self.get_customize_form("Event") self.assertEquals(d.doc_type, "Event") - self.assertEquals(len(d.get("fields")), 35) + self.assertEquals(len(d.get("fields")), 36) d = self.get_customize_form("Event") self.assertEquals(d.doc_type, "Event") diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json index cb6dc2fc03..15e0e4abe1 100644 --- a/frappe/desk/doctype/todo/todo.json +++ b/frappe/desk/doctype/todo/todo.json @@ -144,7 +144,8 @@ "fieldname": "sender", "fieldtype": "Data", "hidden": 1, - "label": "Sender" + "label": "Sender", + "options": "Email" }, { "fieldname": "assignment_rule", @@ -157,7 +158,7 @@ "icon": "fa fa-check", "idx": 2, "links": [], - "modified": "2019-12-27 14:45:25.161055", + "modified": "2020-01-14 17:04:36.971002", "modified_by": "Administrator", "module": "Desk", "name": "ToDo", From b2d70447fd8aa49c35924d7db28fe4a7bbba0fc6 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 14 Jan 2020 22:20:05 +0530 Subject: [PATCH 016/157] fix: update event json and check attr --- frappe/core/doctype/doctype/doctype.py | 2 +- frappe/desk/doctype/event/event.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 9a4b09b24b..a60acaaeb8 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1148,7 +1148,7 @@ def clear_linked_doctype_cache(): frappe.cache().delete_value('linked_doctypes_without_ignore_user_permissions_enabled') def check_email_append_to(doc): - if not doc.email_append_to: + if not hasattr(doc, "email_append_to") or not doc.email_append_to: return # Subject Field diff --git a/frappe/desk/doctype/event/event.json b/frappe/desk/doctype/event/event.json index 59ba7689db..5768f00f32 100644 --- a/frappe/desk/doctype/event/event.json +++ b/frappe/desk/doctype/event/event.json @@ -270,13 +270,14 @@ "fieldname": "sender", "fieldtype": "Data", "label": "Sender", + "options": "Email", "read_only": 1 } ], "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2019-12-27 14:45:51.332025", + "modified": "2020-01-14 21:47:15.825287", "modified_by": "Administrator", "module": "Desk", "name": "Event", From 858784d558fab178ff82db1e3d3385c8e836e950 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sun, 19 Jan 2020 12:07:21 +0530 Subject: [PATCH 017/157] chore: revert code changes --- .../email/doctype/email_account/email_account.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index ad11a50b29..e4a3b41a56 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -426,22 +426,28 @@ class EmailAccount(Document): If no thread id is found and `append_to` is set for the email account, it will create a new parent transaction (e.g. Issue)""" - parent = self.find_parent_from_in_reply_to(communication, email) or None + parent = None + + parent = self.find_parent_from_in_reply_to(communication, email) if not parent and self.append_to: self.set_sender_field_and_subject_field() + + if not parent and self.append_to: parent = self.find_parent_based_on_subject_and_sender(communication, email) - if not self.append_to == "Communication": - parent = self.create_new_parent(communication, email) + if not parent and self.append_to and self.append_to!="Communication": + parent = self.create_new_parent(communication, email) if parent: communication.reference_doctype = parent.doctype communication.reference_name = parent.name # check if message is notification and disable notifications for this message - if email.mail.get("isnotification") and "notification" in email.mail.get("isnotification"): - communication.unread_notification_sent = 1 + isnotification = email.mail.get("isnotification") + if isnotification: + if "notification" in isnotification: + communication.unread_notification_sent = 1 def set_sender_field_and_subject_field(self): '''Identify the sender and subject fields from the `append_to` DocType''' From 230073ebf2131bfb6f176352416659703146f0d5 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sun, 19 Jan 2020 14:54:18 +0530 Subject: [PATCH 018/157] fix: change to quotes in postgres.sql file --- frappe/database/postgres/framework_postgres.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql index 16114f8cf8..26760dbcc9 100644 --- a/frappe/database/postgres/framework_postgres.sql +++ b/frappe/database/postgres/framework_postgres.sql @@ -222,9 +222,9 @@ CREATE TABLE "tabDocType" ( "allow_guest_to_view" smallint NOT NULL DEFAULT 0, "route" varchar(255) DEFAULT NULL, "is_published_field" varchar(255) DEFAULT NULL, - `email_append_to` smallint NOT NULL DEFAULT 0, - `subject_field` varchar(255) DEFAULT NULL, - `sender_field` varchar(255) DEFAULT NULL, + "email_append_to" smallint NOT NULL DEFAULT 0, + "subject_field" varchar(255) DEFAULT NULL, + "sender_field" varchar(255) DEFAULT NULL, PRIMARY KEY ("name") ) ; From 0f5326a4cda82fcf2ff3fcc26b0fb45919d6a98f Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 29 Jan 2020 18:21:34 +0530 Subject: [PATCH 019/157] feat: api indexing --- .../doctype/google_indexing/__init__.py | 0 .../google_indexing/google_indexing.js | 31 +++++ .../google_indexing/google_indexing.json | 59 +++++++++ .../google_indexing/google_indexing.py | 120 ++++++++++++++++++ .../google_indexing/test_google_indexing.py | 10 ++ 5 files changed, 220 insertions(+) create mode 100644 frappe/integrations/doctype/google_indexing/__init__.py create mode 100644 frappe/integrations/doctype/google_indexing/google_indexing.js create mode 100644 frappe/integrations/doctype/google_indexing/google_indexing.json create mode 100644 frappe/integrations/doctype/google_indexing/google_indexing.py create mode 100644 frappe/integrations/doctype/google_indexing/test_google_indexing.py diff --git a/frappe/integrations/doctype/google_indexing/__init__.py b/frappe/integrations/doctype/google_indexing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/integrations/doctype/google_indexing/google_indexing.js b/frappe/integrations/doctype/google_indexing/google_indexing.js new file mode 100644 index 0000000000..d742effd05 --- /dev/null +++ b/frappe/integrations/doctype/google_indexing/google_indexing.js @@ -0,0 +1,31 @@ +// Copyright (c) 2020, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Google Indexing', { + refresh: function(frm) { + if (!frm.doc.enable) { + frm.dashboard.set_headline(__("To use Google API Indexing, enable {0}.", [`${__('Google Settings')}`])); + } + }, + + authorize_api_indexing_access: function(frm) { + let reauthorize = 0; + if(frm.doc.authorization_code) { + reauthorize = 1; + } + + frappe.call({ + method: "frappe.integrations.doctype.google_indexing.google_indexing.authorize_access", + args: { + "g_contact": frm.doc.name, + "reauthorize": reauthorize + }, + callback: function(r) { + if(!r.exc) { + frm.save(); + window.open(r.message.url); + } + } + }); + } +}); \ No newline at end of file diff --git a/frappe/integrations/doctype/google_indexing/google_indexing.json b/frappe/integrations/doctype/google_indexing/google_indexing.json new file mode 100644 index 0000000000..669c46b5f1 --- /dev/null +++ b/frappe/integrations/doctype/google_indexing/google_indexing.json @@ -0,0 +1,59 @@ +{ + "actions": [], + "creation": "2020-02-03 09:24:27.503449", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enable_indexing", + "authorize_api_indexing_access", + "refresh_token", + "access_token" + ], + "fields": [ + { + "default": "0", + "fieldname": "enable_indexing", + "fieldtype": "Check", + "label": "Enable Indexing" + }, + { + "fieldname": "authorize_api_indexing_access", + "fieldtype": "Button", + "label": "Authorize API Indexing Access" + }, + { + "fieldname": "refresh_token", + "fieldtype": "Password", + "label": "Refresh Token" + }, + { + "fieldname": "access_token", + "fieldtype": "Password", + "label": "Access Token" + } + ], + "issingle": 1, + "links": [], + "modified": "2020-02-03 09:24:51.894310", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Google Indexing", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/integrations/doctype/google_indexing/google_indexing.py b/frappe/integrations/doctype/google_indexing/google_indexing.py new file mode 100644 index 0000000000..f9b62ce5c8 --- /dev/null +++ b/frappe/integrations/doctype/google_indexing/google_indexing.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import requests +import googleapiclient.discovery +import google.oauth2.credentials +import os + +from frappe import _ +from googleapiclient.errors import HttpError +from frappe.model.document import Document +from frappe.integrations.doctype.google_settings.google_settings import get_auth_url + +SCOPES = "https://www.googleapis.com/auth/indexing" + +class GoogleIndexing(Document): + def validate(self): + if not frappe.db.get_single_value("Google Settings", "enable"): + frappe.throw(_("Enable Google API in Google Settings.")) + + def get_access_token(self): + google_settings = frappe.get_doc("Google Settings") + + if not google_settings.enable: + frappe.throw(_("Google Integration is disabled.")) + + if not self.refresh_token: + button_label = frappe.bold(_("Allow API Indexing Access")) + raise frappe.ValidationError(_("Click on {0} to generate Refresh Token.").format(button_label)) + + data = { + "client_id": google_settings.client_id, + "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), + "refresh_token": self.get_password(fieldname="refresh_token", raise_exception=False), + "grant_type": "refresh_token", + "scope": SCOPES + } + + try: + r = requests.post(get_auth_url(), data=data).json() + except requests.exceptions.HTTPError: + button_label = frappe.bold(_("Allow Google Drive Access")) + frappe.throw(_("Something went wrong during the token generation. Click on {0} to generate a new one.").format(button_label)) + + return r.get("access_token") + +@frappe.whitelist() +def authorize_access(reauthorize=None): + """ + If no Authorization code get it from Google and then request for Refresh Token. + Google Contact Name is set to flags to set_value after Authorization Code is obtained. + """ + + google_settings = frappe.get_doc("Google Settings") + google_indexing = frappe.get_doc("Google Indexing") + + redirect_uri = get_request_site_address(True) + "?cmd=frappe.integrations.doctype.google_indexing.google_indexing.google_callback" + + if not google_drive.authorization_code or reauthorize: + return get_authentication_url(client_id=google_settings.client_id, redirect_uri=redirect_uri) + else: + try: + data = { + "code": google_indexing.authorization_code, + "client_id": google_settings.client_id, + "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), + "redirect_uri": redirect_uri, + "grant_type": "authorization_code" + } + r = requests.post(get_auth_url(), data=data).json() + + if "refresh_token" in r: + frappe.db.set_value("Google Indexing", google_indexing.name, "refresh_token", r.get("refresh_token")) + frappe.db.commit() + + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = "/desk#Form/{0}".format(quote("Google Indexing")) + + frappe.msgprint(_("Google Indexing has been configured.")) + except Exception as e: + frappe.throw(e) + +def get_authentication_url(client_id, redirect_uri): + return { + "url": "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&prompt=consent&client_id={}&include_granted_scopes=true&scope={}&redirect_uri={}".format(client_id, SCOPES, redirect_uri) + } + +@frappe.whitelist() +def google_callback(code=None): + """ + Authorization code is sent to callback as per the API configuration + """ + frappe.db.set_value("Google Indexing", None, "authorization_code", code) + frappe.db.commit() + + authorize_access() + +def get_google_indexing_object(): + """ + Returns an object of Google Indexing Service + """ + google_settings = frappe.get_doc("Google Settings") + account = frappe.get_doc("Google Indexing") + + credentials_dict = { + "token": account.get_access_token(), + "refresh_token": account.get_password(fieldname="refresh_token", raise_exception=False), + "token_uri": get_auth_url(), + "client_id": google_settings.client_id, + "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), + "scopes": "https://www.googleapis.com/auth/indexing" + } + + credentials = google.oauth2.credentials.Credentials(**credentials_dict) + google_indexing = googleapiclient.discovery.build("indexing", "v3", credentials=credentials) + + return google_indexing, account \ No newline at end of file diff --git a/frappe/integrations/doctype/google_indexing/test_google_indexing.py b/frappe/integrations/doctype/google_indexing/test_google_indexing.py new file mode 100644 index 0000000000..6bf7dc9d3e --- /dev/null +++ b/frappe/integrations/doctype/google_indexing/test_google_indexing.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestGoogleIndexing(unittest.TestCase): + pass From dcbbe68bec6cecbaf76b616596724af8d4d290e6 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 30 Jan 2020 12:29:50 +0530 Subject: [PATCH 020/157] feat: add methods to publish url --- .../google_indexing/google_indexing.json | 11 +++++++---- .../doctype/google_indexing/google_indexing.py | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/frappe/integrations/doctype/google_indexing/google_indexing.json b/frappe/integrations/doctype/google_indexing/google_indexing.json index 669c46b5f1..00b544449d 100644 --- a/frappe/integrations/doctype/google_indexing/google_indexing.json +++ b/frappe/integrations/doctype/google_indexing/google_indexing.json @@ -8,7 +8,7 @@ "enable_indexing", "authorize_api_indexing_access", "refresh_token", - "access_token" + "authorization_code" ], "fields": [ { @@ -18,6 +18,7 @@ "label": "Enable Indexing" }, { + "depends_on": "eval:!doc.__islocal && doc.enable_indexing", "fieldname": "authorize_api_indexing_access", "fieldtype": "Button", "label": "Authorize API Indexing Access" @@ -25,17 +26,19 @@ { "fieldname": "refresh_token", "fieldtype": "Password", + "hidden": 1, "label": "Refresh Token" }, { - "fieldname": "access_token", + "fieldname": "authorization_code", "fieldtype": "Password", - "label": "Access Token" + "hidden": 1, + "label": "Authorization Code" } ], "issingle": 1, "links": [], - "modified": "2020-02-03 09:24:51.894310", + "modified": "2020-02-03 17:11:52.094597", "modified_by": "Administrator", "module": "Integrations", "name": "Google Indexing", diff --git a/frappe/integrations/doctype/google_indexing/google_indexing.py b/frappe/integrations/doctype/google_indexing/google_indexing.py index f9b62ce5c8..2ca09b60f1 100644 --- a/frappe/integrations/doctype/google_indexing/google_indexing.py +++ b/frappe/integrations/doctype/google_indexing/google_indexing.py @@ -11,7 +11,9 @@ import os from frappe import _ from googleapiclient.errors import HttpError +from frappe.utils import get_request_site_address from frappe.model.document import Document +from six.moves.urllib.parse import quote from frappe.integrations.doctype.google_settings.google_settings import get_auth_url SCOPES = "https://www.googleapis.com/auth/indexing" @@ -59,7 +61,7 @@ def authorize_access(reauthorize=None): redirect_uri = get_request_site_address(True) + "?cmd=frappe.integrations.doctype.google_indexing.google_indexing.google_callback" - if not google_drive.authorization_code or reauthorize: + if not google_indexing.authorization_code or reauthorize: return get_authentication_url(client_id=google_settings.client_id, redirect_uri=redirect_uri) else: try: @@ -117,4 +119,15 @@ def get_google_indexing_object(): credentials = google.oauth2.credentials.Credentials(**credentials_dict) google_indexing = googleapiclient.discovery.build("indexing", "v3", credentials=credentials) - return google_indexing, account \ No newline at end of file + return google_indexing + +def publish_site(url, operation_type="URL_UPDATED"): + google_indexing = get_google_indexing_object() + body = { + "url": url, + "type": operation_type + } + try: + google_indexing.urlNotifications().publish(body=body, x__xgafv='2').execute() + except HttpError as e: + frappe.log_error(message=e, title='API Indexing Issue') \ No newline at end of file From d8de63b341b1ee4f70816e4b4f63518a94507a17 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 31 Jan 2020 17:31:05 +0530 Subject: [PATCH 021/157] fix: enqueue publish on update and trash --- frappe/website/website_generator.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frappe/website/website_generator.py b/frappe/website/website_generator.py index 654eed838b..36946964db 100644 --- a/frappe/website/website_generator.py +++ b/frappe/website/website_generator.py @@ -81,8 +81,18 @@ class WebsiteGenerator(Document): '''Return breadcrumbs''' pass + def on_update(self): + if frappe.db.get_single_value('Google Indexing', 'enable_indexing'): + url = frappe.utils.get_url(self.route) + frappe.enqueue('frappe.integrations.doctype.google_indexing.google_indexing.publish_site', \ + url=url) + def on_trash(self): self.clear_cache() + if frappe.db.get_single_value('Google Indexing', 'enable_indexing'): + url = frappe.utils.get_url(self.route) + frappe.enqueue('frappe.integrations.doctype.google_indexing.google_indexing.publish_site', \ + url=url, operation_type='URL_DELETED') def is_website_published(self): """Return true if published in website""" From 1ff6333666b369702ea9fbe25ba41995cf402811 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 3 Feb 2020 18:35:00 +0530 Subject: [PATCH 022/157] fix: update blog post and web page on update and on trash --- frappe/website/doctype/blog_post/blog_post.py | 4 ++++ frappe/website/doctype/web_page/web_page.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index 8dbc176f89..148ba15be7 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -44,8 +44,12 @@ class BlogPost(WebsiteGenerator): WHERE `name`=%s""", (self.blogger,)) def on_update(self): + super(BlogPost, self).on_update() clear_cache("writers") + def on_trash(self): + super(BlogPost, self).on_trash() + def get_context(self, context): # this is for double precaution. usually it wont reach this code if not published if not cint(self.published): diff --git a/frappe/website/doctype/web_page/web_page.py b/frappe/website/doctype/web_page/web_page.py index 9509e57798..d6cb7cccb7 100644 --- a/frappe/website/doctype/web_page/web_page.py +++ b/frappe/website/doctype/web_page/web_page.py @@ -28,6 +28,12 @@ class WebPage(WebsiteGenerator): def get_feed(self): return self.title + def on_update(self): + super(BlogPost, self).on_update() + + def on_trash(self): + super(BlogPost, self).on_trash() + def get_context(self, context): context.main_section = get_html_content_based_on_type(self, 'main_section', self.content_type) self.render_dynamic(context) From 6684c0cd3ceec5761ccda709e484bece741a8956 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 5 Feb 2020 17:47:13 +0530 Subject: [PATCH 023/157] fix: add indexing configuration to website settings --- .../doctype/google_indexing/__init__.py | 0 .../google_indexing/google_indexing.js | 31 --------- .../google_indexing/google_indexing.json | 62 ------------------ .../website_settings}/google_indexing.py | 64 ++++++------------- .../test_website_settings.py} | 2 +- .../website_settings/website_settings.js | 21 ++++++ .../website_settings/website_settings.json | 34 +++++++++- .../website_settings/website_settings.py | 36 +++++++++++ frappe/website/website_generator.py | 20 +++--- 9 files changed, 121 insertions(+), 149 deletions(-) delete mode 100644 frappe/integrations/doctype/google_indexing/__init__.py delete mode 100644 frappe/integrations/doctype/google_indexing/google_indexing.js delete mode 100644 frappe/integrations/doctype/google_indexing/google_indexing.json rename frappe/{integrations/doctype/google_indexing => website/doctype/website_settings}/google_indexing.py (58%) rename frappe/{integrations/doctype/google_indexing/test_google_indexing.py => website/doctype/website_settings/test_website_settings.py} (79%) diff --git a/frappe/integrations/doctype/google_indexing/__init__.py b/frappe/integrations/doctype/google_indexing/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/integrations/doctype/google_indexing/google_indexing.js b/frappe/integrations/doctype/google_indexing/google_indexing.js deleted file mode 100644 index d742effd05..0000000000 --- a/frappe/integrations/doctype/google_indexing/google_indexing.js +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Google Indexing', { - refresh: function(frm) { - if (!frm.doc.enable) { - frm.dashboard.set_headline(__("To use Google API Indexing, enable {0}.", [`${__('Google Settings')}`])); - } - }, - - authorize_api_indexing_access: function(frm) { - let reauthorize = 0; - if(frm.doc.authorization_code) { - reauthorize = 1; - } - - frappe.call({ - method: "frappe.integrations.doctype.google_indexing.google_indexing.authorize_access", - args: { - "g_contact": frm.doc.name, - "reauthorize": reauthorize - }, - callback: function(r) { - if(!r.exc) { - frm.save(); - window.open(r.message.url); - } - } - }); - } -}); \ No newline at end of file diff --git a/frappe/integrations/doctype/google_indexing/google_indexing.json b/frappe/integrations/doctype/google_indexing/google_indexing.json deleted file mode 100644 index 00b544449d..0000000000 --- a/frappe/integrations/doctype/google_indexing/google_indexing.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "actions": [], - "creation": "2020-02-03 09:24:27.503449", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "enable_indexing", - "authorize_api_indexing_access", - "refresh_token", - "authorization_code" - ], - "fields": [ - { - "default": "0", - "fieldname": "enable_indexing", - "fieldtype": "Check", - "label": "Enable Indexing" - }, - { - "depends_on": "eval:!doc.__islocal && doc.enable_indexing", - "fieldname": "authorize_api_indexing_access", - "fieldtype": "Button", - "label": "Authorize API Indexing Access" - }, - { - "fieldname": "refresh_token", - "fieldtype": "Password", - "hidden": 1, - "label": "Refresh Token" - }, - { - "fieldname": "authorization_code", - "fieldtype": "Password", - "hidden": 1, - "label": "Authorization Code" - } - ], - "issingle": 1, - "links": [], - "modified": "2020-02-03 17:11:52.094597", - "modified_by": "Administrator", - "module": "Integrations", - "name": "Google Indexing", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/frappe/integrations/doctype/google_indexing/google_indexing.py b/frappe/website/doctype/website_settings/google_indexing.py similarity index 58% rename from frappe/integrations/doctype/google_indexing/google_indexing.py rename to frappe/website/doctype/website_settings/google_indexing.py index 2ca09b60f1..c674e66f6a 100644 --- a/frappe/integrations/doctype/google_indexing/google_indexing.py +++ b/frappe/website/doctype/website_settings/google_indexing.py @@ -7,7 +7,6 @@ import frappe import requests import googleapiclient.discovery import google.oauth2.credentials -import os from frappe import _ from googleapiclient.errors import HttpError @@ -18,94 +17,66 @@ from frappe.integrations.doctype.google_settings.google_settings import get_auth SCOPES = "https://www.googleapis.com/auth/indexing" -class GoogleIndexing(Document): - def validate(self): - if not frappe.db.get_single_value("Google Settings", "enable"): - frappe.throw(_("Enable Google API in Google Settings.")) - - def get_access_token(self): - google_settings = frappe.get_doc("Google Settings") - - if not google_settings.enable: - frappe.throw(_("Google Integration is disabled.")) - - if not self.refresh_token: - button_label = frappe.bold(_("Allow API Indexing Access")) - raise frappe.ValidationError(_("Click on {0} to generate Refresh Token.").format(button_label)) - - data = { - "client_id": google_settings.client_id, - "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), - "refresh_token": self.get_password(fieldname="refresh_token", raise_exception=False), - "grant_type": "refresh_token", - "scope": SCOPES - } - - try: - r = requests.post(get_auth_url(), data=data).json() - except requests.exceptions.HTTPError: - button_label = frappe.bold(_("Allow Google Drive Access")) - frappe.throw(_("Something went wrong during the token generation. Click on {0} to generate a new one.").format(button_label)) - - return r.get("access_token") @frappe.whitelist() def authorize_access(reauthorize=None): """ If no Authorization code get it from Google and then request for Refresh Token. - Google Contact Name is set to flags to set_value after Authorization Code is obtained. """ google_settings = frappe.get_doc("Google Settings") - google_indexing = frappe.get_doc("Google Indexing") + website_settings = frappe.get_doc("Website Settings") - redirect_uri = get_request_site_address(True) + "?cmd=frappe.integrations.doctype.google_indexing.google_indexing.google_callback" + redirect_uri = get_request_site_address(True) + "?cmd=frappe.website.doctype.website_settings.google_indexing.google_callback" - if not google_indexing.authorization_code or reauthorize: + if not website_settings.authorization_code or reauthorize: return get_authentication_url(client_id=google_settings.client_id, redirect_uri=redirect_uri) else: try: data = { - "code": google_indexing.authorization_code, + "code": website_settings.authorization_code, "client_id": google_settings.client_id, "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), "redirect_uri": redirect_uri, "grant_type": "authorization_code" } - r = requests.post(get_auth_url(), data=data).json() + res = requests.post(get_auth_url(), data=data).json() - if "refresh_token" in r: - frappe.db.set_value("Google Indexing", google_indexing.name, "refresh_token", r.get("refresh_token")) + if "refresh_token" in res: + frappe.db.set_value("Website Settings", website_settings.name, "refresh_token", res.get("refresh_token")) frappe.db.commit() frappe.local.response["type"] = "redirect" - frappe.local.response["location"] = "/desk#Form/{0}".format(quote("Google Indexing")) + frappe.local.response["location"] = "/desk#Form/{0}".format(quote("Website Settings")) frappe.msgprint(_("Google Indexing has been configured.")) except Exception as e: frappe.throw(e) def get_authentication_url(client_id, redirect_uri): + ''' Return authentication url with the client id and redirect uri ''' return { "url": "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&prompt=consent&client_id={}&include_granted_scopes=true&scope={}&redirect_uri={}".format(client_id, SCOPES, redirect_uri) } + @frappe.whitelist() def google_callback(code=None): """ Authorization code is sent to callback as per the API configuration """ - frappe.db.set_value("Google Indexing", None, "authorization_code", code) + frappe.db.set_value("Website Settings", None, "authorization_code", code) frappe.db.commit() authorize_access() + def get_google_indexing_object(): """ - Returns an object of Google Indexing Service + Returns an object of Website Settings Service """ google_settings = frappe.get_doc("Google Settings") - account = frappe.get_doc("Google Indexing") + account = frappe.get_doc("Website Settings") credentials_dict = { "token": account.get_access_token(), @@ -121,13 +92,16 @@ def get_google_indexing_object(): return google_indexing + def publish_site(url, operation_type="URL_UPDATED"): + ''' Send an update/remove url request ''' + google_indexing = get_google_indexing_object() - body = { + body = { "url": url, "type": operation_type } try: google_indexing.urlNotifications().publish(body=body, x__xgafv='2').execute() except HttpError as e: - frappe.log_error(message=e, title='API Indexing Issue') \ No newline at end of file + frappe.log_error(message=e, title='API Indexing Issue') diff --git a/frappe/integrations/doctype/google_indexing/test_google_indexing.py b/frappe/website/doctype/website_settings/test_website_settings.py similarity index 79% rename from frappe/integrations/doctype/google_indexing/test_google_indexing.py rename to frappe/website/doctype/website_settings/test_website_settings.py index 6bf7dc9d3e..9eca957713 100644 --- a/frappe/integrations/doctype/google_indexing/test_google_indexing.py +++ b/frappe/website/doctype/website_settings/test_website_settings.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe import unittest -class TestGoogleIndexing(unittest.TestCase): +class TestWebsiteSettings(unittest.TestCase): pass diff --git a/frappe/website/doctype/website_settings/website_settings.js b/frappe/website/doctype/website_settings/website_settings.js index fd2d490c54..6afffb3bce 100644 --- a/frappe/website/doctype/website_settings/website_settings.js +++ b/frappe/website/doctype/website_settings/website_settings.js @@ -35,6 +35,27 @@ frappe.ui.form.on('Website Settings', { } }, + authorize_api_indexing_access: function(frm) { + let reauthorize = 0; + if(frm.doc.authorization_code) { + reauthorize = 1; + } + + frappe.call({ + method: "frappe.website.doctype.website_settings.google_indexing.authorize_access", + args: { + "g_indexing": frm.doc.name, + "reauthorize": reauthorize + }, + callback: function(r) { + if(!r.exc) { + frm.save(); + window.open(r.message.url); + } + } + }); + }, + set_parent_options: function(frm, doctype, name) { var item = frappe.get_doc(doctype, name); if(item.parentfield === "top_bar_items") { diff --git a/frappe/website/doctype/website_settings/website_settings.json b/frappe/website/doctype/website_settings/website_settings.json index 025f7df750..2d0ac1f4e8 100644 --- a/frappe/website/doctype/website_settings/website_settings.json +++ b/frappe/website/doctype/website_settings/website_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-04-30 12:58:46", "doctype": "DocType", "document_type": "Other", @@ -28,8 +29,12 @@ "footer_items", "hide_footer_signup", "integrations", - "google_analytics_id", + "enable_google_indexing", + "authorize_api_indexing_access", + "indexing_refresh_token", + "indexing_authorization_code", "column_break_17", + "google_analytics_id", "misc_section", "subdomain", "disable_signup", @@ -287,13 +292,38 @@ "fieldtype": "Table", "label": "Route Redirects", "options": "Website Route Redirect" + }, + { + "default": "0", + "fieldname": "enable_google_indexing", + "fieldtype": "Check", + "label": "Enable Google Indexing" + }, + { + "fieldname": "indexing_refresh_token", + "fieldtype": "Password", + "hidden": 1, + "label": "Indexing Refresh Token" + }, + { + "fieldname": "indexing_authorization_code", + "fieldtype": "Password", + "label": "Indexing Authorization Code", + "read_only": 1 + }, + { + "depends_on": "eval: doc.enable_google_indexing", + "fieldname": "authorize_api_indexing_access", + "fieldtype": "Button", + "label": "Authorize API Indexing Access" } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, + "links": [], "max_attachments": 10, - "modified": "2019-06-12 17:16:37.452872", + "modified": "2020-02-11 00:44:26.731674", "modified_by": "Administrator", "module": "Website", "name": "Website Settings", diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index 64558e751a..b4cf3c37a3 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -2,6 +2,7 @@ # MIT License. See license.txt from __future__ import unicode_literals +import requests import frappe from frappe import _ from frappe.utils import get_request_site_address, encode @@ -9,12 +10,16 @@ from frappe.model.document import Document from six.moves.urllib.parse import quote from frappe.website.router import resolve_route from frappe.website.doctype.website_theme.website_theme import add_website_theme +from frappe.integrations.doctype.google_settings.google_settings import get_auth_url + +INDEXING_SCOPES = "https://www.googleapis.com/auth/indexing" class WebsiteSettings(Document): def validate(self): self.validate_top_bar_items() self.validate_footer_items() self.validate_home_page() + self.validate_google_settings() def validate_home_page(self): if frappe.flags.in_install: @@ -53,6 +58,10 @@ class WebsiteSettings(Document): frappe.throw(_("{0} in row {1} cannot have both URL and child items").format(footer_item.parent_label, footer_item.idx)) + def validate_google_settings(self): + if not frappe.db.get_single_value("Google Settings", "enable"): + frappe.throw(_("Enable Google API in Google Settings.")) + def on_update(self): self.clear_cache() @@ -67,6 +76,33 @@ class WebsiteSettings(Document): # clears role based home pages frappe.clear_cache() + def get_access_token(self): + google_settings = frappe.get_doc("Google Settings") + + if not google_settings.enable: + frappe.throw(_("Google Integration is disabled.")) + + if not self.refresh_token: + button_label = frappe.bold(_("Allow API Indexing Access")) + raise frappe.ValidationError(_("Click on {0} to generate Refresh Token.").format(button_label)) + + data = { + "client_id": google_settings.client_id, + "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), + "refresh_token": self.get_password(fieldname="indexing_refresh_token", raise_exception=False), + "grant_type": "refresh_token", + "scope": INDEXING_SCOPES + } + + try: + res = requests.post(get_auth_url(), data=data).json() + except requests.exceptions.HTTPError: + button_label = frappe.bold(_("Allow Google Indexing Access")) + frappe.throw(_("Something went wrong during the token generation. Click on {0} to generate a new one.").format(button_label)) + + return res.get("access_token") + + def get_website_settings(): hooks = frappe.get_hooks() context = frappe._dict({ diff --git a/frappe/website/website_generator.py b/frappe/website/website_generator.py index 36946964db..017af714fe 100644 --- a/frappe/website/website_generator.py +++ b/frappe/website/website_generator.py @@ -82,17 +82,11 @@ class WebsiteGenerator(Document): pass def on_update(self): - if frappe.db.get_single_value('Google Indexing', 'enable_indexing'): - url = frappe.utils.get_url(self.route) - frappe.enqueue('frappe.integrations.doctype.google_indexing.google_indexing.publish_site', \ - url=url) + self.send_indexing_request() def on_trash(self): self.clear_cache() - if frappe.db.get_single_value('Google Indexing', 'enable_indexing'): - url = frappe.utils.get_url(self.route) - frappe.enqueue('frappe.integrations.doctype.google_indexing.google_indexing.publish_site', \ - url=url, operation_type='URL_DELETED') + self.send_indexing_request('URL_DELETED') def is_website_published(self): """Return true if published in website""" @@ -126,3 +120,13 @@ class WebsiteGenerator(Document): route.page_title = self.get(self.get_title_field()) return route + + def send_indexing_request(self, operation_type='URL_UPDATED'): + ''' Send indexing request on update/trash operation ''' + + if frappe.db.get_single_value('Google Indexing', 'enable_indexing')\ + and self.meta.is_published_field and self.meta.allow_guest_to_view: + + url = frappe.utils.get_url(self.route) + frappe.enqueue('frappe.integrations.doctype.google_indexing.google_indexing.publish_site', \ + url=url, operation_type=operation_type) \ No newline at end of file From 3633b452adf8387438c2b1b46a5e8ca738b346b7 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 10 Feb 2020 23:52:52 +0530 Subject: [PATCH 024/157] fix: make field names more descriptive --- .../doctype/website_settings/google_indexing.py | 10 +++++----- .../doctype/website_settings/website_settings.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frappe/website/doctype/website_settings/google_indexing.py b/frappe/website/doctype/website_settings/google_indexing.py index c674e66f6a..b6ddc043f9 100644 --- a/frappe/website/doctype/website_settings/google_indexing.py +++ b/frappe/website/doctype/website_settings/google_indexing.py @@ -29,12 +29,12 @@ def authorize_access(reauthorize=None): redirect_uri = get_request_site_address(True) + "?cmd=frappe.website.doctype.website_settings.google_indexing.google_callback" - if not website_settings.authorization_code or reauthorize: + if not website_settings.indexing_authorization_code or reauthorize: return get_authentication_url(client_id=google_settings.client_id, redirect_uri=redirect_uri) else: try: data = { - "code": website_settings.authorization_code, + "code": website_settings.indexing_authorization_code, "client_id": google_settings.client_id, "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), "redirect_uri": redirect_uri, @@ -43,7 +43,7 @@ def authorize_access(reauthorize=None): res = requests.post(get_auth_url(), data=data).json() if "refresh_token" in res: - frappe.db.set_value("Website Settings", website_settings.name, "refresh_token", res.get("refresh_token")) + frappe.db.set_value("Website Settings", website_settings.name, "indexing_refresh_token", res.get("refresh_token")) frappe.db.commit() frappe.local.response["type"] = "redirect" @@ -65,7 +65,7 @@ def google_callback(code=None): """ Authorization code is sent to callback as per the API configuration """ - frappe.db.set_value("Website Settings", None, "authorization_code", code) + frappe.db.set_value("Website Settings", None, "indexing_authorization_code", code) frappe.db.commit() authorize_access() @@ -80,7 +80,7 @@ def get_google_indexing_object(): credentials_dict = { "token": account.get_access_token(), - "refresh_token": account.get_password(fieldname="refresh_token", raise_exception=False), + "refresh_token": account.get_password(fieldname="indexing_refresh_token", raise_exception=False), "token_uri": get_auth_url(), "client_id": google_settings.client_id, "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index b4cf3c37a3..20e0fa67a2 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -82,7 +82,7 @@ class WebsiteSettings(Document): if not google_settings.enable: frappe.throw(_("Google Integration is disabled.")) - if not self.refresh_token: + if not self.indexing_refresh_token: button_label = frappe.bold(_("Allow API Indexing Access")) raise frappe.ValidationError(_("Click on {0} to generate Refresh Token.").format(button_label)) From 6dff5d383192f2480c8e6834c34869be168dec21 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 11 Feb 2020 09:58:43 +0530 Subject: [PATCH 025/157] fix: validate google settings --- .../website/doctype/website_settings/website_settings.json | 6 ++++-- frappe/website/doctype/website_settings/website_settings.py | 2 +- frappe/website/website_generator.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frappe/website/doctype/website_settings/website_settings.json b/frappe/website/doctype/website_settings/website_settings.json index 2d0ac1f4e8..b14e8c246d 100644 --- a/frappe/website/doctype/website_settings/website_settings.json +++ b/frappe/website/doctype/website_settings/website_settings.json @@ -303,11 +303,13 @@ "fieldname": "indexing_refresh_token", "fieldtype": "Password", "hidden": 1, - "label": "Indexing Refresh Token" + "label": "Indexing Refresh Token", + "read_only": 1 }, { "fieldname": "indexing_authorization_code", "fieldtype": "Password", + "hidden": 1, "label": "Indexing Authorization Code", "read_only": 1 }, @@ -323,7 +325,7 @@ "issingle": 1, "links": [], "max_attachments": 10, - "modified": "2020-02-11 00:44:26.731674", + "modified": "2020-02-11 09:53:08.872362", "modified_by": "Administrator", "module": "Website", "name": "Website Settings", diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index 20e0fa67a2..4356b1aa82 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -59,7 +59,7 @@ class WebsiteSettings(Document): footer_item.idx)) def validate_google_settings(self): - if not frappe.db.get_single_value("Google Settings", "enable"): + if self.enable_google_indexing and not frappe.db.get_single_value("Google Settings", "enable"): frappe.throw(_("Enable Google API in Google Settings.")) def on_update(self): diff --git a/frappe/website/website_generator.py b/frappe/website/website_generator.py index 017af714fe..3bec631131 100644 --- a/frappe/website/website_generator.py +++ b/frappe/website/website_generator.py @@ -124,9 +124,9 @@ class WebsiteGenerator(Document): def send_indexing_request(self, operation_type='URL_UPDATED'): ''' Send indexing request on update/trash operation ''' - if frappe.db.get_single_value('Google Indexing', 'enable_indexing')\ + if frappe.db.get_single_value('Website Settings', 'enable_google_indexing')\ and self.meta.is_published_field and self.meta.allow_guest_to_view: url = frappe.utils.get_url(self.route) - frappe.enqueue('frappe.integrations.doctype.google_indexing.google_indexing.publish_site', \ + frappe.enqueue('frappe.website.doctype.website_settings.google_indexing.publish_site', \ url=url, operation_type=operation_type) \ No newline at end of file From 12621d4a23ecf2ef15ec573f8bac75e08034802f Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 11 Feb 2020 12:22:30 +0530 Subject: [PATCH 026/157] style: formatting fixes --- frappe/website/doctype/web_page/web_page.py | 4 ++-- frappe/website/doctype/website_settings/website_settings.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/website/doctype/web_page/web_page.py b/frappe/website/doctype/web_page/web_page.py index d6cb7cccb7..ee0c4de074 100644 --- a/frappe/website/doctype/web_page/web_page.py +++ b/frappe/website/doctype/web_page/web_page.py @@ -29,10 +29,10 @@ class WebPage(WebsiteGenerator): return self.title def on_update(self): - super(BlogPost, self).on_update() + super(WebPage, self).on_update() def on_trash(self): - super(BlogPost, self).on_trash() + super(WebPage, self).on_trash() def get_context(self, context): context.main_section = get_html_content_based_on_type(self, 'main_section', self.content_type) diff --git a/frappe/website/doctype/website_settings/website_settings.js b/frappe/website/doctype/website_settings/website_settings.js index 6afffb3bce..38e1ff993a 100644 --- a/frappe/website/doctype/website_settings/website_settings.js +++ b/frappe/website/doctype/website_settings/website_settings.js @@ -37,7 +37,7 @@ frappe.ui.form.on('Website Settings', { authorize_api_indexing_access: function(frm) { let reauthorize = 0; - if(frm.doc.authorization_code) { + if (frm.doc.authorization_code) { reauthorize = 1; } @@ -48,7 +48,7 @@ frappe.ui.form.on('Website Settings', { "reauthorize": reauthorize }, callback: function(r) { - if(!r.exc) { + if (!r.exc) { frm.save(); window.open(r.message.url); } From c1c5a4e363134b5a1ab600b546da1c1cfe1b50c2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 18 Feb 2020 22:26:06 +0530 Subject: [PATCH 027/157] fix: minor changes --- frappe/website/doctype/website_settings/google_indexing.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/website/doctype/website_settings/google_indexing.py b/frappe/website/doctype/website_settings/google_indexing.py index b6ddc043f9..dded822c9d 100644 --- a/frappe/website/doctype/website_settings/google_indexing.py +++ b/frappe/website/doctype/website_settings/google_indexing.py @@ -11,7 +11,6 @@ import google.oauth2.credentials from frappe import _ from googleapiclient.errors import HttpError from frappe.utils import get_request_site_address -from frappe.model.document import Document from six.moves.urllib.parse import quote from frappe.integrations.doctype.google_settings.google_settings import get_auth_url @@ -73,7 +72,7 @@ def google_callback(code=None): def get_google_indexing_object(): """ - Returns an object of Website Settings Service + Returns an object of Google Indexing object """ google_settings = frappe.get_doc("Google Settings") account = frappe.get_doc("Website Settings") From 6157719a7d24f5ac7befdd8c4d279bb59873e697 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 18 Feb 2020 23:37:32 +0530 Subject: [PATCH 028/157] feat(newsletter): add an option for hourly schedule send --- .../email/doctype/newsletter/newsletter.json | 20 ++++++++++++++++--- frappe/email/doctype/newsletter/newsletter.py | 10 +++++++++- frappe/hooks.py | 3 ++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/frappe/email/doctype/newsletter/newsletter.json b/frappe/email/doctype/newsletter/newsletter.json index 1ef51027ed..fcd2d5bfe2 100644 --- a/frappe/email/doctype/newsletter/newsletter.json +++ b/frappe/email/doctype/newsletter/newsletter.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_rename": 1, "creation": "2013-01-10 16:34:31", "description": "Create and Send Newsletters", @@ -6,9 +7,11 @@ "document_type": "Other", "engine": "InnoDB", "field_order": [ + "send_from", + "column_break_2", + "schedule_send", "recipients", "email_group", - "send_from", "email_sent", "newsletter_content", "subject", @@ -41,7 +44,7 @@ "default": "0", "fieldname": "email_sent", "fieldtype": "Check", - "label": "Email Sent?", + "label": "Email Sent", "no_copy": 1, "read_only": 1 }, @@ -115,14 +118,25 @@ "fieldname": "recipients", "fieldtype": "Section Break", "label": "Recipients" + }, + { + "depends_on": "eval: !doc.email_sent", + "fieldname": "schedule_send", + "fieldtype": "Datetime", + "label": "Schedule Send" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" } ], "has_web_view": 1, "icon": "fa fa-envelope", "idx": 1, "is_published_field": "published", + "links": [], "max_attachments": 3, - "modified": "2019-09-06 22:15:55.471254", + "modified": "2020-02-18 09:51:41.090261", "modified_by": "Administrator", "module": "Email", "name": "Newsletter", diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index b0d1756643..c7134eb9b5 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -11,7 +11,7 @@ from frappe.utils.verified_command import get_signed_params, verify_request from frappe.utils.background_jobs import enqueue from frappe.email.queue import send from frappe.email.doctype.email_group.email_group import add_subscribers -from frappe.utils import parse_addr +from frappe.utils import parse_addr, now_datetime from frappe.utils import validate_email_address @@ -250,3 +250,11 @@ def get_newsletter_list(doctype, txt, filters, limit_start, limit_page_length=20 '''.format(','.join(['%s'] * len(email_group_list)), limit_page_length, limit_start), email_group_list, as_dict=1) +def send_scheduled_email(): + ''' Send scheduled newsletter to the recipients ''' + scheduled_newsletter = frappe.get_all('Newsletter', filters = { + 'schedule_send': ('<=', now_datetime()), + 'email_sent': 0 + }, fields = ['name']) + for newsletter in scheduled_newsletter: + send_newsletter(newsletter.name) \ No newline at end of file diff --git a/frappe/hooks.py b/frappe/hooks.py index a132eb69d5..794ef7ef38 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -183,7 +183,8 @@ scheduler_events = { "frappe.desk.page.backups.backups.delete_downloadable_backups", "frappe.deferred_insert.save_to_db", "frappe.desk.form.document_follow.send_hourly_updates", - "frappe.integrations.doctype.google_calendar.google_calendar.sync" + "frappe.integrations.doctype.google_calendar.google_calendar.sync", + "frappe.email.doctype.newsletter.newsletter.send_scheduled_email" ], "daily": [ "frappe.email.queue.clear_outbox", From 7e618550f516997074c83e1edb21ec09fc1de728 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 21 Feb 2020 11:39:46 +0530 Subject: [PATCH 029/157] feat(newsletter): add confirmation email, welcome email for email group --- .../doctype/email_group/email_group.json | 170 +++++++----------- frappe/email/doctype/newsletter/newsletter.js | 31 +++- frappe/email/doctype/newsletter/newsletter.py | 55 ++++-- 3 files changed, 127 insertions(+), 129 deletions(-) diff --git a/frappe/email/doctype/email_group/email_group.json b/frappe/email/doctype/email_group/email_group.json index d02cead0b0..0d784d409a 100644 --- a/frappe/email/doctype/email_group/email_group.json +++ b/frappe/email/doctype/email_group/email_group.json @@ -1,120 +1,70 @@ { - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "field:title", - "beta": 0, - "creation": "2015-03-18 06:08:32.729800", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "autoname": "field:title", + "creation": "2015-03-18 06:08:32.729800", + "doctype": "DocType", + "document_type": "Setup", + "field_order": [ + "title", + "total_subscribers", + "confirmation_email_template", + "welcome_email_template" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "no_copy": 1, + "reqd": 1, + "unique": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "total_subscribers", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Total Subscribers", - "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 + "default": "0", + "fieldname": "total_subscribers", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Total Subscribers", + "read_only": 1 + }, + { + "fieldname": "confirmation_email_template", + "fieldtype": "Link", + "label": "Confirmation Email Template", + "options": "Email Template" + }, + { + "fieldname": "welcome_email_template", + "fieldtype": "Link", + "label": "Welcome Email Template", + "options": "Email Template" } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-02-27 19:01:17.203845", - "modified_by": "Administrator", - "module": "Email", - "name": "Email Group", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-02-21 14:12:48.884738", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Group", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Newsletter Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Newsletter Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/email/doctype/newsletter/newsletter.js b/frappe/email/doctype/newsletter/newsletter.js index 7bf0ae6b9a..f25763d79a 100644 --- a/frappe/email/doctype/newsletter/newsletter.js +++ b/frappe/email/doctype/newsletter/newsletter.js @@ -4,17 +4,40 @@ frappe.ui.form.on('Newsletter', { refresh(frm) { let doc = frm.doc; - if(!doc.__islocal && !cint(doc.email_sent) && !doc.__unsaved + let today = new Date(); + if (!doc.__islocal && !cint(doc.email_sent) && !doc.__unsaved && in_list(frappe.boot.user.can_write, doc.doctype)) { - frm.add_custom_button(__('Send'), function() { - frm.call('send_emails').then(() => { - frm.refresh(); + frm.add_custom_button(__('Send Now'), function() { + frappe.confirm(__("Do you really want to send this email?"), function() { + frm.call('send_emails').then(() => { + frm.refresh(); + }); }); }, "fa fa-play", "btn-success"); } frm.events.setup_dashboard(frm); + // setting datepicker options to set min date & min time + frm.get_field('schedule_send').$input.datepicker({ + maxMinutes: 0, + minDate: today, + timeFormat: 'hh', + onSelect: function (fd, d, picker) { + if (!d) return; + var date = d.getDate(); + if (date === today) { + picker.update({ + minHours: (today.getHours() + 1) + }); + } else { + picker.update({ + minHours: 0 + }); + } + } + }); + if(doc.__islocal && !doc.send_from) { let { fullname, email } = frappe.user_info(doc.owner); frm.set_value('send_from', `${fullname} <${email}>`); diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index c7134eb9b5..819ab96546 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -160,26 +160,40 @@ def create_lead(email_id): @frappe.whitelist(allow_guest=True) -def subscribe(email): +def subscribe(email, email_group=_('Website')): url = frappe.utils.get_url("/api/method/frappe.email.doctype.newsletter.newsletter.confirm_subscription") +\ - "?" + get_signed_params({"email": email}) + "?" + get_signed_params({"email": email, "email_group": email_group}) - messages = ( - _("Thank you for your interest in subscribing to our updates"), - _("Please verify your Email Address"), - url, - _("Click here to verify") - ) + confirmation_template = frappe.db.get_value('Email Group', email_group, ['confirmation_email']) - content = """ -

{0}. {1}.

-

{3}

- """ + if confirmation_email: + args = frappe._dict(dict( + user_email=email, + confirmation_url=url, + email_group=email_group + )) - frappe.sendmail(email, subject=_("Confirm Your Email"), content=content.format(*messages)) + email_template = frappe.get_doc("Email Template", welcome_email) + content = frappe.render_template(email_template.response, args) + + + if not message: + messages = ( + _("Thank you for your interest in subscribing to our updates"), + _("Please verify your Email Address"), + url, + _("Click here to verify") + ) + + content = """ +

{0}. {1}.

+

{3}

+ """.format(*messages) + + frappe.sendmail(email, subject=email_template.subject or _("Confirm Your Email"), content=content) @frappe.whitelist(allow_guest=True) -def confirm_subscription(email): +def confirm_subscription(email, email_group=_('Website')): if not verify_request(): return @@ -189,12 +203,23 @@ def confirm_subscription(email): "title": _("Website") }).insert(ignore_permissions=True) - frappe.flags.ignore_permissions = True add_subscribers(_("Website"), email) frappe.db.commit() + welcome_email = frappe.db.get_value('Email Group', _('Website'), 'welcome_email') + + if welcome_email: + args = frappe._dict(dict( + user_email=email, + email_group=email_group + )) + + email_template = frappe.get_doc("Email Template", welcome_email) + message = frappe.render_template(email_template.response, args) + frappe.sendmail(email, subject=email_template.subject, message=message) + frappe.respond_as_web_page(_("Confirmed"), _("{0} has been successfully added to the Email Group.").format(email), indicator_color='green') From 5afbad89306b6efed8c0fc4e0a837eb698cf7ae2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 21 Feb 2020 21:17:26 +0530 Subject: [PATCH 030/157] fix: add description to indexing check --- .../doctype/website_settings/website_settings.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frappe/website/doctype/website_settings/website_settings.json b/frappe/website/doctype/website_settings/website_settings.json index b14e8c246d..39ffa2329f 100644 --- a/frappe/website/doctype/website_settings/website_settings.json +++ b/frappe/website/doctype/website_settings/website_settings.json @@ -186,7 +186,7 @@ "collapsible": 1, "fieldname": "integrations", "fieldtype": "Section Break", - "label": "Integrations" + "label": "Google Integrations" }, { "description": "Add Google Analytics ID: eg. UA-89XXX57-1. Please search help on Google Analytics for more information.", @@ -295,20 +295,21 @@ }, { "default": "0", + "description": "To use Google Indexing, enable Google Settings.", "fieldname": "enable_google_indexing", "fieldtype": "Check", "label": "Enable Google Indexing" }, { "fieldname": "indexing_refresh_token", - "fieldtype": "Password", + "fieldtype": "Data", "hidden": 1, "label": "Indexing Refresh Token", "read_only": 1 }, { "fieldname": "indexing_authorization_code", - "fieldtype": "Password", + "fieldtype": "Data", "hidden": 1, "label": "Indexing Authorization Code", "read_only": 1 @@ -325,7 +326,7 @@ "issingle": 1, "links": [], "max_attachments": 10, - "modified": "2020-02-11 09:53:08.872362", + "modified": "2020-02-21 16:46:59.947403", "modified_by": "Administrator", "module": "Website", "name": "Website Settings", From 5bc6da78c16858fecec14d73a5b802c1804ab50a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 27 Feb 2020 19:18:36 +0530 Subject: [PATCH 031/157] fix: send email after adding subscriber --- .../email/doctype/email_group/email_group.py | 22 +++++++++++- frappe/email/doctype/newsletter/newsletter.py | 36 +++++++------------ 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index 70210faf37..359c42171e 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -66,6 +66,11 @@ def import_from(name, doctype): def add_subscribers(name, email_list): if not isinstance(email_list, (list, tuple)): email_list = email_list.replace(",", "\n").split("\n") + + template = frappe.db.get_value('Email Group', name, 'welcome_email_template') + if template: + welcome_email = frappe.get_doc("Email Template", template) + count = 0 for email in email_list: email = email.strip() @@ -78,7 +83,9 @@ def add_subscribers(name, email_list): "doctype": "Email Group Member", "email_group": name, "email": parsed_email - }).insert(ignore_permissions = frappe.flags.ignore_permissions) + }).insert(ignore_permissions=frappe.flags.ignore_permissions) + + send_welcome_email(welcome_email, parsed_email, name) count += 1 else: @@ -90,3 +97,16 @@ def add_subscribers(name, email_list): return frappe.get_doc("Email Group", name).update_total_subscribers() +def send_welcome_email(welcome_email, email, email_group): + ''' Send welcome email for the subscribers of a given email group ''' + + if not welcome_email: + return + + args = dict( + email=email, + email_group=email_group + ) + + message = frappe.render_template(welcome_email.response, args) + frappe.sendmail(email, subject=welcome_email.subject, message=message) \ No newline at end of file diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index 819ab96546..4c9de88aad 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -164,20 +164,20 @@ def subscribe(email, email_group=_('Website')): url = frappe.utils.get_url("/api/method/frappe.email.doctype.newsletter.newsletter.confirm_subscription") +\ "?" + get_signed_params({"email": email, "email_group": email_group}) - confirmation_template = frappe.db.get_value('Email Group', email_group, ['confirmation_email']) + email_template = frappe.db.get_value('Email Group', email_group, ['confirmation_email_template']) - if confirmation_email: - args = frappe._dict(dict( - user_email=email, + content='' + if email_template: + args = dict( + email=email, confirmation_url=url, email_group=email_group - )) + ) - email_template = frappe.get_doc("Email Template", welcome_email) + email_template = frappe.get_doc("Email Template", email_template) content = frappe.render_template(email_template.response, args) - - if not message: + if not content: messages = ( _("Thank you for your interest in subscribing to our updates"), _("Please verify your Email Address"), @@ -190,36 +190,24 @@ def subscribe(email, email_group=_('Website')):

{3}

""".format(*messages) - frappe.sendmail(email, subject=email_template.subject or _("Confirm Your Email"), content=content) + frappe.sendmail(email, subject=getattr('email_template', 'subject', '') or _("Confirm Your Email"), content=content) @frappe.whitelist(allow_guest=True) def confirm_subscription(email, email_group=_('Website')): if not verify_request(): return - if not frappe.db.exists("Email Group", _("Website")): + if not frappe.db.exists("Email Group", email_group): frappe.get_doc({ "doctype": "Email Group", - "title": _("Website") + "title": email_group }).insert(ignore_permissions=True) frappe.flags.ignore_permissions = True - add_subscribers(_("Website"), email) + add_subscribers(email_group, email) frappe.db.commit() - welcome_email = frappe.db.get_value('Email Group', _('Website'), 'welcome_email') - - if welcome_email: - args = frappe._dict(dict( - user_email=email, - email_group=email_group - )) - - email_template = frappe.get_doc("Email Template", welcome_email) - message = frappe.render_template(email_template.response, args) - frappe.sendmail(email, subject=email_template.subject, message=message) - frappe.respond_as_web_page(_("Confirmed"), _("{0} has been successfully added to the Email Group.").format(email), indicator_color='green') From 2696eca30a07f845463740184cc7f9eba7ef3959 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 2 Mar 2020 11:39:22 +0530 Subject: [PATCH 032/157] fix: make the schedule send field read only on sending newsletter --- frappe/email/doctype/newsletter/newsletter.js | 20 ++++++++++++------- .../email/doctype/newsletter/newsletter.json | 3 +-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/frappe/email/doctype/newsletter/newsletter.js b/frappe/email/doctype/newsletter/newsletter.js index f25763d79a..76933d1cc4 100644 --- a/frappe/email/doctype/newsletter/newsletter.js +++ b/frappe/email/doctype/newsletter/newsletter.js @@ -4,20 +4,31 @@ frappe.ui.form.on('Newsletter', { refresh(frm) { let doc = frm.doc; - let today = new Date(); if (!doc.__islocal && !cint(doc.email_sent) && !doc.__unsaved && in_list(frappe.boot.user.can_write, doc.doctype)) { frm.add_custom_button(__('Send Now'), function() { frappe.confirm(__("Do you really want to send this email?"), function() { frm.call('send_emails').then(() => { - frm.refresh(); + frm.set_value('schedule_send', new Date()); + frm.save(); }); }); }, "fa fa-play", "btn-success"); } + if (!doc.__islocal && cint(doc.email_sent)) { + frm.set_df_property('schedule_send', "read_only", 1); + } frm.events.setup_dashboard(frm); + if (doc.__islocal && !doc.send_from) { + let { fullname, email } = frappe.user_info(doc.owner); + frm.set_value('send_from', `${fullname} <${email}>`); + } + }, + + schedule_send(frm) { + let today = new Date(); // setting datepicker options to set min date & min time frm.get_field('schedule_send').$input.datepicker({ maxMinutes: 0, @@ -37,11 +48,6 @@ frappe.ui.form.on('Newsletter', { } } }); - - if(doc.__islocal && !doc.send_from) { - let { fullname, email } = frappe.user_info(doc.owner); - frm.set_value('send_from', `${fullname} <${email}>`); - } }, setup_dashboard(frm) { diff --git a/frappe/email/doctype/newsletter/newsletter.json b/frappe/email/doctype/newsletter/newsletter.json index fcd2d5bfe2..719d51c176 100644 --- a/frappe/email/doctype/newsletter/newsletter.json +++ b/frappe/email/doctype/newsletter/newsletter.json @@ -120,7 +120,6 @@ "label": "Recipients" }, { - "depends_on": "eval: !doc.email_sent", "fieldname": "schedule_send", "fieldtype": "Datetime", "label": "Schedule Send" @@ -136,7 +135,7 @@ "is_published_field": "published", "links": [], "max_attachments": 3, - "modified": "2020-02-18 09:51:41.090261", + "modified": "2020-03-02 06:26:51.622521", "modified_by": "Administrator", "module": "Email", "name": "Newsletter", From 91f023edbe1bd5e0e4b6711768a2ad9fdb71f440 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 2 Mar 2020 16:06:03 +0530 Subject: [PATCH 033/157] feat: include custom JS and CSS files in web form --- frappe/utils/boilerplate.py | 4 +++ frappe/website/doctype/web_form/web_form.py | 40 ++++++++++++++------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index b81d802a07..e65fa44253 100755 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -151,6 +151,10 @@ app_license = "{app_license}" # web_include_css = "/assets/{app_name}/css/{app_name}.css" # web_include_js = "/assets/{app_name}/js/{app_name}.js" +# include js, css files in header of web form +# webform_include_js = {"doctype": "public/js/doctype.js"} +# webform_include_css = {"doctype": "public/css/doctype.css"} + # include js in page # page_js = {{"page" : "public/js/file.js"}} diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index ae2c27c420..a188503ea3 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -2,18 +2,23 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, json, os -from frappe.website.website_generator import WebsiteGenerator + +import json +import os + +from six import iteritems +from six.moves.urllib.parse import urlencode + +import frappe from frappe import _, scrub +from frappe.core.doctype.file.file import get_max_file_size, remove_file_by_url +from frappe.custom.doctype.customize_form.customize_form import docfield_properties +from frappe.desk.form.meta import get_code_files_via_hooks +from frappe.integrations.utils import get_payment_gateway_controller +from frappe.modules.utils import export_module_json, get_doc_module from frappe.utils import cstr from frappe.website.utils import get_comment_list -from frappe.custom.doctype.customize_form.customize_form import docfield_properties -from frappe.core.doctype.file.file import get_max_file_size -from frappe.core.doctype.file.file import remove_file_by_url -from frappe.modules.utils import export_module_json, get_doc_module -from six.moves.urllib.parse import urlencode -from frappe.integrations.utils import get_payment_gateway_controller -from six import iteritems +from frappe.website.website_generator import WebsiteGenerator class WebForm(WebsiteGenerator): @@ -237,11 +242,23 @@ def get_context(context): js_path = os.path.join(os.path.dirname(self.web_form_module.__file__), scrub(self.name) + '.js') if os.path.exists(js_path): - context.script = frappe.render_template(open(js_path, 'r').read(), context) + script = frappe.render_template(open(js_path, 'r').read(), context) + + for path in get_code_files_via_hooks("webform_include_js", context.doc_type): + custom_js = frappe.render_template(open(path, 'r').read(), context) + script = "\n\n".join([script, custom_js]) + + context.script = script css_path = os.path.join(os.path.dirname(self.web_form_module.__file__), scrub(self.name) + '.css') if os.path.exists(css_path): - context.style = open(css_path, 'r').read() + style = open(css_path, 'r').read() + + for path in get_code_files_via_hooks("webform_include_css", context.doc_type): + custom_css = open(path, 'r').read() + style = "\n\n".join([style, custom_css]) + + context.style = style def get_layout(self): layout = [] @@ -589,4 +606,3 @@ def get_link_options(web_form_name, doctype, allow_read_on_all_link_options=Fals else: raise frappe.PermissionError('Not Allowed, {0}'.format(doctype)) - From 4e30fecb5f02f1af3670a78c5eb7450b69657f61 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 5 Mar 2020 10:53:09 +0530 Subject: [PATCH 034/157] feat: added prepared report user permission --- .../doctype/prepared_report/prepared_report.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/prepared_report/prepared_report.json b/frappe/core/doctype/prepared_report/prepared_report.json index ab6650d9e3..4663dcb463 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.json +++ b/frappe/core/doctype/prepared_report/prepared_report.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "REP.#####", "creation": "2018-06-25 18:39:11.152960", "doctype": "DocType", @@ -101,7 +102,8 @@ } ], "in_create": 1, - "modified": "2019-09-18 04:00:55.644257", + "links": [], + "modified": "2020-03-05 10:52:56.598365", "modified_by": "Administrator", "module": "Core", "name": "Prepared Report", @@ -118,6 +120,15 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Prepared Report User", + "share": 1 } ], "quick_entry": 1, From a88a17e0b8e9e85b08a6056998627f0081bcb50a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 5 Mar 2020 10:54:21 +0530 Subject: [PATCH 035/157] feat: added permission condition for list --- .../doctype/prepared_report/prepared_report.py | 17 +++++++++++++++++ frappe/hooks.py | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index bc9a1fcdcd..0d1f43c51f 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -98,3 +98,20 @@ def download_attachment(dn): attached_file = frappe.get_doc("File", attachment.name) frappe.local.response.filecontent = gzip_decompress(attached_file.get_content()) frappe.local.response.type = "binary" + + +def get_permission_query_condition(user): + if not user: user = frappe.session.user + if user == "Administrator": + return None + + from frappe.utils.user import UserPermissions + user = UserPermissions(user) + + if "System Manager" in user.roles: + return None + + reports = [ '"%s"'%report for report in user.get_all_reports().keys() ] + + return """`tabPrepared Report`.ref_report_doctype in ({reports})"""\ + .format(reports=','.join(reports)) diff --git a/frappe/hooks.py b/frappe/hooks.py index a132eb69d5..1d27685de6 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -94,7 +94,8 @@ permission_query_conditions = { "Contact": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_contact", "Address": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_address", "Communication": "frappe.core.doctype.communication.communication.get_permission_query_conditions_for_communication", - "Workflow Action": "frappe.workflow.doctype.workflow_action.workflow_action.get_permission_query_conditions" + "Workflow Action": "frappe.workflow.doctype.workflow_action.workflow_action.get_permission_query_conditions", + "Prepared Report": "frappe.core.doctype.prepared_report.prepared_report.get_permission_query_condition" } has_permission = { From 4b42961ed79a6a49a23dd4b3572a5c2606e1ffab Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 5 Mar 2020 10:54:43 +0530 Subject: [PATCH 036/157] feat: added permission check for doc --- .../doctype/prepared_report/prepared_report.py | 14 ++++++++++++++ frappe/hooks.py | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index 0d1f43c51f..e8aca6a2c5 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -115,3 +115,17 @@ def get_permission_query_condition(user): return """`tabPrepared Report`.ref_report_doctype in ({reports})"""\ .format(reports=','.join(reports)) + + +def has_permission(doc, user): + if not user: user = frappe.session.user + if user == "Administrator": + return True + + from frappe.utils.user import UserPermissions + user = UserPermissions(user) + + if "System Manager" in user.roles: + return True + + return doc.ref_report_doctype in user.get_all_reports().keys() \ No newline at end of file diff --git a/frappe/hooks.py b/frappe/hooks.py index 1d27685de6..6132618297 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -108,7 +108,8 @@ has_permission = { "Address": "frappe.contacts.address_and_contact.has_permission", "Communication": "frappe.core.doctype.communication.communication.has_permission", "Workflow Action": "frappe.workflow.doctype.workflow_action.workflow_action.has_permission", - "File": "frappe.core.doctype.file.file.has_permission" + "File": "frappe.core.doctype.file.file.has_permission", + "Prepared Report": "frappe.core.doctype.prepared_report.prepared_report.has_permission" } has_website_permission = { From 7e6a22ac72a64b90daa84992acb9b39c10dd3163 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 6 Mar 2020 21:54:17 +0530 Subject: [PATCH 037/157] feat: add button to go to list in customize form --- frappe/custom/doctype/customize_form/customize_form.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js index 63b094615a..5c9fa37ef5 100644 --- a/frappe/custom/doctype/customize_form/customize_form.js +++ b/frappe/custom/doctype/customize_form/customize_form.js @@ -85,6 +85,10 @@ frappe.ui.form.on("Customize Form", { if(frm.doc.doc_type) { frappe.customize_form.set_primary_action(frm); + frm.add_custom_button(__('Go to {0} List', [frm.doc.doc_type]), function() { + frappe.set_route('List', frm.doc.doc_type); + }); + frm.add_custom_button(__('Refresh Form'), function() { frm.script_manager.trigger("doc_type"); }, "fa fa-refresh", "btn-default"); From 057dea2f92490f318c59b50ec0f155289c4487ae Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 13 Mar 2020 12:25:24 +0530 Subject: [PATCH 038/157] Update frappe/core/doctype/prepared_report/prepared_report.py Co-Authored-By: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/core/doctype/prepared_report/prepared_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index e8aca6a2c5..4e87bb92dd 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -111,7 +111,7 @@ def get_permission_query_condition(user): if "System Manager" in user.roles: return None - reports = [ '"%s"'%report for report in user.get_all_reports().keys() ] + reports = [frappe.db.escape(report) for report in user.get_all_reports().keys()] return """`tabPrepared Report`.ref_report_doctype in ({reports})"""\ .format(reports=','.join(reports)) @@ -128,4 +128,4 @@ def has_permission(doc, user): if "System Manager" in user.roles: return True - return doc.ref_report_doctype in user.get_all_reports().keys() \ No newline at end of file + return doc.ref_report_doctype in user.get_all_reports().keys() From 1f82f051efb258eb8925ab23eea6eb66b05bc927 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 13 Mar 2020 19:56:12 +0530 Subject: [PATCH 039/157] fix: mve email_append_to to a section --- frappe/custom/doctype/customize_form/customize_form.js | 10 ---------- .../custom/doctype/customize_form/customize_form.json | 9 ++++++--- frappe/email/doctype/email_account/email_account.py | 6 +++--- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js index 858cf565be..444d9dc495 100644 --- a/frappe/custom/doctype/customize_form/customize_form.js +++ b/frappe/custom/doctype/customize_form/customize_form.js @@ -139,16 +139,6 @@ frappe.ui.form.on("Customize Form", { }, 1000); } - }, - - email_append_to: function(frm) { - frm.set_df_property("subject_field", "reqd", 0); - frm.set_df_property("sender_field", "reqd", 0); - - if (frm.doc.email_append_to) { - frm.set_df_property("subject_field", "reqd", 1); - frm.set_df_property("sender_field", "reqd", 1); - } } }); diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index f719add96c..bfc31d60e6 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -21,7 +21,6 @@ "allow_auto_repeat", "allow_import", "image_view", - "email_append_to", "column_break_5", "title_field", "image_field", @@ -31,6 +30,7 @@ "column_break_10", "sort_order", "section_break_23", + "email_append_to", "subject_field", "cb_01", "sender_field", @@ -182,14 +182,17 @@ "label": "Allow Import (via Data Import Tool)" }, { + "depends_on": "email_append_to", "fieldname": "subject_field", "fieldtype": "Data", "label": "Subject Field" }, { + "depends_on": "email_append_to", "fieldname": "sender_field", "fieldtype": "Data", - "label": "Sender Field" + "label": "Sender Field", + "mandatory_depends_on": "email_append_to" }, { "default": "0", @@ -212,7 +215,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2019-12-27 14:29:49.097980", + "modified": "2020-03-13 19:54:39.652134", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index cddc5dd25d..c0a198f5e5 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -678,9 +678,9 @@ def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len email_append_to_list = [] # Set Email Append To DocTypes via DocType - for dt in frappe.get_all("DocType", filters={"istable": 0, "issingle": 0}, fields=["name", "email_append_to"]): - if dt.get("email_append_to") and dt.email_append_to: - email_append_to_list.append(dt.name) + filters = {"istable": 0, "issingle": 0, "email_append_to": 1} + for dt in frappe.get_all("DocType", filters=filters, fields=["name", "email_append_to"]): + email_append_to_list.append(dt.name) # Set Email Append To DocTypes set via Customize Form for dt in frappe.get_list("Property Setter", filters={"property": "email_append_to", "value": 1}, fields=["doc_type"]): From 7fe09dc3caa10ffa94f573035eb5f7bfdb72988a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 16 Mar 2020 12:58:18 +0530 Subject: [PATCH 040/157] fix: Dont enqueue contact creation if in test --- frappe/core/doctype/user/user.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index b411f809cd..5eb3f44d47 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -101,7 +101,8 @@ class User(Document): frappe.enqueue( 'frappe.core.doctype.user.user.create_contact', user=self, - ignore_mandatory=True + ignore_mandatory=True, + now=frappe.flags.in_test ) if self.name not in ('Administrator', 'Guest') and not self.user_image: frappe.enqueue('frappe.core.doctype.user.user.update_gravatar', name=self.name) @@ -1038,8 +1039,9 @@ def create_contact(user, ignore_links=False, ignore_mandatory=False): from frappe.contacts.doctype.contact.contact import get_contact_name if user.name in ["Administrator", "Guest"]: return - contact_exists = get_contact_name(user.email) - if not contact_exists: + contact_name = frappe.db.get_value("Contact Email", {"email_id": user.email}, 'parent') + contact_name = frappe.db.exists("Contact", contact_name) + if not contact_name: contact = frappe.get_doc({ "doctype": "Contact", "first_name": user.first_name, @@ -1058,7 +1060,7 @@ def create_contact(user, ignore_links=False, ignore_mandatory=False): contact.add_phone(user.mobile_no, is_primary_mobile_no=True) contact.insert(ignore_permissions=True, ignore_links=ignore_links, ignore_mandatory=ignore_mandatory) else: - contact = frappe.get_doc("Contact", contact_exists) + contact = frappe.get_doc("Contact", contact_name) contact.first_name = user.first_name contact.last_name = user.last_name contact.gender = user.gender From 71bb527f8cae2474fa42fade10557b85f5603945 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 17 Mar 2020 12:54:06 +0530 Subject: [PATCH 041/157] fix: add a more descriptive confirmation prompt --- frappe/email/doctype/email_group/email_group.py | 2 +- frappe/email/doctype/newsletter/newsletter.js | 2 +- frappe/email/doctype/newsletter/newsletter.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index 359c42171e..8aa4893dc9 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -98,7 +98,7 @@ def add_subscribers(name, email_list): return frappe.get_doc("Email Group", name).update_total_subscribers() def send_welcome_email(welcome_email, email, email_group): - ''' Send welcome email for the subscribers of a given email group ''' + ''' Send welcome email for the subscribers of a given email group. ''' if not welcome_email: return diff --git a/frappe/email/doctype/newsletter/newsletter.js b/frappe/email/doctype/newsletter/newsletter.js index 76933d1cc4..691cdcb18f 100644 --- a/frappe/email/doctype/newsletter/newsletter.js +++ b/frappe/email/doctype/newsletter/newsletter.js @@ -7,7 +7,7 @@ frappe.ui.form.on('Newsletter', { if (!doc.__islocal && !cint(doc.email_sent) && !doc.__unsaved && in_list(frappe.boot.user.can_write, doc.doctype)) { frm.add_custom_button(__('Send Now'), function() { - frappe.confirm(__("Do you really want to send this email?"), function() { + frappe.confirm(__("Do you really want to send this email newsletter?"), function() { frm.call('send_emails').then(() => { frm.set_value('schedule_send', new Date()); frm.save(); diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index 4c9de88aad..fcd5d513b6 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -264,7 +264,7 @@ def get_newsletter_list(doctype, txt, filters, limit_start, limit_page_length=20 limit_page_length, limit_start), email_group_list, as_dict=1) def send_scheduled_email(): - ''' Send scheduled newsletter to the recipients ''' + ''' Send scheduled newsletter to the recipients. ''' scheduled_newsletter = frappe.get_all('Newsletter', filters = { 'schedule_send': ('<=', now_datetime()), 'email_sent': 0 From 94b2d856fcfcb3a2fc27064efd821020d530bfd5 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 19 Mar 2020 21:44:45 +0530 Subject: [PATCH 042/157] feat(ldap): allow resetting ldap password from user settings currently, there is no way to reset password for those logging in through ldap. i understand that this shouldn't really be handled by erpnext, but there are people that have requested resetting the ldap password from with the instance itself, and hence, we'll let that happen now. Signed-off-by: Chinmay D. Pai --- frappe/core/doctype/user/user.js | 42 +++++++++++++++++++ .../doctype/ldap_settings/ldap_settings.py | 42 +++++++++++++++++-- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index c4873ee40e..46332e1893 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -97,6 +97,48 @@ frappe.ui.form.on('User', { }); }, __("Password")); + frappe.db.get_single_value("LDAP Settings", "enabled").then((value) => { + if (value === 1 && frm.name != "Administrator") { + frm.add_custom_button(__("Reset LDAP Password"), function(){ + const d = new frappe.ui.Dialog({ + title: __("Reset LDAP Password"), + fields: [ + { + label: __("New Password"), + fieldtype: "Password", + fieldname: "new_password", + reqd: 1 + }, + { + label: __("Confirm New Password"), + fieldtype: "Password", + fieldname: "confirm_password", + reqd: 1 + }, + { + label: __("Logout All Sessions"), + fieldtype: "Check", + fieldname: "logout_sessions" + } + ], + primary_action: (values) => { + d.hide(); + if(values.new_password !== values.confirm_password) { + frappe.throw(__("Passwords do not match!")); + } + frappe.call( + "frappe.integrations.doctype.ldap_settings.ldap_settings.reset_password", { + user: frm.doc.email, + password: values.new_password, + logout: values.logout_sessions + }).then(() => done()); + } + }); + d.show(); + }, __("Password")); + } + }); + frm.add_custom_button(__("Reset OTP Secret"), function() { frappe.call({ method: "frappe.core.doctype.user.user.reset_otp_secret", diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index c0f12df04a..63e0b8ff55 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe import _ +from frappe import _, safe_encode from frappe.model.document import Document @@ -19,7 +19,7 @@ class LDAPSettings(Document): else: frappe.throw(_("LDAP Search String needs to end with a placeholder, eg sAMAccountName={0}")) - def connect_to_ldap(self, base_dn, password): + def connect_to_ldap(self, base_dn, password, read_only=True): try: import ldap3 import ssl @@ -44,7 +44,7 @@ class LDAPSettings(Document): user=base_dn, password=password, auto_bind=bind_type, - read_only=True, + read_only=read_only, raise_exceptions=True) return conn @@ -170,6 +170,34 @@ class LDAPSettings(Document): else: frappe.throw(_("Invalid username or password")) + def reset_password(self, user, password, logout_sessions=False): + from ldap3 import HASHED_SALTED_SHA, MODIFY_REPLACE + from ldap3.utils.hashed import hashed + + search_filter = "({0}={1})".format(self.ldap_email_field, user) + + conn = self.connect_to_ldap(self.base_dn, self.get_password(raise_exception=False), + read_only=False) + + if conn.search( + search_base=self.organizational_unit, + search_filter=search_filter, + attributes=self.get_ldap_attributes() + ): + if conn.entries and conn.entries[0]: + entry_dn = conn.entries[0].entry_dn + hashed_password = hashed(HASHED_SALTED_SHA, safe_encode(password)) + changes = {'userPassword': [(MODIFY_REPLACE, [hashed_password])]} + if conn.modify(entry_dn, changes=changes): + if logout_sessions: + from frappe.sessions import clear_sessions + clear_sessions(user=user, force=True) + frappe.msgprint(_("Password changed successfully.")) + else: + frappe.throw(_("Failed to change password.")) + else: + frappe.throw(_("LDAP User does not exist!")) + def convert_ldap_entry_to_dict(self, user_entry): # support multiple email values @@ -211,3 +239,11 @@ def login(): # because of a GET request! frappe.db.commit() + + +@frappe.whitelist() +def reset_password(user, password, logout): + ldap = frappe.get_doc("LDAP Settings") + if not ldap.enabled: + frappe.throw(_("LDAP is not enabled.")) + ldap.reset_password(user, password, logout_sessions=int(logout)) From bc77455d132704f8838ebfa50aee9ff01026661b Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 19 Mar 2020 21:49:58 +0530 Subject: [PATCH 043/157] chore: correct indentation for ldap exceptions Signed-off-by: Chinmay D. Pai --- frappe/integrations/doctype/ldap_settings/ldap_settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index 63e0b8ff55..f9dce6800c 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -196,7 +196,9 @@ class LDAPSettings(Document): else: frappe.throw(_("Failed to change password.")) else: - frappe.throw(_("LDAP User does not exist!")) + frappe.throw(_("No Entry for the User {0} found within LDAP!").format(user)) + else: + frappe.throw(_("LDAP User does not exist!")) def convert_ldap_entry_to_dict(self, user_entry): From 5fbf1f8c08249a3c6d724b9832efa5f6264af31a Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 19 Mar 2020 21:53:39 +0530 Subject: [PATCH 044/157] chore: make the exception read slightly better Signed-off-by: Chinmay D. Pai --- frappe/integrations/doctype/ldap_settings/ldap_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index f9dce6800c..558f7117c0 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -198,7 +198,7 @@ class LDAPSettings(Document): else: frappe.throw(_("No Entry for the User {0} found within LDAP!").format(user)) else: - frappe.throw(_("LDAP User does not exist!")) + frappe.throw(_("No LDAP User found for email: {0}").format(user)) def convert_ldap_entry_to_dict(self, user_entry): From 3b8d76e5a7dc6798c67afef38d38970c489bf602 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 19 Mar 2020 22:02:02 +0530 Subject: [PATCH 045/157] chore: check the correct variable for docname Signed-off-by: Chinmay D. Pai --- frappe/core/doctype/user/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index 46332e1893..0f474c85f2 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -98,7 +98,7 @@ frappe.ui.form.on('User', { }, __("Password")); frappe.db.get_single_value("LDAP Settings", "enabled").then((value) => { - if (value === 1 && frm.name != "Administrator") { + if (value === 1 && frm.doc.name != "Administrator") { frm.add_custom_button(__("Reset LDAP Password"), function(){ const d = new frappe.ui.Dialog({ title: __("Reset LDAP Password"), From 15801f870880e9b5a7457fb42a988db0325f7da8 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 19 Mar 2020 22:15:01 +0530 Subject: [PATCH 046/157] chore: codacy fixes Signed-off-by: Chinmay D. Pai --- frappe/core/doctype/user/user.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index 0f474c85f2..de4dc0c272 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -99,7 +99,7 @@ frappe.ui.form.on('User', { frappe.db.get_single_value("LDAP Settings", "enabled").then((value) => { if (value === 1 && frm.doc.name != "Administrator") { - frm.add_custom_button(__("Reset LDAP Password"), function(){ + frm.add_custom_button(__("Reset LDAP Password"), function() { const d = new frappe.ui.Dialog({ title: __("Reset LDAP Password"), fields: [ @@ -123,7 +123,7 @@ frappe.ui.form.on('User', { ], primary_action: (values) => { d.hide(); - if(values.new_password !== values.confirm_password) { + if (values.new_password !== values.confirm_password) { frappe.throw(__("Passwords do not match!")); } frappe.call( From daa8d14f91d147cdc17b55ccf1f6dbb8134a10e4 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 20 Mar 2020 18:58:20 +0530 Subject: [PATCH 047/157] fix: setup schedule send onload post render --- frappe/email/doctype/newsletter/newsletter.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/email/doctype/newsletter/newsletter.js b/frappe/email/doctype/newsletter/newsletter.js index 691cdcb18f..f5ca832fd8 100644 --- a/frappe/email/doctype/newsletter/newsletter.js +++ b/frappe/email/doctype/newsletter/newsletter.js @@ -27,7 +27,15 @@ frappe.ui.form.on('Newsletter', { } }, + onload_post_render(frm) { + frm.trigger('setup_schedule_send'); + }, + schedule_send(frm) { + frm.trigger('setup_schedule_send'); + }, + + setup_schedule_send(frm) { let today = new Date(); // setting datepicker options to set min date & min time frm.get_field('schedule_send').$input.datepicker({ From 7b7d55860477225e85bfa315eede31980a831274 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 20 Mar 2020 18:10:54 +0530 Subject: [PATCH 048/157] feat: add validate_phone api to window to validate phone numbers --- frappe/public/js/frappe/utils/datatype.js | 4 ++++ frappe/public/js/frappe/utils/utils.js | 3 +++ 2 files changed, 7 insertions(+) diff --git a/frappe/public/js/frappe/utils/datatype.js b/frappe/public/js/frappe/utils/datatype.js index 0f73526e04..16f87b872f 100644 --- a/frappe/public/js/frappe/utils/datatype.js +++ b/frappe/public/js/frappe/utils/datatype.js @@ -44,6 +44,10 @@ window.validate_email = function(txt) { return frappe.utils.validate_type(txt, "email"); }; +window.validate_phone = function(txt) { + return frappe.utils.validate_type(txt, "phone"); +}; + window.nth = function(number) { number = cint(number); var s = 'th'; diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 278c80897e..1af37c1f60 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -232,6 +232,9 @@ Object.assign(frappe.utils, { var regExp; switch ( type ) { + case "phone": + regExp = /^([0-9\ \+\_\-\,\.\*\#\(\)]){1,20}$/; + break; case "number": regExp = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/; break; From 64150d471721d4ef8ac40ad3b09bf722db5a880d Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 20 Mar 2020 21:13:35 +0530 Subject: [PATCH 049/157] fix: fix dropdown caret spacing in list sidebar --- frappe/public/js/frappe/list/list_sidebar.html | 2 +- frappe/public/js/frappe/list/list_sidebar_group_by.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/list/list_sidebar.html b/frappe/public/js/frappe/list/list_sidebar.html index f8e868da20..90189ccdea 100644 --- a/frappe/public/js/frappe/list/list_sidebar.html +++ b/frappe/public/js/frappe/list/list_sidebar.html @@ -64,7 +64,7 @@