diff --git a/charts/planka/Chart.yaml b/charts/planka/Chart.yaml index 04e8140..1d733e7 100644 --- a/charts/planka/Chart.yaml +++ b/charts/planka/Chart.yaml @@ -15,13 +15,13 @@ type: application # 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. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.18 +version: 0.1.21 # 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 # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.15.7" +appVersion: "1.16.1" dependencies: - alias: postgresql diff --git a/charts/planka/README.md b/charts/planka/README.md index ed305b0..f9250e1 100644 --- a/charts/planka/README.md +++ b/charts/planka/README.md @@ -46,7 +46,7 @@ helm install planka . --set secretkey=$SECRETKEY \ --set admin_email="demo@demo.demo" \ --set admin_password="demo" \ --set admin_name="Demo Demo" \ ---set admin_username="demo" +--set admin_username="demo" \ --set ingress.enabled=true \ --set ingress.hosts[0].host=planka.example.dev \ @@ -55,7 +55,7 @@ helm install planka . --set secretkey=$SECRETKEY \ --set admin_email="demo@demo.demo" \ --set admin_password="demo" \ --set admin_name="Demo Demo" \ ---set admin_username="demo" +--set admin_username="demo" \ --set ingress.enabled=true \ --set ingress.hosts[0].host=planka.example.dev \ --set ingress.tls[0].secretName=planka-tls \ @@ -76,6 +76,7 @@ admin_name: "Demo Demo" admin_username: "demo" # Admin user +# Ingress ingress: enabled: true hosts: diff --git a/charts/planka/templates/hpa.yaml b/charts/planka/templates/hpa.yaml index 09e4c39..257a09d 100644 --- a/charts/planka/templates/hpa.yaml +++ b/charts/planka/templates/hpa.yaml @@ -1,5 +1,5 @@ {{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 +apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: {{ include "planka.fullname" . }} @@ -17,12 +17,16 @@ spec: - type: Resource resource: name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} {{- end }} {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - type: Resource resource: name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} {{- end }} {{- end }} diff --git a/client/.env b/client/.env index 9e7b08c..0718c00 100644 --- a/client/.env +++ b/client/.env @@ -1 +1 @@ -REACT_APP_VERSION=1.15.7 +REACT_APP_VERSION=1.16.1 diff --git a/client/src/components/BoardMembershipsStep/BoardMembershipsStep.module.scss b/client/src/components/BoardMembershipsStep/BoardMembershipsStep.module.scss index deec2a3..6d50f19 100644 --- a/client/src/components/BoardMembershipsStep/BoardMembershipsStep.module.scss +++ b/client/src/components/BoardMembershipsStep/BoardMembershipsStep.module.scss @@ -4,7 +4,6 @@ max-height: 60vh; overflow-x: hidden; overflow-y: auto; - scrollbar-width: thin; width: 100%; &::-webkit-scrollbar { diff --git a/client/src/components/Boards/Boards.module.scss b/client/src/components/Boards/Boards.module.scss index 848dbe5..e38de2d 100644 --- a/client/src/components/Boards/Boards.module.scss +++ b/client/src/components/Boards/Boards.module.scss @@ -85,7 +85,6 @@ height: 56px; overflow-x: auto; overflow-y: hidden; - scrollbar-width: thin; &:hover { height: 38px; diff --git a/client/src/components/Header/NotificationsStep.module.scss b/client/src/components/Header/NotificationsStep.module.scss index cdad65b..a86a0e9 100644 --- a/client/src/components/Header/NotificationsStep.module.scss +++ b/client/src/components/Header/NotificationsStep.module.scss @@ -49,7 +49,6 @@ max-height: 60vh; overflow-x: hidden; overflow-y: auto; - scrollbar-width: thin; &::-webkit-scrollbar { width: 5px; diff --git a/client/src/components/LabelsStep/LabelsStep.module.scss b/client/src/components/LabelsStep/LabelsStep.module.scss index c5ec023..1be274c 100644 --- a/client/src/components/LabelsStep/LabelsStep.module.scss +++ b/client/src/components/LabelsStep/LabelsStep.module.scss @@ -25,7 +25,6 @@ max-height: 60vh; overflow-x: hidden; overflow-y: auto; - scrollbar-width: thin; &::-webkit-scrollbar { width: 5px; diff --git a/client/src/components/List/List.module.scss b/client/src/components/List/List.module.scss index 7b47c89..b1a7096 100644 --- a/client/src/components/List/List.module.scss +++ b/client/src/components/List/List.module.scss @@ -43,7 +43,6 @@ max-height: calc(100vh - 268px); overflow-x: hidden; overflow-y: auto; - scrollbar-width: thin; width: 290px; &:hover { diff --git a/client/src/components/Memberships/AddStep/AddStep.module.scss b/client/src/components/Memberships/AddStep/AddStep.module.scss index 95be8d0..917142f 100644 --- a/client/src/components/Memberships/AddStep/AddStep.module.scss +++ b/client/src/components/Memberships/AddStep/AddStep.module.scss @@ -4,7 +4,6 @@ max-height: 60vh; overflow-x: hidden; overflow-y: auto; - scrollbar-width: thin; &::-webkit-scrollbar { width: 5px; diff --git a/client/src/locales/id/core.js b/client/src/locales/id/core.js new file mode 100644 index 0000000..b2de922 --- /dev/null +++ b/client/src/locales/id/core.js @@ -0,0 +1,241 @@ +import dateFns from 'date-fns/locale/id'; + +export default { + dateFns, + + format: { + date: 'dd MMM yyyy', + time: 'p', + dateTime: '$t(format:date) $t(format:time)', + longDate: 'd MMM', + longDateTime: "d MMMM 'pada' p", + fullDate: 'd MMM, y', + fullDateTime: "d MMM, y 'pada' p", + }, + + translation: { + common: { + aboutPlanka: 'Tentang Planka', + account: 'Akun', + actions: 'Tindakan', + addAttachment_title: 'Tambah Lampiran', + addComment: 'Tambahkan komentar', + addManager_title: 'Tambahkan Manager', + addMember_title: 'Tambahkan Anggota', + addUser_title: 'Tambahkan Pengguna', + administrator: 'Administrator', + all: 'Semua', + allChangesWillBeAutomaticallySavedAfterConnectionRestored: + 'Semua perubahan akan disimpan
setelah koneksi pulih.', + areYouSureYouWantToDeleteThisAttachment: 'Apakah anda ingin menghapus lampiran ini?', + areYouSureYouWantToDeleteThisBoard: 'Apakah anda ingin menghapus papan ini?', + areYouSureYouWantToDeleteThisCard: 'Apakah anda ingin menghapus kartu ini?', + areYouSureYouWantToDeleteThisComment: 'Apakah anda ingin menghapus komentar ini?', + areYouSureYouWantToDeleteThisLabel: 'Apakah anda ingin menghapus label ini?', + areYouSureYouWantToDeleteThisList: 'Apakah anda ingin menghapus daftar ini?', + areYouSureYouWantToDeleteThisProject: 'Apakah anda ingin menghapus proyek ini?', + areYouSureYouWantToDeleteThisTask: 'Apakah anda ingin menghapus tugas ini?', + areYouSureYouWantToDeleteThisUser: 'Apakah anda ingin menghapus pengguna ini?', + areYouSureYouWantToLeaveBoard: 'Apakah anda ingin keluar dari papan ini?', + areYouSureYouWantToLeaveProject: 'Apakah anda ingin keluar dari proyek ini?', + areYouSureYouWantToRemoveThisManagerFromProject: + 'Apakah anda ingin menghapus manajer ini dari papan ini?', + areYouSureYouWantToRemoveThisMemberFromBoard: + 'Apakah anda ingin menghapus anggota ini dari papan ini?', + attachment: 'Lampiran', + attachments: 'Lampiran-lampiran', + authentication: 'Autentikasi', + background: 'Latar belakang', + board: 'Papan', + boardNotFound_title: 'Papan Tidak Ditemukan', + canComment: 'Bisa berkomentar', + canEditContentOfBoard: 'Bisa mengubah isi papan.', + canOnlyViewBoard: 'Hanya dapat menglihat isi papan.', + cardActions_title: 'Aksi Kartu', + cardNotFound_title: 'Kartu Tidak Ditemukan', + cardOrActionAreDeleted: 'Kartu atau aksi telah dihapus.', + color: 'Warna', + createBoard_title: 'Buat Papan', + createLabel_title: 'Buat Label', + createNewOneOrSelectExistingOne: 'Create a new one or select
an existing one.', + createProject_title: 'Buat Proyek', + createTextFile_title: 'Buat Berkas Teks', + currentPassword: 'Kata sandi sekarang', + dangerZone_title: 'Zona Berbahaya', + date: 'Tanggal', + dueDate_title: 'Tenggat Waktu', + deleteAttachment_title: 'Hapus Lampiran', + deleteBoard_title: 'Hapus Papan', + deleteCard_title: 'Hapus Kartu', + deleteComment_title: 'Hapus Komentar', + deleteLabel_title: 'Hapus Label', + deleteList_title: 'Hapus Daftar', + deleteProject_title: 'Hapus Proyek', + deleteTask_title: 'Hapus Tugas', + deleteUser_title: 'Hapus Pengguna', + description: 'Deskripsi', + detectAutomatically: 'Deteksi otomatis', + dropFileToUpload: 'Tarik berkas untuk menggungah', + editor: 'Pengubah', + editAttachment_title: 'Ubah Lampiran', + editAvatar_title: 'Ubah Avatar', + editBoard_title: 'Ubah Papan', + editDueDate_title: 'Ubah Tenggat Waktu', + editEmail_title: 'Ubah E-mail', + editInformation_title: 'Ubah Informasi', + editLabel_title: 'Ubah Label', + editPassword_title: 'Ubah Kata Sandi', + editPermissions_title: 'Ubah Izin', + editStopwatch_title: 'Ubah Stopwatch', + editUsername_title: 'Ubah Username', + email: 'E-mail', + emailAlreadyInUse: 'E-mail telah digunakan', + enterCardTitle: 'Masukkan judul kartu... [Ctrl+Enter] untuk membuka otomatis.', + enterDescription: 'Masukkan deskripsi...', + enterFilename: 'Masukkan nama berkas...', + enterListTitle: 'Masukkan judul daftar...', + enterProjectTitle: 'Masukkan judul proyek', + enterTaskDescription: 'Masukkan deskripsi tugas...', + filterByLabels_title: 'Saring berdasarkan Label', + filterByMembers_title: 'Saring berdasarkan Anggota', + fromComputer_title: 'Dari Komputer', + fromTrello: 'Dari Trello', + general: 'Umum', + hours: 'Jam', + importBoard_title: 'Impor Papan', + invalidCurrentPassword: 'Kata sandi saat ini tidak valid', + labels: 'Label', + language: 'Bahasa', + leaveBoard_title: 'Keluar dari Papan', + leaveProject_title: 'Keluar dari Proyek', + list: 'Daftar', + listActions_title: 'Aksi Daftar', + managers: 'Manager', + members: 'Anggota', + minutes: 'Menit', + moveCard_title: 'Pindahkan Kartu', + name: 'Nama', + newEmail: 'E-mail baru', + newPassword: 'Kata sandi baru', + newUsername: 'Username baru', + noConnectionToServer: 'Tidak ada koneksi ke server', + noBoards: 'Tidak ada papan', + noLists: 'Tidak ada daftar', + noProjects: 'Tidak ada projek', + notifications: 'Notifikasi', + noUnreadNotifications: 'Tiada notifikasi yang belum dibaca.', + openBoard_title: 'Buka Papan', + optional_inline: 'opsional', + organization: 'Organisasi', + phone: 'Ponsel', + preferences: 'Preferensi', + pressPasteShortcutToAddAttachmentFromClipboard: + 'Tip: tekan Ctrl-V (Cmd-V di Mac) untuk menambahkan lampiran dari papan klip.', + project: 'Proyek', + projectNotFound_title: 'Proyek Tidak Ditemukan', + removeManager_title: 'Hapus Manager', + removeMember_title: 'Hapus Anggota', + searchLabels: 'Cari label...', + searchMembers: 'Cari anggota...', + searchUsers: 'Cari pengguna...', + seconds: 'Detik', + selectBoard: 'Pilih papan', + selectList: 'Pilih daftar', + selectPermissions_title: 'Pilih Izin', + selectProject: 'Pilih proyek', + settings: 'Setelan', + stopwatch: 'Stopwatch', + subscribeToMyOwnCardsByDefault: 'Berlangganan kartu saya sendiri secara default', + taskActions_title: 'Aksi Tugas', + tasks: 'Tugas', + thereIsNoPreviewAvailableForThisAttachment: + 'Tidak ada pratinjau yang tersedia untuk lampiran ini.', + time: 'Waktu', + title: 'Judul', + userActions_title: 'Aksi Pengguna', + userAddedThisCardToList: '<0>{{user}}<1> menambahkan kartu ini ke {{list}}', + userLeftNewCommentToCard: '{{user}} mengomentari «{{comment}}» di <2>{{card}}', + userMovedCardFromListToList: + '{{user}} memindahkan <2>{{card}} dari {{fromList}} ke {{toList}}', + userMovedThisCardFromListToList: + '<0>{{user}}<1> memindahkan kartu ini dari {{fromList}} ke {{toList}}', + username: 'Username', + usernameAlreadyInUse: 'Username telah digunakan', + users: 'Pengguna', + version: 'Versi', + viewer: 'Penglihat', + writeComment: 'Tuliskan komentar...', + }, + + action: { + addAnotherCard: 'Tambahkan kartu lain', + addAnotherList: 'Tambahkan daftar lain', + addAnotherTask: 'Tambahkan aksi lain', + addCard: 'Tambah kartu', + addCard_title: 'Tambah Kartu', + addComment: 'Tambah komentar', + addList: 'Tambah daftar', + addMember: 'Tambah anggota', + addMoreDetailedDescription: 'Tambahkan deskripsi yang lebih detail', + addTask: 'Tambah tugas', + addToCard: 'Tambahkan ke kartu', + addUser: 'Tambah pengguna', + createBoard: 'Tambah papan', + createFile: 'Tambah berkas', + createLabel: 'Tambah label', + createNewLabel: 'Tambah label baru', + createProject: 'Tambah proyek', + delete: 'Hapus', + deleteAttachment: 'Hapus lampiran', + deleteAvatar: 'Hapus avatar', + deleteBoard: 'Hapus papan', + deleteCard: 'Hapus kartu', + deleteCard_title: 'Hapus Kartu', + deleteComment: 'Hapus komentar', + deleteImage: 'Hapus gambar', + deleteLabel: 'Hapus labek', + deleteList: 'Hapus daftar', + deleteList_title: 'Hapus Daftar', + deleteProject: 'Hapus proyek', + deleteProject_title: 'Hapus Proyek', + deleteTask: 'Hapus tugas', + deleteTask_title: 'Hapus Tugas', + deleteUser: 'Hapus pengguna', + edit: 'Ubah', + editDueDate_title: 'Ubah Tenggat Waktu', + editDescription_title: 'Ubdah Deskripsi', + editEmail_title: 'Ubah E-mail', + editInformation_title: 'Ubah Informasi', + editPassword_title: 'Ubah Kata Sandi', + editPermissions: 'Ubah izin', + editStopwatch_title: 'Ubah Stopwatch', + editTitle_title: 'Ubah Judul', + editUsername_title: 'Ubah Username', + hideDetails: 'Sembunyikan detail', + import: 'Impor', + leaveBoard: 'Keluar dari papan', + leaveProject: 'Keluar dari proyek', + logOut_title: 'Keluar', + makeCover_title: 'Buat Cover', + move: 'Pindah', + moveCard_title: 'Pindahkan Kartu', + remove: 'Hapus', + removeBackground: 'Hapus latar belakang', + removeCover_title: 'Hapus Cover', + removeFromBoard: 'Hapus dari papan', + removeFromProject: 'Hapus dari proyek', + removeManager: 'Hapus manager', + removeMember: 'Hapus papan', + save: 'Simpan', + showAllAttachments: 'Tampilkan semua lampiran ({{hidden}} tersembunyi)', + showDetails: 'Tampilkan detail', + showFewerAttachments: 'Tampilkan lampiran lebih sedikit', + start: 'Mulai', + stop: 'Berhenti', + subscribe: 'Berlanggan', + unsubscribe: 'Berhenti berlangganan', + uploadNewAvatar: 'Unggah avatar baru', + uploadNewImage: 'Unggah gambar baru', + }, + }, +}; diff --git a/client/src/locales/id/index.js b/client/src/locales/id/index.js new file mode 100644 index 0000000..42801f3 --- /dev/null +++ b/client/src/locales/id/index.js @@ -0,0 +1,8 @@ +import login from './login'; + +export default { + language: 'id', + country: 'id', + name: 'Bahasa Indonesia', + embeddedLocale: login, +}; diff --git a/client/src/locales/id/login.js b/client/src/locales/id/login.js new file mode 100644 index 0000000..2660d45 --- /dev/null +++ b/client/src/locales/id/login.js @@ -0,0 +1,22 @@ +export default { + translation: { + common: { + emailOrUsername: 'E-mail atau username', + invalidEmailOrUsername: 'E-mail atau username salah', + invalidPassword: 'Kata sandi salah', + logInToPlanka: 'Masuk ke Planka', + noInternetConnection: 'Tidak ada koneksi internet', + pageNotFound_title: 'Halaman Tidak Ditemukan', + password: 'Kata sandi', + projectManagement: 'Manajemen projek', + serverConnectionFailed: 'Koneksi server gagal', + unknownError: 'Kesalahan tidak diketahui, coba lagi nanti.', + useSingleSignOn: 'Gunakan single sign-on', + }, + + action: { + logIn: 'Masuk', + logInWithSSO: 'Masuk dengan SSO', + }, + }, +}; diff --git a/client/src/locales/index.js b/client/src/locales/index.js index edd1392..307662e 100644 --- a/client/src/locales/index.js +++ b/client/src/locales/index.js @@ -4,10 +4,13 @@ import de from './de'; import en from './en'; import es from './es'; import fr from './fr'; +import id from './id'; import it from './it'; import ja from './ja'; import ko from './ko'; +import nl from './nl'; import pl from './pl'; +import pt from './pt'; import ro from './ro'; import ru from './ru'; import sk from './sk'; @@ -17,7 +20,29 @@ import ua from './ua'; import uz from './uz'; import zh from './zh'; -const locales = [cs, da, de, en, es, fr, it, ja, ko, pl, ro, ru, sk, sv, tr, ua, uz, zh]; +const locales = [ + cs, + da, + de, + en, + es, + fr, + id, + it, + ja, + ko, + nl, + pl, + pt, + ro, + ru, + sk, + sv, + tr, + ua, + uz, + zh, +]; export default locales; diff --git a/client/src/locales/nl/core.js b/client/src/locales/nl/core.js new file mode 100644 index 0000000..a012193 --- /dev/null +++ b/client/src/locales/nl/core.js @@ -0,0 +1,242 @@ +import dateFns from 'date-fns/locale/nl'; + +export default { + dateFns, + + format: { + date: 'd-M-yyyy', + time: 'p', + dateTime: '$t(format:date) $t(format:time)', + longDate: 'd MMM', + longDateTime: "d MMMM 'om' p", + fullDate: 'd MMM y', + fullDateTime: "d MMMM y 'om' p", + }, + + translation: { + common: { + aboutPlanka: 'Over Planka', + account: 'Account', + actions: 'Acties', + addAttachment_title: 'Bijlage toevoegen', + addComment: 'Opmerking toevoegen', + addManager_title: 'Manager toevoegen', + addMember_title: 'Lid toevoegen', + addUser_title: 'Gebruiker toevoegen', + administrator: 'Beheerder', + all: 'Alle', + allChangesWillBeAutomaticallySavedAfterConnectionRestored: + 'Alle wijzigingen worden automatisch opgeslagen
nadat de verbinding is hersteld.', + areYouSureYouWantToDeleteThisAttachment: 'Weet u zeker dat u deze bijlage wilt verwijderen?', + areYouSureYouWantToDeleteThisBoard: 'Weet u zeker dat u dit bord wilt verwijderen?', + areYouSureYouWantToDeleteThisCard: 'Weet u zeker dat u deze kaart wilt verwijderen?', + areYouSureYouWantToDeleteThisComment: 'Weet u zeker dat u deze opmerking wilt verwijderen?', + areYouSureYouWantToDeleteThisLabel: 'Weet u zeker dat u dit label wilt verwijderen?', + areYouSureYouWantToDeleteThisList: 'Weet u zeker dat u deze lijst wilt verwijderen?', + areYouSureYouWantToDeleteThisProject: 'Weet u zeker dat u dit project wilt verwijderen?', + areYouSureYouWantToDeleteThisTask: 'Weet u zeker dat u deze taak wilt verwijderen?', + areYouSureYouWantToDeleteThisUser: 'Weet u zeker dat u deze gebruiker wilt verwijderen?', + areYouSureYouWantToLeaveBoard: 'Weet u zeker dat u het bord wilt verlaten?', + areYouSureYouWantToLeaveProject: 'Weet u zeker dat u het project wilt verlaten?', + areYouSureYouWantToRemoveThisManagerFromProject: + 'Weet u zeker dat u deze manager uit het project wilt verwijderen?', + areYouSureYouWantToRemoveThisMemberFromBoard: + 'Weet u zeker dat u dit lid uit het bord wilt verwijderen?', + attachment: 'Bijlage', + attachments: 'Bijlagen', + authentication: 'Authenticatie', + background: 'Achtergrond', + board: 'Bord', + boardNotFound_title: 'Bord niet gevonden', + canComment: 'Kan opmerking plaatsen', + canEditContentOfBoard: 'Kan de inhoud van het bord bewerken.', + canOnlyViewBoard: 'Kan alleen het bord bekijken.', + cardActions_title: 'Kaartacties', + cardNotFound_title: 'Kaart niet gevonden', + cardOrActionAreDeleted: 'Kaart of actie zijn verwijderd.', + color: 'Kleur', + createBoard_title: 'Bord aanmaken', + createLabel_title: 'Label aanmaken', + createNewOneOrSelectExistingOne: 'Maak een nieuwe of selecteer een bestaande.', + createProject_title: 'Project aanmaken', + createTextFile_title: 'Tekstbestand aanmaken', + currentPassword: 'Huidig wachtwoord', + dangerZone_title: 'Gevaarlijke zone', + date: 'Datum', + dueDate_title: 'Vervaldatum', + deleteAttachment_title: 'Bijlage verwijderen', + deleteBoard_title: 'Bord verwijderen', + deleteCard_title: 'Kaart verwijderen', + deleteComment_title: 'Opmerking verwijderen', + deleteLabel_title: 'Label verwijderen', + deleteList_title: 'Lijst verwijderen', + deleteProject_title: 'Project verwijderen', + deleteTask_title: 'Taak verwijderen', + deleteUser_title: 'Gebruiker verwijderen', + description: 'Beschrijving', + detectAutomatically: 'Automatisch detecteren', + dropFileToUpload: 'Sleep bestand om te uploaden', + editor: 'Editor', + editAttachment_title: 'Bijlage bewerken', + editAvatar_title: 'Avatar bewerken', + editBoard_title: 'Bord bewerken', + editDueDate_title: 'Vervaldatum bewerken', + editEmail_title: 'E-mail bewerken', + editInformation_title: 'Informatie bewerken', + editLabel_title: 'Label bewerken', + editPassword_title: 'Wachtwoord bewerken', + editPermissions_title: 'Machtigingen bewerken', + editStopwatch_title: 'Stopwatch bewerken', + editUsername_title: 'Gebruikersnaam bewerken', + email: 'E-mail', + emailAlreadyInUse: 'E-mail is al in gebruik', + enterCardTitle: 'Voer kaarttitel in... [Ctrl+Enter] om automatisch te openen.', + enterDescription: 'Beschrijving invoeren...', + enterFilename: 'Bestandsnaam invoeren', + enterListTitle: 'Voer lijsttitel in...', + enterProjectTitle: 'Voer projecttitel in', + enterTaskDescription: 'Taakbeschrijving invoeren...', + filterByLabels_title: 'Filteren op labels', + filterByMembers_title: 'Filteren op leden', + fromComputer_title: 'Van computer', + fromTrello: 'Van Trello', + general: 'Algemeen', + hours: 'Uren', + importBoard_title: 'Bord importeren', + invalidCurrentPassword: 'Ongeldig huidig wachtwoord', + labels: 'Labels', + language: 'Taal', + leaveBoard_title: 'Bord verlaten', + leaveProject_title: 'Project verlaten', + list: 'Lijst', + listActions_title: 'Lijstacties', + managers: 'Managers', + members: 'Leden', + minutes: 'Minuten', + moveCard_title: 'Kaart verplaatsen', + name: 'Naam', + newEmail: 'Nieuwe e-mail', + newPassword: 'Nieuw wachtwoord', + newUsername: 'Nieuwe gebruikersnaam', + noConnectionToServer: 'Geen verbinding met server', + noBoards: 'Geen borden', + noLists: 'Geen lijsten', + noProjects: 'Geen projecten', + notifications: 'Meldingen', + noUnreadNotifications: 'Geen ongelezen meldingen.', + openBoard_title: 'Bord openen', + optional_inline: 'optioneel', + organization: 'Organisatie', + phone: 'Telefoon', + preferences: 'Voorkeuren', + pressPasteShortcutToAddAttachmentFromClipboard: + 'Tip: druk op Ctrl-V (Cmd-V op Mac) om een bijlage van het klembord toe te voegen.', + project: 'Project', + projectNotFound_title: 'Project niet gevonden', + removeManager_title: 'Manager verwijderen', + removeMember_title: 'Lid verwijderen', + searchLabels: 'Labels zoeken...', + searchMembers: 'Leden zoeken...', + searchUsers: 'Gebruikers zoeken...', + seconds: 'Seconden', + selectBoard: 'Bord selecteren', + selectList: 'Lijst selecteren', + selectPermissions_title: 'Machtigingen selecteren', + selectProject: 'Project selecteren', + settings: 'Instellingen', + stopwatch: 'Stopwatch', + subscribeToMyOwnCardsByDefault: 'Standaard abonneren op mijn eigen kaarten', + taskActions_title: 'Takenacties', + tasks: 'Taken', + thereIsNoPreviewAvailableForThisAttachment: + 'Er is geen voorbeeld beschikbaar voor deze bijlage.', + time: 'Tijd', + title: 'Titel', + userActions_title: 'Gebruikersacties', + userAddedThisCardToList: '<0>{{user}}<1> heeft deze kaart toegevoegd aan {{list}}', + userLeftNewCommentToCard: + '{{user}} heeft een nieuwe opmerking achtergelaten «{{comment}}» bij <2>{{card}}', + userMovedCardFromListToList: + '{{user}} heeft <2>{{card}} verplaatst van {{fromList}} naar {{toList}}', + userMovedThisCardFromListToList: + '<0>{{user}}<1> heeft deze kaart verplaatst van {{fromList}} naar {{toList}}', + username: 'Gebruikersnaam', + usernameAlreadyInUse: 'Gebruikersnaam is al in gebruik', + users: 'Gebruikers', + version: 'Versie', + viewer: 'Kijker', + writeComment: 'Schrijf een opmerking...', + }, + + action: { + addAnotherCard: 'Voeg nog een kaart toe', + addAnotherList: 'Voeg nog een lijst toe', + addAnotherTask: 'Voeg nog een taak toe', + addCard: 'Kaart toevoegen', + addCard_title: 'Kaart toevoegen', + addComment: 'Opmerking toevoegen', + addList: 'Lijst toevoegen', + addMember: 'Lid toevoegen', + addMoreDetailedDescription: 'Meer gedetailleerde beschrijving toevoegen', + addTask: 'Taak toevoegen', + addToCard: 'Toevoegen aan kaart', + addUser: 'Gebruiker toevoegen', + createBoard: 'Bord aanmaken', + createFile: 'Bestand aanmaken', + createLabel: 'Label aanmaken', + createNewLabel: 'Nieuw label aanmaken', + createProject: 'Project aanmaken', + delete: 'Verwijderen', + deleteAttachment: 'Bijlage verwijderen', + deleteAvatar: 'Avatar verwijderen', + deleteBoard: 'Bord verwijderen', + deleteCard: 'Kaart verwijderen', + deleteCard_title: 'Kaart verwijderen', + deleteComment: 'Opmerking verwijderen', + deleteImage: 'Afbeelding verwijderen', + deleteLabel: 'Label verwijderen', + deleteList: 'Lijst verwijderen', + deleteList_title: 'Lijst verwijderen', + deleteProject: 'Project verwijderen', + deleteProject_title: 'Project verwijderen', + deleteTask: 'Taak verwijderen', + deleteTask_title: 'Taak verwijderen', + deleteUser: 'Gebruiker verwijderen', + edit: 'Bewerken', + editDueDate_title: 'Vervaldatum bewerken', + editDescription_title: 'Beschrijving bewerken', + editEmail_title: 'E-mail bewerken', + editInformation_title: 'Informatie bewerken', + editPassword_title: 'Wachtwoord bewerken', + editPermissions: 'Machtigingen bewerken', + editStopwatch_title: 'Stopwatch bewerken', + editTitle_title: 'Titel bewerken', + editUsername_title: 'Gebruikersnaam bewerken', + hideDetails: 'Details verbergen', + import: 'Importeren', + leaveBoard: 'Bord verlaten', + leaveProject: 'Project verlaten', + logOut_title: 'Uitloggen', + makeCover_title: 'Omslag maken', + move: 'Verplaatsen', + moveCard_title: 'Kaart verplaatsen', + remove: 'Verwijderen', + removeBackground: 'Achtergrond verwijderen', + removeCover_title: 'Omslag verwijderen', + removeFromBoard: 'Verwijderen van bord', + removeFromProject: 'Verwijderen van project', + removeManager: 'Manager verwijderen', + removeMember: 'Lid verwijderen', + save: 'Opslaan', + showAllAttachments: 'Alle bijlagen weergeven ({{hidden}} verbergen)', + showDetails: 'Details weergeven', + showFewerAttachments: 'Minder bijlagen weergeven', + start: 'Start', + stop: 'Stop', + subscribe: 'Abonneren', + unsubscribe: 'Afmelden', + uploadNewAvatar: 'Nieuwe avatar uploaden', + uploadNewImage: 'Nieuwe afbeelding uploaden', + }, + }, +}; diff --git a/client/src/locales/nl/index.js b/client/src/locales/nl/index.js new file mode 100644 index 0000000..6e24c66 --- /dev/null +++ b/client/src/locales/nl/index.js @@ -0,0 +1,8 @@ +import login from './login'; + +export default { + language: 'nl', + country: 'nl', + name: 'Nederlands', + embeddedLocale: login, +}; diff --git a/client/src/locales/nl/login.js b/client/src/locales/nl/login.js new file mode 100644 index 0000000..43ac4b1 --- /dev/null +++ b/client/src/locales/nl/login.js @@ -0,0 +1,22 @@ +export default { + translation: { + common: { + emailOrUsername: 'E-mail of gebruikersnaam', + invalidEmailOrUsername: 'Ongeldig e-mailadres of gebruikersnaam', + invalidPassword: 'Ongeldig wachtwoord', + logInToPlanka: 'Inloggen bij Planka', + noInternetConnection: 'Geen internetverbinding', + pageNotFound_title: 'Pagina niet gevonden', + password: 'Wachtwoord', + projectManagement: 'Projectbeheer', + serverConnectionFailed: 'Verbinding met de server mislukt', + unknownError: 'Onbekende fout, probeer het later opnieuw', + useSingleSignOn: 'Gebruik single sign-on', + }, + + action: { + logIn: 'Inloggen', + logInWithSSO: 'Inloggen met SSO', + }, + }, +}; diff --git a/client/src/locales/pt/core.js b/client/src/locales/pt/core.js new file mode 100644 index 0000000..3d557e8 --- /dev/null +++ b/client/src/locales/pt/core.js @@ -0,0 +1,241 @@ +import dateFns from 'date-fns/locale/pt-BR'; + +export default { + dateFns, + + format: { + date: 'dd/MM/yyyy', + time: 'p', + dateTime: '$t(format:date) $t(format:time)', + longDate: 'd MMM', + longDateTime: "d 'de' MMMM 'às' p", + fullDate: 'd MMM, y', + fullDateTime: "d 'de' MMMM, y 'às' p", + }, + + translation: { + common: { + aboutPlanka: 'Sobre o Planka', + account: 'Conta', + actions: 'Ações', + addAttachment_title: 'Adicionar Anexo', + addComment: 'Adicionar comentário', + addManager_title: 'Adicionar Gerente', + addMember_title: 'Adicionar Membro', + addUser_title: 'Adicionar Usuário', + administrator: 'Administrador', + all: 'Todos', + allChangesWillBeAutomaticallySavedAfterConnectionRestored: + 'Todas as alterações serão salvas automaticamente
após a conexão ser restaurada.', + areYouSureYouWantToDeleteThisAttachment: 'Tem certeza de que deseja excluir este anexo?', + areYouSureYouWantToDeleteThisBoard: 'Tem certeza de que deseja excluir este quadro?', + areYouSureYouWantToDeleteThisCard: 'Tem certeza de que deseja excluir este cartão?', + areYouSureYouWantToDeleteThisComment: 'Tem certeza de que deseja excluir este comentário?', + areYouSureYouWantToDeleteThisLabel: 'Tem certeza de que deseja excluir este rótulo?', + areYouSureYouWantToDeleteThisList: 'Tem certeza de que deseja excluir esta lista?', + areYouSureYouWantToDeleteThisProject: 'Tem certeza de que deseja excluir este projeto?', + areYouSureYouWantToDeleteThisTask: 'Tem certeza de que deseja excluir esta tarefa?', + areYouSureYouWantToDeleteThisUser: 'Tem certeza de que deseja excluir este usuário?', + areYouSureYouWantToLeaveBoard: 'Tem certeza de que deseja sair do quadro?', + areYouSureYouWantToLeaveProject: 'Tem certeza de que deseja sair do projeto?', + areYouSureYouWantToRemoveThisManagerFromProject: + 'Tem certeza de que deseja remover este gerente do projeto?', + areYouSureYouWantToRemoveThisMemberFromBoard: + 'Tem certeza de que deseja remover este membro do quadro?', + attachment: 'Anexo', + attachments: 'Anexos', + authentication: 'Autenticação', + background: 'Fundo', + board: 'Quadro', + boardNotFound_title: 'Quadro não encontrado', + canComment: 'Pode comentar', + canEditContentOfBoard: 'Pode editar o conteúdo do quadro.', + canOnlyViewBoard: 'Só pode visualizar o quadro.', + cardActions_title: 'Ações do Cartão', + cardNotFound_title: 'Cartão não encontrado', + cardOrActionAreDeleted: 'Cartão ou ação foram excluídos.', + color: 'Cor', + createBoard_title: 'Criar Quadro', + createLabel_title: 'Criar Rótulo', + createNewOneOrSelectExistingOne: 'Criar um novo ou selecionar
um existente.', + createProject_title: 'Criar Projeto', + createTextFile_title: 'Criar Arquivo de Texto', + currentPassword: 'Senha atual', + dangerZone_title: 'Zona de Perigo', + date: 'Data', + dueDate_title: 'Data de Vencimento', + deleteAttachment_title: 'Excluir Anexo', + deleteBoard_title: 'Excluir Quadro', + deleteCard_title: 'Excluir Cartão', + deleteComment_title: 'Excluir Comentário', + deleteLabel_title: 'Excluir Rótulo', + deleteList_title: 'Excluir Lista', + deleteProject_title: 'Excluir Projeto', + deleteTask_title: 'Excluir Tarefa', + deleteUser_title: 'Excluir Usuário', + description: 'Descrição', + detectAutomatically: 'Detectar automaticamente', + dropFileToUpload: 'Solte o arquivo para enviar', + editor: 'Editor', + editAttachment_title: 'Editar Anexo', + editAvatar_title: 'Editar Avatar', + editBoard_title: 'Editar Quadro', + editDueDate_title: 'Editar Data de Vencimento', + editEmail_title: 'Editar E-mail', + editInformation_title: 'Editar Informações', + editLabel_title: 'Editar Rótulo', + editPassword_title: 'Editar Senha', + editPermissions_title: 'Editar Permissões', + editStopwatch_title: 'Editar Cronômetro', + editUsername_title: 'Editar Nome de Usuário', + email: 'E-mail', + emailAlreadyInUse: 'E-mail já está em uso', + enterCardTitle: 'Digite o título do cartão... [Ctrl+Enter] para abrir automaticamente.', + enterDescription: 'Digite a descrição...', + enterFilename: 'Digite o nome do arquivo', + enterListTitle: 'Digite o título da lista...', + enterProjectTitle: 'Digite o título do projeto', + enterTaskDescription: 'Digite a descrição da tarefa...', + filterByLabels_title: 'Filtrar por Rótulos', + filterByMembers_title: 'Filtrar por Membros', + fromComputer_title: 'Do Computador', + fromTrello: 'Do Trello', + general: 'Geral', + hours: 'Horas', + importBoard_title: 'Importar Quadro', + invalidCurrentPassword: 'Senha atual inválida', + labels: 'Rótulos', + language: 'Idioma', + leaveBoard_title: 'Sair do Quadro', + leaveProject_title: 'Sair do Projeto', + list: 'Lista', + listActions_title: 'Ações da Lista', + managers: 'Gerentes', + members: 'Membros', + minutes: 'Minutos', + moveCard_title: 'Mover Cartão', + name: 'Nome', + newEmail: 'Novo e-mail', + newPassword: 'Nova senha', + newUsername: 'Novo nome de usuário', + noConnectionToServer: 'Sem conexão com o servidor', + noBoards: 'Sem quadros', + noLists: 'Sem listas', + noProjects: 'Sem projetos', + notifications: 'Notificações', + noUnreadNotifications: 'Nenhuma notificação não lida.', + openBoard_title: 'Abrir Quadro', + optional_inline: 'opcional', + organization: 'Organização', + phone: 'Telefone', + preferences: 'Preferências', + pressPasteShortcutToAddAttachmentFromClipboard: + 'Dica: pressione Ctrl-V (Cmd-V no Mac) para adicionar um anexo da área de transferência.', + project: 'Projeto', + projectNotFound_title: 'Projeto não encontrado', + removeManager_title: 'Remover Gerente', + removeMember_title: 'Remover Membro', + searchLabels: 'Pesquisar rótulos...', + searchMembers: 'Pesquisar membros...', + searchUsers: 'Pesquisar usuários...', + seconds: 'Segundos', + selectBoard: 'Selecionar quadro', + selectList: 'Selecionar lista', + selectPermissions_title: 'Selecionar Permissões', + selectProject: 'Selecionar projeto', + settings: 'Configurações', + stopwatch: 'Cronômetro', + subscribeToMyOwnCardsByDefault: 'Inscrever-se automaticamente em meus próprios cartões', + taskActions_title: 'Ações da Tarefa', + tasks: 'Tarefas', + thereIsNoPreviewAvailableForThisAttachment: + 'Não há pré-visualização disponível para este anexo.', + time: 'Tempo', + title: 'Título', + userActions_title: 'Ações do Usuário', + userAddedThisCardToList: '<0>{{user}}<1> adicionou este cartão a {{list}}', + userLeftNewCommentToCard: + '{{user}} deixou um novo comentário «{{comment}}» em <2>{{card}}', + userMovedCardFromListToList: '{{user}} moveu <2>{{card}} de {{fromList}} para {{toList}}', + userMovedThisCardFromListToList: + '<0>{{user}}<1> moveu este cartão de {{fromList}} para {{toList}}', + username: 'Nome de usuário', + usernameAlreadyInUse: 'Nome de usuário já está em uso', + users: 'Usuários', + version: 'Versão', + viewer: 'Visualizador', + writeComment: 'Escreva um comentário...', + }, + + action: { + addAnotherCard: 'Adicionar outro cartão', + addAnotherList: 'Adicionar outra lista', + addAnotherTask: 'Adicionar outra tarefa', + addCard: 'Adicionar cartão', + addCard_title: 'Adicionar Cartão', + addComment: 'Adicionar comentário', + addList: 'Adicionar lista', + addMember: 'Adicionar membro', + addMoreDetailedDescription: 'Adicionar descrição mais detalhada', + addTask: 'Adicionar tarefa', + addToCard: 'Adicionar ao cartão', + addUser: 'Adicionar usuário', + createBoard: 'Criar quadro', + createFile: 'Criar arquivo', + createLabel: 'Criar rótulo', + createNewLabel: 'Criar novo rótulo', + createProject: 'Criar projeto', + delete: 'Excluir', + deleteAttachment: 'Excluir anexo', + deleteAvatar: 'Excluir avatar', + deleteBoard: 'Excluir quadro', + deleteCard: 'Excluir cartão', + deleteCard_title: 'Excluir Cartão', + deleteComment: 'Excluir comentário', + deleteImage: 'Excluir imagem', + deleteLabel: 'Excluir rótulo', + deleteList: 'Excluir lista', + deleteList_title: 'Excluir Lista', + deleteProject: 'Excluir projeto', + deleteProject_title: 'Excluir Projeto', + deleteTask: 'Excluir tarefa', + deleteTask_title: 'Excluir Tarefa', + deleteUser: 'Excluir usuário', + edit: 'Editar', + editDueDate_title: 'Editar Data de Vencimento', + editDescription_title: 'Editar Descrição', + editEmail_title: 'Editar E-mail', + editInformation_title: 'Editar Informações', + editPassword_title: 'Editar Senha', + editPermissions: 'Editar permissões', + editStopwatch_title: 'Editar Cronômetro', + editTitle_title: 'Editar Título', + editUsername_title: 'Editar Nome de Usuário', + hideDetails: 'Ocultar detalhes', + import: 'Importar', + leaveBoard: 'Sair do quadro', + leaveProject: 'Sair do projeto', + logOut_title: 'Sair', + makeCover_title: 'Tornar Capa', + move: 'Mover', + moveCard_title: 'Mover Cartão', + remove: 'Remover', + removeBackground: 'Remover fundo', + removeCover_title: 'Remover Capa', + removeFromBoard: 'Remover do quadro', + removeFromProject: 'Remover do projeto', + removeManager: 'Remover gerente', + removeMember: 'Remover membro', + save: 'Salvar', + showAllAttachments: 'Mostrar todos os anexos ({{hidden}} ocultos)', + showDetails: 'Mostrar detalhes', + showFewerAttachments: 'Mostrar menos anexos', + start: 'Iniciar', + stop: 'Parar', + subscribe: 'Inscrever-se', + unsubscribe: 'Cancelar inscrição', + uploadNewAvatar: 'Enviar novo avatar', + uploadNewImage: 'Enviar nova imagem', + }, + }, +}; diff --git a/client/src/locales/pt/index.js b/client/src/locales/pt/index.js new file mode 100644 index 0000000..b3c867f --- /dev/null +++ b/client/src/locales/pt/index.js @@ -0,0 +1,8 @@ +import login from './login'; + +export default { + language: 'pt', + country: 'br', + name: 'Português', + embeddedLocale: login, +}; diff --git a/client/src/locales/pt/login.js b/client/src/locales/pt/login.js new file mode 100644 index 0000000..07a4489 --- /dev/null +++ b/client/src/locales/pt/login.js @@ -0,0 +1,22 @@ +export default { + translation: { + common: { + emailOrUsername: 'E-mail ou nome de usuário', + invalidEmailOrUsername: 'E-mail ou nome de usuário inválido', + invalidPassword: 'Senha inválida', + logInToPlanka: 'Entrar no Planka', + noInternetConnection: 'Sem conexão com a internet', + pageNotFound_title: 'Página não encontrada', + password: 'Senha', + projectManagement: 'Gerenciamento de projetos', + serverConnectionFailed: 'Falha na conexão com o servidor', + unknownError: 'Erro desconhecido, tente novamente mais tarde', + useSingleSignOn: 'Usar login único', + }, + + action: { + logIn: 'Entrar', + logInWithSSO: 'Entrar com SSO', + }, + }, +}; diff --git a/client/src/locales/zh/core.js b/client/src/locales/zh/core.js index 486cfcd..95ae46c 100644 --- a/client/src/locales/zh/core.js +++ b/client/src/locales/zh/core.js @@ -67,7 +67,7 @@ export default { deleteTask_title: '删除任务', deleteUser_title: '删除用户', description: '描述', - detectAutomatically: '自动删除', + detectAutomatically: '自动检测', dropFileToUpload: '拖放文件以上传', editor: '编辑器', editAttachment_title: '编辑附件', diff --git a/client/src/sagas/login/services/login.js b/client/src/sagas/login/services/login.js index ef02a38..7340307 100644 --- a/client/src/sagas/login/services/login.js +++ b/client/src/sagas/login/services/login.js @@ -33,10 +33,10 @@ export function* authenticateUsingOidc() { const oidcConfig = yield select(selectors.selectOidcConfig); const state = nanoid(); - window.sessionStorage.setItem('oidc-state', state); + window.localStorage.setItem('oidc-state', state); const nonce = nanoid(); - window.sessionStorage.setItem('oidc-nonce', nonce); + window.localStorage.setItem('oidc-nonce', nonce); let redirectUrl = `${oidcConfig.authorizationUrl}`; redirectUrl += `&state=${encodeURIComponent(state)}`; @@ -49,11 +49,11 @@ export function* authenticateUsingOidcCallback() { // https://github.com/plankanban/planka/issues/511#issuecomment-1771385639 const params = new URLSearchParams(window.location.hash.substring(1) || window.location.search); - const state = window.sessionStorage.getItem('oidc-state'); - window.sessionStorage.removeItem('oidc-state'); + const state = window.localStorage.getItem('oidc-state'); + window.localStorage.removeItem('oidc-state'); - const nonce = window.sessionStorage.getItem('oidc-nonce'); - window.sessionStorage.removeItem('oidc-nonce'); + const nonce = window.localStorage.getItem('oidc-nonce'); + window.localStorage.removeItem('oidc-nonce'); yield put(replace(Paths.LOGIN)); diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 48c501a..9fe7085 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -31,6 +31,14 @@ services: # - DEFAULT_ADMIN_NAME=Demo Demo # - DEFAULT_ADMIN_USERNAME=demo + # Email Notifications (https://nodemailer.com/smtp/) + # - SMTP_HOST= + # - SMTP_PORT=587 + # - SMTP_SECURE=true + # - SMTP_USER= + # - SMTP_PASSWORD= + # - SMTP_FROM="Demo Demo" + # - OIDC_ISSUER= # - OIDC_CLIENT_ID= # - OIDC_CLIENT_SECRET= diff --git a/docker-compose.yml b/docker-compose.yml index 3d63069..51ac55c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,6 +31,14 @@ services: # - DEFAULT_ADMIN_NAME=Demo Demo # - DEFAULT_ADMIN_USERNAME=demo + # Email Notifications (https://nodemailer.com/smtp/) + # - SMTP_HOST= + # - SMTP_PORT=587 + # - SMTP_SECURE=true + # - SMTP_USER= + # - SMTP_PASSWORD= + # - SMTP_FROM="Demo Demo" + # - OIDC_ISSUER= # - OIDC_CLIENT_ID= # - OIDC_CLIENT_SECRET= diff --git a/package-lock.json b/package-lock.json index dc5a259..c98327b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "planka", - "version": "1.15.7", + "version": "1.16.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "planka", - "version": "1.15.7", + "version": "1.16.1", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 59d29aa..ccb7269 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "planka", - "version": "1.15.7", + "version": "1.16.1", "private": true, "homepage": "https://plankanban.github.io/planka", "repository": { diff --git a/server/.env.sample b/server/.env.sample index 8d962b6..fb5a828 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -22,6 +22,14 @@ SECRET_KEY=notsecretkey # DEFAULT_ADMIN_NAME=Demo Demo # DEFAULT_ADMIN_USERNAME=demo +# Email Notifications (https://nodemailer.com/smtp/) +# SMTP_HOST= +# SMTP_PORT=587 +# SMTP_SECURE=true +# SMTP_USER= +# SMTP_PASSWORD= +# SMTP_FROM="Demo Demo" + # OIDC_ISSUER= # OIDC_CLIENT_ID= # OIDC_CLIENT_SECRET= diff --git a/server/api/controllers/cards/create.js b/server/api/controllers/cards/create.js index ab0c444..b847c3e 100755 --- a/server/api/controllers/cards/create.js +++ b/server/api/controllers/cards/create.js @@ -78,12 +78,12 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { list } = await sails.helpers.lists + const { board, list } = await sails.helpers.lists .getProjectPath(inputs.listId) .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); const boardMembership = await BoardMembership.findOne({ - boardId: list.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -99,6 +99,7 @@ module.exports = { const card = await sails.helpers.cards.createOne .with({ + board, values: { ...values, list, diff --git a/server/api/controllers/comment-actions/create.js b/server/api/controllers/comment-actions/create.js index ddf4b99..7712a3a 100755 --- a/server/api/controllers/comment-actions/create.js +++ b/server/api/controllers/comment-actions/create.js @@ -32,12 +32,12 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { card } = await sails.helpers.cards + const { board, card } = await sails.helpers.cards .getProjectPath(inputs.cardId) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); const boardMembership = await BoardMembership.findOne({ - boardId: card.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -55,6 +55,7 @@ module.exports = { }; const action = await sails.helpers.actions.createOne.with({ + board, values: { ...values, card, diff --git a/server/api/helpers/actions/create-one.js b/server/api/helpers/actions/create-one.js index e96df93..78d5e0c 100644 --- a/server/api/helpers/actions/create-one.js +++ b/server/api/helpers/actions/create-one.js @@ -21,6 +21,10 @@ module.exports = { custom: valuesValidator, required: true, }, + board: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -56,6 +60,9 @@ module.exports = { userId, action, }, + user: values.user, + board: inputs.board, + card: values.card, }), ), ); diff --git a/server/api/helpers/cards/create-one.js b/server/api/helpers/cards/create-one.js index e1a96b4..847a00c 100644 --- a/server/api/helpers/cards/create-one.js +++ b/server/api/helpers/cards/create-one.js @@ -25,6 +25,10 @@ module.exports = { custom: valuesValidator, required: true, }, + board: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -104,6 +108,7 @@ module.exports = { }, user: values.creatorUser, }, + board: inputs.board, }); return card; diff --git a/server/api/helpers/cards/update-one.js b/server/api/helpers/cards/update-one.js index 1a78f8d..4363f9b 100644 --- a/server/api/helpers/cards/update-one.js +++ b/server/api/helpers/cards/update-one.js @@ -232,6 +232,7 @@ module.exports = { toList: _.pick(values.list, ['id', 'name']), }, }, + board: inputs.board, }); } diff --git a/server/api/helpers/notifications/create-one.js b/server/api/helpers/notifications/create-one.js index cc6a0b6..271a917 100644 --- a/server/api/helpers/notifications/create-one.js +++ b/server/api/helpers/notifications/create-one.js @@ -14,6 +14,40 @@ const valuesValidator = (value) => { return true; }; +// TODO: use templates (views) to build html +const buildAndSendEmail = async (user, board, card, action, notifiableUser) => { + let emailData; + switch (action.type) { + case Action.Types.MOVE_CARD: + emailData = { + subject: `${user.name} moved ${card.name} from ${action.data.fromList.name} to ${action.data.toList.name} on ${board.name}`, + html: + `

${user.name} moved ` + + `${card.name} ` + + `from ${action.data.fromList.name} to ${action.data.toList.name} ` + + `on ${board.name}

`, + }; + break; + case Action.Types.COMMENT_CARD: + emailData = { + subject: `${user.name} left a new comment to ${card.name} on ${board.name}`, + html: + `

${user.name} left a new comment to ` + + `${card.name} ` + + `on ${board.name}

` + + `

${action.data.text}

`, + }; + break; + default: + return; + } + + await sails.helpers.utils.sendEmail.with({ + ...emailData, + to: notifiableUser.email, + }); +}; + module.exports = { inputs: { values: { @@ -21,6 +55,18 @@ module.exports = { custom: valuesValidator, required: true, }, + user: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + card: { + type: 'ref', + required: true, + }, }, async fn(inputs) { @@ -40,6 +86,17 @@ module.exports = { item: notification, }); + if (sails.hooks.smtp.isActive()) { + let notifiableUser; + if (values.user) { + notifiableUser = values.user; + } else { + notifiableUser = await sails.helpers.users.getOne(notification.userId); + } + + buildAndSendEmail(inputs.user, inputs.board, inputs.card, values.action, notifiableUser); + } + return notification; }, }; diff --git a/server/api/helpers/utils/send-email.js b/server/api/helpers/utils/send-email.js new file mode 100644 index 0000000..5d9be72 --- /dev/null +++ b/server/api/helpers/utils/send-email.js @@ -0,0 +1,31 @@ +module.exports = { + inputs: { + to: { + type: 'string', + required: true, + }, + subject: { + type: 'string', + required: true, + }, + html: { + type: 'string', + required: true, + }, + }, + + async fn(inputs) { + const transporter = sails.hooks.smtp.getTransporter(); // TODO: check if active? + + try { + const info = await transporter.sendMail({ + ...inputs, + from: sails.config.custom.smtpFrom, + }); + + sails.log.info('Email sent: %s', info.messageId); + } catch (error) { + sails.log.error(error); + } + }, +}; diff --git a/server/api/hooks/smtp/index.js b/server/api/hooks/smtp/index.js new file mode 100644 index 0000000..4cd0503 --- /dev/null +++ b/server/api/hooks/smtp/index.js @@ -0,0 +1,35 @@ +const nodemailer = require('nodemailer'); + +module.exports = function smtpServiceHook(sails) { + let transporter = null; + + return { + /** + * Runs when this Sails app loads/lifts. + */ + + async initialize() { + if (sails.config.custom.smtpHost) { + transporter = nodemailer.createTransport({ + pool: true, + host: sails.config.custom.smtpHost, + port: sails.config.custom.smtpPort, + secure: sails.config.custom.smtpSecure, + auth: sails.config.custom.smtpUser && { + user: sails.config.custom.smtpUser, + pass: sails.config.custom.smtpPassword, + }, + }); + sails.log.info('SMTP hook has been loaded successfully'); + } + }, + + getTransporter() { + return transporter; + }, + + isActive() { + return transporter !== null; + }, + }; +}; diff --git a/server/config/custom.js b/server/config/custom.js index f074575..7647324 100644 --- a/server/config/custom.js +++ b/server/config/custom.js @@ -34,6 +34,13 @@ module.exports.custom = { defaultAdminEmail: process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase(), + smtpHost: process.env.SMTP_HOST, + smtpPort: process.env.SMTP_PORT || 587, + smtpSecure: process.env.SMTP_SECURE === 'true', + smtpUser: process.env.SMTP_USER, + smtpPassword: process.env.SMTP_PASSWORD, + smtpFrom: process.env.SMTP_FROM, + oidcIssuer: process.env.OIDC_ISSUER, oidcClientId: process.env.OIDC_CLIENT_ID, oidcClientSecret: process.env.OIDC_CLIENT_SECRET, diff --git a/server/package-lock.json b/server/package-lock.json index 05527a7..93453a8 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -15,6 +15,7 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "move-file": "^2.1.0", + "nodemailer": "^6.9.12", "openid-client": "^5.6.1", "rimraf": "^5.0.5", "sails": "^1.5.7", @@ -5257,6 +5258,14 @@ } } }, + "node_modules/nodemailer": { + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz", + "integrity": "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", @@ -12852,6 +12861,11 @@ "whatwg-url": "^5.0.0" } }, + "nodemailer": { + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz", + "integrity": "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==" + }, "nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", diff --git a/server/package.json b/server/package.json index 59bb2f7..67fc5e0 100644 --- a/server/package.json +++ b/server/package.json @@ -36,6 +36,7 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "move-file": "^2.1.0", + "nodemailer": "^6.9.12", "openid-client": "^5.6.1", "rimraf": "^5.0.5", "sails": "^1.5.7",