Merge branch 'develop' into auto-generate-rtl-style

This commit is contained in:
Suraj Shetty 2021-07-12 09:26:55 +05:30 committed by GitHub
commit 5bea9dbdb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 268 additions and 254 deletions

View file

@ -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']
@ -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:

View file

@ -1,4 +1,5 @@
{
"actions": [],
"creation": "2019-06-14 00:08:37.255003",
"doctype": "DocType",
"engine": "InnoDB",
@ -8,7 +9,10 @@
"client_id",
"client_secret",
"sb_01",
"api_key"
"api_key",
"section_break_7",
"google_drive_picker_enabled",
"app_id"
],
"fields": [
{
@ -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",
@ -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",
@ -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",
@ -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,

View file

@ -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
}

View file

@ -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", ""))

View file

@ -113,22 +113,20 @@ frappe.ui.form.PrintView = class {
},
).$input;
this.letterhead_selector = this.add_sidebar_item(
this.letterhead_selector_df = this.add_sidebar_item(
{
fieldtype: 'Select',
fieldtype: 'Autocomplete',
fieldname: 'letterhead',
label: __('Select Letterhead'),
options: [
this.get_default_option_for_select(__('Select Letterhead')),
__('No Letterhead')
],
placeholder: __('Select Letterhead'),
options: [__('No Letterhead')],
change: () => this.preview(),
default: this.print_settings.with_letterhead
? __('No Letterhead')
: __('Select Letterhead')
},
).$input;
);
this.letterhead_selector = this.letterhead_selector_df.$input;
this.sidebar_dynamic_section = $(
`<div class="dynamic-settings"></div>`
).appendTo(this.sidebar);
@ -336,23 +334,19 @@ frappe.ui.form.PrintView = class {
}
set_letterhead_options() {
let letterhead_options = [
this.get_default_option_for_select(__('Select Letterhead')),
__('No Letterhead')
];
let letterhead_options = [__('No Letterhead')];
let default_letterhead;
let doc_letterhead = this.frm.doc.letter_head;
return frappe.db
.get_list('Letter Head', { fields: ['name', 'is_default'] })
.get_list('Letter Head', { fields: ['name', 'is_default'], limit: 0 })
.then((letterheads) => {
this.letterhead_selector.empty();
letterheads.map((letterhead) => {
if (letterhead.is_default) default_letterhead = letterhead.name;
return letterhead_options.push(letterhead.name);
});
this.letterhead_selector.add_options(letterhead_options);
this.letterhead_selector_df.set_data(letterhead_options);
let selected_letterhead = doc_letterhead || default_letterhead;
if (selected_letterhead)
this.letterhead_selector.val(selected_letterhead);

View file

@ -0,0 +1 @@
<svg viewBox="0 0 87.3 78" xmlns="http://www.w3.org/2000/svg"><path d="m6.6 66.85 3.85 6.65c.8 1.4 1.95 2.5 3.3 3.3l13.75-23.8h-27.5c0 1.55.4 3.1 1.2 4.5z" fill="#0066da"/><path d="m43.65 25-13.75-23.8c-1.35.8-2.5 1.9-3.3 3.3l-25.4 44a9.06 9.06 0 0 0 -1.2 4.5h27.5z" fill="#00ac47"/><path d="m73.55 76.8c1.35-.8 2.5-1.9 3.3-3.3l1.6-2.75 7.65-13.25c.8-1.4 1.2-2.95 1.2-4.5h-27.502l5.852 11.5z" fill="#ea4335"/><path d="m43.65 25 13.75-23.8c-1.35-.8-2.9-1.2-4.5-1.2h-18.5c-1.6 0-3.15.45-4.5 1.2z" fill="#00832d"/><path d="m59.8 53h-32.3l-13.75 23.8c1.35.8 2.9 1.2 4.5 1.2h50.8c1.6 0 3.15-.45 4.5-1.2z" fill="#2684fc"/><path d="m73.4 26.5-12.7-22c-.8-1.4-1.95-2.5-3.3-3.3l-13.75 23.8 16.15 28h27.45c0-1.55-.4-3.1-1.2-4.5z" fill="#ffba00"/></svg>

After

Width:  |  Height:  |  Size: 742 B

View file

@ -10,7 +10,7 @@ frappe.db = {
if (!args.fields) {
args.fields = ['name'];
}
if (!args.limit) {
if (!('limit' in args)) {
args.limit = 20;
}
return new Promise ((resolve) => {

View file

@ -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 }}
@ -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',
@ -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: {
@ -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: {
@ -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);
@ -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())

View file

@ -5,7 +5,6 @@ frappe.ui.form.Dashboard = class FormDashboard {
constructor(opts) {
$.extend(this, opts);
this.setup_dashboard_sections();
this.set_open_count = frappe.utils.throttle(this.set_open_count, 500);
}
setup_dashboard_sections() {
@ -179,7 +178,6 @@ frappe.ui.form.Dashboard = class FormDashboard {
return;
}
this.render_links();
// this.set_open_count();
show = true;
}
@ -206,6 +204,7 @@ frappe.ui.form.Dashboard = class FormDashboard {
$(this).removeClass('hidden');
}
});
this.set_open_count();
}
init_data() {

View file

@ -171,8 +171,8 @@ frappe.router = {
} else {
standard_route = ['List', doctype_route.doctype, frappe.utils.to_title_case(route[2])];
if (route[3]) {
// calendar / kanban / dashboard name
standard_route.push(route[3]);
// calendar / kanban / dashboard / folder name
standard_route.push(...route.splice(3, route.length));
}
}
return standard_route;
@ -297,8 +297,8 @@ frappe.router = {
if (route[2] && route[2] !== 'list' && !$.isPlainObject(route[2])) {
new_route = [this.slug(route[1]), 'view', route[2].toLowerCase()];
// calendar / inbox
if (route[3]) new_route.push(route[3]);
// calendar / inbox / file folder
if (route[3]) new_route.push(...route.slice(3, route.length));
} else {
if ($.isPlainObject(route[2])) {
frappe.route_options = route[2];

View file

@ -116,10 +116,7 @@ frappe.ui.Capture = class {
})
.catch(err => {
if (this.options.error) {
const alert = `<span class="indicator red"/> ${
frappe.ui.Capture.ERR_MESSAGE
}`;
frappe.show_alert(alert, 3);
frappe.show_alert(frappe.ui.Capture.ERR_MESSAGE, 3);
}
throw err;

View file

@ -315,7 +315,7 @@ frappe.views.FileView = class FileView extends frappe.views.ListView {
acc += "/" + curr;
}
return acc;
}, "/app/file");
}, "/app/file/view");
return `<a href="${route}">${folder}</a>`;
})

View file

@ -252,7 +252,7 @@ class DesktopPage {
return;
}
this.refresh();
}).finally(this.page.find('.workspace_loading_skeleton').remove);
}).finally(() => this.page.find('.workspace_loading_skeleton').remove());
}
refresh() {

View file

@ -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);
}
}
}

View file

@ -11,6 +11,7 @@ html {
/* Works on Chrome, Edge, and Safari */
*::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb-color);
border-radius: 6px;
}
*::-webkit-scrollbar-track,

View file

@ -0,0 +1,8 @@
# Copyright (c) 2021, Frappe Technologies and Contributors
# See license.txt
# import frappe
import unittest
class TestWorkflowAction(unittest.TestCase):
pass

View file

@ -1,262 +1,77 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"actions": [],
"creation": "2018-05-17 18:29:03.923384",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"status",
"reference_name",
"reference_doctype",
"user",
"workflow_state",
"completed_by"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"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": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nCompleted",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"options": "Open\nCompleted"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"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": "Reference Name",
"length": 0,
"no_copy": 0,
"options": "reference_doctype",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reference_doctype",
"fieldtype": "Link",
"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": "Reference Document Type",
"length": 0,
"no_copy": 0,
"options": "DocType",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "workflow_state",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Workflow State",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "completed_by",
"fieldtype": "Link",
"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": "Completed By",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"options": "User"
}
],
"has_web_view": 0,
"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": "2019-09-05 14:22:27.664645",
"links": [],
"modified": "2021-07-01 09:07:52.848618",
"modified_by": "Administrator",
"module": "Workflow",
"name": "Workflow Action",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 1,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "All",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
"role": "All"
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "reference_name",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
"track_changes": 1
}