Merge pull request #33753 from frappe/drive-integration
feat: allow adding other methods of file upload
This commit is contained in:
commit
98eaa04b2e
4 changed files with 63 additions and 10 deletions
|
|
@ -17,7 +17,13 @@ from frappe.database.schema import SPECIAL_CHAR_PATTERN
|
|||
from frappe.exceptions import DoesNotExistError
|
||||
from frappe.model.document import Document
|
||||
from frappe.permissions import SYSTEM_USER_ROLE, get_doctypes_with_read
|
||||
from frappe.utils import call_hook_method, cint, get_files_path, get_hook_method, get_url
|
||||
from frappe.utils import (
|
||||
call_hook_method,
|
||||
cint,
|
||||
get_files_path,
|
||||
get_hook_method,
|
||||
get_url,
|
||||
)
|
||||
from frappe.utils.file_manager import is_safe_path
|
||||
from frappe.utils.image import optimize_image, strip_exif_data
|
||||
|
||||
|
|
@ -31,7 +37,7 @@ from .utils import *
|
|||
|
||||
exclude_from_linked_with = True
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
URL_PREFIXES = ("http://", "https://")
|
||||
URL_PREFIXES = ("http://", "https://", "/api/method/")
|
||||
FILE_ENCODING_OPTIONS = ("utf-8-sig", "utf-8", "windows-1250", "windows-1252")
|
||||
|
||||
|
||||
|
|
@ -139,7 +145,10 @@ class File(Document):
|
|||
return
|
||||
|
||||
if not self.attached_to_name or not isinstance(self.attached_to_name, str | int):
|
||||
frappe.throw(_("Attached To Name must be a string or an integer"), frappe.ValidationError)
|
||||
frappe.throw(
|
||||
_("Attached To Name must be a string or an integer"),
|
||||
frappe.ValidationError,
|
||||
)
|
||||
|
||||
if self.attached_to_field and SPECIAL_CHAR_PATTERN.search(self.attached_to_field):
|
||||
frappe.throw(_("The fieldname you've specified in Attached To Field is invalid"))
|
||||
|
|
@ -213,8 +222,8 @@ class File(Document):
|
|||
if self.is_remote_file or not self.file_url:
|
||||
return
|
||||
|
||||
if not self.file_url.startswith(("/files/", "/private/files/")):
|
||||
# Probably an invalid URL since it doesn't start with http either
|
||||
if not self.file_url.startswith(("/files/", "/private/files/", "/api/method/")):
|
||||
# Probably an invalid URL since it doesn't start with http and isn't an internal URL either
|
||||
frappe.throw(
|
||||
_("URL must start with http:// or https://"),
|
||||
title=_("Invalid URL"),
|
||||
|
|
@ -318,7 +327,9 @@ class File(Document):
|
|||
if current_attachment_count >= attachment_limit:
|
||||
frappe.throw(
|
||||
_("Maximum Attachment Limit of {0} has been reached for {1} {2}.").format(
|
||||
frappe.bold(attachment_limit), self.attached_to_doctype, self.attached_to_name
|
||||
frappe.bold(attachment_limit),
|
||||
self.attached_to_doctype,
|
||||
self.attached_to_name,
|
||||
),
|
||||
exc=AttachmentLimitReached,
|
||||
title=_("Attachment Limit Reached"),
|
||||
|
|
@ -372,7 +383,10 @@ class File(Document):
|
|||
return
|
||||
|
||||
if self.file_type not in allowed_extensions.splitlines():
|
||||
frappe.throw(_("File type of {0} is not allowed").format(self.file_type), exc=FileTypeNotAllowed)
|
||||
frappe.throw(
|
||||
_("File type of {0} is not allowed").format(self.file_type),
|
||||
exc=FileTypeNotAllowed,
|
||||
)
|
||||
|
||||
def validate_duplicate_entry(self):
|
||||
if not self.flags.ignore_duplicate_entry_error and not self.is_folder:
|
||||
|
|
@ -407,7 +421,8 @@ class File(Document):
|
|||
def set_file_name(self):
|
||||
if not self.file_name and not self.file_url:
|
||||
frappe.throw(
|
||||
_("Fields `file_name` or `file_url` must be set for File"), exc=frappe.MandatoryError
|
||||
_("Fields `file_name` or `file_url` must be set for File"),
|
||||
exc=frappe.MandatoryError,
|
||||
)
|
||||
elif not self.file_name and self.file_url:
|
||||
self.file_name = self.file_url.split("/")[-1]
|
||||
|
|
@ -779,6 +794,9 @@ class File(Document):
|
|||
frappe.clear_messages()
|
||||
|
||||
def set_is_private(self):
|
||||
if self.is_private:
|
||||
return
|
||||
|
||||
if self.file_url:
|
||||
self.is_private = cint(self.file_url.startswith("/private"))
|
||||
|
||||
|
|
|
|||
|
|
@ -163,6 +163,23 @@
|
|||
</svg>
|
||||
<div class="mt-1">{{ __("Google Drive") }}</div>
|
||||
</button>
|
||||
<template v-for="option in additional_upload_handlers">
|
||||
<button class="btn btn-file-upload" @click="option.wrappedAction">
|
||||
<svg
|
||||
v-if="typeof option.icon === 'string'"
|
||||
v-html="option.icon"
|
||||
width="30"
|
||||
height="30"
|
||||
/>
|
||||
<component
|
||||
v-else-if="option.icon"
|
||||
:is="option.icon"
|
||||
width="30"
|
||||
height="30"
|
||||
/>
|
||||
<div class="mt-1">{{ option.label }}</div>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<div class="mt-3 text-center" v-if="upload_notes">
|
||||
{{ upload_notes }}
|
||||
|
|
@ -291,6 +308,9 @@ const props = defineProps({
|
|||
allow_google_drive: {
|
||||
default: true,
|
||||
},
|
||||
additional_upload_handlers: {
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
// variables
|
||||
|
|
@ -645,7 +665,9 @@ function upload_file(file, i) {
|
|||
if (file.file_url) {
|
||||
form_data.append("file_url", file.file_url);
|
||||
}
|
||||
|
||||
if (file.file_size) {
|
||||
form_data.append("file_size", file.file_size);
|
||||
}
|
||||
if (file.file_name) {
|
||||
form_data.append("file_name", file.file_name);
|
||||
}
|
||||
|
|
@ -762,6 +784,7 @@ watch(
|
|||
defineExpose({
|
||||
files,
|
||||
add_files,
|
||||
upload_file,
|
||||
upload_files,
|
||||
toggle_all_private,
|
||||
wrapper_ready,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import FileUploaderComponent from "./FileUploader.vue";
|
|||
import { watch } from "vue";
|
||||
|
||||
class FileUploader {
|
||||
static UploadOptions = [];
|
||||
constructor({
|
||||
wrapper,
|
||||
method,
|
||||
|
|
@ -65,6 +66,17 @@ class FileUploader {
|
|||
allow_toggle_private,
|
||||
allow_toggle_optimize,
|
||||
allow_google_drive,
|
||||
additional_upload_handlers: this.constructor.UploadOptions.map((k) => ({
|
||||
...k,
|
||||
wrappedAction: () =>
|
||||
k.action({
|
||||
dialog: this.dialog,
|
||||
uploader: this.uploader,
|
||||
doctype,
|
||||
docname,
|
||||
fieldname,
|
||||
}),
|
||||
})),
|
||||
});
|
||||
SetVueGlobals(app);
|
||||
this.uploader = app.mount(this.wrapper);
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@ def add_attachments(doctype, name, attachments):
|
|||
|
||||
|
||||
def is_safe_path(path: str) -> bool:
|
||||
if path.startswith(("http://", "https://")):
|
||||
if path.startswith(("http://", "https://", "/api/method/")):
|
||||
return True
|
||||
|
||||
basedir = frappe.get_site_path()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue