Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 85 additions & 8 deletions drive/api/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import jwt
import magic
import mimemapper
import shutil
from pypika import Order
from werkzeug.utils import secure_filename, send_file
from werkzeug.wrappers import Response
Expand Down Expand Up @@ -200,7 +201,7 @@ def create_document_entity(title, personal, team, content, parent=None):
)
drive_doc = frappe.new_doc("Drive Document")
drive_doc.title = title
drive_doc.content = content
drive_doc.raw_content = content
drive_doc.version = 2
drive_doc.save()

Expand Down Expand Up @@ -541,24 +542,27 @@ def remove_or_restore(entity_names):
def depth_zero_toggle_is_active(doc):
if doc.is_active:
flag = 0
manager.move_to_trash(doc)
if doc.path:
manager.move_to_trash(doc)
else:
storage_data = storage_bar_data(doc.team)
if (storage_data["limit"] - storage_data["total_size"]) < doc.file_size:
frappe.throw("You're out of storage!", ValueError)
manager.restore(doc)
if doc.path:
manager.restore(doc)
flag = 1

doc.is_active = flag
folder_size = frappe.db.get_value("Drive File", doc.parent_entity, "file_size")
frappe.db.set_value(
doc.save()

if doc.parent_entity:
folder_size = frappe.db.get_value("Drive File", doc.parent_entity, "file_size")
frappe.db.set_value(
"Drive File",
doc.parent_entity,
"file_size",
folder_size + doc.file_size * (1 if flag else -1),
)

doc.save()
)

for entity in entity_names:
doc = frappe.get_doc("Drive File", entity)
Expand Down Expand Up @@ -681,6 +685,79 @@ def move(entity_names, new_parent=None, is_private=None):

return res

@frappe.whitelist()
def create_copy(entity_name, parent=None):
"""
Creates a copy of file/files.

:param entity_name: Name of the file to copy.
:param parent: Optional. Parent folder.
"""
original_doc = frappe.get_doc("Drive File", entity_name)
home_folder = get_home_folder(original_doc.team)
if original_doc.is_group or original_doc.is_link:
frappe.throw("Copying folders or links is not supported.")

destination_parent = parent or original_doc.parent_entity
if not user_has_permission(entity_name, "read"):
frappe.throw(
"You do not have permission to view the original file.",
frappe.PermissionError
)
if not user_has_permission(destination_parent, "upload"):
frappe.throw(
"You do not have permission to create a file in the destination folder.",
frappe.PermissionError
)

storage_data = storage_bar_data(original_doc.team)
if (storage_data["limit"] - storage_data["total_size"]) < original_doc.file_size:
frappe.throw("Not enough storage space to create a copy.", ValueError)

new_title = get_new_title(original_doc.title, destination_parent)
new_entity = None

if original_doc.document:
original_content = frappe.get_value(
"Drive Document", original_doc.document, "raw_content"
)

new_entity = create_document_entity(
new_title,
original_doc.is_private,
original_doc.team,
original_content,
destination_parent
)
frappe.db.set_value("Drive File", new_entity.name, "title", new_title)

else:
manager = FileManager()

original_relative_path = manager.get_disk_path(original_doc, home_folder, embed=0)

new_entity = create_drive_file(
original_doc.team,
original_doc.is_private,
new_title,
destination_parent,
original_doc.mime_type,
lambda entity: manager.get_disk_path(entity, home_folder, embed=0),
original_doc.file_size
)

new_relative_path = new_entity.path
base_private_path = frappe.get_site_path("private", "files")
absolute_original_path = os.path.join(base_private_path, original_relative_path)
absolute_new_path = os.path.join(base_private_path, new_relative_path)

if not os.path.exists(os.path.dirname(absolute_new_path)):
os.makedirs(os.path.dirname(absolute_new_path))

shutil.copy2(absolute_original_path, absolute_new_path)

update_file_size(destination_parent, original_doc.file_size)
# return new_entity

@frappe.whitelist()
def search(query, team):
Expand Down
24 changes: 14 additions & 10 deletions drive/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,17 +240,21 @@ def get_file_type(r):
except StopIteration:
return "Unknown"

def update_file_size(folder_name, size_change):
if not folder_name or not isinstance(size_change, (int, float)):
return

