Co-authored-by: Pytal <24800714+Pytal@users.noreply.github.com> Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>tags/v28.0.5rc1
@@ -120,7 +120,14 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth | |||
// If we do not allow overwriting then find an unique name | |||
if (!overwrite) { | |||
const otherNodes = await client.getDirectoryContents(destinationPath) as FileStat[] | |||
target = getUniqueName(node.basename, otherNodes.map((n) => n.basename), copySuffix) | |||
target = getUniqueName( | |||
node.basename, | |||
otherNodes.map((n) => n.basename), | |||
{ | |||
suffix: copySuffix, | |||
ignoreFileExtension: node.type === FileType.Folder, | |||
}, | |||
) | |||
} | |||
await client.copyFile(currentPath, join(destinationPath, target)) | |||
// If the node is copied into current directory the view needs to be updated | |||
@@ -150,7 +157,7 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth | |||
} | |||
} catch (error) { | |||
// User cancelled | |||
showError(t('files','Move cancelled')) | |||
showError(t('files', 'Move cancelled')) | |||
return | |||
} | |||
} |
@@ -12,7 +12,7 @@ | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
@@ -28,15 +28,31 @@ import { basename, extname } from 'path' | |||
* Create an unique file name | |||
* @param name The initial name to use | |||
* @param otherNames Other names that are already used | |||
* @param suffix A function that takes an index an returns a suffix to add, defaults to '(index)' | |||
* @param options Optional parameters for tuning the behavior | |||
* @param options.suffix A function that takes an index and returns a suffix to add to the file name, defaults to '(index)' | |||
* @param options.ignoreFileExtension Set to true to ignore the file extension when adding the suffix (when getting a unique directory name) | |||
* @return Either the initial name, if unique, or the name with the suffix so that the name is unique | |||
*/ | |||
export const getUniqueName = (name: string, otherNames: string[], suffix = (n: number) => `(${n})`): string => { | |||
export const getUniqueName = ( | |||
name: string, | |||
otherNames: string[], | |||
options: { | |||
suffix?: (i: number) => string, | |||
ignoreFileExtension?: boolean, | |||
} = {}, | |||
): string => { | |||
const opts = { | |||
suffix: (n: number) => `(${n})`, | |||
ignoreFileExtension: false, | |||
...options, | |||
} | |||
let newName = name | |||
let i = 1 | |||
while (otherNames.includes(newName)) { | |||
const ext = extname(name) | |||
newName = `${basename(name, ext)} ${suffix(i++)}${ext}` | |||
const ext = opts.ignoreFileExtension ? '' : extname(name) | |||
const base = basename(name, ext) | |||
newName = `${base} ${opts.suffix(i++)}${ext}` | |||
} | |||
return newName | |||
} |
@@ -93,7 +93,9 @@ describe('Files: Move or copy files', { testIsolation: true }, () => { | |||
getRowForFile('new-folder').should('not.exist') | |||
}) | |||
// This was a bug previously | |||
/** | |||
* Test for https://github.com/nextcloud/server/issues/41768 | |||
*/ | |||
it('Can move a file to folder with similar name', () => { | |||
cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original') | |||
.mkdir(currentUser, '/original folder') | |||
@@ -180,6 +182,30 @@ describe('Files: Move or copy files', { testIsolation: true }, () => { | |||
getRowForFile('original (copy 2).txt').should('be.visible') | |||
}) | |||
/** | |||
* Test that a copied folder with a dot will be renamed correctly ('foo.bar' -> 'foo.bar (copy)') | |||
* Test for: https://github.com/nextcloud/server/issues/43843 | |||
*/ | |||
it('Can copy a folder to same folder', () => { | |||
cy.mkdir(currentUser, '/foo.bar') | |||
cy.login(currentUser) | |||
cy.visit('/apps/files') | |||
// intercept the copy so we can wait for it | |||
cy.intercept('COPY', /\/remote.php\/dav\/files\//).as('copyFile') | |||
getRowForFile('foo.bar').should('be.visible') | |||
triggerActionForFile('foo.bar', 'move-copy') | |||
// click copy | |||
cy.get('.file-picker').contains('button', 'Copy').should('be.visible').click() | |||
cy.wait('@copyFile') | |||
getRowForFile('foo.bar').should('be.visible') | |||
getRowForFile('foo.bar (copy)').should('be.visible') | |||
}) | |||
/** Test for https://github.com/nextcloud/server/issues/43329 */ | |||
context('escaping file and folder names', () => { | |||
it('Can handle files with special characters', () => { |