Merge pull request #31996 from barredterra/public-file-warning
feat: warn when uploading public files
This commit is contained in:
commit
43a8dbfcbf
2 changed files with 118 additions and 82 deletions
|
|
@ -1,69 +1,82 @@
|
|||
<template>
|
||||
<div class="file-preview">
|
||||
<div class="file-icon">
|
||||
<img v-if="is_image" :src="src" :alt="file.name" />
|
||||
<div class="fallback" v-else v-html="frappe.utils.icon('file', 'md')"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<a class="flex" :href="file.doc.file_url" v-if="file.doc" target="_blank">
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
</a>
|
||||
<span class="file-name" v-else>{{ file.name }}</span>
|
||||
<div class="file-preview-outline">
|
||||
<div class="file-preview">
|
||||
<div class="file-icon">
|
||||
<img v-if="is_image" :src="src" :alt="file.name" />
|
||||
<div class="fallback" v-else v-html="frappe.utils.icon('file', 'md')"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<a class="flex" :href="file.doc.file_url" v-if="file.doc" target="_blank">
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
</a>
|
||||
<span class="file-name" v-else>{{ file.name }}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="file-size">
|
||||
{{ file_size }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="file-size">
|
||||
{{ file_size }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex config-area">
|
||||
<label v-if="allow_toggle_optimize" class="frappe-checkbox"
|
||||
><input
|
||||
type="checkbox"
|
||||
:checked="optimize"
|
||||
@change="emit('toggle_optimize')"
|
||||
/>{{ __("Optimize") }}</label
|
||||
>
|
||||
<label v-if="allow_toggle_private" class="frappe-checkbox"
|
||||
><input
|
||||
type="checkbox"
|
||||
:checked="file.private"
|
||||
@change="emit('toggle_private')"
|
||||
/>{{ __("Private") }}</label
|
||||
>
|
||||
<div class="flex config-area">
|
||||
<label v-if="allow_toggle_optimize" class="frappe-checkbox"
|
||||
><input
|
||||
type="checkbox"
|
||||
:checked="optimize"
|
||||
@change="emit('toggle_optimize')"
|
||||
/>{{ __("Optimize") }}</label
|
||||
>
|
||||
<label v-if="allow_toggle_private" class="frappe-checkbox"
|
||||
><input
|
||||
type="checkbox"
|
||||
:checked="file.private"
|
||||
@change="emit('toggle_private')"
|
||||
/>{{ __("Private") }}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span v-if="file.error_message" class="file-error text-danger">
|
||||
{{ file.error_message }}
|
||||
</span>
|
||||
<div class="file-actions">
|
||||
<ProgressRing
|
||||
v-show="file.uploading && !uploaded && !file.failed"
|
||||
primary="var(--primary-color)"
|
||||
secondary="var(--gray-200)"
|
||||
:radius="24"
|
||||
:progress="progress"
|
||||
:stroke="3"
|
||||
/>
|
||||
<div v-if="uploaded" v-html="frappe.utils.icon('solid-success', 'lg')"></div>
|
||||
<div v-if="file.failed" v-html="frappe.utils.icon('solid-error', 'lg')"></div>
|
||||
<div class="file-action-buttons">
|
||||
<button
|
||||
v-if="is_cropable"
|
||||
class="btn btn-crop muted"
|
||||
@click="emit('toggle_image_cropper')"
|
||||
v-html="frappe.utils.icon('crop', 'md')"
|
||||
></button>
|
||||
<button
|
||||
v-if="!uploaded && !file.uploading && !file.failed"
|
||||
class="btn muted"
|
||||
@click="emit('remove')"
|
||||
v-html="frappe.utils.icon('delete', 'md')"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-actions">
|
||||
<ProgressRing
|
||||
v-show="file.uploading && !uploaded && !file.failed"
|
||||
primary="var(--primary-color)"
|
||||
secondary="var(--gray-200)"
|
||||
:radius="24"
|
||||
:progress="progress"
|
||||
:stroke="3"
|
||||
/>
|
||||
<div v-if="uploaded" v-html="frappe.utils.icon('solid-success', 'lg')"></div>
|
||||
<div v-if="file.failed" v-html="frappe.utils.icon('solid-error', 'lg')"></div>
|
||||
<div class="file-action-buttons">
|
||||
<button
|
||||
v-if="is_cropable"
|
||||
class="btn btn-crop muted"
|
||||
@click="emit('toggle_image_cropper')"
|
||||
v-html="frappe.utils.icon('crop', 'md')"
|
||||
></button>
|
||||
<button
|
||||
v-if="!uploaded && !file.uploading && !file.failed"
|
||||
class="btn muted"
|
||||
@click="emit('remove')"
|
||||
v-html="frappe.utils.icon('delete', 'md')"
|
||||
></button>
|
||||
<div style="width: 100%">
|
||||
<div v-if="file.error_message" class="alert alert-danger mb-0 mt-2" role="alert">
|
||||
{{ file.error_message }}
|
||||
</div>
|
||||
<div
|
||||
v-if="!file.private && !file.error_message"
|
||||
class="alert alert-warning mb-0"
|
||||
role="alert"
|
||||
>
|
||||
{{
|
||||
__(
|
||||
"This file is public and can be accessed by anyone, even without logging in. Mark it private to limit access."
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -114,6 +127,9 @@ let allow_toggle_optimize = computed(() => {
|
|||
!props.file.failed
|
||||
);
|
||||
});
|
||||
let allow_toggle_private = computed(() => {
|
||||
return props.allow_toggle_private && !uploaded.value && !props.file.failed;
|
||||
});
|
||||
let is_cropable = computed(() => {
|
||||
let croppable_types = ["image/jpeg", "image/png"];
|
||||
return (
|
||||
|
|
@ -144,24 +160,30 @@ onMounted(() => {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-preview-outline {
|
||||
padding: 0.75rem;
|
||||
border: 1px solid transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.file-preview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid transparent;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.file-preview + .file-preview {
|
||||
.file-preview-outline + .file-preview-outline {
|
||||
border-top-color: var(--border-color);
|
||||
}
|
||||
|
||||
.file-preview:hover {
|
||||
.file-preview-outline:hover {
|
||||
background-color: var(--bg-color);
|
||||
border-color: var(--dark-border-color);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.file-preview:hover + .file-preview {
|
||||
.file-preview-outline:hover + .file-preview-outline {
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
|
|
@ -242,9 +264,4 @@ onMounted(() => {
|
|||
.config-area {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.file-error {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--text-bold);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -595,23 +595,22 @@ function upload_file(file, i) {
|
|||
}
|
||||
} else if (xhr.status === 403) {
|
||||
file.failed = true;
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
file.error_message = `Not permitted. ${response._error_message || ""}.`;
|
||||
|
||||
try {
|
||||
// Append server messages which are useful hint for perm issues
|
||||
let server_messages = JSON.parse(response._server_messages);
|
||||
|
||||
server_messages.forEach((m) => {
|
||||
m = JSON.parse(m);
|
||||
file.error_message += `\n ${m.message} `;
|
||||
});
|
||||
} catch (e) {
|
||||
console.warning("Failed to parse server message", e);
|
||||
let response = parse_error_response(xhr.responseText);
|
||||
file.error_message = `Not permitted. ${response.error_message || ""}.`;
|
||||
if (response.server_messages.length) {
|
||||
file.error_message += `\n${response.server_messages.join("\n")}`;
|
||||
}
|
||||
} else if (xhr.status === 413) {
|
||||
file.failed = true;
|
||||
file.error_message = "Size exceeds the maximum allowed file size.";
|
||||
} else if (xhr.status === 417) {
|
||||
// regular frappe.throw() in backend
|
||||
file.failed = true;
|
||||
file.error_message = null;
|
||||
let response = parse_error_response(xhr.responseText);
|
||||
if (response.server_messages.length) {
|
||||
file.error_message = response.server_messages.join("\n");
|
||||
}
|
||||
} else {
|
||||
file.failed = true;
|
||||
file.error_message =
|
||||
|
|
@ -679,6 +678,26 @@ function upload_file(file, i) {
|
|||
xhr.send(form_data);
|
||||
});
|
||||
}
|
||||
function parse_error_response(response_text) {
|
||||
let response = JSON.parse(response_text);
|
||||
let error_message = response._error_message;
|
||||
let server_messages = [];
|
||||
|
||||
try {
|
||||
server_messages.push(
|
||||
...JSON.parse(response._server_messages).map((m) => {
|
||||
let parsed = JSON.parse(m);
|
||||
return parsed.message;
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
console.warning("Failed to parse server message", e);
|
||||
}
|
||||
return {
|
||||
error_message,
|
||||
server_messages,
|
||||
};
|
||||
}
|
||||
function capture_image() {
|
||||
const capture = new frappe.ui.Capture({
|
||||
animate: false,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue