diff options
Diffstat (limited to 'app')
30 files changed, 1677 insertions, 499 deletions
diff --git a/app/Api/Me.php b/app/Api/Me.php index 37851731..df8ec070 100644 --- a/app/Api/Me.php +++ b/app/Api/Me.php @@ -44,7 +44,7 @@ class Me extends Base 'is_private' => 1, ); - list($valid, ) = $this->project->validateCreation($values); + list($valid, ) = $this->projectValidator->validateCreation($values); return $valid ? $this->project->create($values, $this->userSession->getId(), true) : false; } diff --git a/app/Api/Project.php b/app/Api/Project.php index f934432d..8e311f7f 100644 --- a/app/Api/Project.php +++ b/app/Api/Project.php @@ -69,7 +69,7 @@ class Project extends Base 'description' => $description ); - list($valid, ) = $this->project->validateCreation($values); + list($valid, ) = $this->projectValidator->validateCreation($values); return $valid ? $this->project->create($values) : false; } @@ -81,7 +81,7 @@ class Project extends Base 'description' => $description ); - list($valid, ) = $this->project->validateModification($values); + list($valid, ) = $this->projectValidator->validateModification($values); return $valid && $this->project->update($values); } } diff --git a/app/Api/Subtask.php b/app/Api/Subtask.php index 7baee3d3..782fdb02 100644 --- a/app/Api/Subtask.php +++ b/app/Api/Subtask.php @@ -36,7 +36,7 @@ class Subtask extends \Kanboard\Core\Base 'status' => $status, ); - list($valid, ) = $this->subtask->validateCreation($values); + list($valid, ) = $this->subtaskValidator->validateCreation($values); return $valid ? $this->subtask->create($values) : false; } @@ -58,7 +58,7 @@ class Subtask extends \Kanboard\Core\Base } } - list($valid, ) = $this->subtask->validateApiModification($values); + list($valid, ) = $this->subtaskValidator->validateApiModification($values); return $valid && $this->subtask->update($values); } } diff --git a/app/Api/User.php b/app/Api/User.php index 06e305f2..63c222fe 100644 --- a/app/Api/User.php +++ b/app/Api/User.php @@ -42,7 +42,7 @@ class User extends \Kanboard\Core\Base 'role' => $role, ); - list($valid, ) = $this->user->validateCreation($values); + list($valid, ) = $this->userValidator->validateCreation($values); return $valid ? $this->user->create($values) : false; } @@ -94,7 +94,7 @@ class User extends \Kanboard\Core\Base } } - list($valid, ) = $this->user->validateApiModification($values); + list($valid, ) = $this->userValidator->validateApiModification($values); return $valid && $this->user->update($values); } } diff --git a/app/Controller/PasswordReset.php b/app/Controller/PasswordReset.php index ebc1f77a..23567c9c 100644 --- a/app/Controller/PasswordReset.php +++ b/app/Controller/PasswordReset.php @@ -67,7 +67,7 @@ class PasswordReset extends Base /** * Set the new password */ - public function update(array $values = array(), array $errors = array()) + public function update() { $this->checkActivation(); diff --git a/app/Controller/Project.php b/app/Controller/Project.php index 5e75db4e..27c827d1 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -169,7 +169,7 @@ class Project extends Base } } - list($valid, $errors) = $this->project->validateModification($values); + list($valid, $errors) = $this->projectValidator->validateModification($values); if ($valid) { if ($this->project->update($values)) { @@ -329,7 +329,7 @@ class Project extends Base public function save() { $values = $this->request->getValues(); - list($valid, $errors) = $this->project->validateCreation($values); + list($valid, $errors) = $this->projectValidator->validateCreation($values); if ($valid) { $project_id = $this->project->create($values, $this->userSession->getId(), true); diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php index c93b637d..caaaa85e 100644 --- a/app/Controller/Subtask.php +++ b/app/Controller/Subtask.php @@ -63,7 +63,7 @@ class Subtask extends Base $task = $this->getTask(); $values = $this->request->getValues(); - list($valid, $errors) = $this->subtask->validateCreation($values); + list($valid, $errors) = $this->subtaskValidator->validateCreation($values); if ($valid) { if ($this->subtask->create($values)) { @@ -113,7 +113,7 @@ class Subtask extends Base $this->getSubtask(); $values = $this->request->getValues(); - list($valid, $errors) = $this->subtask->validateModification($values); + list($valid, $errors) = $this->subtaskValidator->validateModification($values); if ($valid) { if ($this->subtask->update($values)) { diff --git a/app/Controller/Swimlane.php b/app/Controller/Swimlane.php index 5229621c..66410888 100644 --- a/app/Controller/Swimlane.php +++ b/app/Controller/Swimlane.php @@ -60,7 +60,7 @@ class Swimlane extends Base { $project = $this->getProject(); $values = $this->request->getValues(); - list($valid, $errors) = $this->swimlane->validateCreation($values); + list($valid, $errors) = $this->swimlaneValidator->validateCreation($values); if ($valid) { if ($this->swimlane->create($values)) { @@ -84,7 +84,7 @@ class Swimlane extends Base $project = $this->getProject(); $values = $this->request->getValues() + array('show_default_swimlane' => 0); - list($valid, ) = $this->swimlane->validateDefaultModification($values); + list($valid, ) = $this->swimlaneValidator->validateDefaultModification($values); if ($valid) { if ($this->swimlane->updateDefault($values)) { @@ -126,7 +126,7 @@ class Swimlane extends Base $project = $this->getProject(); $values = $this->request->getValues(); - list($valid, $errors) = $this->swimlane->validateModification($values); + list($valid, $errors) = $this->swimlaneValidator->validateModification($values); if ($valid) { if ($this->swimlane->update($values)) { diff --git a/app/Controller/Tasklink.php b/app/Controller/Tasklink.php index 068bf16d..a81d3ee5 100644 --- a/app/Controller/Tasklink.php +++ b/app/Controller/Tasklink.php @@ -69,7 +69,7 @@ class Tasklink extends Base $values = $this->request->getValues(); $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); - list($valid, $errors) = $this->taskLink->validateCreation($values); + list($valid, $errors) = $this->taskLinkValidator->validateCreation($values); if ($valid) { if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) { @@ -125,7 +125,7 @@ class Tasklink extends Base $task = $this->getTask(); $values = $this->request->getValues(); - list($valid, $errors) = $this->taskLink->validateModification($values); + list($valid, $errors) = $this->taskLinkValidator->validateModification($values); if ($valid) { if ($this->taskLink->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) { diff --git a/app/Controller/User.php b/app/Controller/User.php index 2a811219..97e01553 100644 --- a/app/Controller/User.php +++ b/app/Controller/User.php @@ -108,7 +108,7 @@ class User extends Base public function save() { $values = $this->request->getValues(); - list($valid, $errors) = $this->user->validateCreation($values); + list($valid, $errors) = $this->userValidator->validateCreation($values); if ($valid) { $project_id = empty($values['project_id']) ? 0 : $values['project_id']; @@ -329,7 +329,7 @@ class User extends Base if ($this->request->isPost()) { $values = $this->request->getValues(); - list($valid, $errors) = $this->user->validatePasswordModification($values); + list($valid, $errors) = $this->userValidator->validatePasswordModification($values); if ($valid) { if ($this->user->update($values)) { @@ -371,7 +371,7 @@ class User extends Base } } - list($valid, $errors) = $this->user->validateModification($values); + list($valid, $errors) = $this->userValidator->validateModification($values); if ($valid) { if ($this->user->update($values)) { @@ -409,7 +409,7 @@ class User extends Base if ($this->request->isPost()) { $values = $this->request->getValues() + array('disable_login_form' => 0, 'is_ldap_user' => 0); - list($valid, $errors) = $this->user->validateModification($values); + list($valid, $errors) = $this->userValidator->validateModification($values); if ($valid) { if ($this->user->update($values)) { diff --git a/app/Core/Base.php b/app/Core/Base.php index f32c1442..723831e3 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -79,6 +79,7 @@ use Pimple\Container; * @property \Kanboard\Model\ProjectMetadata $projectMetadata * @property \Kanboard\Model\ProjectPermission $projectPermission * @property \Kanboard\Model\ProjectUserRole $projectUserRole + * @property \Kanboard\Model\projectUserRoleFilter $projectUserRoleFilter * @property \Kanboard\Model\ProjectGroupRole $projectGroupRole * @property \Kanboard\Model\ProjectNotification $projectNotification * @property \Kanboard\Model\ProjectNotificationType $projectNotificationType @@ -100,7 +101,6 @@ use Pimple\Container; * @property \Kanboard\Model\TaskPermission $taskPermission * @property \Kanboard\Model\TaskPosition $taskPosition * @property \Kanboard\Model\TaskStatus $taskStatus - * @property \Kanboard\Model\TaskValidator $taskValidator * @property \Kanboard\Model\TaskMetadata $taskMetadata * @property \Kanboard\Model\Transition $transition * @property \Kanboard\Model\User $user @@ -114,6 +114,12 @@ use Pimple\Container; * @property \Kanboard\Model\UserMetadata $userMetadata * @property \Kanboard\Model\Webhook $webhook * @property \Kanboard\Validator\PasswordResetValidator $passwordResetValidator + * @property \Kanboard\Validator\ProjectValidator $projectValidator + * @property \Kanboard\Validator\SubtaskValidator $subtaskValidator + * @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator + * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator + * @property \Kanboard\Validator\TaskValidator $taskValidator + * @property \Kanboard\Validator\UserValidator $userValidator * @property \Psr\Log\LoggerInterface $logger * @property \PicoDb\Database $db * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher diff --git a/app/Core/Session/SessionStorage.php b/app/Core/Session/SessionStorage.php index f55a7ee3..5ee1d811 100644 --- a/app/Core/Session/SessionStorage.php +++ b/app/Core/Session/SessionStorage.php @@ -20,6 +20,8 @@ namespace Kanboard\Core\Session; * @property bool $hasSubtaskInProgress * @property bool $hasRememberMe * @property bool $boardCollapsed + * @property bool $twoFactorBeforeCodeCalled + * @property string $twoFactorSecret */ class SessionStorage { diff --git a/app/Locale/my_MY/translations.php b/app/Locale/my_MY/translations.php new file mode 100644 index 00000000..9b0f6c3d --- /dev/null +++ b/app/Locale/my_MY/translations.php @@ -0,0 +1,1100 @@ +<?php +return array( + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', + 'None' => 'Tiada', + 'edit' => 'sunting', + 'Edit' => 'Sunting', + 'remove' => 'hapus', + 'Remove' => 'Hapus', + 'Update' => 'Kemaskini', + 'Yes' => 'Ya', + 'No' => 'Tidak', + 'cancel' => 'batal', + 'or' => 'atau', + 'Yellow' => 'Kuning', + 'Blue' => 'Biru', + 'Green' => 'Hijau', + 'Purple' => 'Ungu', + 'Red' => 'Merah', + 'Orange' => 'Oren', + 'Grey' => 'Kelabu', + 'Brown' => 'Coklat', + 'Deep Orange' => 'Oren Gelap', + 'Dark Grey' => 'Kelabu Malap', + 'Pink' => 'Merah Jambu', + 'Teal' => 'Teal', + 'Cyan' => 'Sian', + 'Lime' => 'Lime', + 'Light Green' => 'Hijau Muda', + 'Amber' => 'Amber', + 'Save' => 'Simpan', + 'Login' => 'Masuk', + 'Official website:' => 'Laman rasmi :', + 'Unassigned' => 'Belum ditugaskan', + 'View this task' => 'Lihat tugas ini', + 'Remove user' => 'Hapus pengguna', + 'Do you really want to remove this user: "%s"?' => 'Anda yakin mahu menghapus pengguna ini : « %s » ?', + 'New user' => 'Pengguna baru', + 'All users' => 'Semua pengguna', + 'Username' => 'Nama pengguna', + 'Password' => 'Kata laluan', + 'Administrator' => 'Pentadbir', + 'Sign in' => 'Masuk', + 'Users' => 'Para Pengguna', + 'No user' => 'Tiada pengguna', + 'Forbidden' => 'Larangan', + 'Access Forbidden' => 'Akses Dilarang', + 'Edit user' => 'Ubah Pengguna', + 'Logout' => 'Keluar', + 'Bad username or password' => 'Nama pengguna atau kata laluan tidak sepadan', + 'Edit project' => 'Ubah projek', + 'Name' => 'Nama', + 'Projects' => 'Projek', + 'No project' => 'Tiada projek', + 'Project' => 'Projek', + 'Status' => 'Status', + 'Tasks' => 'Tugasan', + 'Board' => 'Papan', + 'Actions' => 'Tindakan', + 'Inactive' => 'Tidak Aktif', + 'Active' => 'Aktif', + 'Add this column' => 'Tambahkan kolom ini', + '%d tasks on the board' => '%d tugasan di papan', + '%d tasks in total' => 'Sejumlah %d tugasan', + 'Unable to update this board.' => 'Tidak berupaya mengemaskini papan ini', + 'Edit board' => 'ubah papan', + 'Disable' => 'Nyah-Upaya', + 'Enable' => 'Aktifkan', + 'New project' => 'Projek Baru', + 'Do you really want to remove this project: "%s"?' => 'Anda yakin mahu menghapus projek ini : « %s » ?', + 'Remove project' => 'Hapus projek', + 'Edit the board for "%s"' => 'Ubah papan untuk « %s »', + 'All projects' => 'Semua projek', + 'Change columns' => 'Ubah kolom', + 'Add a new column' => 'Tambah kolom baru', + 'Title' => 'Judul', + 'Nobody assigned' => 'Tidak ada yang ditugaskan', + 'Assigned to %s' => 'Ditugaskan ke %s', + 'Remove a column' => 'Hapus kolom', + 'Remove a column from a board' => 'Hapus kolom dari papan', + 'Unable to remove this column.' => 'Tidak dapat menghapus kolom ini.', + 'Do you really want to remove this column: "%s"?' => 'Apakah anda yakin akan menghapus kolom ini : « %s » ?', + 'This action will REMOVE ALL TASKS associated to this column!' => 'tindakan ini akan MENGHAPUS SEMUA TUGAS yang terkait dengan kolom ini!', + 'Settings' => 'Penetapan', + 'Application settings' => 'Penetapan aplikasi', + 'Language' => 'Bahasa', + 'Webhook token:' => 'Token webhook :', + 'API token:' => 'Token API :', + 'Database size:' => 'Saiz pengkalan data:', + 'Download the database' => 'Muat turun pengkalan data', + 'Optimize the database' => 'Optimakan pengkalan data', + '(VACUUM command)' => '(perintah VACUUM)', + '(Gzip compressed Sqlite file)' => '(File Sqlite yang termampat Gzip)', + 'Close a task' => 'Tutup tugas', + 'Edit a task' => 'Sunting tugas', + 'Column' => 'Kolom', + 'Color' => 'Warna', + 'Assignee' => 'Orang yang ditugaskan', + 'Create another task' => 'Buat tugas lain', + 'New task' => 'Tugasan baru', + 'Open a task' => 'Buka tugas', + 'Do you really want to open this task: "%s"?' => 'Anda yakin untuk buka tugas ini : « %s » ?', + 'Back to the board' => 'Kembali ke papan', + 'Created on %B %e, %Y at %k:%M %p' => 'Dicipta pada tanggal %d/%m/%Y à %H:%M', + 'There is nobody assigned' => 'Tidak ada orang yand ditugaskan', + 'Column on the board:' => 'Kolom di dalam papan : ', + 'Status is open' => 'Status terbuka', + 'Status is closed' => 'Status ditutup', + 'Close this task' => 'Tutup tugas ini', + 'Open this task' => 'Buka tugas ini', + 'There is no description.' => 'Tidak ada keterangan.', + 'Add a new task' => 'Tambah tugas baru', + 'The username is required' => 'Nama pengguna adalah wajib', + 'The maximum length is %d characters' => 'Panjang maksimum adalah %d karakter', + 'The minimum length is %d characters' => 'Panjang minimum adalah %d karakter', + 'The password is required' => 'Kata laluan adalah wajib', + 'This value must be an integer' => 'Nilai ini harus integer', + 'The username must be unique' => 'Nama pengguna semestinya unik', + 'The user id is required' => 'Id Pengguna adalah wajib', + 'Passwords don\'t match' => 'Kata laluan tidak sepadan', + 'The confirmation is required' => 'Pengesahan diperlukan', + 'The project is required' => 'Projek diperlukan', + 'The id is required' => 'Id diperlukan', + 'The project id is required' => 'Id projek diperlukan', + 'The project name is required' => 'Nama projek diperlukan', + 'The title is required' => 'Judul diperlukan', + 'Settings saved successfully.' => 'Penetapan berjaya disimpan.', + 'Unable to save your settings.' => 'Tidak dapat menyimpan penetapan anda.', + 'Database optimization done.' => 'Optimasi pengkalan data selesai.', + 'Your project have been created successfully.' => 'Projek anda berhasil dibuat.', + 'Unable to create your project.' => 'Tidak dapat membuat projek anda.', + 'Project updated successfully.' => 'projek berhasil diperbaharui.', + 'Unable to update this project.' => 'Tidak dapat memperbaharui projek ini.', + 'Unable to remove this project.' => 'Tidak dapat menghapus projek ini.', + 'Project removed successfully.' => 'projek berhasil dihapus.', + 'Project activated successfully.' => 'projek berhasil diaktivasi.', + 'Unable to activate this project.' => 'Tidak dapat mengaktifkan projek ini.', + 'Project disabled successfully.' => 'projek berhasil dinonaktifkan.', + 'Unable to disable this project.' => 'Tidak dapat menonaktifkan projek ini.', + 'Unable to open this task.' => 'Tidak dapat membuka tugas ini.', + 'Task opened successfully.' => 'Tugas berhasil dibuka.', + 'Unable to close this task.' => 'Tidak dapat menutup tugas ini.', + 'Task closed successfully.' => 'Tugas berhasil ditutup.', + 'Unable to update your task.' => 'Tidak dapat memperbaharui tugas ini.', + 'Task updated successfully.' => 'Tugas berhasil diperbaharui.', + 'Unable to create your task.' => 'Tidak dapat membuat tugas anda.', + 'Task created successfully.' => 'Tugas berhasil dibuat.', + 'User created successfully.' => 'Pengguna berhasil dibuat.', + 'Unable to create your user.' => 'Tidak dapat membuat pengguna anda.', + 'User updated successfully.' => 'Pengguna berhasil diperbaharui.', + 'Unable to update your user.' => 'Tidak dapat memperbaharui pengguna anda.', + 'User removed successfully.' => 'pengguna berhasil dihapus.', + 'Unable to remove this user.' => 'Tidak dapat menghapus pengguna ini.', + 'Board updated successfully.' => 'Papan berhasil diperbaharui.', + 'Ready' => 'Siap', + 'Backlog' => 'Tertunda', + 'Work in progress' => 'Sedang dalam pengerjaan', + 'Done' => 'Selesai', + 'Application version:' => 'Versi aplikasi :', + 'Completed on %B %e, %Y at %k:%M %p' => 'Diselesaikan pada tanggal %d/%m/%Y à %H:%M', + '%B %e, %Y at %k:%M %p' => '%d/%m/%Y à %H:%M', + 'Date created' => 'Tanggal dibuat', + 'Date completed' => 'Tanggal diselesaikan', + 'Id' => 'Id.', + '%d closed tasks' => '%d tugas yang ditutup', + 'No task for this project' => 'Tidak ada tugas dalam projek ini', + 'Public link' => 'Pautan publik', + 'Change assignee' => 'Mengubah orang yand ditugaskan', + 'Change assignee for the task "%s"' => 'Mengubah orang yang ditugaskan untuk tugas « %s »', + 'Timezone' => 'Zona waktu', + 'Sorry, I didn\'t find this information in my database!' => 'Maaf, saya tidak menemukan informasi ini dalam basis data saya !', + 'Page not found' => 'Halaman tidak ditemukan', + 'Complexity' => 'Kompleksitas', + 'Task limit' => 'Batas tugas.', + 'Task count' => 'Jumlah tugas', + 'User' => 'Pengguna', + 'Comments' => 'Komentar', + 'Write your text in Markdown' => 'Menulis teks anda didalam Markdown', + 'Leave a comment' => 'Tinggalkan komentar', + 'Comment is required' => 'Komentar diperlukan', + 'Leave a description' => 'Tinggalkan deskripsi', + 'Comment added successfully.' => 'Komentar berhasil ditambahkan.', + 'Unable to create your comment.' => 'Tidak dapat menambahkan komentar anda.', + 'Edit this task' => 'Modifikasi tugas ini', + 'Due Date' => 'Batas Tanggal Terakhir', + 'Invalid date' => 'Tanggal tidak valid', + 'Must be done before %B %e, %Y' => 'Harus diselesaikan sebelum tanggal %d/%m/%Y', + '%B %e, %Y' => '%d %B %Y', + '%b %e, %Y' => '%d/%m/%Y', + 'Automatic actions' => 'Tindakan otomatis', + 'Your automatic action have been created successfully.' => 'Tindakan otomatis anda berhasil dibuat.', + 'Unable to create your automatic action.' => 'Tidak dapat membuat tindakan otomatis anda.', + 'Remove an action' => 'Hapus tindakan', + 'Unable to remove this action.' => 'Tidak dapat menghapus tindakan ini', + 'Action removed successfully.' => 'Tindakan berhasil dihapus.', + 'Automatic actions for the project "%s"' => 'Tindakan otomatis untuk projek ini « %s »', + 'Defined actions' => 'Tindakan didefinisikan', + 'Add an action' => 'Tambah tindakan', + 'Event name' => 'Nama acara', + 'Action name' => 'Nama tindakan', + 'Action parameters' => 'Parameter tindakan', + 'Action' => 'Tindakan', + 'Event' => 'Acara', + 'When the selected event occurs execute the corresponding action.' => 'Ketika acara yang dipilih terjadi, melakukan tindakan yang sesuai.', + 'Next step' => 'Langkah selanjutnya', + 'Define action parameters' => 'Definisi parameter tindakan', + 'Save this action' => 'Simpan tindakan ini', + 'Do you really want to remove this action: "%s"?' => 'Apakah anda yakin akan menghapus tindakan ini « %s » ?', + 'Remove an automatic action' => 'Hapus tindakan otomatis', + 'Assign the task to a specific user' => 'Menetapkan tugas untuk pengguna tertentu', + 'Assign the task to the person who does the action' => 'Memberikan tugas untuk orang yang melakukan tindakan', + 'Duplicate the task to another project' => 'Duplikasi tugas ke projek lain', + 'Move a task to another column' => 'Pindahkan tugas ke kolom lain', + 'Task modification' => 'Modifikasi tugas', + 'Task creation' => 'Membuat tugas', + 'Closing a task' => 'Menutup tugas', + 'Assign a color to a specific user' => 'Menetapkan warna untuk pengguna tertentu', + 'Column title' => 'Judul kolom', + 'Position' => 'Posisi', + 'Move Up' => 'Pindah ke atas', + 'Move Down' => 'Pindah ke bawah', + 'Duplicate to another project' => 'Duplikasi ke projek lain', + 'Duplicate' => 'Duplikasi', + 'link' => 'Pautan', + 'Comment updated successfully.' => 'Komentar berhasil diperbaharui.', + 'Unable to update your comment.' => 'Tidak dapat memperbaharui komentar anda.', + 'Remove a comment' => 'Hapus komentar', + 'Comment removed successfully.' => 'Komentar berhasil dihapus.', + 'Unable to remove this comment.' => 'Tidak dapat menghapus komentar ini.', + 'Do you really want to remove this comment?' => 'Apakah anda yakin akan menghapus komentar ini ?', + 'Only administrators or the creator of the comment can access to this page.' => 'Hanya administrator atau pembuat komentar yang dapat mengakses halaman ini.', + 'Current password for the user "%s"' => 'Kata laluan saat ini untuk pengguna « %s »', + 'The current password is required' => 'Kata laluan saat ini diperlukan', + 'Wrong password' => 'Kata laluan salah', + 'Unknown' => 'Tidak diketahui', + 'Last logins' => 'Masuk terakhir', + 'Login date' => 'Tanggal masuk', + 'Authentication method' => 'Metode otentifikasi', + 'IP address' => 'Alamat IP', + 'User agent' => 'Agen Pengguna', + 'Persistent connections' => 'Koneksi persisten', + 'No session.' => 'Tidak ada sesi.', + 'Expiration date' => 'Tanggal kadaluarsa', + 'Remember Me' => 'Ingat Saya', + 'Creation date' => 'Tanggal dibuat', + 'Everybody' => 'Semua orang', + 'Open' => 'Terbuka', + 'Closed' => 'Ditutup', + 'Search' => 'Cari', + 'Nothing found.' => 'Tidak ditemukan.', + 'Due date' => 'Batas tanggal terakhir', + 'Others formats accepted: %s and %s' => 'Format lain yang didukung : %s et %s', + 'Description' => 'Deskripsi', + '%d comments' => '%d komentar', + '%d comment' => '%d komentar', + 'Email address invalid' => 'Alamat email tidak valid', + 'Your external account is not linked anymore to your profile.' => 'Akaun eksternal anda tidak lagi terhubung ke profil anda.', + 'Unable to unlink your external account.' => 'Tidak dapat memutuskan Akaun eksternal anda.', + 'External authentication failed' => 'Otentifikasi eksternal gagal', + 'Your external account is linked to your profile successfully.' => 'Akaun eksternal anda berhasil dihubungkan ke profil anda.', + 'Email' => 'Email', + 'Link my Google Account' => 'Hubungkan Akaun Google saya', + 'Unlink my Google Account' => 'Putuskan Akaun Google saya', + 'Login with my Google Account' => 'Masuk menggunakan Akaun Google saya', + 'Project not found.' => 'projek tidak ditemukan.', + 'Task removed successfully.' => 'Tugas berhasil dihapus.', + 'Unable to remove this task.' => 'Tidak dapat menghapus tugas ini.', + 'Remove a task' => 'Hapus tugas', + 'Do you really want to remove this task: "%s"?' => 'Apakah anda yakin akan menghapus tugas ini « %s » ?', + 'Assign automatically a color based on a category' => 'Otomatis menetapkan warna berdasarkan kategori', + 'Assign automatically a category based on a color' => 'Otomatis menetapkan kategori berdasarkan warna', + 'Task creation or modification' => 'Tugas dibuat atau di mofifikasi', + 'Category' => 'Kategori', + 'Category:' => 'Kategori :', + 'Categories' => 'Kategori', + 'Category not found.' => 'Kategori tidak ditemukan', + 'Your category have been created successfully.' => 'Kategori anda berhasil dibuat.', + 'Unable to create your category.' => 'Tidak dapat membuat kategori anda.', + 'Your category have been updated successfully.' => 'Kategori anda berhasil diperbaharui.', + 'Unable to update your category.' => 'Tidak dapat memperbaharui kategori anda.', + 'Remove a category' => 'Hapus kategori', + 'Category removed successfully.' => 'Kategori berhasil dihapus.', + 'Unable to remove this category.' => 'Tidak dapat menghapus kategori ini.', + 'Category modification for the project "%s"' => 'Modifikasi kategori untuk projek « %s »', + 'Category Name' => 'Nama Kategori', + 'Add a new category' => 'Tambah kategori baru', + 'Do you really want to remove this category: "%s"?' => 'Apakah anda yakin akan menghapus kategori ini « %s » ?', + 'All categories' => 'Semua kategori', + 'No category' => 'Tidak ada kategori', + 'The name is required' => 'Nama diperlukan', + 'Remove a file' => 'Hapus berkas', + 'Unable to remove this file.' => 'Tidak dapat menghapus berkas ini.', + 'File removed successfully.' => 'Berkas berhasil dihapus.', + 'Attach a document' => 'Lampirkan dokumen', + 'Do you really want to remove this file: "%s"?' => 'Apakah anda yakin akan menghapus berkas ini « %s » ?', + 'Attachments' => 'Lampiran', + 'Edit the task' => 'Modifikasi tugas', + 'Edit the description' => 'Modifikasi deskripsi', + 'Add a comment' => 'Tambahkan komentar', + 'Edit a comment' => 'Modifikasi komentar', + 'Summary' => 'Ringkasan', + 'Time tracking' => 'Pelacakan waktu', + 'Estimate:' => 'Estimasi :', + 'Spent:' => 'Menghabiskan:', + 'Do you really want to remove this sub-task?' => 'Apakah anda yakin akan menghapus sub-tugas ini ?', + 'Remaining:' => 'Tersisa:', + 'hours' => 'jam', + 'spent' => 'menghabiskan', + 'estimated' => 'perkiraan', + 'Sub-Tasks' => 'Sub-tugas', + 'Add a sub-task' => 'Tambahkan sub-tugas', + 'Original estimate' => 'Perkiraan semula', + 'Create another sub-task' => 'Tambahkan sub-tugas lainnya', + 'Time spent' => 'Waktu yang dihabiskan', + 'Edit a sub-task' => 'Modifikasi sub-tugas', + 'Remove a sub-task' => 'Hapus sub-tugas', + 'The time must be a numeric value' => 'Waktu harus berisikan numerik', + 'Todo' => 'Yang harus dilakukan', + 'In progress' => 'Sedang proses', + 'Sub-task removed successfully.' => 'Sub-tugas berhasil dihapus.', + 'Unable to remove this sub-task.' => 'Tidak dapat menghapus sub-tugas.', + 'Sub-task updated successfully.' => 'Sub-tugas berhasil diperbaharui.', + 'Unable to update your sub-task.' => 'Tidak dapat memperbaharui sub-tugas anda.', + 'Unable to create your sub-task.' => 'Tidak dapat membuat sub-tugas anda.', + 'Sub-task added successfully.' => 'Sub-tugas berhasil dibuat.', + 'Maximum size: ' => 'Ukuran maksimum: ', + 'Unable to upload the file.' => 'Tidak dapat mengunggah berkas.', + 'Display another project' => 'Lihat projek lain', + 'Login with my Github Account' => 'Masuk menggunakan Akaun Github saya', + 'Link my Github Account' => 'Hubungkan Akaun Github saya ', + 'Unlink my Github Account' => 'Putuskan Akaun Github saya', + 'Created by %s' => 'Dibuat oleh %s', + 'Last modified on %B %e, %Y at %k:%M %p' => 'Modifikasi terakhir pada tanggal %d/%m/%Y à %H:%M', + 'Tasks Export' => 'Ekspor Tugas', + 'Tasks exportation for "%s"' => 'Tugas di ekspor untuk « %s »', + 'Start Date' => 'Tanggal Mulai', + 'End Date' => 'Tanggal Berakhir', + 'Execute' => 'Eksekusi', + 'Task Id' => 'Id Tugas', + 'Creator' => 'Pembuat', + 'Modification date' => 'Tanggal modifikasi', + 'Completion date' => 'Tanggal penyelesaian', + 'Clone' => 'Klon', + 'Project cloned successfully.' => 'Kloning projek berhasil.', + 'Unable to clone this project.' => 'Tidak dapat mengkloning projek.', + 'Enable email notifications' => 'Aktifkan pemberitahuan dari email', + 'Task position:' => 'Posisi tugas :', + 'The task #%d have been opened.' => 'Tugas #%d telah dibuka.', + 'The task #%d have been closed.' => 'Tugas #%d telah ditutup.', + 'Sub-task updated' => 'Sub-tugas diperbaharui', + 'Title:' => 'Judul :', + 'Status:' => 'Status :', + 'Assignee:' => 'Ditugaskan ke :', + 'Time tracking:' => 'Pelacakan waktu :', + 'New sub-task' => 'Sub-tugas baru', + 'New attachment added "%s"' => 'Lampiran baru ditambahkan « %s »', + 'Comment updated' => 'Komentar diperbaharui', + 'New comment posted by %s' => 'Komentar baru ditambahkan oleh « %s »', + 'New attachment' => 'Lampirkan baru', + 'New comment' => 'Komentar baru', + 'New subtask' => 'Sub-tugas baru', + 'Subtask updated' => 'Sub-tugas diperbaharui', + 'Task updated' => 'Tugas diperbaharui', + 'Task closed' => 'Tugas ditutup', + 'Task opened' => 'Tugas dibuka', + 'I want to receive notifications only for those projects:' => 'Saya ingin menerima pemberitahuan hanya untuk projek-projek yang dipilih :', + 'view the task on Kanboard' => 'lihat tugas di Kanboard', + 'Public access' => 'Akses awam', + 'User management' => 'Manajemen pengguna', + 'Active tasks' => 'Tugas aktif', + 'Disable public access' => 'Nyahaktifkan akses awam', + 'Enable public access' => 'Aktifkan akses awam', + 'Public access disabled' => 'Akses awam dinyahaktif', + 'Do you really want to disable this project: "%s"?' => 'Anda yakin menyah-aktifkan projek ini : « %s » ?', + 'Do you really want to enable this project: "%s"?' => 'Anda yakin untuk mengaktifkan projek ini : « %s » ?', + 'Project activation' => 'Aktifkan projek', + 'Move the task to another project' => 'Pindahkan tugas ke projek lain', + 'Move to another project' => 'Pindahkan ke projek lain', + 'Do you really want to duplicate this task?' => 'Anda yakin mengembarkan tugas ini ?', + 'Duplicate a task' => 'Kembarkan tugas', + 'External accounts' => 'Akaun luaran', + 'Account type' => 'Jenis Akaun', + 'Local' => 'Lokal', + 'Remote' => 'Jauh', + 'Enabled' => 'Aktif', + 'Disabled' => 'Tidak aktif', + 'Username:' => 'Nama pengguna :', + 'Name:' => 'Nama:', + 'Email:' => 'Emel:', + 'Notifications:' => 'Makluman:', + 'Notifications' => 'Makluman', + 'Account type:' => 'Jenis Akaun :', + 'Edit profile' => 'Sunting profil', + 'Change password' => 'Rubah kata sandri', + 'Password modification' => 'Modifikasi kata laluan', + 'External authentications' => 'Otentifikasi eksternal', + 'Google Account' => 'Akaun Google', + 'Github Account' => 'Akaun Github', + 'Never connected.' => 'Tidak pernah terhubung.', + 'No account linked.' => 'Tidak ada Akaun terhubung.', + 'Account linked.' => 'Akaun terhubung.', + 'No external authentication enabled.' => 'Tidak ada otentifikasi eksternal yang aktif.', + 'Password modified successfully.' => 'Kata laluan telah berjaya ditukar.', + 'Unable to change the password.' => 'Tidak dapat merubah kata laluanr.', + 'Change category for the task "%s"' => 'Rubah kategori untuk tugas « %s »', + 'Change category' => 'Tukar kategori', + '%s updated the task %s' => '%s memperbaharui tugas %s', + '%s opened the task %s' => '%s membuka tugas %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s memindahkan tugas %s ke posisi n°%d dalam kolom « %s »', + '%s moved the task %s to the column "%s"' => '%s memindahkan tugas %s ke kolom « %s »', + '%s created the task %s' => '%s membuat tugas %s', + '%s closed the task %s' => '%s menutup tugas %s', + '%s created a subtask for the task %s' => '%s membuat subtugas untuk tugas %s', + '%s updated a subtask for the task %s' => '%s memperbaharui subtugas untuk tugas %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Ditugaskan untuk %s dengan perkiraan %s/%sh', + 'Not assigned, estimate of %sh' => 'Tiada yang ditugaskan, perkiraan %sh', + '%s updated a comment on the task %s' => '%s memperbaharui komentar pada tugas %s', + '%s commented the task %s' => '%s memberikan komentar pada tugas %s', + '%s\'s activity' => 'Aktifitas dari %s', + 'RSS feed' => 'RSS feed', + '%s updated a comment on the task #%d' => '%s memperbaharui komentar pada tugas n°%d', + '%s commented on the task #%d' => '%s memberikan komentar pada tugas n°%d', + '%s updated a subtask for the task #%d' => '%s memperbaharui subtugas untuk tugas n°%d', + '%s created a subtask for the task #%d' => '%s membuat subtugas untuk tugas n°%d', + '%s updated the task #%d' => '%s memperbaharui tugas n°%d', + '%s created the task #%d' => '%s membuat tugas n°%d', + '%s closed the task #%d' => '%s menutup tugas n°%d', + '%s open the task #%d' => '%s membuka tugas n°%d', + '%s moved the task #%d to the column "%s"' => '%s memindahkan tugas n°%d ke kolom « %s »', + '%s moved the task #%d to the position %d in the column "%s"' => '%s memindahkan tugas n°%d ke posisi n°%d dalam kolom « %s »', + 'Activity' => 'Aktifitas', + 'Default values are "%s"' => 'Standar nilai adalah« %s »', + 'Default columns for new projects (Comma-separated)' => 'Kolom default untuk projek baru (dipisahkan dengan koma)', + 'Task assignee change' => 'Mengubah orang ditugaskan untuk tugas', + '%s change the assignee of the task #%d to %s' => '%s rubah orang yang ditugaskan dari tugas n%d ke %s', + '%s changed the assignee of the task %s to %s' => '%s mengubah orang yang ditugaskan dari tugas %s ke %s', + 'New password for the user "%s"' => 'Kata laluan baru untuk pengguna « %s »', + 'Choose an event' => 'Pilih sebuah acara', + 'Create a task from an external provider' => 'Buat tugas dari pemasok eksternal', + 'Change the assignee based on an external username' => 'Rubah penugasan berdasarkan nama pengguna eksternal', + 'Change the category based on an external label' => 'Rubah kategori berdasarkan label eksternal', + 'Reference' => 'Referensi', + 'Reference: %s' => 'Referensi : %s', + 'Label' => 'Label', + 'Database' => 'Pengkalan data', + 'About' => 'Tentang', + 'Database driver:' => 'Driver pengkalan data:', + 'Board settings' => 'Pengaturan papan', + 'URL and token' => 'URL dan token', + 'Webhook settings' => 'Penetapan webhook', + 'URL for task creation:' => 'URL untuk cipta tugas:', + 'Reset token' => 'Menetap semula token', + 'API endpoint:' => 'API endpoint :', + 'Refresh interval for private board' => 'Interval pembaruan untuk papan pribadi', + 'Refresh interval for public board' => 'Interval pembaruan untuk papan publik', + 'Task highlight period' => 'Periode puncak tugas', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode (dalam detik) untuk mempertimbangkan tugas yang baru dimodifikasi (0 untuk menonaktifkan, standar 2 hari)', + 'Frequency in second (60 seconds by default)' => 'Frequensi dalam detik (standar 60 saat)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekuensi dalam detik (0 untuk menonaktifkan fitur ini, standar 10 detik)', + 'Application URL' => 'URL Aplikasi', + 'Example: http://example.kanboard.net/ (used by email notifications)' => 'Contoh: http://example.kanboard.net/ (digunakan untuk pemberitahuan email)', + 'Token regenerated.' => 'Token diregenerasi.', + 'Date format' => 'Format tarikh', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO selalunya diterima, contoh: « %s » et « %s »', + 'New private project' => 'Projek peribadi baharu', + 'This project is private' => 'projek ini adalah peribadi', + 'Type here to create a new sub-task' => 'Ketik disini untuk membuat sub-tugas baru', + 'Add' => 'Tambah', + 'Estimated time: %s hours' => 'Anggaran waktu: %s jam', + 'Time spent: %s hours' => 'Waktu dihabiskan : %s jam', + 'Started on %B %e, %Y' => 'Dimulai pada %d/%m/%Y', + 'Start date' => 'Tarikh mula', + 'Time estimated' => 'Anggaran masa', + 'There is nothing assigned to you.' => 'Tidak ada yang diberikan kepada anda.', + 'My tasks' => 'Tugas saya', + 'Activity stream' => 'Arus aktifitas', + 'Dashboard' => 'Dasbor', + 'Confirmation' => 'Konfirmasi', + 'Allow everybody to access to this project' => 'Memungkinkan semua orang untuk mengakses projek ini', + 'Everybody have access to this project.' => 'Semua orang mendapat akses untuk projek ini.', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Buat komentar dari pemasok eksternal', + 'Project management' => 'Manajemen projek', + 'My projects' => 'projek saya', + 'Columns' => 'Kolom', + 'Task' => 'Tugas', + 'Your are not member of any project.' => 'Anda bukan anggota dari setiap projek.', + 'Percentage' => 'Persentasi', + 'Number of tasks' => 'Jumlah dari tugas', + 'Task distribution' => 'Pembagian tugas', + 'Reportings' => 'Pelaporan', + 'Task repartition for "%s"' => 'Pembagian tugas untuk « %s »', + 'Analytics' => 'Analitis', + 'Subtask' => 'Subtugas', + 'My subtasks' => 'Subtugas saya', + 'User repartition' => 'Partisi ulang pengguna', + 'User repartition for "%s"' => 'Partisi ulang pengguna untuk « %s »', + 'Clone this project' => 'Gandakan projek ini', + 'Column removed successfully.' => 'Kolom berhasil dihapus.', + 'Not enough data to show the graph.' => 'Tidak cukup data untuk menampilkan grafik.', + 'Previous' => 'Sebelumnya', + 'The id must be an integer' => 'Id harus integer', + 'The project id must be an integer' => 'Id projek harus integer', + 'The status must be an integer' => 'Status harus integer', + 'The subtask id is required' => 'Id subtugas diperlukan', + 'The subtask id must be an integer' => 'Id subtugas harus integer', + 'The task id is required' => 'Id tugas diperlukan', + 'The task id must be an integer' => 'Id tugas harus integer', + 'The user id must be an integer' => 'Id user harus integer', + 'This value is required' => 'Nilai ini diperlukan', + 'This value must be numeric' => 'Nilai ini harus angka', + 'Unable to create this task.' => 'Tidak dapat membuat tugas ini', + 'Cumulative flow diagram' => 'Diagram alir kumulatif', + 'Cumulative flow diagram for "%s"' => 'Diagram alir kumulatif untuk « %s »', + 'Daily project summary' => 'Ringkasan projek harian', + 'Daily project summary export' => 'Ekspot ringkasan projek harian', + 'Daily project summary export for "%s"' => 'Ekspor ringkasan projek harian untuk « %s »', + 'Exports' => 'Ekspor', + 'This export contains the number of tasks per column grouped per day.' => 'Ekspor ini berisi jumlah dari tugas per kolom dikelompokan perhari.', + 'Nothing to preview...' => 'Tiada yang dapat diintai...', + 'Preview' => 'Intai', + 'Write' => 'Tulis', + 'Active swimlanes' => 'Swimlanes aktif', + 'Add a new swimlane' => 'Tambah swimlane baharu', + 'Change default swimlane' => 'Tukar piawai swimlane', + 'Default swimlane' => 'Piawai swimlane', + 'Do you really want to remove this swimlane: "%s"?' => 'Anda yakin untuk menghapus swimlane ini : « %s » ?', + 'Inactive swimlanes' => 'Swimlanes tidak aktif', + 'Remove a swimlane' => 'Padam swimlane', + 'Rename' => 'Namakan semula', + 'Show default swimlane' => 'Tampilkan piawai swimlane', + 'Swimlane modification for the project "%s"' => 'Modifikasi swimlane untuk projek « %s »', + 'Swimlane not found.' => 'Swimlane tidak ditemui.', + 'Swimlane removed successfully.' => 'Swimlane telah dipadamkan.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane telah dikemaskini.', + 'The default swimlane have been updated successfully.' => 'Standar swimlane berhasil diperbaharui.', + 'Unable to create your swimlane.' => 'Tidak dapat membuat swimlane anda.', + 'Unable to remove this swimlane.' => 'Tidak dapat menghapus swimlane ini.', + 'Unable to update this swimlane.' => 'Tidak dapat memperbaharui swimlane ini.', + 'Your swimlane have been created successfully.' => 'Swimlane anda berhasil dibuat.', + 'Example: "Bug, Feature Request, Improvement"' => 'Contoh: « Insiden, Permintaan Ciri, Pembaikan »', + 'Default categories for new projects (Comma-separated)' => 'Piawaian kategori untuk projek baru (asingkan guna koma)', + 'Integrations' => 'Integrasi', + 'Integration with third-party services' => 'Integrasi dengan khidmat pihak ketiga', + 'Subtask Id' => 'Id Subtugas', + 'Subtasks' => 'Subtugas', + 'Subtasks Export' => 'Ekspot Subtugas', + 'Subtasks exportation for "%s"' => 'Ekspor subtugas untuk « %s »', + 'Task Title' => 'Judul Tugas', + 'Untitled' => 'Tanpa nama', + 'Application default' => 'Aplikasi Piawaian', + 'Language:' => 'Bahasa:', + 'Timezone:' => 'Zon masa:', + 'All columns' => 'Semua kolom', + 'Calendar' => 'Kalender', + 'Next' => 'Selanjutnya', + '#%d' => 'n°%d', + 'All swimlanes' => 'Semua swimlane', + 'All colors' => 'Semua warna', + 'Moved to column %s' => 'Pindah ke kolom %s', + 'Change description' => 'Ubah keterangan', + 'User dashboard' => 'Papan Kenyataan pengguna', + 'Allow only one subtask in progress at the same time for a user' => 'Izinkan hanya satu subtugas dalam proses secara bersamaan untuk satu pengguna', + 'Edit column "%s"' => 'Modifikasi kolom « %s »', + 'Select the new status of the subtask: "%s"' => 'Pilih status baru untuk subtugas : « %s »', + 'Subtask timesheet' => 'Subtugas absen', + 'There is nothing to show.' => 'Tidak ada yang dapat diperlihatkan.', + 'Time Tracking' => 'Pelacakan waktu', + 'You already have one subtask in progress' => 'Anda sudah ada satu subtugas dalam proses', + 'Which parts of the project do you want to duplicate?' => 'Bagian dalam projek mana yang ingin anda duplikasi?', + 'Disallow login form' => 'Larang formulir masuk', + 'Start' => 'Mula', + 'End' => 'Selesai', + 'Task age in days' => 'Usia tugas dalam bentuk harian', + 'Days in this column' => 'Hari dalam kolom ini', + '%dd' => '%dj', + 'Add a link' => 'Menambahkan pautan', + 'Add a new link' => 'Tambah Pautan baru', + 'Do you really want to remove this link: "%s"?' => 'Anda yakin akan menghapus Pautan ini : « %s » ?', + 'Do you really want to remove this link with task #%d?' => 'Anda yakin akan menghapus Pautan ini dengan tugas n°%d ?', + 'Field required' => 'Medan diperlukan', + 'Link added successfully.' => 'Pautan berhasil ditambahkan.', + 'Link updated successfully.' => 'Pautan berhasil diperbaharui.', + 'Link removed successfully.' => 'Pautan berhasil dihapus.', + 'Link labels' => 'Label Pautan', + 'Link modification' => 'Modifikasi Pautan', + 'Links' => 'Pautan', + 'Link settings' => 'Pengaturan Pautan', + 'Opposite label' => 'Label berlawanan', + 'Remove a link' => 'Hapus Pautan', + 'Task\'s links' => 'Pautan tugas', + 'The labels must be different' => 'Label harus berbeda', + 'There is no link.' => 'Tidak ada Pautan.', + 'This label must be unique' => 'Label ini harus unik', + 'Unable to create your link.' => 'Tidak dapat membuat Pautan anda.', + 'Unable to update your link.' => 'Tidak dapat memperbaharui Pautan anda.', + 'Unable to remove this link.' => 'Tidak dapat menghapus Pautan ini.', + 'relates to' => 'berhubungan dengan', + 'blocks' => 'blok', + 'is blocked by' => 'diblokir oleh', + 'duplicates' => 'duplikat', + 'is duplicated by' => 'diduplikasi oleh', + 'is a child of' => 'anak dari', + 'is a parent of' => 'orant tua dari', + 'targets milestone' => 'milestone target', + 'is a milestone of' => 'adalah milestone dari', + 'fixes' => 'perbaikan', + 'is fixed by' => 'diperbaiki oleh', + 'This task' => 'Tugas ini', + '<1h' => '<1h', + '%dh' => '%dh', + '%b %e' => '%e %b', + 'Expand tasks' => 'Perluas tugas', + 'Collapse tasks' => 'Lipat tugas', + 'Expand/collapse tasks' => 'Perluas/lipat tugas', + 'Close dialog box' => 'Tutup kotak dialog', + 'Submit a form' => 'Submit formulir', + 'Board view' => 'Table halaman', + 'Keyboard shortcuts' => 'pintas keyboard', + 'Open board switcher' => 'Buka table switcher', + 'Application' => 'Aplikasi', + 'since %B %e, %Y at %k:%M %p' => 'sejak %d/%m/%Y à %H:%M', + 'Compact view' => 'Tampilan kompak', + 'Horizontal scrolling' => 'Horisontal bergulir', + 'Compact/wide view' => 'Beralih antara tampilan kompak dan diperluas', + 'No results match:' => 'Tidak ada hasil :', + 'Currency' => 'Mata uang', + 'Files' => 'Arsip', + 'Images' => 'Gambar', + 'Private project' => 'projek pribadi', + 'AUD - Australian Dollar' => 'AUD - Dollar Australia', + 'CAD - Canadian Dollar' => 'CAD - Dollar Kanada', + 'CHF - Swiss Francs' => 'CHF - Swiss Prancis', + 'Custom Stylesheet' => 'Kustomisasi Stylesheet', + 'download' => 'unduh', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Poundsterling inggris', + 'INR - Indian Rupee' => 'INR - Rupe India', + 'JPY - Japanese Yen' => 'JPY - Yen Jepang', + 'NZD - New Zealand Dollar' => 'NZD - Dollar Selandia baru', + 'RSD - Serbian dinar' => 'RSD - Dinar Serbia', + 'USD - US Dollar' => 'USD - Dollar Amerika', + 'Destination column' => 'Kolom tujuan', + 'Move the task to another column when assigned to a user' => 'Pindahkan tugas ke kolom lain ketika ditugaskan ke pengguna', + 'Move the task to another column when assignee is cleared' => 'Pindahkan tugas ke kolom lain ketika orang yang ditugaskan dibersihkan', + 'Source column' => 'Sumber kolom', + 'Transitions' => 'Transisi', + 'Executer' => 'Eksekusi', + 'Time spent in the column' => 'Waktu yang dihabiskan dalam kolom', + 'Task transitions' => 'Transisi tugas', + 'Task transitions export' => 'Ekspor transisi tugas', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Laporan ini berisi semua kolom yang pindah untuk setiap tugas dengan tanggal, pengguna dan waktu yang dihabiskan untuk setiap transisi.', + 'Currency rates' => 'Nilai tukar mata uang', + 'Rate' => 'Tarif', + 'Change reference currency' => 'Mengubah referensi mata uang', + 'Add a new currency rate' => 'Tambahkan nilai tukar mata uang baru', + 'Reference currency' => 'Referensi mata uang', + 'The currency rate have been added successfully.' => 'Nilai tukar mata uang berhasil ditambahkan.', + 'Unable to add this currency rate.' => 'Tidak dapat menambahkan nilai tukar mata uang', + 'Webhook URL' => 'URL webhook', + '%s remove the assignee of the task %s' => '%s menghapus penugasan dari tugas %s', + 'Enable Gravatar images' => 'Mengaktifkan gambar Gravatar', + 'Information' => 'Informasi', + 'Check two factor authentication code' => 'Cek dua faktor kode otentifikasi', + 'The two factor authentication code is not valid.' => 'Kode dua faktor kode otentifikasi tidak valid.', + 'The two factor authentication code is valid.' => 'Kode dua faktor kode otentifikasi valid.', + 'Code' => 'Kode', + 'Two factor authentication' => 'Dua faktor otentifikasi', + 'This QR code contains the key URI: ' => 'kode QR ini mengandung kunci URI : ', + 'Check my code' => 'Memeriksa kode saya', + 'Secret key: ' => 'Kunci rahasia : ', + 'Test your device' => 'Menguji perangkat anda', + 'Assign a color when the task is moved to a specific column' => 'Menetapkan warna ketika tugas tersebut dipindahkan ke kolom tertentu', + '%s via Kanboard' => '%s via Kanboard', + 'uploaded by: %s' => 'diunggah oleh %s', + 'uploaded on: %s' => 'diunggah pada %s', + 'size: %s' => 'ukuran : %s', + 'Burndown chart for "%s"' => 'Grafik Burndown untku « %s »', + 'Burndown chart' => 'Grafik Burndown', + 'This chart show the task complexity over the time (Work Remaining).' => 'Grafik ini menunjukkan kompleksitas tugas dari waktu ke waktu (Sisa Pekerjaan).', + 'Screenshot taken %s' => 'Screenshot diambil %s', + 'Add a screenshot' => 'Tambah screenshot', + 'Take a screenshot and press CTRL+V or ?+V to paste here.' => 'Mengambil screenshot dan tekan CTRL + V atau ? + V untuk paste di sini.', + 'Screenshot uploaded successfully.' => 'Screenshot berhasil diunggah.', + 'SEK - Swedish Krona' => 'SEK - Krona Swedia', + 'The project identifier is an optional alphanumeric code used to identify your project.' => 'Identifier projek adalah kode alfanumerik opsional digunakan untuk mengidentifikasi projek Anda.', + 'Identifier' => 'Identifier', + 'Disable two factor authentication' => 'Matikan dua faktor otentifikasi', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Apakah anda yakin akan mematikan dua faktor otentifikasi untuk pengguna ini : « %s » ?', + 'Edit link' => 'Modifikasi Pautan', + 'Start to type task title...' => 'Mulai mengetik judul tugas...', + 'A task cannot be linked to itself' => 'Sebuah tugas tidak dapat dikaitkan dengan dirinya sendiri', + 'The exact same link already exists' => 'Pautan yang sama persis sudah ada', + 'Recurrent task is scheduled to be generated' => 'Tugas berulang dijadwalkan akan dihasilkan', + 'Recurring information' => 'Informasi berulang', + 'Score' => 'Skor', + 'The identifier must be unique' => 'Identifier harus unik', + 'This linked task id doesn\'t exists' => 'Id tugas terkait tidak ada', + 'This value must be alphanumeric' => 'Nilai harus alfanumerik', + 'Edit recurrence' => 'Modifikasi pengulangan', + 'Generate recurrent task' => 'Menghasilkan tugas berulang', + 'Trigger to generate recurrent task' => 'Memicu untuk menghasilkan tugas berulang', + 'Factor to calculate new due date' => 'Faktor untuk menghitung tanggal jatuh tempo baru', + 'Timeframe to calculate new due date' => 'Jangka waktu untuk menghitung tanggal jatuh tempo baru', + 'Base date to calculate new due date' => 'Tanggal dasar untuk menghitung tanggal jatuh tempo baru', + 'Action date' => 'Tanggal aksi', + 'Base date to calculate new due date: ' => 'Tanggal dasar untuk menghitung tanggal jatuh tempo baru: ', + 'This task has created this child task: ' => 'Tugas ini telah menciptakan tugas anak ini: ', + 'Day(s)' => 'Hari', + 'Existing due date' => 'Batas waktu yang ada', + 'Factor to calculate new due date: ' => 'Faktor untuk menghitung tanggal jatuh tempo baru: ', + 'Month(s)' => 'Bulan', + 'Recurrence' => 'Pengulangan', + 'This task has been created by: ' => 'Tugas ini telah dibuat oleh:', + 'Recurrent task has been generated:' => 'Tugas berulang telah dihasilkan:', + 'Timeframe to calculate new due date: ' => 'Jangka waktu untuk menghitung tanggal jatuh tempo baru: ', + 'Trigger to generate recurrent task: ' => 'Pemicu untuk menghasilkan tugas berulang: ', + 'When task is closed' => 'Ketika tugas ditutup', + 'When task is moved from first column' => 'Ketika tugas dipindahkan dari kolom pertama', + 'When task is moved to last column' => 'Ketika tugas dipindahkan ke kolom terakhir', + 'Year(s)' => 'Tahun', + 'Calendar settings' => 'Pengaturan kalender', + 'Project calendar view' => 'Tampilan kalender projek', + 'Project settings' => 'Pengaturan projek', + 'Show subtasks based on the time tracking' => 'Tampilkan subtugas berdasarkan pelacakan waktu', + 'Show tasks based on the creation date' => 'Tampilkan tugas berdasarkan tanggal pembuatan', + 'Show tasks based on the start date' => 'Tampilkan tugas berdasarkan tanggal mulai', + 'Subtasks time tracking' => 'Pelacakan waktu subtgas', + 'User calendar view' => 'Pengguna tampilan kalender', + 'Automatically update the start date' => 'Otomatikkan pengemaskinian tanggal', + 'iCal feed' => 'iCal feed', + 'Preferences' => 'Keutamaan', + 'Security' => 'Keamanan', + 'Two factor authentication disabled' => 'Otentifikasi dua faktor dimatikan', + 'Two factor authentication enabled' => 'Otentifikasi dua faktor dihidupkan', + 'Unable to update this user.' => 'Tidak dapat memperbarui pengguna ini.', + 'There is no user management for private projects.' => 'Tidak ada manajemen pengguna untuk projek-projek pribadi.', + 'User that will receive the email' => 'Pengguna yang akan menerima email', + 'Email subject' => 'Subjek Emel', + 'Date' => 'Tanggal', + 'Add a comment log when moving the task between columns' => 'Menambahkan log komentar ketika memindahkan tugas antara kolom', + 'Move the task to another column when the category is changed' => 'Pindahkan tugas ke kolom lain ketika kategori berubah', + 'Send a task by email to someone' => 'Kirim tugas melalui email ke seseorang', + 'Reopen a task' => 'Membuka kembali tugas', + 'Column change' => 'Kolom berubah', + 'Position change' => 'Posisi berubah', + 'Swimlane change' => 'Swimlane berubah', + 'Assignee change' => 'Penerima berubah', + '[%s] Overdue tasks' => '[%s] Tugas terlambat', + 'Notification' => 'Pemberitahuan', + '%s moved the task #%d to the first swimlane' => '%s memindahkan tugas n°%d ke swimlane pertama', + '%s moved the task #%d to the swimlane "%s"' => '%s memindahkan tugas n°%d ke swimlane « %s »', + 'Swimlane' => 'Swimlane', + 'Gravatar' => 'Gravatar', + '%s moved the task %s to the first swimlane' => '%s memindahkan tugas %s ke swimlane pertama', + '%s moved the task %s to the swimlane "%s"' => '%s memindahkan tugas %s ke swimlane « %s »', + 'This report contains all subtasks information for the given date range.' => 'Laporan ini berisi semua informasi subtugas untuk rentang tanggal tertentu.', + 'This report contains all tasks information for the given date range.' => 'Laporan ini berisi semua informasi tugas untuk rentang tanggal tertentu.', + 'Project activities for %s' => 'Aktifitas projek untuk « %s »', + 'view the board on Kanboard' => 'lihat papan di Kanboard', + 'The task have been moved to the first swimlane' => 'Tugas telah dipindahkan ke swimlane pertama', + 'The task have been moved to another swimlane:' => 'Tugas telah dipindahkan ke swimlane lain:', + 'Overdue tasks for the project "%s"' => 'Tugas terlambat untuk projek « %s »', + 'New title: %s' => 'Judul baru : %s', + 'The task is not assigned anymore' => 'Tugas tidak ditugaskan lagi', + 'New assignee: %s' => 'Penerima baru : %s', + 'There is no category now' => 'Tidak ada kategori untuk sekarang', + 'New category: %s' => 'Kategori baru : %s', + 'New color: %s' => 'Warna baru : %s', + 'New complexity: %d' => 'Kompleksitas baru : %d', + 'The due date have been removed' => 'Tanggal jatuh tempo telah dihapus', + 'There is no description anymore' => 'Tidak ada deskripsi lagi', + 'Recurrence settings have been modified' => 'Pengaturan pengulangan telah dimodifikasi', + 'Time spent changed: %sh' => 'Waktu yang dihabiskan berubah : %sh', + 'Time estimated changed: %sh' => 'Perkiraan waktu berubah : %sh', + 'The field "%s" have been updated' => 'Field « %s » telah diperbaharui', + 'The description have been modified' => 'Deskripsi telah dimodifikasi', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Apakah anda yakin akan menutup tugas « %s » beserta semua sub-tugasnya ?', + 'Swimlane: %s' => 'Swimlane : %s', + 'I want to receive notifications for:' => 'Saya ingin menerima pemberitahuan untuk :', + 'All tasks' => 'Semua tugas', + 'Only for tasks assigned to me' => 'Hanya untuk tugas yang ditugaskan ke saya', + 'Only for tasks created by me' => 'Hanya untuk tugas yang dibuat oleh saya', + 'Only for tasks created by me and assigned to me' => 'Hanya untuk tugas yang dibuat oleh saya dan ditugaskan ke saya', + '%A' => '%A', + '%b %e, %Y, %k:%M %p' => '%d/%m/%Y %H:%M', + 'New due date: %B %e, %Y' => 'Tanggal jatuh tempo baru : %d/%m/%Y', + 'Start date changed: %B %e, %Y' => 'Tanggal mulai berubah : %d/%m/%Y', + '%k:%M %p' => '%H:%M', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Total untuk semua kolom', + 'You need at least 2 days of data to show the chart.' => 'Anda memerlukan setidaknya 2 hari dari data yang menunjukkan grafik.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Hentikan timer', + 'Start timer' => 'Mulai timer', + 'Add project member' => 'Tambahkan anggota projek', + 'Enable notifications' => 'Aktifkan pemberitahuan', + 'My activity stream' => 'Aliran kegiatan saya', + 'My calendar' => 'Kalender saya', + 'Search tasks' => 'Cari tugas', + 'Back to the calendar' => 'Kembali ke kalender', + 'Filters' => 'Filter', + 'Reset filters' => 'Reset ulang filter', + 'My tasks due tomorrow' => 'Tugas saya yang berakhir besok', + 'Tasks due today' => 'Tugas yang berakhir hari ini', + 'Tasks due tomorrow' => 'Tugas yang berakhir besok', + 'Tasks due yesterday' => 'Tugas yang berakhir kemarin', + 'Closed tasks' => 'Tugas yang ditutup', + 'Open tasks' => 'Buka Tugas', + 'Not assigned' => 'Tidak ditugaskan', + 'View advanced search syntax' => 'Lihat sintaks pencarian lanjutan', + 'Overview' => 'Ikhtisar', + '%b %e %Y' => '%b %e %Y', + 'Board/Calendar/List view' => 'Tampilan Papan/Kalender/Daftar', + 'Switch to the board view' => 'Beralih ke tampilan papan', + 'Switch to the calendar view' => 'Beralih ke tampilan kalender', + 'Switch to the list view' => 'Beralih ke tampilan daftar', + 'Go to the search/filter box' => 'Pergi ke kotak pencarian/filter', + 'There is no activity yet.' => 'Tidak ada aktifitas saat ini.', + 'No tasks found.' => 'Tidak ada tugas yang ditemukan.', + 'Keyboard shortcut: "%s"' => 'Keyboard shortcut : « %s »', + 'List' => 'Daftar', + 'Filter' => 'Filter', + 'Advanced search' => 'Pencarian lanjutan', + 'Example of query: ' => 'Contoh dari query : ', + 'Search by project: ' => 'Pencarian berdasarkan projek : ', + 'Search by column: ' => 'Pencarian berdasarkan kolom : ', + 'Search by assignee: ' => 'Pencarian berdasarkan penerima : ', + 'Search by color: ' => 'Pencarian berdasarkan warna : ', + 'Search by category: ' => 'Pencarian berdasarkan kategori : ', + 'Search by description: ' => 'Pencarian berdasarkan deskripsi : ', + 'Search by due date: ' => 'Pencarian berdasarkan tanggal jatuh tempo : ', + 'Lead and Cycle time for "%s"' => 'Memimpin dan Siklus waktu untuk « %s »', + 'Average time spent into each column for "%s"' => 'Rata-rata waktu yang dihabiskan dalam setiap kolom untuk « %s »', + 'Average time spent into each column' => 'Rata-rata waktu yang dihabiskan dalam setiap kolom', + 'Average time spent' => 'Rata-rata waktu yang dihabiskan', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Grafik ini menunjukkan rata-rata waktu yang dihabiskan dalam setiap kolom untuk %d tugas.', + 'Average Lead and Cycle time' => 'Rata-rata Memimpin dan Siklus waktu', + 'Average lead time: ' => 'Rata-rata waktu pimpinan : ', + 'Average cycle time: ' => 'Rata-rata siklus waktu : ', + 'Cycle Time' => 'Siklus Waktu', + 'Lead Time' => 'Lead Time', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Grafik ini menunjukkan memimpin rata-rata dan waktu siklus untuk %d tugas terakhir dari waktu ke waktu.', + 'Average time into each column' => 'Rata-rata waktu ke setiap kolom', + 'Lead and cycle time' => 'Lead dan siklus waktu', + 'Google Authentication' => 'Google Otentifikasi', + 'Help on Google authentication' => 'Bantuan pada otentifikasi Google', + 'Github Authentication' => 'Otentifikasi Github', + 'Help on Github authentication' => 'Bantuan pada otentifikasi Github', + 'Lead time: ' => 'Lead time : ', + 'Cycle time: ' => 'Siklus waktu : ', + 'Time spent into each column' => 'Waktu yang dihabiskan di setiap kolom', + 'The lead time is the duration between the task creation and the completion.' => 'Lead time adalah durasi antara pembuatan tugas dan penyelesaian.', + 'The cycle time is the duration between the start date and the completion.' => 'Siklus waktu adalah durasi antara tanggal mulai dan tanggal penyelesaian.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Jika tugas tidak ditutup waktu saat ini yang digunakan sebagai pengganti tanggal penyelesaian.', + 'Set automatically the start date' => 'Secara otomatis mengatur tanggal mulai', + 'Edit Authentication' => 'Modifikasi Otentifikasi', + 'Google Id' => 'Id Google', + 'Github Id' => 'Id Github', + 'Remote user' => 'Pengguna jauh', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Pengguna jauh tidak menyimpan kata laluan mereka dalam basis data Kanboard, contoh: Akaun LDAP, Google dan Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Jika anda mencentang kotak "Larang formulir login", kredensial masuk ke formulis login akan diabaikan.', + 'New remote user' => 'Pengguna baru jauh', + 'New local user' => 'Pengguna baru lokal', + 'Default task color' => 'Standar warna tugas', + 'Hide sidebar' => 'Sembunyikan sidebar', + 'Expand sidebar' => 'Perluas sidebar', + 'This feature does not work with all browsers.' => 'Ciri ini tidak dapat digunakan pada semua browsers', + 'There is no destination project available.' => 'Tiada destinasi projek yang tersedia.', + 'Trigger automatically subtask time tracking' => 'Picu pengesanan subtugas secara otomatik', + 'Include closed tasks in the cumulative flow diagram' => 'Termasuk tugas yang ditutup pada diagram aliran kumulatif', + 'Current swimlane: %s' => 'Swimlane saat ini : %s', + 'Current column: %s' => 'Kolom saat ini : %s', + 'Current category: %s' => 'Kategori saat ini : %s', + 'no category' => 'tiada kategori', + 'Current assignee: %s' => 'Saat ini ditugaskan pada: %s', + 'not assigned' => 'Belum ditugaskan', + 'Author:' => 'Penulis:', + 'contributors' => 'Penggiat', + 'License:' => 'Lesen:', + 'License' => 'Lesen', + 'Enter the text below' => 'Masukkan teks di bawah', + 'Gantt chart for %s' => 'Carta Gantt untuk %s', + 'Sort by position' => 'Urutkan berdasarkan posisi', + 'Sort by date' => 'Urutkan berdasarkan tanggal', + 'Add task' => 'Tambah tugas', + 'Start date:' => 'Tanggal mulai:', + 'Due date:' => 'Batas waktu:', + 'There is no start date or due date for this task.' => 'Tiada tanggal mulai dan batas waktu untuk tugas ini.', + 'Moving or resizing a task will change the start and due date of the task.' => 'Memindahkan atau mengubah ukuran tugas anda akan mengubah tanggal mulai dan batas waktu dari tugas ini.', + 'There is no task in your project.' => 'Tiada tugas didalam projek anda.', + 'Gantt chart' => 'Carta Gantt', + 'People who are project managers' => 'Orang-orang yang menjadi pengurus projek', + 'People who are project members' => 'Orang-orang yang menjadi anggota projek', + 'NOK - Norwegian Krone' => 'NOK - Krone Norwegia', + 'Show this column' => 'Perlihatkan kolom ini', + 'Hide this column' => 'Sembunyikan kolom ini', + 'open file' => 'buka fail', + 'End date' => 'Waktu berakhir', + 'Users overview' => 'Ikhtisar pengguna', + 'Managers' => 'Pengurus', + 'Members' => 'Anggota', + 'Shared project' => 'projek bersama', + 'Project managers' => 'Pengurus projek', + 'Gantt chart for all projects' => 'Carta Gantt untuk kesemua projek', + 'Projects list' => 'Senarai projek', + 'Gantt chart for this project' => 'Carta Gantt untuk projek ini', + 'Project board' => 'Papan projek', + 'End date:' => 'Waktu berakhir :', + 'There is no start date or end date for this project.' => 'Tidak ada waktu mula atau waktu berakhir pada projek ini', + 'Projects Gantt chart' => 'projekkan carta Gantt', + 'Start date: %s' => 'Waktu mulai: %s', + 'End date: %s' => 'Waktu berakhir: %s', + 'Link type' => 'Jenis pautan', + 'Change task color when using a specific task link' => 'Rubah warna tugas ketika menggunakan Pautan tugas yang spesifik', + 'Task link creation or modification' => 'Pautan tugas pada penciptaan atau penyuntingan', + 'Login with my Gitlab Account' => 'Masuk menggunakan Akaun Gitlab saya', + 'Milestone' => 'Batu Tanda', + 'Gitlab Authentication' => 'Otentifikasi Gitlab', + 'Help on Gitlab authentication' => 'Bantuan pada otentifikasi Gitlab', + 'Gitlab Id' => 'Id Gitlab', + 'Gitlab Account' => 'Akaun Gitlab', + 'Link my Gitlab Account' => 'Hubungkan akaun Gitlab saya', + 'Unlink my Gitlab Account' => 'Putuskan akaun Gitlab saya', + 'Documentation: %s' => 'Dokumentasi : %s', + 'Switch to the Gantt chart view' => 'Beralih ke tampilan Carta Gantt', + 'Reset the search/filter box' => 'Tetap semula pencarian/saringan', + 'Documentation' => 'Dokumentasi', + 'Table of contents' => 'Isi kandungan', + 'Gantt' => 'Gantt', + // 'Author' => '', + // 'Version' => '', + // 'Plugins' => '', + // 'There is no plugin loaded.' => '', + // 'Set maximum column height' => '', + // 'Remove maximum column height' => '', + // 'My notifications' => '', + // 'Custom filters' => '', + // 'Your custom filter have been created successfully.' => '', + // 'Unable to create your custom filter.' => '', + // 'Custom filter removed successfully.' => '', + // 'Unable to remove this custom filter.' => '', + // 'Edit custom filter' => '', + // 'Your custom filter have been updated successfully.' => '', + // 'Unable to update custom filter.' => '', + // 'Web' => '', + // 'New attachment on task #%d: %s' => '', + // 'New comment on task #%d' => '', + // 'Comment updated on task #%d' => '', + // 'New subtask on task #%d' => '', + // 'Subtask updated on task #%d' => '', + // 'New task #%d: %s' => '', + // 'Task updated #%d' => '', + // 'Task #%d closed' => '', + // 'Task #%d opened' => '', + // 'Column changed for task #%d' => '', + // 'New position for task #%d' => '', + // 'Swimlane changed for task #%d' => '', + // 'Assignee changed on task #%d' => '', + // '%d overdue tasks' => '', + // 'Task #%d is overdue' => '', + // 'No new notifications.' => '', + // 'Mark all as read' => '', + // 'Mark as read' => '', + // 'Total number of tasks in this column across all swimlanes' => '', + // 'Collapse swimlane' => '', + // 'Expand swimlane' => '', + // 'Add a new filter' => '', + // 'Share with all project members' => '', + // 'Shared' => '', + // 'Owner' => '', + // 'Unread notifications' => '', + // 'My filters' => '', + // 'Notification methods:' => '', + // 'Import tasks from CSV file' => '', + // 'Unable to read your file' => '', + // '%d task(s) have been imported successfully.' => '', + // 'Nothing have been imported!' => '', + // 'Import users from CSV file' => '', + // '%d user(s) have been imported successfully.' => '', + // 'Comma' => '', + // 'Semi-colon' => '', + // 'Tab' => '', + // 'Vertical bar' => '', + // 'Double Quote' => '', + // 'Single Quote' => '', + // '%s attached a file to the task #%d' => '', + // 'There is no column or swimlane activated in your project!' => '', + // 'Append filter (instead of replacement)' => '', + // 'Append/Replace' => '', + // 'Append' => '', + // 'Replace' => '', + // 'There is no notification method registered.' => '', + // 'Import' => '', + // 'change sorting' => '', + // 'Tasks Importation' => '', + // 'Delimiter' => '', + // 'Enclosure' => '', + // 'CSV File' => '', + // 'Instructions' => '', + // 'Your file must use the predefined CSV format' => '', + // 'Your file must be encoded in UTF-8' => '', + // 'The first row must be the header' => '', + // 'Duplicates are not verified for you' => '', + // 'The due date must use the ISO format: YYYY-MM-DD' => '', + // 'Download CSV template' => '', + // 'No external integration registered.' => '', + // 'Duplicates are not imported' => '', + // 'Usernames must be lowercase and unique' => '', + // 'Passwords will be encrypted if present' => '', + // '%s attached a new file to the task %s' => '', + // 'Assign automatically a category based on a link' => '', + // 'BAM - Konvertible Mark' => '', + // 'Assignee Username' => '', + // 'Assignee Name' => '', + // 'Groups' => '', + // 'Members of %s' => '', + // 'New group' => '', + // 'Group created successfully.' => '', + // 'Unable to create your group.' => '', + // 'Edit group' => '', + // 'Group updated successfully.' => '', + // 'Unable to update your group.' => '', + // 'Add group member to "%s"' => '', + // 'Group member added successfully.' => '', + // 'Unable to add group member.' => '', + // 'Remove user from group "%s"' => '', + // 'User removed successfully from this group.' => '', + // 'Unable to remove this user from the group.' => '', + // 'Remove group' => '', + // 'Group removed successfully.' => '', + // 'Unable to remove this group.' => '', + // 'Project Permissions' => '', + // 'Manager' => '', + // 'Project Manager' => '', + // 'Project Member' => '', + // 'Project Viewer' => '', + // 'Your account is locked for %d minutes' => '', + // 'Invalid captcha' => '', + // 'The name must be unique' => '', + // 'View all groups' => '', + // 'View group members' => '', + // 'There is no user available.' => '', + // 'Do you really want to remove the user "%s" from the group "%s"?' => '', + // 'There is no group.' => '', + // 'External Id' => '', + // 'Add group member' => '', + // 'Do you really want to remove this group: "%s"?' => '', + // 'There is no user in this group.' => '', + // 'Remove this user' => '', + // 'Permissions' => '', + // 'Allowed Users' => '', + // 'No user have been allowed specifically.' => '', + // 'Role' => '', + // 'Enter user name...' => '', + // 'Allowed Groups' => '', + // 'No group have been allowed specifically.' => '', + // 'Group' => '', + // 'Group Name' => '', + // 'Enter group name...' => '', + 'Role:' => 'Peranan', + 'Project members' => 'Anggota projek', + // 'Compare hours for "%s"' => '', + // '%s mentioned you in the task #%d' => '', + // '%s mentioned you in a comment on the task #%d' => '', + // 'You were mentioned in the task #%d' => '', + // 'You were mentioned in a comment on the task #%d' => '', + // 'Mentioned' => '', + // 'Compare Estimated Time vs Actual Time' => '', + // 'Estimated hours: ' => '', + // 'Actual hours: ' => '', + // 'Hours Spent' => '', + // 'Hours Estimated' => '', + // 'Estimated Time' => '', + // 'Actual Time' => '', + // 'Estimated vs actual time' => '', + // 'RUB - Russian Ruble' => '', + // 'Assign the task to the person who does the action when the column is changed' => '', + // 'Close a task in a specific column' => '', + // 'Time-based One-time Password Algorithm' => '', + // 'Two-Factor Provider: ' => '', + // 'Disable two-factor authentication' => '', + // 'Enable two-factor authentication' => '', + // 'There is no integration registered at the moment.' => '', + // 'Password Reset for Kanboard' => '', + // 'Forgot password?' => '', + // 'Enable "Forget Password"' => '', + // 'Password Reset' => '', + // 'New password' => '', + // 'Change Password' => '', + // 'To reset your password click on this link:' => '', + // 'Last Password Reset' => '', + //'The password has never been reinitialized.' => 'Kata laluan tidak pernah ', + 'Creation' => 'Ciptaan', + 'Expiration' => 'Jangka hayat', + 'Password reset history' => 'Sirah tetap semula kata laluan', +); diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php index b1652dd9..6b387bf2 100644 --- a/app/Locale/pt_PT/translations.php +++ b/app/Locale/pt_PT/translations.php @@ -1064,38 +1064,38 @@ return array( 'Enter group name...' => 'Escreva o nome do Grupo', 'Role:' => 'Função:', 'Project members' => 'Membros do projecto', - // 'Compare hours for "%s"' => '', - // '%s mentioned you in the task #%d' => '', - // '%s mentioned you in a comment on the task #%d' => '', - // 'You were mentioned in the task #%d' => '', - // 'You were mentioned in a comment on the task #%d' => '', - // 'Mentioned' => '', - // 'Compare Estimated Time vs Actual Time' => '', - // 'Estimated hours: ' => '', - // 'Actual hours: ' => '', - // 'Hours Spent' => '', - // 'Hours Estimated' => '', - // 'Estimated Time' => '', - // 'Actual Time' => '', - // 'Estimated vs actual time' => '', - // 'RUB - Russian Ruble' => '', - // 'Assign the task to the person who does the action when the column is changed' => '', - // 'Close a task in a specific column' => '', - // 'Time-based One-time Password Algorithm' => '', - // 'Two-Factor Provider: ' => '', - // 'Disable two-factor authentication' => '', - // 'Enable two-factor authentication' => '', - // 'There is no integration registered at the moment.' => '', - // 'Password Reset for Kanboard' => '', - // 'Forgot password?' => '', - // 'Enable "Forget Password"' => '', - // 'Password Reset' => '', - // 'New password' => '', - // 'Change Password' => '', - // 'To reset your password click on this link:' => '', - // 'Last Password Reset' => '', - // 'The password has never been reinitialized.' => '', - // 'Creation' => '', - // 'Expiration' => '', - // 'Password reset history' => '', + 'Compare hours for "%s"' => 'Comparar horas para "%s"', + '%s mentioned you in the task #%d' => '%s mencionou-te na tarefa #%d', + '%s mentioned you in a comment on the task #%d' => '%s mencionou-te num comentário na tarefa #%d', + 'You were mentioned in the task #%d' => 'Foi mencionado na tarefa #%d', + 'You were mentioned in a comment on the task #%d' => 'Foi mencionado num comentário na tarefa #%d', + 'Mentioned' => 'Mencionado', + 'Compare Estimated Time vs Actual Time' => 'Comparar Tempo Estimado vs Tempo Real', + 'Estimated hours: ' => 'Horas estimadas: ', + 'Actual hours: ' => 'Horas reais: ', + 'Hours Spent' => 'Horas Gastas', + 'Hours Estimated' => 'Horas Estimadas', + 'Estimated Time' => 'Tempo Estimado', + 'Actual Time' => 'Tempo Real', + 'Estimated vs actual time' => 'Tempo estimado vs real', + 'RUB - Russian Ruble' => 'RUB - Rublo Russo', + 'Assign the task to the person who does the action when the column is changed' => 'Assignar a tarefa à pessoa que realiza a acção quando a coluna é alterada', + 'Close a task in a specific column' => 'Fechar tarefa numa coluna especifica', + 'Time-based One-time Password Algorithm' => 'Algoritmo de password para uso único baseado em tempo', + 'Two-Factor Provider: ' => 'Provedor de Dois Passos: ', + 'Disable two-factor authentication' => 'Desactivar autenticação de dois passos', + 'Enable two-factor authentication' => 'Activar autenticação de dois passos', + 'There is no integration registered at the moment.' => 'Não existe nenhuma integração registrada até ao momento.', + 'Password Reset for Kanboard' => 'Redefinir Password para Kanboard', + 'Forgot password?' => 'Esqueceu a password?', + 'Enable "Forget Password"' => 'Activar "Esqueceu a password"', + 'Password Reset' => 'Redefinir a Password', + 'New password' => 'Nova Password', + 'Change Password' => 'Alterar Password', + 'To reset your password click on this link:' => 'Para redefinir a sua password click nesta ligação:', + 'Last Password Reset' => 'Última Redefinição da Password', + 'The password has never been reinitialized.' => 'A password nunca foi redefinida.', + 'Creation' => 'Criação', + 'Expiration' => 'Expiração', + 'Password reset history' => 'Histórico da redefinição da password', ); diff --git a/app/Model/Config.php b/app/Model/Config.php index 0e9311f1..839a2fd4 100644 --- a/app/Model/Config.php +++ b/app/Model/Config.php @@ -80,6 +80,7 @@ class Config extends Setting 'fr_FR' => 'Français', 'it_IT' => 'Italiano', 'hu_HU' => 'Magyar', + 'my_MY' => 'Melayu', 'nl_NL' => 'Nederlands', 'nb_NO' => 'Norsk', 'pl_PL' => 'Polski', diff --git a/app/Model/Project.php b/app/Model/Project.php index 8a949ba6..63077348 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -2,8 +2,6 @@ namespace Kanboard\Model; -use SimpleValidator\Validator; -use SimpleValidator\Validators; use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; @@ -510,71 +508,4 @@ class Project extends Base ->eq('id', $project_id) ->save(array('is_public' => 0, 'token' => '')); } - - /** - * Common validation rules - * - * @access private - * @return array - */ - private function commonValidationRules() - { - return array( - new Validators\Integer('id', t('This value must be an integer')), - new Validators\Integer('is_active', t('This value must be an integer')), - new Validators\Required('name', t('The project name is required')), - new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50), - new Validators\MaxLength('identifier', t('The maximum length is %d characters', 50), 50), - new Validators\MaxLength('start_date', t('The maximum length is %d characters', 10), 10), - new Validators\MaxLength('end_date', t('The maximum length is %d characters', 10), 10), - new Validators\AlphaNumeric('identifier', t('This value must be alphanumeric')) , - new Validators\Unique('identifier', t('The identifier must be unique'), $this->db->getConnection(), self::TABLE), - ); - } - - /** - * Validate project creation - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateCreation(array $values) - { - if (! empty($values['identifier'])) { - $values['identifier'] = strtoupper($values['identifier']); - } - - $v = new Validator($values, $this->commonValidationRules()); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate project modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateModification(array $values) - { - if (! empty($values['identifier'])) { - $values['identifier'] = strtoupper($values['identifier']); - } - - $rules = array( - new Validators\Required('id', t('This value is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } } diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php index 664e41e1..0e039bb3 100644 --- a/app/Model/Subtask.php +++ b/app/Model/Subtask.php @@ -4,11 +4,9 @@ namespace Kanboard\Model; use PicoDb\Database; use Kanboard\Event\SubtaskEvent; -use SimpleValidator\Validator; -use SimpleValidator\Validators; /** - * Subtask model + * Subtask Model * * @package model * @author Frederic Guillot @@ -451,90 +449,4 @@ class Subtask extends Base } }); } - - /** - * Validate creation - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateCreation(array $values) - { - $rules = array( - new Validators\Required('task_id', t('The task id is required')), - new Validators\Required('title', t('The title is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateModification(array $values) - { - $rules = array( - new Validators\Required('id', t('The subtask id is required')), - new Validators\Required('task_id', t('The task id is required')), - new Validators\Required('title', t('The title is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate API modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateApiModification(array $values) - { - $rules = array( - new Validators\Required('id', t('The subtask id is required')), - new Validators\Required('task_id', t('The task id is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Common validation rules - * - * @access private - * @return array - */ - private function commonValidationRules() - { - return array( - new Validators\Integer('id', t('The subtask id must be an integer')), - new Validators\Integer('task_id', t('The task id must be an integer')), - new Validators\MaxLength('title', t('The maximum length is %d characters', 255), 255), - new Validators\Integer('user_id', t('The user id must be an integer')), - new Validators\Integer('status', t('The status must be an integer')), - new Validators\Numeric('time_estimated', t('The time must be a numeric value')), - new Validators\Numeric('time_spent', t('The time must be a numeric value')), - ); - } } diff --git a/app/Model/Swimlane.php b/app/Model/Swimlane.php index df44985a..e5124e8e 100644 --- a/app/Model/Swimlane.php +++ b/app/Model/Swimlane.php @@ -2,9 +2,6 @@ namespace Kanboard\Model; -use SimpleValidator\Validator; -use SimpleValidator\Validators; - /** * Swimlanes * @@ -470,85 +467,4 @@ class Swimlane extends Base return true; } - - /** - * Validate creation - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateCreation(array $values) - { - $rules = array( - new Validators\Required('project_id', t('The project id is required')), - new Validators\Required('name', t('The name is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateModification(array $values) - { - $rules = array( - new Validators\Required('id', t('The id is required')), - new Validators\Required('name', t('The name is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate default swimlane modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateDefaultModification(array $values) - { - $rules = array( - new Validators\Required('id', t('The id is required')), - new Validators\Required('default_swimlane', t('The name is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Common validation rules - * - * @access private - * @return array - */ - private function commonValidationRules() - { - return array( - new Validators\Integer('id', t('The id must be an integer')), - new Validators\Integer('project_id', t('The project id must be an integer')), - new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50) - ); - } } diff --git a/app/Model/TaskLink.php b/app/Model/TaskLink.php index 1ac59203..87aae55e 100644 --- a/app/Model/TaskLink.php +++ b/app/Model/TaskLink.php @@ -2,8 +2,6 @@ namespace Kanboard\Model; -use SimpleValidator\Validator; -use SimpleValidator\Validators; use Kanboard\Event\TaskLinkEvent; /** @@ -261,59 +259,4 @@ class TaskLink extends Base return true; } - - /** - * Common validation rules - * - * @access private - * @return array - */ - private function commonValidationRules() - { - return array( - new Validators\Required('task_id', t('Field required')), - new Validators\Required('opposite_task_id', t('Field required')), - new Validators\Required('link_id', t('Field required')), - new Validators\NotEquals('opposite_task_id', 'task_id', t('A task cannot be linked to itself')), - new Validators\Exists('opposite_task_id', t('This linked task id doesn\'t exists'), $this->db->getConnection(), Task::TABLE, 'id') - ); - } - - /** - * Validate creation - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateCreation(array $values) - { - $v = new Validator($values, $this->commonValidationRules()); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateModification(array $values) - { - $rules = array( - new Validators\Required('id', t('Field required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } } diff --git a/app/Model/User.php b/app/Model/User.php index 50e9b310..ac0e7491 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -3,9 +3,6 @@ namespace Kanboard\Model; use PicoDb\Database; -use SimpleValidator\Validator; -use SimpleValidator\Validators; -use Kanboard\Core\Session\SessionManager; use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; @@ -369,132 +366,4 @@ class User extends Base ->eq('id', $user_id) ->save(array('token' => '')); } - - /** - * Common validation rules - * - * @access private - * @return array - */ - private function commonValidationRules() - { - return array( - new Validators\MaxLength('role', t('The maximum length is %d characters', 25), 25), - new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50), - new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'), - new Validators\Email('email', t('Email address invalid')), - new Validators\Integer('is_ldap_user', t('This value must be an integer')), - ); - } - - /** - * Common password validation rules - * - * @access private - * @return array - */ - private function commonPasswordValidationRules() - { - return array( - new Validators\Required('password', t('The password is required')), - new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6), - new Validators\Required('confirmation', t('The confirmation is required')), - new Validators\Equals('password', 'confirmation', t('Passwords don\'t match')), - ); - } - - /** - * Validate user creation - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateCreation(array $values) - { - $rules = array( - new Validators\Required('username', t('The username is required')), - ); - - if (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) { - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - } else { - $v = new Validator($values, array_merge($rules, $this->commonValidationRules(), $this->commonPasswordValidationRules())); - } - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate user modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateModification(array $values) - { - $rules = array( - new Validators\Required('id', t('The user id is required')), - new Validators\Required('username', t('The username is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate user API modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateApiModification(array $values) - { - $rules = array( - new Validators\Required('id', t('The user id is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** - * Validate password modification - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validatePasswordModification(array $values) - { - $rules = array( - new Validators\Required('id', t('The user id is required')), - new Validators\Required('current_password', t('The current password is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonPasswordValidationRules())); - - if ($v->execute()) { - if ($this->authenticationManager->passwordAuthentication($this->userSession->getUsername(), $values['current_password'], false)) { - return array(true, array()); - } else { - return array(false, array('current_password' => array(t('Wrong password')))); - } - } - - return array(false, $v->getErrors()); - } } diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index e206ef68..bb9e757e 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -63,7 +63,6 @@ class ClassProvider implements ServiceProviderInterface 'TaskPermission', 'TaskPosition', 'TaskStatus', - 'TaskValidator', 'TaskImport', 'TaskMetadata', 'Transition', @@ -87,6 +86,12 @@ class ClassProvider implements ServiceProviderInterface ), 'Validator' => array( 'PasswordResetValidator', + 'ProjectValidator', + 'SubtaskValidator', + 'SwimlaneValidator', + 'TaskValidator', + 'TaskLinkValidator', + 'UserValidator', ), 'Core' => array( 'DateParser', diff --git a/app/Template/subtask/show.php b/app/Template/subtask/show.php index 283057f4..6945840f 100644 --- a/app/Template/subtask/show.php +++ b/app/Template/subtask/show.php @@ -13,7 +13,7 @@ <th><?= t('Assignee') ?></th> <th><?= t('Time tracking') ?></th> <?php if ($editable): ?> - <th><?= t('Actions') ?></th> + <th class="column-5"></th> <?php endif ?> </tr> <?php foreach ($subtasks as $subtask): ?> @@ -61,6 +61,8 @@ </td> <?php if ($editable): ?> <td> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <?php if ($subtask['position'] != $first_position): ?> <li> @@ -79,6 +81,7 @@ <?= $this->url->link(t('Remove'), 'subtask', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?> </li> </ul> + </div> </td> <?php endif ?> </tr> @@ -87,7 +90,6 @@ <?php endif ?> <?php if ($editable && $this->user->hasProjectAccess('subtask', 'save', $task['project_id'])): ?> - <?php if (empty($subtasks)): ?> <div class="page-header"> <h2><?= t('Sub-Tasks') ?></h2> diff --git a/app/Validator/Base.php b/app/Validator/Base.php index 6c56e2fd..ba32a503 100644 --- a/app/Validator/Base.php +++ b/app/Validator/Base.php @@ -2,6 +2,8 @@ namespace Kanboard\Validator; +use SimpleValidator\Validators; + /** * Base Validator * @@ -33,4 +35,20 @@ class Base extends \Kanboard\Core\Base return array($result, $errors); } + + /** + * Common password validation rules + * + * @access protected + * @return array + */ + protected function commonPasswordValidationRules() + { + return array( + new Validators\Required('password', t('The password is required')), + new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6), + new Validators\Required('confirmation', t('The confirmation is required')), + new Validators\Equals('password', 'confirmation', t('Passwords don\'t match')), + ); + } } diff --git a/app/Validator/PasswordResetValidator.php b/app/Validator/PasswordResetValidator.php index 6f21cbca..baf2d8d7 100644 --- a/app/Validator/PasswordResetValidator.php +++ b/app/Validator/PasswordResetValidator.php @@ -35,12 +35,7 @@ class PasswordResetValidator extends Base */ public function validateModification(array $values) { - $v = new Validator($values, array( - new Validators\Required('password', t('The password is required')), - new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6), - new Validators\Required('confirmation', t('The confirmation is required')), - new Validators\Equals('password', 'confirmation', t('Passwords don\'t match')), - )); + $v = new Validator($values, $this->commonPasswordValidationRules()); return array( $v->execute(), @@ -78,7 +73,6 @@ class PasswordResetValidator extends Base */ protected function validateCaptcha(array $values) { - $result = true; $errors = array(); if (! isset($this->sessionStorage->captcha)) { diff --git a/app/Validator/ProjectValidator.php b/app/Validator/ProjectValidator.php new file mode 100644 index 00000000..53cb7a37 --- /dev/null +++ b/app/Validator/ProjectValidator.php @@ -0,0 +1,83 @@ +<?php + +namespace Kanboard\Validator; + +use SimpleValidator\Validator; +use SimpleValidator\Validators; +use Kanboard\Model\Project; + +/** + * Project Validator + * + * @package validator + * @author Frederic Guillot + */ +class ProjectValidator extends Base +{ + /** + * Common validation rules + * + * @access private + * @return array + */ + private function commonValidationRules() + { + return array( + new Validators\Integer('id', t('This value must be an integer')), + new Validators\Integer('is_active', t('This value must be an integer')), + new Validators\Required('name', t('The project name is required')), + new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50), + new Validators\MaxLength('identifier', t('The maximum length is %d characters', 50), 50), + new Validators\MaxLength('start_date', t('The maximum length is %d characters', 10), 10), + new Validators\MaxLength('end_date', t('The maximum length is %d characters', 10), 10), + new Validators\AlphaNumeric('identifier', t('This value must be alphanumeric')) , + new Validators\Unique('identifier', t('The identifier must be unique'), $this->db->getConnection(), Project::TABLE), + ); + } + + /** + * Validate project creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateCreation(array $values) + { + if (! empty($values['identifier'])) { + $values['identifier'] = strtoupper($values['identifier']); + } + + $v = new Validator($values, $this->commonValidationRules()); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate project modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + if (! empty($values['identifier'])) { + $values['identifier'] = strtoupper($values['identifier']); + } + + $rules = array( + new Validators\Required('id', t('This value is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/SubtaskValidator.php b/app/Validator/SubtaskValidator.php new file mode 100644 index 00000000..1989b7f4 --- /dev/null +++ b/app/Validator/SubtaskValidator.php @@ -0,0 +1,101 @@ +<?php + +namespace Kanboard\Validator; + +use SimpleValidator\Validator; +use SimpleValidator\Validators; + +/** + * Subtask Validator + * + * @package validator + * @author Frederic Guillot + */ +class SubtaskValidator extends Base +{ + /** + * Validate creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateCreation(array $values) + { + $rules = array( + new Validators\Required('task_id', t('The task id is required')), + new Validators\Required('title', t('The title is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The subtask id is required')), + new Validators\Required('task_id', t('The task id is required')), + new Validators\Required('title', t('The title is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate API modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateApiModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The subtask id is required')), + new Validators\Required('task_id', t('The task id is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Common validation rules + * + * @access private + * @return array + */ + private function commonValidationRules() + { + return array( + new Validators\Integer('id', t('The subtask id must be an integer')), + new Validators\Integer('task_id', t('The task id must be an integer')), + new Validators\MaxLength('title', t('The maximum length is %d characters', 255), 255), + new Validators\Integer('user_id', t('The user id must be an integer')), + new Validators\Integer('status', t('The status must be an integer')), + new Validators\Numeric('time_estimated', t('The time must be a numeric value')), + new Validators\Numeric('time_spent', t('The time must be a numeric value')), + ); + } +} diff --git a/app/Validator/SwimlaneValidator.php b/app/Validator/SwimlaneValidator.php new file mode 100644 index 00000000..4cc780f9 --- /dev/null +++ b/app/Validator/SwimlaneValidator.php @@ -0,0 +1,96 @@ +<?php + +namespace Kanboard\Validator; + +use SimpleValidator\Validator; +use SimpleValidator\Validators; + +/** + * Swimlane Validator + * + * @package validator + * @author Frederic Guillot + */ +class SwimlaneValidator extends Base +{ + /** + * Validate creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateCreation(array $values) + { + $rules = array( + new Validators\Required('project_id', t('The project id is required')), + new Validators\Required('name', t('The name is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The id is required')), + new Validators\Required('name', t('The name is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate default swimlane modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateDefaultModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The id is required')), + new Validators\Required('default_swimlane', t('The name is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Common validation rules + * + * @access private + * @return array + */ + private function commonValidationRules() + { + return array( + new Validators\Integer('id', t('The id must be an integer')), + new Validators\Integer('project_id', t('The project id must be an integer')), + new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50) + ); + } +} diff --git a/app/Validator/TaskLinkValidator.php b/app/Validator/TaskLinkValidator.php new file mode 100644 index 00000000..c88c2b16 --- /dev/null +++ b/app/Validator/TaskLinkValidator.php @@ -0,0 +1,71 @@ +<?php + +namespace Kanboard\Validator; + +use SimpleValidator\Validator; +use SimpleValidator\Validators; +use Kanboard\Model\Task; + +/** + * Task Link Validator + * + * @package validator + * @author Frederic Guillot + */ +class TaskLinkValidator extends Base +{ + /** + * Common validation rules + * + * @access private + * @return array + */ + private function commonValidationRules() + { + return array( + new Validators\Required('task_id', t('Field required')), + new Validators\Required('opposite_task_id', t('Field required')), + new Validators\Required('link_id', t('Field required')), + new Validators\NotEquals('opposite_task_id', 'task_id', t('A task cannot be linked to itself')), + new Validators\Exists('opposite_task_id', t('This linked task id doesn\'t exists'), $this->db->getConnection(), Task::TABLE, 'id') + ); + } + + /** + * Validate creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateCreation(array $values) + { + $v = new Validator($values, $this->commonValidationRules()); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('Field required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} diff --git a/app/Model/TaskValidator.php b/app/Validator/TaskValidator.php index 683cb0b1..c18a9761 100644 --- a/app/Model/TaskValidator.php +++ b/app/Validator/TaskValidator.php @@ -1,14 +1,14 @@ <?php -namespace Kanboard\Model; +namespace Kanboard\Validator; use SimpleValidator\Validator; use SimpleValidator\Validators; /** - * Task validator model + * Task Validator * - * @package model + * @package validator * @author Frederic Guillot */ class TaskValidator extends Base diff --git a/app/Validator/UserValidator.php b/app/Validator/UserValidator.php new file mode 100644 index 00000000..d85d335f --- /dev/null +++ b/app/Validator/UserValidator.php @@ -0,0 +1,128 @@ +<?php + +namespace Kanboard\Validator; + +use SimpleValidator\Validator; +use SimpleValidator\Validators; +use Kanboard\Model\User; + +/** + * User Validator + * + * @package validator + * @author Frederic Guillot + */ +class UserValidator extends Base +{ + /** + * Common validation rules + * + * @access private + * @return array + */ + private function commonValidationRules() + { + return array( + new Validators\MaxLength('role', t('The maximum length is %d characters', 25), 25), + new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50), + new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), User::TABLE, 'id'), + new Validators\Email('email', t('Email address invalid')), + new Validators\Integer('is_ldap_user', t('This value must be an integer')), + ); + } + + /** + * Validate user creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateCreation(array $values) + { + $rules = array( + new Validators\Required('username', t('The username is required')), + ); + + if (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) { + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + } else { + $v = new Validator($values, array_merge($rules, $this->commonValidationRules(), $this->commonPasswordValidationRules())); + } + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate user modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The user id is required')), + new Validators\Required('username', t('The username is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate user API modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateApiModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The user id is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate password modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validatePasswordModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The user id is required')), + new Validators\Required('current_password', t('The current password is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonPasswordValidationRules())); + + if ($v->execute()) { + if ($this->authenticationManager->passwordAuthentication($this->userSession->getUsername(), $values['current_password'], false)) { + return array(true, array()); + } else { + return array(false, array('current_password' => array(t('Wrong password')))); + } + } + + return array(false, $v->getErrors()); + } +} |