Skip to content

Commit b7b6498

Browse files
Merge pull request #7131 from christianbeeznest/fixes-updates193
Internal: Fix personal file manager upload for embedded editor
2 parents e573d86 + f15add9 commit b7b6498

File tree

1 file changed

+160
-125
lines changed

1 file changed

+160
-125
lines changed
Lines changed: 160 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,38 @@
11
<template>
22
<BaseToolbar v-if="!embedded">
3-
<BaseButton :label="t('Back')" icon="back" type="black" @click="back" />
3+
<BaseButton
4+
:label="t('Back')"
5+
icon="back"
6+
type="black"
7+
@click="back"
8+
/>
49
</BaseToolbar>
10+
511
<div class="flex flex-col justify-center items-center">
612
<div class="mb-4 w-full">
713
<Dashboard
8-
v-if="uppy"
14+
:plugins="['Webcam', 'ImageEditor']"
15+
:props="{
16+
proudlyDisplayPoweredByUppy: false,
17+
width: '100%',
18+
height: '350px',
19+
}"
920
:uppy="uppy"
10-
:proudlyDisplayPoweredByUppy="false"
11-
:width="'100%'"
12-
:height="'350px'"
1321
/>
1422
</div>
1523
</div>
1624
</template>
1725
<script setup>
18-
import { ref, onMounted } from "vue"
26+
import { ref, onBeforeUnmount } from "vue"
1927
import "@uppy/core/dist/style.css"
2028
import "@uppy/dashboard/dist/style.css"
2129
import "@uppy/image-editor/dist/style.css"
2230
import "@uppy/webcam/dist/style.css"
2331
import Uppy from "@uppy/core"
32+
import Webcam from "@uppy/webcam"
2433
import { Dashboard } from "@uppy/vue"
25-
26-
const Webcam = require("@uppy/webcam").default
27-
const XHRUpload = require("@uppy/xhr-upload").default
28-
const ImageEditor = require("@uppy/image-editor").default
34+
import XHRUpload from "@uppy/xhr-upload"
35+
import ImageEditor from "@uppy/image-editor"
2936
3037
import { useRouter } from "vue-router"
3138
import { ENTRYPOINT } from "../../config/entrypoint"
@@ -48,25 +55,30 @@ const { gid, sid, cid } = useCidReq()
4855
const { onCreated } = useUpload()
4956
const { t } = useI18n()
5057
51-
const LOG_PREFIX = "[UPLOAD DBG]"
52-
function log(...args) { console.log(LOG_PREFIX, ...args) }
58+
const LOG_PREFIX = "[FILEMANAGER UPLOAD]"
59+
function log(...args) {
60+
// Keep logs in English for easier debugging
61+
console.log(LOG_PREFIX, ...args)
62+
}
5363
54-
function resolveParentFromSessionThenProp() {
64+
/**
65+
* Resolve parent node from session storage (pf_parent) or component props.
66+
* This mirrors the legacy file manager behavior.
67+
*/
68+
function resolveParentResourceNodeId() {
5569
try {
56-
const ssRaw = sessionStorage.getItem("pf_parent")
57-
const ss = Number(ssRaw || 0)
58-
if (ss) {
59-
return ss
70+
const pfParentRaw = sessionStorage.getItem("pf_parent")
71+
const pfParent = Number(pfParentRaw || 0)
72+
if (pfParent) {
73+
return pfParent
6074
}
61-
const p = Number(props.parentResourceNodeId || 0)
62-
return p || 0
6375
} catch (e) {
64-
return Number(props.parentResourceNodeId || 0)
76+
log("Failed to read pf_parent from sessionStorage, falling back to props", e)
6577
}
66-
}
6778
68-
const parentIdRef = ref(0)
69-
const fileTypeRef = ref(String(props.filetype || "file"))
79+
const fromProps = Number(props.parentResourceNodeId || 0)
80+
return fromProps || 0
81+
}
7082
7183
function buildEndpoint(parentId) {
7284
const pid = String(Number(parentId || 0))
@@ -75,122 +87,145 @@ function buildEndpoint(parentId) {
7587
parentResourceNodeId: pid,
7688
parent: pid,
7789
}).toString()
78-
const ep = `${ENTRYPOINT}personal_files?${qs}`
79-
return ep
90+
91+
return `${ENTRYPOINT}personal_files?${qs}`
8092
}
8193
94+
const parentResourceNodeId = ref(resolveParentResourceNodeId())
8295
const uploadedItems = ref([])
83-
const uppy = ref(null)
8496
85-
function applyCurrentParentToUppy(hook = "") {
86-
const freshParent = resolveParentFromSessionThenProp()
87-
parentIdRef.value = freshParent
88-
89-
const plugin = uppy.value?.getPlugin?.("XHRUpload")
90-
91-
if (plugin?.setOptions) {
92-
const newEndpoint = buildEndpoint(freshParent)
93-
plugin.setOptions({ endpoint: newEndpoint })
94-
}
95-
96-
const beforeMeta = uppy.value?.getMeta?.() || {}
97-
uppy.value?.setMeta({
98-
filetype: fileTypeRef.value,
99-
parentResourceNodeId: String(freshParent),
100-
parentResourceNode: `/api/resource_nodes/${freshParent}`,
101-
"resourceNode.parent": String(freshParent),
102-
resourceLinkList: JSON.stringify([{ gid, sid, cid, visibility: RESOURCE_LINK_PUBLISHED }]),
103-
isUncompressZipEnabled: false,
104-
fileExistsOption: "rename",
97+
const allowedFiletypes = ["file", "video", "certificate"]
98+
const filetype = allowedFiletypes.includes(props.filetype) ? props.filetype : "file"
99+
100+
const resourceLinkList = JSON.stringify([
101+
{
102+
gid,
103+
sid,
104+
cid,
105+
visibility: RESOURCE_LINK_PUBLISHED,
106+
},
107+
])
108+
109+
// Advanced options defaults – we do not expose UI here, just send sane defaults
110+
const isUncompressZipEnabled = false
111+
const fileExistsOption = "rename"
112+
113+
/**
114+
* Single shared Uppy instance (same pattern as DocumentUpload.vue).
115+
* This avoids reactivity wrappers around the instance.
116+
*/
117+
const uppy = new Uppy({ autoProceed: false })
118+
.use(Webcam)
119+
.use(ImageEditor, {
120+
cropperOptions: {
121+
viewMode: 1,
122+
background: false,
123+
autoCropArea: 1,
124+
responsive: true,
125+
},
126+
actions: {
127+
revert: true,
128+
rotate: true,
129+
granularRotate: true,
130+
flip: true,
131+
zoomIn: true,
132+
zoomOut: true,
133+
cropSquare: true,
134+
cropWidescreen: true,
135+
cropWidescreenVertical: true,
136+
},
105137
})
106-
const afterMeta = uppy.value?.getMeta?.() || {}
107-
const ep = plugin?.opts?.endpoint
108-
}
109-
110-
onMounted(() => {
111-
parentIdRef.value = resolveParentFromSessionThenProp()
112-
uppy.value = new Uppy({
113-
autoProceed: true,
114-
debug: true,
115-
restrictions: { allowedFileTypes: fileTypeRef.value === "certificate" ? [".html"] : null },
138+
.use(XHRUpload, {
139+
endpoint: buildEndpoint(parentResourceNodeId.value),
140+
formData: true,
141+
fieldName: "uploadFile",
142+
allowedMetaFields: [
143+
"filetype",
144+
"parentResourceNodeId",
145+
"parentResourceNode",
146+
"resourceNode.parent",
147+
"resourceLinkList",
148+
"isUncompressZipEnabled",
149+
"fileExistsOption",
150+
],
116151
})
117-
.use(ImageEditor, {
118-
cropperOptions: { viewMode: 1, background: false, autoCropArea: 1, responsive: true },
119-
actions: {
120-
revert: true, rotate: true, granularRotate: true, flip: true,
121-
zoomIn: true, zoomOut: true, cropSquare: true, cropWidescreen: true, cropWidescreenVertical: true,
122-
},
123-
})
124-
.use(XHRUpload, {
125-
endpoint: buildEndpoint(parentIdRef.value),
126-
formData: true,
127-
fieldName: "uploadFile",
128-
allowedMetaFields: [
129-
"filetype",
130-
"parentResourceNodeId",
131-
"parentResourceNode",
132-
"resourceNode.parent",
133-
"resourceLinkList",
134-
"isUncompressZipEnabled",
135-
"fileExistsOption",
136-
],
137-
})
138-
.on("file-added", (file) => {
139-
applyCurrentParentToUppy("file-added")
140-
})
141-
.on("upload", (data) => {
142-
applyCurrentParentToUppy("upload")
143-
})
144-
.on("upload-progress", (file, progress) => {
145-
})
146-
.on("upload-success", (file, response) => {
147-
onCreated(response?.body)
148-
if (response?.body) uploadedItems.value.push(response.body)
149-
})
150-
.on("complete", (result) => {
151-
if (props.embedded) {
152-
emit("done", { parentNodeId: parentIdRef.value, items: uploadedItems.value })
153-
uploadedItems.value = []
154-
return
155-
}
156-
router.push({ name: "FileManagerList", params: { node: parentIdRef.value } })
157-
})
158-
.on("error", (err) => {
159-
})
160-
.on("restriction-failed", (file, error) => {
161-
})
152+
.on("upload-success", (_file, response) => {
153+
log("Upload success", response)
154+
if (response?.body) {
155+
onCreated(response.body)
156+
uploadedItems.value.push(response.body)
157+
}
158+
})
159+
.on("complete", () => {
160+
const parentNodeId = parentResourceNodeId.value
161+
log("Upload complete, items:", uploadedItems.value, "parent:", parentNodeId)
162+
163+
// Embedded mode (e.g. editor): notify parent and stay in Vue world
164+
if (props.embedded) {
165+
emit("done", {
166+
parentNodeId,
167+
items: uploadedItems.value,
168+
})
169+
uploadedItems.value = []
170+
return
171+
}
162172
163-
if (fileTypeRef.value !== "certificate") {
164-
uppy.value.use(Webcam)
165-
}
173+
// Standalone file manager: go back to listing
174+
router.push({
175+
name: "FileManagerList",
176+
params: { node: parentNodeId || 0 },
177+
query: { cid, sid, gid },
178+
})
179+
})
180+
.on("error", (err) => {
181+
// Avoid crashing the app on Uppy internal errors
182+
log("Uppy error", err)
183+
})
184+
.on("restriction-failed", (file, error) => {
185+
log("Uppy restriction failed", file?.name, error?.message || error)
186+
})
166187
167-
const initialMeta = {
168-
filetype: fileTypeRef.value,
169-
parentResourceNodeId: String(parentIdRef.value),
170-
parentResourceNode: `/api/resource_nodes/${parentIdRef.value}`,
171-
"resourceNode.parent": String(parentIdRef.value),
172-
resourceLinkList: JSON.stringify([{ gid, sid, cid, visibility: RESOURCE_LINK_PUBLISHED }]),
173-
isUncompressZipEnabled: false,
174-
fileExistsOption: "rename",
175-
}
176-
uppy.value.setMeta(initialMeta)
177-
178-
const initialEndpoint = uppy.value.getPlugin("XHRUpload")?.opts?.endpoint
179-
window.__UPPY_DBG = () => {
180-
try {
181-
const plugin = uppy.value?.getPlugin?.("XHRUpload")
182-
const ep = plugin?.opts?.endpoint
183-
} catch (e) {
184-
log("__UPPY_DBG error:", e)
185-
}
186-
}
188+
// Initial meta – mirrors DocumentUpload but targeting personal_files
189+
uppy.setMeta({
190+
filetype,
191+
parentResourceNodeId: parentResourceNodeId.value,
192+
parentResourceNode: `/api/resource_nodes/${parentResourceNodeId.value}`,
193+
"resourceNode.parent": parentResourceNodeId.value,
194+
resourceLinkList,
195+
isUncompressZipEnabled,
196+
fileExistsOption,
187197
})
188198
199+
// Per-filetype restrictions (same concept as DocumentUpload.vue)
200+
if (filetype === "certificate") {
201+
uppy.setOptions({ restrictions: { allowedFileTypes: [".html"] } })
202+
} else if (filetype === "video") {
203+
uppy.setOptions({ restrictions: { allowedFileTypes: ["video/*"] } })
204+
} else {
205+
uppy.setOptions({ restrictions: { allowedFileTypes: null } })
206+
}
207+
189208
function back() {
209+
// Embedded (editor): close popup and let parent decide
190210
if (props.embedded) {
191211
emit("cancel")
192212
return
193213
}
194-
router.push({ name: "FileManagerList", params: { node: parentIdRef.value || 0 } })
214+
215+
const parentNodeId = parentResourceNodeId.value || 0
216+
router.push({
217+
name: "FileManagerList",
218+
params: { node: parentNodeId },
219+
query: { cid, sid, gid },
220+
})
195221
}
222+
223+
onBeforeUnmount(() => {
224+
try {
225+
log("Closing Uppy instance from Upload.vue")
226+
uppy.close()
227+
} catch (e) {
228+
log("Error while closing Uppy", e)
229+
}
230+
})
196231
</script>

0 commit comments

Comments
 (0)