From ab8b338b9279e64a7d10bddbd13c087e1ae2d65c Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 6 Sep 2019 10:44:51 +0530 Subject: [PATCH 01/10] feat: migrate to google python api --- frappe/contacts/doctype/contact/contact.json | 16 ++- .../google_contacts/google_contacts.json | 42 +++++- .../google_contacts/google_contacts.py | 132 +++++++++++------- 3 files changed, 127 insertions(+), 63 deletions(-) diff --git a/frappe/contacts/doctype/contact/contact.json b/frappe/contacts/doctype/contact/contact.json index f700411e80..fe692a0ff5 100644 --- a/frappe/contacts/doctype/contact/contact.json +++ b/frappe/contacts/doctype/contact/contact.json @@ -31,7 +31,7 @@ "department", "unsubscribed", "column_break_17", - "source", + "pulled_from_google_contacts", "google_contacts_description" ], "fields": [ @@ -165,11 +165,6 @@ "fieldtype": "Check", "label": "Unsubscribed" }, - { - "fieldname": "source", - "fieldtype": "Data", - "label": "Source" - }, { "fieldname": "middle_name", "fieldtype": "Data", @@ -203,12 +198,19 @@ "fieldtype": "Table", "label": "Phone Nos", "options": "Contact Phone" + }, + { + "default": "0", + "fieldname": "pulled_from_google_contacts", + "fieldtype": "Check", + "label": "Pulled from Google Contacts", + "read_only": 1 } ], "icon": "fa fa-user", "idx": 1, "image_field": "image", - "modified": "2019-08-09 10:23:00.486673", + "modified": "2019-09-06 06:43:02.159233", "modified_by": "Administrator", "module": "Contacts", "name": "Contact", diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.json b/frappe/integrations/doctype/google_contacts/google_contacts.json index 42fb9e68c8..746c52e786 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.json +++ b/frappe/integrations/doctype/google_contacts/google_contacts.json @@ -1,4 +1,5 @@ { + "_comments": "[]", "autoname": "format:GC-{email_id}", "creation": "2019-06-14 00:09:39.441961", "doctype": "DocType", @@ -11,7 +12,12 @@ "cb_00", "last_sync_on", "authorization_code", - "refresh_token" + "refresh_token", + "next_sync_token", + "sync", + "pull_from_google_contacts", + "column_break_12", + "push_to_google_contacts" ], "fields": [ { @@ -22,14 +28,12 @@ }, { "fieldname": "authorization_code", - "fieldtype": "Password", - "hidden": 1, + "fieldtype": "Data", "label": "Authorization Code" }, { "fieldname": "refresh_token", - "fieldtype": "Password", - "hidden": 1, + "fieldtype": "Data", "label": "Refresh Token" }, { @@ -62,9 +66,35 @@ "fieldname": "authorize_google_contacts_access", "fieldtype": "Button", "label": "Authorize Google Contacts Access" + }, + { + "fieldname": "next_sync_token", + "fieldtype": "Data", + "label": "Next Sync Token" + }, + { + "fieldname": "sync", + "fieldtype": "Section Break", + "label": "Sync" + }, + { + "default": "0", + "fieldname": "pull_from_google_contacts", + "fieldtype": "Check", + "label": "Pull from Google Contacts" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "push_to_google_contacts", + "fieldtype": "Check", + "label": "Push to Google Contacts" } ], - "modified": "2019-08-23 13:50:52.789503", + "modified": "2019-09-06 17:11:15.530952", "modified_by": "Administrator", "module": "Integrations", "name": "Google Contacts", diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index 4955fc9d7f..27d0cb3bd5 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -5,6 +5,9 @@ from __future__ import unicode_literals import frappe import requests +import googleapiclient.discovery +import google.oauth2.credentials + from frappe.model.document import Document from frappe import _ from frappe.utils import get_request_site_address @@ -60,7 +63,7 @@ def authorize_access(g_contact, reauthorize=None): if not google_contact.authorization_code or reauthorize: frappe.cache().hset("google_contacts", "google_contact", google_contact.name) - return google_callback(client_id=google_settings.client_id, redirect_uri=redirect_uri) + return get_authentication_url(client_id=google_settings.client_id, redirect_uri=redirect_uri) else: try: data = { @@ -83,21 +86,42 @@ def authorize_access(g_contact, reauthorize=None): except Exception as e: frappe.throw(e) +def get_authentication_url(client_id=None, redirect_uri=None): + 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(client_id=None, redirect_uri=None, code=None): +def google_callback(code=None): """ Authorization code is sent to callback as per the API configuration """ - if code is None: - 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) - } - else: - google_contact = frappe.cache().hget("google_contacts", "google_contact") - frappe.db.set_value("Google Contacts", google_contact, "authorization_code", code) - frappe.db.commit() + google_contact = frappe.cache().hget("google_contacts", "google_contact") + frappe.db.set_value("Google Contacts", google_contact, "authorization_code", code) + frappe.db.commit() - authorize_access(google_contact) + authorize_access(google_contact) + +def get_google_contacts_object(g_contact): + """ + Returns an object of Google Calendar along with Google Calendar doc. + """ + google_settings = frappe.get_doc("Google Settings") + account = frappe.get_doc("Google Contacts", g_calendar) + + 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/contacts" + } + + credentials = google.oauth2.credentials.Credentials(**credentials_dict) + google_contacts = googleapiclient.discovery.build("people", "v1", credentials=credentials) + + return google_contacts, account @frappe.whitelist() def sync(g_contact=None): @@ -108,58 +132,66 @@ def sync(g_contact=None): google_contacts = frappe.get_list("Google Contacts", filters=filters) - for google_contact in google_contacts: - doc = frappe.get_doc("Google Contacts", google_contact.name) - access_token = doc.get_access_token() + for g in google_contacts: + return sync_contacts_from_google_contacts(g.name) - headers = {"Authorization": "Bearer {}".format(access_token)} +def sync_contacts_from_google_contacts(g_contact, page_length=10): + """ + Syncs Contacts from Google Contacts. + https://developers.google.com/people/api/rest/v1/contactGroups/list + """ + google_contacts, account = get_google_contacts_object(google_contact.name) + results = [] + contacts_updated = 0 + while True: try: - r = requests.get(REQUEST, headers=headers, params=PARAMS) - except Exception as e: - frappe.throw(e) + sync_token = account.get_password(fieldname="next_sync_token", raise_exception=False) or None + contacts = google_contacts.people().connections().list(resourceName='people/me', maxResults=page_length, + syncToken=sync_token).execute() + except HttpError as err: + frappe.throw(_("Google Contacts - Could not sync contacts from Google Contacts, error code {0}.").format(err.resp.status)) - try: - r = r.json() - except Exception as e: - # if request doesn't return json show HTML ask permissions or to identify the error on google side - frappe.throw(e) + for contact in contacts.get("items"): + results.append(contact) - connections = r.get("connections") - contacts_updated = 0 + if not contacts.get("nextPageToken"): + if contacts.get("nextSyncToken"): + frappe.db.set_value("Google Contacts", account.name, "next_sync_token", contacts.get("nextSyncToken")) + frappe.db.commit() + break - frappe.db.set_value("Google Contacts", doc.name, "last_sync_on", frappe.utils.now_datetime()) + frappe.db.set_value("Google Contacts", doc.name, "last_sync_on", frappe.utils.now_datetime()) - if connections: - for idx, connection in enumerate(connections): - frappe.publish_realtime('import_google_contacts', dict(progress=idx+1, total=r.get("totalPeople")), user=frappe.session.user) + for idx, connection in enumerate(results): + frappe.publish_realtime('import_google_contacts', dict(progress=idx+1, total=r.get("totalPeople")), user=frappe.session.user) - for name in connection.get("names"): - if name.get("metadata").get("primary"): - contact = frappe.get_doc({ - "doctype": "Contact", - "salutation": name.get("honorificPrefix") or "", - "first_name": name.get("givenName") or "", - "middle_name": name.get("middleName") or "", - "last_name": name.get("familyName") or "", - "designation": get_indexed_value(connection.get("organizations"), 0, "title"), - "source": "Google Contacts", - "google_contacts_description": get_indexed_value(connection.get("organizations"), 0, "name") - }) + for name in connection.get("names"): + if name.get("metadata").get("primary"): + contact = frappe.get_doc({ + "doctype": "Contact", + "salutation": name.get("honorificPrefix") or "", + "first_name": name.get("givenName") or "", + "middle_name": name.get("middleName") or "", + "last_name": name.get("familyName") or "", + "designation": get_indexed_value(connection.get("organizations"), 0, "title"), + "source": "Google Contacts", + "google_contacts_description": get_indexed_value(connection.get("organizations"), 0, "name") + }) - for email in connection.get("emailAddresses", []): - contact.add_email(email_id=email.get("value"), is_primary=1 if email.get("primary") else 0) + for email in connection.get("emailAddresses", []): + contact.add_email(email_id=email.get("value"), is_primary=1 if email.get("primary") else 0) - for phone in connection.get("phoneNumbers", []): - contact.add_phone(phone=phone.get("value"), is_primary=1 if phone.get("primary") else 0) + for phone in connection.get("phoneNumbers", []): + contact.add_phone(phone=phone.get("value"), is_primary=1 if phone.get("primary") else 0) - contact.insert(ignore_permissions=True) + contact.insert(ignore_permissions=True) - if g_contact: - return _("{0} Google Contacts synced.").format(contacts_updated) if contacts_updated > 0 else _("No new Google Contacts synced.") + return _("{0} Google Contacts synced.").format(contacts_updated) if contacts_updated > 0 \ + else _("No new Google Contacts synced.") - if g_contact: - return _("No Google Contacts present to sync.") # If no Google Contacts to sync +def insert_contacts_to_google_contacts(doc, method=None): + pass def get_indexed_value(d, index, key): if not d: From 0c86588bf490351ab2eb8459857845a664d887d2 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 6 Sep 2019 10:57:07 +0530 Subject: [PATCH 02/10] fix: variable names --- .../integrations/doctype/google_contacts/google_contacts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index 27d0cb3bd5..609ee19728 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -107,7 +107,7 @@ def get_google_contacts_object(g_contact): Returns an object of Google Calendar along with Google Calendar doc. """ google_settings = frappe.get_doc("Google Settings") - account = frappe.get_doc("Google Contacts", g_calendar) + account = frappe.get_doc("Google Contacts", g_contact) credentials_dict = { "token": account.get_access_token(), @@ -140,7 +140,7 @@ def sync_contacts_from_google_contacts(g_contact, page_length=10): Syncs Contacts from Google Contacts. https://developers.google.com/people/api/rest/v1/contactGroups/list """ - google_contacts, account = get_google_contacts_object(google_contact.name) + google_contacts, account = get_google_contacts_object(g_contact) results = [] contacts_updated = 0 From 6de3e9a1bf5232b0f278213abfbde0f5ec6ca076 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 6 Sep 2019 13:56:00 +0530 Subject: [PATCH 03/10] feat: push contacts to google contacts --- frappe/contacts/doctype/contact/contact.json | 52 +++++++-- frappe/contacts/doctype/contact/contact.py | 3 + frappe/hooks.py | 12 ++ .../google_contacts/google_contacts.py | 103 +++++++++++++++--- 4 files changed, 145 insertions(+), 25 deletions(-) diff --git a/frappe/contacts/doctype/contact/contact.json b/frappe/contacts/doctype/contact/contact.json index fe692a0ff5..45f843428f 100644 --- a/frappe/contacts/doctype/contact/contact.json +++ b/frappe/contacts/doctype/contact/contact.json @@ -1,4 +1,5 @@ { + "_comments": "[]", "allow_events_in_timeline": 1, "allow_import": 1, "allow_rename": 1, @@ -21,7 +22,14 @@ "gender", "phone", "image", + "sync_with_google_contacts", "sb_00", + "google_contacts", + "google_contacts_id", + "cb_00", + "pulled_from_google_contacts", + "google_contacts_description", + "sb_01", "email_ids", "phone_nos", "contact_details", @@ -29,10 +37,7 @@ "links", "more_info", "department", - "unsubscribed", - "column_break_17", - "pulled_from_google_contacts", - "google_contacts_description" + "unsubscribed" ], "fields": [ { @@ -155,10 +160,6 @@ "fieldtype": "Data", "label": "Designation" }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, { "default": "0", "fieldname": "unsubscribed", @@ -171,15 +172,17 @@ "label": "Middle Name" }, { - "depends_on": "eval:doc.source==\"Google Contacts\"", + "depends_on": "pulled_from_google_contacts", "fieldname": "google_contacts_description", "fieldtype": "Small Text", "label": "Google Contacts Description" }, { + "collapsible": 1, + "depends_on": "eval:doc.sync_with_google_contacts || doc.pulled_from_google_contacts", "fieldname": "sb_00", "fieldtype": "Section Break", - "label": "Contact Details" + "label": "Google Contacts" }, { "fieldname": "email_ids", @@ -205,12 +208,39 @@ "fieldtype": "Check", "label": "Pulled from Google Contacts", "read_only": 1 + }, + { + "default": "0", + "fieldname": "sync_with_google_contacts", + "fieldtype": "Check", + "label": "Sync with Google Contacts" + }, + { + "fieldname": "google_contacts", + "fieldtype": "Link", + "label": "Google Contacts", + "options": "Google Contacts" + }, + { + "fieldname": "cb_00", + "fieldtype": "Column Break" + }, + { + "fieldname": "sb_01", + "fieldtype": "Section Break", + "label": "Contact Details" + }, + { + "fieldname": "google_contacts_id", + "fieldtype": "Data", + "label": "Google Contacts Id", + "read_only": 1 } ], "icon": "fa fa-user", "idx": 1, "image_field": "image", - "modified": "2019-09-06 06:43:02.159233", + "modified": "2019-09-06 20:23:15.088231", "modified_by": "Administrator", "module": "Contacts", "name": "Contact", diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 085452988a..6857d013a1 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -42,6 +42,9 @@ class Contact(Document): if self.email_id and not self.image: self.image = has_gravatar(self.email_id) + if self.sync_with_google_contacts and not self.google_contacts: + frappe.throw(_("Select Google Contacts to which contact should be synced.")) + deduplicate_dynamic_links(self) def set_user(self): diff --git a/frappe/hooks.py b/frappe/hooks.py index eb0d5bbe77..8243283353 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -136,6 +136,18 @@ doc_events = { "on_change": [ "frappe.social.doctype.energy_point_rule.energy_point_rule.process_energy_points" ], + }, + "Email Group Member": { + "validate": "frappe.email.doctype.email_group.email_group.restrict_email_group" + }, + "Event": { + "after_insert": "frappe.integrations.doctype.google_calendar.google_calendar.insert_event_in_google_calendar", + "on_update": "frappe.integrations.doctype.google_calendar.google_calendar.update_event_in_google_calendar", + "on_trash": "frappe.integrations.doctype.google_calendar.google_calendar.delete_event_from_google_calendar", + }, + "Contact": { + "after_insert": "frappe.integrations.doctype.google_contacts.google_contacts.insert_contacts_to_google_contacts", + "on_update": "frappe.integrations.doctype.google_contacts.google_contacts.update_contacts_to_google_contacts", } } diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index 609ee19728..aaae39b264 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -10,12 +10,11 @@ import google.oauth2.credentials from frappe.model.document import Document from frappe import _ +from googleapiclient.errors import HttpError from frappe.utils import get_request_site_address from frappe.integrations.doctype.google_settings.google_settings import get_auth_url SCOPES = "https://www.googleapis.com/auth/contacts" -REQUEST = "https://people.googleapis.com/v1/people/me/connections" -PARAMS = {"personFields": "names,emailAddresses,organizations,phoneNumbers"} class GoogleContacts(Document): @@ -135,24 +134,28 @@ def sync(g_contact=None): for g in google_contacts: return sync_contacts_from_google_contacts(g.name) -def sync_contacts_from_google_contacts(g_contact, page_length=10): +def sync_contacts_from_google_contacts(g_contact): """ Syncs Contacts from Google Contacts. https://developers.google.com/people/api/rest/v1/contactGroups/list """ google_contacts, account = get_google_contacts_object(g_contact) + + if not account.pull_from_google_contacts: + return + results = [] contacts_updated = 0 while True: try: sync_token = account.get_password(fieldname="next_sync_token", raise_exception=False) or None - contacts = google_contacts.people().connections().list(resourceName='people/me', maxResults=page_length, - syncToken=sync_token).execute() + contacts = google_contacts.people().connections().list(resourceName='people/me',syncToken=sync_token, + personFields="names,emailAddresses,organizations,phoneNumbers").execute() except HttpError as err: - frappe.throw(_("Google Contacts - Could not sync contacts from Google Contacts, error code {0}.").format(err.resp.status)) + frappe.throw(_("Google Contacts - Could not sync contacts from Google Contacts {0}, error code {1}.").format(account.name, err.resp.status)) - for contact in contacts.get("items"): + for contact in contacts.get("connections"): results.append(contact) if not contacts.get("nextPageToken"): @@ -161,29 +164,29 @@ def sync_contacts_from_google_contacts(g_contact, page_length=10): frappe.db.commit() break - frappe.db.set_value("Google Contacts", doc.name, "last_sync_on", frappe.utils.now_datetime()) + frappe.db.set_value("Google Contacts", account.name, "last_sync_on", frappe.utils.now_datetime()) for idx, connection in enumerate(results): - frappe.publish_realtime('import_google_contacts', dict(progress=idx+1, total=r.get("totalPeople")), user=frappe.session.user) + frappe.publish_realtime('import_google_contacts', dict(progress=idx+1, total=len(results)), user=frappe.session.user) for name in connection.get("names"): if name.get("metadata").get("primary"): + contacts_updated += 1 contact = frappe.get_doc({ "doctype": "Contact", - "salutation": name.get("honorificPrefix") or "", "first_name": name.get("givenName") or "", "middle_name": name.get("middleName") or "", "last_name": name.get("familyName") or "", "designation": get_indexed_value(connection.get("organizations"), 0, "title"), - "source": "Google Contacts", + "pulled_from_google_contacts": 1, "google_contacts_description": get_indexed_value(connection.get("organizations"), 0, "name") }) for email in connection.get("emailAddresses", []): - contact.add_email(email_id=email.get("value"), is_primary=1 if email.get("primary") else 0) + contact.add_email(email_id=email.get("value"), is_primary=1 if email.get("metadata").get("primary") else 0) for phone in connection.get("phoneNumbers", []): - contact.add_phone(phone=phone.get("value"), is_primary=1 if phone.get("primary") else 0) + contact.add_phone(phone=phone.get("value"), is_primary=1 if phone.get("metadata").get("primary") else 0) contact.insert(ignore_permissions=True) @@ -191,7 +194,79 @@ def sync_contacts_from_google_contacts(g_contact, page_length=10): else _("No new Google Contacts synced.") def insert_contacts_to_google_contacts(doc, method=None): - pass + """ + Syncs Contacts from Google Contacts. + https://developers.google.com/people/api/rest/v1/people/createContact + """ + if not frappe.db.exists("Google Contacts", {"name": doc.google_contacts}) or doc.pulled_from_google_contacts \ + or not doc.sync_with_google_contacts: + return + + google_contacts, account = get_google_contacts_object(doc.google_contacts) + + if not account.push_to_google_contacts: + return + + names = { + "givenName": doc.first_name, + "middleName": doc.middle_name, + "familyName": doc.last_name + } + + phoneNumbers = [{"value": phone_no.phone} for phone_no in doc.phone_nos] + emailAddresses = [{"value": email_id.email_id} for email_id in doc.email_ids] + + try: + contact = google_contacts.people().createContact(parent='people/me', body={"names": [names],"phoneNumbers": phoneNumbers, + "emailAddresses": emailAddresses}).execute() + frappe.db.set_value("Contact", doc.name, "google_contacts_id", contact.get("resourceName")) + except HttpError as err: + frappe.msgprint(_("Google Calendar - Could not insert contact in Google Contacts {0}, error code {1}.").format(account.name, err.resp.status)) + +def update_contacts_to_google_contacts(doc, method=None): + """ + Syncs Contacts from Google Contacts. + https://developers.google.com/people/api/rest/v1/people/updateContact + """ + # Workaround to avoid triggering updation when Event is being inserted since + # creation and modified are same when inserting doc + if not frappe.db.exists("Google Contacts", {"name": doc.google_contacts}) or doc.modified == doc.creation \ + or not doc.sync_with_google_contacts: + return + + if doc.sync_with_google_contacts and not doc.google_contacts_id: + # If sync_with_google_contacts is checked later, then insert the contact rather than updating it. + insert_contacts_to_google_contacts(doc) + return + + google_contacts, account = get_google_contacts_object(doc.google_contacts) + + if not account.push_to_google_contacts: + return + + names = { + "givenName": doc.first_name, + "middleName": doc.middle_name, + "familyName": doc.last_name + } + + phoneNumbers = [{"value": phone_no.phone} for phone_no in doc.phone_nos] + emailAddresses = [{"value": email_id.email_id} for email_id in doc.email_ids] + + try: + contact = google_contacts.people().get(resourceName=doc.google_contacts_id, \ + personFields="names,emailAddresses,organizations,phoneNumbers").execute() + + contact["names"] = [names] + contact["phoneNumbers"] = phoneNumbers + contact["emailAddresses"] = emailAddresses + + google_contacts.people().updateContact(resourceName=doc.google_contacts_id,body={"names":[names], + "phoneNumbers":phoneNumbers,"emailAddresses":emailAddresses}, + updatePersonFields="names,emailAddresses,organizations,phoneNumbers").execute() + frappe.msgprint(_("Contact Synced with Google Contacts.")) + except HttpError as err: + frappe.msgprint(_("Google Contacts - Could not update contact in Google Contacts {0}, error code {1}.").format(account.name, err.resp.status)) def get_indexed_value(d, index, key): if not d: From 124b1918b0e1f4daca8d0f3bd15479ccda55ef5d Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 6 Sep 2019 14:12:31 +0530 Subject: [PATCH 04/10] fix: pass etag parameter for updation --- .../integrations/doctype/google_contacts/google_contacts.json | 1 - frappe/integrations/doctype/google_contacts/google_contacts.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.json b/frappe/integrations/doctype/google_contacts/google_contacts.json index 746c52e786..bf2039ea25 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.json +++ b/frappe/integrations/doctype/google_contacts/google_contacts.json @@ -1,5 +1,4 @@ { - "_comments": "[]", "autoname": "format:GC-{email_id}", "creation": "2019-06-14 00:09:39.441961", "doctype": "DocType", diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index aaae39b264..faf036c594 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -262,7 +262,7 @@ def update_contacts_to_google_contacts(doc, method=None): contact["emailAddresses"] = emailAddresses google_contacts.people().updateContact(resourceName=doc.google_contacts_id,body={"names":[names], - "phoneNumbers":phoneNumbers,"emailAddresses":emailAddresses}, + "phoneNumbers":phoneNumbers,"emailAddresses":emailAddresses,"etag":contact.get("etag")}, updatePersonFields="names,emailAddresses,organizations,phoneNumbers").execute() frappe.msgprint(_("Contact Synced with Google Contacts.")) except HttpError as err: From db331620f214a3d349d7a2bc0ea96d2c649a92e7 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 6 Sep 2019 16:41:42 +0530 Subject: [PATCH 05/10] fix: remove unused hooks --- frappe/hooks.py | 3 --- .../doctype/google_contacts/google_contacts.json | 11 +++++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frappe/hooks.py b/frappe/hooks.py index 8243283353..d2e8570644 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -137,9 +137,6 @@ doc_events = { "frappe.social.doctype.energy_point_rule.energy_point_rule.process_energy_points" ], }, - "Email Group Member": { - "validate": "frappe.email.doctype.email_group.email_group.restrict_email_group" - }, "Event": { "after_insert": "frappe.integrations.doctype.google_calendar.google_calendar.insert_event_in_google_calendar", "on_update": "frappe.integrations.doctype.google_calendar.google_calendar.update_event_in_google_calendar", diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.json b/frappe/integrations/doctype/google_contacts/google_contacts.json index bf2039ea25..ef22770b51 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.json +++ b/frappe/integrations/doctype/google_contacts/google_contacts.json @@ -27,12 +27,14 @@ }, { "fieldname": "authorization_code", - "fieldtype": "Data", + "fieldtype": "Password", + "hidden": 1, "label": "Authorization Code" }, { "fieldname": "refresh_token", - "fieldtype": "Data", + "fieldtype": "Password", + "hidden": 1, "label": "Refresh Token" }, { @@ -68,7 +70,8 @@ }, { "fieldname": "next_sync_token", - "fieldtype": "Data", + "fieldtype": "Password", + "hidden": 1, "label": "Next Sync Token" }, { @@ -93,7 +96,7 @@ "label": "Push to Google Contacts" } ], - "modified": "2019-09-06 17:11:15.530952", + "modified": "2019-09-06 20:43:38.124849", "modified_by": "Administrator", "module": "Integrations", "name": "Google Contacts", From e3d044473de0d16788dde4ecd441f7dae7cae601 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 6 Sep 2019 17:14:12 +0530 Subject: [PATCH 06/10] chore: fix reference link for api in docstring --- frappe/integrations/doctype/google_contacts/google_contacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index faf036c594..b267e09429 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -137,7 +137,7 @@ def sync(g_contact=None): def sync_contacts_from_google_contacts(g_contact): """ Syncs Contacts from Google Contacts. - https://developers.google.com/people/api/rest/v1/contactGroups/list + https://developers.google.com/people/api/rest/v1/people.connections/list """ google_contacts, account = get_google_contacts_object(g_contact) From aef0fb7b893624a09d304e0b3cfd61da92561c21 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 6 Sep 2019 17:19:25 +0530 Subject: [PATCH 07/10] fix: remove _comments from json --- frappe/contacts/doctype/contact/contact.json | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/contacts/doctype/contact/contact.json b/frappe/contacts/doctype/contact/contact.json index 45f843428f..02f29747b6 100644 --- a/frappe/contacts/doctype/contact/contact.json +++ b/frappe/contacts/doctype/contact/contact.json @@ -1,5 +1,4 @@ { - "_comments": "[]", "allow_events_in_timeline": 1, "allow_import": 1, "allow_rename": 1, From 737fb3f9b1618af3c6acd614ade5d3a4e9fe4ae3 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 12 Sep 2019 23:26:52 +0530 Subject: [PATCH 08/10] fix: set google contact account while fetching --- frappe/integrations/doctype/google_contacts/google_contacts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index b267e09429..1e17ece0a3 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -179,6 +179,7 @@ def sync_contacts_from_google_contacts(g_contact): "last_name": name.get("familyName") or "", "designation": get_indexed_value(connection.get("organizations"), 0, "title"), "pulled_from_google_contacts": 1, + "google_contacts": account.name, "google_contacts_description": get_indexed_value(connection.get("organizations"), 0, "name") }) From 9366334fbd9e7faf0a03257971af68e0f039b038 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 13 Sep 2019 15:53:41 +0530 Subject: [PATCH 09/10] fix: auto fetch google contact --- frappe/contacts/doctype/contact/contact.js | 9 +++++++++ frappe/contacts/doctype/contact/contact.json | 19 +++++++++---------- .../google_contacts/google_contacts.json | 5 +++-- .../google_contacts/google_contacts.py | 2 +- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/frappe/contacts/doctype/contact/contact.js b/frappe/contacts/doctype/contact/contact.js index c6ad8cc269..9e9b0248d9 100644 --- a/frappe/contacts/doctype/contact/contact.js +++ b/frappe/contacts/doctype/contact/contact.js @@ -61,6 +61,15 @@ frappe.ui.form.on("Contact", { } } ]); + }, + sync_with_google_contacts: function(frm) { + if (frm.doc.sync_with_google_contacts) { + frappe.db.get_value("Google Contacts", {"email_id": frappe.session.user}, "name", (r) => { + if (r && r.name) { + frm.set_value("google_contacts", r.name); + } + }) + } } }); diff --git a/frappe/contacts/doctype/contact/contact.json b/frappe/contacts/doctype/contact/contact.json index 02f29747b6..85986dd9d5 100644 --- a/frappe/contacts/doctype/contact/contact.json +++ b/frappe/contacts/doctype/contact/contact.json @@ -14,20 +14,20 @@ "email_id", "user", "address", + "sync_with_google_contacts", "cb00", "status", "salutation", "designation", "gender", "phone", + "company_name", "image", - "sync_with_google_contacts", "sb_00", "google_contacts", "google_contacts_id", "cb_00", "pulled_from_google_contacts", - "google_contacts_description", "sb_01", "email_ids", "phone_nos", @@ -170,12 +170,6 @@ "fieldtype": "Data", "label": "Middle Name" }, - { - "depends_on": "pulled_from_google_contacts", - "fieldname": "google_contacts_description", - "fieldtype": "Small Text", - "label": "Google Contacts Description" - }, { "collapsible": 1, "depends_on": "eval:doc.sync_with_google_contacts || doc.pulled_from_google_contacts", @@ -234,13 +228,18 @@ "fieldtype": "Data", "label": "Google Contacts Id", "read_only": 1 + }, + { + "fieldname": "company_name", + "fieldtype": "Data", + "label": "Company Name" } ], "icon": "fa fa-user", "idx": 1, "image_field": "image", - "modified": "2019-09-06 20:23:15.088231", - "modified_by": "Administrator", + "modified": "2019-09-13 15:50:38.999884", + "modified_by": "himanshu@erpnext.com", "module": "Contacts", "name": "Contact", "name_case": "Title Case", diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.json b/frappe/integrations/doctype/google_contacts/google_contacts.json index ef22770b51..1089c6b635 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.json +++ b/frappe/integrations/doctype/google_contacts/google_contacts.json @@ -75,6 +75,7 @@ "label": "Next Sync Token" }, { + "depends_on": "enable", "fieldname": "sync", "fieldtype": "Section Break", "label": "Sync" @@ -96,8 +97,8 @@ "label": "Push to Google Contacts" } ], - "modified": "2019-09-06 20:43:38.124849", - "modified_by": "Administrator", + "modified": "2019-09-13 15:53:19.569924", + "modified_by": "himanshu@erpnext.com", "module": "Integrations", "name": "Google Contacts", "owner": "Administrator", diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index 1e17ece0a3..596c4b64ff 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -180,7 +180,7 @@ def sync_contacts_from_google_contacts(g_contact): "designation": get_indexed_value(connection.get("organizations"), 0, "title"), "pulled_from_google_contacts": 1, "google_contacts": account.name, - "google_contacts_description": get_indexed_value(connection.get("organizations"), 0, "name") + "company_name": get_indexed_value(connection.get("organizations"), 0, "name") }) for email in connection.get("emailAddresses", []): From 8dd559b9e53ef10c096124ae4d4c450421f3a7c1 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Fri, 13 Sep 2019 16:07:31 +0530 Subject: [PATCH 10/10] Update frappe/integrations/doctype/google_contacts/google_contacts.py Co-Authored-By: Faris Ansari --- frappe/integrations/doctype/google_contacts/google_contacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index 596c4b64ff..738c097f63 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -231,7 +231,7 @@ def update_contacts_to_google_contacts(doc, method=None): """ # Workaround to avoid triggering updation when Event is being inserted since # creation and modified are same when inserting doc - if not frappe.db.exists("Google Contacts", {"name": doc.google_contacts}) or doc.modified == doc.creation \ + if not frappe.db.exists("Google Contacts", {"name": doc.google_contacts}) or doc.is_new() \ or not doc.sync_with_google_contacts: return