Compare commits

...
15 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
Maksim Eltyshev 8d74cc1732 chore: Update version 2024-07-15 16:24:31 +02:00
Emmanuel Guyot ea94fe8bf3 fix: Order users by name (#806) 2024-07-15 15:26:26 +02:00
Αλέξανδρος 6052f8999f fix: Fix application crashing when webhook is unavailable (#815) 2024-07-15 14:40:33 +02:00
ItzAndriss 67d1b1c194 fix: Update Hungarian translation (#823) 2024-07-15 14:25:31 +02:00
Stephan Michard b3cb8405f5 fix: Update German translation (#821) 2024-07-15 14:19:06 +02:00
Maksim Eltyshev 7acaec6d04 fix: Fix incorrect related data when transferring card
Closes #431, closes #803
2024-07-15 14:01:06 +02:00
109 changed files with 698 additions and 160 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.2 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.1" appVersion: "1.20.1"
dependencies: dependencies:
- alias: postgresql - alias: postgresql
+1 -1
View File
@@ -1 +1 @@
REACT_APP_VERSION=1.19.1 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",
+6 -1
View File
@@ -57,10 +57,15 @@ updateCard.failure = (id, error) => ({
}, },
}); });
const handleCardUpdate = (card) => ({ const handleCardUpdate = (card, isFetched, cardMemberships, cardLabels, tasks, attachments) => ({
type: ActionTypes.CARD_UPDATE_HANDLE, type: ActionTypes.CARD_UPDATE_HANDLE,
payload: { payload: {
card, card,
isFetched,
cardMemberships,
cardLabels,
tasks,
attachments,
}, },
}); });
-3
View File
@@ -195,9 +195,6 @@ export default {
CARD_UPDATE__SUCCESS: 'CARD_UPDATE__SUCCESS', CARD_UPDATE__SUCCESS: 'CARD_UPDATE__SUCCESS',
CARD_UPDATE__FAILURE: 'CARD_UPDATE__FAILURE', CARD_UPDATE__FAILURE: 'CARD_UPDATE__FAILURE',
CARD_UPDATE_HANDLE: 'CARD_UPDATE_HANDLE', CARD_UPDATE_HANDLE: 'CARD_UPDATE_HANDLE',
CARD_TRANSFER: 'CARD_TRANSFER',
CARD_TRANSFER__SUCCESS: 'CARD_TRANSFER__SUCCESS',
CARD_TRANSFER__FAILURE: 'CARD_TRANSFER__FAILURE',
CARD_DUPLICATE: 'CARD_DUPLICATE', CARD_DUPLICATE: 'CARD_DUPLICATE',
CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS', CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS',
CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE', CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE',
+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,
@@ -192,6 +192,8 @@ export default {
deleteTask: 'Aufgabe löschen', deleteTask: 'Aufgabe löschen',
deleteTask_title: 'Aufgabe löschen', deleteTask_title: 'Aufgabe löschen',
deleteUser: 'Benutzer löschen', deleteUser: 'Benutzer löschen',
duplicate: 'Kopieren',
duplicateCard_title: 'Karte kopieren',
edit: 'Bearbeiten', edit: 'Bearbeiten',
editDueDate_title: 'Fälligkeitsdatum bearbeiten', editDueDate_title: 'Fälligkeitsdatum bearbeiten',
editDescription_title: 'Beschreibung ändern', editDescription_title: 'Beschreibung ändern',
@@ -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,
@@ -15,6 +15,16 @@ export default {
translation: { translation: {
common: { common: {
aboutPlanka: 'Plankáról',
account: 'Fiók',
actions: 'Műveletek',
addAttachment_title: 'Melléklet hozzáadása',
addComment: 'Megjegyzés hozzáadása',
addManager_title: 'Menedzser hozzáadása',
addMember_title: 'Tag létrehozása',
addUser_title: 'Felhasználó hozzáadása',
administrator: 'Rendszergazda',
all: 'Összes',
allChangesWillBeAutomaticallySavedAfterConnectionRestored: allChangesWillBeAutomaticallySavedAfterConnectionRestored:
'Az összes változás automatikusan mentésre kerül a kapcsolat helyreállása után.', 'Az összes változás automatikusan mentésre kerül a kapcsolat helyreállása után.',
areYouSureYouWantToDeleteThisAttachment: 'Biztosan törölni szeretné ezt a mellékletet?', areYouSureYouWantToDeleteThisAttachment: 'Biztosan törölni szeretné ezt a mellékletet?',
@@ -29,7 +39,7 @@ export default {
areYouSureYouWantToLeaveBoard: 'Biztosan el akarja hagyni a táblát?', areYouSureYouWantToLeaveBoard: 'Biztosan el akarja hagyni a táblát?',
areYouSureYouWantToLeaveProject: 'Biztosan el akarja hagyni a projektet?', areYouSureYouWantToLeaveProject: 'Biztosan el akarja hagyni a projektet?',
areYouSureYouWantToRemoveThisManagerFromProject: areYouSureYouWantToRemoveThisManagerFromProject:
'Biztosan eltávolítja ezt a vezetőt a projektből?', 'Biztosan eltávolítja ezt a menedzsert a projektből?',
areYouSureYouWantToRemoveThisMemberFromBoard: 'Biztosan eltávolítja ezt a tagot a tábláról?', areYouSureYouWantToRemoveThisMemberFromBoard: 'Biztosan eltávolítja ezt a tagot a tábláról?',
attachment: 'Melléklet', attachment: 'Melléklet',
attachments: 'Mellékletek', attachments: 'Mellékletek',
@@ -44,6 +54,7 @@ export default {
cardNotFound_title: 'Kártya nem található', cardNotFound_title: 'Kártya nem található',
cardOrActionAreDeleted: 'A kártya vagy művelet törölve lett.', cardOrActionAreDeleted: 'A kártya vagy művelet törölve lett.',
color: 'Szín', color: 'Szín',
copy_inline: 'másolás',
createBoard_title: 'Tábla létrehozása', createBoard_title: 'Tábla létrehozása',
createLabel_title: 'Címke létrehozása', createLabel_title: 'Címke létrehozása',
createNewOneOrSelectExistingOne: 'Hozzon létre egy újat, vagy válasszon ki egy meglévőt.', createNewOneOrSelectExistingOne: 'Hozzon létre egy újat, vagy válasszon ki egy meglévőt.',
@@ -52,6 +63,7 @@ export default {
currentPassword: 'Jelenlegi jelszó', currentPassword: 'Jelenlegi jelszó',
dangerZone_title: 'Veszélyzóna', dangerZone_title: 'Veszélyzóna',
date: 'Dátum', date: 'Dátum',
dueDate: 'Határidő',
dueDate_title: 'Esedékesség dátuma', dueDate_title: 'Esedékesség dátuma',
deleteAttachment_title: 'Melléklet törlése', deleteAttachment_title: 'Melléklet törlése',
deleteBoard_title: 'Tábla törlése', deleteBoard_title: 'Tábla törlése',
@@ -97,13 +109,17 @@ export default {
language: 'Nyelv', language: 'Nyelv',
leaveBoard_title: 'Tábla elhagyása', leaveBoard_title: 'Tábla elhagyása',
leaveProject_title: 'Projekt elhagyása', leaveProject_title: 'Projekt elhagyása',
linkIsCopied: 'Link másolva',
list: 'Lista', list: 'Lista',
listActions_title: 'Lista műveletek', listActions_title: 'Lista műveletek',
managers: 'Vezetők', managers: 'Menedzserek',
managerActions_title: 'Menedzseri műveletek',
members: 'Tagok', members: 'Tagok',
memberActions_title: 'Tag műveletek',
minutes: 'Percek', minutes: 'Percek',
moveCard_title: 'Kártya áthelyezése', moveCard_title: 'Kártya áthelyezése',
name: 'Név', name: 'Név',
newestFirst: 'Újabbak előre',
newEmail: 'Új e-mail', newEmail: 'Új e-mail',
newPassword: 'Új jelszó', newPassword: 'Új jelszó',
newUsername: 'Új felhasználónév', newUsername: 'Új felhasználónév',
@@ -113,6 +129,7 @@ export default {
noProjects: 'Nincsenek projektek', noProjects: 'Nincsenek projektek',
notifications: 'Értesítések', notifications: 'Értesítések',
noUnreadNotifications: 'Nincsenek olvasatlan értesítések.', noUnreadNotifications: 'Nincsenek olvasatlan értesítések.',
oldestFirst: 'Régebbi előre',
openBoard_title: 'Tábla megnyitása', openBoard_title: 'Tábla megnyitása',
optional_inline: 'opcionális', optional_inline: 'opcionális',
organization: 'Szervezet', organization: 'Szervezet',
@@ -122,17 +139,19 @@ export default {
'Tipp: nyomja meg a Ctrl-V (Cmd-V a Mac-en) billentyűkombinációt a vágólapról történő melléklet hozzáadásához.', 'Tipp: nyomja meg a Ctrl-V (Cmd-V a Mac-en) billentyűkombinációt a vágólapról történő melléklet hozzáadásához.',
project: 'Projekt', project: 'Projekt',
projectNotFound_title: 'Projekt nem található', projectNotFound_title: 'Projekt nem található',
removeManager_title: 'Vezető eltávolítása', removeManager_title: 'Menedzser eltávolítása',
removeMember_title: 'Tag eltávolítása', removeMember_title: 'Tag eltávolítása',
searchLabels: 'Címkék keresése...', searchLabels: 'Címkék keresése...',
searchMembers: 'Tagok keresése...', searchMembers: 'Tagok keresése...',
searchUsers: 'Felhasználók keresése...', searchUsers: 'Felhasználók keresése...',
searchCards: 'Kártyák keresése..',
seconds: 'Másodpercek', seconds: 'Másodpercek',
selectBoard: 'Válassza ki a táblát', selectBoard: 'Válassza ki a táblát',
selectList: 'Válassza ki a listát', selectList: 'Válassza ki a listát',
selectPermissions_title: 'Jogosultságok kiválasztása', selectPermissions_title: 'Jogosultságok kiválasztása',
selectProject: 'Válassza ki a projektet', selectProject: 'Válassza ki a projektet',
settings: 'Beállítások', settings: 'Beállítások',
sortList_title: 'Rendezés listában',
stopwatch: 'Stopper', stopwatch: 'Stopper',
subscribeToMyOwnCardsByDefault: 'Alapértelmezés szerint feliratkozás a saját kártyáimra', subscribeToMyOwnCardsByDefault: 'Alapértelmezés szerint feliratkozás a saját kártyáimra',
taskActions_title: 'Feladat műveletek', taskActions_title: 'Feladat műveletek',
@@ -170,6 +189,7 @@ export default {
addTask: 'Feladat hozzáadása', addTask: 'Feladat hozzáadása',
addToCard: 'Kártyához hozzáadása', addToCard: 'Kártyához hozzáadása',
addUser: 'Felhasználó hozzáadása', addUser: 'Felhasználó hozzáadása',
copyLink_title: 'Link másolása',
createBoard: 'Tábla létrehozása', createBoard: 'Tábla létrehozása',
createFile: 'Fájl létrehozása', createFile: 'Fájl létrehozása',
createLabel: 'Címke létrehozása', createLabel: 'Címke létrehozása',
@@ -191,6 +211,8 @@ export default {
deleteTask: 'Feladat törlése', deleteTask: 'Feladat törlése',
deleteTask_title: 'Feladat törlése', deleteTask_title: 'Feladat törlése',
deleteUser: 'Felhasználó törlése', deleteUser: 'Felhasználó törlése',
duplicate: 'Másolás',
duplicateCard_title: 'Kártya másolása',
edit: 'Szerkesztés', edit: 'Szerkesztés',
editDueDate_title: 'Esedékesség dátumának szerkesztése', editDueDate_title: 'Esedékesség dátumának szerkesztése',
editDescription_title: 'Leírás szerkesztése', editDescription_title: 'Leírás szerkesztése',
@@ -214,12 +236,13 @@ export default {
removeCover_title: 'Borító eltávolítása', removeCover_title: 'Borító eltávolítása',
removeFromBoard: 'Eltávolítás a tábláról', removeFromBoard: 'Eltávolítás a tábláról',
removeFromProject: 'Eltávolítás a projektről', removeFromProject: 'Eltávolítás a projektről',
removeManager: 'Vezető eltávolítása', removeManager: 'Menedzser eltávolítása',
removeMember: 'Tag eltávolítása', removeMember: 'Tag eltávolítása',
save: 'Mentés', save: 'Mentés',
showAllAttachments: 'Összes melléklet megjelenítése ({{hidden}} rejtve)', showAllAttachments: 'Összes melléklet megjelenítése ({{hidden}} rejtve)',
showDetails: 'Részletek megjelenítése', showDetails: 'Részletek megjelenítése',
showFewerAttachments: 'Kevesebb melléklet megjelenítése', showFewerAttachments: 'Kevesebb melléklet megjelenítése',
sortList_title: 'Rendezés listában',
start: 'Indítás', start: 'Indítás',
stop: 'Megállítás', stop: 'Megállítás',
subscribe: 'Feliratkozás', subscribe: 'Feliratkozás',
@@ -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,
+1
View File
@@ -28,6 +28,7 @@ export default class extends BaseModel {
case ActionTypes.CORE_INITIALIZE: case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE: case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE: case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.attachments) { if (payload.attachments) {
payload.attachments.forEach((attachment) => { payload.attachments.forEach((attachment) => {
Attachment.upsert(attachment); Attachment.upsert(attachment);
+7 -4
View File
@@ -1,3 +1,4 @@
import orderBy from 'lodash/orderBy';
import { attr, fk, many } from 'redux-orm'; import { attr, fk, many } from 'redux-orm';
import BaseModel from './BaseModel'; import BaseModel from './BaseModel';
@@ -218,10 +219,6 @@ export default class extends BaseModel {
} }
} }
getOrderedMembershipsQuerySet() {
return this.memberships.orderBy('createdAt');
}
getOrderedLabelsQuerySet() { getOrderedLabelsQuerySet() {
return this.labels.orderBy('position'); return this.labels.orderBy('position');
} }
@@ -230,6 +227,12 @@ export default class extends BaseModel {
return this.lists.orderBy('position'); return this.lists.orderBy('position');
} }
getOrderedMembershipsModelArray() {
return orderBy(this.memberships.toModelArray(), (boardMembershipModel) =>
boardMembershipModel.user.name.toLocaleLowerCase(),
);
}
getMembershipModelForUser(userId) { getMembershipModelForUser(userId) {
return this.memberships return this.memberships
.filter({ .filter({
+34 -3
View File
@@ -180,7 +180,6 @@ export default class extends BaseModel {
break; break;
case ActionTypes.CARD_CREATE: case ActionTypes.CARD_CREATE:
case ActionTypes.CARD_UPDATE__SUCCESS: case ActionTypes.CARD_UPDATE__SUCCESS:
case ActionTypes.CARD_UPDATE_HANDLE:
Card.upsert(payload.card); Card.upsert(payload.card);
break; break;
@@ -202,8 +201,40 @@ export default class extends BaseModel {
break; break;
} }
case ActionTypes.CARD_UPDATE: case ActionTypes.CARD_UPDATE: {
Card.withId(payload.id).update(payload.data); const cardModel = Card.withId(payload.id);
// TODO: introduce separate action?
if (payload.data.boardId && payload.data.boardId !== cardModel.boardId) {
cardModel.deleteWithRelated();
} else {
cardModel.update(payload.data);
}
break;
}
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.isFetched) {
const cardModel = Card.withId(payload.card.id);
if (cardModel) {
cardModel.deleteWithRelated();
}
}
Card.upsert(payload.card);
if (payload.cardMemberships) {
payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId);
});
}
if (payload.cardLabels) {
payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId);
});
}
break; break;
case ActionTypes.CARD_DUPLICATE: { case ActionTypes.CARD_DUPLICATE: {
+1
View File
@@ -27,6 +27,7 @@ export default class extends BaseModel {
case ActionTypes.CORE_INITIALIZE: case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE: case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE: case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.tasks) { if (payload.tasks) {
payload.tasks.forEach((task) => { payload.tasks.forEach((task) => {
Task.upsert(task); Task.upsert(task);
+1 -1
View File
@@ -298,7 +298,7 @@ export default class extends BaseModel {
static getOrderedUndeletedQuerySet() { static getOrderedUndeletedQuerySet() {
return this.filter({ return this.filter({
deletedAt: null, deletedAt: null,
}).orderBy('createdAt'); }).orderBy((user) => user.name.toLocaleLowerCase());
} }
getOrderedProjectManagersQuerySet() { getOrderedProjectManagersQuerySet() {
+23 -2
View File
@@ -81,9 +81,30 @@ export function* updateCurrentCard(data) {
yield call(updateCard, cardId, data); yield call(updateCard, cardId, data);
} }
// TODO: handle card transfer
export function* handleCardUpdate(card) { export function* handleCardUpdate(card) {
yield put(actions.handleCardUpdate(card)); let fetch = false;
if (card.boardId) {
const prevCard = yield select(selectors.selectCardById, card.id);
fetch = !prevCard || prevCard.boardId !== card.boardId;
}
let cardMemberships;
let cardLabels;
let tasks;
let attachments;
if (fetch) {
try {
({
item: card, // eslint-disable-line no-param-reassign
included: { cardMemberships, cardLabels, tasks, attachments },
} = yield call(request, api.getCard, card.id));
} catch (error) {
fetch = false;
}
}
yield put(actions.handleCardUpdate(card, fetch, cardMemberships, cardLabels, tasks, attachments));
} }
export function* moveCard(id, listId, index = 0) { export function* moveCard(id, listId, index = 0) {
+8 -11
View File
@@ -55,17 +55,14 @@ export const selectMembershipsForCurrentBoard = createSelector(
return boardModel; return boardModel;
} }
return boardModel return boardModel.getOrderedMembershipsModelArray().map((boardMembershipModel) => ({
.getOrderedMembershipsQuerySet() ...boardMembershipModel.ref,
.toModelArray() isPersisted: !isLocalId(boardMembershipModel.id),
.map((boardMembershipModel) => ({ user: {
...boardMembershipModel.ref, ...boardMembershipModel.user.ref,
isPersisted: !isLocalId(boardMembershipModel.id), isCurrent: boardMembershipModel.user.id === currentUserId,
user: { },
...boardMembershipModel.user.ref, }));
isCurrent: boardMembershipModel.user.id === currentUserId,
},
}));
}, },
); );
+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.1", "version": "1.20.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "planka", "name": "planka",
"version": "1.19.1", "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.1", "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();
const authorizationUrlParams = {
scope: sails.config.custom.oidcScopes,
};
if (!sails.config.custom.oidcUseDefaultResponseMode) {
authorizationUrlParams.response_mode = sails.config.custom.oidcResponseMode;
}
oidc = { oidc = {
authorizationUrl: oidcClient.authorizationUrl({ authorizationUrl: oidcClient.authorizationUrl(authorizationUrlParams),
scope: sails.config.custom.oidcScopes,
response_mode: 'fragment',
}),
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: {
+9
View File
@@ -204,6 +204,15 @@ module.exports = {
), ),
); );
sails.sockets.broadcast(
`board:${inputs.record.boardId}`,
'cardDelete', // TODO: introduce separate event
{
item: inputs.record,
},
inputs.request,
);
sails.sockets.broadcast(`board:${card.boardId}`, 'cardUpdate', { sails.sockets.broadcast(`board:${card.boardId}`, 'cardUpdate', {
item: card, item: card,
}); });
@@ -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';
} }
+14 -10
View File
@@ -116,18 +116,22 @@ async function sendWebhook(webhook, event, data, user) {
user: sails.helpers.utils.jsonifyRecord(user), user: sails.helpers.utils.jsonifyRecord(user),
}); });
const response = await fetch(webhook.url, { try {
headers, const response = await fetch(webhook.url, {
body, headers,
method: 'POST', body,
}); method: 'POST',
});
if (!response.ok) { if (!response.ok) {
const message = await response.text(); const message = await response.text();
sails.log.error( sails.log.error(
`Webhook ${webhook.url} failed with status ${response.status} and message: ${message}`, `Webhook ${webhook.url} failed with status ${response.status} and message: ${message}`,
); );
}
} catch (e) {
sails.log.error(`Webhook ${webhook.url} failed with error message: ${e.message}`);
} }
} }

Some files were not shown because too many files have changed in this diff Show More