From 784e5c17db2ebeaded6c4588663fb8bf7dfbbb7c Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:42:56 +0000 Subject: [PATCH 1/4] fix(nc-gui): fix duplicate filename suffix issue --- packages/nc-gui/utils/fileUtils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/utils/fileUtils.ts b/packages/nc-gui/utils/fileUtils.ts index c7a6a1a913..da3bdf45f9 100644 --- a/packages/nc-gui/utils/fileUtils.ts +++ b/packages/nc-gui/utils/fileUtils.ts @@ -87,8 +87,11 @@ export function populateUniqueFileName(fn: string, attachments: any[], mimeType: } let c = 1 + let originalFn = fn + while (attachments.some((att) => att?.title === fn || att?.fileName === fn)) { - fn = fn.replace(/(.+?)(\.[^.]+)$/, `$1(${c++})$2`) + const match = fn.match(/^(.+?)(\(\d+\))?(\.[^.]+)$/) + fn = match ? `${match[1]}(${c++})${match[3]}` : `${originalFn}(${c++})` } return fn } From e033c9b79fac21bcd72a24e5946771774ce6bafb Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:43:01 +0000 Subject: [PATCH 2/4] fix(nc-gui): give proper suffix for duplicate filename --- .../components/cell/attachment/utils.ts | 5 +-- .../useMultiSelect/convertCellData.ts | 8 +++-- .../composables/useMultiSelect/index.ts | 12 +++++-- packages/nc-gui/utils/fileUtils.ts | 18 ----------- .../nocodb-sdk/src/lib/helperFunctions.ts | 31 +++++++++++++++++++ 5 files changed, 50 insertions(+), 24 deletions(-) diff --git a/packages/nc-gui/components/cell/attachment/utils.ts b/packages/nc-gui/components/cell/attachment/utils.ts index 0e53d235c4..70b8fb1f8d 100644 --- a/packages/nc-gui/components/cell/attachment/utils.ts +++ b/packages/nc-gui/components/cell/attachment/utils.ts @@ -1,4 +1,5 @@ import type { AttachmentReqType, AttachmentType } from 'nocodb-sdk' +import { populateUniqueFileName } from 'nocodb-sdk' import DOMPurify from 'isomorphic-dompurify' import RenameFile from './RenameFile.vue' import { @@ -161,7 +162,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( } else { const fileName = populateUniqueFileName( (file as AttachmentReqType).fileName ?? '', - [...attachments.value, ...imageUrls], + [...attachments.value, ...imageUrls].map((fn) => fn?.title || fn?.fileName || 'image'), (file as File)?.type || (file as AttachmentReqType)?.mimetype || '', ) @@ -225,7 +226,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( ...uploadedFile, title: populateUniqueFileName( uploadedFile?.title, - [...attachments.value, ...newAttachments], + [...attachments.value, ...newAttachments].map((fn) => fn?.title || fn?.fileName || 'image'), uploadedFile?.mimetype, ), }) diff --git a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts index 5ca19962a0..1d147c0296 100644 --- a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts +++ b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs' import type { AttachmentType, ColumnType, LinkToAnotherRecordType, SelectOptionsType } from 'nocodb-sdk' -import { UITypes } from 'nocodb-sdk' +import { UITypes, populateUniqueFileName } from 'nocodb-sdk' import type { AppInfo } from '~/composables/useGlobal' import { isBt, isMm, parseProp } from '#imports' @@ -203,7 +203,11 @@ export default function convertCellData( for (const att of attachments) { newAttachments.push({ ...att, - title: populateUniqueFileName(att?.title, [...oldAttachments, ...newAttachments], att?.mimetype), + title: populateUniqueFileName( + att?.title, + [...oldAttachments, ...newAttachments].map((fn) => fn?.title || fn?.fileName || 'image'), + att?.mimetype, + ), }) } return JSON.stringify([...oldAttachments, ...newAttachments]) diff --git a/packages/nc-gui/composables/useMultiSelect/index.ts b/packages/nc-gui/composables/useMultiSelect/index.ts index 3cedc82563..f2afe20969 100644 --- a/packages/nc-gui/composables/useMultiSelect/index.ts +++ b/packages/nc-gui/composables/useMultiSelect/index.ts @@ -11,7 +11,15 @@ import type { UserFieldRecordType, ViewType, } from 'nocodb-sdk' -import { UITypes, dateFormats, isDateMonthFormat, isSystemColumn, isVirtualCol, timeFormats } from 'nocodb-sdk' +import { + UITypes, + dateFormats, + isDateMonthFormat, + isSystemColumn, + isVirtualCol, + timeFormats, + populateUniqueFileName, +} from 'nocodb-sdk' import { parse } from 'papaparse' import type { Cell } from './cellRange' import { CellRange } from './cellRange' @@ -1253,7 +1261,7 @@ export function useMultiSelect( ...uploadedFile, title: populateUniqueFileName( uploadedFile?.title, - [...handleParseAttachmentCellData(oldValue), ...newAttachments], + [...handleParseAttachmentCellData(oldValue), ...newAttachments].map((fn) => fn?.title || fn?.fileName || 'image'), uploadedFile?.mimetype, ), }) diff --git a/packages/nc-gui/utils/fileUtils.ts b/packages/nc-gui/utils/fileUtils.ts index da3bdf45f9..521bbf6e7c 100644 --- a/packages/nc-gui/utils/fileUtils.ts +++ b/packages/nc-gui/utils/fileUtils.ts @@ -77,21 +77,3 @@ export function extractImageSrcFromRawHtml(rawText: string) { return imgElement.getAttribute('src') } } - -export function populateUniqueFileName(fn: string, attachments: any[], mimeType: string) { - if (!mimeType) return fn - - // If the image extension is not present, the while loop will go into an infinite loop. So, add the extension first if not present. - if (!fn?.endsWith(mimeType.split('/')[1])) { - fn = `${fn}.${mimeType.split('/')[1]}` - } - - let c = 1 - let originalFn = fn - - while (attachments.some((att) => att?.title === fn || att?.fileName === fn)) { - const match = fn.match(/^(.+?)(\(\d+\))?(\.[^.]+)$/) - fn = match ? `${match[1]}(${c++})${match[3]}` : `${originalFn}(${c++})` - } - return fn -} diff --git a/packages/nocodb-sdk/src/lib/helperFunctions.ts b/packages/nocodb-sdk/src/lib/helperFunctions.ts index 527e479af6..b9fa956923 100644 --- a/packages/nocodb-sdk/src/lib/helperFunctions.ts +++ b/packages/nocodb-sdk/src/lib/helperFunctions.ts @@ -97,6 +97,36 @@ const getAvailableRollupForUiType = (type: string) => { } }; +function populateUniqueFileName( + fileName: string, + attachments: string[], + mimeType: string +) { + if (!mimeType) return fileName; + + // If the file extension is not present, the while loop will go into an infinite loop. So, add the extension first if not present. + if (!fileName?.endsWith(`.${mimeType.split('/')[1]}`)) { + fileName = `${fileName}.${mimeType.split('/')[1]}`; + } else if ( + fileName?.endsWith(`.${mimeType.split('/')[1]}`) && + fileName.length === `.${mimeType.split('/')[1]}`.length + ) { + fileName = `image.${mimeType.split('/')[1]}`; + } + + let match = fileName.match(/^(.+?)(\((\d+)\))?(\.[^.]+)$/); + + if (!match) return fileName; + + let c = match && !isNaN(parseInt(match[3])) ? parseInt(match[3]) : 1; + + while (attachments.some((fn) => fn === fileName)) { + fileName = `${match[1]}(${c++})${match[4]}`; + } + + return fileName; +} + export { filterOutSystemColumns, getSystemColumnsIds, @@ -106,4 +136,5 @@ export { extractRolesObj, stringifyRolesObj, getAvailableRollupForUiType, + populateUniqueFileName, }; From 0e6231870b9dbca1bc223dfb9e741ed4eb7f3a4a Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:43:06 +0000 Subject: [PATCH 3/4] fic(nc-gui): handle duplicate filename suffix properly #2379 --- .../components/cell/attachment/utils.ts | 4 ++-- .../useMultiSelect/convertCellData.ts | 2 +- .../composables/useMultiSelect/index.ts | 2 +- .../src/services/public-datas.service.ts | 22 +++++++++---------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/nc-gui/components/cell/attachment/utils.ts b/packages/nc-gui/components/cell/attachment/utils.ts index 70b8fb1f8d..d77969db66 100644 --- a/packages/nc-gui/components/cell/attachment/utils.ts +++ b/packages/nc-gui/components/cell/attachment/utils.ts @@ -162,7 +162,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( } else { const fileName = populateUniqueFileName( (file as AttachmentReqType).fileName ?? '', - [...attachments.value, ...imageUrls].map((fn) => fn?.title || fn?.fileName || 'image'), + [...attachments.value, ...imageUrls].map((fn) => fn?.title || fn?.fileName), (file as File)?.type || (file as AttachmentReqType)?.mimetype || '', ) @@ -226,7 +226,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( ...uploadedFile, title: populateUniqueFileName( uploadedFile?.title, - [...attachments.value, ...newAttachments].map((fn) => fn?.title || fn?.fileName || 'image'), + [...attachments.value, ...newAttachments].map((fn) => fn?.title || fn?.fileName), uploadedFile?.mimetype, ), }) diff --git a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts index 1d147c0296..1ca5e60c32 100644 --- a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts +++ b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts @@ -205,7 +205,7 @@ export default function convertCellData( ...att, title: populateUniqueFileName( att?.title, - [...oldAttachments, ...newAttachments].map((fn) => fn?.title || fn?.fileName || 'image'), + [...oldAttachments, ...newAttachments].map((fn) => fn?.title || fn?.fileName), att?.mimetype, ), }) diff --git a/packages/nc-gui/composables/useMultiSelect/index.ts b/packages/nc-gui/composables/useMultiSelect/index.ts index f2afe20969..c81ec5f702 100644 --- a/packages/nc-gui/composables/useMultiSelect/index.ts +++ b/packages/nc-gui/composables/useMultiSelect/index.ts @@ -1261,7 +1261,7 @@ export function useMultiSelect( ...uploadedFile, title: populateUniqueFileName( uploadedFile?.title, - [...handleParseAttachmentCellData(oldValue), ...newAttachments].map((fn) => fn?.title || fn?.fileName || 'image'), + [...handleParseAttachmentCellData(oldValue), ...newAttachments].map((fn) => fn?.title || fn?.fileName), uploadedFile?.mimetype, ), }) diff --git a/packages/nocodb/src/services/public-datas.service.ts b/packages/nocodb/src/services/public-datas.service.ts index 675a072616..338e4a879c 100644 --- a/packages/nocodb/src/services/public-datas.service.ts +++ b/packages/nocodb/src/services/public-datas.service.ts @@ -1,7 +1,12 @@ import path from 'path'; import { Injectable } from '@nestjs/common'; import { nanoid } from 'nanoid'; -import { ErrorMessages, UITypes, ViewTypes } from 'nocodb-sdk'; +import { + ErrorMessages, + UITypes, + ViewTypes, + populateUniqueFileName, +} from 'nocodb-sdk'; import slash from 'slash'; import { nocoExecute } from 'nc-help'; @@ -396,16 +401,11 @@ export class PublicDatasService { attachments[fieldName] = attachments[fieldName] || []; let originalName = utf8ify(file.originalname); - let c = 1; - while ( - path.extname(originalName) && - attachments[fieldName].some((att) => att?.title === originalName) - ) { - originalName = originalName.replace( - /(.+?)(\.[^.]+)$/, - `$1(${c++})$2`, - ); - } + originalName = populateUniqueFileName( + originalName, + attachments[fieldName].map((att) => att?.title), + file.mimetype, + ); const fileName = `${nanoid(18)}${path.extname(originalName)}`; From add084a8eac4f22236abc1699c45d59bf850d649 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:43:08 +0000 Subject: [PATCH 4/4] fix: review comments --- packages/nocodb-sdk/src/lib/helperFunctions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nocodb-sdk/src/lib/helperFunctions.ts b/packages/nocodb-sdk/src/lib/helperFunctions.ts index b9fa956923..43a8d878ec 100644 --- a/packages/nocodb-sdk/src/lib/helperFunctions.ts +++ b/packages/nocodb-sdk/src/lib/helperFunctions.ts @@ -114,11 +114,11 @@ function populateUniqueFileName( fileName = `image.${mimeType.split('/')[1]}`; } - let match = fileName.match(/^(.+?)(\((\d+)\))?(\.[^.]+)$/); + const match = fileName.match(/^(.+?)(\((\d+)\))?(\.[^.]+)$/); if (!match) return fileName; - let c = match && !isNaN(parseInt(match[3])) ? parseInt(match[3]) : 1; + let c = !isNaN(parseInt(match[3])) ? parseInt(match[3]) : 1; while (attachments.some((fn) => fn === fileName)) { fileName = `${match[1]}(${c++})${match[4]}`;