From 25a4eb07575094766111c5cca9b46f79dd1ed59f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 19:25:41 +0200 Subject: [PATCH 01/39] feat: google drive picker --- .../google_settings/google_settings.json | 12 ++- .../google_settings/test_google_settings.py | 10 +++ frappe/public/icons/social/google_drive.svg | 1 + .../js/frappe/file_uploader/FileUploader.vue | 46 ++++++++++- frappe/public/js/integrations/google_drive.js | 81 +++++++++++++++++++ 5 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 frappe/integrations/doctype/google_settings/test_google_settings.py create mode 100644 frappe/public/icons/social/google_drive.svg create mode 100644 frappe/public/js/integrations/google_drive.js diff --git a/frappe/integrations/doctype/google_settings/google_settings.json b/frappe/integrations/doctype/google_settings/google_settings.json index 086c56c020..6a4f181f2d 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.json +++ b/frappe/integrations/doctype/google_settings/google_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-06-14 00:08:37.255003", "doctype": "DocType", "engine": "InnoDB", @@ -8,7 +9,8 @@ "client_id", "client_secret", "sb_01", - "api_key" + "api_key", + "app_id" ], "fields": [ { @@ -46,10 +48,16 @@ "fieldname": "sb_01", "fieldtype": "Section Break", "label": "API Key" + }, + { + "fieldname": "app_id", + "fieldtype": "Data", + "label": "App ID" } ], "issingle": 1, - "modified": "2019-08-06 22:37:41.699703", + "links": [], + "modified": "2021-03-28 22:24:05.963403", "modified_by": "Administrator", "module": "Integrations", "name": "Google Settings", diff --git a/frappe/integrations/doctype/google_settings/test_google_settings.py b/frappe/integrations/doctype/google_settings/test_google_settings.py new file mode 100644 index 0000000000..476e772b58 --- /dev/null +++ b/frappe/integrations/doctype/google_settings/test_google_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestGoogleSettings(unittest.TestCase): + pass diff --git a/frappe/public/icons/social/google_drive.svg b/frappe/public/icons/social/google_drive.svg new file mode 100644 index 0000000000..d43b4d3dbd --- /dev/null +++ b/frappe/public/icons/social/google_drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index d0b09c7593..61b87606a5 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -63,6 +63,12 @@
{{ __('Camera') }}
+
{{ upload_notes }} @@ -116,6 +122,7 @@ import FilePreview from './FilePreview.vue'; import FileBrowser from './FileBrowser.vue'; import WebLink from './WebLink.vue'; +import GoogleDrive from '../../integrations/google_drive'; export default { name: 'FileUploader', @@ -173,8 +180,16 @@ export default { currently_uploading: -1, show_file_browser: false, show_web_link: false, + allow_take_photo: false, + allow_google_drive: false } }, + created() { + this.allow_take_photo = window.navigator.mediaDevices; + frappe.db.get_single_value("Google Settings", "enable").then(resp => { + this.allow_google_drive = Boolean(resp); + }); + }, watch: { files(newvalue, oldvalue) { if (!this.allow_multiple && newvalue.length > 1) { @@ -187,9 +202,6 @@ export default { return this.files.length > 0 && this.files.every( file => file.total !== 0 && file.progress === file.total); - }, - allow_take_photo() { - return window.navigator.mediaDevices; } }, methods: { @@ -408,6 +420,10 @@ export default { form_data.append('file_url', file.file_url); } + if (file.file_name) { + form_data.append('file_name', file.file_name); + } + if (this.doctype && this.docname) { form_data.append('doctype', this.doctype); form_data.append('docname', this.docname); @@ -437,6 +453,30 @@ export default { ); }); }, + show_google_drive_picker() { + frappe.db.get_value("Google Settings", "Google Settings", ["client_id", "api_key", "app_id"]).then(resp => { + let dialog = cur_dialog; + dialog.hide(); + let google_drive = new GoogleDrive({ + pickerCallback: data => this.google_drive_callback(data, dialog), + developerKey: resp.message.api_key, + clientId: resp.message.client_id, + appId: resp.message.app_id + }); + google_drive.loadPicker(); + }); + }, + google_drive_callback(data, dialog) { + if (data.action == google.picker.Action.PICKED) { + // debugger; + this.upload_file({ + file_url: data.docs[0].url, + file_name: data.docs[0].name + }); + } else if (data.action == google.picker.Action.CANCEL) { + dialog.show(); + } + }, url_to_file(url, filename, mime_type) { return fetch(url) .then(res => res.arrayBuffer()) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js new file mode 100644 index 0000000000..9c6aea5375 --- /dev/null +++ b/frappe/public/js/integrations/google_drive.js @@ -0,0 +1,81 @@ +export default class GoogleDrive { + constructor({ + pickerCallback, + developerKey, + clientId, + appId + } = {}) { + console.log('GoogleDrive constructor'); + this.pickerCallback = pickerCallback; + this.pickerApiLoaded = false; + this.scope = ['https://www.googleapis.com/auth/drive.file']; + this.developerKey = developerKey; + this.clientId = clientId; + this.appId = appId; + } + + loadPicker() { + // load the google API library + $.ajax({ + method: "GET", + url: "https://apis.google.com/js/api.js", + dataType: "script", + cache: true, + context: this + }).done(function() { + this.loadGapi(); + }.bind(this)); + } + + loadGapi() { + // load auth and picker libraries + if (!frappe.boot.user.google_drive_token) { + gapi.load('auth', function() { + console.log('gapi.load("auth") callback'); + this.onAuthApiLoad(); + }.bind(this)); + } + + gapi.load('picker', function() { + console.log('gapi.load("picker") callback'); + this.onPickerApiLoad(); + }.bind(this)); + } + + onAuthApiLoad() { + gapi.auth.authorize({ + 'client_id': this.clientId, + 'scope': this.scope, + 'immediate': false + }, function(authResult) { + this.handleAuthResult(authResult); + }.bind(this)); + } + + handleAuthResult(authResult) { + if (authResult && !authResult.error) { + frappe.boot.user.google_drive_token = authResult.access_token; + this.createPicker(); + } + } + + onPickerApiLoad() { + this.pickerApiLoaded = true; + this.createPicker(); + } + + createPicker() { + // Create and render a Picker object for searching images. + if (this.pickerApiLoaded && frappe.boot.user.google_drive_token) { + var view = new google.picker.View(google.picker.ViewId.DOCS); + var picker = new google.picker.PickerBuilder() + .setAppId(this.appId) + .setOAuthToken(frappe.boot.user.google_drive_token) + .addView(view) + .setDeveloperKey(this.developerKey) + .setCallback(this.pickerCallback) + .build(); + picker.setVisible(true); + } + } +} From 7d333dbf703001c50dc80aa0a28477b741e9b51f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 21:09:06 +0200 Subject: [PATCH 02/39] fix: scope for google picker --- frappe/public/js/integrations/google_drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index 9c6aea5375..1c7e4dda21 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -8,7 +8,7 @@ export default class GoogleDrive { console.log('GoogleDrive constructor'); this.pickerCallback = pickerCallback; this.pickerApiLoaded = false; - this.scope = ['https://www.googleapis.com/auth/drive.file']; + this.scope = ['https://www.googleapis.com/auth/drive.readonly']; this.developerKey = developerKey; this.clientId = clientId; this.appId = appId; From c44cecae5db1ef7231b742ca84ade406224e17d6 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 21:11:26 +0200 Subject: [PATCH 03/39] fix: remove log statements --- frappe/public/js/integrations/google_drive.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index 1c7e4dda21..e7093fb29a 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -5,7 +5,6 @@ export default class GoogleDrive { clientId, appId } = {}) { - console.log('GoogleDrive constructor'); this.pickerCallback = pickerCallback; this.pickerApiLoaded = false; this.scope = ['https://www.googleapis.com/auth/drive.readonly']; @@ -31,13 +30,11 @@ export default class GoogleDrive { // load auth and picker libraries if (!frappe.boot.user.google_drive_token) { gapi.load('auth', function() { - console.log('gapi.load("auth") callback'); this.onAuthApiLoad(); }.bind(this)); } gapi.load('picker', function() { - console.log('gapi.load("picker") callback'); this.onPickerApiLoad(); }.bind(this)); } From f9dbbf69e0e89e3f36ac821994cfccb6a576f32d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 21:22:13 +0200 Subject: [PATCH 04/39] feat: better home view for google drive picker --- frappe/public/js/integrations/google_drive.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index e7093fb29a..6b9ee9d0ff 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -64,7 +64,10 @@ export default class GoogleDrive { createPicker() { // Create and render a Picker object for searching images. if (this.pickerApiLoaded && frappe.boot.user.google_drive_token) { - var view = new google.picker.View(google.picker.ViewId.DOCS); + var view = new google.picker.DocsView(google.picker.ViewId.DOCS) + .setParent('root') // show the root folder by default + .setIncludeFolders(true); // also show folders, not just files + var picker = new google.picker.PickerBuilder() .setAppId(this.appId) .setOAuthToken(frappe.boot.user.google_drive_token) From 11f4edf051bf9842e58228faae2d2a349d2e763d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 21:22:37 +0200 Subject: [PATCH 05/39] feat: localize google drive picker --- frappe/public/js/integrations/google_drive.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index 6b9ee9d0ff..076178046e 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -70,11 +70,13 @@ export default class GoogleDrive { var picker = new google.picker.PickerBuilder() .setAppId(this.appId) + .setDeveloperKey(this.developerKey) .setOAuthToken(frappe.boot.user.google_drive_token) .addView(view) - .setDeveloperKey(this.developerKey) + .setLocale(frappe.boot.lang) .setCallback(this.pickerCallback) .build(); + picker.setVisible(true); } } From 741011085b6ba3cccc86ba3514d9deff4eb82b54 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 30 Mar 2021 00:49:02 +0200 Subject: [PATCH 06/39] refactor: bind this --- frappe/public/js/integrations/google_drive.js | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index 076178046e..06d9cbbe54 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -5,9 +5,9 @@ export default class GoogleDrive { clientId, appId } = {}) { - this.pickerCallback = pickerCallback; - this.pickerApiLoaded = false; this.scope = ['https://www.googleapis.com/auth/drive.readonly']; + this.pickerApiLoaded = false; + this.pickerCallback = pickerCallback; this.developerKey = developerKey; this.clientId = clientId; this.appId = appId; @@ -19,24 +19,17 @@ export default class GoogleDrive { method: "GET", url: "https://apis.google.com/js/api.js", dataType: "script", - cache: true, - context: this - }).done(function() { - this.loadGapi(); - }.bind(this)); + cache: true + }).done(this.loadGapi.bind(this)); } loadGapi() { // load auth and picker libraries if (!frappe.boot.user.google_drive_token) { - gapi.load('auth', function() { - this.onAuthApiLoad(); - }.bind(this)); + gapi.load('auth', this.onAuthApiLoad.bind(this)); } - gapi.load('picker', function() { - this.onPickerApiLoad(); - }.bind(this)); + gapi.load('picker', this.onPickerApiLoad.bind(this)); } onAuthApiLoad() { @@ -44,9 +37,7 @@ export default class GoogleDrive { 'client_id': this.clientId, 'scope': this.scope, 'immediate': false - }, function(authResult) { - this.handleAuthResult(authResult); - }.bind(this)); + }, this.handleAuthResult.bind(this)); } handleAuthResult(authResult) { From a018e7b4458c028ebed29d8e889ae72f086d0479 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 30 Mar 2021 01:28:12 +0200 Subject: [PATCH 07/39] refactor: rename GoogleDrive -> GoogleDrivePicker --- frappe/public/js/frappe/file_uploader/FileUploader.vue | 4 ++-- .../integrations/{google_drive.js => google_drive_picker.js} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename frappe/public/js/integrations/{google_drive.js => google_drive_picker.js} (98%) diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index 61b87606a5..154d4e0ea1 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -122,7 +122,7 @@ import FilePreview from './FilePreview.vue'; import FileBrowser from './FileBrowser.vue'; import WebLink from './WebLink.vue'; -import GoogleDrive from '../../integrations/google_drive'; +import GoogleDrivePicker from '../../integrations/google_drive_picker'; export default { name: 'FileUploader', @@ -457,7 +457,7 @@ export default { frappe.db.get_value("Google Settings", "Google Settings", ["client_id", "api_key", "app_id"]).then(resp => { let dialog = cur_dialog; dialog.hide(); - let google_drive = new GoogleDrive({ + let google_drive = new GoogleDrivePicker({ pickerCallback: data => this.google_drive_callback(data, dialog), developerKey: resp.message.api_key, clientId: resp.message.client_id, diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive_picker.js similarity index 98% rename from frappe/public/js/integrations/google_drive.js rename to frappe/public/js/integrations/google_drive_picker.js index 06d9cbbe54..7ce9810f72 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive_picker.js @@ -1,4 +1,4 @@ -export default class GoogleDrive { +export default class GoogleDrivePicker { constructor({ pickerCallback, developerKey, From 144608241e9825af20c88b122a70f29d22aa5a41 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 30 Mar 2021 02:20:11 +0200 Subject: [PATCH 08/39] refactor: save one API call --- .../google_settings/google_settings.py | 22 +++++++++++++-- .../js/frappe/file_uploader/FileUploader.vue | 28 +++++++++---------- .../js/integrations/google_drive_picker.js | 8 ++++-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/frappe/integrations/doctype/google_settings/google_settings.py b/frappe/integrations/doctype/google_settings/google_settings.py index ecc975235a..bd0e845977 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.py +++ b/frappe/integrations/doctype/google_settings/google_settings.py @@ -3,11 +3,29 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe +import frappe from frappe.model.document import Document class GoogleSettings(Document): pass def get_auth_url(): - return "https://www.googleapis.com/oauth2/v4/token" \ No newline at end of file + return "https://www.googleapis.com/oauth2/v4/token" + + +@frappe.whitelist(allow_guest=True) +def get_file_picker_settings(): + """Return all the data FileUploader needs to start the Google Drive Picker.""" + if frappe.session.user == 'Guest': + return {'enabled': False} + + google_settings = frappe.get_single("Google Settings") + if not google_settings.enable: + return {'enabled': False} + + return { + 'enabled': True, + 'appId': google_settings.app_id, + 'developerKey': google_settings.api_key, + 'clientId': google_settings.client_id + } diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index 154d4e0ea1..64365cfc11 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -63,7 +63,7 @@
{{ __('Camera') }}
-