Compare commits

...
9 Commits
Author SHA1 Message Date
Maksim Eltyshev eeb1bd80cc chore: Update version 2024-07-21 19:44:49 +02:00
dependabot[bot] e6b8538863 chore(deps): Bump ws, engine.io and socket.io-adapter in /server (#830)
Bumps [ws](https://github.com/websockets/ws), [engine.io](https://github.com/socketio/engine.io) and [socket.io-adapter](https://github.com/socketio/socket.io-adapter). These dependencies needed to be updated together.

Updates `ws` from 8.11.0 to 8.17.1
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.11.0...8.17.1)

Updates `engine.io` from 6.5.4 to 6.5.5
- [Release notes](https://github.com/socketio/engine.io/releases)
- [Changelog](https://github.com/socketio/engine.io/blob/6.5.5/CHANGELOG.md)
- [Commits](https://github.com/socketio/engine.io/compare/6.5.4...6.5.5)

Updates `socket.io-adapter` from 2.5.4 to 2.5.5
- [Release notes](https://github.com/socketio/socket.io-adapter/releases)
- [Changelog](https://github.com/socketio/socket.io-adapter/blob/2.5.5/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-adapter/compare/2.5.4...2.5.5)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
- dependency-name: engine.io
  dependency-type: indirect
- dependency-name: socket.io-adapter
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-21 19:42:36 +02:00
Maksim Eltyshev 9dc38932fb feat: Languages with country codes 2024-07-21 19:33:57 +02:00
Aurélien Troncy e9b4a4adfc fix: Disable x-powered-by header (#829) 2024-07-21 18:27:26 +02:00
Mazyar Yousefiniyae shad 76c78c7252 feat: Add Persian translation (#828) 2024-07-21 18:21:18 +02:00
Maksim Eltyshev 04f52a63f2 chore: Update version 2024-07-17 14:03:00 +02:00
Maksim Eltyshev 6b16a104b1 docs: Update docker pulls counter 2024-07-16 12:39:57 +02:00
Maksim Eltyshev aff853c602 ref: Refactoring, fix linting 2024-07-16 12:33:38 +02:00
aleb_the_flash ad2966c5d6 feat: Improve OIDC support for strict providers (#824) 2024-07-16 12:19:27 +02:00
98 changed files with 565 additions and 121 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# Planka # Planka
#### Elegant open source project tracking. #### Elegant open source project tracking.
![David (path)](https://img.shields.io/github/package-json/v/plankanban/planka) ![Docker Pulls](https://img.shields.io/docker/pulls/meltyshev/planka) ![GitHub](https://img.shields.io/github/license/plankanban/planka) ![David (path)](https://img.shields.io/github/package-json/v/plankanban/planka) ![Docker Pulls](https://img.shields.io/badge/docker_pulls-4M%2B-%23066da5) ![GitHub](https://img.shields.io/github/license/plankanban/planka)
![](https://raw.githubusercontent.com/plankanban/planka/master/demo.gif) ![](https://raw.githubusercontent.com/plankanban/planka/master/demo.gif)
+2 -2
View File
@@ -15,13 +15,13 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes # This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version. # to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/) # Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.2.3 version: 0.2.5
# This is the version number of the application being deployed. This version number should be # This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to # incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using. # follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes. # It is recommended to use it with quotes.
appVersion: "1.19.2" appVersion: "1.20.1"
dependencies: dependencies:
- alias: postgresql - alias: postgresql
+1 -1
View File
@@ -1 +1 @@
REACT_APP_VERSION=1.19.2 REACT_APP_VERSION=1.20.1
+12 -12
View File
@@ -12,7 +12,7 @@
"dequal": "^2.0.3", "dequal": "^2.0.3",
"easymde": "^2.18.0", "easymde": "^2.18.0",
"history": "^5.3.0", "history": "^5.3.0",
"i18next": "^23.11.5", "i18next": "^23.12.2",
"i18next-browser-languagedetector": "^8.0.0", "i18next-browser-languagedetector": "^8.0.0",
"initials": "^3.1.2", "initials": "^3.1.2",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
@@ -30,7 +30,7 @@
"react-datepicker": "^4.25.0", "react-datepicker": "^4.25.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-i18next": "^14.1.2", "react-i18next": "^15.0.0",
"react-input-mask": "^2.0.4", "react-input-mask": "^2.0.4",
"react-markdown": "^8.0.7", "react-markdown": "^8.0.7",
"react-photoswipe-gallery": "^2.2.7", "react-photoswipe-gallery": "^2.2.7",
@@ -2007,9 +2007,9 @@
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.24.6", "version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz",
"integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==", "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.14.0" "regenerator-runtime": "^0.14.0"
}, },
@@ -11975,9 +11975,9 @@
} }
}, },
"node_modules/i18next": { "node_modules/i18next": {
"version": "23.11.5", "version": "23.12.2",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.5.tgz", "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.12.2.tgz",
"integrity": "sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==", "integrity": "sha512-XIeh5V+bi8SJSWGL3jqbTEBW5oD6rbP5L+E7dVQh1MNTxxYef0x15rhJVcRb7oiuq4jLtgy2SD8eFlf6P2cmqg==",
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@@ -22084,11 +22084,11 @@
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
}, },
"node_modules/react-i18next": { "node_modules/react-i18next": {
"version": "14.1.2", "version": "15.0.0",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.2.tgz", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.0.0.tgz",
"integrity": "sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==", "integrity": "sha512-2O3IgF4zivg57Q6p6i+ChDgJ371IDcEWbuWC6gvoh5NbkDMs0Q+O7RPr4v61+Se32E0V+LmtwePAeqWZW0bi6g==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.23.9", "@babel/runtime": "^7.24.8",
"html-parse-stringify": "^3.0.1" "html-parse-stringify": "^3.0.1"
}, },
"peerDependencies": { "peerDependencies": {
+2 -2
View File
@@ -65,7 +65,7 @@
"dequal": "^2.0.3", "dequal": "^2.0.3",
"easymde": "^2.18.0", "easymde": "^2.18.0",
"history": "^5.3.0", "history": "^5.3.0",
"i18next": "^23.11.5", "i18next": "^23.12.2",
"i18next-browser-languagedetector": "^8.0.0", "i18next-browser-languagedetector": "^8.0.0",
"initials": "^3.1.2", "initials": "^3.1.2",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
@@ -83,7 +83,7 @@
"react-datepicker": "^4.25.0", "react-datepicker": "^4.25.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-i18next": "^14.1.2", "react-i18next": "^15.0.0",
"react-input-mask": "^2.0.4", "react-input-mask": "^2.0.4",
"react-markdown": "^8.0.7", "react-markdown": "^8.0.7",
"react-photoswipe-gallery": "^2.2.7", "react-photoswipe-gallery": "^2.2.7",
+3 -3
View File
@@ -58,9 +58,9 @@ i18n
.use(initReactI18next) .use(initReactI18next)
.init({ .init({
resources: embeddedLocales, resources: embeddedLocales,
fallbackLng: 'en', fallbackLng: 'en-US',
supportedLngs: languages, supportedLngs: languages,
load: 'languageOnly', load: 'currentOnly',
interpolation: { interpolation: {
escapeValue: false, escapeValue: false,
format(value, format, language) { format(value, format, language) {
@@ -80,7 +80,7 @@ i18n
}); });
i18n.loadCoreLocale = async (language = i18n.resolvedLanguage) => { i18n.loadCoreLocale = async (language = i18n.resolvedLanguage) => {
if (language === 'en') { if (language === i18n.options.fallbackLng[0]) {
return; return;
} }
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'bg', language: 'bg-BG',
country: 'bg', country: 'bg',
name: 'Български', name: 'Български',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'cs', language: 'cs-CZ',
country: 'cz', country: 'cz',
name: 'Čeština', name: 'Čeština',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'da', language: 'da-DK',
country: 'dk', country: 'dk',
name: 'Dansk', name: 'Dansk',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'de', language: 'de-DE',
country: 'de', country: 'de',
name: 'Deutsch', name: 'Deutsch',
embeddedLocale: login, embeddedLocale: login,
@@ -4,8 +4,8 @@ import login from './login';
import core from './core'; import core from './core';
export default { export default {
language: 'en', language: 'en-US',
country: 'gb', country: 'us',
name: 'English', name: 'English',
embeddedLocale: merge(login, core), embeddedLocale: merge(login, core),
}; };
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'es', language: 'es-ES',
country: 'es', country: 'es',
name: 'Español', name: 'Español',
embeddedLocale: login, embeddedLocale: login,
+254
View File
@@ -0,0 +1,254 @@
import dateFns from 'date-fns/locale/fa-IR';
export default {
dateFns,
format: {
date: 'M/d/yyyy',
time: 'p',
dateTime: '$t(format:date) $t(format:time)',
longDate: 'MMM d',
longDateTime: "MMMM d 'at' p",
fullDate: 'MMM d, y',
fullDateTime: "MMMM d, y 'at' p",
},
translation: {
common: {
aboutPlanka: 'درباره Planka',
account: 'حساب کاربری',
actions: 'اقدامات',
addAttachment_title: 'اضافه کردن پیوست',
addComment: 'اضافه کردن نظر',
addManager_title: 'اضافه کردن مدیر',
addMember_title: 'اضافه کردن عضو',
addUser_title: 'اضافه کردن کاربر',
administrator: 'مدیر سیستم',
all: 'همه',
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
'تمام تغییرات به صورت خودکار ذخیره می‌شوند<br />بعد از بازیابی ارتباط.',
areYouSureYouWantToDeleteThisAttachment:
'آیا مطمئن هستید که می‌خواهید این پیوست را حذف کنید؟',
areYouSureYouWantToDeleteThisBoard: 'آیا مطمئن هستید که می‌خواهید این برد را حذف کنید؟',
areYouSureYouWantToDeleteThisCard: 'آیا مطمئن هستید که می‌خواهید این کارت را حذف کنید؟',
areYouSureYouWantToDeleteThisComment: 'آیا مطمئن هستید که می‌خواهید این نظر را حذف کنید؟',
areYouSureYouWantToDeleteThisLabel: 'آیا مطمئن هستید که می‌خواهید این برچسب را حذف کنید؟',
areYouSureYouWantToDeleteThisList: 'آیا مطمئن هستید که می‌خواهید این لیست را حذف کنید؟',
areYouSureYouWantToDeleteThisProject: 'آیا مطمئن هستید که می‌خواهید این پروژه را حذف کنید؟',
areYouSureYouWantToDeleteThisTask: 'آیا مطمئن هستید که می‌خواهید این وظیفه را حذف کنید؟',
areYouSureYouWantToDeleteThisUser: 'آیا مطمئن هستید که می‌خواهید این کاربر را حذف کنید؟',
areYouSureYouWantToLeaveBoard: 'آیا مطمئن هستید که می‌خواهید از برد خارج شوید؟',
areYouSureYouWantToLeaveProject: 'آیا مطمئن هستید که می‌خواهید از پروژه خارج شوید؟',
areYouSureYouWantToRemoveThisManagerFromProject:
'آیا مطمئن هستید که می‌خواهید این مدیر را از پروژه حذف کنید؟',
areYouSureYouWantToRemoveThisMemberFromBoard:
'آیا مطمئن هستید که می‌خواهید این عضو را از برد حذف کنید؟',
attachment: 'پیوست',
attachments: 'پیوست‌ها',
authentication: 'احراز هویت',
background: 'پس‌زمینه',
board: 'برد',
boardNotFound_title: 'برد یافت نشد',
canComment: 'می‌تواند نظر بدهد',
canEditContentOfBoard: 'می‌تواند محتوای برد را ویرایش کند.',
canOnlyViewBoard: 'فقط می‌تواند برد را مشاهده کند.',
cardActions_title: 'اقدامات کارت',
cardNotFound_title: 'کارت یافت نشد',
cardOrActionAreDeleted: 'کارت یا اقدام حذف شده‌اند.',
color: 'رنگ',
copy_inline: 'کپی',
createBoard_title: 'ایجاد برد',
createLabel_title: 'ایجاد برچسب',
createNewOneOrSelectExistingOne: 'یک جدید ایجاد کنید یا<br />یکی موجود را انتخاب کنید.',
createProject_title: 'ایجاد پروژه',
createTextFile_title: 'ایجاد فایل متنی',
currentPassword: 'رمز عبور فعلی',
dangerZone_title: 'منطقه خطر',
date: 'تاریخ',
dueDate: 'تاریخ سررسید',
dueDate_title: 'تاریخ سررسید',
deleteAttachment_title: 'حذف پیوست',
deleteBoard_title: 'حذف برد',
deleteCard_title: 'حذف کارت',
deleteComment_title: 'حذف نظر',
deleteLabel_title: 'حذف برچسب',
deleteList_title: 'حذف لیست',
deleteProject_title: 'حذف پروژه',
deleteTask_title: 'حذف وظیفه',
deleteUser_title: 'حذف کاربر',
description: 'توضیحات',
detectAutomatically: 'تشخیص خودکار',
dropFileToUpload: 'فایل را برای آپلود بکشید',
editor: 'ویرایشگر',
editAttachment_title: 'ویرایش پیوست',
editAvatar_title: 'ویرایش آواتار',
editBoard_title: 'ویرایش برد',
editDueDate_title: 'ویرایش تاریخ سررسید',
editEmail_title: 'ویرایش ایمیل',
editInformation_title: 'ویرایش اطلاعات',
editLabel_title: 'ویرایش برچسب',
editPassword_title: 'ویرایش رمز عبور',
editPermissions_title: 'ویرایش دسترسی‌ها',
editStopwatch_title: 'ویرایش کرنومتر',
editUsername_title: 'ویرایش نام کاربری',
email: 'ایمیل',
emailAlreadyInUse: 'ایمیل قبلا استفاده شده است',
enterCardTitle: 'عنوان کارت را وارد کنید... [Ctrl+Enter] برای باز شدن خودکار.',
enterDescription: 'توضیحات را وارد کنید...',
enterFilename: 'نام فایل را وارد کنید',
enterListTitle: 'عنوان لیست را وارد کنید...',
enterProjectTitle: 'عنوان پروژه را وارد کنید',
enterTaskDescription: 'توضیحات وظیفه را وارد کنید...',
filterByLabels_title: 'فیلتر بر اساس برچسب‌ها',
filterByMembers_title: 'فیلتر بر اساس اعضا',
fromComputer_title: 'از کامپیوتر',
fromTrello: 'از Trello',
general: 'عمومی',
hours: 'ساعت‌ها',
importBoard_title: 'وارد کردن برد',
invalidCurrentPassword: 'رمز عبور فعلی نامعتبر است',
labels: 'برچسب‌ها',
language: 'زبان',
leaveBoard_title: 'ترک برد',
leaveProject_title: 'ترک پروژه',
linkIsCopied: 'لینک کپی شد',
list: 'لیست',
listActions_title: 'اقدامات لیست',
managers: 'مدیران',
managerActions_title: 'اقدامات مدیر',
members: 'اعضا',
memberActions_title: 'اقدامات عضو',
minutes: 'دقیقه‌ها',
moveCard_title: 'انتقال کارت',
name: 'نام',
newestFirst: 'جدیدترین اول',
newEmail: 'ایمیل جدید',
newPassword: 'رمز عبور جدید',
newUsername: 'نام کاربری جدید',
noConnectionToServer: 'ارتباط با سرور قطع است',
noBoards: 'بردی وجود ندارد',
noLists: 'لیستی وجود ندارد',
noProjects: 'پروژه‌ای وجود ندارد',
notifications: 'اعلان‌ها',
noUnreadNotifications: 'اعلان خوانده نشده‌ای وجود ندارد.',
oldestFirst: 'قدیمی‌ترین اول',
openBoard_title: 'باز کردن برد',
optional_inline: 'اختیاری',
organization: 'سازمان',
phone: 'تلفن',
preferences: 'ترجیحات',
pressPasteShortcutToAddAttachmentFromClipboard:
'نکته: با فشردن Ctrl-V (Cmd-V در مک) می‌توانید پیوست را از کلیپ بورد اضافه کنید.',
project: 'پروژه',
projectNotFound_title: 'پروژه یافت نشد',
removeManager_title: 'حذف مدیر',
removeMember_title: 'حذف عضو',
searchLabels: 'جستجوی برچسب‌ها...',
searchMembers: 'جستجوی اعضا...',
searchUsers: 'جستجوی کاربران...',
searchCards: 'جستجوی کارت‌ها...',
seconds: 'ثانیه‌ها',
selectBoard: 'انتخاب برد',
selectList: 'انتخاب لیست',
selectPermissions_title: 'انتخاب دسترسی‌ها',
selectProject: 'انتخاب پروژه',
settings: 'تنظیمات',
sortList_title: 'مرتب‌سازی لیست',
stopwatch: 'کرنومتر',
subscribeToMyOwnCardsByDefault: 'به طور پیش‌فرض به کارت‌های خودم مشترک شوم',
taskActions_title: 'اقدامات وظیفه',
tasks: 'وظایف',
thereIsNoPreviewAvailableForThisAttachment: 'پیش نمایشی برای این پیوست موجود نیست.',
time: 'زمان',
title: 'عنوان',
userActions_title: 'اقدامات کاربر',
userAddedThisCardToList: '<0>{{user}}</0><1> این کارت را به {{list}} اضافه کرد</1>',
userLeftNewCommentToCard: '{{user}} نظر جدید «{{comment}}» را به <2>{{card}}</2> اضافه کرد',
userMovedCardFromListToList:
'{{user}} <2>{{card}}</2> را از {{fromList}} به {{toList}} منتقل کرد',
userMovedThisCardFromListToList:
'<0>{{user}}</0><1> این کارت را از {{fromList}} به {{toList}} منتقل کرد</1>',
username: 'نام کاربری',
usernameAlreadyInUse: 'نام کاربری قبلا استفاده شده است',
users: 'کاربران',
version: 'نسخه',
viewer: 'بیننده',
writeComment: 'نظر بنویسید...',
},
action: {
addAnotherCard: 'اضافه کردن کارت دیگر',
addAnotherList: 'اضافه کردن لیست دیگر',
addAnotherTask: 'اضافه کردن وظیفه دیگر',
addCard: 'اضافه کردن کارت',
addCard_title: 'اضافه کردن کارت',
addComment: 'اضافه کردن نظر',
addList: 'اضافه کردن لیست',
addMember: 'اضافه کردن عضو',
addMoreDetailedDescription: 'اضافه کردن توضیحات بیشتر',
addTask: 'اضافه کردن وظیفه',
addToCard: 'اضافه کردن به کارت',
addUser: 'اضافه کردن کاربر',
copyLink_title: 'کپی لینک',
createBoard: 'ایجاد برد',
createFile: 'ایجاد فایل',
createLabel: 'ایجاد برچسب',
createNewLabel: 'ایجاد برچسب جدید',
createProject: 'ایجاد پروژه',
delete: 'حذف',
deleteAttachment: 'حذف پیوست',
deleteAvatar: 'حذف آواتار',
deleteBoard: 'حذف برد',
deleteCard: 'حذف کارت',
deleteCard_title: 'حذف کارت',
deleteComment: 'حذف نظر',
deleteImage: 'حذف تصویر',
deleteLabel: 'حذف برچسب',
deleteList: 'حذف لیست',
deleteList_title: 'حذف لیست',
deleteProject: 'حذف پروژه',
deleteProject_title: 'حذف پروژه',
deleteTask: 'حذف وظیفه',
deleteTask_title: 'حذف وظیفه',
deleteUser: 'حذف کاربر',
duplicate: 'تکرار',
duplicateCard_title: 'تکرار کارت',
edit: 'ویرایش',
editDueDate_title: 'ویرایش تاریخ سررسید',
editDescription_title: 'ویرایش توضیحات',
editEmail_title: 'ویرایش ایمیل',
editInformation_title: 'ویرایش اطلاعات',
editPassword_title: 'ویرایش رمز عبور',
editPermissions: 'ویرایش دسترسی‌ها',
editStopwatch_title: 'ویرایش کرنومتر',
editTitle_title: 'ویرایش عنوان',
editUsername_title: 'ویرایش نام کاربری',
hideDetails: 'پنهان کردن جزئیات',
import: 'وارد کردن',
leaveBoard: 'ترک برد',
leaveProject: 'ترک پروژه',
logOut_title: 'خروج',
makeCover_title: 'ایجاد کاور',
move: 'انتقال',
moveCard_title: 'انتقال کارت',
remove: 'حذف',
removeBackground: 'حذف پس‌زمینه',
removeCover_title: 'حذف کاور',
removeFromBoard: 'حذف از برد',
removeFromProject: 'حذف از پروژه',
removeManager: 'حذف مدیر',
removeMember: 'حذف عضو',
save: 'ذخیره',
showAllAttachments: 'نمایش همه پیوست‌ها ({{hidden}} مخفی)',
showDetails: 'نمایش جزئیات',
showFewerAttachments: 'نمایش کمتر پیوست‌ها',
sortList_title: 'مرتب‌سازی لیست',
start: 'شروع',
stop: 'توقف',
subscribe: 'مشترک شدن',
unsubscribe: 'لغو اشتراک',
uploadNewAvatar: 'آپلود آواتار جدید',
uploadNewImage: 'آپلود تصویر جدید',
},
},
};
+8
View File
@@ -0,0 +1,8 @@
import login from './login';
export default {
language: 'fa-IR',
country: 'ir',
name: 'فارسی',
embeddedLocale: login,
};
+22
View File
@@ -0,0 +1,22 @@
export default {
translation: {
common: {
emailOrUsername: 'ایمیل یا نام کاربری',
invalidEmailOrUsername: 'ایمیل یا نام کاربری نامعتبر است',
invalidPassword: 'رمز عبور نامعتبر است',
logInToPlanka: 'ورود به Planka',
noInternetConnection: 'بدون اتصال به اینترنت',
pageNotFound_title: 'صفحه یافت نشد',
password: 'رمز عبور',
projectManagement: 'مدیریت پروژه',
serverConnectionFailed: 'اتصال به سرور ناموفق بود',
unknownError: 'خطای ناشناخته، بعداً دوباره تلاش کنید',
useSingleSignOn: 'استفاده از ورود یکپارچه',
},
action: {
logIn: 'ورود',
logInWithSSO: 'ورود با SSO',
},
},
};
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'fr', language: 'fr-FR',
country: 'fr', country: 'fr',
name: 'Français', name: 'Français',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'hu', language: 'hu-HU',
country: 'hu', country: 'hu',
name: 'Magyar', name: 'Magyar',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'id', language: 'id-ID',
country: 'id', country: 'id',
name: 'Bahasa Indonesia', name: 'Bahasa Indonesia',
embeddedLocale: login, embeddedLocale: login,
+48 -46
View File
@@ -1,51 +1,53 @@
import bg from './bg'; import bgBG from './bg-BG';
import cs from './cs'; import csCZ from './cs-CZ';
import da from './da'; import daDK from './da-DK';
import de from './de'; import deDE from './de-DE';
import en from './en'; import enUS from './en-US';
import es from './es'; import esES from './es-ES';
import fr from './fr'; import faIR from './fa-IR';
import hu from './hu'; import frFR from './fr-FR';
import id from './id'; import huHU from './hu-HU';
import it from './it'; import idID from './id-ID';
import ja from './ja'; import itIT from './it-IT';
import ko from './ko'; import jaJP from './ja-JP';
import nl from './nl'; import koKR from './ko-KR';
import pl from './pl'; import nlNL from './nl-NL';
import pt from './pt'; import plPL from './pl-PL';
import ro from './ro'; import ptBR from './pt-BR';
import ru from './ru'; import roRO from './ro-RO';
import sk from './sk'; import ruRU from './ru-RU';
import sv from './sv'; import skSK from './sk-SK';
import tr from './tr'; import svSE from './sv-SE';
import ua from './ua'; import trTR from './tr-TR';
import uz from './uz'; import ukUA from './uk-UA';
import zh from './zh'; import uzUZ from './uz-UZ';
import zhCN from './zh-CN';
const locales = [ const locales = [
bg, bgBG,
cs, csCZ,
da, daDK,
de, deDE,
en, enUS,
es, esES,
fr, faIR,
hu, frFR,
id, huHU,
it, idID,
ja, itIT,
ko, jaJP,
nl, koKR,
pl, nlNL,
pt, plPL,
ro, ptBR,
ru, roRO,
sk, ruRU,
sv, skSK,
tr, svSE,
ua, trTR,
uz, ukUA,
zh, uzUZ,
zhCN,
]; ];
export default locales; export default locales;
@@ -1,4 +1,8 @@
import dateFns from 'date-fns/locale/it';
export default { export default {
dateFns,
format: { format: {
date: 'd/M/yyyy', date: 'd/M/yyyy',
time: 'p', time: 'p',
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'it', language: 'it-IT',
country: 'it', country: 'it',
name: 'Italiano', name: 'Italiano',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'ja', language: 'ja-JP',
country: 'jp', country: 'jp',
name: '日本語', name: '日本語',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'ko', language: 'ko-KR',
country: 'kr', country: 'kr',
name: '한국어', name: '한국어',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'nl', language: 'nl-NL',
country: 'nl', country: 'nl',
name: 'Nederlands', name: 'Nederlands',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'pl', language: 'pl-PL',
country: 'pl', country: 'pl',
name: 'Polski', name: 'Polski',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'pt', language: 'pt-BR',
country: 'br', country: 'br',
name: 'Português', name: 'Português',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'ro', language: 'ro-RO',
country: 'ro', country: 'ro',
name: 'Română', name: 'Română',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'ru', language: 'ru-RU',
country: 'ru', country: 'ru',
name: 'Русский', name: 'Русский',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'sk', language: 'sk-SK',
country: 'sk', country: 'sk',
name: 'Slovenčina', name: 'Slovenčina',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'sv', language: 'sv-SE',
country: 'se', country: 'se',
name: 'Svenska', name: 'Svenska',
embeddedLocale: login, embeddedLocale: login,
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'tr', language: 'tr-TR',
country: 'tr', country: 'tr',
name: 'Türkçe', name: 'Türkçe',
embeddedLocale: login, embeddedLocale: login,
@@ -1,4 +1,8 @@
import dateFns from 'date-fns/locale/uk';
export default { export default {
dateFns,
format: { format: {
date: 'd/M/yyyy', date: 'd/M/yyyy',
time: 'p', time: 'p',
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'ua', language: 'uk-UA',
country: 'ua', country: 'ua',
name: 'Українська', name: 'Українська',
embeddedLocale: login, embeddedLocale: login,
@@ -1,4 +1,8 @@
import dateFns from 'date-fns/locale/uz';
export default { export default {
dateFns,
format: { format: {
date: 'M/d/yyyy', date: 'M/d/yyyy',
time: 'p', time: 'p',
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'uz', language: 'uz-UZ',
country: 'uz', country: 'uz',
name: "O'zbek", name: "O'zbek",
embeddedLocale: login, embeddedLocale: login,
@@ -1,4 +1,8 @@
import dateFns from 'date-fns/locale/zh-CN';
export default { export default {
dateFns,
format: { format: {
date: 'M/d/yyyy', date: 'M/d/yyyy',
time: 'p', time: 'p',
@@ -1,7 +1,7 @@
import login from './login'; import login from './login';
export default { export default {
language: 'zh', language: 'zh-CN',
country: 'cn', country: 'cn',
name: '中文', name: '中文',
embeddedLocale: login, embeddedLocale: login,
+4
View File
@@ -29,7 +29,11 @@ services:
# - OIDC_ISSUER= # - OIDC_ISSUER=
# - OIDC_CLIENT_ID= # - OIDC_CLIENT_ID=
# - OIDC_CLIENT_SECRET= # - OIDC_CLIENT_SECRET=
# - OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# - OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# - OIDC_SCOPES=openid email profile # - OIDC_SCOPES=openid email profile
# - OIDC_RESPONSE_MODE=fragment
# - OIDC_USE_DEFAULT_RESPONSE_MODE=true
# - OIDC_ADMIN_ROLES=admin # - OIDC_ADMIN_ROLES=admin
# - OIDC_EMAIL_ATTRIBUTE=email # - OIDC_EMAIL_ATTRIBUTE=email
# - OIDC_NAME_ATTRIBUTE=name # - OIDC_NAME_ATTRIBUTE=name
+4
View File
@@ -36,7 +36,11 @@ services:
# - OIDC_ISSUER= # - OIDC_ISSUER=
# - OIDC_CLIENT_ID= # - OIDC_CLIENT_ID=
# - OIDC_CLIENT_SECRET= # - OIDC_CLIENT_SECRET=
# - OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# - OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# - OIDC_SCOPES=openid email profile # - OIDC_SCOPES=openid email profile
# - OIDC_RESPONSE_MODE=fragment
# - OIDC_USE_DEFAULT_RESPONSE_MODE=true
# - OIDC_ADMIN_ROLES=admin # - OIDC_ADMIN_ROLES=admin
# - OIDC_EMAIL_ATTRIBUTE=email # - OIDC_EMAIL_ATTRIBUTE=email
# - OIDC_NAME_ATTRIBUTE=name # - OIDC_NAME_ATTRIBUTE=name
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "planka", "name": "planka",
"version": "1.19.2", "version": "1.20.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "planka", "name": "planka",
"version": "1.19.2", "version": "1.20.1",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "planka", "name": "planka",
"version": "1.19.2", "version": "1.20.1",
"private": true, "private": true,
"homepage": "https://plankanban.github.io/planka", "homepage": "https://plankanban.github.io/planka",
"repository": { "repository": {
+4
View File
@@ -27,7 +27,11 @@ SECRET_KEY=notsecretkey
# OIDC_ISSUER= # OIDC_ISSUER=
# OIDC_CLIENT_ID= # OIDC_CLIENT_ID=
# OIDC_CLIENT_SECRET= # OIDC_CLIENT_SECRET=
# OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# OIDC_SCOPES=openid email profile # OIDC_SCOPES=openid email profile
# OIDC_RESPONSE_MODE=fragment
# OIDC_USE_DEFAULT_RESPONSE_MODE=true
# OIDC_ADMIN_ROLES=admin # OIDC_ADMIN_ROLES=admin
# OIDC_EMAIL_ATTRIBUTE=email # OIDC_EMAIL_ATTRIBUTE=email
# OIDC_NAME_ATTRIBUTE=name # OIDC_NAME_ATTRIBUTE=name
@@ -4,6 +4,9 @@ const Errors = {
INVALID_CODE_OR_NONCE: { INVALID_CODE_OR_NONCE: {
invalidCodeOrNonce: 'Invalid code or nonce', invalidCodeOrNonce: 'Invalid code or nonce',
}, },
INVALID_USERINFO_SIGNATURE: {
invalidUserinfoSignature: 'Invalid signature on userinfo due to client misconfiguration',
},
EMAIL_ALREADY_IN_USE: { EMAIL_ALREADY_IN_USE: {
emailAlreadyInUse: 'Email already in use', emailAlreadyInUse: 'Email already in use',
}, },
@@ -31,6 +34,9 @@ module.exports = {
invalidCodeOrNonce: { invalidCodeOrNonce: {
responseType: 'unauthorized', responseType: 'unauthorized',
}, },
invalidUserinfoSignature: {
responseType: 'unauthorized',
},
emailAlreadyInUse: { emailAlreadyInUse: {
responseType: 'conflict', responseType: 'conflict',
}, },
@@ -51,6 +57,7 @@ module.exports = {
sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`); sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`);
return Errors.INVALID_CODE_OR_NONCE; return Errors.INVALID_CODE_OR_NONCE;
}) })
.intercept('invalidUserinfoSignature', () => Errors.INVALID_USERINFO_SIGNATURE)
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE) .intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE) .intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE)
.intercept('missingValues', () => Errors.MISSING_VALUES); .intercept('missingValues', () => Errors.MISSING_VALUES);
+9 -4
View File
@@ -4,11 +4,16 @@ module.exports = {
if (sails.hooks.oidc.isActive()) { if (sails.hooks.oidc.isActive()) {
const oidcClient = sails.hooks.oidc.getClient(); const oidcClient = sails.hooks.oidc.getClient();
oidc = { const authorizationUrlParams = {
authorizationUrl: oidcClient.authorizationUrl({
scope: sails.config.custom.oidcScopes, scope: sails.config.custom.oidcScopes,
response_mode: 'fragment', };
}),
if (!sails.config.custom.oidcUseDefaultResponseMode) {
authorizationUrlParams.response_mode = sails.config.custom.oidcResponseMode;
}
oidc = {
authorizationUrl: oidcClient.authorizationUrl(authorizationUrlParams),
endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null, endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null,
isEnforced: sails.config.custom.oidcEnforced, isEnforced: sails.config.custom.oidcEnforced,
}; };
+1 -1
View File
@@ -50,7 +50,7 @@ module.exports = {
}, },
language: { language: {
type: 'string', type: 'string',
isNotEmptyString: true, isIn: User.LANGUAGES,
allowNull: true, allowNull: true,
}, },
subscribeToOwnCards: { subscribeToOwnCards: {
+1 -1
View File
@@ -36,7 +36,7 @@ module.exports = {
}, },
language: { language: {
type: 'string', type: 'string',
isNotEmptyString: true, isIn: User.LANGUAGES,
allowNull: true, allowNull: true,
}, },
subscribeToOwnCards: { subscribeToOwnCards: {
@@ -12,6 +12,7 @@ module.exports = {
exits: { exits: {
invalidCodeOrNonce: {}, invalidCodeOrNonce: {},
invalidUserinfoSignature: {},
missingValues: {}, missingValues: {},
emailAlreadyInUse: {}, emailAlreadyInUse: {},
usernameAlreadyInUse: {}, usernameAlreadyInUse: {},
@@ -34,6 +35,14 @@ module.exports = {
); );
userInfo = await client.userinfo(tokenSet); userInfo = await client.userinfo(tokenSet);
} catch (e) { } catch (e) {
if (
e instanceof SyntaxError &&
e.message.includes('Unexpected token e in JSON at position 0')
) {
sails.log.warn('Error while exchanging OIDC code: userinfo response is signed');
throw 'invalidUserinfoSignature';
}
sails.log.warn(`Error while exchanging OIDC code: ${e}`); sails.log.warn(`Error while exchanging OIDC code: ${e}`);
throw 'invalidCodeOrNonce'; throw 'invalidCodeOrNonce';
} }
+9 -2
View File
@@ -25,12 +25,19 @@ module.exports = function defineOidcHook(sails) {
const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer); const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);
client = new issuer.Client({ const metadata = {
client_id: sails.config.custom.oidcClientId, client_id: sails.config.custom.oidcClientId,
client_secret: sails.config.custom.oidcClientSecret, client_secret: sails.config.custom.oidcClientSecret,
redirect_uris: [sails.config.custom.oidcRedirectUri], redirect_uris: [sails.config.custom.oidcRedirectUri],
response_types: ['code'], response_types: ['code'],
}); userinfo_signed_response_alg: sails.config.custom.oidcUserinfoSignedResponseAlg,
};
if (sails.config.custom.oidcIdTokenSignedResponseAlg) {
metadata.id_token_signed_response_alg = sails.config.custom.oidcIdTokenSignedResponseAlg;
}
client = new issuer.Client(metadata);
}, },
getClient() { getClient() {
+29 -1
View File
@@ -5,11 +5,39 @@
* @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models * @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models
*/ */
const LANGUAGES = [
'bg-BG',
'cs-CZ',
'da-DK',
'de-DE',
'en-US',
'es-ES',
'fa-IR',
'fr-FR',
'hu-HU',
'id-ID',
'it-IT',
'ja-JP',
'ko-KR',
'nl-NL',
'pl-PL',
'pt-BR',
'ro-RO',
'ru-RU',
'sk-SK',
'sv-SE',
'tr-TR',
'uk-UA',
'uz-UZ',
'zh-CN',
];
const OIDC = { const OIDC = {
id: '_oidc', id: '_oidc',
}; };
module.exports = { module.exports = {
LANGUAGES,
OIDC, OIDC,
attributes: { attributes: {
@@ -62,7 +90,7 @@ module.exports = {
}, },
language: { language: {
type: 'string', type: 'string',
isNotEmptyString: true, isIn: LANGUAGES,
allowNull: true, allowNull: true,
}, },
subscribeToOwnCards: { subscribeToOwnCards: {
+4
View File
@@ -39,7 +39,11 @@ module.exports.custom = {
oidcIssuer: process.env.OIDC_ISSUER, oidcIssuer: process.env.OIDC_ISSUER,
oidcClientId: process.env.OIDC_CLIENT_ID, oidcClientId: process.env.OIDC_CLIENT_ID,
oidcClientSecret: process.env.OIDC_CLIENT_SECRET, oidcClientSecret: process.env.OIDC_CLIENT_SECRET,
oidcIdTokenSignedResponseAlg: process.env.OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG,
oidcUserinfoSignedResponseAlg: process.env.OIDC_USERINFO_SIGNED_RESPONSE_ALG,
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile', oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
oidcResponseMode: process.env.OIDC_RESPONSE_MODE || 'fragment',
oidcUseDefaultResponseMode: process.env.OIDC_USE_DEFAULT_RESPONSE_MODE === 'true',
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [], oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
oidcEmailAttribute: process.env.OIDC_EMAIL_ATTRIBUTE || 'email', oidcEmailAttribute: process.env.OIDC_EMAIL_ATTRIBUTE || 'email',
oidcNameAttribute: process.env.OIDC_NAME_ATTRIBUTE || 'name', oidcNameAttribute: process.env.OIDC_NAME_ATTRIBUTE || 'name',
+2
View File
@@ -48,5 +48,7 @@ module.exports.http = {
// var middlewareFn = skipper({ strict: true }); // var middlewareFn = skipper({ strict: true });
// return middlewareFn; // return middlewareFn;
// })(), // })(),
poweredBy: false,
}, },
}; };
@@ -15,7 +15,7 @@ module.exports.up = async (knex) => {
const attachments = await knex('attachment'); const attachments = await knex('attachment');
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (attachment of attachments) { for (const attachment of attachments) {
if (attachment.is_image) { if (attachment.is_image) {
const image = sharp( const image = sharp(
path.join(config.custom.attachmentsPath, attachment.dirname, attachment.filename), path.join(config.custom.attachmentsPath, attachment.dirname, attachment.filename),
@@ -54,7 +54,7 @@ module.exports.down = async (knex) => {
const attachments = await knex('attachment'); const attachments = await knex('attachment');
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (attachment of attachments) { for (const attachment of attachments) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await knex('attachment') await knex('attachment')
.update({ .update({
@@ -93,7 +93,7 @@ module.exports.up = async (knex) => {
const attachments = await knex('attachment').whereNotNull('image'); const attachments = await knex('attachment').whereNotNull('image');
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (attachment of attachments) { for (const attachment of attachments) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
const image = await processAttachmentImage(attachment, config.custom.attachmentsPath); const image = await processAttachmentImage(attachment, config.custom.attachmentsPath);
@@ -113,7 +113,7 @@ module.exports.up = async (knex) => {
const users = await knex('user_account').whereNotNull('avatar'); const users = await knex('user_account').whereNotNull('avatar');
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (user of users) { for (const user of users) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await processUserAvatar(user, config.custom.userAvatarsPath); await processUserAvatar(user, config.custom.userAvatarsPath);
} }
@@ -121,7 +121,7 @@ module.exports.up = async (knex) => {
const projects = await knex('project').whereNotNull('background_image'); const projects = await knex('project').whereNotNull('background_image');
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (project of projects) { for (const project of projects) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await processProjectBackgroundImage(project, config.custom.projectBackgroundImagesPath); await processProjectBackgroundImage(project, config.custom.projectBackgroundImagesPath);
} }
@@ -129,7 +129,7 @@ module.exports.up = async (knex) => {
const attachments = await knex('attachment').whereNotNull('image'); const attachments = await knex('attachment').whereNotNull('image');
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (attachment of attachments) { for (const attachment of attachments) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await processAttachmentImage(attachment, config.custom.attachmentsPath); await processAttachmentImage(attachment, config.custom.attachmentsPath);
} }
@@ -0,0 +1,68 @@
const _ = require('lodash');
const LANGUAGES = [
'bg-BG',
'cs-CZ',
'da-DK',
'de-DE',
'en-US',
'es-ES',
'fa-IR',
'fr-FR',
'hu-HU',
'id-ID',
'it-IT',
'ja-JP',
'ko-KR',
'nl-NL',
'pl-PL',
'pt-BR',
'ro-RO',
'ru-RU',
'sk-SK',
'sv-SE',
'tr-TR',
'uz-UZ',
'zh-CN',
];
const LANGUAGE_BY_PREV_LANGUAGE = LANGUAGES.reduce(
(result, language) => ({
...result,
[language.split('-')[0]]: language,
}),
{},
);
LANGUAGE_BY_PREV_LANGUAGE.ua = 'uk-UA';
const PREV_LANGUAGE_BY_LANGUAGE = _.invert(LANGUAGE_BY_PREV_LANGUAGE);
module.exports.up = async (knex) => {
const users = await knex('user_account').whereNotNull('language');
const prevLanguages = [...new Set(users.map((user) => user.language))];
// eslint-disable-next-line no-restricted-syntax
for (const prevLanguage of prevLanguages) {
// eslint-disable-next-line no-await-in-loop
await knex('user_account')
.update({
language: LANGUAGE_BY_PREV_LANGUAGE[prevLanguage],
})
.where('language', prevLanguage);
}
};
module.exports.down = async (knex) => {
const users = await knex('user_account').whereNotNull('language');
const languages = [...new Set(users.map((user) => user.language))];
// eslint-disable-next-line no-restricted-syntax
for (const language of languages) {
// eslint-disable-next-line no-await-in-loop
await knex('user_account')
.update({
language: PREV_LANGUAGE_BY_LANGUAGE[language],
})
.where('language', language);
}
};
+12 -12
View File
@@ -2164,9 +2164,9 @@
} }
}, },
"node_modules/engine.io": { "node_modules/engine.io": {
"version": "6.5.4", "version": "6.5.5",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
"integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
"dependencies": { "dependencies": {
"@types/cookie": "^0.4.1", "@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12", "@types/cors": "^2.8.12",
@@ -2177,7 +2177,7 @@
"cors": "~2.8.5", "cors": "~2.8.5",
"debug": "~4.3.1", "debug": "~4.3.1",
"engine.io-parser": "~5.2.1", "engine.io-parser": "~5.2.1",
"ws": "~8.11.0" "ws": "~8.17.1"
}, },
"engines": { "engines": {
"node": ">=10.2.0" "node": ">=10.2.0"
@@ -7119,12 +7119,12 @@
} }
}, },
"node_modules/socket.io-adapter": { "node_modules/socket.io-adapter": {
"version": "2.5.4", "version": "2.5.5",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
"integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
"dependencies": { "dependencies": {
"debug": "~4.3.4", "debug": "~4.3.4",
"ws": "~8.11.0" "ws": "~8.17.1"
} }
}, },
"node_modules/socket.io-parser": { "node_modules/socket.io-parser": {
@@ -8190,15 +8190,15 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.11.0", "version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"bufferutil": "^4.0.1", "bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2" "utf-8-validate": ">=5.0.2"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"bufferutil": { "bufferutil": {