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"
1927import " @uppy/core/dist/style.css"
2028import " @uppy/dashboard/dist/style.css"
2129import " @uppy/image-editor/dist/style.css"
2230import " @uppy/webcam/dist/style.css"
2331import Uppy from " @uppy/core"
32+ import Webcam from " @uppy/webcam"
2433import { 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
3037import { useRouter } from " vue-router"
3138import { ENTRYPOINT } from " ../../config/entrypoint"
@@ -48,25 +55,30 @@ const { gid, sid, cid } = useCidReq()
4855const { onCreated } = useUpload ()
4956const { 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
7183function 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 ())
8295const 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+
189208function 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