55<template >
66 <!-- Rename input -->
77 <form v-if =" isRenaming"
8+ ref =" renameForm"
89 v-on-click-outside =" onRename"
910 :aria-label =" t('files', 'Rename file')"
1011 class =" files-list__row-rename"
1617 :required =" true"
1718 :value.sync =" newName"
1819 enterkeyhint =" done"
19- @keyup =" checkInputValidity"
2020 @keyup.esc =" stopRenaming" />
2121 </form >
2222
4040import type { Node } from ' @nextcloud/files'
4141import type { PropType } from ' vue'
4242
43+ import axios , { isAxiosError } from ' @nextcloud/axios'
4344import { showError , showSuccess } from ' @nextcloud/dialogs'
4445import { emit } from ' @nextcloud/event-bus'
4546import { FileType , NodeStatus , Permission } from ' @nextcloud/files'
46- import { loadState } from ' @nextcloud/initial-state'
4747import { translate as t } from ' @nextcloud/l10n'
48- import axios , { isAxiosError } from ' @nextcloud/axios'
4948import { defineComponent } from ' vue'
5049
5150import NcTextField from ' @nextcloud/vue/dist/Components/NcTextField.js'
5251
5352import { useNavigation } from ' ../../composables/useNavigation'
5453import { useRenamingStore } from ' ../../store/renaming.ts'
54+ import { getFilenameValidity } from ' ../../utils/filenameValidity.ts'
5555import logger from ' ../../logger.js'
5656
57- const forbiddenCharacters = loadState <string []>(' files' , ' forbiddenCharacters' , [])
58-
5957export default defineComponent ({
6058 name: ' FileEntryName' ,
6159
@@ -187,76 +185,51 @@ export default defineComponent({
187185 }
188186 },
189187 },
190- },
191188
192- methods: {
193- /**
194- * Check if the file name is valid and update the
195- * input validity using browser's native validation.
196- * @param event the keyup event
197- */
198- checkInputValidity(event : KeyboardEvent ) {
199- const input = event .target as HTMLInputElement
189+ newName() {
190+ // Check validity of the new name
200191 const newName = this .newName .trim ?.() || ' '
201- logger .debug (' Checking input validity' , { newName })
202- try {
203- this .isFileNameValid (newName )
204- input .setCustomValidity (' ' )
205- input .title = ' '
206- } catch (e ) {
207- if (e instanceof Error ) {
208- input .setCustomValidity (e .message )
209- input .title = e .message
210- } else {
211- input .setCustomValidity (t (' files' , ' Invalid file name' ))
212- }
213- } finally {
214- input .reportValidity ()
192+ const input = (this .$refs .renameInput as Vue | undefined )?.$el .querySelector (' input' )
193+ if (! input ) {
194+ return
215195 }
216- },
217196
218- isFileNameValid(name : string ) {
219- const trimmedName = name .trim ()
220- const char = trimmedName .indexOf (' /' ) !== - 1
221- ? ' /'
222- : forbiddenCharacters .find ((char ) => trimmedName .includes (char ))
223-
224- if (trimmedName === ' .' || trimmedName === ' ..' ) {
225- throw new Error (t (' files' , ' "{name}" is an invalid file name.' , { name }))
226- } else if (trimmedName .length === 0 ) {
227- throw new Error (t (' files' , ' File name cannot be empty.' ))
228- } else if (char ) {
229- throw new Error (t (' files' , ' "{char}" is not allowed inside a file name.' , { char }))
230- } else if (trimmedName .match (window .OC .config .blacklist_files_regex )) {
231- throw new Error (t (' files' , ' "{name}" is not an allowed filetype.' , { name }))
232- } else if (this .checkIfNodeExists (name )) {
233- throw new Error (t (' files' , ' {newName} already exists.' , { newName: name }))
197+ let validity = getFilenameValidity (newName )
198+ // Checking if already exists
199+ if (validity === ' ' && this .checkIfNodeExists (newName )) {
200+ validity = t (' files' , ' Another entry with the same name already exists.' )
234201 }
235-
236- return true
202+ this .$nextTick (() => {
203+ if (this .isRenaming ) {
204+ input .setCustomValidity (validity )
205+ input .reportValidity ()
206+ }
207+ })
237208 },
209+ },
238210
211+ methods: {
239212 checkIfNodeExists(name : string ) {
240213 return this .nodes .find (node => node .basename === name && node !== this .source )
241214 },
242215
243216 startRenaming() {
244217 this .$nextTick (() => {
245218 // Using split to get the true string length
246- const extLength = (this .source .extension || ' ' ).split (' ' ).length
247- const length = this .source .basename .split (' ' ).length - extLength
248- const input = this .$refs .renameInput ?.$refs ?.inputField ?.$refs ?.input
219+ const input = (this .$refs .renameInput as Vue | undefined )?.$el .querySelector (' input' )
249220 if (! input ) {
250221 logger .error (' Could not find the rename input' )
251222 return
252223 }
253- input .setSelectionRange (0 , length )
254224 input .focus ()
225+ const length = this .source .basename .length - (this .source .extension ?? ' ' ).length
226+ input .setSelectionRange (0 , length )
255227
256228 // Trigger a keyup event to update the input validity
257229 input .dispatchEvent (new Event (' keyup' ))
258230 })
259231 },
232+
260233 stopRenaming() {
261234 if (! this .isRenaming ) {
262235 return
@@ -268,25 +241,20 @@ export default defineComponent({
268241
269242 // Rename and move the file
270243 async onRename() {
271- const oldName = this .source .basename
272- const oldEncodedSource = this .source .encodedSource
273244 const newName = this .newName .trim ?.() || ' '
274- if (newName === ' ' ) {
275- showError (t (' files' , ' Name cannot be empty' ))
245+ const form = this .$refs .renameForm as HTMLFormElement
246+ if (! form .checkValidity ()) {
247+ showError (t (' files' , ' Invalid filename.' ) + ' ' + getFilenameValidity (newName ))
276248 return
277249 }
278250
251+ const oldName = this .source .basename
252+ const oldEncodedSource = this .source .encodedSource
279253 if (oldName === newName ) {
280254 this .stopRenaming ()
281255 return
282256 }
283257
284- // Checking if already exists
285- if (this .checkIfNodeExists (newName )) {
286- showError (t (' files' , ' Another entry with the same name already exists' ))
287- return
288- }
289-
290258 // Set loading state
291259 this .$set (this .source , ' status' , NodeStatus .LOADING )
292260
0 commit comments