def update_file_size(entity, delta):
doc = frappe.get_doc("Drive File", entity)
while doc.parent_entity:
doc.file_size += delta
doc.save(ignore_permissions=True)
doc = frappe.get_doc("Drive File", doc.parent_entity)
# Update root
doc.file_size += delta
doc.save(ignore_permissions=True)

frappe.db.sql("""
UPDATE `tabDrive File`
SET
`file_size` = `file_size` + %(size_change)s,
`modified` = %(now)s
WHERE `name` = %(folder_name)s
""", {
"size_change": size_change,
"folder_name": folder_name,
"now": frappe.utils.now()
}, auto_commit=True)

def if_folder_exists(team, folder_name, parent, personal):
values = {
Expand Down
48 changes: 38 additions & 10 deletions frontend/src/components/GenericPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Navbar
v-if="!verify?.error && !getEntities.error"
:root-resource="verify"
:entities="activeEntity ? [activeEntity] : selectedEntitities"
:entities="selectedEntities"
/>

<ErrorPage
Expand All @@ -19,7 +19,7 @@
<DriveToolBar
v-model="rows"
:action-items="actionItems"
:selections="selectedEntitities"
:selections="selectedEntities"
:get-entities="getEntities || { data: [] }"
/>

Expand Down Expand Up @@ -69,7 +69,7 @@ import Navbar from "@/components/Navbar.vue"
import NoFilesSection from "@/components/NoFilesSection.vue"
import ErrorPage from "@/components/ErrorPage.vue"
import { getLink, pasteObj } from "@/utils/files"
import { toggleFav, clearRecent } from "@/resources/files"
import { toggleFav, clearRecent, copyFile } from "@/resources/files"
import { allUsers } from "@/resources/permissions"
import { entitiesDownload } from "@/utils/download"
import FileUploader from "@/components/FileUploader.vue"
Expand All @@ -95,6 +95,7 @@ import LucideShare2 from "~icons/lucide/share-2"
import LucideSquarePen from "~icons/lucide/square-pen"
import LucideStar from "~icons/lucide/star"
import LucideTrash from "~icons/lucide/trash"
import LucideCopy from "~icons/lucide/copy"
import emitter from "../emitter"

const props = defineProps({
Expand Down Expand Up @@ -123,12 +124,22 @@ watch(
store.commit("setListResource", props.getEntities)

const selections = ref(new Set())
const selectedEntitities = computed(
() =>
props.getEntities.data?.filter?.(({ name }) =>
selections.value.has(name)
) || []
)
const selectedEntities = computed(() => {
if (selections.value.size > 0) {
return (
props.getEntities.data?.filter?.(({ name }) =>
selections.value.has(name)
) || []
)
}
if (activeEntity.value) {
const exists = props.getEntities.data?.find(
(e) => e.name === activeEntity.value.name
)
return exists ? [activeEntity.value] : []
}
return []
})

const verifyAccess = computed(() => props.verify?.data || !props.verify)
watchEffect(() => {
Expand Down Expand Up @@ -176,6 +187,8 @@ const onDrop = (targetFile, draggedItem) => {

// Action Items
const actionItems = computed(() => {
const invalidCopyItem = selectedEntities.value.some(e => e.is_group || e.is_link);

if (route.name === "Trash") {
return [
{
Expand Down Expand Up @@ -243,7 +256,22 @@ const actionItems = computed(() => {
label: __("Rename"),
icon: LucideSquarePen,
action: () => (dialog.value = "rn"),
isEnabled: (e) => e.write,
isEnabled: (e) => e.write,
},
{
label: __("Create Copy"),
icon: LucideCopy,
action: (entities) => {
entities.forEach((entity) => {
copyFile.submit({
entity,
parent: entity.parent_entity,
});
});
},
isEnabled: (e) => e.write && !invalidCopyItem,
multi: true,
important: true,
},
{
label: __("Show Info"),
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/resources/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,27 @@ export const createDocument = createResource({
makeParams: (params) => params,
})

export const copyFile = createResource({
url: "drive.api.files.create_copy",

makeParams(data) {
return {
entity_name: data.entity.name,
parent: data.parent,
};
},

onSuccess() { toast({title: "Copied successfully!",});},

onError(error) {
toast({
title: "Error copying file!",
description: error.message || "Something went wrong.",
variant: "error",
});
},
});

export const togglePersonal = createResource({
method: "POST",
url: "drive.api.files.call_controller_method",
Expand Down