Commit f4737a3a authored by orozot's avatar orozot Committed by Natalia Tepluhina

Fix: filename display when copy/paste in comment

Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/343539
Changelog: fixed
parent 63ae977c
......@@ -4,7 +4,7 @@ import VueDraggable from 'vuedraggable';
import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql';
import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
import createFlash, { FLASH_TYPES } from '~/flash';
import { getFilename } from '~/lib/utils/file_upload';
import { getFilename, validateImageName } from '~/lib/utils/file_upload';
import { __, s__, sprintf } from '~/locale';
import DesignDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
import DeleteButton from '../components/delete_button.vue';
......@@ -284,12 +284,16 @@ export default {
return;
}
event.preventDefault();
let filename = getFilename(event);
if (!filename || filename === 'image.png') {
filename = `design_${Date.now()}.png`;
}
const newFile = new File([files[0]], filename);
this.onUploadDesign([newFile]);
const fileList = [...files];
fileList.forEach((file) => {
let filename = getFilename(file);
filename = validateImageName(file);
if (!filename || filename === 'image.png') {
filename = `design_${Date.now()}.png`;
}
const newFile = new File([file], filename);
this.onUploadDesign([newFile]);
});
}
},
toggleOnPasteListener() {
......
......@@ -43,7 +43,6 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
let pasteText;
let addFileToForm;
let updateAttachingMessage;
let isImage;
let uploadFile;
formTextarea.wrap('<div class="div-dropzone"></div>');
......@@ -173,7 +172,7 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
return dropzoneInstance.addFile(file);
});
});
// eslint-disable-next-line consistent-return
handlePaste = (event) => {
const pasteEvent = event.originalEvent;
const { clipboardData } = pasteEvent;
......@@ -186,32 +185,22 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
const text = converter.convertToTableMarkdown();
pasteText(text);
} else {
const image = isImage(pasteEvent);
if (image) {
event.preventDefault();
const MAX_FILE_NAME_LENGTH = 246;
const filename = getFilename(pasteEvent) || 'image.png';
const truncateFilename = truncate(filename, MAX_FILE_NAME_LENGTH);
const text = `{{${truncateFilename}}}`;
pasteText(text);
return uploadFile(image.getAsFile(), truncateFilename);
}
}
}
};
isImage = (data) => {
let i = 0;
while (i < data.clipboardData.items.length) {
const item = data.clipboardData.items[i];
if (item.type.indexOf('image') !== -1) {
return item;
const fileList = [...clipboardData.files];
fileList.forEach((file) => {
if (file.type.indexOf('image') !== -1) {
event.preventDefault();
const MAX_FILE_NAME_LENGTH = 246;
const filename = getFilename(file) || 'image.png';
const truncateFilename = truncate(filename, MAX_FILE_NAME_LENGTH);
const text = `{{${truncateFilename}}}`;
pasteText(text);
uploadFile(file, truncateFilename);
}
});
}
i += 1;
}
return false;
};
pasteText = (text, shouldPad) => {
......
......@@ -15,13 +15,17 @@ export default (buttonSelector, fileSelector) => {
});
};
export const getFilename = ({ clipboardData }) => {
let value;
if (window.clipboardData && window.clipboardData.getData) {
value = window.clipboardData.getData('Text');
} else if (clipboardData && clipboardData.getData) {
value = clipboardData.getData('text/plain');
export const getFilename = (file) => {
let fileName;
if (file) {
fileName = file.name;
}
value = value.split('\r');
return value[0];
return fileName;
};
export const validateImageName = (file) => {
const fileName = file.name ? file.name : 'image.png';
const legalImageRegex = /^[\w.\-+]+\.(png|jpg|jpeg|gif|bmp|tiff|ico|webp)$/;
return legalImageRegex.test(fileName) ? fileName : 'image.png';
};
......@@ -669,6 +669,20 @@ describe('Design management index page', () => {
expect(variables.files).toEqual(event.clipboardData.files.map((f) => new File([f], '')));
});
it('display original file name', () => {
event.clipboardData.files = [new File([new Blob()], 'test.png', { type: 'image/png' })];
document.dispatchEvent(event);
const [{ mutation, variables }] = mockMutate.mock.calls[0];
expect(mutation).toBe(uploadDesignMutation);
expect(variables).toStrictEqual({
files: expect.any(Array),
iid: '1',
projectPath: 'project-path',
});
expect(variables.files[0].name).toEqual('test.png');
});
it('renames a design if it has an image.png filename', () => {
event.clipboardData.getData = () => 'image.png';
document.dispatchEvent(event);
......
......@@ -71,6 +71,7 @@ describe('dropzone_input', () => {
triggerPasteEvent({
types: ['text/plain', 'text/html', 'text/rtf', 'Files'],
getData: () => longFileName,
files: [new File([new Blob()], longFileName, { type: 'image/png' })],
items: [
{
kind: 'file',
......@@ -84,6 +85,24 @@ describe('dropzone_input', () => {
await waitForPromises();
expect(axiosMock.history.post[0].data.get('file').name).toHaveLength(246);
});
it('display original file name in comment box', async () => {
const axiosMock = new MockAdapter(axios);
triggerPasteEvent({
types: ['Files'],
files: [new File([new Blob()], 'test.png', { type: 'image/png' })],
items: [
{
kind: 'file',
type: 'image/png',
getAsFile: () => new Blob(),
},
],
});
axiosMock.onPost().reply(httpStatusCodes.OK, { link: { markdown: 'foo' } });
await waitForPromises();
expect(axiosMock.history.post[0].data.get('file').name).toEqual('test.png');
});
});
describe('shows error message', () => {
......
import fileUpload, { getFilename } from '~/lib/utils/file_upload';
import fileUpload, { getFilename, validateImageName } from '~/lib/utils/file_upload';
describe('File upload', () => {
beforeEach(() => {
......@@ -64,13 +64,23 @@ describe('File upload', () => {
});
describe('getFilename', () => {
it('returns first value correctly', () => {
const event = {
clipboardData: {
getData: () => 'test.png\rtest.txt',
},
};
expect(getFilename(event)).toBe('test.png');
it('returns file name', () => {
const file = new File([], 'test.jpg');
expect(getFilename(file)).toBe('test.jpg');
});
});
describe('file name validator', () => {
it('validate file name', () => {
const file = new File([], 'test.jpg');
expect(validateImageName(file)).toBe('test.jpg');
});
it('illegal file name should be rename to image.png', () => {
const file = new File([], 'test<.png');
expect(validateImageName(file)).toBe('image.png');
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment