Skip to content

Commit 78adcfd

Browse files
AlexAndBearclaudeJammingBen
authored
fix: encode hash character in app route URLs to prevent truncation (#3075)
* fix: encode hash character in file paths for app URLs Files and folders containing hash characters (#) in their names were causing issues when opened in apps (text-editor, preview) because the browser treats # as a URL fragment identifier, truncating the URL. Changes: - Import and use encodePath in useFileActions to encode path segments - Encode hash characters segment-wise in getEditorRouteOpts() - Build URLs manually in useFileActionsOpenWithApp and SaveAsModal to avoid double encoding from router.resolve() - Add unit tests to verify correct encoding behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * format * react to code review * fix: opening document from inside collabora * cleanup --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jannik Stehle <j.stehle@opencloud.eu>
1 parent 1a8bef3 commit 78adcfd

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

packages/web-pkg/src/composables/actions/files/useFileActions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ export const useFileActions = () => {
184184
if (!routeName || !router.hasRoute(routeName)) {
185185
return null
186186
}
187-
const routeOpts = getEditorRouteOpts(routeName, space, resource, remoteItemId)
188-
return router.resolve(routeOpts)
187+
188+
return getEditorRouteOpts(routeName, space, resource, remoteItemId)
189189
}
190190
const getEditorRouteOpts = (
191191
routeName: RouteRecordName,

packages/web-pkg/tests/unit/composables/actions/files/useFileActions.spec.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,51 @@ describe('fileActions', () => {
103103
}
104104
})
105105
mocks.$router.hasRoute.mockReturnValue(true)
106+
107+
const mockSpace = mock<SpaceResource>({
108+
getDriveAliasAndItem: () => 'personal/admin/test.txt'
109+
})
110+
106111
const result = editorAction.route({
107-
space: mock<SpaceResource>(),
112+
space: mockSpace,
108113
resources: actionOptions.resources
109114
})
110115
expect(result).not.toBeNull()
111116
})
117+
118+
it('returns unresolved route options so the router encodes the path', () => {
119+
let editorAction: FileAction
120+
const { mocks } = getWrapper({
121+
setup: ({ getAllOpenWithActions }) => {
122+
const actions = getAllOpenWithActions(actionOptions)
123+
editorAction = actions.find((a) => a.name === 'editor-text-editor')
124+
}
125+
})
126+
mocks.$router.hasRoute.mockReturnValue(true)
127+
128+
const mockSpace = mock<SpaceResource>({
129+
getDriveAliasAndItem: () => 'personal/admin/ticket#1234/logs.txt'
130+
})
131+
const mockResource = mock<Resource>({
132+
path: '/ticket#1234/logs.txt',
133+
fileId: 'test-file-id'
134+
})
135+
136+
const route = editorAction.route({
137+
space: mockSpace,
138+
resources: [mockResource]
139+
})
140+
141+
// Resolving is left to the router. Handing it an already resolved location
142+
// or a pre-encoded path both break the encoding of characters like `#`.
143+
expect(route).toEqual(
144+
expect.objectContaining({
145+
name: 'text-editor',
146+
params: { driveAliasAndItem: 'personal/admin/ticket#1234/logs.txt' }
147+
})
148+
)
149+
expect(route).not.toHaveProperty('href')
150+
})
112151
})
113152

114153
describe('secure view context', () => {

0 commit comments

Comments
 (0)