- {users.length > 0 && (
-
-
- {t('common.members', {
- context: 'title',
- })}
-
- {users.map((user) => (
-
-
-
-
-
- ))}
-
-
+ )}
-
-
{t('common.attachments')}
-
+
{t('common.description')}
+
+ {description ? (
+
+
+
+ ) : (
+
+
+ {t('action.addMoreDetailedDescription')}
+
+
+ )}
+
+
+
+
+
+
+
{t('common.tasks')}
+
- )}
-
-
-
-
-
{t('action.addToCard')}
-
-
-
- {t('common.members')}
+ {attachments.length > 0 && (
+
+
+
+
{t('common.attachments')}
+
+
+
+ )}
+
+
+
+
+
{t('action.addToCard')}
+
+
+
+ {t('common.members')}
+
+
+
+
+
+ {t('common.labels')}
+
+
+
+
+
+ {t('common.dueDate')}
+
+
+
+
+
+ {t('common.timer')}
+
+
+
+
+
+ {t('common.attachment')}
+
+
+
+
+ {t('common.actions')}
+
+
+ {isSubscribed ? t('action.unsubscribe') : t('action.subscribe')}
-
-
-
-
- {t('common.labels')}
-
-
-
-
-
- {t('common.dueDate')}
-
-
-
-
-
- {t('common.timer')}
-
-
-
-
-
- {t('common.attachment')}
-
-
-
-
- {t('common.actions')}
-
-
- {isSubscribed ? t('action.unsubscribe') : t('action.subscribe')}
-
-
-
-
- {t('action.delete')}
-
-
-
-
-
-
+
+
+
+ {t('action.delete')}
+
+
+
+
+
+
+
);
},
diff --git a/client/src/components/Login/Login.jsx b/client/src/components/Login/Login.jsx
index bd787afa..acff2dd8 100755
--- a/client/src/components/Login/Login.jsx
+++ b/client/src/components/Login/Login.jsx
@@ -1,8 +1,8 @@
+import isEmail from 'validator/lib/isEmail';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
-import isEmail from 'validator/lib/isEmail';
import { Form, Grid, Header, Message } from 'semantic-ui-react';
import { useDidUpdate, usePrevious, useToggle } from '../../lib/hooks';
import { Input } from '../../lib/custom-ui';
diff --git a/client/src/hooks/index.js b/client/src/hooks/index.js
index b30bb981..77aee264 100644
--- a/client/src/hooks/index.js
+++ b/client/src/hooks/index.js
@@ -1,6 +1,7 @@
import useField from './use-field';
import useForm from './use-form';
import useSteps from './use-steps';
+import useModal from './use-modal';
import useClosableForm from './use-closable-form';
-export { useField, useForm, useSteps, useClosableForm };
+export { useField, useForm, useSteps, useModal, useClosableForm };
diff --git a/client/src/hooks/use-modal.js b/client/src/hooks/use-modal.js
new file mode 100644
index 00000000..08067034
--- /dev/null
+++ b/client/src/hooks/use-modal.js
@@ -0,0 +1,15 @@
+import { useCallback, useState } from 'react';
+
+export default (initialParams) => {
+ const [modal, setModal] = useState(() => initialParams);
+
+ const open = useCallback((params) => {
+ setModal(params);
+ }, []);
+
+ const handleClose = useCallback(() => {
+ setModal(null);
+ }, []);
+
+ return [modal, open, handleClose];
+};
diff --git a/client/src/hooks/use-steps.js b/client/src/hooks/use-steps.js
index b0b2505c..a1b60562 100644
--- a/client/src/hooks/use-steps.js
+++ b/client/src/hooks/use-steps.js
@@ -14,7 +14,7 @@ const createStep = (type, params = {}) => {
export default (initialType, initialParams) => {
const [step, setStep] = useState(() => createStep(initialType, initialParams));
- const openStep = useCallback((type, params) => {
+ const open = useCallback((type, params) => {
setStep(createStep(type, params));
}, []);
@@ -22,5 +22,5 @@ export default (initialType, initialParams) => {
setStep(null);
}, []);
- return [step, openStep, handleBack];
+ return [step, open, handleBack];
};
diff --git a/client/src/lib/custom-ui/index.css b/client/src/lib/custom-ui/index.css
index e42ac12d..e50747be 100644
--- a/client/src/lib/custom-ui/index.css
+++ b/client/src/lib/custom-ui/index.css
@@ -34814,11 +34814,11 @@ select.ui.dropdown {
overflow: hidden;
}
-.scrolling.dimmable > .dimmer {
+/* .scrolling.dimmable > .dimmer {
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
-}
+} */
.scrolling.dimmable.dimmed > .dimmer {
overflow: auto;
diff --git a/client/src/locales/en/app.js b/client/src/locales/en/app.js
index d2074906..eb2d68be 100644
--- a/client/src/locales/en/app.js
+++ b/client/src/locales/en/app.js
@@ -42,6 +42,7 @@ export default {
createLabel_title: 'Create Label',
createNewOneOrSelectExistingOne: 'Create a new one or select
an existing one',
createProject_title: 'Create Project',
+ createTextFile_title: 'Create Text File',
currentPassword: 'Current password',
date: 'Date',
dueDate: 'Due date',
@@ -55,6 +56,7 @@ export default {
deleteTask_title: 'Delete Task',
deleteUser_title: 'Delete User',
description: 'Description',
+ dropFileToUpload: 'Drop file to upload',
editAttachment_title: 'Edit Attachment',
editAvatar_title: 'Edit Avatar',
editBoard_title: 'Edit Board',
@@ -69,6 +71,7 @@ export default {
emailAlreadyInUse: 'E-mail already in use',
enterCardTitle: 'Enter card title...',
enterDescription: 'Enter description...',
+ enterFilename: 'Enter filename',
enterListTitle: 'Enter list title...',
enterProjectTitle: 'Enter project title',
enterTaskDescription: 'Enter task description...',
@@ -129,6 +132,7 @@ export default {
addToCard: 'Add to card',
addUser: 'Add user',
createBoard: 'Create board',
+ createFile: 'Create file',
createLabel: 'Create label',
createNewLabel: 'Create new label',
createProject: 'Create project',
diff --git a/client/src/locales/ru/app.js b/client/src/locales/ru/app.js
index 725e2597..8219f827 100644
--- a/client/src/locales/ru/app.js
+++ b/client/src/locales/ru/app.js
@@ -46,6 +46,7 @@ export default {
createLabel: 'Создание метки',
createNewOneOrSelectExistingOne: 'Создайте новую или выберите
уже существующую',
createProject: 'Создание проекта',
+ createTextFile: 'Создание текстового файла',
currentPassword: 'Текущий пароль',
date: 'Дата',
dueDate: 'Срок',
@@ -59,6 +60,7 @@ export default {
deleteTask: 'Удаление задачи',
deleteUser: 'Удаление пользователя',
description: 'Описание',
+ dropFileToUpload: 'Перетяните файл, чтобы загрузить',
editAttachment: 'Изменение вложения',
editAvatar: 'Изменение аватара',
editBoard: 'Изменение доски',
@@ -73,6 +75,7 @@ export default {
emailAlreadyInUse: 'E-mail уже занят',
enterCardTitle: 'Введите заголовок для этой карточки...',
enterDescription: 'Введите описание...',
+ enterFilename: 'Введите название файла',
enterListTitle: 'Введите заголовок списка...',
enterProjectTitle: 'Введите название проекта',
enterTaskDescription: 'Введите описание задачи...',
@@ -133,6 +136,7 @@ export default {
addToCard: 'Добавить на карточку',
addUser: 'Добавить пользователя',
createBoard: 'Создать доску',
+ createFile: 'Создать файл',
createLabel: 'Создать метку',
createNewLabel: 'Создать новую метку',
createProject: 'Создать проект',
diff --git a/server/api/controllers/attachments/create.js b/server/api/controllers/attachments/create.js
index 6ab255a0..9f3d77ad 100644
--- a/server/api/controllers/attachments/create.js
+++ b/server/api/controllers/attachments/create.js
@@ -60,7 +60,7 @@ module.exports = {
dirname: file.extra.dirname,
filename: file.filename,
isImage: file.extra.isImage,
- name: file.filename,
+ name: file.extra.name,
},
inputs.requestId,
this.req,
diff --git a/server/api/helpers/create-attachment-receiver.js b/server/api/helpers/create-attachment-receiver.js
index 2fcc9156..ecdb4de8 100644
--- a/server/api/helpers/create-attachment-receiver.js
+++ b/server/api/helpers/create-attachment-receiver.js
@@ -3,6 +3,7 @@ const path = require('path');
const util = require('util');
const stream = require('stream');
const streamToArray = require('stream-to-array');
+const filenamify = require('filenamify');
const { v4: uuid } = require('uuid');
const sharp = require('sharp');
@@ -33,10 +34,13 @@ module.exports = {
try {
const dirname = uuid();
+ // FIXME: https://github.com/sindresorhus/filenamify/issues/13
+ const filename = filenamify(file.filename);
+
const rootPath = path.join(sails.config.custom.attachmentsPath, dirname);
fs.mkdirSync(rootPath);
- await writeFile(path.join(rootPath, file.filename), buffer);
+ await writeFile(path.join(rootPath, filename), buffer);
const image = sharp(buffer);
let imageMetadata;
@@ -68,8 +72,12 @@ module.exports = {
file.extra = {
dirname,
isImage: !!imageMetadata,
+ name: file.filename,
};
+ // eslint-disable-next-line no-param-reassign
+ file.filename = filename;
+
return done();
} catch (error) {
return done(error);
diff --git a/server/package-lock.json b/server/package-lock.json
index fdb80057..8c402120 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -2386,6 +2386,21 @@
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
"integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY="
},
+ "filename-reserved-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
+ "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik="
+ },
+ "filenamify": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.1.0.tgz",
+ "integrity": "sha512-KQV/uJDI9VQgN7sHH1Zbk6+42cD6mnQ2HONzkXUfPJ+K2FC8GZ1dpewbbHw0Sz8Tf5k3EVdHVayM4DoAwWlmtg==",
+ "requires": {
+ "filename-reserved-regex": "^2.0.0",
+ "strip-outer": "^1.0.1",
+ "trim-repeated": "^1.0.0"
+ }
+ },
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -6450,6 +6465,14 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
+ "strip-outer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
+ "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
+ "requires": {
+ "escape-string-regexp": "^1.0.2"
+ }
+ },
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
@@ -6674,6 +6697,14 @@
}
}
},
+ "trim-repeated": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
+ "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=",
+ "requires": {
+ "escape-string-regexp": "^1.0.2"
+ }
+ },
"tslib": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
diff --git a/server/package.json b/server/package.json
index c2924540..9e778d50 100644
--- a/server/package.json
+++ b/server/package.json
@@ -42,6 +42,7 @@
"bcrypt": "^4.0.1",
"dotenv": "^8.2.0",
"dotenv-cli": "^3.1.0",
+ "filenamify": "^4.1.0",
"jsonwebtoken": "^8.5.1",
"knex": "^0.20.13",
"lodash": "^4.17.15",