Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Google Drive Picker #12715

Merged
merged 27 commits into from Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
25a4eb0
feat: google drive picker
barredterra Mar 29, 2021
7d333db
fix: scope for google picker
barredterra Mar 29, 2021
c44ceca
fix: remove log statements
barredterra Mar 29, 2021
f9dbbf6
feat: better home view for google drive picker
barredterra Mar 29, 2021
11f4edf
feat: localize google drive picker
barredterra Mar 29, 2021
7410110
refactor: bind this
barredterra Mar 29, 2021
a018e7b
refactor: rename GoogleDrive -> GoogleDrivePicker
barredterra Mar 29, 2021
1446082
refactor: save one API call
barredterra Mar 30, 2021
8618e81
Merge branch 'develop' into google_drive_picker
barredterra Mar 30, 2021
4706be3
fix: spaces to tabs
barredterra Mar 30, 2021
ffc4c3e
fix: define global vars for sider
barredterra Mar 30, 2021
eb98f33
feat: separate switch for picker, descriptions
barredterra Mar 30, 2021
91826bc
Merge branch 'develop' into google_drive_picker
barredterra Apr 2, 2021
7c78fda
test: google settings
barredterra Apr 2, 2021
41c6c88
test: picker enabled
barredterra Apr 3, 2021
6d523c7
feat: use nice file name
barredterra Apr 17, 2021
41e6aa6
fix: new google drive logo
barredterra Apr 17, 2021
eb1111a
Merge branch 'develop' into google_drive_picker
barredterra Apr 17, 2021
5c2a0f4
Merge branch 'develop' into google_drive_picker
barredterra Apr 27, 2021
7f7e8ac
Merge branch 'develop' into google_drive_picker
barredterra Jun 3, 2021
bd0839b
Merge branch 'develop' into google_drive_picker
barredterra Jun 18, 2021
37dc0b1
refactor: suggestions from review
barredterra Jun 18, 2021
024b896
feat: links to google cloud console in description
barredterra Jun 18, 2021
99594b0
test: change test cases after interface change
barredterra Jun 29, 2021
b02d271
test: remove test_picker_as_guest
barredterra Jun 29, 2021
d904481
style: use double quotes
barredterra Jun 29, 2021
5592bf6
feat: remove "All" access from Google Settings
barredterra Jun 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions frappe/handler.py
Expand Up @@ -144,8 +144,8 @@ def upload_file():
file_url = frappe.form_dict.file_url
folder = frappe.form_dict.folder or 'Home'
method = frappe.form_dict.method
filename = frappe.form_dict.file_name
content = None
filename = None

if 'file' in files:
file = files['file']
Expand All @@ -155,7 +155,7 @@ def upload_file():
frappe.local.uploaded_file = content
frappe.local.uploaded_filename = filename

if frappe.session.user == 'Guest' or (user and not user.has_desk_access()):
if not file_url and (frappe.session.user == "Guest" or (user and not user.has_desk_access())):
import mimetypes
filetype = mimetypes.guess_type(filename)[0]
if filetype not in ALLOWED_MIMETYPES:
Expand Down
47 changes: 32 additions & 15 deletions 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",
Expand All @@ -8,7 +9,10 @@
"client_id",
"client_secret",
"sb_01",
"api_key"
"api_key",
"section_break_7",
"google_drive_picker_enabled",
"app_id"
],
"fields": [
{
Expand All @@ -18,10 +22,12 @@
"label": "Enable"
},
{
"description": "The Client ID obtained from the Google Cloud Console under <a href=\"https://console.cloud.google.com/apis/credentials\">\n\"APIs &amp; Services\" &gt; \"Credentials\"\n</a>",
"fieldname": "client_id",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Client ID"
"label": "Client ID",
"mandatory_depends_on": "google_drive_picker_enabled"
},
{
"fieldname": "client_secret",
Expand All @@ -30,10 +36,11 @@
"label": "Client Secret"
},
{
"description": "Used For Google Maps Integration.",
"description": "The browser API key obtained from the Google Cloud Console under <a href=\"https://console.cloud.google.com/apis/credentials\">\n\"APIs &amp; Services\" &gt; \"Credentials\"\n</a>",
"fieldname": "api_key",
"fieldtype": "Data",
"label": "API Key"
"label": "API Key",
"mandatory_depends_on": "google_drive_picker_enabled"
},
{
"depends_on": "enable",
Expand All @@ -46,10 +53,30 @@
"fieldname": "sb_01",
"fieldtype": "Section Break",
"label": "API Key"
},
{
"depends_on": "google_drive_picker_enabled",
"description": "The project number obtained from Google Cloud Console under <a href=\"https://console.cloud.google.com/iam-admin/settings\">\n\"IAM &amp; Admin\" &gt; \"Settings\"\n</a>",
"fieldname": "app_id",
"fieldtype": "Data",
"label": "App ID",
"mandatory_depends_on": "google_drive_picker_enabled"
},
{
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"label": "Google Drive Picker"
},
{
"default": "0",
"fieldname": "google_drive_picker_enabled",
"fieldtype": "Check",
"label": "Google Drive Picker Enabled"
}
],
"issingle": 1,
"modified": "2019-08-06 22:37:41.699703",
"links": [],
"modified": "2021-06-29 18:26:07.094851",
"modified_by": "Administrator",
"module": "Integrations",
"name": "Google Settings",
Expand All @@ -64,16 +91,6 @@
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "All",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
Expand Down
19 changes: 17 additions & 2 deletions frappe/integrations/doctype/google_settings/google_settings.py
Expand Up @@ -2,11 +2,26 @@
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt

# 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"
return "https://www.googleapis.com/oauth2/v4/token"


@frappe.whitelist()
def get_file_picker_settings():
"""Return all the data FileUploader needs to start the Google Drive Picker."""
google_settings = frappe.get_single("Google Settings")
if not (google_settings.enable and google_settings.google_drive_picker_enabled):
return {}

return {
"enabled": True,
"appId": google_settings.app_id,
"developerKey": google_settings.api_key,
"clientId": google_settings.client_id
}
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals

import frappe
import unittest

from .google_settings import get_file_picker_settings

class TestGoogleSettings(unittest.TestCase):

def setUp(self):
settings = frappe.get_single("Google Settings")
settings.client_id = "test_client_id"
settings.app_id = "test_app_id"
settings.api_key = "test_api_key"
settings.save()

def test_picker_disabled(self):
"""Google Drive Picker should be disabled if it is not enabled in Google Settings."""
frappe.db.set_value("Google Settings", None, "enable", 1)
frappe.db.set_value("Google Settings", None, "google_drive_picker_enabled", 0)
settings = get_file_picker_settings()

self.assertEqual(settings, {})

def test_google_disabled(self):
"""Google Drive Picker should be disabled if Google integration is not enabled."""
frappe.db.set_value("Google Settings", None, "enable", 0)
frappe.db.set_value("Google Settings", None, "google_drive_picker_enabled", 1)
settings = get_file_picker_settings()

self.assertEqual(settings, {})

def test_picker_enabled(self):
"""If picker is enabled, get_file_picker_settings should return the credentials."""
frappe.db.set_value("Google Settings", None, "enable", 1)
frappe.db.set_value("Google Settings", None, "google_drive_picker_enabled", 1)
settings = get_file_picker_settings()

self.assertEqual(True, settings.get("enabled", False))
self.assertEqual("test_client_id", settings.get("clientId", ""))
self.assertEqual("test_app_id", settings.get("appId", ""))
self.assertEqual("test_api_key", settings.get("developerKey", ""))
1 change: 1 addition & 0 deletions frappe/public/icons/social/google_drive.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 48 additions & 3 deletions frappe/public/js/frappe/file_uploader/FileUploader.vue
Expand Up @@ -63,6 +63,12 @@
</svg>
<div class="mt-1">{{ __('Camera') }}</div>
</button>
<button v-if="google_drive_settings.enabled" class="btn btn-file-upload" @click="show_google_drive_picker">
<svg width="30" height="30">
<image xlink:href="/assets/frappe/icons/social/google_drive.svg" width="30" height="30"/>
</svg>
<div class="mt-1">{{ __('Google Drive') }}</div>
</button>
</div>
<div class="text-muted text-medium">
{{ upload_notes }}
Expand Down Expand Up @@ -116,6 +122,7 @@
import FilePreview from './FilePreview.vue';
import FileBrowser from './FileBrowser.vue';
import WebLink from './WebLink.vue';
import GoogleDrivePicker from '../../integrations/google_drive_picker';

export default {
name: 'FileUploader',
Expand Down Expand Up @@ -173,6 +180,24 @@ export default {
currently_uploading: -1,
show_file_browser: false,
show_web_link: false,
allow_take_photo: false,
google_drive_settings: {
enabled: false
}
}
},
created() {
this.allow_take_photo = window.navigator.mediaDevices;
if (frappe.user_id !== "Guest") {
frappe.call({
// method only available after login
method: "frappe.integrations.doctype.google_settings.google_settings.get_file_picker_settings",
callback: (resp) => {
if (!resp.exc) {
this.google_drive_settings = resp.message;
}
}
});
}
},
watch: {
Expand All @@ -187,9 +212,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: {
Expand Down Expand Up @@ -408,6 +430,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);
Expand Down Expand Up @@ -437,6 +463,25 @@ export default {
);
});
},
show_google_drive_picker() {
let dialog = cur_dialog;
dialog.hide();
let google_drive = new GoogleDrivePicker({
pickerCallback: data => this.google_drive_callback(data, dialog),
...this.google_drive_settings
});
google_drive.loadPicker();
},
google_drive_callback(data, dialog) {
if (data.action == google.picker.Action.PICKED) {
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())
Expand Down
77 changes: 77 additions & 0 deletions frappe/public/js/integrations/google_drive_picker.js
@@ -0,0 +1,77 @@
/* global gapi:false, google:false */
export default class GoogleDrivePicker {
constructor({
pickerCallback,
enabled,
appId,
developerKey,
clientId
} = {}) {
this.scope = ['https://www.googleapis.com/auth/drive.readonly'];
this.pickerApiLoaded = false;
this.enabled = enabled;
this.appId = appId;
this.pickerCallback = pickerCallback;
this.developerKey = developerKey;
this.clientId = clientId;
}

loadPicker() {
// load the google API library
$.ajax({
method: "GET",
url: "https://apis.google.com/js/api.js",
dataType: "script",
cache: true
}).done(this.loadGapi.bind(this));
}

loadGapi() {
// load auth and picker libraries
if (!frappe.boot.user.google_drive_token) {
gapi.load('auth', this.onAuthApiLoad.bind(this));
}

gapi.load('picker', this.onPickerApiLoad.bind(this));
}

onAuthApiLoad() {
gapi.auth.authorize({
'client_id': this.clientId,
'scope': this.scope,
'immediate': false
}, this.handleAuthResult.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.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)
.setDeveloperKey(this.developerKey)
.setOAuthToken(frappe.boot.user.google_drive_token)
.addView(view)
.setLocale(frappe.boot.lang)
.setCallback(this.pickerCallback)
.build();

picker.setVisible(true);
}
}
}