1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/kanboard_ynh.git synced 2024-09-03 19:36:17 +02:00

Update kanboard v1.0.13

This commit is contained in:
mbugeia 2015-04-21 17:56:16 +02:00
parent 932fb1198c
commit 4ce57aec07
252 changed files with 11509 additions and 795 deletions

View file

@ -0,0 +1,83 @@
<?php
namespace Action;
use Model\Task;
/**
* Assign a color to a task
*
* @package action
*/
class TaskAssignColorColumn extends Base
{
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
Task::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
'color_id' => t('Color'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'column_id',
);
}
/**
* Execute the action (set the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'color_id' => $this->getParam('color_id'),
);
return $this->taskModification->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id');
}
}

View file

@ -0,0 +1,90 @@
<?php
namespace Action;
use Model\Task;
/**
* Move a task to another column when an assignee is set
*
* @package action
* @author Francois Ferrand
*/
class TaskMoveColumnAssigned extends Base
{
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
Task::EVENT_ASSIGNEE_CHANGE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'src_column_id' => t('Source column'),
'dest_column_id' => t('Destination column')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'column_id',
'project_id',
'owner_id'
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$original_task = $this->taskFinder->getById($data['task_id']);
return $this->taskPosition->movePosition(
$data['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
$original_task['position'],
$original_task['swimlane_id'],
false
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('src_column_id') && $data['owner_id'];
}
}

View file

@ -0,0 +1,90 @@
<?php
namespace Action;
use Model\Task;
/**
* Move a task to another column when an assignee is cleared
*
* @package action
* @author Francois Ferrand
*/
class TaskMoveColumnUnAssigned extends Base
{
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
Task::EVENT_ASSIGNEE_CHANGE
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'src_column_id' => t('Source column'),
'dest_column_id' => t('Destination column')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'column_id',
'project_id',
'owner_id'
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$original_task = $this->taskFinder->getById($data['task_id']);
return $this->taskPosition->movePosition(
$data['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
$original_task['position'],
$original_task['swimlane_id'],
false
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('src_column_id') && ! $data['owner_id'];
}
}

View file

@ -10,6 +10,7 @@ use Pimple\Container;
* @package auth * @package auth
* @author Frederic Guillot * @author Frederic Guillot
* *
* @property \Core\Session $session
* @property \Model\Acl $acl * @property \Model\Acl $acl
* @property \Model\LastLogin $lastLogin * @property \Model\LastLogin $lastLogin
* @property \Model\User $user * @property \Model\User $user

View file

@ -34,7 +34,7 @@ class GitHub extends Base
{ {
$user = $this->user->getByGitHubId($github_id); $user = $this->user->getByGitHubId($github_id);
if ($user) { if (! empty($user)) {
$this->userSession->refresh($user); $this->userSession->refresh($user);
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id'])); $this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
return true; return true;

View file

@ -35,7 +35,7 @@ class Google extends Base
{ {
$user = $this->user->getByGoogleId($google_id); $user = $this->user->getByGoogleId($google_id);
if ($user) { if (! empty($user)) {
$this->userSession->refresh($user); $this->userSession->refresh($user);
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id'])); $this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
return true; return true;

View file

@ -36,7 +36,7 @@ class Ldap extends Base
$user = $this->user->getByUsername($username); $user = $this->user->getByUsername($username);
if ($user) { if (! empty($user)) {
// There is already a local user with that name // There is already a local user with that name
if ($user['is_ldap_user'] == 0) { if ($user['is_ldap_user'] == 0) {
@ -241,7 +241,7 @@ class Ldap extends Base
} }
// User id not retrieved: LDAP_ACCOUNT_ID not properly configured // User id not retrieved: LDAP_ACCOUNT_ID not properly configured
if (! $username && ! isset($info[0][LDAP_ACCOUNT_ID][0])) { if (empty($username) && ! isset($info[0][LDAP_ACCOUNT_ID][0])) {
return false; return false;
} }
@ -261,7 +261,7 @@ class Ldap extends Base
private function getQuery($username, $email) private function getQuery($username, $email)
{ {
if ($username && $email) { if ($username && $email) {
return '(&('.sprintf(LDAP_USER_PATTERN, $username).')('.sprintf(LDAP_ACCOUNT_EMAIL, $email).')'; return '(&('.sprintf(LDAP_USER_PATTERN, $username).')('.LDAP_ACCOUNT_EMAIL.'='.$email.'))';
} }
else if ($username) { else if ($username) {
return sprintf(LDAP_USER_PATTERN, $username); return sprintf(LDAP_USER_PATTERN, $username);

View file

@ -103,6 +103,9 @@ class RememberMe extends Base
// Create the session // Create the session
$this->userSession->refresh($this->user->getById($record['user_id'])); $this->userSession->refresh($this->user->getById($record['user_id']));
// Do not ask 2FA for remember me session
$this->session['2fa_validated'] = true;
$this->container['dispatcher']->dispatch( $this->container['dispatcher']->dispatch(
'auth.success', 'auth.success',
new AuthEvent(self::AUTH_NAME, $this->userSession->getId()) new AuthEvent(self::AUTH_NAME, $this->userSession->getId())

View file

@ -32,7 +32,7 @@ class ReverseProxy extends Base
$login = $_SERVER[REVERSE_PROXY_USER_HEADER]; $login = $_SERVER[REVERSE_PROXY_USER_HEADER];
$user = $this->user->getByUsername($login); $user = $this->user->getByUsername($login);
if (! $user) { if (empty($user)) {
$this->createUser($login); $this->createUser($login);
$user = $this->user->getByUsername($login); $user = $this->user->getByUsername($login);
} }

View file

@ -20,6 +20,7 @@ use Symfony\Component\Console\Command\Command;
* @property \Model\Task $task * @property \Model\Task $task
* @property \Model\TaskExport $taskExport * @property \Model\TaskExport $taskExport
* @property \Model\TaskFinder $taskFinder * @property \Model\TaskFinder $taskFinder
* @property \Model\Transition $transition
*/ */
abstract class Base extends Command abstract class Base extends Command
{ {

View file

@ -0,0 +1,34 @@
<?php
namespace Console;
use Core\Tool;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class TransitionExport extends Base
{
protected function configure()
{
$this
->setName('export:transitions')
->setDescription('Task transitions CSV export')
->addArgument('project_id', InputArgument::REQUIRED, 'Project id')
->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)')
->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$data = $this->transition->export(
$input->getArgument('project_id'),
$input->getArgument('start_date'),
$input->getArgument('end_date')
);
if (is_array($data)) {
Tool::csv($data);
}
}
}

View file

@ -157,7 +157,7 @@ class Action extends Base
$project = $this->getProject(); $project = $this->getProject();
$action = $this->action->getById($this->request->getIntegerParam('action_id')); $action = $this->action->getById($this->request->getIntegerParam('action_id'));
if ($action && $this->action->remove($action['id'])) { if (! empty($action) && $this->action->remove($action['id'])) {
$this->session->flash(t('Action removed successfully.')); $this->session->flash(t('Action removed successfully.'));
} else { } else {
$this->session->flashError(t('Unable to remove this action.')); $this->session->flashError(t('Unable to remove this action.'));

View file

@ -46,21 +46,21 @@ class App extends Base
$project_ids = array_keys($projects); $project_ids = array_keys($projects);
$task_paginator = $this->paginator $task_paginator = $this->paginator
->setUrl('app', $action, array('pagination' => 'tasks')) ->setUrl('app', $action, array('pagination' => 'tasks', 'user_id' => $user_id))
->setMax(10) ->setMax(10)
->setOrder('tasks.id') ->setOrder('tasks.id')
->setQuery($this->taskFinder->getUserQuery($user_id)) ->setQuery($this->taskFinder->getUserQuery($user_id))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks'); ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks');
$subtask_paginator = $this->paginator $subtask_paginator = $this->paginator
->setUrl('app', $action, array('pagination' => 'subtasks')) ->setUrl('app', $action, array('pagination' => 'subtasks', 'user_id' => $user_id))
->setMax(10) ->setMax(10)
->setOrder('tasks.id') ->setOrder('tasks.id')
->setQuery($this->subtask->getUserQuery($user_id, $status)) ->setQuery($this->subtask->getUserQuery($user_id, $status))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
$project_paginator = $this->paginator $project_paginator = $this->paginator
->setUrl('app', $action, array('pagination' => 'projects')) ->setUrl('app', $action, array('pagination' => 'projects', 'user_id' => $user_id))
->setMax(10) ->setMax(10)
->setOrder('name') ->setOrder('name')
->setQuery($this->project->getQueryColumnStats($project_ids)) ->setQuery($this->project->getQueryColumnStats($project_ids))

View file

@ -34,6 +34,7 @@ use Symfony\Component\EventDispatcher\Event;
* @property \Model\Config $config * @property \Model\Config $config
* @property \Model\DateParser $dateParser * @property \Model\DateParser $dateParser
* @property \Model\File $file * @property \Model\File $file
* @property \Model\HourlyRate $hourlyRate
* @property \Model\LastLogin $lastLogin * @property \Model\LastLogin $lastLogin
* @property \Model\Notification $notification * @property \Model\Notification $notification
* @property \Model\Project $project * @property \Model\Project $project
@ -43,6 +44,7 @@ use Symfony\Component\EventDispatcher\Event;
* @property \Model\ProjectActivity $projectActivity * @property \Model\ProjectActivity $projectActivity
* @property \Model\ProjectDailySummary $projectDailySummary * @property \Model\ProjectDailySummary $projectDailySummary
* @property \Model\Subtask $subtask * @property \Model\Subtask $subtask
* @property \Model\SubtaskForecast $subtaskForecast
* @property \Model\Swimlane $swimlane * @property \Model\Swimlane $swimlane
* @property \Model\Task $task * @property \Model\Task $task
* @property \Model\Link $link * @property \Model\Link $link
@ -56,12 +58,16 @@ use Symfony\Component\EventDispatcher\Event;
* @property \Model\TaskPosition $taskPosition * @property \Model\TaskPosition $taskPosition
* @property \Model\TaskPermission $taskPermission * @property \Model\TaskPermission $taskPermission
* @property \Model\TaskStatus $taskStatus * @property \Model\TaskStatus $taskStatus
* @property \Model\Timetable $timetable
* @property \Model\TimetableDay $timetableDay
* @property \Model\TimetableWeek $timetableWeek
* @property \Model\TimetableExtra $timetableExtra
* @property \Model\TimetableOff $timetableOff
* @property \Model\TaskValidator $taskValidator * @property \Model\TaskValidator $taskValidator
* @property \Model\TaskLink $taskLink * @property \Model\TaskLink $taskLink
* @property \Model\CommentHistory $commentHistory * @property \Model\CommentHistory $commentHistory
* @property \Model\SubtaskHistory $subtaskHistory * @property \Model\SubtaskHistory $subtaskHistory
* @property \Model\SubtaskTimeTracking $subtaskTimeTracking * @property \Model\SubtaskTimeTracking $subtaskTimeTracking
* @property \Model\TimeTracking $timeTracking
* @property \Model\User $user * @property \Model\User $user
* @property \Model\UserSession $userSession * @property \Model\UserSession $userSession
* @property \Model\Webhook $webhook * @property \Model\Webhook $webhook
@ -148,7 +154,7 @@ abstract class Base
$this->response->xss(); $this->response->xss();
// Allow the public board iframe inclusion // Allow the public board iframe inclusion
if ($action !== 'readonly') { if (ENABLE_XFRAME && $action !== 'readonly') {
$this->response->xframe(); $this->response->xframe();
} }
@ -171,6 +177,7 @@ abstract class Base
if (! $this->acl->isPublicAction($controller, $action)) { if (! $this->acl->isPublicAction($controller, $action)) {
$this->handleAuthentication(); $this->handleAuthentication();
$this->handle2FA($controller, $action);
$this->handleAuthorization($controller, $action); $this->handleAuthorization($controller, $action);
$this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId()); $this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
@ -194,6 +201,25 @@ abstract class Base
} }
} }
/**
* Check 2FA
*
* @access public
*/
public function handle2FA($controller, $action)
{
$ignore = ($controller === 'twofactor' && in_array($action, array('code', 'check'))) || ($controller === 'user' && $action === 'logout');
if ($ignore === false && $this->userSession->has2FA() && ! $this->userSession->check2FA()) {
if ($this->request->isAjax()) {
$this->response->text('Not Authorized', 401);
}
$this->response->redirect($this->helper->url('twofactor', 'code'));
}
}
/** /**
* Check page access and authorization * Check page access and authorization
* *
@ -311,7 +337,7 @@ abstract class Base
{ {
$task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id')); $task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id'));
if (! $task) { if (empty($task)) {
$this->notfound(); $this->notfound();
} }
@ -330,7 +356,7 @@ abstract class Base
$project_id = $this->request->getIntegerParam('project_id', $project_id); $project_id = $this->request->getIntegerParam('project_id', $project_id);
$project = $this->project->getById($project_id); $project = $this->project->getById($project_id);
if (! $project) { if (empty($project)) {
$this->session->flashError(t('Project not found.')); $this->session->flashError(t('Project not found.'));
$this->response->redirect('?controller=project'); $this->response->redirect('?controller=project');
} }

View file

@ -117,7 +117,7 @@ class Board extends Base
$project = $this->project->getByToken($token); $project = $this->project->getByToken($token);
// Token verification // Token verification
if (! $project) { if (empty($project)) {
$this->forbidden(true); $this->forbidden(true);
} }
@ -127,6 +127,7 @@ class Board extends Base
'swimlanes' => $this->board->getBoard($project['id']), 'swimlanes' => $this->board->getBoard($project['id']),
'categories' => $this->category->getList($project['id'], false), 'categories' => $this->category->getList($project['id'], false),
'title' => $project['name'], 'title' => $project['name'],
'description' => $project['description'],
'no_layout' => true, 'no_layout' => true,
'not_editable' => true, 'not_editable' => true,
'board_public_refresh_interval' => $this->config->get('board_public_refresh_interval'), 'board_public_refresh_interval' => $this->config->get('board_public_refresh_interval'),
@ -187,6 +188,7 @@ class Board extends Base
'swimlanes' => $this->board->getBoard($project['id']), 'swimlanes' => $this->board->getBoard($project['id']),
'categories' => $this->category->getList($project['id'], true, true), 'categories' => $this->category->getList($project['id'], true, true),
'title' => $project['name'], 'title' => $project['name'],
'description' => $project['description'],
'board_selector' => $board_selector, 'board_selector' => $board_selector,
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
'board_highlight_period' => $this->config->get('board_highlight_period'), 'board_highlight_period' => $this->config->get('board_highlight_period'),
@ -309,7 +311,7 @@ class Board extends Base
$this->checkCSRFParam(); $this->checkCSRFParam();
$column = $this->board->getColumn($this->request->getIntegerParam('column_id')); $column = $this->board->getColumn($this->request->getIntegerParam('column_id'));
if ($column && $this->board->removeColumn($column['id'])) { if (! empty($column) && $this->board->removeColumn($column['id'])) {
$this->session->flash(t('Column removed successfully.')); $this->session->flash(t('Column removed successfully.'));
} else { } else {
$this->session->flashError(t('Unable to remove this column.')); $this->session->flashError(t('Unable to remove this column.'));
@ -439,7 +441,8 @@ class Board extends Base
$task = $this->getTask(); $task = $this->getTask();
$this->response->html($this->template->render('board/files', array( $this->response->html($this->template->render('board/files', array(
'files' => $this->file->getAll($task['id']), 'files' => $this->file->getAllDocuments($task['id']),
'images' => $this->file->getAllImages($task['id']),
'task' => $task, 'task' => $task,
))); )));
} }

View file

@ -0,0 +1,135 @@
<?php
namespace Controller;
/**
* Budget
*
* @package controller
* @author Frederic Guillot
*/
class Budget extends Base
{
/**
* Budget index page
*
* @access public
*/
public function index()
{
$project = $this->getProject();
$this->response->html($this->projectLayout('budget/index', array(
'daily_budget' => $this->budget->getDailyBudgetBreakdown($project['id']),
'project' => $project,
'title' => t('Budget')
)));
}
/**
* Cost breakdown by users/subtasks/tasks
*
* @access public
*/
public function breakdown()
{
$project = $this->getProject();
$paginator = $this->paginator
->setUrl('budget', 'breakdown', array('project_id' => $project['id']))
->setMax(30)
->setOrder('start')
->setDirection('DESC')
->setQuery($this->budget->getSubtaskBreakdown($project['id']))
->calculate();
$this->response->html($this->projectLayout('budget/breakdown', array(
'paginator' => $paginator,
'project' => $project,
'title' => t('Budget')
)));
}
/**
* Create budget lines
*
* @access public
*/
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
if (empty($values)) {
$values['date'] = date('Y-m-d');
}
$this->response->html($this->projectLayout('budget/create', array(
'lines' => $this->budget->getAll($project['id']),
'values' => $values + array('project_id' => $project['id']),
'errors' => $errors,
'project' => $project,
'title' => t('Budget')
)));
}
/**
* Validate and save a new budget
*
* @access public
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
list($valid, $errors) = $this->budget->validateCreation($values);
if ($valid) {
if ($this->budget->create($values['project_id'], $values['amount'], $values['comment'], $values['date'])) {
$this->session->flash(t('The budget line have been created successfully.'));
$this->response->redirect($this->helper->url('budget', 'create', array('project_id' => $project['id'])));
}
else {
$this->session->flashError(t('Unable to create the budget line.'));
}
}
$this->create($values, $errors);
}
/**
* Confirmation dialog before removing a budget
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$this->response->html($this->projectLayout('budget/remove', array(
'project' => $project,
'budget_id' => $this->request->getIntegerParam('budget_id'),
'title' => t('Remove a budget line'),
)));
}
/**
* Remove a budget
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
if ($this->budget->remove($this->request->getIntegerParam('budget_id'))) {
$this->session->flash(t('Budget line removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this budget line.'));
}
$this->response->redirect($this->helper->url('budget', 'create', array('project_id' => $project['id'])));
}
}

View file

@ -14,7 +14,7 @@ use Model\Task as TaskModel;
class Calendar extends Base class Calendar extends Base
{ {
/** /**
* Show calendar view * Show calendar view for projects
* *
* @access public * @access public
*/ */
@ -59,9 +59,7 @@ class Calendar extends Base
->filterByDueDateRange($start, $end) ->filterByDueDateRange($start, $end)
->toCalendarEvents(); ->toCalendarEvents();
$subtask_timeslots = $this->subtaskTimeTracking->getProjectCalendarEvents($project_id, $start, $end); $this->response->json($due_tasks);
$this->response->json(array_merge($due_tasks, $subtask_timeslots));
} }
/** /**
@ -84,7 +82,9 @@ class Calendar extends Base
$subtask_timeslots = $this->subtaskTimeTracking->getUserCalendarEvents($user_id, $start, $end); $subtask_timeslots = $this->subtaskTimeTracking->getUserCalendarEvents($user_id, $start, $end);
$this->response->json(array_merge($due_tasks, $subtask_timeslots)); $subtask_forcast = $this->config->get('subtask_forecast') == 1 ? $this->subtaskForecast->getCalendarEvents($user_id, $end) : array();
$this->response->json(array_merge($due_tasks, $subtask_timeslots, $subtask_forcast));
} }
/** /**
@ -100,7 +100,7 @@ class Calendar extends Base
$this->taskModification->update(array( $this->taskModification->update(array(
'id' => $values['task_id'], 'id' => $values['task_id'],
'date_due' => $values['date_due'], 'date_due' => substr($values['date_due'], 0, 10),
)); ));
} }
} }

View file

@ -21,7 +21,7 @@ class Category extends Base
{ {
$category = $this->category->getById($this->request->getIntegerParam('category_id')); $category = $this->category->getById($this->request->getIntegerParam('category_id'));
if (! $category) { if (empty($category)) {
$this->session->flashError(t('Category not found.')); $this->session->flashError(t('Category not found.'));
$this->response->redirect('?controller=category&action=index&project_id='.$project_id); $this->response->redirect('?controller=category&action=index&project_id='.$project_id);
} }

View file

@ -20,7 +20,7 @@ class Comment extends Base
{ {
$comment = $this->comment->getById($this->request->getIntegerParam('comment_id')); $comment = $this->comment->getById($this->request->getIntegerParam('comment_id'));
if (! $comment) { if (empty($comment)) {
$this->notfound(); $this->notfound();
} }

View file

@ -38,7 +38,14 @@ class Config extends Base
{ {
if ($this->request->isPost()) { if ($this->request->isPost()) {
$values = $this->request->getValues() + array('subtask_restriction' => 0, 'subtask_time_tracking' => 0); $values = $this->request->getValues();
if ($redirect === 'board') {
$values += array('subtask_restriction' => 0, 'subtask_time_tracking' => 0, 'subtask_forecast' => 0);
}
else if ($redirect === 'integrations') {
$values += array('integration_slack_webhook' => 0, 'integration_hipchat' => 0, 'integration_gravatar' => 0);
}
if ($this->config->save($values)) { if ($this->config->save($values)) {
$this->config->reload(); $this->config->reload();
@ -97,6 +104,20 @@ class Config extends Base
))); )));
} }
/**
* Display the integration settings page
*
* @access public
*/
public function integrations()
{
$this->common('integrations');
$this->response->html($this->layout('config/integrations', array(
'title' => t('Settings').' &gt; '.t('Integrations'),
)));
}
/** /**
* Display the webhook settings page * Display the webhook settings page
* *

View file

@ -0,0 +1,89 @@
<?php
namespace Controller;
/**
* Currency controller
*
* @package controller
* @author Frederic Guillot
*/
class Currency extends Base
{
/**
* Common layout for config views
*
* @access private
* @param string $template Template name
* @param array $params Template parameters
* @return string
*/
private function layout($template, array $params)
{
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
$params['config_content_for_layout'] = $this->template->render($template, $params);
return $this->template->layout('config/layout', $params);
}
/**
* Display all currency rates and form
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$this->response->html($this->layout('currency/index', array(
'config_values' => array('application_currency' => $this->config->get('application_currency')),
'values' => $values,
'errors' => $errors,
'rates' => $this->currency->getAll(),
'currencies' => $this->config->getCurrencies(),
'title' => t('Settings').' &gt; '.t('Currency rates'),
)));
}
/**
* Validate and save a new currency rate
*
* @access public
*/
public function create()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->currency->validate($values);
if ($valid) {
if ($this->currency->create($values['currency'], $values['rate'])) {
$this->session->flash(t('The currency rate have been added successfully.'));
$this->response->redirect($this->helper->url('currency', 'index'));
}
else {
$this->session->flashError(t('Unable to add this currency rate.'));
}
}
$this->index($values, $errors);
}
/**
* Save reference currency
*
* @access public
*/
public function reference()
{
$values = $this->request->getValues();
if ($this->config->save($values)) {
$this->config->reload();
$this->session->flash(t('Settings saved successfully.'));
}
else {
$this->session->flashError(t('Unable to save your settings.'));
}
$this->response->redirect($this->helper->url('currency', 'index'));
}
}

View file

@ -72,4 +72,14 @@ class Export extends Base
{ {
$this->common('projectDailySummary', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export')); $this->common('projectDailySummary', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
} }
/**
* Transition export
*
* @access public
*/
public function transitions()
{
$this->common('transition', 'export', t('Transitions'), 'transitions', t('Task transitions export'));
}
} }

View file

@ -101,6 +101,70 @@ class File extends Base
} }
} }
/**
* Return image thumbnails
*
* @access public
*/
public function thumbnail()
{
$task = $this->getTask();
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
$width_param = $this->request->getIntegerParam('width');
$height_param = $this->request->getIntegerParam('height');
$filename = FILES_DIR.$file['path'];
if ($file['task_id'] == $task['id'] && file_exists($filename)) {
// Get new sizes
list($width, $height) = getimagesize($filename);
if ($width_param == 0 && $height_param == 0) {
$newwidth = 100;
$newheight = 100;
} elseif ($width_param > 0 && $height_param == 0) {
$newwidth = $width_param;
$newheight = floor($height * ($width_param / $width));
} elseif ($width_param == 0 && $height_param > 0) {
$newwidth = floor($width * ($height_param / $height));
$newheight = $height_param;
} else {
$newwidth = $width_param;
$newheight = $height_param;
}
// Load
$thumb = imagecreatetruecolor($newwidth, $newheight);
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
switch ($extension) {
case 'jpeg':
case 'jpg':
$source = imagecreatefromjpeg($filename);
break;
case 'png':
$source = imagecreatefrompng($filename);
break;
case 'gif':
$source = imagecreatefromgif($filename);
break;
default:
die('File "' . $filename . '" is not valid jpg, png or gif image.');
break;
}
// Resize
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
$metadata = getimagesize($filename);
if (isset($metadata['mime'])) {
$this->response->contentType($metadata['mime']);
imagejpeg($thumb);
}
}
}
/** /**
* Remove a file * Remove a file
* *

View file

@ -0,0 +1,89 @@
<?php
namespace Controller;
/**
* Hourly Rate controller
*
* @package controller
* @author Frederic Guillot
*/
class Hourlyrate extends User
{
/**
* Display rate and form
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$user = $this->getUser();
$this->response->html($this->layout('hourlyrate/index', array(
'rates' => $this->hourlyRate->getAllByUser($user['id']),
'currencies_list' => $this->config->getCurrencies(),
'values' => $values + array('user_id' => $user['id']),
'errors' => $errors,
'user' => $user,
)));
}
/**
* Validate and save a new rate
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->hourlyRate->validateCreation($values);
if ($valid) {
if ($this->hourlyRate->create($values['user_id'], $values['rate'], $values['currency'], $values['date_effective'])) {
$this->session->flash(t('Hourly rate created successfully.'));
$this->response->redirect($this->helper->url('hourlyrate', 'index', array('user_id' => $values['user_id'])));
}
else {
$this->session->flashError(t('Unable to save the hourly rate.'));
}
}
$this->index($values, $errors);
}
/**
* Confirmation dialag box to remove a row
*
* @access public
*/
public function confirm()
{
$user = $this->getUser();
$this->response->html($this->layout('hourlyrate/remove', array(
'rate_id' => $this->request->getIntegerParam('rate_id'),
'user' => $user,
)));
}
/**
* Remove a row
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
if ($this->hourlyRate->remove($this->request->getIntegerParam('rate_id'))) {
$this->session->flash(t('Rate removed successfully.'));
}
else {
$this->session->flash(t('Unable to remove this rate.'));
}
$this->response->redirect($this->helper->url('hourlyrate', 'index', array('user_id' => $user['id'])));
}
}

View file

@ -37,7 +37,7 @@ class Link extends Base
{ {
$link = $this->link->getById($this->request->getIntegerParam('link_id')); $link = $this->link->getById($this->request->getIntegerParam('link_id'));
if (! $link) { if (empty($link)) {
$this->notfound(); $this->notfound();
} }

View file

@ -128,6 +128,11 @@ class Project extends Base
{ {
$project = $this->getProject(); $project = $this->getProject();
$values = $this->request->getValues(); $values = $this->request->getValues();
if ($project['is_private'] == 1) {
$values += array('is_private' => 0);
}
list($valid, $errors) = $this->project->validateModification($values); list($valid, $errors) = $this->project->validateModification($values);
if ($valid) { if ($valid) {

View file

@ -22,7 +22,7 @@ class Subtask extends Base
{ {
$subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id')); $subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id'));
if (! $subtask) { if (empty($subtask)) {
$this->notfound(); $this->notfound();
} }
@ -259,4 +259,22 @@ class Subtask extends Base
$this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); $this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
} }
} }
/**
* Move subtask position
*
* @access public
*/
public function movePosition()
{
$this->checkCSRFParam();
$project_id = $this->request->getIntegerParam('project_id');
$task_id = $this->request->getIntegerParam('task_id');
$subtask_id = $this->request->getIntegerParam('subtask_id');
$direction = $this->request->getStringParam('direction');
$method = $direction === 'up' ? 'moveUp' : 'moveDown';
$this->subtask->$method($task_id, $subtask_id);
$this->response->redirect($this->helper->url('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id)).'#subtasks');
}
} }

View file

@ -23,7 +23,7 @@ class Swimlane extends Base
{ {
$swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id')); $swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id'));
if (! $swimlane) { if (empty($swimlane)) {
$this->session->flashError(t('Swimlane not found.')); $this->session->flashError(t('Swimlane not found.'));
$this->response->redirect('?controller=swimlane&action=index&project_id='.$project_id); $this->response->redirect('?controller=swimlane&action=index&project_id='.$project_id);
} }
@ -86,7 +86,7 @@ class Swimlane extends Base
{ {
$project = $this->getProject(); $project = $this->getProject();
$values = $this->request->getValues(); $values = $this->request->getValues() + array('show_default_swimlane' => 0);
list($valid,) = $this->swimlane->validateDefaultModification($values); list($valid,) = $this->swimlane->validateDefaultModification($values);
if ($valid) { if ($valid) {

View file

@ -22,13 +22,13 @@ class Task extends Base
$project = $this->project->getByToken($this->request->getStringParam('token')); $project = $this->project->getByToken($this->request->getStringParam('token'));
// Token verification // Token verification
if (! $project) { if (empty($project)) {
$this->forbidden(true); $this->forbidden(true);
} }
$task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id')); $task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id'));
if (! $task) { if (empty($task)) {
$this->notfound(true); $this->notfound(true);
} }
@ -68,12 +68,14 @@ class Task extends Base
$this->response->html($this->taskLayout('task/show', array( $this->response->html($this->taskLayout('task/show', array(
'project' => $this->project->getById($task['project_id']), 'project' => $this->project->getById($task['project_id']),
'files' => $this->file->getAll($task['id']), 'files' => $this->file->getAllDocuments($task['id']),
'images' => $this->file->getAllImages($task['id']),
'comments' => $this->comment->getAll($task['id']), 'comments' => $this->comment->getAll($task['id']),
'subtasks' => $subtasks, 'subtasks' => $subtasks,
'links' => $this->taskLink->getLinks($task['id']), 'links' => $this->taskLink->getLinks($task['id']),
'task' => $task, 'task' => $task,
'values' => $values, 'values' => $values,
'link_label_list' => $this->link->getList(0, false),
'columns_list' => $this->board->getColumnsList($task['project_id']), 'columns_list' => $this->board->getColumnsList($task['project_id']),
'colors_list' => $this->color->getList(), 'colors_list' => $this->color->getList(),
'date_format' => $this->config->get('application_date_format'), 'date_format' => $this->config->get('application_date_format'),
@ -82,6 +84,23 @@ class Task extends Base
))); )));
} }
/**
* Display task activities
*
* @access public
*/
public function activites()
{
$task = $this->getTask();
$this->response->html($this->taskLayout('task/activity', array(
'title' => $task['title'],
'task' => $task,
'ajax' => $this->request->isAjax(),
'events' => $this->projectActivity->getTask($task['id']),
)));
}
/** /**
* Display a form to create a new task * Display a form to create a new task
* *
@ -91,11 +110,12 @@ class Task extends Base
{ {
$project = $this->getProject(); $project = $this->getProject();
$method = $this->request->isAjax() ? 'render' : 'layout'; $method = $this->request->isAjax() ? 'render' : 'layout';
$swimlanes_list = $this->swimlane->getList($project['id']);
if (empty($values)) { if (empty($values)) {
$values = array( $values = array(
'swimlane_id' => $this->request->getIntegerParam('swimlane_id'), 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)),
'column_id' => $this->request->getIntegerParam('column_id'), 'column_id' => $this->request->getIntegerParam('column_id'),
'color_id' => $this->request->getStringParam('color_id'), 'color_id' => $this->request->getStringParam('color_id'),
'owner_id' => $this->request->getIntegerParam('owner_id'), 'owner_id' => $this->request->getIntegerParam('owner_id'),
@ -112,6 +132,7 @@ class Task extends Base
'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true), 'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true),
'colors_list' => $this->color->getList(), 'colors_list' => $this->color->getList(),
'categories_list' => $this->category->getList($project['id']), 'categories_list' => $this->category->getList($project['id']),
'swimlanes_list' => $swimlanes_list,
'date_format' => $this->config->get('application_date_format'), 'date_format' => $this->config->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats(), 'date_formats' => $this->dateParser->getAvailableFormats(),
'title' => $project['name'].' &gt; '.t('New task') 'title' => $project['name'].' &gt; '.t('New task')
@ -522,4 +543,19 @@ class Task extends Base
'subtask_paginator' => $subtask_paginator, 'subtask_paginator' => $subtask_paginator,
))); )));
} }
/**
* Display the task transitions
*
* @access public
*/
public function transitions()
{
$task = $this->getTask();
$this->response->html($this->taskLayout('task/transitions', array(
'task' => $task,
'transitions' => $this->transition->getAllByTask($task['id']),
)));
}
} }

View file

@ -21,7 +21,7 @@ class Tasklink extends Base
{ {
$link = $this->taskLink->getById($this->request->getIntegerParam('link_id')); $link = $this->taskLink->getById($this->request->getIntegerParam('link_id'));
if (! $link) { if (empty($link)) {
$this->notfound(); $this->notfound();
} }
@ -36,6 +36,7 @@ class Tasklink extends Base
public function create(array $values = array(), array $errors = array()) public function create(array $values = array(), array $errors = array())
{ {
$task = $this->getTask(); $task = $this->getTask();
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
if (empty($values)) { if (empty($values)) {
$values = array( $values = array(
@ -43,6 +44,17 @@ class Tasklink extends Base
); );
} }
if ($ajax) {
$this->response->html($this->template->render('tasklink/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'labels' => $this->link->getList(0, false),
'title' => t('Add a new link'),
'ajax' => $ajax,
)));
}
$this->response->html($this->taskLayout('tasklink/create', array( $this->response->html($this->taskLayout('tasklink/create', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
@ -61,6 +73,7 @@ class Tasklink extends Base
{ {
$task = $this->getTask(); $task = $this->getTask();
$values = $this->request->getValues(); $values = $this->request->getValues();
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
list($valid, $errors) = $this->taskLink->validateCreation($values); list($valid, $errors) = $this->taskLink->validateCreation($values);
@ -68,6 +81,9 @@ class Tasklink extends Base
if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) { if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
$this->session->flash(t('Link added successfully.')); $this->session->flash(t('Link added successfully.'));
if ($ajax) {
$this->response->redirect($this->helper->url('board', 'show', array('project_id' => $task['project_id'])));
}
$this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links'); $this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
} }
else { else {

View file

@ -0,0 +1,39 @@
<?php
namespace Controller;
use DateTime;
/**
* Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetable extends User
{
/**
* Display timetable for the user
*
* @access public
*/
public function index()
{
$user = $this->getUser();
$from = $this->request->getStringParam('from', date('Y-m-d'));
$to = $this->request->getStringParam('to', date('Y-m-d', strtotime('next week')));
$timetable = $this->timetable->calculate($user['id'], new DateTime($from), new DateTime($to));
$this->response->html($this->layout('timetable/index', array(
'user' => $user,
'timetable' => $timetable,
'values' => array(
'from' => $from,
'to' => $to,
'controller' => 'timetable',
'action' => 'index',
'user_id' => $user['id'],
),
)));
}
}

View file

@ -0,0 +1,88 @@
<?php
namespace Controller;
/**
* Day Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetableday extends User
{
/**
* Display timetable for the user
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$user = $this->getUser();
$this->response->html($this->layout('timetable_day/index', array(
'timetable' => $this->timetableDay->getByUser($user['id']),
'values' => $values + array('user_id' => $user['id']),
'errors' => $errors,
'user' => $user,
)));
}
/**
* Validate and save
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->timetableDay->validateCreation($values);
if ($valid) {
if ($this->timetableDay->create($values['user_id'], $values['start'], $values['end'])) {
$this->session->flash(t('Time slot created successfully.'));
$this->response->redirect($this->helper->url('timetableday', 'index', array('user_id' => $values['user_id'])));
}
else {
$this->session->flashError(t('Unable to save this time slot.'));
}
}
$this->index($values, $errors);
}
/**
* Confirmation dialag box to remove a row
*
* @access public
*/
public function confirm()
{
$user = $this->getUser();
$this->response->html($this->layout('timetable_day/remove', array(
'slot_id' => $this->request->getIntegerParam('slot_id'),
'user' => $user,
)));
}
/**
* Remove a row
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
if ($this->timetableDay->remove($this->request->getIntegerParam('slot_id'))) {
$this->session->flash(t('Time slot removed successfully.'));
}
else {
$this->session->flash(t('Unable to remove this time slot.'));
}
$this->response->redirect($this->helper->url('timetableday', 'index', array('user_id' => $user['id'])));
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Controller;
/**
* Over-time Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetableextra extends Timetableoff
{
protected $model = 'timetableExtra';
protected $controller_url = 'timetableextra';
protected $template_dir = 'timetable_extra';
}

View file

@ -0,0 +1,107 @@
<?php
namespace Controller;
/**
* Time-off Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetableoff extends User
{
protected $model = 'timetableOff';
protected $controller_url = 'timetableoff';
protected $template_dir = 'timetable_off';
/**
* Display timetable for the user
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$user = $this->getUser();
$paginator = $this->paginator
->setUrl($this->controller_url, 'index', array('user_id' => $user['id']))
->setMax(10)
->setOrder('date')
->setDirection('desc')
->setQuery($this->{$this->model}->getUserQuery($user['id']))
->calculate();
$this->response->html($this->layout($this->template_dir.'/index', array(
'values' => $values + array('user_id' => $user['id']),
'errors' => $errors,
'paginator' => $paginator,
'user' => $user,
)));
}
/**
* Validate and save
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->{$this->model}->validateCreation($values);
if ($valid) {
if ($this->{$this->model}->create(
$values['user_id'],
$values['date'],
isset($values['all_day']) && $values['all_day'] == 1,
$values['start'],
$values['end'],
$values['comment'])) {
$this->session->flash(t('Time slot created successfully.'));
$this->response->redirect($this->helper->url($this->controller_url, 'index', array('user_id' => $values['user_id'])));
}
else {
$this->session->flashError(t('Unable to save this time slot.'));
}
}
$this->index($values, $errors);
}
/**
* Confirmation dialag box to remove a row
*
* @access public
*/
public function confirm()
{
$user = $this->getUser();
$this->response->html($this->layout($this->template_dir.'/remove', array(
'slot_id' => $this->request->getIntegerParam('slot_id'),
'user' => $user,
)));
}
/**
* Remove a row
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
if ($this->{$this->model}->remove($this->request->getIntegerParam('slot_id'))) {
$this->session->flash(t('Time slot removed successfully.'));
}
else {
$this->session->flash(t('Unable to remove this time slot.'));
}
$this->response->redirect($this->helper->url($this->controller_url, 'index', array('user_id' => $user['id'])));
}
}

View file

@ -0,0 +1,99 @@
<?php
namespace Controller;
/**
* Week Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetableweek extends User
{
/**
* Display timetable for the user
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$user = $this->getUser();
if (empty($values)) {
$day = $this->timetableDay->getByUser($user['id']);
$values = array(
'user_id' => $user['id'],
'start' => isset($day[0]['start']) ? $day[0]['start'] : null,
'end' => isset($day[0]['end']) ? $day[0]['end'] : null,
);
}
$this->response->html($this->layout('timetable_week/index', array(
'timetable' => $this->timetableWeek->getByUser($user['id']),
'values' => $values,
'errors' => $errors,
'user' => $user,
)));
}
/**
* Validate and save
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->timetableWeek->validateCreation($values);
if ($valid) {
if ($this->timetableWeek->create($values['user_id'], $values['day'], $values['start'], $values['end'])) {
$this->session->flash(t('Time slot created successfully.'));
$this->response->redirect($this->helper->url('timetableweek', 'index', array('user_id' => $values['user_id'])));
}
else {
$this->session->flashError(t('Unable to save this time slot.'));
}
}
$this->index($values, $errors);
}
/**
* Confirmation dialag box to remove a row
*
* @access public
*/
public function confirm()
{
$user = $this->getUser();
$this->response->html($this->layout('timetable_week/remove', array(
'slot_id' => $this->request->getIntegerParam('slot_id'),
'user' => $user,
)));
}
/**
* Remove a row
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
if ($this->timetableWeek->remove($this->request->getIntegerParam('slot_id'))) {
$this->session->flash(t('Time slot removed successfully.'));
}
else {
$this->session->flash(t('Unable to remove this time slot.'));
}
$this->response->redirect($this->helper->url('timetableweek', 'index', array('user_id' => $user['id'])));
}
}

View file

@ -0,0 +1,140 @@
<?php
namespace Controller;
use Otp\Otp;
use Otp\GoogleAuthenticator;
use Base32\Base32;
/**
* Two Factor Auth controller
*
* @package controller
* @author Frederic Guillot
*/
class Twofactor extends User
{
/**
* Only the current user can access to 2FA settings
*
* @access private
*/
private function checkCurrentUser(array $user)
{
if ($user['id'] != $this->userSession->getId()) {
$this->forbidden();
}
}
/**
* Index
*
* @access public
*/
public function index()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
$label = $user['email'] ?: $user['username'];
$this->response->html($this->layout('twofactor/index', array(
'user' => $user,
'qrcode_url' => $user['twofactor_activated'] == 1 ? GoogleAuthenticator::getQrCodeUrl('totp', $label, $user['twofactor_secret']) : '',
'key_url' => $user['twofactor_activated'] == 1 ? GoogleAuthenticator::getKeyUri('totp', $label, $user['twofactor_secret']) : '',
)));
}
/**
* Enable/disable 2FA
*
* @access public
*/
public function save()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
$values = $this->request->getValues();
if (isset($values['twofactor_activated']) && $values['twofactor_activated'] == 1) {
$this->user->update(array(
'id' => $user['id'],
'twofactor_activated' => 1,
'twofactor_secret' => GoogleAuthenticator::generateRandom(),
));
}
else {
$this->user->update(array(
'id' => $user['id'],
'twofactor_activated' => 0,
'twofactor_secret' => '',
));
}
// Allow the user to test or disable the feature
$_SESSION['user']['twofactor_activated'] = false;
$this->session->flash(t('User updated successfully.'));
$this->response->redirect($this->helper->url('twofactor', 'index', array('user_id' => $user['id'])));
}
/**
* Test 2FA
*
* @access public
*/
public function test()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
$otp = new Otp;
$values = $this->request->getValues();
if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) {
$this->session->flash(t('The two factor authentication code is valid.'));
}
else {
$this->session->flashError(t('The two factor authentication code is not valid.'));
}
$this->response->redirect($this->helper->url('twofactor', 'index', array('user_id' => $user['id'])));
}
/**
* Check 2FA
*
* @access public
*/
public function check()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
$otp = new Otp;
$values = $this->request->getValues();
if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) {
$this->session['2fa_validated'] = true;
$this->session->flash(t('The two factor authentication code is valid.'));
$this->response->redirect($this->helper->url('app', 'index'));
}
else {
$this->session->flashError(t('The two factor authentication code is not valid.'));
$this->response->redirect($this->helper->url('twofactor', 'code'));
}
}
/**
* Ask the 2FA code
*
* @access public
*/
public function code()
{
$this->response->html($this->template->layout('twofactor/check', array(
'title' => t('Check two factor authentication code'),
)));
}
}

View file

@ -69,12 +69,12 @@ class User extends Base
/** /**
* Common layout for user views * Common layout for user views
* *
* @access private * @access protected
* @param string $template Template name * @param string $template Template name
* @param array $params Template parameters * @param array $params Template parameters
* @return string * @return string
*/ */
private function layout($template, array $params) protected function layout($template, array $params)
{ {
$content = $this->template->render($template, $params); $content = $this->template->render($template, $params);
$params['user_content_for_layout'] = $content; $params['user_content_for_layout'] = $content;
@ -90,14 +90,14 @@ class User extends Base
/** /**
* Common method to get the user * Common method to get the user
* *
* @access private * @access protected
* @return array * @return array
*/ */
private function getUser() protected function getUser()
{ {
$user = $this->user->getById($this->request->getIntegerParam('user_id')); $user = $this->user->getById($this->request->getIntegerParam('user_id'));
if (! $user) { if (empty($user)) {
$this->notfound(); $this->notfound();
} }

View file

@ -502,7 +502,7 @@ class Helper
public function markdown($text, array $link = array()) public function markdown($text, array $link = array())
{ {
$parser = new Markdown($link, $this); $parser = new Markdown($link, $this);
$parser->setMarkupEscaped(true); $parser->setMarkupEscaped(MARKDOWN_ESCAPE_HTML);
return $parser->text($text); return $parser->text($text);
} }
@ -677,4 +677,114 @@ class Helper
array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'redirect' => $redirect) array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'redirect' => $redirect)
); );
} }
/**
* Get all hours for day
*
* @access public
* @return array
*/
public function getDayHours()
{
$values = array();
foreach (range(0, 23) as $hour) {
foreach (array(0, 30) as $minute) {
$time = sprintf('%02d:%02d', $hour, $minute);
$values[$time] = $time;
}
}
return $values;
}
/**
* Get all days of a week
*
* @access public
* @return array
*/
public function getWeekDays()
{
$values = array();
foreach (range(1, 7) as $day) {
$values[$day] = $this->getWeekDay($day);
}
return $values;
}
/**
* Get the localized day name from the day number
*
* @access public
* @param integer $day Day number
* @return string
*/
public function getWeekDay($day)
{
return dt('%A', strtotime('next Monday +'.($day - 1).' days'));
}
/**
* Get file icon
*
* @access public
* @param string $filename Filename
* @return string Font-Awesome-Icon-Name
*/
public function getFileIcon($filename){
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
switch ($extension) {
case 'jpeg':
case 'jpg':
case 'png':
case 'gif':
return 'fa-file-image-o';
case 'xls':
case 'xlsx':
return 'fa-file-excel-o';
case 'doc':
case 'docx':
return 'fa-file-word-o';
case 'ppt':
case 'pptx':
return 'fa-file-powerpoint-o';
case 'zip':
case 'rar':
return 'fa-archive-o';
case 'mp3':
return 'fa-audio-o';
case 'avi':
return 'fa-video-o';
case 'php':
case 'html':
case 'css':
return 'fa-code-o';
case 'pdf':
return 'fa-file-pdf-o';
}
return 'fa-file-o';
}
/**
* Display gravatar image
*
* @access public
* @param string $email
* @param string $alt
* @return string
*/
public function avatar($email, $alt = '')
{
if (! empty($email) && $this->config->get('integration_gravatar') == 1) {
return '<img class="avatar" src="https://www.gravatar.com/avatar/'.md5(strtolower($email)).'?s=25" alt="'.$this->e($alt).'" title="'.$this->e($alt).'">';
}
return '';
}
} }

View file

@ -0,0 +1,68 @@
<?php
namespace Core;
/**
* HTTP client
*
* @package core
* @author Frederic Guillot
*/
class HttpClient
{
/**
* HTTP connection timeout in seconds
*
* @var integer
*/
const HTTP_TIMEOUT = 2;
/**
* Number of maximum redirections for the HTTP client
*
* @var integer
*/
const HTTP_MAX_REDIRECTS = 2;
/**
* HTTP client user agent
*
* @var string
*/
const HTTP_USER_AGENT = 'Kanboard Webhook';
/**
* Send a POST HTTP request
*
* @static
* @access public
* @param string $url
* @param array $data
* @return string
*/
public static function post($url, array $data)
{
if (empty($url)) {
return '';
}
$headers = array(
'User-Agent: '.self::HTTP_USER_AGENT,
'Content-Type: application/json',
'Connection: close',
);
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'protocol_version' => 1.1,
'timeout' => self::HTTP_TIMEOUT,
'max_redirects' => self::HTTP_MAX_REDIRECTS,
'header' => implode("\r\n", $headers),
'content' => json_encode($data)
)
));
return @file_get_contents(trim($url), false, $context);
}
}

View file

@ -10,6 +10,7 @@ use Pimple\Container;
* @package integration * @package integration
* @author Frederic Guillot * @author Frederic Guillot
* *
* @property \Model\ProjectActivity $projectActivity
* @property \Model\Task $task * @property \Model\Task $task
* @property \Model\TaskFinder $taskFinder * @property \Model\TaskFinder $taskFinder
* @property \Model\User $user * @property \Model\User $user

View file

@ -78,7 +78,7 @@ class BitbucketWebhook extends Base
$task = $this->taskFinder->getById($task_id); $task = $this->taskFinder->getById($task_id);
if (! $task) { if (empty($task)) {
return false; return false;
} }

View file

@ -86,7 +86,7 @@ class GithubWebhook extends Base
$task = $this->taskFinder->getById($task_id); $task = $this->taskFinder->getById($task_id);
if (! $task) { if (empty($task)) {
continue; continue;
} }
@ -142,7 +142,7 @@ class GithubWebhook extends Base
$task = $this->taskFinder->getByReference($payload['issue']['number']); $task = $this->taskFinder->getByReference($payload['issue']['number']);
$user = $this->user->getByUsername($payload['comment']['user']['login']); $user = $this->user->getByUsername($payload['comment']['user']['login']);
if ($task && $user) { if (! empty($task) && ! empty($user)) {
$event = array( $event = array(
'project_id' => $this->project_id, 'project_id' => $this->project_id,
@ -198,7 +198,7 @@ class GithubWebhook extends Base
{ {
$task = $this->taskFinder->getByReference($issue['number']); $task = $this->taskFinder->getByReference($issue['number']);
if ($task) { if (! empty($task)) {
$event = array( $event = array(
'project_id' => $this->project_id, 'project_id' => $this->project_id,
'task_id' => $task['id'], 'task_id' => $task['id'],
@ -227,7 +227,7 @@ class GithubWebhook extends Base
{ {
$task = $this->taskFinder->getByReference($issue['number']); $task = $this->taskFinder->getByReference($issue['number']);
if ($task) { if (! empty($task)) {
$event = array( $event = array(
'project_id' => $this->project_id, 'project_id' => $this->project_id,
'task_id' => $task['id'], 'task_id' => $task['id'],
@ -257,7 +257,7 @@ class GithubWebhook extends Base
$user = $this->user->getByUsername($issue['assignee']['login']); $user = $this->user->getByUsername($issue['assignee']['login']);
$task = $this->taskFinder->getByReference($issue['number']); $task = $this->taskFinder->getByReference($issue['number']);
if ($user && $task) { if (! empty($user) && ! empty($task)) {
$event = array( $event = array(
'project_id' => $this->project_id, 'project_id' => $this->project_id,
@ -288,7 +288,7 @@ class GithubWebhook extends Base
{ {
$task = $this->taskFinder->getByReference($issue['number']); $task = $this->taskFinder->getByReference($issue['number']);
if ($task) { if (! empty($task)) {
$event = array( $event = array(
'project_id' => $this->project_id, 'project_id' => $this->project_id,
@ -320,7 +320,7 @@ class GithubWebhook extends Base
{ {
$task = $this->taskFinder->getByReference($issue['number']); $task = $this->taskFinder->getByReference($issue['number']);
if ($task) { if (! empty($task)) {
$event = array( $event = array(
'project_id' => $this->project_id, 'project_id' => $this->project_id,
@ -352,7 +352,7 @@ class GithubWebhook extends Base
{ {
$task = $this->taskFinder->getByReference($issue['number']); $task = $this->taskFinder->getByReference($issue['number']);
if ($task) { if (! empty($task)) {
$event = array( $event = array(
'project_id' => $this->project_id, 'project_id' => $this->project_id,

View file

@ -122,7 +122,7 @@ class GitlabWebhook extends Base
$task = $this->taskFinder->getById($task_id); $task = $this->taskFinder->getById($task_id);
if (! $task) { if (empty($task)) {
return false; return false;
} }
@ -193,7 +193,7 @@ class GitlabWebhook extends Base
{ {
$task = $this->taskFinder->getByReference($issue['id']); $task = $this->taskFinder->getByReference($issue['id']);
if ($task) { if (! empty($task)) {
$event = array( $event = array(
'project_id' => $this->project_id, 'project_id' => $this->project_id,
'task_id' => $task['id'], 'task_id' => $task['id'],

View file

@ -0,0 +1,53 @@
<?php
namespace Integration;
/**
* Hipchat Webhook
*
* @package integration
* @author Frederic Guillot
*/
class Hipchat extends Base
{
/**
* Send message to the Hipchat room
*
* @access public
* @param integer $project_id Project id
* @param integer $task_id Task id
* @param string $event_name Event name
* @param array $event Event data
*/
public function notify($project_id, $task_id, $event_name, array $event)
{
$project = $this->project->getbyId($project_id);
$event['event_name'] = $event_name;
$event['author'] = $this->user->getFullname($this->session['user']);
$html = '<img src="http://kanboard.net/assets/img/favicon-32x32.png"/>';
$html .= '<strong>'.$project['name'].'</strong><br/>';
$html .= $this->projectActivity->getTitle($event);
if ($this->config->get('application_url')) {
$html .= '<br/><a href="'.$this->config->get('application_url');
$html .= $this->helper->u('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id)).'">';
$html .= t('view the task on Kanboard').'</a>';
}
$payload = array(
'message' => $html,
'color' => 'yellow',
);
$url = sprintf(
'%s/v2/room/%s/notification?auth_token=%s',
$this->config->get('integration_hipchat_api_url'),
$this->config->get('integration_hipchat_room_id'),
$this->config->get('integration_hipchat_room_token')
);
$this->httpClient->post($url, $payload);
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace Integration;
/**
* Slack Webhook
*
* @package integration
* @author Frederic Guillot
*/
class SlackWebhook extends Base
{
/**
* Send message to the incoming Slack webhook
*
* @access public
* @param integer $project_id Project id
* @param integer $task_id Task id
* @param string $event_name Event name
* @param array $event Event data
*/
public function notify($project_id, $task_id, $event_name, array $event)
{
$project = $this->project->getbyId($project_id);
$event['event_name'] = $event_name;
$event['author'] = $this->user->getFullname($this->session['user']);
$payload = array(
'text' => '*['.$project['name'].']* '.str_replace('&quot;', '"', $this->projectActivity->getTitle($event)),
'username' => 'Kanboard',
'icon_url' => 'http://kanboard.net/assets/img/favicon.png',
);
if ($this->config->get('application_url')) {
$payload['text'] .= ' - <'.$this->config->get('application_url');
$payload['text'] .= $this->helper->u('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id));
$payload['text'] .= '|'.t('view the task on Kanboard').'>';
}
$this->httpClient->post($this->config->get('integration_slack_webhook_url'), $payload);
}
}

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Ingen', 'None' => 'Ingen',
'edit' => 'rediger', 'edit' => 'rediger',
'Edit' => 'Rediger', 'Edit' => 'Rediger',
@ -734,4 +736,114 @@ return array(
// 'Filter recently updated' => '', // 'Filter recently updated' => '',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', // 'More filters' => '',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
'number.decimals_separator' => ',',
'number.thousands_separator' => '.',
'None' => 'Keines', 'None' => 'Keines',
'edit' => 'Bearbeiten', 'edit' => 'Bearbeiten',
'Edit' => 'Bearbeiten', 'Edit' => 'Bearbeiten',
@ -408,13 +410,13 @@ return array(
'Comment updated' => 'Kommentar wurde aktualisiert', 'Comment updated' => 'Kommentar wurde aktualisiert',
'New comment posted by %s' => 'Neuer Kommentar verfasst durch %s', 'New comment posted by %s' => 'Neuer Kommentar verfasst durch %s',
'List of due tasks for the project "%s"' => 'Liste der fälligen Aufgaben für das Projekt "%s"', 'List of due tasks for the project "%s"' => 'Liste der fälligen Aufgaben für das Projekt "%s"',
// 'New attachment' => '', 'New attachment' => 'Neuer Anhang',
// 'New comment' => '', 'New comment' => 'Neuer Kommentar',
// 'New subtask' => '', 'New subtask' => 'Neue Teilaufgabe',
// 'Subtask updated' => '', 'Subtask updated' => 'Teilaufgabe aktualisiert',
// 'Task updated' => '', 'Task updated' => 'Aufgabe aktualisiert',
// 'Task closed' => '', 'Task closed' => 'Aufgabe geschlossen',
// 'Task opened' => '', 'Task opened' => 'Aufgabe geöffnet',
'[%s][Due tasks]' => '[%s][Fällige Aufgaben]', '[%s][Due tasks]' => '[%s][Fällige Aufgaben]',
'[Kanboard] Notification' => '[Kanboard] Benachrichtigung', '[Kanboard] Notification' => '[Kanboard] Benachrichtigung',
'I want to receive notifications only for those projects:' => 'Ich möchte nur für diese Projekte Benachrichtigungen erhalten:', 'I want to receive notifications only for those projects:' => 'Ich möchte nur für diese Projekte Benachrichtigungen erhalten:',
@ -498,9 +500,9 @@ return array(
'Task assignee change' => 'Zuständigkeit geändert', 'Task assignee change' => 'Zuständigkeit geändert',
'%s change the assignee of the task #%d to %s' => '%s hat die Zusständigkeit der Aufgabe #%d geändert um %s', '%s change the assignee of the task #%d to %s' => '%s hat die Zusständigkeit der Aufgabe #%d geändert um %s',
'%s changed the assignee of the task %s to %s' => '%s hat die Zuständigkeit der Aufgabe %s geändert um %s', '%s changed the assignee of the task %s to %s' => '%s hat die Zuständigkeit der Aufgabe %s geändert um %s',
// 'Column Change' => '', 'Column Change' => 'Spalte ändern',
// 'Position Change' => '', 'Position Change' => 'Position ändern',
// 'Assignee Change' => '', 'Assignee Change' => 'Zuordnung ändern',
'New password for the user "%s"' => 'Neues Passwort des Benutzers "%s"', 'New password for the user "%s"' => 'Neues Passwort des Benutzers "%s"',
'Choose an event' => 'Aktion wählen', 'Choose an event' => 'Aktion wählen',
'Github commit received' => 'Github commit empfangen', 'Github commit received' => 'Github commit empfangen',
@ -625,8 +627,8 @@ return array(
'Example: "Bug, Feature Request, Improvement"' => 'Beispiel: "Bug, Funktionswünsche, Verbesserung"', 'Example: "Bug, Feature Request, Improvement"' => 'Beispiel: "Bug, Funktionswünsche, Verbesserung"',
'Default categories for new projects (Comma-separated)' => 'Standard Kategorien für neue Projekte (Komma-getrennt)', 'Default categories for new projects (Comma-separated)' => 'Standard Kategorien für neue Projekte (Komma-getrennt)',
'Gitlab commit received' => 'Gitlab commit erhalten', 'Gitlab commit received' => 'Gitlab commit erhalten',
'Gitlab issue opened' => 'Gitlab Thema eröffnet', 'Gitlab issue opened' => 'Gitlab Fehler eröffnet',
'Gitlab issue closed' => 'Gitlab Thema geschlossen', 'Gitlab issue closed' => 'Gitlab Fehler geschlossen',
'Gitlab webhooks' => 'Gitlab Webhook', 'Gitlab webhooks' => 'Gitlab Webhook',
'Help on Gitlab webhooks' => 'Hilfe für Gitlab Webhooks', 'Help on Gitlab webhooks' => 'Hilfe für Gitlab Webhooks',
'Integrations' => 'Integration', 'Integrations' => 'Integration',
@ -635,7 +637,7 @@ return array(
'Project manager' => 'Projektmanager', 'Project manager' => 'Projektmanager',
'Project member' => 'Projektmitglied', 'Project member' => 'Projektmitglied',
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Ein Projektmanager kann die Projekteinstellungen ändern und hat mehr Rechte als ein normaler Benutzer.', 'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Ein Projektmanager kann die Projekteinstellungen ändern und hat mehr Rechte als ein normaler Benutzer.',
'Gitlab Issue' => 'Gitlab Thema', 'Gitlab Issue' => 'Gitlab Fehler',
'Subtask Id' => 'Teilaufgaben Id', 'Subtask Id' => 'Teilaufgaben Id',
'Subtasks' => 'Teilaufgaben', 'Subtasks' => 'Teilaufgaben',
'Subtasks Export' => 'Teilaufgaben Export', 'Subtasks Export' => 'Teilaufgaben Export',
@ -734,4 +736,114 @@ return array(
'Filter recently updated' => 'Zuletzt geänderte anzeigen', 'Filter recently updated' => 'Zuletzt geänderte anzeigen',
'since %B %e, %Y at %k:%M %p' => 'seit %B %e, %Y um %k:%M %p', 'since %B %e, %Y at %k:%M %p' => 'seit %B %e, %Y um %k:%M %p',
'More filters' => 'Mehr Filter', 'More filters' => 'Mehr Filter',
'Compact view' => 'Kompaktansicht',
'Horizontal scrolling' => 'Horizontales Scrollen',
'Compact/wide view' => 'Kompakt/Breite-Ansicht',
'No results match:' => 'Keine Ergebnisse:',
'Remove hourly rate' => 'Stundensatz entfernen',
'Do you really want to remove this hourly rate?' => 'Diesen Stundensatz wirklich entfernen?',
'Hourly rates' => 'Stundensätze',
'Hourly rate' => 'Stundensatz',
'Currency' => 'Währung',
'Effective date' => 'Inkraftsetzung',
'Add new rate' => 'Neue Rate hinzufügen',
'Rate removed successfully.' => 'Rate erfolgreich entfernt',
'Unable to remove this rate.' => 'Nicht in der Lage, diese Rate zu entfernen.',
'Unable to save the hourly rate.' => 'Nicht in der Lage, diese Rate zu speichern',
'Hourly rate created successfully.' => 'Stundensatz erfolgreich angelegt.',
'Start time' => 'Startzeit',
'End time' => 'Endzeit',
'Comment' => 'Kommentar',
'All day' => 'ganztägig',
'Day' => 'Tag',
'Manage timetable' => 'Zeitplan verwalten',
'Overtime timetable' => 'Überstunden Zeitplan',
'Time off timetable' => 'Freizeit Zeitplan',
'Timetable' => 'Zeitplan',
'Work timetable' => 'Arbeitszeitplan',
'Week timetable' => 'Wochenzeitplan',
'Day timetable' => 'Tageszeitplan',
'From' => 'von',
'To' => 'bis',
'Time slot created successfully.' => 'Zeitfenster erfolgreich erstellt.',
'Unable to save this time slot.' => 'Nicht in der Lage, dieses Zeitfenster zu speichern.',
'Time slot removed successfully.' => 'Zeitfenster erfolgreich entfernt.',
'Unable to remove this time slot.' => 'Nicht in der Lage, dieses Zeitfenster zu entfernen',
'Do you really want to remove this time slot?' => 'Soll diese Zeitfenster wirklich gelöscht werden?',
'Remove time slot' => 'Zeitfenster entfernen',
'Add new time slot' => 'Neues Zeitfenster hinzufügen',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Dieses Zeitfenster wird verwendet, wenn die Checkbox "gantägig" für Freizeit und Überstunden angeklickt ist.',
'Files' => 'Dateien',
'Images' => 'Bilder',
'Private project' => 'privates Projekt',
'Amount' => 'Betrag',
// 'AUD - Australian Dollar' => '',
'Budget' => 'Budget',
'Budget line' => 'Budgetlinie',
'Budget line removed successfully.' => 'Budgetlinie erfolgreich entfernt',
'Budget lines' => 'Budgetlinien',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
'Cost' => 'Kosten',
'Cost breakdown' => 'Kostenaufschlüsselung',
'Custom Stylesheet' => 'benutzerdefiniertes Stylesheet',
'download' => 'Download',
'Do you really want to remove this budget line?' => 'Soll diese Budgetlinie wirklich entfernt werden?',
// 'EUR - Euro' => '',
'Expenses' => 'Kosten',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
'New budget line' => 'Neue Budgetlinie',
// 'NZD - New Zealand Dollar' => '',
'Remove a budget line' => 'Budgetlinie entfernen',
'Remove budget line' => 'Budgetlinie entfernen',
// 'RSD - Serbian dinar' => '',
'The budget line have been created successfully.' => 'Die Budgetlinie wurde erfolgreich angelegt.',
'Unable to create the budget line.' => 'Budgetlinie konnte nicht erstellt werden.',
'Unable to remove this budget line.' => 'Budgetlinie konnte nicht gelöscht werden.',
// 'USD - US Dollar' => '',
'Remaining' => 'Verbleibend',
'Destination column' => 'Zielspalte',
'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein User zugeordnet wurde.',
'Move the task to another column when assignee is cleared' => 'Aufgabe in eine andere Spalte verschieben, wenn die Zuordnung gelöscht wurde.',
'Source column' => 'Quellspalte',
'Show subtask estimates in the user calendar' => 'Teilaufgabenschätzung in Benutzerkalender anzeigen.',
'Transitions' => 'Übergänge',
'Executer' => 'Ausführender',
'Time spent in the column' => 'Zeit in Spalte verbracht',
'Task transitions' => 'Aufgaben Übergänge',
'Task transitions export' => 'Aufgaben Übergänge exportieren',
'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Diese Auswertung enthält alle Spaltenbewegungen für jede Aufgabe mit Datum, Benutzer und Zeit vor jedem Wechsel.',
'Currency rates' => 'Währungskurse',
'Rate' => 'Kurse',
'Change reference currency' => 'Referenzwährung ändern',
'Add a new currency rate' => 'Neuen Währungskurs hinzufügen',
'Currency rates are used to calculate project budget.' => 'Währungskurse werden verwendet um das Projektbudget zu berechnen.',
'Reference currency' => 'Referenzwährung',
'The currency rate have been added successfully.' => 'Der Währungskurs wurde erfolgreich hinzugefügt.',
'Unable to add this currency rate.' => 'Währungskurs konnte nicht hinzugefügt werden',
'Send notifications to a Slack channel' => 'Benachrichtigung an einen Slack-Kanal senden',
'Webhook URL' => 'Webhook URL',
'Help on Slack integration' => 'Hilfe für Slack integration.',
'%s remove the assignee of the task %s' => '%s Zuordnung für die Aufgabe %s entfernen',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Ninguno', 'None' => 'Ninguno',
'edit' => 'modificar', 'edit' => 'modificar',
'Edit' => 'Modificar', 'Edit' => 'Modificar',
@ -734,4 +736,114 @@ return array(
// 'Filter recently updated' => '', // 'Filter recently updated' => '',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', // 'More filters' => '',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Ei mikään', 'None' => 'Ei mikään',
'edit' => 'muokkaa', 'edit' => 'muokkaa',
'Edit' => 'Muokkaa', 'Edit' => 'Muokkaa',
@ -734,4 +736,114 @@ return array(
// 'Filter recently updated' => '', // 'Filter recently updated' => '',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', // 'More filters' => '',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
'number.decimals_separator' => ',',
'number.thousands_separator' => ' ',
'None' => 'Aucun', 'None' => 'Aucun',
'edit' => 'modifier', 'edit' => 'modifier',
'Edit' => 'Modifier', 'Edit' => 'Modifier',
@ -736,4 +738,114 @@ return array(
'Filter recently updated' => 'Récemment modifié', 'Filter recently updated' => 'Récemment modifié',
'since %B %e, %Y at %k:%M %p' => 'depuis le %d/%m/%Y à %H:%M', 'since %B %e, %Y at %k:%M %p' => 'depuis le %d/%m/%Y à %H:%M',
'More filters' => 'Plus de filtres', 'More filters' => 'Plus de filtres',
'Compact view' => 'Vue compacte',
'Horizontal scrolling' => 'Défilement horizontal',
'Compact/wide view' => 'Basculer entre la vue compacte et étendue',
'No results match:' => 'Aucun résultat :',
'Remove hourly rate' => 'Supprimer un taux horaire',
'Do you really want to remove this hourly rate?' => 'Voulez-vous vraiment supprimer ce taux horaire ?',
'Hourly rates' => 'Taux horaires',
'Hourly rate' => 'Taux horaire',
'Currency' => 'Devise',
'Effective date' => 'Date d\'effet',
'Add new rate' => 'Ajouter un nouveau taux horaire',
'Rate removed successfully.' => 'Taux horaire supprimé avec succès.',
'Unable to remove this rate.' => 'Impossible de supprimer ce taux horaire.',
'Unable to save the hourly rate.' => 'Impossible de sauvegarder ce taux horaire.',
'Hourly rate created successfully.' => 'Taux horaire créé avec succès.',
'Start time' => 'Date de début',
'End time' => 'Date de fin',
'Comment' => 'Commentaire',
'All day' => 'Toute la journée',
'Day' => 'Jour',
'Manage timetable' => 'Gérer les horaires',
'Overtime timetable' => 'Heures supplémentaires',
'Time off timetable' => 'Heures d\'absences',
'Timetable' => 'Horaires',
'Work timetable' => 'Horaires travaillés',
'Week timetable' => 'Horaires de la semaine',
'Day timetable' => 'Horaire d\'une journée',
'From' => 'Depuis',
'To' => 'À',
'Time slot created successfully.' => 'Créneau horaire créé avec succès.',
'Unable to save this time slot.' => 'Impossible de sauvegarder ce créneau horaire.',
'Time slot removed successfully.' => 'Créneau horaire supprimé avec succès.',
'Unable to remove this time slot.' => 'Impossible de supprimer ce créneau horaire.',
'Do you really want to remove this time slot?' => 'Voulez-vous vraiment supprimer ce créneau horaire ?',
'Remove time slot' => 'Supprimer un créneau horaire',
'Add new time slot' => 'Ajouter un créneau horaire',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ces horaires sont utilisés lorsque la case « Toute la journée » est cochée pour les heures d\'absences ou supplémentaires programmées.',
'Files' => 'Fichiers',
'Images' => 'Images',
'Private project' => 'Projet privé',
'Amount' => 'Montant',
'AUD - Australian Dollar' => 'AUD - Dollar australien',
'Budget' => 'Budget',
'Budget line' => 'Ligne budgétaire',
'Budget line removed successfully.' => 'Ligne budgétaire supprimée avec succès.',
'Budget lines' => 'Lignes budgétaire',
'CAD - Canadian Dollar' => 'CAD - Dollar canadien',
'CHF - Swiss Francs' => 'CHF - Franc suisse',
'Cost' => 'Coût',
'Cost breakdown' => 'Détail des coûts',
'Custom Stylesheet' => 'Feuille de style personalisée',
'download' => 'télécharger',
'Do you really want to remove this budget line?' => 'Voulez-vous vraiment supprimer cette ligne budgétaire ?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Dépenses',
'GBP - British Pound' => 'GBP - Livre sterling',
'INR - Indian Rupee' => 'INR - Roupie indienne',
'JPY - Japanese Yen' => 'JPY - Yen',
'New budget line' => 'Nouvelle ligne budgétaire',
'NZD - New Zealand Dollar' => 'NZD - Dollar néo-zélandais',
'Remove a budget line' => 'Supprimer une ligne budgétaire',
'Remove budget line' => 'Supprimer une ligne budgétaire',
'RSD - Serbian dinar' => 'RSD - Dinar serbe',
'The budget line have been created successfully.' => 'La ligne de budgétaire a été créée avec succès.',
'Unable to create the budget line.' => 'Impossible de créer cette ligne budgétaire.',
'Unable to remove this budget line.' => 'Impossible de supprimer cette ligne budgétaire.',
'USD - US Dollar' => 'USD - Dollar américain',
'Remaining' => 'Restant',
'Destination column' => 'Colonne de destination',
'Move the task to another column when assigned to a user' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci est assignée à quelqu\'un',
'Move the task to another column when assignee is cleared' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci n\'est plus assignée',
'Source column' => 'Colonne d\'origine',
'Show subtask estimates in the user calendar' => 'Afficher le temps estimé des sous-tâches dans le calendrier utilisateur',
'Transitions' => 'Transitions',
'Executer' => 'Exécutant',
'Time spent in the column' => 'Temps passé dans la colonne',
'Task transitions' => 'Transitions des tâches',
'Task transitions export' => 'Export des transitions des tâches',
'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ce rapport contient tous les mouvements de colonne pour chaque tâche avec la date, l\'utilisateur et le temps passé pour chaque transition.',
'Currency rates' => 'Taux de change des devises',
'Rate' => 'Taux',
'Change reference currency' => 'Changer la monnaie de référence',
'Add a new currency rate' => 'Ajouter un nouveau taux pour une devise',
'Currency rates are used to calculate project budget.' => 'Le cours des devises est utilisé pour calculer le budget des projets.',
'Reference currency' => 'Devise de référence',
'The currency rate have been added successfully.' => 'Le taux de change a été ajouté avec succès.',
'Unable to add this currency rate.' => 'Impossible d\'ajouter ce taux de change',
'Send notifications to a Slack channel' => 'Envoyer les notifications sur un salon de discussion Slack',
'Webhook URL' => 'URL du webhook',
'Help on Slack integration' => 'Aide sur l\'intégration avec Slack',
'%s remove the assignee of the task %s' => '%s a enlevé la personne assignée à la tâche %s',
'Send notifications to Hipchat' => 'Envoyer les notifications vers Hipchat',
'API URL' => 'URL de l\'api',
'Room API ID or name' => 'Nom ou identifiant du salon de discussion',
'Room notification token' => 'Jeton de sécurité du salon de discussion',
'Help on Hipchat integration' => 'Aide sur l\'intégration avec Hipchat',
'Enable Gravatar images' => 'Activer les images Gravatar',
'Information' => 'Informations',
'Check two factor authentication code' => 'Vérification du code pour l\'authentification à deux-facteurs',
'The two factor authentication code is not valid.' => 'Le code pour l\'authentification à deux-facteurs n\'est pas valide.',
'The two factor authentication code is valid.' => 'Le code pour l\'authentification à deux-facteurs est valide.',
'Code' => 'Code',
'Two factor authentication' => 'Authentification à deux-facteurs',
'Enable/disable two factor authentication' => 'Activer/désactiver l\'authentification à deux-facteurs',
'This QR code contains the key URI: ' => 'Ce code QR contient l\'url de la clé : ',
'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => 'Sauvegardez cette clé secrete dans votre logiciel TOTP (par exemple Google Authenticator ou FreeOTP).',
'Check my code' => 'Vérifier mon code',
'Secret key: ' => 'Clé secrète : ',
'Test your device' => 'Testez votre appareil',
'Assign a color when the task is moved to a specific column' => 'Assigner une couleur lorsque la tâche est déplacée dans une colonne spécifique',
); );

View file

@ -1,11 +1,13 @@
<?php <?php
return array( return array(
'number.decimals_separator' => ',',
'number.thousands_separator' => ' ',
'None' => 'Nincs', 'None' => 'Nincs',
'edit' => 'szerkesztés', 'edit' => 'szerkesztés',
'Edit' => 'Szerkesztés', 'Edit' => 'Szerkesztés',
'remove' => 'eltávolítás', 'remove' => 'törlés',
'Remove' => 'Eltávolítás', 'Remove' => 'Törlés',
'Update' => 'Frissítés', 'Update' => 'Frissítés',
'Yes' => 'Igen', 'Yes' => 'Igen',
'No' => 'Nem', 'No' => 'Nem',
@ -104,7 +106,7 @@ return array(
'Open a task' => 'Feladat felnyitás', 'Open a task' => 'Feladat felnyitás',
'Do you really want to open this task: "%s"?' => 'Tényleg meg akarja nyitni ezt a feladatot: "%s"?', 'Do you really want to open this task: "%s"?' => 'Tényleg meg akarja nyitni ezt a feladatot: "%s"?',
'Back to the board' => 'Vissza a táblához', 'Back to the board' => 'Vissza a táblához',
'Created on %B %e, %Y at %k:%M %p' => 'Létrehozva: %Y.%m.%d %H:%M', 'Created on %B %e, %Y at %k:%M %p' => 'Létrehozva: %Y. %m. %d. %H:%M',
'There is nobody assigned' => 'Nincs felelős', 'There is nobody assigned' => 'Nincs felelős',
'Column on the board:' => 'Tábla oszlopa: ', 'Column on the board:' => 'Tábla oszlopa: ',
'Status is open' => 'Nyitott állapot', 'Status is open' => 'Nyitott állapot',
@ -133,12 +135,12 @@ return array(
'The title is required' => 'A címet meg kell adni', 'The title is required' => 'A címet meg kell adni',
'The language is required' => 'A nyelvet meg kell adni', 'The language is required' => 'A nyelvet meg kell adni',
'There is no active project, the first step is to create a new project.' => 'Nincs aktív projekt. Először létre kell hozni egy projektet.', 'There is no active project, the first step is to create a new project.' => 'Nincs aktív projekt. Először létre kell hozni egy projektet.',
'Settings saved successfully.' => 'A beállítások mentése sikeres.', 'Settings saved successfully.' => 'A beállítások sikeresen mentve.',
'Unable to save your settings.' => 'A beállítások mentése sikertelen.', 'Unable to save your settings.' => 'A beállítások mentése sikertelen.',
'Database optimization done.' => 'Adatbázis optimalizálás kész.', 'Database optimization done.' => 'Adatbázis optimalizálás kész.',
'Your project have been created successfully.' => 'Projekt sikeresen létrehozva', 'Your project have been created successfully.' => 'Projekt sikeresen létrehozva',
'Unable to create your project.' => 'Projekt létrehozása sikertelen.', 'Unable to create your project.' => 'Projekt létrehozása sikertelen.',
'Project updated successfully.' => 'Projekt frissítése sikeres.', 'Project updated successfully.' => 'Projekt sikeresen frissítve.',
'Unable to update this project.' => 'Projekt frissítése sikertelen.', 'Unable to update this project.' => 'Projekt frissítése sikertelen.',
'Unable to remove this project.' => 'Projekt törlése sikertelen.', 'Unable to remove this project.' => 'Projekt törlése sikertelen.',
'Project removed successfully.' => 'Projekt sikeresen törölve.', 'Project removed successfully.' => 'Projekt sikeresen törölve.',
@ -146,7 +148,7 @@ return array(
'Unable to activate this project.' => 'Projekt aktiválása sikertelen.', 'Unable to activate this project.' => 'Projekt aktiválása sikertelen.',
'Project disabled successfully.' => 'Projekt sikeresen letiltva.', 'Project disabled successfully.' => 'Projekt sikeresen letiltva.',
'Unable to disable this project.' => 'Projekt letiltása sikertelen.', 'Unable to disable this project.' => 'Projekt letiltása sikertelen.',
'Unable to open this task.' => 'A feladat felnyitása nem sikerült.', 'Unable to open this task.' => 'A feladat felnyitása sikertelen.',
'Task opened successfully.' => 'Feladat sikeresen megnyitva .', 'Task opened successfully.' => 'Feladat sikeresen megnyitva .',
'Unable to close this task.' => 'A feladat lezárása sikertelen.', 'Unable to close this task.' => 'A feladat lezárása sikertelen.',
'Task closed successfully.' => 'Feladat sikeresen lezárva.', 'Task closed successfully.' => 'Feladat sikeresen lezárva.',
@ -166,8 +168,8 @@ return array(
'Work in progress' => 'Folyamatban', 'Work in progress' => 'Folyamatban',
'Done' => 'Kész', 'Done' => 'Kész',
'Application version:' => 'Alkalmazás verzió:', 'Application version:' => 'Alkalmazás verzió:',
'Completed on %B %e, %Y at %k:%M %p' => 'Elkészült %Y.%m.%d %H:%M ..', 'Completed on %B %e, %Y at %k:%M %p' => 'Elkészült: %Y. %m. %d. %H:%M',
'%B %e, %Y at %k:%M %p' => '%Y.%m.%d %H:%M', '%B %e, %Y at %k:%M %p' => '%Y. %m. %d. %H:%M',
'Date created' => 'Létrehozás időpontja', 'Date created' => 'Létrehozás időpontja',
'Date completed' => 'Befejezés időpontja', 'Date completed' => 'Befejezés időpontja',
'Id' => 'ID', 'Id' => 'ID',
@ -211,9 +213,9 @@ return array(
'Edit this task' => 'Feladat módosítása', 'Edit this task' => 'Feladat módosítása',
'Due Date' => 'Határidő', 'Due Date' => 'Határidő',
'Invalid date' => 'Érvénytelen dátum', 'Invalid date' => 'Érvénytelen dátum',
'Must be done before %B %e, %Y' => 'Kész kell lennie %Y.%m.%d előtt', 'Must be done before %B %e, %Y' => 'Kész kell lennie %Y. %m. %d. előtt',
'%B %e, %Y' => '%Y.%m.%d', '%B %e, %Y' => '%Y. %m. %d.',
'%b %e, %Y' => '%Y.%m.%d', '%b %e, %Y' => '%Y. %m. %d.',
'Automatic actions' => 'Automatikus intézkedések', 'Automatic actions' => 'Automatikus intézkedések',
'Your automatic action have been created successfully.' => 'Az automatikus intézkedés sikeresen elkészült.', 'Your automatic action have been created successfully.' => 'Az automatikus intézkedés sikeresen elkészült.',
'Unable to create your automatic action.' => 'Automatikus intézkedés létrehozása nem lehetséges.', 'Unable to create your automatic action.' => 'Automatikus intézkedés létrehozása nem lehetséges.',
@ -254,7 +256,7 @@ return array(
'link' => 'link', 'link' => 'link',
'Update this comment' => 'Hozzászólás frissítése', 'Update this comment' => 'Hozzászólás frissítése',
'Comment updated successfully.' => 'Megjegyzés sikeresen frissítve.', 'Comment updated successfully.' => 'Megjegyzés sikeresen frissítve.',
'Unable to update your comment.' => 'Megjegyzés frissítése nem sikerült.', 'Unable to update your comment.' => 'Megjegyzés frissítése sikertelen.',
'Remove a comment' => 'Megjegyzés törlése', 'Remove a comment' => 'Megjegyzés törlése',
'Comment removed successfully.' => 'Megjegyzés sikeresen törölve.', 'Comment removed successfully.' => 'Megjegyzés sikeresen törölve.',
'Unable to remove this comment.' => 'Megjegyzés törölése nem lehetséges.', 'Unable to remove this comment.' => 'Megjegyzés törölése nem lehetséges.',
@ -294,8 +296,8 @@ return array(
'Your Google Account is not linked anymore to your profile.' => 'Google Fiók már nincs a profilhoz kapcsolva.', 'Your Google Account is not linked anymore to your profile.' => 'Google Fiók már nincs a profilhoz kapcsolva.',
'Unable to unlink your Google Account.' => 'Leválasztás a Google fiókról nem lehetséges.', 'Unable to unlink your Google Account.' => 'Leválasztás a Google fiókról nem lehetséges.',
'Google authentication failed' => 'Google azonosítás sikertelen', 'Google authentication failed' => 'Google azonosítás sikertelen',
'Unable to link your Google Account.' => 'Google profilhoz kapcsolás nem sikerült.', 'Unable to link your Google Account.' => 'A Google profilhoz kapcsolás sikertelen.',
'Your Google Account is linked to your profile successfully.' => 'Sikeresen összekapcsolva a Google fiókkal.', 'Your Google Account is linked to your profile successfully.' => 'Google fiókkal sikeresen összekapcsolva.',
'Email' => 'E-mail', 'Email' => 'E-mail',
'Link my Google Account' => 'Kapcsold össze a Google fiókkal', 'Link my Google Account' => 'Kapcsold össze a Google fiókkal',
'Unlink my Google Account' => 'Válaszd le a Google fiókomat', 'Unlink my Google Account' => 'Válaszd le a Google fiókomat',
@ -377,7 +379,7 @@ return array(
'Link my GitHub Account' => 'GitHub fiók csatolása', 'Link my GitHub Account' => 'GitHub fiók csatolása',
'Unlink my GitHub Account' => 'GitHub fiók leválasztása', 'Unlink my GitHub Account' => 'GitHub fiók leválasztása',
'Created by %s' => 'Készítette: %s', 'Created by %s' => 'Készítette: %s',
'Last modified on %B %e, %Y at %k:%M %p' => 'Utolsó módosítás: %Y.%m.%d %H:%M', 'Last modified on %B %e, %Y at %k:%M %p' => 'Utolsó módosítás: %Y. %m. %d. %H:%M',
'Tasks Export' => 'Feladatok exportálása', 'Tasks Export' => 'Feladatok exportálása',
'Tasks exportation for "%s"' => 'Feladatok exportálása: "%s"', 'Tasks exportation for "%s"' => 'Feladatok exportálása: "%s"',
'Start Date' => 'Kezdés dátuma', 'Start Date' => 'Kezdés dátuma',
@ -391,7 +393,7 @@ return array(
'Webhook URL for task modification' => 'Webhook URL a feladatot módosításakor', 'Webhook URL for task modification' => 'Webhook URL a feladatot módosításakor',
'Clone' => 'Másolat', 'Clone' => 'Másolat',
'Clone Project' => 'Projekt másolása', 'Clone Project' => 'Projekt másolása',
'Project cloned successfully.' => 'A projekt másolása sikeres', 'Project cloned successfully.' => 'A projekt sikeresen másolva.',
'Unable to clone this project.' => 'A projekt másolása sikertelen.', 'Unable to clone this project.' => 'A projekt másolása sikertelen.',
'Email notifications' => 'E-mail értesítések', 'Email notifications' => 'E-mail értesítések',
'Enable email notifications' => 'E-mail értesítések engedélyezése', 'Enable email notifications' => 'E-mail értesítések engedélyezése',
@ -435,7 +437,7 @@ return array(
'Move the task to another project' => 'Feladat áthelyezése másik projektbe', 'Move the task to another project' => 'Feladat áthelyezése másik projektbe',
'Move to another project' => 'Áthelyezés másik projektbe', 'Move to another project' => 'Áthelyezés másik projektbe',
'Do you really want to duplicate this task?' => 'Tényleg szeretné megkettőzni ezt a feladatot?', 'Do you really want to duplicate this task?' => 'Tényleg szeretné megkettőzni ezt a feladatot?',
'Duplicate a task' => 'Feladat megkettőzése', 'Duplicate a task' => 'Feladat másolása',
'External accounts' => 'Külső fiókok', 'External accounts' => 'Külső fiókok',
'Account type' => 'Fiók típusa', 'Account type' => 'Fiók típusa',
'Local' => 'Helyi', 'Local' => 'Helyi',
@ -517,7 +519,7 @@ return array(
'Label' => 'Címke', 'Label' => 'Címke',
'Database' => 'Adatbázis', 'Database' => 'Adatbázis',
'About' => 'Kanboard információ', 'About' => 'Kanboard információ',
'Database driver:' => 'Adatbázis driver:', 'Database driver:' => 'Adatbázis motor:',
'Board settings' => 'Tábla beállítások', 'Board settings' => 'Tábla beállítások',
'URL and token' => 'URL és tokenek', 'URL and token' => 'URL és tokenek',
'Webhook settings' => 'Webhook beállítások', 'Webhook settings' => 'Webhook beállítások',
@ -541,7 +543,7 @@ return array(
'Add' => 'Hozzáadás', 'Add' => 'Hozzáadás',
'Estimated time: %s hours' => 'Becsült idő: %s óra', 'Estimated time: %s hours' => 'Becsült idő: %s óra',
'Time spent: %s hours' => 'Eltöltött idő: %s óra', 'Time spent: %s hours' => 'Eltöltött idő: %s óra',
'Started on %B %e, %Y' => 'Elkezdve: %Y.%m.%d', 'Started on %B %e, %Y' => 'Elkezdve: %Y. %m. %d.',
'Start date' => 'Kezdés dátuma', 'Start date' => 'Kezdés dátuma',
'Time estimated' => 'Becsült időtartam', 'Time estimated' => 'Becsült időtartam',
'There is nothing assigned to you.' => 'Nincs kiosztott feladat.', 'There is nothing assigned to you.' => 'Nincs kiosztott feladat.',
@ -574,8 +576,8 @@ return array(
'My subtasks' => 'Részfeladataim', 'My subtasks' => 'Részfeladataim',
'User repartition' => 'Felhasználó újrafelosztás', 'User repartition' => 'Felhasználó újrafelosztás',
'User repartition for "%s"' => 'Felhasználó újrafelosztás: %s', 'User repartition for "%s"' => 'Felhasználó újrafelosztás: %s',
'Clone this project' => 'Projekt megkettőzése', 'Clone this project' => 'Projekt másolása',
'Column removed successfully.' => 'Oszlop sikeresen eltávolítva.', 'Column removed successfully.' => 'Oszlop sikeresen törölve.',
'Edit Project' => 'Projekt szerkesztése', 'Edit Project' => 'Projekt szerkesztése',
'Github Issue' => 'Github issue', 'Github Issue' => 'Github issue',
'Not enough data to show the graph.' => 'Nincs elég adat a grafikonhoz.', 'Not enough data to show the graph.' => 'Nincs elég adat a grafikonhoz.',
@ -669,8 +671,8 @@ return array(
'There is nothing to show.' => 'Nincs megjelenítendő adat.', 'There is nothing to show.' => 'Nincs megjelenítendő adat.',
'Time Tracking' => 'Idő követés', 'Time Tracking' => 'Idő követés',
'You already have one subtask in progress' => 'Már van egy folyamatban levő részfeladata', 'You already have one subtask in progress' => 'Már van egy folyamatban levő részfeladata',
'Which parts of the project do you want to duplicate?' => 'A projekt mely részeit szeretné duplikálni?', 'Which parts of the project do you want to duplicate?' => 'A projekt mely részeit szeretné másolni?',
'Change dashboard view' => 'Vezérlőpult megjelenítés változtatás', 'Change dashboard view' => 'Vezérlőpult megjelenés változtatás',
'Show/hide activities' => 'Tevékenységek megjelenítése/elrejtése', 'Show/hide activities' => 'Tevékenységek megjelenítése/elrejtése',
'Show/hide projects' => 'Projektek megjelenítése/elrejtése', 'Show/hide projects' => 'Projektek megjelenítése/elrejtése',
'Show/hide subtasks' => 'Részfeladatok megjelenítése/elrejtése', 'Show/hide subtasks' => 'Részfeladatok megjelenítése/elrejtése',
@ -728,10 +730,120 @@ return array(
'Close dialog box' => 'Ablak bezárása', 'Close dialog box' => 'Ablak bezárása',
'Submit a form' => 'Űrlap beküldése', 'Submit a form' => 'Űrlap beküldése',
'Board view' => 'Tábla nézet', 'Board view' => 'Tábla nézet',
'Keyboard shortcuts' => 'Billentyű kombináció', 'Keyboard shortcuts' => 'Billentyű kombinációk',
'Open board switcher' => 'Tábla választó lenyitása', 'Open board switcher' => 'Tábla választó lenyitása',
// 'Application' => '', 'Application' => 'Alkalmazás',
// 'Filter recently updated' => '', 'Filter recently updated' => 'Szűrés az utolsó módosítás ideje szerint',
// 'since %B %e, %Y at %k:%M %p' => '', 'since %B %e, %Y at %k:%M %p' => '%Y. %m. %d. %H:%M óta',
// 'More filters' => '', 'More filters' => 'További szűrők',
'Compact view' => 'Kompakt nézet',
'Horizontal scrolling' => 'Vízszintes görgetés',
'Compact/wide view' => 'Kompakt/széles nézet',
'No results match:' => 'Nincs találat:',
'Remove hourly rate' => 'Órabér törlése',
'Do you really want to remove this hourly rate?' => 'Valóban törölni kívánja az órabért?',
'Hourly rates' => 'Órabérek',
'Hourly rate' => 'Órabér',
'Currency' => 'Pénznem',
'Effective date' => 'Hatálybalépés ideje',
'Add new rate' => 'Új bér',
'Rate removed successfully.' => 'Bér sikeresen törölve.',
'Unable to remove this rate.' => 'Bér törlése sikertelen.',
'Unable to save the hourly rate.' => 'Órabér mentése sikertelen.',
'Hourly rate created successfully.' => 'Órabér sikeresen mentve.',
'Start time' => 'Kezdés ideje',
'End time' => 'Végzés ideje',
'Comment' => 'Megjegyzés',
'All day' => 'Egész nap',
'Day' => 'Nap',
'Manage timetable' => 'Időbeosztás kezelése',
'Overtime timetable' => 'Túlóra időbeosztás',
'Time off timetable' => 'Szabadság időbeosztás',
'Timetable' => 'Időbeosztás',
'Work timetable' => 'Munka időbeosztás',
'Week timetable' => 'Heti időbeosztás',
'Day timetable' => 'Napi időbeosztás',
'From' => 'Feladó:',
'To' => 'Címzett:',
'Time slot created successfully.' => 'Időszelet sikeresen létrehozva.',
'Unable to save this time slot.' => 'Időszelet mentése sikertelen.',
'Time slot removed successfully.' => 'Időszelet sikeresen törölve.',
'Unable to remove this time slot.' => 'Időszelet törlése sikertelen.',
'Do you really want to remove this time slot?' => 'Biztos törli ezt az időszeletet?',
'Remove time slot' => 'Időszelet törlése',
'Add new time slot' => 'Új Időszelet',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ez az időbeosztás van használatban ha az "egész nap" jelölőnégyzet be van jelölve a tervezett szabadságnál és túlóránál.',
'Files' => 'Fájlok',
'Images' => 'Képek',
'Private project' => 'Privát projekt',
'Amount' => 'Összeg',
'AUD - Australian Dollar' => 'AUD - Ausztrál dollár',
'Budget' => 'Költségvetés',
'Budget line' => 'Költségvetési tétel',
'Budget line removed successfully.' => 'Költségvetési tétel sikeresen törölve.',
'Budget lines' => 'Költségvetési tételek',
'CAD - Canadian Dollar' => 'CAD - Kanadai dollár',
'CHF - Swiss Francs' => 'CHF - Svájci frank',
'Cost' => 'Költség',
'Cost breakdown' => 'Költség visszaszámlálás',
'Custom Stylesheet' => 'Egyéni sítluslap',
'download' => 'letöltés',
'Do you really want to remove this budget line?' => 'Biztos törölni akarja ezt a költségvetési tételt?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Kiadások',
'GBP - British Pound' => 'GBP - Angol font',
'INR - Indian Rupee' => 'INR - Indiai rúpia',
'JPY - Japanese Yen' => 'JPY - Japán Yen',
'New budget line' => 'Új költségvetési tétel',
'NZD - New Zealand Dollar' => 'NZD - Új-Zélandi dollár',
'Remove a budget line' => 'Költségvetési tétel törlése',
'Remove budget line' => 'Költségvetési tétel törlése',
'RSD - Serbian dinar' => 'RSD - Szerb dínár',
'The budget line have been created successfully.' => 'Költségvetési tétel sikeresen létrehozva.',
'Unable to create the budget line.' => 'Költségvetési tétel létrehozása sikertelen.',
'Unable to remove this budget line.' => 'Költségvetési tétel törlése sikertelen.',
'USD - US Dollar' => 'USD - Amerikai ollár',
'Remaining' => 'Maradék',
'Destination column' => 'Cél oszlop',
'Move the task to another column when assigned to a user' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés után',
'Move the task to another column when assignee is cleared' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés törlésekor',
'Source column' => 'Forrás oszlop',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Nessuno', 'None' => 'Nessuno',
'edit' => 'modificare', 'edit' => 'modificare',
'Edit' => 'Modificare', 'Edit' => 'Modificare',
@ -734,4 +736,114 @@ return array(
// 'Filter recently updated' => '', // 'Filter recently updated' => '',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', // 'More filters' => '',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'なし', 'None' => 'なし',
'edit' => '変更', 'edit' => '変更',
'Edit' => '変更', 'Edit' => '変更',
@ -734,4 +736,114 @@ return array(
// 'Filter recently updated' => '', // 'Filter recently updated' => '',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', // 'More filters' => '',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -0,0 +1,849 @@
<?php
return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Geen',
'edit' => 'bewerken',
'Edit' => 'Bewerken',
'remove' => 'verwijderen',
'Remove' => 'Verwijderen',
'Update' => 'Update',
'Yes' => 'Ja',
'No' => 'Nee',
'cancel' => 'annuleren',
'or' => 'of',
'Yellow' => 'Geel',
'Blue' => 'Blauw',
'Green' => 'Groen',
'Purple' => 'Paars',
'Red' => 'Rood',
'Orange' => 'Oranje',
'Grey' => 'Grijs',
'Save' => 'Opslaan',
'Login' => 'Inloggen',
'Official website:' => 'Officiële website :',
'Unassigned' => 'Niet toegewezen',
'View this task' => 'Deze taak bekijken',
'Remove user' => 'Gebruiker verwijderen',
'Do you really want to remove this user: "%s"?' => 'Weet u zeker dat u deze gebruiker wil verwijderen : « %s » ?',
'New user' => 'Nieuwe gebruiker',
'All users' => 'Alle gebruikers',
'Username' => 'Gebruikersnaam',
'Password' => 'Wachtwoord',
'Default project' => 'Standaard wachtwoord',
'Administrator' => 'Administrator',
'Sign in' => 'Inloggen',
'Users' => 'Gebruikers',
'No user' => 'Geen gebruiker',
'Forbidden' => 'Geweigerd',
'Access Forbidden' => 'Toegang geweigerd',
'Only administrators can access to this page.' => 'Alleen administrators hebben toegang tot deze pagina.',
'Edit user' => 'Gebruiker bewerken',
'Logout' => 'Uitloggen',
'Bad username or password' => 'Verkeerde gebruikersnaam of wachtwoord',
'users' => 'gebruikers',
'projects' => 'projecten',
'Edit project' => 'Project bewerken',
'Name' => 'Naam',
'Activated' => 'Geactiveerd',
'Projects' => 'Projecten',
'No project' => 'Geen project',
'Project' => 'Project',
'Status' => 'Status',
'Tasks' => 'Taken',
'Board' => 'Bord',
'Actions' => 'Acties',
'Inactive' => 'Inactief',
'Active' => 'Actief',
'Column %d' => 'Kolom %d',
'Add this column' => 'Deze kolom toevoegen',
'%d tasks on the board' => '%d taken op het bord',
'%d tasks in total' => '%d taken in totaal',
'Unable to update this board.' => 'Update van dit bord niet mogelijk.',
'Edit board' => 'Bord bewerken',
'Disable' => 'Deactiveren',
'Enable' => 'Activeren',
'New project' => 'Nieuw project',
'Do you really want to remove this project: "%s"?' => 'Weet u zeker dat u dit project wil verwijderen : « %s » ?',
'Remove project' => 'Project verwijderen',
'Boards' => 'Borden',
'Edit the board for "%s"' => 'Bord bewerken voor « %s »',
'All projects' => 'Alle projecten',
'Change columns' => 'Kolommen veranderen',
'Add a new column' => 'Kolom toevoegen',
'Title' => 'Titel',
'Add Column' => 'Kolom toevoegen',
'Project "%s"' => 'Project « %s »',
'Nobody assigned' => 'Niemand toegewezen',
'Assigned to %s' => 'Toegewezen aan %s',
'Remove a column' => 'Kolom verwijderen',
'Remove a column from a board' => 'Kolom verwijderen van het bord',
'Unable to remove this column.' => 'Verwijderen van deze kolom niet mogelijk.',
'Do you really want to remove this column: "%s"?' => 'Weet u zeker dat u deze kolom wil verwijderen : « %s » ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Deze actie zal ALLE TAKEN VERWIJDEREN die zijn geassocieerd met deze kolom!',
'Settings' => 'Instellingen',
'Application settings' => 'Applicatie instellingen',
'Language' => 'Taal',
'Webhook token:' => 'Webhook token :',
'API token:' => 'API token :',
'More information' => 'Meer informatie',
'Database size:' => 'Database grootte :',
'Download the database' => 'Download de database',
'Optimize the database' => 'Optimaliseer de database',
'(VACUUM command)' => '(VACUUM commando)',
'(Gzip compressed Sqlite file)' => '(Gzip ingepakt Sqlite bestand)',
'User settings' => 'Gebruikers instellingen',
'My default project:' => 'Mijn standaard project : ',
'Close a task' => 'Taak sluiten',
'Do you really want to close this task: "%s"?' => 'Weet u zeker dat u deze taak wil sluiten : « %s » ?',
'Edit a task' => 'Taak bewerken',
'Column' => 'Kolom',
'Color' => 'Kleur',
'Assignee' => 'Toegewezene',
'Create another task' => 'Nog een taak aanmaken',
'New task' => 'Nieuwe taak',
'Open a task' => 'Een taak openen',
'Do you really want to open this task: "%s"?' => 'Weet u zeker dat u deze taak wil openen : « %s » ?',
'Back to the board' => 'Terug naar het bord',
'Created on %B %e, %Y at %k:%M %p' => 'Aangemaakt op %d/%m/%Y à %H:%M',
'There is nobody assigned' => 'Er is niemand toegewezen',
'Column on the board:' => 'Kolom op het bord : ',
'Status is open' => 'Status is open',
'Status is closed' => 'Status is gesloten',
'Close this task' => 'Deze taak sluiten',
'Open this task' => 'Deze taak openen',
'There is no description.' => 'Er is geen omschrijving.',
'Add a new task' => 'Een nieuwe taak toevoegen',
'The username is required' => 'De gebruikersnaam is verplicht',
'The maximum length is %d characters' => 'De maximale lengte is %d karakters',
'The minimum length is %d characters' => 'De minimale lengte is %d karakters',
'The password is required' => 'Het wachtwoord is verplicht',
'This value must be an integer' => 'Deze waarde dient een integer te zijn',
'The username must be unique' => 'De gebruikersnaam moet uniek zijn',
'The username must be alphanumeric' => 'De gebruikersnaam moet alfanumeriek zijn',
'The user id is required' => 'Het gebruikers id is verplicht',
'Passwords don\'t match' => 'De wachtwoorden komen niet overeen',
'The confirmation is required' => 'De bevestiging is verplicht',
'The column is required' => 'De kolom is verplicht',
'The project is required' => 'Het project is verplicht',
'The color is required' => 'De kleur is verplicht',
'The id is required' => 'Het id is verplicht',
'The project id is required' => 'Het project id is verplicht',
'The project name is required' => 'De projectnaam is verplicht',
'This project must be unique' => 'Dit project moet uniek zijn',
'The title is required' => 'De titel is verplicht',
'The language is required' => 'De taal is verplicht',
'There is no active project, the first step is to create a new project.' => 'Er is geen actief project, de eerste stap is een nieuw project aanmaken.',
'Settings saved successfully.' => 'Instellingen succesvol opgeslagen.',
'Unable to save your settings.' => 'Instellingen opslaan niet gelukt.',
'Database optimization done.' => 'Database optimaliseren voltooid.',
'Your project have been created successfully.' => 'Uw project is succesvol aangemaakt.',
'Unable to create your project.' => 'Het aanmaken van het project is niet gelukt.',
'Project updated successfully.' => 'Project succesvol geupdate.',
'Unable to update this project.' => 'Updaten van project niet gelukt.',
'Unable to remove this project.' => 'Verwijderen van project niet gelukt.',
'Project removed successfully.' => 'Project succesvol verwijderd.',
'Project activated successfully.' => 'Project succesvol geactiveerd.',
'Unable to activate this project.' => 'Project activeren niet gelukt.',
'Project disabled successfully.' => 'Project uitschakelen succesvol.',
'Unable to disable this project.' => 'Project uitschakelen niet gelukt.',
'Unable to open this task.' => 'Openen van deze taak niet gelukt.',
'Task opened successfully.' => 'Taak succesvol geopend.',
'Unable to close this task.' => 'Sluiten van deze taak niet gelukt.',
'Task closed successfully.' => 'Taak succesvol gesloten.',
'Unable to update your task.' => 'Updaten van uw taak mislukt.',
'Task updated successfully.' => 'Taak succesvol geupdate.',
'Unable to create your task.' => 'Taak aanmaken niet gelukt.',
'Task created successfully.' => 'Taak succesvol aangemaakt.',
'User created successfully.' => 'Gebruiker succesvol aangemaakt.',
'Unable to create your user.' => 'Aanmaken van gebruiker niet gelukt.',
'User updated successfully.' => 'Gebruiker succesvol geupdate',
'Unable to update your user.' => 'Updaten van gebruiker niet gelukt.',
'User removed successfully.' => 'Gebruiker succesvol verwijderd.',
'Unable to remove this user.' => 'Verwijderen van gebruikers niet gelukt.',
'Board updated successfully.' => 'Board succesvol geupdate.',
'Ready' => 'Klaar',
'Backlog' => 'En attente',
'Work in progress' => 'In behandeling',
'Done' => 'Klaar',
'Application version:' => 'Applicatie versie :',
'Completed on %B %e, %Y at %k:%M %p' => 'Voltooid op %d/%m/%Y à %H:%M',
'%B %e, %Y at %k:%M %p' => '%d/%m/%Y op %H:%M',
'Date created' => 'Datum aangemaakt',
'Date completed' => 'Datum voltooid',
'Id' => 'Id',
'No task' => 'Geen taak',
'Completed tasks' => 'Voltooide taken',
'List of projects' => 'Lijst van projecten',
'Completed tasks for "%s"' => 'Vooltooide taken voor « %s »',
'%d closed tasks' => '%d gesloten taken',
'No task for this project' => 'Geen taken voor dit project',
'Public link' => 'Publieke link',
'There is no column in your project!' => 'Er is geen kolom in uw project !',
'Change assignee' => 'Toegewezene aanpassen',
'Change assignee for the task "%s"' => 'Toegewezene aanpassen voor taak « %s »',
'Timezone' => 'Tijdzone',
'Sorry, I didn\'t find this information in my database!' => 'Sorry deze informatie kon niet worden gevonden in de database !',
'Page not found' => 'Pagina niet gevonden',
'Complexity' => 'Complexiteit',
'limit' => 'Limiet',
'Task limit' => 'Taak limiet.',
'Task count' => 'Aantal taken',
'This value must be greater than %d' => 'Deze waarde moet groter zijn dan %d',
'Edit project access list' => 'Aanpassen toegangsrechten project',
'Edit users access' => 'Gebruikerstoegang aanpassen',
'Allow this user' => 'Deze gebruiker toestaan',
'Only those users have access to this project:' => 'Alleen deze gebruikers hebben toegang tot dit project :',
'Don\'t forget that administrators have access to everything.' => 'Vergeet niet dat administrators overal toegang hebben.',
'Revoke' => 'Intrekken',
'List of authorized users' => 'Lijst met geautoriseerde gebruikers',
'User' => 'Gebruiker',
'Nobody have access to this project.' => 'Niemand heeft toegang tot dit project',
'You are not allowed to access to this project.' => 'U heeft geen toegang tot dit project.',
'Comments' => 'Commentaar',
'Post comment' => 'Commentaar toevoegen',
'Write your text in Markdown' => 'Schrijf uw tekst in Markdown',
'Leave a comment' => 'Schrijf een commentaar',
'Comment is required' => 'Commentaar is verplicht',
'Leave a description' => 'Schrijf een omschrijving',
'Comment added successfully.' => 'Commentaar succesvol toegevoegd.',
'Unable to create your comment.' => 'Commentaar toevoegen niet gelukt.',
'The description is required' => 'Omschrijving is verplicht',
'Edit this task' => 'Deze taak aanpassen',
'Due Date' => 'Vervaldag',
'Invalid date' => 'Ongeldige datum',
'Must be done before %B %e, %Y' => 'Moet voltooid zijn voor %d/%m/%Y',
'%B %e, %Y' => '%d %B %Y',
'%b %e, %Y' => '%d/%m/%Y',
'Automatic actions' => 'Geautomatiseerd acties',
'Your automatic action have been created successfully.' => 'Geautomatiseerde actie succesvol aangemaakt.',
'Unable to create your automatic action.' => 'Geautomatiseerde actie aanmaken niet gelukt.',
'Remove an action' => 'Actie verwijderen',
'Unable to remove this action.' => 'Actie verwijderen niet gelukt',
'Action removed successfully.' => 'Actie succesvol verwijder.',
'Automatic actions for the project "%s"' => 'Automatiseer acties voor project « %s »',
'Defined actions' => 'Gedefinieerde acties',
'Add an action' => 'Actie toevoegen',
'Event name' => 'Naam gebeurtenis',
'Action name' => 'Actie naam',
'Action parameters' => 'Actie paramaters',
'Action' => 'Actie',
'Event' => 'Evenement',
'When the selected event occurs execute the corresponding action.' => 'Als de geselecteerde gebeurtenis optreedt de volgende actie uitvoeren',
'Next step' => 'Volgende stap',
'Define action parameters' => 'Bepaal actie parameters',
'Save this action' => 'Actie opslaan',
'Do you really want to remove this action: "%s"?' => 'Weet u zeker dat u de volgende actie wil verwijderen : « %s » ?',
'Remove an automatic action' => 'Automatische actie verwijderen',
'Close the task' => 'Taak sluiten',
'Assign the task to a specific user' => 'Taak toewijzen aan een gebruiker',
'Assign the task to the person who does the action' => 'Taak toewijzen aan een gebruiker die de actie uitvoert',
'Duplicate the task to another project' => 'Taak dupliceren in een ander project',
'Move a task to another column' => 'Taak verplaatsen naar een andere kolom',
'Move a task to another position in the same column' => 'Taak verplaatsen naar een andere positie in dezelfde kolom',
'Task modification' => 'Taak aanpassen',
'Task creation' => 'Taak aanmaken',
'Open a closed task' => 'Gesloten taak openen',
'Closing a task' => 'Taak sluiten',
'Assign a color to a specific user' => 'Wijs een kleur toe aan een gebruiker',
'Column title' => 'Kolom titel',
'Position' => 'Positie',
'Move Up' => 'Omhoog verplaatsen',
'Move Down' => 'Omlaag verplaatsen',
'Duplicate to another project' => 'Dupliceren in een ander project',
'Duplicate' => 'Dupliceren',
'link' => 'koppelen',
'Update this comment' => 'Commentaar aanpassen',
'Comment updated successfully.' => 'Commentaar succesvol aangepast.',
'Unable to update your comment.' => 'Commentaar aanpassen niet gelukt.',
'Remove a comment' => 'Commentaar verwijderen',
'Comment removed successfully.' => 'Commentaar succesvol verwijder.',
'Unable to remove this comment.' => 'Commentaar verwijderen niet gelukt.',
'Do you really want to remove this comment?' => 'Weet u zeker dat u dit commentaar wil verwijderen ?',
'Only administrators or the creator of the comment can access to this page.' => 'Alleen administrators of de aanmaker van het commentaar hebben toegang tot deze pagina.',
'Details' => 'Details',
'Current password for the user "%s"' => 'Huidig wachtwoord voor gebruiker « %s »',
'The current password is required' => 'Huidig wachtwoord is verplicht',
'Wrong password' => 'Onjuist wachtwoord',
'Reset all tokens' => 'Alle tokens resetten',
'All tokens have been regenerated.' => 'Alle tokens zijn opnieuw gegenereerd.',
'Unknown' => 'Onbekend',
'Last logins' => 'Laatste logins',
'Login date' => 'Login datum',
'Authentication method' => 'Authenticatie methode',
'IP address' => 'IP adres',
'User agent' => 'User agent',
'Persistent connections' => 'Persistente connectie',
'No session.' => 'Geen sessie.',
'Expiration date' => 'Verloopdatum',
'Remember Me' => 'Onthoud mij',
'Creation date' => 'Aanmaakdatum',
'Filter by user' => 'Filter op gebruiker',
'Filter by due date' => 'Filter op vervaldatum',
'Everybody' => 'Iedereen',
'Open' => 'Open',
'Closed' => 'Gesloten',
'Search' => 'Zoek',
'Nothing found.' => 'Niets gevonden.',
'Search in the project "%s"' => 'Zoek in project « %s »',
'Due date' => 'Vervaldatum',
'Others formats accepted: %s and %s' => 'Andere toegestane formaten : %s en %s',
'Description' => 'Omschrijving',
'%d comments' => '%d commentaren',
'%d comment' => '%d commentaar',
'Email address invalid' => 'Ongeldig emailadres',
'Your Google Account is not linked anymore to your profile.' => 'Uw Google Account is niet meer aan uw profiel gelinkt.',
'Unable to unlink your Google Account.' => 'Verwijderen link met Google Account niet gelukt.',
'Google authentication failed' => 'Google authenticatie niet gelukt',
'Unable to link your Google Account.' => 'Linken met Google Account niet gelukt',
'Your Google Account is linked to your profile successfully.' => 'Linken met Google Account succesvol.',
'Email' => 'Email',
'Link my Google Account' => 'Link mijn Google Account',
'Unlink my Google Account' => 'Link met Google Account verwijderen',
'Login with my Google Account' => 'Inloggen met mijn Google Account',
'Project not found.' => 'Project niet gevonden.',
'Task #%d' => 'Taak %d',
'Task removed successfully.' => 'Taak succesvol verwijderd.',
'Unable to remove this task.' => 'Taak verwijderen niet gelukt.',
'Remove a task' => 'Taak verwijderen',
'Do you really want to remove this task: "%s"?' => 'Weet u zeker dat u deze taak wil verwijderen « %s » ?',
'Assign automatically a color based on a category' => 'Automatisch een kleur toewijzen aan de hand van een categorie',
'Assign automatically a category based on a color' => 'Automatisch een categorie toewijzen aan de hand van een kleur',
'Task creation or modification' => 'Taak aanmaken of wijzigen',
'Category' => 'Categorie',
'Category:' => 'Categorie :',
'Categories' => 'Categorieën',
'Category not found.' => 'Categorie niet gevonden',
'Your category have been created successfully.' => 'Categorie succesvol aangemaakt.',
'Unable to create your category.' => 'Categorie aanmaken niet gelukt.',
'Your category have been updated successfully.' => 'Categorie succesvol aangepast.',
'Unable to update your category.' => 'Aanpassen van categorie niet gelukt.',
'Remove a category' => 'Categorie verwijderen',
'Category removed successfully.' => 'Categorie succesvol verwijderd.',
'Unable to remove this category.' => 'Categorie verwijderen niet gelukt.',
'Category modification for the project "%s"' => 'Categorie aanpassen voor project « %s »',
'Category Name' => 'Categorie naam',
'Categories for the project "%s"' => 'Categorieën voor project « %s »',
'Add a new category' => 'Categorie toevoegen',
'Do you really want to remove this category: "%s"?' => 'Weet u zeker dat u deze categorie wil verwijderen: « %s » ?',
'Filter by category' => 'Filter op categorie',
'All categories' => 'Alle categorieën',
'No category' => 'Geen categorie',
'The name is required' => 'De naam is verplicht',
'Remove a file' => 'Bestand verwijderen',
'Unable to remove this file.' => 'Bestand verwijderen niet gelukt.',
'File removed successfully.' => 'Bestand succesvol verwijdered.',
'Attach a document' => 'Document toevoegen',
'Do you really want to remove this file: "%s"?' => 'Weet u zeker dat u dit bestand wil verwijderen: « %s » ?',
'open' => 'openen',
'Attachments' => 'Bijlages',
'Edit the task' => 'Taak aanpassen',
'Edit the description' => 'Omschrijving aanpassen',
'Add a comment' => 'Commentaar toevoegen',
'Edit a comment' => 'Commentaar aanpassen',
'Summary' => 'Samenvatting',
'Time tracking' => 'Tijdschrijven',
'Estimate:' => 'Schatting :',
'Spent:' => 'Besteed :',
'Do you really want to remove this sub-task?' => 'Weet u zeker dat u deze subtaak wil verwijderen ?',
'Remaining:' => 'Restant :',
'hours' => 'uren',
'spent' => 'besteed',
'estimated' => 'geschat',
'Sub-Tasks' => 'Subtaken',
'Add a sub-task' => 'Subtaak toevoegen',
'Original estimate' => 'Orginele schatting',
'Create another sub-task' => 'Nog een subtaak toevoegen',
'Time spent' => 'Tijd besteed',
'Edit a sub-task' => 'Subtaak aanpassen',
'Remove a sub-task' => 'Subtaak verwijderen',
'The time must be a numeric value' => 'De tijd moet een numerieke waarde zijn',
'Todo' => 'Nog te doen',
'In progress' => 'In behandeling',
'Sub-task removed successfully.' => 'Subtaak succesvol verwijderd.',
'Unable to remove this sub-task.' => 'Subtaak verwijderen niet gelukt.',
'Sub-task updated successfully.' => 'Subtaak succesvol aangepast.',
'Unable to update your sub-task.' => 'Subtaak aanpassen niet gelukt.',
'Unable to create your sub-task.' => 'Subtaak aanmaken niet gelukt.',
'Sub-task added successfully.' => 'Subtaak succesvol aangemaakt.',
'Maximum size: ' => 'Maximale grootte : ',
'Unable to upload the file.' => 'Uploaden van bestand niet gelukt.',
'Display another project' => 'Een ander project weergeven',
'Your GitHub account was successfully linked to your profile.' => 'Uw Github Account is succesvol gelinkt aan uw profiel.',
'Unable to link your GitHub Account.' => 'Linken van uw Github Account niet gelukt.',
'GitHub authentication failed' => 'Github Authenticatie niet gelukt',
'Your GitHub account is no longer linked to your profile.' => 'Uw Github Account is niet langer gelinkt aan uw profiel.',
'Unable to unlink your GitHub Account.' => 'Verwijdern van de link met uw Github Account niet gelukt.',
'Login with my GitHub Account' => 'Login met mijn Github Account',
'Link my GitHub Account' => 'Link met mijn Github',
'Unlink my GitHub Account' => 'Link met mijn Github verwijderen',
'Created by %s' => 'Aangemaakt door %s',
'Last modified on %B %e, %Y at %k:%M %p' => 'Laatst gewijzigd op %d/%m/%Y à %H:%M',
'Tasks Export' => 'Taken exporteren',
'Tasks exportation for "%s"' => 'Taken exporteren voor « %s »',
'Start Date' => 'Startdatum',
'End Date' => 'Einddatum',
'Execute' => 'Uitvoeren',
'Task Id' => 'Taak Id',
'Creator' => 'Aangemaakt door',
'Modification date' => 'Wijzigingsdatum',
'Completion date' => 'Afgerond op',
'Webhook URL for task creation' => 'Webhook URL voor aanmaken taak',
'Webhook URL for task modification' => 'Webhook URL voor wijzigen taak',
'Clone' => 'Kloon',
'Clone Project' => 'Project klonen',
'Project cloned successfully.' => 'Project succesvol gekloond.',
'Unable to clone this project.' => 'Klonen van project niet gelukt.',
'Email notifications' => 'Email notificatie',
'Enable email notifications' => 'Email notificatie aanzetten',
'Task position:' => 'Taak positie :',
'The task #%d have been opened.' => 'Taak #%d is geopend.',
'The task #%d have been closed.' => 'Taak #%d is gesloten.',
'Sub-task updated' => 'Subtaak aangepast',
'Title:' => 'Titel :',
'Status:' => 'Status :',
'Assignee:' => 'Toegewezene :',
'Time tracking:' => 'Tijdschrijven :',
'New sub-task' => 'Nieuwe subtaak',
'New attachment added "%s"' => 'Nieuwe bijlage toegevoegd « %s »',
'Comment updated' => 'Commentaar aangepast',
'New comment posted by %s' => 'Nieuw commentaar geplaatst door « %s »',
'List of due tasks for the project "%s"' => 'Lijst van taken die binnenkort voltooid moeten worden voor project « %s »',
'New attachment' => 'Nieuwe bijlage',
'New comment' => 'Nieuw commentaar',
'New subtask' => 'Nieuwe subtaak',
'Subtask updated' => 'Subtaak aangepast',
'Task updated' => 'Taak aangepast',
'Task closed' => 'Taak gesloten',
'Task opened' => 'Taak geopend',
'[%s][Due tasks]' => '[%s][binnekort te voltooien taken]',
'[Kanboard] Notification' => '[Kanboard] Notificatie',
'I want to receive notifications only for those projects:' => 'Ik wil notificaties ontvangen van de volgende projecten :',
'view the task on Kanboard' => 'taak bekijken op Kanboard',
'Public access' => 'Publieke toegang',
'Category management' => 'Categorie management',
'User management' => 'Gebruikers management',
'Active tasks' => 'Actieve taken',
'Disable public access' => 'Publieke toegang uitschakelen',
'Enable public access' => 'Publieke toegang inschakelen',
'Active projects' => 'Actieve projecten',
'Inactive projects' => 'Inactieve projecten',
'Public access disabled' => 'Publieke toegang uitgeschakeld',
'Do you really want to disable this project: "%s"?' => 'Weet u zeker dat u dit project wil uitschakelen : « %s » ?',
'Do you really want to duplicate this project: "%s"?' => 'Weet u zeker dat u dit project wil dupliceren : « %s » ?',
'Do you really want to enable this project: "%s"?' => 'Weet u zeker dat u dit project wil activeren : « %s » ?',
'Project activation' => 'Project activatie',
'Move the task to another project' => 'Taak verplaatsen naar een ander project',
'Move to another project' => 'Verplaats naar een ander project',
'Do you really want to duplicate this task?' => 'Weet u zeker dat u deze taak wil dupliceren ?',
'Duplicate a task' => 'Taak dupliceren',
'External accounts' => 'Externe accounts',
'Account type' => 'Account type',
'Local' => 'Lokaal',
'Remote' => 'Remote',
'Enabled' => 'Actief',
'Disabled' => 'Inactief',
'Google account linked' => 'Gelinkt Google Account',
'Github account linked' => 'Gelinkt Github Account',
'Username:' => 'Gebruikersnaam :',
'Name:' => 'Naam :',
'Email:' => 'Email :',
'Default project:' => 'Standaard project :',
'Notifications:' => 'Notificaties :',
'Notifications' => 'Notificaties',
'Group:' => 'Groep :',
'Regular user' => 'Normale gebruiker',
'Account type:' => 'Account type:',
'Edit profile' => 'Profiel aanpassen',
'Change password' => 'Wachtwoord aanpassen',
'Password modification' => 'Wachtwoord aanpassen',
'External authentications' => 'Externe authenticatie',
'Google Account' => 'Google Account',
'Github Account' => 'Github Account',
'Never connected.' => 'Nooit verbonden.',
'No account linked.' => 'Geen account gelinkt.',
'Account linked.' => 'Account gelinkt.',
'No external authentication enabled.' => 'Geen externe authenticatie aangezet.',
'Password modified successfully.' => 'Wachtwoord succesvol aangepast.',
'Unable to change the password.' => 'Aanpassen van wachtwoord niet gelukt.',
'Change category for the task "%s"' => 'Pas categorie aan voor taak « %s »',
'Change category' => 'Categorie aanpassen',
'%s updated the task %s' => '%s heeft taak %s aangepast',
'%s opened the task %s' => '%s heeft taak %s geopend',
'%s moved the task %s to the position #%d in the column "%s"' => '%s heeft taak %s naar positie %d in de kolom « %s » verplaatst',
'%s moved the task %s to the column "%s"' => '%s heeft taak %s verplaatst naar kolom « %s »',
'%s created the task %s' => '%s heeft taak %s aangemaakt',
'%s closed the task %s' => '%s heeft taak %s gesloten',
'%s created a subtask for the task %s' => '%s heeft een subtaak aangemaakt voor taak %s',
'%s updated a subtask for the task %s' => '%s heeft een subtaak aangepast voor taak %s',
'Assigned to %s with an estimate of %s/%sh' => 'Toegewezen aan %s met een schatting van %s/%sh',
'Not assigned, estimate of %sh' => 'Niet toegewezen, schatting: %sh',
'%s updated a comment on the task %s' => '%s heeft een commentaar aangepast voor taak %s',
'%s commented the task %s' => '%s heeft een commentaar geplaatst voor taak %s',
'%s\'s activity' => 'Activiteiten van %s',
'No activity.' => 'Geen activiteiten.',
'RSS feed' => 'RSS feed',
'%s updated a comment on the task #%d' => '%s heeft een commentaar aangepast voor taak %d',
'%s commented on the task #%d' => '%s heeft commentaar geplaatst voor taak %d',
'%s updated a subtask for the task #%d' => '%s heeft een commentaar aangepast voor subtaak %d',
'%s created a subtask for the task #%d' => '%s heeft een subtaak aangemaakt voor taak %d',
'%s updated the task #%d' => '%s heeft taak %d aangepast',
'%s created the task #%d' => '%s heeft taak %d aangemaakt',
'%s closed the task #%d' => '%s heeft taak %d gesloten',
'%s open the task #%d' => '%s a heeft taak %d geopend',
'%s moved the task #%d to the column "%s"' => '%s heeft taak %d verplaatst naar kolom « %s »',
'%s moved the task #%d to the position %d in the column "%s"' => '%s heeft taak %d verplaatst naar positie %d in kolom « %s »',
'Activity' => 'Activiteit',
'Default values are "%s"' => 'Standaardwaarden zijn « %s »',
'Default columns for new projects (Comma-separated)' => 'Standaard kolommen voor nieuw projecten (komma gescheiden)',
'Task assignee change' => 'Taak toegewezene verandering',
'%s change the assignee of the task #%d to %s' => '%s heeft de toegewezene voor taak %d veranderd in %s',
'%s changed the assignee of the task %s to %s' => '%s heeft de toegewezene voor taak %d veranderd in %s',
'Column Change' => 'Kolom verandering',
'Position Change' => 'Positie verandering',
'Assignee Change' => 'Toegewezene verandering',
'New password for the user "%s"' => 'Nieuw wachtwoord voor gebruiker « %s »',
'Choose an event' => 'Kies een gebeurtenis',
'Github commit received' => 'Github commentaar ontvangen',
'Github issue opened' => 'Github issue geopend',
'Github issue closed' => 'Github issue gesloten',
'Github issue reopened' => 'Github issue heropend',
'Github issue assignee change' => 'Github toegewezen veranderd',
'Github issue label change' => 'Github issue label verander',
'Create a task from an external provider' => 'Maak een taak aan vanuit een externe provider',
'Change the assignee based on an external username' => 'Verander de toegewezene aan de hand van de externe gebruikersnaam',
'Change the category based on an external label' => 'Verander de categorie aan de hand van een extern label',
'Reference' => 'Referentie',
'Reference: %s' => 'Referentie : %s',
'Label' => 'Label',
'Database' => 'Database',
'About' => 'Over',
'Database driver:' => 'Database driver :',
'Board settings' => 'Bord instellingen',
'URL and token' => 'URL en token',
'Webhook settings' => 'Webhook instellingen',
'URL for task creation:' => 'URL voor aanmaken taken :',
'Reset token' => 'Token resetten',
'API endpoint:' => 'API endpoint :',
'Refresh interval for private board' => 'Verversingsinterval voor private borden',
'Refresh interval for public board' => 'Verversingsinterval voor publieke borden',
'Task highlight period' => 'Taak highlight periode',
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode (in seconden) om aan te geven of een taak recent is aangepast (0 om uit te schakelen, standaard 2 dagen)',
'Frequency in second (60 seconds by default)' => 'Frequentie in seconden (stadaard 60)',
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequentie in seconden (0 om uit te schakelen, standaard 10)',
'Application URL' => 'Applicatie URL',
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Voorbeeld: http://example.kanboard.net/ (gebruikt voor email notificaties)',
'Token regenerated.' => 'Token opnieuw gegenereerd.',
'Date format' => 'Datum formaat',
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formaat is altijd geaccepteerd, bijvoorbeeld : « %s » et « %s »',
'New private project' => 'Nieuw privé project',
'This project is private' => 'Dit project is privé',
'Type here to create a new sub-task' => 'Typ hier om een nieuwe subtaak aan te maken',
'Add' => 'Toevoegen',
'Estimated time: %s hours' => 'Geschatte tijd: %s hours',
'Time spent: %s hours' => 'Tijd besteed : %s heures',
'Started on %B %e, %Y' => 'Gestart op %d/%m/%Y',
'Start date' => 'Startdatum',
'Time estimated' => 'Geschatte tijd',
'There is nothing assigned to you.' => 'Er is niets aan u toegewezen.',
'My tasks' => 'Mijn taken',
'Activity stream' => 'Activiteiten',
'Dashboard' => 'Dashboard',
// 'Confirmation' => '',
'Allow everybody to access to this project' => 'Geef iedereen toegang tot dit project',
'Everybody have access to this project.' => 'Iedereen heeft toegang tot dit project.',
'Webhooks' => 'Webhooks',
'API' => 'API',
'Integration' => 'Integratue',
'Github webhooks' => 'Github webhooks',
'Help on Github webhooks' => 'Hulp bij Github webhooks',
'Create a comment from an external provider' => 'Voeg een commentaar toe van een externe provider',
'Github issue comment created' => 'Github issue commentaar aangemaakt',
'Configure' => 'Configureren',
'Project management' => 'Project management',
'My projects' => 'Mijn projecten',
'Columns' => 'Kolommen',
'Task' => 'Taak',
'Your are not member of any project.' => 'U bent van geen enkel project lid.',
'Percentage' => 'Percentage',
'Number of tasks' => 'Aantal taken',
'Task distribution' => 'Distributie van taken',
'Reportings' => 'Rapporten',
'Task repartition for "%s"' => 'Taakverdeling voor « %s »',
'Analytics' => 'Analytics',
'Subtask' => 'Subtaak',
'My subtasks' => 'Mijn subtaken',
'User repartition' => 'Gebruikerverdeling',
'User repartition for "%s"' => 'Gebruikerverdeling voor « %s »',
'Clone this project' => 'Kloon dit project',
'Column removed successfully.' => 'Kolom succesvol verwijderd.',
'Edit Project' => 'Project aanpassen',
'Github Issue' => 'Github issue',
'Not enough data to show the graph.' => 'Niet genoeg data om de grafiek te laten zien.',
// 'Previous' => '',
'The id must be an integer' => 'Het id moet een integer zijn',
'The project id must be an integer' => 'Het project id moet een integer zijn',
'The status must be an integer' => 'De status moet een integer zijn',
'The subtask id is required' => 'Het id van de subtaak is verplicht',
'The subtask id must be an integer' => 'Het id van de subtaak moet een integer zijn',
'The task id is required' => 'Het id van de taak is verplicht',
'The task id must be an integer' => 'Het id van de taak moet een integer zijn',
'The user id must be an integer' => 'Het id van de gebruiker moet een integer zijn',
'This value is required' => 'Deze waarde is verplicht',
'This value must be numeric' => 'Deze waarde moet numeriek zijn',
'Unable to create this task.' => 'Aanmaken van de taak mislukt',
'Cumulative flow diagram' => 'Cummulatief stroomdiagram',
'Cumulative flow diagram for "%s"' => 'Cummulatief stroomdiagram voor « %s »',
'Daily project summary' => 'Dagelijkse project samenvatting',
'Daily project summary export' => 'Dagelijkse project samenvatting export',
'Daily project summary export for "%s"' => 'Dagelijkse project samenvatting voor « %s »',
'Exports' => 'Exports',
'This export contains the number of tasks per column grouped per day.' => 'Dit rapport bevat het aantal taken per kolom gegroupeerd per dag.',
'Nothing to preview...' => 'Niets om te previewen...',
'Preview' => 'Preview',
'Write' => 'Schrijf',
'Active swimlanes' => 'Actieve swinlanes',
'Add a new swimlane' => 'Nieuwe swimlane toevoegen',
'Change default swimlane' => 'Standaard swimlane aapassen',
'Default swimlane' => 'Standaard swinlane',
'Do you really want to remove this swimlane: "%s"?' => 'Weet u zeker dat u deze swimlane wil verwijderen : « %s » ?',
'Inactive swimlanes' => 'Inactieve swinlanes',
'Set project manager' => 'Project manager instellen',
'Set project member' => 'Project lid instellen',
'Remove a swimlane' => 'Verwijder swinlane',
'Rename' => 'Hernoemen',
'Show default swimlane' => 'Standaard swimlane tonen',
'Swimlane modification for the project "%s"' => 'Swinlane aanpassing voor project « %s »',
'Swimlane not found.' => 'Swimlane niet gevonden.',
'Swimlane removed successfully.' => 'Swimlane succesvol verwijderd.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane succesvol aangepast.',
'The default swimlane have been updated successfully.' => 'De standaard swimlane is succesvol aangepast.',
'Unable to create your swimlane.' => 'Swimlane aanmaken niet gelukt.',
'Unable to remove this swimlane.' => 'Swimlane verwijderen niet gelukt.',
'Unable to update this swimlane.' => 'Swimlane aanpassen niet gelukt.',
'Your swimlane have been created successfully.' => 'Swimlane succesvol aangemaakt.',
'Example: "Bug, Feature Request, Improvement"' => 'Voorbeeld: « Bug, Feature Request, Improvement »',
'Default categories for new projects (Comma-separated)' => 'Standaard categorieën voor nieuwe projecten (komma gescheiden)',
'Gitlab commit received' => 'Gitlab commir ontvangen',
'Gitlab issue opened' => 'Gitlab issue geopend',
'Gitlab issue closed' => 'Gitlab issue gesloten',
'Gitlab webhooks' => 'Gitlab webhooks',
'Help on Gitlab webhooks' => 'Hulp bij Gitlab webhooks',
'Integrations' => 'Integraties',
'Integration with third-party services' => 'Integratie met derde-partij-services',
'Role for this project' => 'Rol voor dit project',
'Project manager' => 'Project manager',
'Project member' => 'Project lid',
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Een project manager kan de instellingen van het project wijzigen en heeft meer rechten dan een normale gebruiker.',
'Gitlab Issue' => 'Gitlab issue',
'Subtask Id' => 'Subtaak id',
'Subtasks' => 'Subtaken',
'Subtasks Export' => 'Subtaken exporteren',
'Subtasks exportation for "%s"' => 'Subtaken exporteren voor project « %s »',
'Task Title' => 'Taak title',
'Untitled' => 'Geen titel',
'Application default' => 'Standaard taal voor applicatie',
'Language:' => 'Taal :',
'Timezone:' => 'Tijdzone :',
'All columns' => 'Alle kolommen',
'Calendar for "%s"' => 'Agenda voor « %s »',
'Filter by column' => 'Filter op kolom',
'Filter by status' => 'Filter op status',
'Calendar' => 'Agenda',
'Next' => 'Volgende',
'#%d' => '%d',
'Filter by color' => 'Filter op kleur',
'Filter by swimlane' => 'Filter op swimlane',
'All swimlanes' => 'Alle swimlanes',
'All colors' => 'Alle kleuren',
'All status' => 'Alle statussen',
'Add a comment logging moving the task between columns' => 'Voeg een commentaar toe bij het verplaatsen van een taak tussen kolommen',
'Moved to column %s' => 'Verplaatst naar kolom',
'Change description' => 'Verandering omschrijving',
'User dashboard' => 'Gebruiker dashboard',
'Allow only one subtask in progress at the same time for a user' => 'Sta maximaal één subtaak in behandeling toe per gebruiker',
'Edit column "%s"' => 'Kolom « %s » aanpassen',
'Enable time tracking for subtasks' => 'Activeer tijdschrijven voor subtaken',
'Select the new status of the subtask: "%s"' => 'Selecteer nieuwe status voor subtaak : « %s »',
'Subtask timesheet' => 'Subtaak timesheet',
'There is nothing to show.' => 'Er is niets om te laten zijn.',
'Time Tracking' => 'Tijdschrijven',
'You already have one subtask in progress' => 'U heeft al een subtaak in behandeling',
'Which parts of the project do you want to duplicate?' => 'Welke onderdelen van het project wilt u dupliceren?',
'Change dashboard view' => 'Pas dashboard aan',
'Show/hide activities' => 'Toon/verberg activiteiten',
'Show/hide projects' => 'Toon/verberg projecten',
'Show/hide subtasks' => 'Toon/verberg subtaken',
'Show/hide tasks' => 'Toon/verberg taken',
'Disable login form' => 'Schakel login scherm uit',
'Show/hide calendar' => 'Toon/verberg agenda',
'User calendar' => 'Agenda gebruiker',
'Bitbucket commit received' => 'Bitbucket commit ontvangen',
'Bitbucket webhooks' => 'Bitbucket webhooks',
'Help on Bitbucket webhooks' => 'Help bij Bitbucket webhooks',
'Start' => 'Start',
'End' => 'Eind',
'Task age in days' => 'Leeftijd taak in dagen',
'Days in this column' => 'Dagen in deze kolom',
'%dd' => '%dj',
'Add a link' => 'Link toevoegen',
'Add a new link' => 'Nieuwe link toevoegen',
'Do you really want to remove this link: "%s"?' => 'Weet u zeker dat u deze link wil verwijderen : « %s » ?',
'Do you really want to remove this link with task #%d?' => 'Weet u zeker dat u deze link met taak %d wil verwijderen?',
'Field required' => 'Veld verplicht',
'Link added successfully.' => 'Link succesvol toegevoegd.',
'Link updated successfully.' => 'Link succesvol aangepast.',
'Link removed successfully.' => 'Link succesvol verwijderd.',
'Link labels' => 'Link labels',
'Link modification' => 'Link aanpassing',
'Links' => 'Links',
'Link settings' => 'Link instellingen',
'Opposite label' => 'Tegenovergesteld label',
'Remove a link' => 'Link verwijderen',
'Task\'s links' => 'Links van taak',
'The labels must be different' => 'De labels moeten verschillend zijn',
'There is no link.' => 'Er is geen link.',
'This label must be unique' => 'Dit label moet uniek zijn',
'Unable to create your link.' => 'Link aanmaken niet gelukt.',
'Unable to update your link.' => 'Link aanpassen niet gelukt.',
'Unable to remove this link.' => 'Link verwijderen niet gelukt.',
'relates to' => 'is gerelateerd aan',
'blocks' => 'blokkeert',
'is blocked by' => 'is geblokkeerd door',
'duplicates' => 'dupliceert',
'is duplicated by' => 'is gedupliceerd',
'is a child of' => 'is een kind van',
'is a parent of' => 'is een ouder van',
'targets milestone' => 'is nodig voor milestone',
'is a milestone of' => 'is een milestone voor',
'fixes' => 'corrigeert',
'is fixed by' => 'word gecorrigeerd door',
'This task' => 'Deze taal',
'<1h' => '<1h',
'%dh' => '%dh',
'%b %e' => '%e %b',
'Expand tasks' => 'Taken uitklappen',
'Collapse tasks' => 'Taken inklappen',
'Expand/collapse tasks' => 'Taken in/uiklappen',
'Close dialog box' => 'Venster sluiten',
'Submit a form' => 'Formulier insturen',
'Board view' => 'Bord weergave',
'Keyboard shortcuts' => 'Keyboard snelkoppelingen',
'Open board switcher' => 'Open bord switcher',
'Application' => 'Applicatie',
'Filter recently updated' => 'Filter recent aangepast',
'since %B %e, %Y at %k:%M %p' => 'sinds %d/%m/%Y à %H:%M',
'More filters' => 'Meer filters',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
);

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Brak', 'None' => 'Brak',
'edit' => 'edytuj', 'edit' => 'edytuj',
'Edit' => 'Edytuj', 'Edit' => 'Edytuj',
@ -734,4 +736,114 @@ return array(
// 'Filter recently updated' => '', // 'Filter recently updated' => '',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', // 'More filters' => '',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
'number.decimals_separator' => ',',
'number.thousands_separator' => ' ',
'None' => 'Nenhum', 'None' => 'Nenhum',
'edit' => 'editar', 'edit' => 'editar',
'Edit' => 'Editar', 'Edit' => 'Editar',
@ -719,19 +721,129 @@ return array(
'fixes' => 'corrige', 'fixes' => 'corrige',
'is fixed by' => 'foi corrigido por', 'is fixed by' => 'foi corrigido por',
'This task' => 'Esta tarefa', 'This task' => 'Esta tarefa',
// '<1h' => '', '<1h' => '<1h',
// '%dh' => '', '%dh' => '%dh',
// '%b %e' => '', '%b %e' => '%e %b',
'Expand tasks' => 'Expandir tarefas', 'Expand tasks' => 'Expandir tarefas',
'Collapse tasks' => 'Contrair tarefas', 'Collapse tasks' => 'Contrair tarefas',
'Expand/collapse tasks' => 'Expandir/Contrair tarefas', 'Expand/collapse tasks' => 'Expandir/Contrair tarefas',
// 'Close dialog box' => '', 'Close dialog box' => 'Fechar a caixa de diálogo',
'Submit a form' => 'Envia o formulário', 'Submit a form' => 'Envia o formulário',
// 'Board view' => '', 'Board view' => 'Página do painel',
// 'Keyboard shortcuts' => '', 'Keyboard shortcuts' => 'Atalhos de teclado',
// 'Open board switcher' => '', 'Open board switcher' => 'Abrir o comutador de painel',
'Application' => 'Aplicação', 'Application' => 'Aplicação',
'Filter recently updated' => 'Filtro recentemente atualizado', 'Filter recently updated' => 'Filtro recentemente atualizado',
// 'since %B %e, %Y at %k:%M %p' => '', 'since %B %e, %Y at %k:%M %p' => 'desde o %d/%m/%Y às %H:%M',
'More filters' => 'Mais filtros', 'More filters' => 'Mais filtros',
'Compact view' => 'Vista reduzida',
'Horizontal scrolling' => 'Rolagem horizontal',
'Compact/wide view' => 'Alternar entre a vista compacta e ampliada',
'No results match:' => 'Nenhum resultado:',
'Remove hourly rate' => 'Retirar taxa horária',
'Do you really want to remove this hourly rate?' => 'Você deseja realmente remover esta taxa horária?',
'Hourly rates' => 'Taxas horárias',
'Hourly rate' => 'Taxa horária',
'Currency' => 'Moeda',
'Effective date' => 'Data efetiva',
'Add new rate' => 'Adicionar nova taxa',
'Rate removed successfully.' => 'Taxa removido com sucesso.',
'Unable to remove this rate.' => 'Impossível de remover esta taxa.',
'Unable to save the hourly rate.' => 'Impossível salvar a taxa horária.',
'Hourly rate created successfully.' => 'Taxa horária criada com sucesso.',
'Start time' => 'Horário de início',
'End time' => 'Horário de término',
'Comment' => 'comentário',
'All day' => 'Dia inteiro',
'Day' => 'Dia',
'Manage timetable' => 'Gestão dos horários',
'Overtime timetable' => 'Horas extras',
'Time off timetable' => 'Horas de ausência',
'Timetable' => 'Horários',
'Work timetable' => 'Horas trabalhadas',
'Week timetable' => 'Horário da semana',
'Day timetable' => 'Horário de un dia',
'From' => 'Desde',
'To' => 'A',
'Time slot created successfully.' => 'Intervalo de tempo criado com sucesso.',
'Unable to save this time slot.' => 'Impossível de guardar este intervalo de tempo.',
'Time slot removed successfully.' => 'Intervalo de tempo removido com sucesso.',
'Unable to remove this time slot.' => 'Impossível de remover esse intervalo de tempo.',
'Do you really want to remove this time slot?' => 'Você deseja realmente remover este intervalo de tempo?',
'Remove time slot' => 'Remover um intervalo de tempo',
'Add new time slot' => 'Adicionar um intervalo de tempo',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Esses horários são usados quando a caixa de seleção "Dia inteiro" está marcada para Horas de ausência ou Extras',
'Files' => 'Arquivos',
'Images' => 'Imagens',
'Private project' => 'Projeto privado',
'Amount' => 'Quantia',
'AUD - Australian Dollar' => 'AUD - Dólar australiano',
'Budget' => 'Orçamento',
'Budget line' => 'Rubrica orçamental',
'Budget line removed successfully.' => 'Rubrica orçamental removida com sucesso',
'Budget lines' => 'Rubricas orçamentais',
'CAD - Canadian Dollar' => 'CAD - Dólar canadense',
'CHF - Swiss Francs' => 'CHF - Francos Suíços',
'Cost' => 'Custo',
'Cost breakdown' => 'Repartição dos custos',
'Custom Stylesheet' => 'Folha de estilo personalizado',
'download' => 'baixar',
'Do you really want to remove this budget line?' => 'Você deseja realmente remover esta rubrica orçamental?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Despesas',
'GBP - British Pound' => 'GBP - Libra Esterlina',
'INR - Indian Rupee' => 'INR - Rúpia indiana',
'JPY - Japanese Yen' => 'JPY - Iene japonês',
'New budget line' => 'Nova rubrica orçamental',
'NZD - New Zealand Dollar' => 'NZD - Dólar Neozelandês',
'Remove a budget line' => 'Remover uma rubrica orçamental',
'Remove budget line' => 'Remover uma rubrica orçamental',
'RSD - Serbian dinar' => 'RSD - Dinar sérvio',
'The budget line have been created successfully.' => 'A rubrica orçamental foi criada com sucesso.',
'Unable to create the budget line.' => 'Impossível de adicionar esta rubrica orçamental.',
'Unable to remove this budget line.' => 'Impossível de remover esta rubrica orçamental.',
'USD - US Dollar' => 'USD - Dólar norte-americano',
'Remaining' => 'Restante',
'Destination column' => 'Coluna de destino',
'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuída a um usuário',
'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuída',
'Source column' => 'Coluna de origem',
'Show subtask estimates in the user calendar' => 'Mostrar o tempo estimado das subtarefas no calendário do usuário',
'Transitions' => 'Transições',
'Executer' => 'Executor(a)',
'Time spent in the column' => 'Tempo gasto na coluna',
'Task transitions' => 'Transições das tarefas',
'Task transitions export' => 'Exportação das transições das tarefas',
'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Este relatório contém todos os movimentos de coluna para cada tarefa com a data, o usuário e o tempo gasto para cada transição.',
'Currency rates' => 'Taxas de câmbio das moedas estrangeiras',
'Rate' => 'Taxa',
'Change reference currency' => 'Mudar a moeda de referência',
'Add a new currency rate' => 'Adicionar uma nova taxa para uma moeda',
'Currency rates are used to calculate project budget.' => 'As taxas de câmbio são utilizadas para calcular o orçamento do projeto.',
'Reference currency' => 'Moeda de Referência',
'The currency rate have been added successfully.' => 'A taxa de câmbio foi adicionada com sucesso.',
'Unable to add this currency rate.' => 'Impossível de adicionar essa taxa de câmbio.',
'Send notifications to a Slack channel' => 'Enviar as notificações em um canal Slack',
'Webhook URL' => 'URL do webhook',
'Help on Slack integration' => 'Ajuda na integração com o Slack',
'%s remove the assignee of the task %s' => '%s removeu a pessoa designada para a tarefa %s',
'Send notifications to Hipchat' => 'Enviar as notificações para o Hipchat',
'API URL' => 'URL da API',
'Room API ID or name' => 'Nome ou ID da sala de discussão',
'Room notification token' => 'Código de segurança da sala de discussão',
'Help on Hipchat integration' => 'Ajuda na integração com o Hipchat',
'Enable Gravatar images' => 'Ativar imagem Gravatar',
'Information' => 'Informações',
'Check two factor authentication code' => 'Verificação do código de autenticação à fator duplo',
'The two factor authentication code is not valid.' => 'O código de autenticação à fator duplo não é válido',
'The two factor authentication code is valid.' => 'O código de autenticação à fator duplo é válido',
'Code' => 'Código',
'Two factor authentication' => 'Autenticação à fator duplo',
'Enable/disable two factor authentication' => 'Ativar/Desativar autenticação à fator duplo',
'This QR code contains the key URI: ' => 'Este Código QR contém a chave URI:',
'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => 'Salve esta chave secreta no seu software TOTP (por exemplo Google Authenticator ou FreeOTP).',
'Check my code' => 'Verifique o meu código',
'Secret key: ' => 'Chave secreta:',
'Test your device' => 'Teste o seu dispositivo',
'Assign a color when the task is moved to a specific column' => 'Atribuir uma cor quando a tarefa é movida em uma coluna específica',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Отсутствует', 'None' => 'Отсутствует',
'edit' => 'изменить', 'edit' => 'изменить',
'Edit' => 'Изменить', 'Edit' => 'Изменить',
@ -63,7 +65,7 @@ return array(
'Disable' => 'Деактивировать', 'Disable' => 'Деактивировать',
'Enable' => 'Активировать', 'Enable' => 'Активировать',
'New project' => 'Новый проект', 'New project' => 'Новый проект',
'Do you really want to remove this project: "%s"?' => 'Вы точно хотите удалить этот проект? : « %s » ?', 'Do you really want to remove this project: "%s"?' => 'Вы точно хотите удалить проект: « %s » ?',
'Remove project' => 'Удалить проект', 'Remove project' => 'Удалить проект',
'Boards' => 'Доски', 'Boards' => 'Доски',
'Edit the board for "%s"' => 'Изменить доску для « %s »', 'Edit the board for "%s"' => 'Изменить доску для « %s »',
@ -119,7 +121,7 @@ return array(
'The password is required' => 'Требуется пароль', 'The password is required' => 'Требуется пароль',
'This value must be an integer' => 'Это значение должно быть целым', 'This value must be an integer' => 'Это значение должно быть целым',
'The username must be unique' => 'Требуется уникальное имя пользователя', 'The username must be unique' => 'Требуется уникальное имя пользователя',
'The username must be alphanumeric' => 'Имя пользователя должно быть букво-цифровым', 'The username must be alphanumeric' => 'Имя пользователя должно быть буквенно-цифровым',
'The user id is required' => 'Требуется ID пользователя', 'The user id is required' => 'Требуется ID пользователя',
'Passwords don\'t match' => 'Пароли не совпадают', 'Passwords don\'t match' => 'Пароли не совпадают',
'The confirmation is required' => 'Требуется подтверждение', 'The confirmation is required' => 'Требуется подтверждение',
@ -144,13 +146,13 @@ return array(
'Project removed successfully.' => 'Проект удален.', 'Project removed successfully.' => 'Проект удален.',
'Project activated successfully.' => 'Проект активирован.', 'Project activated successfully.' => 'Проект активирован.',
'Unable to activate this project.' => 'Невозможно активировать проект.', 'Unable to activate this project.' => 'Невозможно активировать проект.',
'Project disabled successfully.' => 'Проект успешно выключен.', 'Project disabled successfully.' => 'Проект успешно деактивирован.',
'Unable to disable this project.' => 'Не удалось выключить проект.', 'Unable to disable this project.' => 'Не удалось деактивировать проект.',
'Unable to open this task.' => 'Не удалось открыть задачу.', 'Unable to open this task.' => 'Не удалось открыть задачу.',
'Task opened successfully.' => 'Задача открыта.', 'Task opened successfully.' => 'Задача открыта.',
'Unable to close this task.' => 'Не удалось закрыть задачу.', 'Unable to close this task.' => 'Не удалось закрыть задачу.',
'Task closed successfully.' => 'Задача закрыта.', 'Task closed successfully.' => 'Задача закрыта.',
'Unable to update your task.' => 'Не удалось обновить вашу задачу.', 'Unable to update your task.' => 'Не удалось обновить задачу.',
'Task updated successfully.' => 'Задача обновлена.', 'Task updated successfully.' => 'Задача обновлена.',
'Unable to create your task.' => 'Не удалось создать задачу.', 'Unable to create your task.' => 'Не удалось создать задачу.',
'Task created successfully.' => 'Задача создана.', 'Task created successfully.' => 'Задача создана.',
@ -160,7 +162,7 @@ return array(
'Unable to update your user.' => 'Не удалось обновить пользователя.', 'Unable to update your user.' => 'Не удалось обновить пользователя.',
'User removed successfully.' => 'Пользователь удален.', 'User removed successfully.' => 'Пользователь удален.',
'Unable to remove this user.' => 'Не удалось удалить пользователя.', 'Unable to remove this user.' => 'Не удалось удалить пользователя.',
'Board updated successfully.' => 'Доска обновлена.', 'Board updated successfully.' => 'Доска успешно обновлена.',
'Ready' => 'Готовые', 'Ready' => 'Готовые',
'Backlog' => 'Ожидающие', 'Backlog' => 'Ожидающие',
'Work in progress' => 'В процессе', 'Work in progress' => 'В процессе',
@ -174,9 +176,9 @@ return array(
'No task' => 'Нет задачи', 'No task' => 'Нет задачи',
'Completed tasks' => 'Завершенные задачи', 'Completed tasks' => 'Завершенные задачи',
'List of projects' => 'Список проектов', 'List of projects' => 'Список проектов',
'Completed tasks for "%s"' => 'Задачи завершенные для « %s »', 'Completed tasks for "%s"' => 'Завершенные задачи для « %s »',
'%d closed tasks' => '%d завершенных задач', '%d closed tasks' => '%d завершенных задач',
'No task for this project' => 'нет задач для этого проекта', 'No task for this project' => 'Нет задач для этого проекта',
'Public link' => 'Ссылка для просмотра', 'Public link' => 'Ссылка для просмотра',
'There is no column in your project!' => 'Нет колонки в вашем проекте!', 'There is no column in your project!' => 'Нет колонки в вашем проекте!',
'Change assignee' => 'Сменить назначенного', 'Change assignee' => 'Сменить назначенного',
@ -193,18 +195,18 @@ return array(
'Edit users access' => 'Изменить доступ пользователей', 'Edit users access' => 'Изменить доступ пользователей',
'Allow this user' => 'Разрешить этого пользователя', 'Allow this user' => 'Разрешить этого пользователя',
'Only those users have access to this project:' => 'Только эти пользователи имеют доступ к проекту:', 'Only those users have access to this project:' => 'Только эти пользователи имеют доступ к проекту:',
'Don\'t forget that administrators have access to everything.' => 'Помните, администратор имеет доступ ко всему.', 'Don\'t forget that administrators have access to everything.' => 'Помните, администратор имеет неограниченные права.',
'Revoke' => 'отозвать', 'Revoke' => 'Отозвать',
'List of authorized users' => 'Список авторизованных пользователей', 'List of authorized users' => 'Список авторизованных пользователей',
'User' => 'Пользователь', 'User' => 'Пользователь',
'Nobody have access to this project.' => 'Ни у кого нет доступа к этому проекту', 'Nobody have access to this project.' => 'Ни у кого нет доступа к этому проекту',
'You are not allowed to access to this project.' => 'Вам запрешен доступ к этому проекту.', 'You are not allowed to access to this project.' => 'Вам запрещен доступ к этому проекту.',
'Comments' => 'Комментарии', 'Comments' => 'Комментарии',
'Post comment' => 'Оставить комментарий', 'Post comment' => 'Оставить комментарий',
'Write your text in Markdown' => 'Справка по синтаксису Markdown', 'Write your text in Markdown' => 'Справка по синтаксису Markdown',
'Leave a comment' => 'Оставить комментарий 2', 'Leave a comment' => 'Оставить комментарий 2',
'Comment is required' => 'Нужен комментарий', 'Comment is required' => 'Нужен комментарий',
'Leave a description' => 'Оставьте описание', 'Leave a description' => 'Напишите описание',
'Comment added successfully.' => 'Комментарий успешно добавлен.', 'Comment added successfully.' => 'Комментарий успешно добавлен.',
'Unable to create your comment.' => 'Невозможно создать комментарий.', 'Unable to create your comment.' => 'Невозможно создать комментарий.',
'The description is required' => 'Требуется описание', 'The description is required' => 'Требуется описание',
@ -215,7 +217,7 @@ return array(
'%B %e, %Y' => '%d/%m/%Y', '%B %e, %Y' => '%d/%m/%Y',
// '%b %e, %Y' => '', // '%b %e, %Y' => '',
'Automatic actions' => 'Автоматические действия', 'Automatic actions' => 'Автоматические действия',
'Your automatic action have been created successfully.' => 'Автоматика настроена.', 'Your automatic action have been created successfully.' => 'Автоматика успешно настроена.',
'Unable to create your automatic action.' => 'Не удалось создать автоматизированное действие.', 'Unable to create your automatic action.' => 'Не удалось создать автоматизированное действие.',
'Remove an action' => 'Удалить действие', 'Remove an action' => 'Удалить действие',
'Unable to remove this action.' => 'Не удалось удалить действие', 'Unable to remove this action.' => 'Не удалось удалить действие',
@ -259,7 +261,7 @@ return array(
'Comment removed successfully.' => 'Комментарий удален.', 'Comment removed successfully.' => 'Комментарий удален.',
'Unable to remove this comment.' => 'Не удалось удалить этот комментарий.', 'Unable to remove this comment.' => 'Не удалось удалить этот комментарий.',
'Do you really want to remove this comment?' => 'Вы точно хотите удалить этот комментарий?', 'Do you really want to remove this comment?' => 'Вы точно хотите удалить этот комментарий?',
'Only administrators or the creator of the comment can access to this page.' => 'Только администратор или автор комментарий могут получить доступ.', 'Only administrators or the creator of the comment can access to this page.' => 'Только администратор и автор комментария имеют доступ к этой странице.',
'Details' => 'Подробности', 'Details' => 'Подробности',
'Current password for the user "%s"' => 'Текущий пароль для пользователя « %s »', 'Current password for the user "%s"' => 'Текущий пароль для пользователя « %s »',
'The current password is required' => 'Требуется текущий пароль', 'The current password is required' => 'Требуется текущий пароль',
@ -278,7 +280,7 @@ return array(
'Remember Me' => 'Запомнить меня', 'Remember Me' => 'Запомнить меня',
'Creation date' => 'Дата создания', 'Creation date' => 'Дата создания',
'Filter by user' => 'Фильтр по пользователям', 'Filter by user' => 'Фильтр по пользователям',
'Filter by due date' => 'Фильтр по сроку', 'Filter by due date' => 'Фильтр по дате',
'Everybody' => 'Все', 'Everybody' => 'Все',
'Open' => 'Открытый', 'Open' => 'Открытый',
'Closed' => 'Закрытый', 'Closed' => 'Закрытый',
@ -290,13 +292,13 @@ return array(
'Description' => 'Описание', 'Description' => 'Описание',
'%d comments' => '%d комментариев', '%d comments' => '%d комментариев',
'%d comment' => '%d комментарий', '%d comment' => '%d комментарий',
'Email address invalid' => 'Adresse email invalide', 'Email address invalid' => 'Некорректный e-mail адрес',
'Your Google Account is not linked anymore to your profile.' => 'Ваш аккаунт в Google больше не привязан к вашему профилю.', 'Your Google Account is not linked anymore to your profile.' => 'Ваш аккаунт в Google больше не привязан к вашему профилю.',
'Unable to unlink your Google Account.' => 'Не удалось отвязать ваш профиль от Google.', 'Unable to unlink your Google Account.' => 'Не удалось отвязать ваш профиль от Google.',
'Google authentication failed' => 'Аутентификация Google не удалась', 'Google authentication failed' => 'Аутентификация Google не удалась',
'Unable to link your Google Account.' => 'Не удалось привязать ваш профиль к Google.', 'Unable to link your Google Account.' => 'Не удалось привязать ваш профиль к Google.',
'Your Google Account is linked to your profile successfully.' => 'Ваш профиль успешно привязан к Google.', 'Your Google Account is linked to your profile successfully.' => 'Ваш профиль успешно привязан к Google.',
'Email' => 'Email', 'Email' => 'E-mail',
'Link my Google Account' => 'Привязать мой профиль к Google', 'Link my Google Account' => 'Привязать мой профиль к Google',
'Unlink my Google Account' => 'Отвязать мой профиль от Google', 'Unlink my Google Account' => 'Отвязать мой профиль от Google',
'Login with my Google Account' => 'Аутентификация через Google', 'Login with my Google Account' => 'Аутентификация через Google',
@ -369,7 +371,7 @@ return array(
'Unable to upload the file.' => 'Не удалось загрузить файл.', 'Unable to upload the file.' => 'Не удалось загрузить файл.',
'Display another project' => 'Показать другой проект', 'Display another project' => 'Показать другой проект',
'Your GitHub account was successfully linked to your profile.' => 'Ваш GitHub привязан к вашему профилю.', 'Your GitHub account was successfully linked to your profile.' => 'Ваш GitHub привязан к вашему профилю.',
'Unable to link your GitHub Account.' => 'Не удалось привязать ваш профиль к Github.', 'Unable to link your GitHub Account.' => 'Не удалось привязать ваш профиль к GitHub.',
'GitHub authentication failed' => 'Аутентификация в GitHub не удалась', 'GitHub authentication failed' => 'Аутентификация в GitHub не удалась',
'Your GitHub account is no longer linked to your profile.' => 'Ваш GitHub отвязан от вашего профиля.', 'Your GitHub account is no longer linked to your profile.' => 'Ваш GitHub отвязан от вашего профиля.',
'Unable to unlink your GitHub Account.' => 'Не удалось отвязать ваш профиль от GitHub.', 'Unable to unlink your GitHub Account.' => 'Не удалось отвязать ваш профиль от GitHub.',
@ -393,8 +395,8 @@ return array(
'Clone Project' => 'Клонировать проект', 'Clone Project' => 'Клонировать проект',
'Project cloned successfully.' => 'Проект клонирован.', 'Project cloned successfully.' => 'Проект клонирован.',
'Unable to clone this project.' => 'Не удалось клонировать проект.', 'Unable to clone this project.' => 'Не удалось клонировать проект.',
'Email notifications' => 'Уведомления по email', 'Email notifications' => 'Уведомления по e-mail',
'Enable email notifications' => 'Включить уведомления по email', 'Enable email notifications' => 'Включить уведомления по e-mail',
'Task position:' => 'Позиция задачи:', 'Task position:' => 'Позиция задачи:',
'The task #%d have been opened.' => 'Задача #%d была открыта.', 'The task #%d have been opened.' => 'Задача #%d была открыта.',
'The task #%d have been closed.' => 'Задача #%d была закрыта.', 'The task #%d have been closed.' => 'Задача #%d была закрыта.',
@ -428,9 +430,9 @@ return array(
'Active projects' => 'Активные проекты', 'Active projects' => 'Активные проекты',
'Inactive projects' => 'Неактивные проекты', 'Inactive projects' => 'Неактивные проекты',
'Public access disabled' => 'Общий доступ отключен', 'Public access disabled' => 'Общий доступ отключен',
'Do you really want to disable this project: "%s"?' => 'Вы точно хотите отключить проект: "%s"?', 'Do you really want to disable this project: "%s"?' => 'Вы точно хотите деактивировать проект: "%s"?',
'Do you really want to duplicate this project: "%s"?' => 'Вы точно хотите клонировать проект: "%s"?', 'Do you really want to duplicate this project: "%s"?' => 'Вы точно хотите клонировать проект: "%s"?',
'Do you really want to enable this project: "%s"?' => 'Вы точно хотите включить проект: "%s"?', 'Do you really want to enable this project: "%s"?' => 'Вы точно хотите активировать проект: "%s"?',
'Project activation' => 'Активация проекта', 'Project activation' => 'Активация проекта',
'Move the task to another project' => 'Переместить задачу в другой проект', 'Move the task to another project' => 'Переместить задачу в другой проект',
'Move to another project' => 'Переместить в другой проект', 'Move to another project' => 'Переместить в другой проект',
@ -446,7 +448,7 @@ return array(
'Github account linked' => 'Профиль GitHub связан', 'Github account linked' => 'Профиль GitHub связан',
'Username:' => 'Имя пользователя:', 'Username:' => 'Имя пользователя:',
'Name:' => 'Имя:', 'Name:' => 'Имя:',
'Email:' => 'Email:', 'Email:' => 'E-mail:',
'Default project:' => 'Проект по умолчанию:', 'Default project:' => 'Проект по умолчанию:',
'Notifications:' => 'Уведомления:', 'Notifications:' => 'Уведомления:',
'Notifications' => 'Уведомления', 'Notifications' => 'Уведомления',
@ -483,7 +485,7 @@ return array(
'No activity.' => 'Нет активности', 'No activity.' => 'Нет активности',
'RSS feed' => 'RSS лента', 'RSS feed' => 'RSS лента',
'%s updated a comment on the task #%d' => '%s обновил комментарий задачи #%d', '%s updated a comment on the task #%d' => '%s обновил комментарий задачи #%d',
'%s commented on the task #%d' => '%s откомментировал задачу #%d', '%s commented on the task #%d' => '%s прокомментировал задачу #%d',
'%s updated a subtask for the task #%d' => '%s обновил подзадачу задачи #%d', '%s updated a subtask for the task #%d' => '%s обновил подзадачу задачи #%d',
'%s created a subtask for the task #%d' => '%s создал подзадачу для задачи #%d', '%s created a subtask for the task #%d' => '%s создал подзадачу для задачи #%d',
'%s updated the task #%d' => '%s обновил задачу #%d', '%s updated the task #%d' => '%s обновил задачу #%d',
@ -501,14 +503,14 @@ return array(
// 'Column Change' => '', // 'Column Change' => '',
// 'Position Change' => '', // 'Position Change' => '',
// 'Assignee Change' => '', // 'Assignee Change' => '',
'New password for the user "%s"' => 'Новый пароль для пользователя %s"', 'New password for the user "%s"' => 'Новый пароль для пользователя "%s"',
'Choose an event' => 'Выберите событие', 'Choose an event' => 'Выберите событие',
'Github commit received' => 'Github: коммит получен', 'Github commit received' => 'GitHub: коммит получен',
'Github issue opened' => 'Github: новая проблема', 'Github issue opened' => 'GitHub: новая проблема',
'Github issue closed' => 'Github: проблема закрыта', 'Github issue closed' => 'GitHub: проблема закрыта',
'Github issue reopened' => 'Github: проблема переоткрыта', 'Github issue reopened' => 'GitHub: проблема переоткрыта',
'Github issue assignee change' => 'Github: сменить ответственного за проблему', 'Github issue assignee change' => 'GitHub: сменить ответственного за проблему',
'Github issue label change' => 'Github: ярлык проблемы изменен', 'Github issue label change' => 'GitHub: ярлык проблемы изменен',
'Create a task from an external provider' => 'Создать задачу из внешнего источника', 'Create a task from an external provider' => 'Создать задачу из внешнего источника',
'Change the assignee based on an external username' => 'Изменить назначенного основываясь на внешнем имени пользователя', 'Change the assignee based on an external username' => 'Изменить назначенного основываясь на внешнем имени пользователя',
'Change the category based on an external label' => 'Изменить категорию основываясь на внешнем ярлыке', 'Change the category based on an external label' => 'Изменить категорию основываясь на внешнем ярлыке',
@ -560,9 +562,9 @@ return array(
// 'Github issue comment created' => '', // 'Github issue comment created' => '',
// 'Configure' => '', // 'Configure' => '',
// 'Project management' => '', // 'Project management' => '',
// 'My projects' => '', 'My projects' => 'Мои проекты',
// 'Columns' => '', 'Columns' => 'Колонки',
// 'Task' => '', 'Task' => 'Задача',
// 'Your are not member of any project.' => '', // 'Your are not member of any project.' => '',
// 'Percentage' => '', // 'Percentage' => '',
// 'Number of tasks' => '', // 'Number of tasks' => '',
@ -570,8 +572,8 @@ return array(
// 'Reportings' => '', // 'Reportings' => '',
// 'Task repartition for "%s"' => '', // 'Task repartition for "%s"' => '',
// 'Analytics' => '', // 'Analytics' => '',
// 'Subtask' => '', 'Subtask' => 'Подзадача',
// 'My subtasks' => '', 'My subtasks' => 'Мои подзадачи',
// 'User repartition' => '', // 'User repartition' => '',
// 'User repartition for "%s"' => '', // 'User repartition for "%s"' => '',
// 'Clone this project' => '', // 'Clone this project' => '',
@ -646,10 +648,10 @@ return array(
// 'Language:' => '', // 'Language:' => '',
// 'Timezone:' => '', // 'Timezone:' => '',
// 'All columns' => '', // 'All columns' => '',
// 'Calendar for "%s"' => '', 'Calendar for "%s"' => 'Календарь для "%s"',
// 'Filter by column' => '', 'Filter by column' => 'Фильтр по колонке',
// 'Filter by status' => '', 'Filter by status' => 'Фильтр по статусу',
// 'Calendar' => '', 'Calendar' => 'Календарь',
// 'Next' => '', // 'Next' => '',
// '#%d' => '', // '#%d' => '',
// 'Filter by color' => '', // 'Filter by color' => '',
@ -686,18 +688,18 @@ return array(
// 'Task age in days' => '', // 'Task age in days' => '',
// 'Days in this column' => '', // 'Days in this column' => '',
// '%dd' => '', // '%dd' => '',
// 'Add a link' => '', 'Add a link' => 'Добавить ссылку на другие задачи',
// 'Add a new link' => '', 'Add a new link' => 'Добавление новой ссылки',
// 'Do you really want to remove this link: "%s"?' => '', 'Do you really want to remove this link: "%s"?' => 'Вы уверены что хотите удалить ссылку: "%s"?',
// 'Do you really want to remove this link with task #%d?' => '', 'Do you really want to remove this link with task #%d?' => 'Вы уверены что хотите удалить ссылку вместе с задачей #%d?',
// 'Field required' => '', 'Field required' => 'Поле обязательно для заполнения',
// 'Link added successfully.' => '', 'Link added successfully.' => 'Ссылка успешно добавлена',
// 'Link updated successfully.' => '', 'Link updated successfully.' => 'Ссылка успешно обновлена',
// 'Link removed successfully.' => '', 'Link removed successfully.' => 'Ссылка успешно удалена',
// 'Link labels' => '', 'Link labels' => 'Метки для ссылки',
// 'Link modification' => '', 'Link modification' => 'Обновление ссылки',
// 'Links' => '', 'Links' => 'Ссылки',
// 'Link settings' => '', 'Link settings' => 'Настройки ссылки',
// 'Opposite label' => '', // 'Opposite label' => '',
// 'Remove a link' => '', // 'Remove a link' => '',
// 'Task\'s links' => '', // 'Task\'s links' => '',
@ -707,31 +709,141 @@ return array(
// 'Unable to create your link.' => '', // 'Unable to create your link.' => '',
// 'Unable to update your link.' => '', // 'Unable to update your link.' => '',
// 'Unable to remove this link.' => '', // 'Unable to remove this link.' => '',
// 'relates to' => '', 'relates to' => 'связана с',
// 'blocks' => '', 'blocks' => 'блокирует',
// 'is blocked by' => '', 'is blocked by' => 'заблокирована в',
// 'duplicates' => '', 'duplicates' => 'дублирует',
// 'is duplicated by' => '', 'is duplicated by' => 'дублирована в',
// 'is a child of' => '', 'is a child of' => 'наследник',
// 'is a parent of' => '', 'is a parent of' => 'родитель',
// 'targets milestone' => '', 'targets milestone' => 'часть этапа',
// 'is a milestone of' => '', 'is a milestone of' => '',
// 'fixes' => '', 'fixes' => 'исправляет',
// 'is fixed by' => '', 'is fixed by' => 'исправлено в',
// 'This task' => '', 'This task' => 'Эта задача',
// '<1h' => '', // '<1h' => '',
// '%dh' => '', // '%dh' => '',
// '%b %e' => '', // '%b %e' => '',
// 'Expand tasks' => '', 'Expand tasks' => 'Развернуть задачи',
// 'Collapse tasks' => '', 'Collapse tasks' => 'Свернуть задачи',
// 'Expand/collapse tasks' => '', 'Expand/collapse tasks' => 'Развернуть/свернуть задачи',
// 'Close dialog box' => '', 'Close dialog box' => 'Закрыть диалог',
// 'Submit a form' => '', 'Submit a form' => 'Отправить форму',
// 'Board view' => '', 'Board view' => 'Просмотр доски',
// 'Keyboard shortcuts' => '', 'Keyboard shortcuts' => 'Горячие клавиши',
// 'Open board switcher' => '', 'Open board switcher' => 'Открыть переключатель доски',
// 'Application' => '', 'Application' => 'Приложение',
// 'Filter recently updated' => '', 'Filter recently updated' => 'Сортировать по дате обновления',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', 'More filters' => 'Использовать фильтры',
'Compact view' => 'Компактный вид',
'Horizontal scrolling' => 'Горизонтальная прокрутка',
'Compact/wide view' => 'Компактный/широкий вид',
'No results match:' => 'Отсутствуют результаты:',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -0,0 +1,849 @@
<?php
return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'None',
'edit' => 'izmeni',
'Edit' => 'Izmeni',
'remove' => 'ukloni',
'Remove' => 'Ukloni',
'Update' => 'Ažuriraj',
'Yes' => 'Da',
'No' => 'Ne',
'cancel' => 'odustani',
'or' => 'ili',
'Yellow' => 'Žuta',
'Blue' => 'Plava',
'Green' => 'Zelena',
'Purple' => 'Ljubičasta',
'Red' => 'Crvena',
'Orange' => 'Narandžasta',
'Grey' => 'Siva',
'Save' => 'Snimi',
'Login' => 'Prijava',
'Official website:' => 'Zvanična strana:',
'Unassigned' => 'Nedodeljen',
'View this task' => 'Pregledaj zadatak',
'Remove user' => 'Ukloni korisnika',
'Do you really want to remove this user: "%s"?' => 'Da li zaista želiš da ukloniš korisnika: "%s"?',
'New user' => 'novi korisnik',
'All users' => 'Svi korisnici',
'Username' => 'Korisnik',
'Password' => 'Lozinka',
'Default project' => 'Podrazumevani projekat',
'Administrator' => 'Administrator',
'Sign in' => 'Odjava',
'Users' => 'Korisnik',
'No user' => 'Ne',
'Forbidden' => 'Zabranjeno',
'Access Forbidden' => 'Zabranjen prostup',
'Only administrators can access to this page.' => 'Samo administrator može videti ovu stranu.',
'Edit user' => 'Izmeni korisnika',
'Logout' => 'Odjava',
'Bad username or password' => 'Loše korisničko ime ili lozinka',
'users' => 'korisnici',
'projects' => 'projekti',
'Edit project' => 'Izmeni projekat',
'Name' => 'Ime',
'Activated' => 'Aktiviran',
'Projects' => 'Projekti',
'No project' => 'Bez projekta',
'Project' => 'Projekat',
'Status' => 'Status',
'Tasks' => 'Zadatak',
'Board' => 'Tabla',
'Actions' => 'Akcje',
'Inactive' => 'Neaktivan',
'Active' => 'Aktivan',
'Column %d' => 'Kolona %d',
'Add this column' => 'Dodaj kolonu',
'%d tasks on the board' => '%d zadataka na tabli',
'%d tasks in total' => '%d zadataka ukupno',
'Unable to update this board.' => 'Nemogu da ažuriram ovu tablu.',
'Edit board' => 'Izmeni tablu',
'Disable' => 'Onemogući',
'Enable' => 'Omogući',
'New project' => 'Novi projekat',
'Do you really want to remove this project: "%s"?' => 'Da li želiš da ukloniš projekat: "%s"?',
'Remove project' => 'Ukloni projekat',
'Boards' => 'Table',
'Edit the board for "%s"' => 'Izmeni tablu za "%s"',
'All projects' => 'Svi projekti',
'Change columns' => 'Zameni kolonu',
'Add a new column' => 'Dodaj novu kolonu',
'Title' => 'Naslov',
'Add Column' => 'Dodaj kolunu',
'Project "%s"' => 'Projekt "%s"',
'Nobody assigned' => 'Niko nije dodeljen',
'Assigned to %s' => 'Dodeljen korisniku %s',
'Remove a column' => 'Ukloni kolonu',
'Remove a column from a board' => 'Ukloni kolonu sa table',
'Unable to remove this column.' => 'Nemoguće uklanjanje kolone.',
'Do you really want to remove this column: "%s"?' => 'Da li zaista želiš da ukoniš ovu kolonu: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Ova akcija BRIŠE SVE ZADATKE vezane za ovu kolonu!',
'Settings' => 'Podešavanja',
'Application settings' => 'Podešavanja aplikacije',
'Language' => 'Jezik',
'Webhook token:' => 'Token :',
'API token:' => 'Token za API',
'More information' => 'Još informacja',
'Database size:' => 'Veličina baze :',
'Download the database' => 'Preuzmi bazu',
'Optimize the database' => 'Optimizuj bazu',
'(VACUUM command)' => '(komanda VACUUM)',
'(Gzip compressed Sqlite file)' => '(Sqlite baza spakovana Gzip-om)',
'User settings' => 'Korisnička podešavanja',
'My default project:' => 'Moj podrazumevani projekat:',
'Close a task' => 'Zatvori zadatak',
'Do you really want to close this task: "%s"?' => 'Da li zaista želiš da zatvoriš ovaj zadatak: "%s"?',
'Edit a task' => 'Izmeni zadatak',
'Column' => 'Kolona',
'Color' => 'Boja',
'Assignee' => 'Dodeli',
'Create another task' => 'Dodaj zadatak',
'New task' => 'Novi zadatak',
'Open a task' => 'Otvori zadatak',
'Do you really want to open this task: "%s"?' => 'Da li zaista želiš da otvoriš zadatak: "%s"?',
'Back to the board' => 'Nazad na tablu',
'Created on %B %e, %Y at %k:%M %p' => 'Kreiran %e %B %Y o %k:%M',
'There is nobody assigned' => 'Niko nije dodeljen!',
'Column on the board:' => 'Kolona na tabli:',
'Status is open' => 'Status otvoren',
'Status is closed' => 'Status zatvoren',
'Close this task' => 'Zatvori ovaj zadatak',
'Open this task' => 'Otvori ovaj zadatak',
'There is no description.' => 'Bez opisa.',
'Add a new task' => 'Dodaj zadatak',
'The username is required' => 'Korisničko ime je obavezno',
'The maximum length is %d characters' => 'Maksimalna dužina je %d znakova',
'The minimum length is %d characters' => 'Minimalna dužina je %d znakova',
'The password is required' => 'Lozinka je obavezna',
'This value must be an integer' => 'Mora biti ceo broj',
'The username must be unique' => 'Korisničko ime mora biti jedinstveno',
'The username must be alphanumeric' => 'Korisničko ime sme sadržati samo brojeve i slova',
'The user id is required' => 'ID korisnika je obavezan',
'Passwords don\'t match' => 'Lozinke se ne podudaraju',
'The confirmation is required' => 'Potvrda je obavezna',
'The column is required' => 'Kolona je obavezna',
'The project is required' => 'Projekat je obavezan',
'The color is required' => 'Boja je obavezna',
'The id is required' => 'ID je obavezan',
'The project id is required' => 'ID projekta je obavezan',
'The project name is required' => 'Naziv projekta je obavezan',
'This project must be unique' => 'Projekat mora biti jedinstven',
'The title is required' => 'Naslov je obavezan',
'The language is required' => 'Jezik je obavezan',
'There is no active project, the first step is to create a new project.' => 'Nema aktivnih projekata. Potrebno je prvo napraviti novi projekat.',
'Settings saved successfully.' => 'Podešavanja uspešno snimljena.',
'Unable to save your settings.' => 'Nemoguće snimanje podešavanja.',
'Database optimization done.' => 'Optimizacija baze je završena.',
'Your project have been created successfully.' => 'Projekat je uspešno napravljen.',
'Unable to create your project.' => 'Nemoguće kreiranje projekta.',
'Project updated successfully.' => 'Projekt je uspešno ažuriran.',
'Unable to update this project.' => 'Nemoguće ažuriranje projekta.',
'Unable to remove this project.' => 'Nemoguće uklanjanje projekta.',
'Project removed successfully.' => 'Projekat uspešno uklonjen.',
'Project activated successfully.' => 'Projekt uspešno aktiviran.',
'Unable to activate this project.' => 'Nemoguće aktiviranje projekta.',
'Project disabled successfully.' => 'Projekat uspešno deaktiviran.',
'Unable to disable this project.' => 'nemoguće deaktiviranje projekta.',
'Unable to open this task.' => 'Nemoguće otvaranje zadatka.',
'Task opened successfully.' => 'Zadatak uspešno otvoren.',
'Unable to close this task.' => 'Nije moguće zatvaranje ovog zadatka.',
'Task closed successfully.' => 'Zadatak uspešno zatvoren.',
'Unable to update your task.' => 'Nije moguće ažuriranje zadatka.',
'Task updated successfully.' => 'Zadatak uspešno ažuriran.',
'Unable to create your task.' => 'Nije moguće kreiranje zadatka.',
'Task created successfully.' => 'Zadatak uspešno kreiran.',
'User created successfully.' => 'Korisnik uspešno kreiran',
'Unable to create your user.' => 'Nije uspelo kreiranje korisnika.',
'User updated successfully.' => 'Korisnik uspešno ažuriran.',
'Unable to update your user.' => 'Nije moguće ažuriranje korisnika.',
'User removed successfully.' => 'Korisnik uspešno uklonjen.',
'Unable to remove this user.' => 'Nije moguće uklanjanje korisnika.',
'Board updated successfully.' => 'Tabla uspešno ažurirana.',
'Ready' => 'Spreman',
'Backlog' => 'Log',
'Work in progress' => 'U radu',
'Done' => 'Gotovo',
'Application version:' => 'Verzija aplikacije:',
'Completed on %B %e, %Y at %k:%M %p' => 'Završeno u %e %B %Y o %k:%M',
'%B %e, %Y at %k:%M %p' => '%e %B %Y o %k:%M',
'Date created' => 'Kreiran dana',
'Date completed' => 'Završen dana',
'Id' => 'Id',
'No task' => 'bez zadataka',
'Completed tasks' => 'Zatvoreni zadaci',
'List of projects' => 'Spisak projekata',
'Completed tasks for "%s"' => 'zatvoreni zadaci za "%s"',
'%d closed tasks' => '%d zatvorenih zadataka',
'No task for this project' => 'Nema dodeljenih zadataka ovom projektu',
'Public link' => 'Javni link',
'There is no column in your project!' => 'Nema dodeljenih kolona ovom projektu',
'Change assignee' => 'Izmeni dodelu',
'Change assignee for the task "%s"' => 'Izmeni dodelu za ovaj zadatak "%s"',
'Timezone' => 'Vremenska zona',
'Sorry, I didn\'t find this information in my database!' => 'Na žalost, nije pronađena informacija u bazi',
'Page not found' => 'Strana nije pronađena',
'Complexity' => 'Složenost',
'limit' => 'ograničenje',
'Task limit' => 'Ograničenje zadatka',
'Task count' => 'Broj zadataka',
'This value must be greater than %d' => 'Vrednost mora biti veća od %d',
'Edit project access list' => 'Izmeni prava pristupa projektu',
'Edit users access' => 'Izmeni korisnička prava',
'Allow this user' => 'Dozvoli ovog korisnika',
'Only those users have access to this project:' => 'Samo ovi korisnici imaju pristup projektu:',
'Don\'t forget that administrators have access to everything.' => 'Zapamti: Administrator može pristupiti svemu!',
'Revoke' => 'Povuci',
'List of authorized users' => 'Spisak odobrenih korisnika',
'User' => 'Korisnik',
'Nobody have access to this project.' => 'Niko nema pristup ovom projektu',
'You are not allowed to access to this project.' => 'Nije ti dozvoljen pristup ovom projektu.',
'Comments' => 'Komentari',
'Post comment' => 'Dodaj komentar',
'Write your text in Markdown' => 'Pisanje teksta pomoću Markdown',
'Leave a comment' => 'Ostavi komentar',
'Comment is required' => 'Komentar je obavezan',
'Leave a description' => 'Dodaj opis',
'Comment added successfully.' => 'Komentar uspešno ostavljen',
'Unable to create your comment.' => 'Nemoguće kreiranje komentara',
'The description is required' => 'Opis je obavezan',
'Edit this task' => 'Izmeni ovaj zadatak',
'Due Date' => 'Termin',
'Invalid date' => 'Loš datum',
'Must be done before %B %e, %Y' => 'Termin do %e %B %Y',
'%B %e, %Y' => '%e %B %Y',
// '%b %e, %Y' => '',
'Automatic actions' => 'Automatske akcije',
'Your automatic action have been created successfully.' => 'Uspešno kreirana automatska akcija',
'Unable to create your automatic action.' => 'Nemoguće kreiranje automatske akcije',
'Remove an action' => 'Obriši akciju',
'Unable to remove this action.' => 'Nije moguće obrisati akciju',
'Action removed successfully.' => 'Akcija obrisana',
'Automatic actions for the project "%s"' => 'Akcje za automatizaciju projekta "%s"',
'Defined actions' => 'Definisane akcje',
'Add an action' => 'dodaj akcju',
'Event name' => 'Naziv događaja',
'Action name' => 'Naziv akcije',
'Action parameters' => 'Parametri akcije',
'Action' => 'Akcija',
'Event' => 'Događaj',
'When the selected event occurs execute the corresponding action.' => 'Kad se događaj desi izvrši odgovarajuću akciju',
'Next step' => 'Sledeći korak',
'Define action parameters' => 'Definiši parametre akcije',
'Save this action' => 'Snimi akciju',
'Do you really want to remove this action: "%s"?' => 'Da li da obrišem akciju "%s"?',
'Remove an automatic action' => 'Obriši automatsku akciju',
'Close the task' => 'Zatvori zadatak',
'Assign the task to a specific user' => 'Dodeli zadatak određenom korisniku',
'Assign the task to the person who does the action' => 'Dodeli zadatak korisniku koji je izvršio akciju',
'Duplicate the task to another project' => 'Kopiraj akciju u drugi projekat',
'Move a task to another column' => 'Premesti zadatak u drugu kolonu',
'Move a task to another position in the same column' => 'Promeni poziciju zadatka u istoj koloni',
'Task modification' => 'Izman zadatka',
'Task creation' => 'Kreiranje zadatka',
'Open a closed task' => 'Otvori zatvoreni zadatak',
'Closing a task' => 'Zatvaranja zadatka',
'Assign a color to a specific user' => 'Dodeli boju korisniku',
'Column title' => 'Naslov kolone',
'Position' => 'Pozicija',
'Move Up' => 'Podigni',
'Move Down' => 'Spusti',
'Duplicate to another project' => 'Kopiraj u drugi projekat',
'Duplicate' => 'Napravi kopiju',
'link' => 'link',
'Update this comment' => 'Ažuriraj komentar',
'Comment updated successfully.' => 'Komentar uspešno ažuriran.',
'Unable to update your comment.' => 'Neuspešno ažuriranje komentara.',
'Remove a comment' => 'Obriši komentar',
'Comment removed successfully.' => 'Komentar je uspešno obrisan.',
'Unable to remove this comment.' => 'Neuspešno brisanje komentara.',
'Do you really want to remove this comment?' => 'Da li da obrišem ovaj komentar?',
'Only administrators or the creator of the comment can access to this page.' => 'Samo administrator i kreator komentara mogu ga obrisati.',
'Details' => 'Detalji',
'Current password for the user "%s"' => 'Trenutna lozinka za korisnika "%s"',
'The current password is required' => 'Trenutna lozinka je obavezna',
'Wrong password' => 'Pogrešna lozinka',
'Reset all tokens' => 'Resetuj tokene',
'All tokens have been regenerated.' => 'Svi tokeni su ponovo generisani.',
'Unknown' => 'Nepoznat',
'Last logins' => 'Poslednja prijava',
'Login date' => 'Datum prijave',
'Authentication method' => 'Metod autentikacije',
'IP address' => 'IP adresa',
'User agent' => 'Browser',
'Persistent connections' => 'Stalna konekcija',
'No session.' => 'Bez sesjie',
'Expiration date' => 'Ističe',
'Remember Me' => 'Zapamti me',
'Creation date' => 'Datum kreiranja',
'Filter by user' => 'Po korisniku',
'Filter by due date' => 'Po terminu',
'Everybody' => 'Svi',
'Open' => 'Otvoreni',
'Closed' => 'Zatvoreni',
'Search' => 'Traži',
'Nothing found.' => 'Ništa nije pronađeno',
'Search in the project "%s"' => 'Traži u prijektu "%s"',
'Due date' => 'Termin',
'Others formats accepted: %s and %s' => 'Ostali formati: %s i %s',
'Description' => 'Opis',
'%d comments' => '%d Komentara',
'%d comment' => '%d Komentar',
'Email address invalid' => 'Pogrešan e-mail',
'Your Google Account is not linked anymore to your profile.' => 'Tvoj google nalog više nije povezan sa profilom',
'Unable to unlink your Google Account.' => 'Neuspešno ukidanje veze od Google naloga',
'Google authentication failed' => 'Neuspešna Google autentikacija',
'Unable to link your Google Account.' => 'Neuspešno povezivanje sa Google nalogom',
'Your Google Account is linked to your profile successfully.' => 'Vaš Google nalog je uspešno povezan sa vašim profilom',
'Email' => 'E-mail',
'Link my Google Account' => 'Poveži sa Google nalogom',
'Unlink my Google Account' => 'Ukini vezu sa Google nalogom',
'Login with my Google Account' => 'Prijavi se preko Google naloga',
'Project not found.' => 'Projekat nije pronađen.',
'Task #%d' => 'Zadatak #%d',
'Task removed successfully.' => 'Zadatak uspešno uklonjen.',
'Unable to remove this task.' => 'Nemoguće uklanjanje zadatka.',
'Remove a task' => 'Ukloni zadatak',
'Do you really want to remove this task: "%s"?' => 'Da li da obrišem zadatak "%s"?',
'Assign automatically a color based on a category' => 'Automatski dodeli boju po kategoriji',
'Assign automatically a category based on a color' => 'Automatski dodeli kategoriju po boji',
'Task creation or modification' => 'Kreiranje ili izmena zadatka',
'Category' => 'Kategorija',
'Category:' => 'Kategorija:',
'Categories' => 'Kategorije',
'Category not found.' => 'Kategorija nije pronađena',
'Your category have been created successfully.' => 'Uspešno kreirana kategorija.',
'Unable to create your category.' => 'Nije moguće kreirati kategoriju.',
'Your category have been updated successfully.' => 'Kategorija je uspešno izmenjena',
'Unable to update your category.' => 'Nemoguće izmeniti kategoriju',
'Remove a category' => 'Obriši kategoriju',
'Category removed successfully.' => 'Kategorija uspešno uklonjena.',
'Unable to remove this category.' => 'Nije moguće ukloniti kategoriju.',
'Category modification for the project "%s"' => 'Izmena kategorije za projekat "%s"',
'Category Name' => 'Naziv kategorije',
'Categories for the project "%s"' => 'Kategorije u projektu',
'Add a new category' => 'Dodaj novu kategoriju',
'Do you really want to remove this category: "%s"?' => 'Da li zaista želiš da ukloniš kategoriju: "%s"?',
'Filter by category' => 'Po kategoriji',
'All categories' => 'Sve kategorije',
'No category' => 'Bez kategorije',
'The name is required' => 'Naziv je obavezan',
'Remove a file' => 'Ukloni fajl',
'Unable to remove this file.' => 'Fajl nije moguće ukloniti.',
'File removed successfully.' => 'Uspešno uklonjen fajl.',
'Attach a document' => 'Prikači dokument',
'Do you really want to remove this file: "%s"?' => 'Da li da uklonim fajl: "%s"?',
'open' => 'otvori',
'Attachments' => 'Prilozi',
'Edit the task' => 'Izmena Zadatka',
'Edit the description' => 'Izmena opisa',
'Add a comment' => 'Dodaj komentar',
'Edit a comment' => 'Izmeni komentar',
'Summary' => 'Pregled',
'Time tracking' => 'Praćenje vremena',
'Estimate:' => 'Procena:',
'Spent:' => 'Potrošeno:',
'Do you really want to remove this sub-task?' => 'Da li da uklonim pod-zdadatak?',
'Remaining:' => 'Preostalo:',
'hours' => 'sati',
'spent' => 'potrošeno',
'estimated' => 'procenjeno',
'Sub-Tasks' => 'Pod-zadaci',
'Add a sub-task' => 'Dodaj pod-zadatak',
'Original estimate' => 'Originalna procena',
'Create another sub-task' => 'Dodaj novi pod-zadatak',
'Time spent' => 'Utrošeno vreme',
'Edit a sub-task' => 'Izmeni pod-zadatak',
'Remove a sub-task' => 'Ukloni pod-zadatak',
'The time must be a numeric value' => 'Vreme mora biti broj',
'Todo' => 'Za rad',
'In progress' => 'U radu',
'Sub-task removed successfully.' => 'Pod-zadatak uspešno uklonjen.',
'Unable to remove this sub-task.' => 'Nie można usunąć tego pod-zadania.',
'Sub-task updated successfully.' => 'Pod-zadatak zaktualizowane pomyślnie.',
'Unable to update your sub-task.' => 'Nie można zaktalizować tego pod-zadania.',
'Unable to create your sub-task.' => 'Nie można utworzyć tego pod-zadania.',
'Sub-task added successfully.' => 'Pod-zadatak utworzone pomyślnie',
'Maximum size: ' => 'Maksimalna veličina: ',
'Unable to upload the file.' => 'Nije moguće snimiti fajl.',
'Display another project' => 'Prikaži drugi projekat',
'Your GitHub account was successfully linked to your profile.' => 'Konto Github podłączone pomyślnie.',
'Unable to link your GitHub Account.' => 'Nie można połączyć z kontem Github.',
'GitHub authentication failed' => 'Autentykacja Github nieudana',
'Your GitHub account is no longer linked to your profile.' => 'Konto Github nie jest już podłączone do twojego profilu.',
'Unable to unlink your GitHub Account.' => 'Nie można odłączyć konta Github.',
'Login with my GitHub Account' => 'Zaloguj przy użyciu konta Github',
'Link my GitHub Account' => 'Podłącz konto Github',
'Unlink my GitHub Account' => 'Odłącz konto Github',
'Created by %s' => 'Kreirao %s',
'Last modified on %B %e, %Y at %k:%M %p' => 'Poslednja izmena %e %B %Y o %k:%M',
'Tasks Export' => 'Izvoz zadataka',
'Tasks exportation for "%s"' => 'Izvoz zadataka za "%s"',
'Start Date' => 'Početni datum',
'End Date' => 'Krajni datum',
'Execute' => 'Izvrši',
'Task Id' => 'Identifikator Zadatka',
'Creator' => 'Autor',
'Modification date' => 'Datum izmene',
'Completion date' => 'Datum kompletiranja',
'Webhook URL for task creation' => 'Webhook URL zadatka za kreiranje',
'Webhook URL for task modification' => 'Webhook URL zadatka za izmenu',
'Clone' => 'Iskopiraj',
'Clone Project' => 'Iskopiraj projekat',
'Project cloned successfully.' => 'Projekat uspešno iskopiran.',
'Unable to clone this project.' => 'Nije moguće iskopirati projekat.',
'Email notifications' => 'Obaveštenje e-mailom',
'Enable email notifications' => 'Omogući obaveštenja e-mailom',
'Task position:' => 'Pozicija zadatka:',
'The task #%d have been opened.' => 'Zadatak #%d je otvoren.',
'The task #%d have been closed.' => 'Zadatak #$d je zatvoren.',
'Sub-task updated' => 'Pod-zadatak izmenjen',
'Title:' => 'Naslov:',
// 'Status:' => '',
'Assignee:' => 'Dodeli:',
'Time tracking:' => 'Praćenje vremena: ',
'New sub-task' => 'Novi Pod-zadatak',
'New attachment added "%s"' => 'Novi prilog ubačen "%s"',
'Comment updated' => 'Komentar izmenjen',
'New comment posted by %s' => 'Novi komentar ostavio %s',
'List of due tasks for the project "%s"' => 'Spisak dospelih zadataka za projekat "%s"',
// 'New attachment' => '',
// 'New comment' => '',
// 'New subtask' => '',
// 'Subtask updated' => '',
// 'Task updated' => '',
'Task closed' => 'Zadatak je zatvoren',
'Task opened' => 'Zadatak je otvoren',
'[%s][Due tasks]' => '[%s][Dospeli zadaci]',
'[Kanboard] Notification' => '[Kanboard] Obaveštenja',
'I want to receive notifications only for those projects:' => 'Želim obaveštenja samo za ovaj projekat:',
'view the task on Kanboard' => 'Pregledaj zadatke',
'Public access' => 'Javni pristup',
'Category management' => 'Uređivanje kategorija',
'User management' => 'Uređivanje korisnika',
'Active tasks' => 'Aktivni zadaci',
'Disable public access' => 'Zabrani javni pristup',
'Enable public access' => 'Dozvoli javni pristup',
'Active projects' => 'Aktivni projekti',
'Inactive projects' => 'Neaktivni projekti',
'Public access disabled' => 'Javni pristup onemogućen!',
'Do you really want to disable this project: "%s"?' => 'Da li zaista želiš da deaktiviraš projekat: "%s"?',
'Do you really want to duplicate this project: "%s"?' => 'Da li da napravim kopiju ovog projekta: "%s"?',
'Do you really want to enable this project: "%s"?' => 'Da li zaista želiš da aktiviraš projekat: "%s"?',
'Project activation' => 'Aktivacija projekta',
'Move the task to another project' => 'Premesti zadatak u drugi projekat',
'Move to another project' => 'Premesti u drugi projekat',
'Do you really want to duplicate this task?' => 'Da li da napravim kopiju ovog projekta: "%s"?',
'Duplicate a task' => 'Kopiraj zadatak',
'External accounts' => 'Spoljni nalozi',
'Account type' => 'Tip naloga',
'Local' => 'Lokalno',
'Remote' => 'Udaljno',
'Enabled' => 'Omogući',
'Disabled' => 'Onemogući',
'Google account linked' => 'Połączone konto Google',
'Github account linked' => 'Połączone konto Github',
'Username:' => 'Korisničko ime:',
'Name:' => 'Ime i Prezime',
'Email:' => 'Email: ',
'Default project:' => 'Osnovni projekat:',
'Notifications:' => 'Obaveštenja: ',
'Notifications' => 'Obaveštenja',
'Group:' => 'Grupa:',
'Regular user' => 'Standardni korisnik',
'Account type:' => 'Vrsta naloga:',
'Edit profile' => 'Izmeni profil',
'Change password' => 'Izmeni lozinku',
'Password modification' => 'Izmena lozinke',
'External authentications' => 'Spoljne akcije',
'Google Account' => 'Google nalog',
'Github Account' => 'Github nalog',
'Never connected.' => 'Bez konekcija.',
'No account linked.' => 'Bez povezanih naloga.',
'Account linked.' => 'Nalog povezan.',
'No external authentication enabled.' => 'Bez omogućenih spoljnih autentikacija.',
'Password modified successfully.' => 'Uspešna izmena lozinke.',
'Unable to change the password.' => 'Nije moguće izmeniti lozinku.',
'Change category for the task "%s"' => 'Izmeni kategoriju zadatka "%s"',
'Change category' => 'Izmeni kategoriju',
'%s updated the task %s' => '%s izmeni zadatak %s',
'%s opened the task %s' => '%s aktivni zadaci %s',
'%s moved the task %s to the position #%d in the column "%s"' => '%s premešten zadatak %s na poziciju #%d u koloni "%s"',
'%s moved the task %s to the column "%s"' => '%s premešten zadatak %s u kolonu "%s"',
'%s created the task %s' => '%s kreirao zadatak %s',
'%s closed the task %s' => '%s zatvorio zadatak %s',
'%s created a subtask for the task %s' => '%s kreiran pod-zadatak zadatka %s',
'%s updated a subtask for the task %s' => '%s izmenjen pod-zadatak zadatka %s',
'Assigned to %s with an estimate of %s/%sh' => 'Dodeljen korisniku %s uz procenu vremena %s/%sh',
'Not assigned, estimate of %sh' => 'Ne dodeljen, procenjeno vreme %sh',
'%s updated a comment on the task %s' => '%s izmenjen komentar zadatka %s',
'%s commented the task %s' => '%s komentarisao zadatak %s',
'%s\'s activity' => 'Aktivnosti %s',
'No activity.' => 'Bez aktivnosti.',
'RSS feed' => 'RSS kanal',
'%s updated a comment on the task #%d' => '%s izmenjen komentar zadatka #%d',
'%s commented on the task #%d' => '%s komentarisao zadatak #%d',
'%s updated a subtask for the task #%d' => '%s izmenjen pod-zadatak zadatka #%d',
'%s created a subtask for the task #%d' => '%s kreirao pod-zadatak zadatka #%d',
'%s updated the task #%d' => '%s izmenjen zadatak #%d',
'%s created the task #%d' => '%s kreirao zadatak #%d',
'%s closed the task #%d' => '%s zatvorio zadatak #%d',
'%s open the task #%d' => '%s otvorio zadatak #%d',
'%s moved the task #%d to the column "%s"' => '%s premestio zadatak #%d u kolonu "%s"',
'%s moved the task #%d to the position %d in the column "%s"' => '%s premestio zadatak #%d na pozycję %d w kolmnie "%s"',
'Activity' => 'Aktivnosti',
'Default values are "%s"' => 'Osnovne vrednosti su: "%s"',
'Default columns for new projects (Comma-separated)' => 'Osnovne kolone za novi projekat (Odvojeni zarezom)',
'Task assignee change' => 'Zmień osobę odpowiedzialną',
'%s change the assignee of the task #%d to %s' => '%s zamena dodele za zadatak #%d na %s',
'%s changed the assignee of the task %s to %s' => '%s zamena dodele za zadatak %s na %s',
// 'Column Change' => '',
// 'Position Change' => '',
// 'Assignee Change' => '',
'New password for the user "%s"' => 'Nova lozinka za korisnika "%s"',
'Choose an event' => 'Izaberi događaj',
// 'Github commit received' => '',
// 'Github issue opened' => '',
// 'Github issue closed' => '',
// 'Github issue reopened' => '',
// 'Github issue assignee change' => '',
// 'Github issue label change' => '',
'Create a task from an external provider' => 'Kreiraj zadatak preko posrednika',
'Change the assignee based on an external username' => 'Zmień osobę odpowiedzialną na podstawie zewnętrznej nazwy użytkownika',
'Change the category based on an external label' => 'Zmień kategorię na podstawie zewnętrzenj etykiety',
// 'Reference' => '',
// 'Reference: %s' => '',
'Label' => 'Etikieta',
'Database' => 'Baza',
'About' => 'Informacje',
'Database driver:' => 'Database driver:',
'Board settings' => 'Podešavanje table',
'URL and token' => 'URL i token',
// 'Webhook settings' => '',
'URL for task creation:' => 'URL za kreiranje zadataka',
'Reset token' => 'Resetuj token',
// 'API endpoint:' => '',
'Refresh interval for private board' => 'Interval osvežavanja privatnih tabli',
'Refresh interval for public board' => 'Interval osvežavanja javnih tabli',
'Task highlight period' => 'Task highlight period',
// 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '',
// 'Frequency in second (60 seconds by default)' => '',
// 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '',
'Application URL' => 'Adres URL aplikacji',
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Primer: http://example.kanboard.net/ (koristi se u obaveštenjima putem mail-a)',
'Token regenerated.' => 'Token wygenerowany ponownie.',
'Date format' => 'Format daty',
'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO je uvek prihvatljiv, primer: "%s", "%s"',
'New private project' => 'Novi privatni projekat',
'This project is private' => 'Ovaj projekat je privatan',
'Type here to create a new sub-task' => 'Kucaj ovde za kreiranje novog pod-zadatka',
'Add' => 'Dodaj',
'Estimated time: %s hours' => 'Procenjeno vreme: %s godzin',
'Time spent: %s hours' => 'Utrošeno vreme: %s godzin',
'Started on %B %e, %Y' => 'Započeto dana %e %B %Y',
'Start date' => 'Datum početka',
'Time estimated' => 'Procenjeno vreme',
'There is nothing assigned to you.' => 'Ništa vam nije dodeljeno',
'My tasks' => 'Moji zadaci',
'Activity stream' => 'Spisak aktinosti',
'Dashboard' => 'Panel',
'Confirmation' => 'Potvrda',
'Allow everybody to access to this project' => 'Dozvoli svima pristup projektu',
'Everybody have access to this project.' => 'Svima je dozvoljen pristup.',
// 'Webhooks' => '',
// 'API' => '',
'Integration' => 'Integracja',
// 'Github webhooks' => '',
// 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
'Configure' => 'Podesi',
'Project management' => 'Uređivanje projekata',
'My projects' => 'Moji projekti',
'Columns' => 'Kolone',
'Task' => 'Zadaci',
'Your are not member of any project.' => 'Nisi član ni jednog projekta',
'Percentage' => 'Procenat',
'Number of tasks' => 'Broj zadataka',
'Task distribution' => 'Podela zadataka',
'Reportings' => 'Izveštaji',
'Task repartition for "%s"' => 'Zaduženja zadataka za "%s"',
'Analytics' => 'Analiza',
'Subtask' => 'Pod-zadatak',
'My subtasks' => 'Moji pod-zadaci',
'User repartition' => 'Zaduženja korisnika',
'User repartition for "%s"' => 'Zaduženja korisnika za "%s"',
'Clone this project' => 'Kopiraj projekat',
'Column removed successfully.' => 'Kolumna usunięta pomyslnie.',
'Edit Project' => 'Izmeni projekat',
// 'Github Issue' => '',
'Not enough data to show the graph.' => 'Nedovoljno podataka za grafikon.',
'Previous' => 'Prethodni',
'The id must be an integer' => 'ID musi być liczbą całkowitą',
'The project id must be an integer' => 'ID projektu musi być liczbą całkowitą',
'The status must be an integer' => 'Status musi być liczbą całkowitą',
'The subtask id is required' => 'ID pod-zadatak jest wymagane',
'The subtask id must be an integer' => 'ID pod-zadania musi być liczbą całkowitą',
'The task id is required' => 'ID zadania jest wymagane',
'The task id must be an integer' => 'ID zadatka mora biti broj',
'The user id must be an integer' => 'ID korisnika mora biti broj',
'This value is required' => 'Vrednost je obavezna',
'This value must be numeric' => 'Vrednost mora biti broj',
'Unable to create this task.' => 'Nije moguće kreirati zadatak.',
'Cumulative flow diagram' => 'Zbirni dijagram toka',
'Cumulative flow diagram for "%s"' => 'Zbirni dijagram toka za "%s"',
'Daily project summary' => 'Zbirni pregled po danima',
'Daily project summary export' => 'Izvoz zbirnog pregleda po danima',
'Daily project summary export for "%s"' => 'Izvoz zbirnig pregleda po danima za "%s"',
'Exports' => 'Izvoz',
// 'This export contains the number of tasks per column grouped per day.' => '',
'Nothing to preview...' => 'Ništa za prikazivanje...',
'Preview' => 'Pregled',
'Write' => 'Piši',
'Active swimlanes' => 'Aktivni razdelnik',
'Add a new swimlane' => 'Dodaj razdelnik',
'Change default swimlane' => 'Zameni osnovni razdelnik',
'Default swimlane' => 'Osnovni razdelnik',
'Do you really want to remove this swimlane: "%s"?' => 'Da li da uklonim razdelnik: "%s"?',
'Inactive swimlanes' => 'Neaktivni razdelniki',
'Set project manager' => 'Podesi menadžera projekta',
'Set project member' => 'Podesi učesnika projekat',
'Remove a swimlane' => 'Ukloni razdelnik',
'Rename' => 'Preimenuj',
'Show default swimlane' => 'Prikaži osnovni razdelnik',
'Swimlane modification for the project "%s"' => 'Izmena razdelnika za projekat "%s"',
'Swimlane not found.' => 'Razdelnik nije pronađen.',
'Swimlane removed successfully.' => 'Razdelnik uspešno uklonjen.',
'Swimlanes' => 'Razdelnici',
'Swimlane updated successfully.' => 'Razdelnik zaktualizowany pomyślnie.',
// 'The default swimlane have been updated successfully.' => '',
// 'Unable to create your swimlane.' => '',
// 'Unable to remove this swimlane.' => '',
// 'Unable to update this swimlane.' => '',
'Your swimlane have been created successfully.' => 'Razdelnik je uspešno kreiran.',
'Example: "Bug, Feature Request, Improvement"' => 'Npr: "Greška, Zahtev za izmenama, Poboljšanje"',
'Default categories for new projects (Comma-separated)' => 'Osnovne kategorije za projekat',
// 'Gitlab commit received' => '',
// 'Gitlab issue opened' => '',
// 'Gitlab issue closed' => '',
// 'Gitlab webhooks' => '',
// 'Help on Gitlab webhooks' => '',
'Integrations' => 'Integracje',
'Integration with third-party services' => 'Integracja sa uslugama spoljnih servisa',
'Role for this project' => 'Uloga u ovom projektu',
'Project manager' => 'Manadžer projekta',
'Project member' => 'Učesnik projekta',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
'Subtask Id' => 'ID pod-zadania',
'Subtasks' => 'Pod-zadataka',
'Subtasks Export' => 'Eksport pod-zadań',
'Subtasks exportation for "%s"' => 'Izvoz pod-zadań dla "%s"',
'Task Title' => 'Naslov zadatka',
'Untitled' => 'Bez naslova',
'Application default' => 'Postavke aplikacje',
'Language:' => 'Jezik:',
'Timezone:' => 'Vremenska zona:',
'All columns' => 'Sve kolone',
'Calendar for "%s"' => 'Kalendar za "%s"',
'Filter by column' => 'Po koloni',
'Filter by status' => 'Po statusu',
'Calendar' => 'Kalendar',
'Next' => 'Sledeći',
// '#%d' => '',
'Filter by color' => 'Po boji',
'Filter by swimlane' => 'Po razdelniku',
'All swimlanes' => 'Svi razdelniki',
'All colors' => 'Sve boje',
'All status' => 'Svi statusi',
'Add a comment logging moving the task between columns' => 'Dodaj logovanje premeštanja zadataka po kolonama',
'Moved to column %s' => 'Premešten u kolonu %s',
// 'Change description' => '',
'User dashboard' => 'Korisnički panel',
// 'Allow only one subtask in progress at the same time for a user' => '',
// 'Edit column "%s"' => '',
// 'Enable time tracking for subtasks' => '',
// 'Select the new status of the subtask: "%s"' => '',
// 'Subtask timesheet' => '',
'There is nothing to show.' => 'Nema podataka',
'Time Tracking' => 'Praćenje vremena',
// 'You already have one subtask in progress' => '',
'Which parts of the project do you want to duplicate?' => 'Koje delove projekta želite da kopirate',
// 'Change dashboard view' => '',
// 'Show/hide activities' => '',
// 'Show/hide projects' => '',
// 'Show/hide subtasks' => '',
// 'Show/hide tasks' => '',
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
// 'Start' => '',
// 'End' => '',
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
'Add a link' => 'Dodaj link',
// 'Add a new link' => '',
// 'Do you really want to remove this link: "%s"?' => '',
// 'Do you really want to remove this link with task #%d?' => '',
// 'Field required' => '',
// 'Link added successfully.' => '',
// 'Link updated successfully.' => '',
// 'Link removed successfully.' => '',
// 'Link labels' => '',
// 'Link modification' => '',
// 'Links' => '',
// 'Link settings' => '',
// 'Opposite label' => '',
// 'Remove a link' => '',
// 'Task\'s links' => '',
// 'The labels must be different' => '',
// 'There is no link.' => '',
// 'This label must be unique' => '',
// 'Unable to create your link.' => '',
// 'Unable to update your link.' => '',
// 'Unable to remove this link.' => '',
// 'relates to' => '',
// 'blocks' => '',
// 'is blocked by' => '',
// 'duplicates' => '',
// 'is duplicated by' => '',
// 'is a child of' => '',
// 'is a parent of' => '',
// 'targets milestone' => '',
// 'is a milestone of' => '',
// 'fixes' => '',
// 'is fixed by' => '',
// 'This task' => '',
// '<1h' => '',
// '%dh' => '',
// '%b %e' => '',
// 'Expand tasks' => '',
// 'Collapse tasks' => '',
// 'Expand/collapse tasks' => '',
// 'Close dialog box' => '',
// 'Submit a form' => '',
// 'Board view' => '',
// 'Keyboard shortcuts' => '',
// 'Open board switcher' => '',
// 'Application' => '',
// 'Filter recently updated' => '',
// 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
);

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Ingen', 'None' => 'Ingen',
'edit' => 'redigera', 'edit' => 'redigera',
'Edit' => 'Redigera', 'Edit' => 'Redigera',
@ -408,13 +410,13 @@ return array(
'Comment updated' => 'Kommentaren har uppdaterats', 'Comment updated' => 'Kommentaren har uppdaterats',
'New comment posted by %s' => 'Ny kommentar postad av %s', 'New comment posted by %s' => 'Ny kommentar postad av %s',
'List of due tasks for the project "%s"' => 'Lista med uppgifter för projektet "%s"', 'List of due tasks for the project "%s"' => 'Lista med uppgifter för projektet "%s"',
// 'New attachment' => '', 'New attachment' => 'Ny bifogning',
// 'New comment' => '', 'New comment' => 'Ny kommentar',
// 'New subtask' => '', 'New subtask' => 'Ny deluppgift',
// 'Subtask updated' => '', 'Subtask updated' => 'Deluppgiften har uppdaterats',
// 'Task updated' => '', 'Task updated' => 'Uppgiften har uppdaterats',
// 'Task closed' => '', 'Task closed' => 'Uppgiften har stängts',
// 'Task opened' => '', 'Task opened' => 'Uppgiften har öppnats',
'[%s][Due tasks]' => '[%s][Förfallen uppgift]', '[%s][Due tasks]' => '[%s][Förfallen uppgift]',
'[Kanboard] Notification' => '[Kanboard] Notis', '[Kanboard] Notification' => '[Kanboard] Notis',
'I want to receive notifications only for those projects:' => 'Jag vill endast få notiser för dessa projekt:', 'I want to receive notifications only for those projects:' => 'Jag vill endast få notiser för dessa projekt:',
@ -498,9 +500,9 @@ return array(
'Task assignee change' => 'Ändra tilldelning av uppgiften', 'Task assignee change' => 'Ändra tilldelning av uppgiften',
'%s change the assignee of the task #%d to %s' => '%s byt tilldelning av uppgiften #%d till %s', '%s change the assignee of the task #%d to %s' => '%s byt tilldelning av uppgiften #%d till %s',
'%s changed the assignee of the task %s to %s' => '%s byt tilldelning av uppgiften %s till %s', '%s changed the assignee of the task %s to %s' => '%s byt tilldelning av uppgiften %s till %s',
// 'Column Change' => '', 'Column Change' => 'Ändring av kolumn',
// 'Position Change' => '', 'Position Change' => 'Ändring av position',
// 'Assignee Change' => '', 'Assignee Change' => 'Ändring av tilldelning',
'New password for the user "%s"' => 'Nytt lösenord för användaren "%s"', 'New password for the user "%s"' => 'Nytt lösenord för användaren "%s"',
'Choose an event' => 'Välj en händelse', 'Choose an event' => 'Välj en händelse',
'Github commit received' => 'Github-bidrag mottaget', 'Github commit received' => 'Github-bidrag mottaget',
@ -645,93 +647,203 @@ return array(
'Application default' => 'Applikationsstandard', 'Application default' => 'Applikationsstandard',
'Language:' => 'Språk', 'Language:' => 'Språk',
'Timezone:' => 'Tidszon', 'Timezone:' => 'Tidszon',
// 'All columns' => '', 'All columns' => 'Alla kolumner',
// 'Calendar for "%s"' => '', 'Calendar for "%s"' => 'Kalender för "%s"',
// 'Filter by column' => '', 'Filter by column' => 'Filtrera på kolumn',
// 'Filter by status' => '', 'Filter by status' => 'Filtrera på status',
// 'Calendar' => '', 'Calendar' => 'Kalender',
'Next' => 'Nästa', 'Next' => 'Nästa',
// '#%d' => '', '#%d' => '#%d',
// 'Filter by color' => '', 'Filter by color' => 'Filtrera på färg',
// 'Filter by swimlane' => '', 'Filter by swimlane' => 'Filtrera på swimlane',
// 'All swimlanes' => '', 'All swimlanes' => 'Alla swimlanes',
// 'All colors' => '', 'All colors' => 'Alla färger',
// 'All status' => '', 'All status' => 'Alla status',
// 'Add a comment logging moving the task between columns' => '', 'Add a comment logging moving the task between columns' => 'Lägg till en kommentar för att logga förflyttning av en uppgift mellan kolumner',
// 'Moved to column %s' => '', 'Moved to column %s' => 'Flyttad till kolumn %s',
// 'Change description' => '', 'Change description' => 'Ändra beskrivning',
// 'User dashboard' => '', 'User dashboard' => 'Användardashboard',
// 'Allow only one subtask in progress at the same time for a user' => '', 'Allow only one subtask in progress at the same time for a user' => 'Tillåt endast en deluppgift igång samtidigt för en användare',
// 'Edit column "%s"' => '', 'Edit column "%s"' => 'Ändra kolumn "%s"',
// 'Enable time tracking for subtasks' => '', 'Enable time tracking for subtasks' => 'Aktivera tidsbevakning för deluppgifter',
// 'Select the new status of the subtask: "%s"' => '', 'Select the new status of the subtask: "%s"' => 'Välj ny status för deluppgiften: "%s"',
// 'Subtask timesheet' => '', 'Subtask timesheet' => 'Tidrapport för deluppgiften',
// 'There is nothing to show.' => '', 'There is nothing to show.' => 'Det finns inget att visa',
// 'Time Tracking' => '', 'Time Tracking' => 'Tidsbevakning',
// 'You already have one subtask in progress' => '', 'You already have one subtask in progress' => 'Du har redan en deluppgift igång',
// 'Which parts of the project do you want to duplicate?' => '', 'Which parts of the project do you want to duplicate?' => 'Vilka delar av projektet vill du duplicera?',
// 'Change dashboard view' => '', 'Change dashboard view' => 'Ändra dashboard vy',
// 'Show/hide activities' => '', 'Show/hide activities' => 'Visa/dölj aktiviteter',
// 'Show/hide projects' => '', 'Show/hide projects' => 'Visa/dölj projekt',
// 'Show/hide subtasks' => '', 'Show/hide subtasks' => 'Visa/dölj deluppgifter',
// 'Show/hide tasks' => '', 'Show/hide tasks' => 'Visa/dölj uppgifter',
// 'Disable login form' => '', 'Disable login form' => 'Inaktivera loginformuläret',
// 'Show/hide calendar' => '', 'Show/hide calendar' => 'Visa/dölj kalender',
// 'User calendar' => '', 'User calendar' => 'Användarkalender',
// 'Bitbucket commit received' => '', 'Bitbucket commit received' => 'Bitbucket bidrag mottaget',
// 'Bitbucket webhooks' => '', 'Bitbucket webhooks' => 'Bitbucket webhooks',
// 'Help on Bitbucket webhooks' => '', 'Help on Bitbucket webhooks' => 'Hjälp för Bitbucket webhooks',
// 'Start' => '', 'Start' => 'Start',
// 'End' => '', 'End' => 'Slut',
// 'Task age in days' => '', 'Task age in days' => 'Uppgiftsålder i dagar',
// 'Days in this column' => '', 'Days in this column' => 'Dagar i denna kolumn',
// '%dd' => '', '%dd' => '%dd',
// 'Add a link' => '', 'Add a link' => 'Lägg till länk',
// 'Add a new link' => '', 'Add a new link' => 'Lägg till ny länk',
// 'Do you really want to remove this link: "%s"?' => '', 'Do you really want to remove this link: "%s"?' => 'Vill du verkligen ta bort länken: "%s"?',
// 'Do you really want to remove this link with task #%d?' => '', 'Do you really want to remove this link with task #%d?' => 'Vill du verkligen ta bort länken till uppgiften #%d?',
// 'Field required' => '', 'Field required' => 'Fältet krävs',
// 'Link added successfully.' => '', 'Link added successfully.' => 'Länken har lagts till',
// 'Link updated successfully.' => '', 'Link updated successfully.' => 'Länken har uppdaterats',
// 'Link removed successfully.' => '', 'Link removed successfully.' => 'Länken har tagits bort',
// 'Link labels' => '', 'Link labels' => 'Länketiketter',
// 'Link modification' => '', 'Link modification' => 'Länkändring',
// 'Links' => '', 'Links' => 'Länkar',
// 'Link settings' => '', 'Link settings' => 'Länkinställningar',
// 'Opposite label' => '', 'Opposite label' => 'Motpartslänk',
// 'Remove a link' => '', 'Remove a link' => 'Ta bort en länk',
// 'Task\'s links' => '', 'Task\'s links' => 'Uppgiftslänkar',
// 'The labels must be different' => '', 'The labels must be different' => 'Etiketterna måste vara olika',
// 'There is no link.' => '', 'There is no link.' => 'Det finns ingen länk',
// 'This label must be unique' => '', 'This label must be unique' => 'Länken måste vara unik',
// 'Unable to create your link.' => '', 'Unable to create your link.' => 'Kunde inte skapa din länk',
// 'Unable to update your link.' => '', 'Unable to update your link.' => 'Kunde inte uppdatera din länk',
// 'Unable to remove this link.' => '', 'Unable to remove this link.' => 'Kunde inte ta bort din länk',
// 'relates to' => '', 'relates to' => 'relaterar till',
// 'blocks' => '', 'blocks' => 'blockerar',
// 'is blocked by' => '', 'is blocked by' => 'blockeras av',
// 'duplicates' => '', 'duplicates' => 'dupplicerar',
// 'is duplicated by' => '', 'is duplicated by' => 'är duplicerad av',
// 'is a child of' => '', 'is a child of' => 'är underliggande till',
// 'is a parent of' => '', 'is a parent of' => 'är överliggande till',
// 'targets milestone' => '', 'targets milestone' => 'milstolpemål',
// 'is a milestone of' => '', 'is a milestone of' => 'är en milstolpe för',
// 'fixes' => '', 'fixes' => 'åtgärdar',
// 'is fixed by' => '', 'is fixed by' => 'åtgärdas av',
// 'This task' => '', 'This task' => 'Denna uppgift',
// '<1h' => '', '<1h' => '<1h',
// '%dh' => '', '%dh' => '%dh',
// '%b %e' => '', '%b %e' => '%b %e',
// 'Expand tasks' => '', 'Expand tasks' => 'Expandera uppgifter',
// 'Collapse tasks' => '', 'Collapse tasks' => 'Minimera uppgifter',
// 'Expand/collapse tasks' => '', 'Expand/collapse tasks' => 'Expandera/minimera uppgifter',
// 'Close dialog box' => '', 'Close dialog box' => 'Stäng dialogruta',
// 'Submit a form' => '', 'Submit a form' => 'Sänd formulär',
// 'Board view' => '', 'Board view' => 'Tavelvy',
// 'Keyboard shortcuts' => '', 'Keyboard shortcuts' => 'Tangentbordsgenvägar',
// 'Open board switcher' => '', 'Open board switcher' => 'Växling av öppen tavla',
// 'Application' => '', 'Application' => 'Applikation',
// 'Filter recently updated' => '', 'Filter recently updated' => 'Filter som uppdaterats nyligen',
// 'since %B %e, %Y at %k:%M %p' => '', 'since %B %e, %Y at %k:%M %p' => 'sedan %B %e, %Y at %k:%M %p',
// 'More filters' => '', 'More filters' => 'Fler filter',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'ไม่มี', 'None' => 'ไม่มี',
'edit' => 'แก้ไข', 'edit' => 'แก้ไข',
'Edit' => 'แก้ไข', 'Edit' => 'แก้ไข',
@ -734,4 +736,114 @@ return array(
// 'Filter recently updated' => '', // 'Filter recently updated' => '',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', // 'More filters' => '',
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
); );

View file

@ -0,0 +1,849 @@
<?php
return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'None' => 'Hiçbiri',
'edit' => 'düzenle',
'Edit' => 'Düzenle',
'remove' => 'sil',
'Remove' => 'Sil',
'Update' => 'Güncelle',
'Yes' => 'Evet',
'No' => 'Hayır',
'cancel' => 'İptal',
'or' => 'veya',
'Yellow' => 'Sarı',
'Blue' => 'Mavi',
'Green' => 'Yeşil',
'Purple' => 'Mor',
'Red' => 'Kırmızı',
'Orange' => 'Turuncu',
'Grey' => 'Gri',
'Save' => 'Kaydet',
'Login' => 'Giriş',
'Official website:' => 'Resmi internet sitesi:',
'Unassigned' => 'Atanmamış',
'View this task' => 'Bu görevi görüntüle',
'Remove user' => 'Kullanıcıyı kaldır',
'Do you really want to remove this user: "%s"?' => 'Bu kullanıcıyı gerçekten silmek istiyor musunuz: "%s"?',
'New user' => 'Yeni kullanıcı',
'All users' => 'Tüm kullanıcılar',
'Username' => 'Kullanıcı adı',
'Password' => 'Şifre',
// 'Default project' => '',
'Administrator' => 'Yönetici',
'Sign in' => 'Giriş yap',
'Users' => 'Kullanıcılar',
'No user' => 'Kullanıcı yok',
'Forbidden' => 'Yasak',
'Access Forbidden' => 'Erişim yasak',
'Only administrators can access to this page.' => 'Bu sayfaya yalnızca yöneticiler erişebilir.',
'Edit user' => 'Kullanıcıyı düzenle',
'Logout' => ıkış yap',
'Bad username or password' => 'Hatalı kullanıcı adı veya şifre',
'users' => 'kullanıcılar',
'projects' => 'projeler',
'Edit project' => 'Projeyi düzenle',
'Name' => 'İsim',
'Activated' => 'Aktif',
'Projects' => 'Projeler',
'No project' => 'Proje yok',
'Project' => 'Proje',
'Status' => 'Durum',
'Tasks' => 'Görevler',
'Board' => 'Tablo',
'Actions' => 'İşlemler',
'Inactive' => 'Aktif değil',
'Active' => 'Aktif',
'Column %d' => 'Sütun %d',
'Add this column' => 'Bu sütunu ekle',
'%d tasks on the board' => '%d görev bu tabloda',
'%d tasks in total' => '%d görev toplam',
'Unable to update this board.' => 'Bu tablo güncellenemiyor.',
'Edit board' => 'Tabloyu düzenle',
'Disable' => 'Devre dışı bırak',
'Enable' => 'Etkinleştir',
'New project' => 'Yeni proje',
'Do you really want to remove this project: "%s"?' => 'Bu projeyi gerçekten silmek istiyor musunuz: "%s"?',
'Remove project' => 'Projeyi sil',
'Boards' => 'Tablolar',
'Edit the board for "%s"' => 'Tabloyu "%s" için güncelle',
'All projects' => 'Tüm projeler',
'Change columns' => 'Sütunları değiştir',
'Add a new column' => 'Yeni sütun ekle',
'Title' => 'Başlık',
'Add Column' => 'Sütun ekle',
'Project "%s"' => 'Proje "%s"',
'Nobody assigned' => 'Kullanıcı atanmamış',
'Assigned to %s' => '%s kullanıcısına atanmış',
'Remove a column' => 'Bir sütunu sil',
'Remove a column from a board' => 'Tablodan bir sütunu sil',
'Unable to remove this column.' => 'Bu sütun silinemiyor.',
'Do you really want to remove this column: "%s"?' => 'Bu sütunu gerçekten silmek istiyor musunuz: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Bu komut sütun içindeki TÜM GÖREVLERİ silecek!',
'Settings' => 'Ayarlar',
'Application settings' => 'Uygulama ayarları',
'Language' => 'Dil',
// 'Webhook token:' => '',
'API token:' => 'API Token:',
'More information' => 'Daha fazla bilgi',
'Database size:' => 'Veritabanı boyutu :',
'Download the database' => 'Veritabanını indir',
'Optimize the database' => 'Veritabanını optimize et',
'(VACUUM command)' => '(VACUUM komutu)',
'(Gzip compressed Sqlite file)' => '(Gzip ile sıkıştırılmış Sqlite dosyası)',
'User settings' => 'Kullanıcı ayarları',
'My default project:' => 'Benim varsayılan projem:',
'Close a task' => 'Bir görevi kapat',
'Do you really want to close this task: "%s"?' => 'Bu görevi gerçekten kapatmak istiyor musunuz: "%s"?',
'Edit a task' => 'Bir görevi düzenle',
'Column' => 'Sütun',
'Color' => 'Renk',
'Assignee' => 'Atanan',
'Create another task' => 'Başka bir görev oluştur',
'New task' => 'Nouvelle tâche',
'Open a task' => 'Bir görevi aç',
'Do you really want to open this task: "%s"?' => 'Bu görevi gerçekten açmak istiyor musunuz: "%s"?',
'Back to the board' => 'Tabloya dön',
// 'Created on %B %e, %Y at %k:%M %p' => '',
'There is nobody assigned' => 'Kimse atanmamış',
'Column on the board:' => 'Tablodaki sütun:',
'Status is open' => 'Açık durumda',
'Status is closed' => 'Kapalı durumda',
'Close this task' => 'Görevi kapat',
'Open this task' => 'Görevi aç',
'There is no description.' => 'Açıklama yok.',
'Add a new task' => 'Yeni görev ekle',
'The username is required' => 'Kullanıcı adı gerekli',
'The maximum length is %d characters' => 'Maksimum uzunluk %d karakterdir',
'The minimum length is %d characters' => 'Minimum uzunluk %d karakterdir',
'The password is required' => 'Şifre gerekli',
'This value must be an integer' => 'Bu değer bir rakam olmak zorunda',
'The username must be unique' => 'Kullanıcı adı daha önceden var',
'The username must be alphanumeric' => 'Kullanıcı adı alfanumerik olmalı (geçersiz karakter var)',
'The user id is required' => 'Kullanıcı kodu gerekli',
'Passwords don\'t match' => 'Şifreler uyuşmuyor',
'The confirmation is required' => 'Onay gerekli',
'The column is required' => 'Sütun gerekli',
'The project is required' => 'Proje gerekli',
'The color is required' => 'Renk gerekli',
'The id is required' => 'Kod gerekli',
'The project id is required' => 'Proje kodu gerekli',
'The project name is required' => 'Proje adı gerekli',
'This project must be unique' => 'Bu projenin tekil olması gerekli',
'The title is required' => 'Başlık gerekli',
'The language is required' => 'Dil seçimi gerekli',
'There is no active project, the first step is to create a new project.' => 'Aktif bir proje yok. İlk aşama yeni bir proje oluşturmak olmalı.',
'Settings saved successfully.' => 'Ayarlar başarıyla kaydedildi.',
'Unable to save your settings.' => 'Ayarlarınız kaydedilemedi.',
'Database optimization done.' => 'Veritabanı optimizasyonu tamamlandı.',
'Your project have been created successfully.' => 'Projeniz başarıyla oluşturuldu.',
'Unable to create your project.' => 'Proje oluşturulamadı.',
'Project updated successfully.' => 'Proje başarıyla güncellendi.',
'Unable to update this project.' => 'Bu proje güncellenemedi.',
'Unable to remove this project.' => 'Bu proje silinemedi.',
'Project removed successfully.' => 'Proje başarıyla silindi.',
'Project activated successfully.' => 'Proje başarıyla aktive edildi.',
'Unable to activate this project.' => 'Bu proje aktive edilemedi.',
'Project disabled successfully.' => 'Proje devre dışı bırakıldı.',
'Unable to disable this project.' => 'Bu proje devre dışı bırakılamadı.',
'Unable to open this task.' => 'Bu görev açılamıyor.',
'Task opened successfully.' => 'Görev başarıyla açıldı.',
'Unable to close this task.' => 'Bu görev kapatılamıyor.',
'Task closed successfully.' => 'Görev başarıyla kapatıldı.',
'Unable to update your task.' => 'Görev güncellenemiyor.',
'Task updated successfully.' => 'Görev başarıyla güncellendi.',
'Unable to create your task.' => 'Görev oluşturulamadı.',
'Task created successfully.' => 'Görev başarıyla oluşturuldu.',
'User created successfully.' => 'Kullanıcı başarıyla oluşturuldu',
'Unable to create your user.' => 'Kullanıcı oluşturulamıyor.',
'User updated successfully.' => 'Kullanıcı başarıyla güncellendi.',
'Unable to update your user.' => 'Kullanıcı güncellenemiyor.',
'User removed successfully.' => 'Kullanıcı silindi.',
'Unable to remove this user.' => 'Bu kullanıcı silinemiyor.',
'Board updated successfully.' => 'Tablo başarıyla güncellendi.',
'Ready' => 'Hazır',
'Backlog' => 'Bekleme listesi',
'Work in progress' => 'İşlemde',
'Done' => 'Tamamlandı',
'Application version:' => 'Uygulama versiyonu:',
// 'Completed on %B %e, %Y at %k:%M %p' => '',
// '%B %e, %Y at %k:%M %p' => '',
'Date created' => 'Oluşturulma tarihi',
'Date completed' => 'Tamamlanma tarihi',
'Id' => 'Kod',
'No task' => 'Görev yok',
'Completed tasks' => 'Tamamlanan görevler',
'List of projects' => 'Proje listesi',
'Completed tasks for "%s"' => '"%s" için tamamlanan görevler',
'%d closed tasks' => '%d kapatılmış görevler',
// 'No task for this project' => '',
'Public link' => 'Dışa açık link',
'There is no column in your project!' => 'Projenizde hiç sütun yok',
'Change assignee' => 'Atanmış Kullanıcıyı değiştir',
'Change assignee for the task "%s"' => '"%s" görevi için atanmış kullanıcıyı değiştir',
'Timezone' => 'Saat dilimi',
// 'Sorry, I didn\'t find this information in my database!' => '',
'Page not found' => 'Sayfa bulunamadı',
'Complexity' => 'Zorluk seviyesi',
'limit' => 'limit',
'Task limit' => 'Görev limiti',
'Task count' => 'Görev sayısı',
'This value must be greater than %d' => 'Bu değer %d den büyük olmalı',
'Edit project access list' => 'Proje erişim listesini düzenle',
'Edit users access' => 'Kullanıcı erişim haklarını düzenle',
'Allow this user' => 'Bu kullanıcıya izin ver',
'Only those users have access to this project:' => 'Bu projeye yalnızca şu kullanıcılar erişebilir:',
'Don\'t forget that administrators have access to everything.' => 'Dikkat: Yöneticilerin herşeye erişimi olduğunu unutmayın!',
'Revoke' => 'Iptal et',
'List of authorized users' => 'Yetkili kullanıcıların listesi',
'User' => 'Kullanıcı',
'Nobody have access to this project.' => 'Bu projeye kimsenin erişimi yok.',
'You are not allowed to access to this project.' => 'Bu projeye giriş yetkiniz yok.',
'Comments' => 'Yorumlar',
'Post comment' => 'Yorum ekle',
'Write your text in Markdown' => 'Yazınızı Markdown ile yazın',
'Leave a comment' => 'Bir yorum ekle',
'Comment is required' => 'Yorum gerekli',
'Leave a description' => 'Açıklama ekleyin',
'Comment added successfully.' => 'Yorum eklendi',
'Unable to create your comment.' => 'Yorumunuz oluşturulamadı',
'The description is required' => 'Açıklama gerekli',
'Edit this task' => 'Bu görevi değiştir',
'Due Date' => 'Termin',
'Invalid date' => 'Geçersiz tarihi',
// 'Must be done before %B %e, %Y' => '',
'%B %e, %Y' => '%d %B %Y',
'%b %e, %Y' => '%d/%m/%Y',
'Automatic actions' => 'Otomatik işlemler',
'Your automatic action have been created successfully.' => 'Otomatik işlem başarıyla oluşturuldu',
'Unable to create your automatic action.' => 'Otomatik işleminiz oluşturulamadı',
'Remove an action' => 'Bir işlemi sil',
'Unable to remove this action.' => 'Bu işlem silinemedi',
'Action removed successfully.' => 'İşlem başarıyla silindi',
'Automatic actions for the project "%s"' => '"%s" projesi için otomatik işlemler',
'Defined actions' => 'Tanımlanan işlemler',
'Add an action' => 'İşlem ekle',
'Event name' => 'Durum adı',
'Action name' => 'İşlem adı',
'Action parameters' => 'İşlem parametreleri',
'Action' => 'İşlem',
'Event' => 'Durum',
'When the selected event occurs execute the corresponding action.' => 'Seçilen durum oluştuğunda ilgili eylemi gerçekleştir.',
'Next step' => 'Sonraki adım',
'Define action parameters' => 'İşlem parametrelerini düzenle',
'Save this action' => 'Bu işlemi kaydet',
'Do you really want to remove this action: "%s"?' => 'Bu işlemi silmek istediğinize emin misiniz: "%s"?',
'Remove an automatic action' => 'Bir otomatik işlemi sil',
'Close the task' => 'Görevi kapat',
'Assign the task to a specific user' => 'Görevi bir kullanıcıya ata',
'Assign the task to the person who does the action' => 'Görevi, işlemi gerçekleştiren kullanıcıya ata',
'Duplicate the task to another project' => 'Görevi bir başka projeye kopyala',
'Move a task to another column' => 'Bir görevi başka bir sütuna taşı',
'Move a task to another position in the same column' => 'Bir görevin aynı sütunda yerini değiştir',
'Task modification' => 'Görev düzenleme',
'Task creation' => 'Görev oluşturma',
'Open a closed task' => 'Kapalı bir görevi aç',
'Closing a task' => 'Bir görev kapatılıyor',
'Assign a color to a specific user' => 'Bir kullanıcıya renk tanımla',
'Column title' => 'Sütun başlığı',
'Position' => 'Pozisyon',
'Move Up' => 'Yukarı taşı',
'Move Down' => 'Aşağı taşı',
'Duplicate to another project' => 'Başka bir projeye kopyala',
'Duplicate' => 'Kopya oluştur',
'link' => 'link',
'Update this comment' => 'Bu yorumu güncelle',
'Comment updated successfully.' => 'Yorum güncellendi.',
'Unable to update your comment.' => 'Yorum güncellenemedi.',
'Remove a comment' => 'Bir yorumu sil',
'Comment removed successfully.' => 'Yorum silindi.',
'Unable to remove this comment.' => 'Bu yorum silinemiyor.',
'Do you really want to remove this comment?' => 'Bu yorumu silmek istediğinize emin misiniz?',
'Only administrators or the creator of the comment can access to this page.' => 'Bu sayfaya yalnızca yorum sahibi ve yöneticiler erişebilir.',
'Details' => 'Detaylar',
'Current password for the user "%s"' => 'Kullanıcı için mevcut şifre "%s"',
'The current password is required' => 'Mevcut şifre gerekli',
'Wrong password' => 'Yanlış Şifre',
'Reset all tokens' => 'Tüm fişleri sıfırla',
'All tokens have been regenerated.' => 'Tüm fişler yeniden oluşturuldu.',
'Unknown' => 'Bilinmeyen',
'Last logins' => 'Son kullanıcı girişleri',
'Login date' => 'Giriş tarihi',
'Authentication method' => 'Doğrulama yöntemi',
'IP address' => 'IP adresi',
'User agent' => 'Kullanıcı sistemi',
'Persistent connections' => 'Kalıcı bağlantılar',
// 'No session.' => '',
'Expiration date' => 'Geçerlilik sonu',
'Remember Me' => 'Beni hatırla',
'Creation date' => 'Oluşturulma tarihi',
'Filter by user' => 'Kullanıcıya göre filtrele',
'Filter by due date' => 'Termine göre filtrele',
'Everybody' => 'Herkes',
'Open' => 'Açık',
'Closed' => 'Kapalı',
'Search' => 'Ara',
'Nothing found.' => 'Hiçbir şey bulunamadı',
'Search in the project "%s"' => '"%s" Projesinde ara',
'Due date' => 'Termin',
'Others formats accepted: %s and %s' => 'Diğer kabul edilen formatlar: %s ve %s',
'Description' => 'Açıklama',
'%d comments' => '%d yorumlar',
'%d comment' => '%d yorum',
'Email address invalid' => 'E-Posta adresi geçersiz',
'Your Google Account is not linked anymore to your profile.' => 'Google hesabınız artık profilinize bağlı değil',
'Unable to unlink your Google Account.' => 'Google hesabınızla bağ koparılamadı',
'Google authentication failed' => 'Google hesap doğrulaması başarısız',
'Unable to link your Google Account.' => 'Google hesabınızla bağ oluşturulamadı',
'Your Google Account is linked to your profile successfully.' => 'Google hesabınız profilinize başarıyla bağlandı',
'Email' => 'E-Posta',
'Link my Google Account' => 'Google hesabımla bağ oluştur',
'Unlink my Google Account' => 'Google hesabımla bağı kaldır',
'Login with my Google Account' => 'Google hesabımla giriş yap',
'Project not found.' => 'Proje bulunamadı',
'Task #%d' => 'Görev #%d',
'Task removed successfully.' => 'Görev silindi',
'Unable to remove this task.' => 'Görev silinemiyor',
'Remove a task' => 'Bir görevi sil',
'Do you really want to remove this task: "%s"?' => 'Bu görevi silmek istediğinize emin misiniz: "%s"?',
'Assign automatically a color based on a category' => 'Kategoriye göre otomatik renk ata',
'Assign automatically a category based on a color' => 'Rengine göre otomatik kategori ata',
'Task creation or modification' => 'Görev oluşturma veya değiştirme',
'Category' => 'Kategori',
'Category:' => 'Kategori:',
'Categories' => 'Kategoriler',
'Category not found.' => 'Kategori bulunamadı',
'Your category have been created successfully.' => 'Kategori oluşturuldu',
'Unable to create your category.' => 'Kategori oluşturulamadı',
'Your category have been updated successfully.' => 'Kategori başarıyla güncellendi',
'Unable to update your category.' => 'Kategori güncellenemedi',
'Remove a category' => 'Bir kategoriyi sil',
'Category removed successfully.' => 'Kategori silindi',
'Unable to remove this category.' => 'Bu kategori silinemedi',
'Category modification for the project "%s"' => '"%s" projesi için kategori değiştirme',
'Category Name' => 'Kategori adı',
'Categories for the project "%s"' => '"%s" Projesi için kategoriler',
'Add a new category' => 'Yeni kategori ekle',
'Do you really want to remove this category: "%s"?' => 'Bu kategoriyi silmek istediğinize emin misiniz: "%s"?',
'Filter by category' => 'Kategoriye göre filtrele',
'All categories' => 'Tüm kategoriler',
'No category' => 'Kategori Yok',
'The name is required' => 'İsim gerekli',
'Remove a file' => 'Dosya sil',
'Unable to remove this file.' => 'Dosya silinemedi',
'File removed successfully.' => 'Dosya silindi',
'Attach a document' => 'Dosya ekle',
'Do you really want to remove this file: "%s"?' => 'Bu dosyayı silmek istediğinize emin misiniz: "%s"?',
'open' => 'aç',
'Attachments' => 'Ekler',
'Edit the task' => 'Görevi değiştir',
'Edit the description' => 'Açıklamayı değiştir',
'Add a comment' => 'Yorum ekle',
'Edit a comment' => 'Yorum değiştir',
'Summary' => 'Özet',
'Time tracking' => 'Zaman takibi',
'Estimate:' => 'Tahmini:',
'Spent:' => 'Harcanan:',
'Do you really want to remove this sub-task?' => 'Bu alt görevi silmek istediğinize emin misiniz',
'Remaining:' => 'Kalan',
'hours' => 'saat',
'spent' => 'harcanan',
'estimated' => 'tahmini',
'Sub-Tasks' => 'Alt Görev',
'Add a sub-task' => 'Alt görev ekle',
// 'Original estimate' => '',
'Create another sub-task' => 'Başka bir alt görev daha oluştur',
// 'Time spent' => '',
'Edit a sub-task' => 'Alt görev düzenle',
'Remove a sub-task' => 'Alt görev sil',
'The time must be a numeric value' => 'Zaman alfanumerik bir değer olmalı',
'Todo' => 'Yapılacaklar',
'In progress' => 'İşlemde',
'Sub-task removed successfully.' => 'Alt görev silindi',
'Unable to remove this sub-task.' => 'Alt görev silinemedi',
'Sub-task updated successfully.' => 'Alt görev güncellendi',
'Unable to update your sub-task.' => 'Alt görev güncellenemiyor',
'Unable to create your sub-task.' => 'Alt görev oluşturulamadı',
'Sub-task added successfully.' => 'Alt görev başarıyla eklendii',
'Maximum size: ' => 'Maksimum boyutu',
'Unable to upload the file.' => 'Karşıya yükleme başarısız',
'Display another project' => 'Başka bir proje göster',
'Your GitHub account was successfully linked to your profile.' => 'GitHub Hesabınız Profilinize bağlandı.',
'Unable to link your GitHub Account.' => 'GitHub hesabınızla bağ oluşturulamadı.',
// 'GitHub authentication failed' => '',
// 'Your GitHub account is no longer linked to your profile.' => '',
// 'Unable to unlink your GitHub Account.' => '',
// 'Login with my GitHub Account' => '',
// 'Link my GitHub Account' => '',
// 'Unlink my GitHub Account' => '',
'Created by %s' => '%s tarafından oluşturuldu',
'Last modified on %B %e, %Y at %k:%M %p' => 'Son değişiklik tarihi %d.%m.%Y, saati %H:%M',
'Tasks Export' => 'Görevleri dışa aktar',
'Tasks exportation for "%s"' => '"%s" için görevleri dışa aktar',
'Start Date' => 'Başlangıç tarihi',
'End Date' => 'Bitiş tarihi',
'Execute' => 'Gerçekleştir',
'Task Id' => 'Görev No',
'Creator' => 'Oluşturan',
'Modification date' => 'Değişiklik tarihi',
'Completion date' => 'Tamamlanma tarihi',
// 'Webhook URL for task creation' => '',
// 'Webhook URL for task modification' => '',
'Clone' => 'Kopya oluştur',
'Clone Project' => 'Projenin kopyasını oluştur',
'Project cloned successfully.' => 'Proje kopyası başarıyla oluşturuldu.',
'Unable to clone this project.' => 'Proje kopyası oluşturulamadı.',
'Email notifications' => 'E-Posta bilgilendirmesi',
'Enable email notifications' => 'E-Posta bilgilendirmesini aç',
'Task position:' => 'Görev pozisyonu',
'The task #%d have been opened.' => '#%d numaralı görev açıldı.',
'The task #%d have been closed.' => '#%d numaralı görev kapatıldı.',
'Sub-task updated' => 'Alt görev güncellendi',
'Title:' => 'Başlık',
'Status:' => 'Durum',
'Assignee:' => 'Sorumlu:',
'Time tracking:' => 'Zaman takibi',
'New sub-task' => 'Yeni alt görev',
'New attachment added "%s"' => 'Yeni dosya "%s" eklendi.',
'Comment updated' => 'Yorum güncellendi',
'New comment posted by %s' => '%s tarafından yeni yorum eklendi',
'List of due tasks for the project "%s"' => '"%s" projesi için ilgili görevlerin listesi',
'New attachment' => 'Yeni dosya eki',
'New comment' => 'Yeni yorum',
'New subtask' => 'Yeni alt görev',
'Subtask updated' => 'Alt görev güncellendi',
'Task updated' => 'Görev güncellendi',
'Task closed' => 'Görev kapatıldı',
'Task opened' => 'Görev açıldı',
'[%s][Due tasks]' => '[%s][İlgili görevler]',
'[Kanboard] Notification' => '[Kanboard] Bildirim',
'I want to receive notifications only for those projects:' => 'Yalnızca bu projelerle ilgili bildirim almak istiyorum:',
'view the task on Kanboard' => 'bu görevi Kanboard\'da göster',
'Public access' => 'Dışa açık erişim',
'Category management' => 'Kategori yönetimi',
'User management' => 'Kullanıcı yönetimi',
'Active tasks' => 'Aktif görevler',
'Disable public access' => 'Dışa açık erişimi kapat',
'Enable public access' => 'Dışa açık erişimi aç',
'Active projects' => 'Aktif projeler',
'Inactive projects' => 'Aktif olmayan projeler',
'Public access disabled' => 'Dışa açık erişim kapatıldı',
'Do you really want to disable this project: "%s"?' => 'Bu projeyi devre dışı bırakmak istediğinize emin misiniz?: "%s"',
'Do you really want to duplicate this project: "%s"?' => 'Bu projenin kopyasını oluşturmak istediğinize emin misiniz?: "%s"',
'Do you really want to enable this project: "%s"?' => 'Bu projeyi aktive etmek istediğinize emin misiniz?: "%s"',
'Project activation' => 'Proje aktivasyonu',
'Move the task to another project' => 'Görevi başka projeye taşı',
'Move to another project' => 'Başka projeye taşı',
'Do you really want to duplicate this task?' => 'Bu görevin kopyasını oluşturmak istediğinize emin misiniz?',
'Duplicate a task' => 'Görevin kopyasını oluştur',
'External accounts' => 'Dış hesaplar',
'Account type' => 'Hesap türü',
'Local' => 'Yerel',
'Remote' => 'Uzak',
'Enabled' => 'Etkinleştirildi',
'Disabled' => 'Devre dışı bırakıldı',
'Google account linked' => 'Google hesabıyla bağlı',
'Github account linked' => 'Github hesabıyla bağlı',
'Username:' => 'Kullanıcı adı',
'Name:' => 'Ad',
'Email:' => 'E-Posta',
'Default project:' => 'Varsayılan Proje:',
'Notifications:' => 'Bildirimler:',
'Notifications' => 'Bildirimler',
'Group:' => 'Grup',
'Regular user' => 'Varsayılan kullanıcı',
'Account type:' => 'Hesap türü:',
'Edit profile' => 'Profili değiştir',
'Change password' => 'Şifre değiştir',
'Password modification' => 'Şifre değişimi',
'External authentications' => 'Dış kimlik doğrulamaları',
'Google Account' => 'Google hesabı',
'Github Account' => 'Github hesabı',
'Never connected.' => 'Hiç bağlanmamış.',
'No account linked.' => 'Bağlanmış hesap yok.',
'Account linked.' => 'Hesap bağlandı',
'No external authentication enabled.' => 'Dış kimlik doğrulama kapalı.',
'Password modified successfully.' => 'Şifre başarıyla değiştirildi.',
'Unable to change the password.' => 'Şifre değiştirilemedi.',
'Change category for the task "%s"' => '"%s" görevi için kategori değiştirme',
'Change category' => 'Kategori değiştirme',
'%s updated the task %s' => '%s kullanıcısı %s görevini güncelledi',
'%s opened the task %s' => '%s kullanıcısı %s görevini açtı',
'%s moved the task %s to the position #%d in the column "%s"' => '%s kullanıcısı %s görevini #%d pozisyonu "%s" sütununa taşıdı',
'%s moved the task %s to the column "%s"' => '%s kullanıcısı %s görevini "%s" sütununa taşıdı',
'%s created the task %s' => '%s kullanıcısı %s görevini oluşturdu',
'%s closed the task %s' => '%s kullanıcısı %s görevini kapattı',
'%s created a subtask for the task %s' => '%s kullanıcısı %s görevi için bir alt görev oluşturdu',
'%s updated a subtask for the task %s' => '%s kullanıcısı %s görevinin bir alt görevini güncelledi',
'Assigned to %s with an estimate of %s/%sh' => '%s kullanıcısına tahmini %s/%s saat tamamlanma süresi ile atanmış',
'Not assigned, estimate of %sh' => 'Kimseye atanmamış, tahmini süre %s saat',
'%s updated a comment on the task %s' => '%s kullanıcısı %s görevinde bir yorumu güncelledi',
'%s commented the task %s' => '%s kullanıcısı %s görevine yorum ekledi',
'%s\'s activity' => '%s\'in aktivitesi',
'No activity.' => 'Aktivite yok.',
'RSS feed' => 'RSS kaynağı',
'%s updated a comment on the task #%d' => '%s kullanıcısı #%d nolu görevde bir yorumu güncelledi',
'%s commented on the task #%d' => '%s kullanıcısı #%d nolu göreve yorum ekledi',
'%s updated a subtask for the task #%d' => '%s kullanıcısı #%d nolu görevin bir alt görevini güncelledi',
'%s created a subtask for the task #%d' => '%s kullanıcısı #%d nolu göreve bir alt görev ekledi',
'%s updated the task #%d' => '%s kullanıcısı #%d nolu görevi güncelledi',
'%s created the task #%d' => '%s kullanıcısı #%d nolu görevi oluşturdu',
'%s closed the task #%d' => '%s kullanıcısı #%d nolu görevi kapattı',
'%s open the task #%d' => '%s kullanıcısı #%d nolu görevi açtı',
'%s moved the task #%d to the column "%s"' => '%s kullanıcısı #%d nolu görevi "%s" sütununa taşıdı',
'%s moved the task #%d to the position %d in the column "%s"' => '%s kullanıcısı #%d nolu görevi %d pozisyonu "%s" sütununa taşıdı',
'Activity' => 'Aktivite',
'Default values are "%s"' => 'Varsayılan değerler "%s"',
'Default columns for new projects (Comma-separated)' => 'Yeni projeler için varsayılan sütunlar (virgül ile ayrılmış)',
'Task assignee change' => 'Göreve atanan kullanıcı değişikliği',
'%s change the assignee of the task #%d to %s' => '%s kullanıcısı #%d nolu görevin sorumlusunu %s olarak değiştirdi',
'%s changed the assignee of the task %s to %s' => '%s kullanıcısı %s görevinin sorumlusunu %s olarak değiştirdi',
'Column Change' => 'Sütun değişikliği',
'Position Change' => 'Konum değişikliği',
'Assignee Change' => 'Sorumlu değişikliği',
'New password for the user "%s"' => '"%s" kullanıcısı için yeni şifre',
'Choose an event' => 'Bir durum seçin',
// 'Github commit received' => '',
// 'Github issue opened' => '',
// 'Github issue closed' => '',
// 'Github issue reopened' => '',
// 'Github issue assignee change' => '',
// 'Github issue label change' => '',
'Create a task from an external provider' => 'Dış sağlayıcı ile bir görev oluştur',
'Change the assignee based on an external username' => 'Dış kaynaklı kullanıcı adı ile göreve atananı değiştir',
'Change the category based on an external label' => 'Dış kaynaklı bir etiket ile kategori değiştir',
'Reference' => 'Referans',
'Reference: %s' => 'Referans: %s',
'Label' => 'Etiket',
'Database' => 'Veri bankası',
'About' => 'Hakkında',
'Database driver:' => 'Veri bankası sürücüsü',
'Board settings' => 'Tablo ayarları',
'URL and token' => 'URL veya Token',
'Webhook settings' => 'Webhook ayarları',
'URL for task creation:' => 'Görev oluşturma için URL',
'Reset token' => 'Reset Token',
'API endpoint:' => 'API endpoint',
'Refresh interval for private board' => 'Özel tablolar için yenileme sıklığı',
'Refresh interval for public board' => 'Dışa açık tablolar için yenileme sıklığı',
'Task highlight period' => 'Görevi öne çıkarma süresi',
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Bir görevin yeni değiştirilmiş sayılması için süre (saniye olarak) (Bu özelliği iptal etmek için 0, varsayılan değer 2 gün)',
'Frequency in second (60 seconds by default)' => 'Saniye olarak frekans (varsayılan 60 saniye)',
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Saniye olarak frekans (Bu özelliği iptal etmek için 0, varsayılan değer 10 saniye)',
'Application URL' => 'Uygulama URL',
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Örneğin: http://example.kanboard.net/ (E-posta bildirimleri için kullanılıyor)',
'Token regenerated.' => 'Token yeniden oluşturuldu.',
'Date format' => 'Tarih formatı',
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formatı her zaman kabul edilir, örneğin: "%s" ve "%s"',
'New private project' => 'Yeni özel proje',
'This project is private' => 'Bu proje özel',
'Type here to create a new sub-task' => 'Yeni bir alt görev oluşturmak için buraya yazın',
'Add' => 'Ekle',
'Estimated time: %s hours' => 'Tahmini süre: %s Saat',
'Time spent: %s hours' => 'Kullanılan süre: %s Saat',
'Started on %B %e, %Y' => '%B %e %Y tarihinde başlatıldı',
'Start date' => 'Başlangıç tarihi',
'Time estimated' => 'Tahmini süre',
'There is nothing assigned to you.' => 'Size atanan hiçbir şey yok.',
'My tasks' => 'Görevlerim',
'Activity stream' => 'Güncel olay akışı',
'Dashboard' => 'Anasayfa',
'Confirmation' => 'Onay',
'Allow everybody to access to this project' => 'Bu projeye herkesin erişimine izin ver',
'Everybody have access to this project.' => 'Bu projeye herkesin erişimi var.',
'Webhooks' => 'Webhooks',
'API' => 'API',
'Integration' => 'Entegrasyon',
'Github webhooks' => 'Github Webhook',
'Help on Github webhooks' => 'Github Webhooks hakkında yardım',
'Create a comment from an external provider' => 'Dış sağlayıcı ile bir yorum oluştur',
'Github issue comment created' => 'Github hata yorumu oluşturuldu',
'Configure' => 'Ayarla',
'Project management' => 'Proje yönetimi',
'My projects' => 'Projelerim',
'Columns' => 'Sütunlar',
'Task' => 'Görev',
'Your are not member of any project.' => 'Hiç bir projenin üyesi değilsiniz.',
'Percentage' => 'Yüzde',
'Number of tasks' => 'Görev sayısı',
'Task distribution' => 'Görev dağılımı',
'Reportings' => 'Raporlar',
'Task repartition for "%s"' => '"%s" için görev dağılımı',
'Analytics' => 'Analiz',
'Subtask' => 'Alt görev',
'My subtasks' => 'Alt görevlerim',
'User repartition' => 'Kullanıcı dağılımı',
'User repartition for "%s"' => '"%s" için kullanıcı dağılımı',
'Clone this project' => 'Projenin kopyasını oluştur',
'Column removed successfully.' => 'Sütun başarıyla kaldırıldı.',
'Edit Project' => 'Projeyi düzenle',
'Github Issue' => 'Github Issue',
'Not enough data to show the graph.' => 'Grafik gösterimi için yeterli veri yok.',
'Previous' => 'Önceki',
'The id must be an integer' => 'ID bir tamsayı olmalı',
'The project id must be an integer' => 'Proje numarası bir tam sayı olmalı',
'The status must be an integer' => 'Durum bir tam sayı olmalı',
'The subtask id is required' => 'Alt görev numarası gerekli',
'The subtask id must be an integer' => 'Alt görev numarası bir tam sayı olmalı',
'The task id is required' => 'Görev numarası gerekli',
'The task id must be an integer' => 'Görev numarası bir tam sayı olmalı',
'The user id must be an integer' => 'Kullanıcı numarası bir tam sayı olmalı',
'This value is required' => 'Bu değer gerekli',
'This value must be numeric' => 'Bu değer sayı olmalı',
'Unable to create this task.' => 'Bu görev oluşturulamıyor.',
'Cumulative flow diagram' => 'Kümülatif akış diyagramı',
'Cumulative flow diagram for "%s"' => '"%s" için kümülatif akış diyagramı',
'Daily project summary' => 'Günlük proje özeti',
'Daily project summary export' => 'Günlük proje özetini dışa aktar',
'Daily project summary export for "%s"' => '"%s" için günlük proje özetinin dışa',
'Exports' => 'Dışa aktarımlar',
'This export contains the number of tasks per column grouped per day.' => 'Bu dışa aktarım günlük gruplanmış olarak her sütundaki görev sayısını içerir.',
'Nothing to preview...' => 'Önizleme yapılacak bir şey yok ...',
'Preview' => 'Önizleme',
'Write' => 'Değiştir',
'Active swimlanes' => 'Aktif Kulvar',
'Add a new swimlane' => 'Yeni bir Kulvar ekle',
'Change default swimlane' => 'Varsayılan Kulvarı değiştir',
'Default swimlane' => 'Varsayılan Kulvar',
'Do you really want to remove this swimlane: "%s"?' => 'Bu Kulvarı silmek istediğinize emin misiniz?: "%s"?',
'Inactive swimlanes' => 'Pasif Kulvarlar',
'Set project manager' => 'Proje yöneticisi olarak ata',
'Set project member' => 'Proje üyesi olarak ata',
'Remove a swimlane' => 'Kulvarı sil',
'Rename' => 'Yeniden adlandır',
'Show default swimlane' => 'Varsayılan Kulvarı göster',
'Swimlane modification for the project "%s"' => '"% s" Projesi için Kulvar değişikliği',
'Swimlane not found.' => 'Kulvar bulunamadı',
'Swimlane removed successfully.' => 'Kulvar başarıyla kaldırıldı.',
'Swimlanes' => 'Kulvarlar',
'Swimlane updated successfully.' => 'Kulvar başarıyla güncellendi.',
'The default swimlane have been updated successfully.' => 'Varsayılan Kulvarlar başarıyla güncellendi.',
'Unable to create your swimlane.' => 'Bu Kulvarı oluşturmak mümkün değil.',
'Unable to remove this swimlane.' => 'Bu Kulvarı silmek mümkün değil.',
'Unable to update this swimlane.' => 'Bu Kulvarı değiştirmek mümkün değil.',
'Your swimlane have been created successfully.' => 'Kulvar başarıyla oluşturuldu.',
'Example: "Bug, Feature Request, Improvement"' => 'Örnek: "Sorun, Özellik talebi, İyileştirme"',
'Default categories for new projects (Comma-separated)' => 'Yeni projeler için varsayılan kategoriler (Virgül ile ayrılmış)',
// 'Gitlab commit received' => '',
// 'Gitlab issue opened' => '',
// 'Gitlab issue closed' => '',
// 'Gitlab webhooks' => '',
// 'Help on Gitlab webhooks' => '',
'Integrations' => 'Entegrasyon',
'Integration with third-party services' => 'Dış servislerle entegrasyon',
'Role for this project' => 'Bu proje için rol',
'Project manager' => 'Proje Yöneticisi',
'Project member' => 'Proje Üyesi',
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Bir Proje Yöneticisi proje ayarlarını değiştirebilir ve bir üyeden daha fazla yetkiye sahiptir.',
// 'Gitlab Issue' => '',
'Subtask Id' => 'Alt görev No:',
'Subtasks' => 'Alt görevler',
'Subtasks Export' => 'Alt görevleri dışa aktar',
'Subtasks exportation for "%s"' => '"%s" için alt görevleri dışa aktarımı',
'Task Title' => 'Görev Başlığı',
'Untitled' => 'Başlıksız',
'Application default' => 'Uygulama varsayılanları',
'Language:' => 'Dil:',
'Timezone:' => 'Saat dilimi:',
'All columns' => 'Tüm sütunlar',
'Calendar for "%s"' => '"%s" için takvim',
'Filter by column' => 'Sütuna göre filtrele',
'Filter by status' => 'Duruma göre filtrele',
'Calendar' => 'Takvim',
'Next' => 'Sonraki',
'#%d' => '#%d',
'Filter by color' => 'Renklere göre filtrele',
'Filter by swimlane' => 'Kulvara göre filtrele',
'All swimlanes' => 'Tüm Kulvarlar',
'All colors' => 'Tüm Renkler',
'All status' => 'Tüm Durumlar',
'Add a comment logging moving the task between columns' => 'Sütun değiştiğinde kayıt olarak yorum ekle',
'Moved to column %s' => '%s Sütununa taşındı',
'Change description' => 'Açıklamayı değiştir',
'User dashboard' => 'Kullanıcı Anasayfası',
'Allow only one subtask in progress at the same time for a user' => 'Bir kullanıcı için aynı anda yalnızca bir alt göreve izin ver',
'Edit column "%s"' => '"%s" sütununu değiştir',
'Enable time tracking for subtasks' => 'Alt görevler için zaman takibini etkinleştir',
'Select the new status of the subtask: "%s"' => '"%s" alt görevi için yeni durum seçin.',
'Subtask timesheet' => 'Alt görev için zaman takip tablosu',
'There is nothing to show.' => 'Gösterilecek hiçbir şey yok.',
'Time Tracking' => 'Zaman takibi',
'You already have one subtask in progress' => 'Zaten işlemde olan bir alt görev var',
'Which parts of the project do you want to duplicate?' => 'Projenin hangi kısımlarının kopyasını oluşturmak istiyorsunuz?',
'Change dashboard view' => 'Anasayfa görünümünü değiştir',
'Show/hide activities' => 'Aktiviteleri göster/gizle',
'Show/hide projects' => 'Projeleri göster/gizle',
'Show/hide subtasks' => 'Alt görevleri göster/gizle',
'Show/hide tasks' => 'Görevleri göster/gizle',
'Disable login form' => 'Giriş formunu devre dışı bırak',
'Show/hide calendar' => 'Takvimi göster/gizle',
'User calendar' => 'Kullanıcı takvimi',
'Bitbucket commit received' => 'Bitbucket commit alındı',
'Bitbucket webhooks' => 'Bitbucket webhooks',
'Help on Bitbucket webhooks' => 'Bitbucket webhooks için yardım',
'Start' => 'Başlangıç',
'End' => 'Son',
'Task age in days' => 'Görev yaşı gün olarak',
'Days in this column' => 'Bu sütunda geçirilen gün',
'%dd' => '%dG',
'Add a link' => 'Link ekle',
'Add a new link' => 'Yeni link ekle',
'Do you really want to remove this link: "%s"?' => '"%s" linkini gerçekten silmek istiyor musunuz?',
'Do you really want to remove this link with task #%d?' => '#%d numaralı görev ile linki gerçekten silmek istiyor musunuz?',
'Field required' => 'Bu alan gerekli',
'Link added successfully.' => 'Link başarıyla eklendi.',
'Link updated successfully.' => 'Link başarıyla güncellendi.',
'Link removed successfully.' => 'Link başarıyla silindi.',
'Link labels' => 'Link etiketleri',
'Link modification' => 'Link değiştirme',
'Links' => 'Links',
'Link settings' => 'Link ayarları',
'Opposite label' => 'Zıt etiket',
'Remove a link' => 'Bir link silmek',
'Task\'s links' => 'Görevin linkleri',
'The labels must be different' => 'Etiketler farklı olmalı',
'There is no link.' => 'Hiç bir bağ yok',
'This label must be unique' => 'Bu etiket tek olmalı',
'Unable to create your link.' => 'Link oluşturulamadı.',
'Unable to update your link.' => 'Link güncellenemiyor.',
'Unable to remove this link.' => 'Link kaldırılamıyor',
'relates to' => 'şununla ilgili',
'blocks' => 'şunu engelliyor',
'is blocked by' => 'şunun tarafından engelleniyor',
'duplicates' => 'şunun kopyasını oluşturuyor',
'is duplicated by' => 'şunun tarafından kopyası oluşturuluyor',
'is a child of' => 'şunun astı',
'is a parent of' => 'şunun üstü',
'targets milestone' => 'şu kilometre taşını hedefliyor',
'is a milestone of' => 'şunun için bir kilometre taşı',
'fixes' => 'düzeltiyor',
'is fixed by' => 'şunun tarafından düzeltildi',
'This task' => 'Bu görev',
'<1h' => '<1s',
'%dh' => '%ds',
// '%b %e' => '',
'Expand tasks' => 'Görevleri genişlet',
'Collapse tasks' => 'Görevleri daralt',
'Expand/collapse tasks' => 'Görevleri genişlet/daralt',
'Close dialog box' => 'İletiyi kapat',
'Submit a form' => 'Formu gönder',
'Board view' => 'Tablo görünümü',
'Keyboard shortcuts' => 'Klavye kısayolları',
'Open board switcher' => 'Tablo seçim listesini aç',
'Application' => 'Uygulama',
'Filter recently updated' => 'Son güncellenenleri göster',
'since %B %e, %Y at %k:%M %p' => '%B %e, %Y saat %k:%M %p\'den beri',
'More filters' => 'Daha fazla filtre',
'Compact view' => 'Ekrana sığdır',
'Horizontal scrolling' => 'Geniş görünüm',
'Compact/wide view' => 'Ekrana sığdır / Geniş görünüm',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
// 'Task transitions' => '',
// 'Task transitions export' => '',
// 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '',
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
// 'The two factor authentication code is not valid.' => '',
// 'The two factor authentication code is valid.' => '',
// 'Code' => '',
// 'Two factor authentication' => '',
// 'Enable/disable two factor authentication' => '',
// 'This QR code contains the key URI: ' => '',
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
// 'Check my code' => '',
// 'Secret key: ' => '',
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
);

View file

@ -1,6 +1,8 @@
<?php <?php
return array( return array(
'number.decimals_separator' => '.',
'number.thousands_separator' => ',',
'None' => '无', 'None' => '无',
'edit' => '编辑', 'edit' => '编辑',
'Edit' => '编辑', 'Edit' => '编辑',
@ -379,7 +381,7 @@ return array(
'Created by %s' => '创建者:%s', 'Created by %s' => '创建者:%s',
'Last modified on %B %e, %Y at %k:%M %p' => '最后修改:%Y/%m/%d/ %H:%M', 'Last modified on %B %e, %Y at %k:%M %p' => '最后修改:%Y/%m/%d/ %H:%M',
'Tasks Export' => '任务导出', 'Tasks Export' => '任务导出',
'Tasks exportation for "%s"' => '导出任务"%s"', 'Tasks exportation for "%s"' => '导出"%s"的任务',
'Start Date' => '开始时间', 'Start Date' => '开始时间',
'End Date' => '结束时间', 'End Date' => '结束时间',
'Execute' => '执行', 'Execute' => '执行',
@ -408,13 +410,13 @@ return array(
'Comment updated' => '更新了评论', 'Comment updated' => '更新了评论',
'New comment posted by %s' => '%s 的新评论', 'New comment posted by %s' => '%s 的新评论',
'List of due tasks for the project "%s"' => '项目"%s"的到期任务列表', 'List of due tasks for the project "%s"' => '项目"%s"的到期任务列表',
// 'New attachment' => '', 'New attachment' => '新建附件',
// 'New comment' => '', 'New comment' => '新建评论',
// 'New subtask' => '', 'New subtask' => '新建子任务',
// 'Subtask updated' => '', 'Subtask updated' => '子任务更新',
// 'Task updated' => '', 'Task updated' => '任务更新',
// 'Task closed' => '', 'Task closed' => '任务关闭',
// 'Task opened' => '', 'Task opened' => '任务开启',
'[%s][Due tasks]' => '[%s][到期任务]', '[%s][Due tasks]' => '[%s][到期任务]',
'[Kanboard] Notification' => '[Kanboard] 通知', '[Kanboard] Notification' => '[Kanboard] 通知',
'I want to receive notifications only for those projects:' => '我仅需要收到下面项目的通知:', 'I want to receive notifications only for those projects:' => '我仅需要收到下面项目的通知:',
@ -498,9 +500,9 @@ return array(
'Task assignee change' => '任务分配变更', 'Task assignee change' => '任务分配变更',
'%s change the assignee of the task #%d to %s' => '%s 将任务 #%d 分配给了 %s', '%s change the assignee of the task #%d to %s' => '%s 将任务 #%d 分配给了 %s',
'%s changed the assignee of the task %s to %s' => '%s 将任务 %s 分配给 %s', '%s changed the assignee of the task %s to %s' => '%s 将任务 %s 分配给 %s',
// 'Column Change' => '', 'Column Change' => '栏目变更',
// 'Position Change' => '', 'Position Change' => '位置变更',
// 'Assignee Change' => '', 'Assignee Change' => '负责人变更',
'New password for the user "%s"' => '用户"%s"的新密码', 'New password for the user "%s"' => '用户"%s"的新密码',
'Choose an event' => '选择一个事件', 'Choose an event' => '选择一个事件',
'Github commit received' => '收到了Github提交', 'Github commit received' => '收到了Github提交',
@ -607,8 +609,8 @@ return array(
'Default swimlane' => '默认泳道', 'Default swimlane' => '默认泳道',
'Do you really want to remove this swimlane: "%s"?' => '确定要删除泳道:"%s"?', 'Do you really want to remove this swimlane: "%s"?' => '确定要删除泳道:"%s"?',
'Inactive swimlanes' => '非活动泳道', 'Inactive swimlanes' => '非活动泳道',
// 'Set project manager' => '', 'Set project manager' => '设为项目经理',
// 'Set project member' => '', 'Set project member' => '设为项目成员',
'Remove a swimlane' => '删除泳道', 'Remove a swimlane' => '删除泳道',
'Rename' => '重命名', 'Rename' => '重命名',
'Show default swimlane' => '显示默认泳道', 'Show default swimlane' => '显示默认泳道',
@ -622,92 +624,92 @@ return array(
'Unable to remove this swimlane.' => '无法删除此泳道', 'Unable to remove this swimlane.' => '无法删除此泳道',
'Unable to update this swimlane.' => '无法更新此泳道', 'Unable to update this swimlane.' => '无法更新此泳道',
'Your swimlane have been created successfully.' => '已经成功创建泳道。', 'Your swimlane have been created successfully.' => '已经成功创建泳道。',
// 'Example: "Bug, Feature Request, Improvement"' => '', 'Example: "Bug, Feature Request, Improvement"' => '示例:“缺陷,功能需求,提升',
// 'Default categories for new projects (Comma-separated)' => '', 'Default categories for new projects (Comma-separated)' => '新项目的默认分类(用逗号分隔)',
// 'Gitlab commit received' => '', 'Gitlab commit received' => '收到 Gitlab 提交',
// 'Gitlab issue opened' => '', 'Gitlab issue opened' => '开启 Gitlab 问题',
// 'Gitlab issue closed' => '', 'Gitlab issue closed' => '关闭 Gitlab 问题',
// 'Gitlab webhooks' => '', 'Gitlab webhooks' => 'Gitlab 网络钩子',
// 'Help on Gitlab webhooks' => '', 'Help on Gitlab webhooks' => 'Gitlab 网络钩子帮助',
// 'Integrations' => '', 'Integrations' => '整合',
// 'Integration with third-party services' => '', 'Integration with third-party services' => '与第三方服务进行整合',
// 'Role for this project' => '', 'Role for this project' => '项目角色',
// 'Project manager' => '', 'Project manager' => '项目管理员',
// 'Project member' => '', 'Project member' => '项目成员',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', 'A project manager can change the settings of the project and have more privileges than a standard user.' => '项目经理可以修改项目的设置,比标准用户多了一些权限',
// 'Gitlab Issue' => '', 'Gitlab Issue' => 'Gitlab 问题',
// 'Subtask Id' => '', 'Subtask Id' => '子任务 Id',
// 'Subtasks' => '', 'Subtasks' => '子任务',
// 'Subtasks Export' => '', 'Subtasks Export' => '子任务导出',
// 'Subtasks exportation for "%s"' => '', 'Subtasks exportation for "%s"' => '导出"%s"的子任务',
// 'Task Title' => '', 'Task Title' => '任务标题',
// 'Untitled' => '', 'Untitled' => '无标题',
// 'Application default' => '', 'Application default' => '程序默认',
// 'Language:' => '', 'Language:' => '语言:',
// 'Timezone:' => '', 'Timezone:' => '时区:',
// 'All columns' => '', 'All columns' => '全部栏目',
// 'Calendar for "%s"' => '', 'Calendar for "%s"' => '"%s"的日程表',
// 'Filter by column' => '', 'Filter by column' => '按栏目过滤',
// 'Filter by status' => '', 'Filter by status' => '按状态过滤',
// 'Calendar' => '', 'Calendar' => '日程表',
// 'Next' => '', 'Next' => '前进',
// '#%d' => '', '#%d' => '#%d',
// 'Filter by color' => '', 'Filter by color' => '按颜色过滤',
// 'Filter by swimlane' => '', 'Filter by swimlane' => '按泳道过滤',
// 'All swimlanes' => '', 'All swimlanes' => '全部泳道',
// 'All colors' => '', 'All colors' => '全部颜色',
// 'All status' => '', 'All status' => '全部状态',
// 'Add a comment logging moving the task between columns' => '', 'Add a comment logging moving the task between columns' => '在不同栏目间移动任务时添加一个评论',
// 'Moved to column %s' => '', 'Moved to column %s' => '移动到栏目 %s',
// 'Change description' => '', 'Change description' => '修改描述',
// 'User dashboard' => '', 'User dashboard' => '用户仪表板',
// 'Allow only one subtask in progress at the same time for a user' => '', 'Allow only one subtask in progress at the same time for a user' => '每用户同时仅有一个活动子任务',
// 'Edit column "%s"' => '', 'Edit column "%s"' => '编辑栏目"%s"',
// 'Enable time tracking for subtasks' => '', 'Enable time tracking for subtasks' => '启用子任务的时间记录',
// 'Select the new status of the subtask: "%s"' => '', 'Select the new status of the subtask: "%s"' => '选择子任务的新状态:"%s"',
// 'Subtask timesheet' => '', 'Subtask timesheet' => '子任务时间',
// 'There is nothing to show.' => '', 'There is nothing to show.' => '无内容。',
// 'Time Tracking' => '', 'Time Tracking' => '时间记录',
// 'You already have one subtask in progress' => '', 'You already have one subtask in progress' => '你已经有了一个进行中的子任务',
// 'Which parts of the project do you want to duplicate?' => '', 'Which parts of the project do you want to duplicate?' => '要复制项目的哪些内容?',
// 'Change dashboard view' => '', 'Change dashboard view' => '修改仪表板视图',
// 'Show/hide activities' => '', 'Show/hide activities' => '显示/隐藏活动',
// 'Show/hide projects' => '', 'Show/hide projects' => '显示/隐藏项目',
// 'Show/hide subtasks' => '', 'Show/hide subtasks' => '显示/隐藏子任务',
// 'Show/hide tasks' => '', 'Show/hide tasks' => '显示/隐藏任务',
// 'Disable login form' => '', 'Disable login form' => '禁用登录界面',
// 'Show/hide calendar' => '', 'Show/hide calendar' => '显示/隐藏日程表',
// 'User calendar' => '', 'User calendar' => '用户日程表',
// 'Bitbucket commit received' => '', 'Bitbucket commit received' => '收到Bitbucket提交',
// 'Bitbucket webhooks' => '', 'Bitbucket webhooks' => 'Bitbucket网络钩子',
// 'Help on Bitbucket webhooks' => '', 'Help on Bitbucket webhooks' => 'Bitbucket网络钩子帮助',
// 'Start' => '', 'Start' => '开始',
// 'End' => '', 'End' => '结束',
// 'Task age in days' => '', 'Task age in days' => '任务存在天数',
// 'Days in this column' => '', 'Days in this column' => '在此栏目的天数',
// '%dd' => '', '%dd' => '%d天',
// 'Add a link' => '', 'Add a link' => '添加一个关联',
// 'Add a new link' => '', 'Add a new link' => '添加一个新关联',
// 'Do you really want to remove this link: "%s"?' => '', 'Do you really want to remove this link: "%s"?' => '确认要删除此关联吗:"%s"?',
// 'Do you really want to remove this link with task #%d?' => '', 'Do you really want to remove this link with task #%d?' => '确认要删除到任务 #%d 的关联吗?',
// 'Field required' => '', 'Field required' => '必须的字段',
// 'Link added successfully.' => '', 'Link added successfully.' => '成功添加关联。',
// 'Link updated successfully.' => '', 'Link updated successfully.' => '成功更新关联。',
// 'Link removed successfully.' => '', 'Link removed successfully.' => '成功删除关联。',
// 'Link labels' => '', 'Link labels' => '关联标签',
// 'Link modification' => '', 'Link modification' => '关联修改',
// 'Links' => '', 'Links' => '关联',
// 'Link settings' => '', 'Link settings' => '关联设置',
// 'Opposite label' => '', 'Opposite label' => '反向标签',
// 'Remove a link' => '', 'Remove a link' => '删除关联',
// 'Task\'s links' => '', 'Task\'s links' => '任务的关联',
// 'The labels must be different' => '', 'The labels must be different' => '标签不能一样',
// 'There is no link.' => '', 'There is no link.' => '没有关联',
// 'This label must be unique' => '', 'This label must be unique' => '关联必须唯一',
// 'Unable to create your link.' => '', 'Unable to create your link.' => '无法创建关联。',
// 'Unable to update your link.' => '', 'Unable to update your link.' => '无法更新关联。',
// 'Unable to remove this link.' => '', 'Unable to remove this link.' => '无法删除关联。',
// 'relates to' => '', 'relates to' => '关联到',
// 'blocks' => '', // 'blocks' => '',
// 'is blocked by' => '', // 'is blocked by' => '',
// 'duplicates' => '', // 'duplicates' => '',
@ -718,20 +720,130 @@ return array(
// 'is a milestone of' => '', // 'is a milestone of' => '',
// 'fixes' => '', // 'fixes' => '',
// 'is fixed by' => '', // 'is fixed by' => '',
// 'This task' => '', 'This task' => '此任务',
// '<1h' => '', '<1h' => '<1h',
// '%dh' => '', '%dh' => '%h',
// '%b %e' => '', // '%b %e' => '',
// 'Expand tasks' => '', 'Expand tasks' => '展开任务',
// 'Collapse tasks' => '', 'Collapse tasks' => '收缩任务',
// 'Expand/collapse tasks' => '', 'Expand/collapse tasks' => '展开/收缩任务',
// 'Close dialog box' => '', 'Close dialog box' => '关闭对话框',
// 'Submit a form' => '', 'Submit a form' => '提交表单',
// 'Board view' => '', 'Board view' => '面板视图',
// 'Keyboard shortcuts' => '', 'Keyboard shortcuts' => '键盘快捷方式',
// 'Open board switcher' => '', 'Open board switcher' => '打开面板切换器',
// 'Application' => '', 'Application' => '应用程序',
// 'Filter recently updated' => '', 'Filter recently updated' => '过滤最近的更新',
// 'since %B %e, %Y at %k:%M %p' => '', // 'since %B %e, %Y at %k:%M %p' => '',
// 'More filters' => '', 'More filters' => '更多过滤',
'Compact view' => '紧凑视图',
'Horizontal scrolling' => '水平滚动',
'Compact/wide view' => '紧凑/宽视图',
'No results match:' => '无匹配结果:',
'Remove hourly rate' => '删除小时工资',
'Do you really want to remove this hourly rate?' => '确定要删除此计时工资吗?',
'Hourly rates' => '小时工资',
'Hourly rate' => '小时工资',
'Currency' => '货币',
'Effective date' => '开始时间',
'Add new rate' => '添加小时工资',
'Rate removed successfully.' => '成功删除工资。',
'Unable to remove this rate.' => '无法删除此小时工资。',
'Unable to save the hourly rate.' => '无法删除小时工资。',
'Hourly rate created successfully.' => '成功创建小时工资。',
'Start time' => '开始时间',
'End time' => '结束时1间',
'Comment' => '注释',
'All day' => '全天',
'Day' => '日期',
'Manage timetable' => '管理时间表',
'Overtime timetable' => '',
'Time off timetable' => '加班时间表',
'Timetable' => '时间表',
'Work timetable' => '工作时间表',
'Week timetable' => '周时间表',
'Day timetable' => '日时间表',
'From' => '从',
'To' => '到',
'Time slot created successfully.' => '成功创建时间段。',
'Unable to save this time slot.' => '无法保存此时间段。',
'Time slot removed successfully.' => '成功删除时间段。',
'Unable to remove this time slot.' => '无法删除此时间段。',
'Do you really want to remove this time slot?' => '确认要删除此时间段吗?',
'Remove time slot' => '删除时间段',
'Add new time slot' => '添加新时间段',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '如果在放假和加班计划中选择全天,则会使用这里配置的时间段。',
'Files' => '文件',
'Images' => '图片',
'Private project' => '私人项目',
'Amount' => '数量',
// 'AUD - Australian Dollar' => '',
'Budget' => '预算',
'Budget line' => '预算线',
'Budget line removed successfully.' => '成功删除预算线',
'Budget lines' => '预算线',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
'Cost' => '成本',
'Cost breakdown' => '成本分解',
'Custom Stylesheet' => '自定义样式表',
'download' => '下载',
'Do you really want to remove this budget line?' => '确定要删除此预算线吗?',
// 'EUR - Euro' => '',
'Expenses' => '花费',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
'New budget line' => '新预算线',
// 'NZD - New Zealand Dollar' => '',
'Remove a budget line' => '删除预算线',
'Remove budget line' => '删除预算线',
// 'RSD - Serbian dinar' => '',
'The budget line have been created successfully.' => '成功创建预算线。',
'Unable to create the budget line.' => '无法创建预算线。',
'Unable to remove this budget line.' => '无法删除此预算线。',
// 'USD - US Dollar' => '',
'Remaining' => '剩余',
'Destination column' => '目标栏目',
'Move the task to another column when assigned to a user' => '指定负责人时移动到其它栏目',
'Move the task to another column when assignee is cleared' => '移除负责人时移动到其它栏目',
'Source column' => '原栏目',
'Show subtask estimates in the user calendar' => '在用户日历中显示子任务预估',
'Transitions' => '变更',
'Executer' => '执行者',
'Time spent in the column' => '栏目中的时间消耗',
'Task transitions' => '任务变更',
'Task transitions export' => '导出任务变更',
'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '此报告记录任务的变更,包含日期、用户和时间消耗。',
'Currency rates' => '汇率',
'Rate' => '汇率',
'Change reference currency' => '修改参考货币',
'Add a new currency rate' => '添加新汇率',
'Currency rates are used to calculate project budget.' => '汇率会用来计算项目预算。',
'Reference currency' => '参考货币',
'The currency rate have been added successfully.' => '成功添加汇率。',
'Unable to add this currency rate.' => '无法添加此汇率',
'Send notifications to a Slack channel' => '发送通知到 Slack 频道',
'Webhook URL' => '网络钩子 URL',
'Help on Slack integration' => 'Slack 整合帮助',
'%s remove the assignee of the task %s' => '%s删除了任务%s的负责人',
'Send notifications to Hipchat' => '发送通知到 Hipchat',
'API URL' => 'API URL',
'Room API ID or name' => '房间 API ID 或名称',
'Room notification token' => '房间通知令牌',
'Help on Hipchat integration' => 'Hipchat 整合帮助',
'Enable Gravatar images' => '启用 Gravatar 图像',
'Information' => '信息',
'Check two factor authentication code' => '检查双重认证码',
'The two factor authentication code is not valid.' => '双重认证码不正确。',
'The two factor authentication code is valid.' => '双重认证码正确。',
'Code' => '认证码',
'Two factor authentication' => '双重认证',
'Enable/disable two factor authentication' => '启用/禁用双重认证',
'This QR code contains the key URI: ' => '此二维码包含密码 URI:',
'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '将密码保存到 TOTP 软件(例如Google 认证或 FreeOTP)',
'Check my code' => '检查我的认证码',
'Secret key: ' => '密码:',
'Test your device' => '测试设备',
'Assign a color when the task is moved to a specific column' => '任务移动到指定栏目时设置颜色',
); );

View file

@ -56,6 +56,7 @@ class Acl extends Base
'export' => array('tasks', 'subtasks', 'summary'), 'export' => array('tasks', 'subtasks', 'summary'),
'project' => array('edit', 'update', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'), 'project' => array('edit', 'update', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'),
'swimlane' => '*', 'swimlane' => '*',
'budget' => '*',
); );
/** /**
@ -70,6 +71,8 @@ class Acl extends Base
'config' => '*', 'config' => '*',
'link' => '*', 'link' => '*',
'project' => array('remove'), 'project' => array('remove'),
'hourlyrate' => '*',
'currency' => '*',
); );
/** /**

View file

@ -45,6 +45,9 @@ class Action extends Base
'TaskAssignCurrentUser' => t('Assign the task to the person who does the action'), 'TaskAssignCurrentUser' => t('Assign the task to the person who does the action'),
'TaskDuplicateAnotherProject' => t('Duplicate the task to another project'), 'TaskDuplicateAnotherProject' => t('Duplicate the task to another project'),
'TaskMoveAnotherProject' => t('Move the task to another project'), 'TaskMoveAnotherProject' => t('Move the task to another project'),
'TaskMoveColumnAssigned' => t('Move the task to another column when assigned to a user'),
'TaskMoveColumnUnAssigned' => t('Move the task to another column when assignee is cleared'),
'TaskAssignColorColumn' => t('Assign a color when the task is moved to a specific column'),
'TaskAssignColorUser' => t('Assign a color to a specific user'), 'TaskAssignColorUser' => t('Assign a color to a specific user'),
'TaskAssignColorCategory' => t('Assign automatically a color based on a category'), 'TaskAssignColorCategory' => t('Assign automatically a color based on a category'),
'TaskAssignCategoryColor' => t('Assign automatically a category based on a color'), 'TaskAssignCategoryColor' => t('Assign automatically a category based on a color'),
@ -211,7 +214,7 @@ class Action extends Base
* *
* @access public * @access public
* @param array $values Required parameters to save an action * @param array $values Required parameters to save an action
* @return bool Success or not * @return boolean|integer
*/ */
public function create(array $values) public function create(array $values)
{ {
@ -248,7 +251,7 @@ class Action extends Base
// $this->container['fileCache']->remove('proxy_action_getAll'); // $this->container['fileCache']->remove('proxy_action_getAll');
return true; return $action_id;
} }
/** /**

View file

@ -16,6 +16,7 @@ use Pimple\Container;
* @property \Model\Action $action * @property \Model\Action $action
* @property \Model\Authentication $authentication * @property \Model\Authentication $authentication
* @property \Model\Board $board * @property \Model\Board $board
* @property \Model\Budget $budget
* @property \Model\Category $category * @property \Model\Category $category
* @property \Model\Comment $comment * @property \Model\Comment $comment
* @property \Model\CommentHistory $commentHistory * @property \Model\CommentHistory $commentHistory
@ -42,7 +43,11 @@ use Pimple\Container;
* @property \Model\TaskLink $taskLink * @property \Model\TaskLink $taskLink
* @property \Model\TaskPosition $taskPosition * @property \Model\TaskPosition $taskPosition
* @property \Model\TaskValidator $taskValidator * @property \Model\TaskValidator $taskValidator
* @property \Model\TimeTracking $timeTracking * @property \Model\Timetable $timetable
* @property \Model\TimetableDay $timetableDay
* @property \Model\TimetableExtra $timetableExtra
* @property \Model\TimetableOff $timetableOfff
* @property \Model\TimetableWeek $timetableWeek
* @property \Model\SubtaskTimeTracking $subtaskTimeTracking * @property \Model\SubtaskTimeTracking $subtaskTimeTracking
* @property \Model\User $user * @property \Model\User $user
* @property \Model\UserSession $userSession * @property \Model\UserSession $userSession

View file

@ -0,0 +1,214 @@
<?php
namespace Model;
use DateInterval;
use DateTime;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Budget
*
* @package model
* @author Frederic Guillot
*/
class Budget extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'budget_lines';
/**
* Get all budget lines for a project
*
* @access public
* @param integer $project_id
* @return array
*/
public function getAll($project_id)
{
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->desc('date')->findAll();
}
/**
* Get the current total of the budget
*
* @access public
* @param integer $project_id
* @return float
*/
public function getTotal($project_id)
{
$result = $this->db->table(self::TABLE)->columns('SUM(amount) as total')->eq('project_id', $project_id)->findOne();
return isset($result['total']) ? (float) $result['total'] : 0;
}
/**
* Get breakdown by tasks/subtasks/users
*
* @access public
* @param integer $project_id
* @return \PicoDb\Table
*/
public function getSubtaskBreakdown($project_id)
{
return $this->db
->table(SubtaskTimeTracking::TABLE)
->columns(
SubtaskTimeTracking::TABLE.'.id',
SubtaskTimeTracking::TABLE.'.user_id',
SubtaskTimeTracking::TABLE.'.subtask_id',
SubtaskTimeTracking::TABLE.'.start',
SubtaskTimeTracking::TABLE.'.time_spent',
Subtask::TABLE.'.task_id',
Subtask::TABLE.'.title AS subtask_title',
Task::TABLE.'.title AS task_title',
Task::TABLE.'.project_id',
User::TABLE.'.username',
User::TABLE.'.name'
)
->join(Subtask::TABLE, 'id', 'subtask_id')
->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE)
->join(User::TABLE, 'id', 'user_id')
->eq(Task::TABLE.'.project_id', $project_id)
->filter(array($this, 'applyUserRate'));
}
/**
* Gather necessary information to display the budget graph
*
* @access public
* @param integer $project_id
* @return array
*/
public function getDailyBudgetBreakdown($project_id)
{
$out = array();
$in = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->gt('amount', 0)->asc('date')->getAll('date', 'amount');
$time_slots = $this->getSubtaskBreakdown($project_id)->findAll();
foreach ($time_slots as $slot) {
$date = date('Y-m-d', $slot['start']);
if (! isset($out[$date])) {
$out[$date] = 0;
}
$out[$date] += $slot['cost'];
}
$start = key($in) ?: key($out);
$end = new DateTime;
$left = 0;
$serie = array();
for ($today = new DateTime($start); $today <= $end; $today->add(new DateInterval('P1D'))) {
$date = $today->format('Y-m-d');
$today_in = isset($in[$date]) ? (int) $in[$date] : 0;
$today_out = isset($out[$date]) ? (int) $out[$date] : 0;
if ($today_in > 0 || $today_out > 0) {
$left += $today_in;
$left -= $today_out;
$serie[] = array(
'date' => $date,
'in' => $today_in,
'out' => -$today_out,
'left' => $left,
);
}
}
return $serie;
}
/**
* Filter callback to apply the rate according to the effective date
*
* @access public
* @param array $records
* @return array
*/
public function applyUserRate(array $records)
{
$rates = $this->hourlyRate->getAllByProject($records[0]['project_id']);
foreach ($records as &$record) {
$hourly_price = 0;
foreach ($rates as $rate) {
if ($rate['user_id'] == $record['user_id'] && date('Y-m-d', $rate['date_effective']) <= date('Y-m-d', $record['start'])) {
$hourly_price = $this->currency->getPrice($rate['currency'], $rate['rate']);
break;
}
}
$record['cost'] = $hourly_price * $record['time_spent'];
}
return $records;
}
/**
* Add a new budget line in the database
*
* @access public
* @param integer $project_id
* @param float $amount
* @param string $comment
* @param string $date
* @return boolean|integer
*/
public function create($project_id, $amount, $comment, $date = '')
{
$values = array(
'project_id' => $project_id,
'amount' => $amount,
'comment' => $comment,
'date' => $date ?: date('Y-m-d'),
);
return $this->persist(self::TABLE, $values);
}
/**
* Remove a specific budget line
*
* @access public
* @param integer $budget_id
* @return boolean
*/
public function remove($budget_id)
{
return $this->db->table(self::TABLE)->eq('id', $budget_id)->remove();
}
/**
* 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, array(
new Validators\Required('project_id', t('Field required')),
new Validators\Required('amount', t('Field required')),
));
return array(
$v->execute(),
$v->getErrors()
);
}
}

View file

@ -47,7 +47,8 @@ class Comment extends Base
self::TABLE.'.user_id', self::TABLE.'.user_id',
self::TABLE.'.comment', self::TABLE.'.comment',
User::TABLE.'.username', User::TABLE.'.username',
User::TABLE.'.name' User::TABLE.'.name',
User::TABLE.'.email'
) )
->join(User::TABLE, 'id', 'user_id') ->join(User::TABLE, 'id', 'user_id')
->orderBy(self::TABLE.'.date', 'ASC') ->orderBy(self::TABLE.'.date', 'ASC')

View file

@ -21,6 +21,28 @@ class Config extends Base
*/ */
const TABLE = 'settings'; const TABLE = 'settings';
/**
* Get available currencies
*
* @access public
* @return array
*/
public function getCurrencies()
{
return array(
'USD' => t('USD - US Dollar'),
'EUR' => t('EUR - Euro'),
'GBP' => t('GBP - British Pound'),
'CHF' => t('CHF - Swiss Francs'),
'CAD' => t('CAD - Canadian Dollar'),
'AUD' => t('AUD - Australian Dollar'),
'NZD' => t('NZD - New Zealand Dollar'),
'INR' => t('INR - Indian Rupee'),
'JPY' => t('JPY - Japanese Yen'),
'RSD' => t('RSD - Serbian dinar'),
);
}
/** /**
* Get available timezones * Get available timezones
* *
@ -58,11 +80,14 @@ class Config extends Base
'fr_FR' => 'Français', 'fr_FR' => 'Français',
'it_IT' => 'Italiano', 'it_IT' => 'Italiano',
'hu_HU' => 'Magyar', 'hu_HU' => 'Magyar',
'nl_NL' => 'Nederlands',
'pl_PL' => 'Polski', 'pl_PL' => 'Polski',
'pt_BR' => 'Português (Brasil)', 'pt_BR' => 'Português (Brasil)',
'ru_RU' => 'Русский', 'ru_RU' => 'Русский',
'sr_Latn_RS' => 'Srpski',
'fi_FI' => 'Suomi', 'fi_FI' => 'Suomi',
'sv_SE' => 'Svenska', 'sv_SE' => 'Svenska',
'tr_TR' => 'Türkçe',
'zh_CN' => '中文(简体)', 'zh_CN' => '中文(简体)',
'ja_JP' => '日本語', 'ja_JP' => '日本語',
'th_TH' => 'ไทย', 'th_TH' => 'ไทย',
@ -99,6 +124,7 @@ class Config extends Base
'zh_CN' => 'zh-cn', 'zh_CN' => 'zh-cn',
'ja_JP' => 'ja', 'ja_JP' => 'ja',
'th_TH' => 'th', 'th_TH' => 'th',
'tr_TR' => 'tr',
); );
$lang = $this->getCurrentLanguage(); $lang = $this->getCurrentLanguage();

View file

@ -0,0 +1,104 @@
<?php
namespace Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Currency
*
* @package model
* @author Frederic Guillot
*/
class Currency extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'currencies';
/**
* Get all currency rates
*
* @access public
* @return array
*/
public function getAll()
{
return $this->db->table(self::TABLE)->findAll();
}
/**
* Calculate the price for the reference currency
*
* @access public
* @return array
*/
public function getPrice($currency, $price)
{
static $rates = null;
$reference = $this->config->get('application_currency', 'USD');
if ($reference !== $currency) {
$rates = $rates === null ? $this->db->hashtable(self::TABLE)->getAll('currency', 'rate') : array();
$rate = isset($rates[$currency]) ? $rates[$currency] : 1;
return $rate * $price;
}
return $price;
}
/**
* Add a new currency rate
*
* @access public
* @param string $currency
* @param float $rate
* @return boolean|integer
*/
public function create($currency, $rate)
{
if ($this->db->table(self::TABLE)->eq('currency', $currency)->count() === 1) {
return $this->update($currency, $rate);
}
return $this->persist(self::TABLE, compact('currency', 'rate'));
}
/**
* Update a currency rate
*
* @access public
* @param string $currency
* @param float $rate
* @return boolean
*/
public function update($currency, $rate)
{
return $this->db->table(self::TABLE)->eq('currency', $currency)->update(array('rate' => $rate));
}
/**
* Validate
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validate(array $values)
{
$v = new Validator($values, array(
new Validators\Required('currency', t('Field required')),
new Validators\Required('rate', t('Field required')),
));
return array(
$v->execute(),
$v->getErrors()
);
}
}

View file

@ -12,6 +12,47 @@ use DateTime;
*/ */
class DateParser extends Base class DateParser extends Base
{ {
/**
* Return true if the date is within the date range
*
* @access public
* @param DateTime $date
* @param DateTime $start
* @param DateTime $end
* @return boolean
*/
public function withinDateRange(DateTime $date, DateTime $start, DateTime $end)
{
return $date >= $start && $date <= $end;
}
/**
* Get the total number of hours between 2 datetime objects
* Minutes are rounded to the nearest quarter
*
* @access public
* @param DateTime $d1
* @param DateTime $d2
* @return float
*/
public function getHours(DateTime $d1, DateTime $d2)
{
$seconds = $this->getRoundedSeconds(abs($d1->getTimestamp() - $d2->getTimestamp()));
return round($seconds / 3600, 2);
}
/**
* Round the timestamp to the nearest quarter
*
* @access public
* @param integer $seconds Timestamp
* @return integer
*/
public function getRoundedSeconds($seconds)
{
return (int) round($seconds / (15 * 60)) * (15 * 60);
}
/** /**
* Return a timestamp if the given date format is correct otherwise return 0 * Return a timestamp if the given date format is correct otherwise return 0
* *

View file

@ -17,7 +17,7 @@ class File extends Base
* *
* @var string * @var string
*/ */
const TABLE = 'task_has_files'; const TABLE = 'files';
/** /**
* Events * Events
@ -112,6 +112,38 @@ class File extends Base
->findAll(); ->findAll();
} }
/**
* Get all images for a given task
*
* @access public
* @param integer $task_id Task id
* @return array
*/
public function getAllImages($task_id)
{
return $this->db->table(self::TABLE)
->eq('task_id', $task_id)
->eq('is_image', 1)
->asc('name')
->findAll();
}
/**
* Get all files without images for a given task
*
* @access public
* @param integer $task_id Task id
* @return array
*/
public function getAllDocuments($task_id)
{
return $this->db->table(self::TABLE)
->eq('task_id', $task_id)
->eq('is_image', 0)
->asc('name')
->findAll();
}
/** /**
* Check if a filename is an image * Check if a filename is an image
* *
@ -121,7 +153,17 @@ class File extends Base
*/ */
public function isImage($filename) public function isImage($filename)
{ {
return getimagesize($filename) !== false; $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
switch ($extension) {
case 'jpeg':
case 'jpg':
case 'png':
case 'gif':
return true;
}
return false;
} }
/** /**
@ -188,7 +230,7 @@ class File extends Base
$task_id, $task_id,
$original_filename, $original_filename,
$destination_filename, $destination_filename,
$this->isImage(FILES_DIR.$destination_filename) $this->isImage($original_filename)
); );
} }
} }

View file

@ -0,0 +1,121 @@
<?php
namespace Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Hourly Rate
*
* @package model
* @author Frederic Guillot
*/
class HourlyRate extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'hourly_rates';
/**
* Get all user rates for a given project
*
* @access public
* @param integer $project_id
* @return array
*/
public function getAllByProject($project_id)
{
$members = $this->projectPermission->getMembers($project_id);
if (empty($members)) {
return array();
}
return $this->db->table(self::TABLE)->in('user_id', array_keys($members))->desc('date_effective')->findAll();
}
/**
* Get all rates for a given user
*
* @access public
* @param integer $user_id User id
* @return array
*/
public function getAllByUser($user_id)
{
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_effective')->findAll();
}
/**
* Get current rate for a given user
*
* @access public
* @param integer $user_id User id
* @return float
*/
public function getCurrentRate($user_id)
{
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_effective')->findOneColumn('rate') ?: 0;
}
/**
* Add a new rate in the database
*
* @access public
* @param integer $user_id User id
* @param float $rate Hourly rate
* @param string $currency Currency code
* @param string $date ISO8601 date format
* @return boolean|integer
*/
public function create($user_id, $rate, $currency, $date)
{
$values = array(
'user_id' => $user_id,
'rate' => $rate,
'currency' => $currency,
'date_effective' => $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($date)),
);
return $this->persist(self::TABLE, $values);
}
/**
* Remove a specific rate
*
* @access public
* @param integer $rate_id
* @return boolean
*/
public function remove($rate_id)
{
return $this->db->table(self::TABLE)->eq('id', $rate_id)->remove();
}
/**
* 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, array(
new Validators\Required('user_id', t('Field required')),
new Validators\Required('rate', t('Field required')),
new Validators\Numeric('rate', t('This value must be numeric')),
new Validators\Required('date_effective', t('Field required')),
new Validators\Required('currency', t('Field required')),
));
return array(
$v->execute(),
$v->getErrors()
);
}
}

View file

@ -81,17 +81,63 @@ class ProjectActivity extends Base
return array(); return array();
} }
$query = $this->db->table(self::TABLE) $query = $this
->db
->table(self::TABLE)
->columns( ->columns(
self::TABLE.'.*', self::TABLE.'.*',
User::TABLE.'.username AS author_username', User::TABLE.'.username AS author_username',
User::TABLE.'.name AS author_name' User::TABLE.'.name AS author_name',
User::TABLE.'.email'
) )
->in('project_id', $project_ids) ->in('project_id', $project_ids)
->join(User::TABLE, 'id', 'creator_id') ->join(User::TABLE, 'id', 'creator_id')
->desc(self::TABLE.'.id') ->desc(self::TABLE.'.id')
->limit($limit); ->limit($limit);
return $this->getEvents($query, $start, $end);
}
/**
* Get all events for the given task
*
* @access public
* @param integer $task_id Task id
* @param integer $limit Maximum events number
* @param integer $start Timestamp of earliest activity
* @param integer $end Timestamp of latest activity
* @return array
*/
public function getTask($task_id, $limit = 50, $start = null, $end = null)
{
$query = $this
->db
->table(self::TABLE)
->columns(
self::TABLE.'.*',
User::TABLE.'.username AS author_username',
User::TABLE.'.name AS author_name',
User::TABLE.'.email'
)
->eq('task_id', $task_id)
->join(User::TABLE, 'id', 'creator_id')
->desc(self::TABLE.'.id')
->limit($limit);
return $this->getEvents($query, $start, $end);
}
/**
* Common function to return events
*
* @access public
* @param \PicoDb\Table $query PicoDb Query
* @param integer $start Timestamp of earliest activity
* @param integer $end Timestamp of latest activity
* @return array
*/
private function getEvents(\PicoDb\Table $query, $start, $end)
{
if (! is_null($start)){ if (! is_null($start)){
$query->gte('date_creation', $start); $query->gte('date_creation', $start);
} }
@ -162,7 +208,13 @@ class ProjectActivity extends Base
{ {
switch ($event['event_name']) { switch ($event['event_name']) {
case Task::EVENT_ASSIGNEE_CHANGE: case Task::EVENT_ASSIGNEE_CHANGE:
return t('%s change the assignee of the task #%d to %s', $event['author'], $event['task']['id'], $event['task']['assignee_name'] ?: $event['task']['assignee_username']); $assignee = $event['task']['assignee_name'] ?: $event['task']['assignee_username'];
if (! empty($assignee)) {
return t('%s change the assignee of the task #%d to %s', $event['author'], $event['task']['id'], $assignee);
}
return t('%s remove the assignee of the task %s', $event['author'], e('#%d', $event['task']['id']));
case Task::EVENT_UPDATE: case Task::EVENT_UPDATE:
return t('%s updated the task #%d', $event['author'], $event['task']['id']); return t('%s updated the task #%d', $event['author'], $event['task']['id']);
case Task::EVENT_CREATE: case Task::EVENT_CREATE:

View file

@ -316,7 +316,10 @@ class ProjectPermission extends Base
{ {
return $this->db return $this->db
->hashtable(Project::TABLE) ->hashtable(Project::TABLE)
->eq('user_id', $user_id) ->beginOr()
->eq(self::TABLE.'.user_id', $user_id)
->eq(Project::TABLE.'.is_everybody_allowed', 1)
->closeOr()
->join(self::TABLE, 'project_id', 'id') ->join(self::TABLE, 'project_id', 'id')
->getAll('projects.id', 'name'); ->getAll('projects.id', 'name');
} }
@ -332,7 +335,10 @@ class ProjectPermission extends Base
{ {
return $this->db return $this->db
->table(Project::TABLE) ->table(Project::TABLE)
->eq('user_id', $user_id) ->beginOr()
->eq(self::TABLE.'.user_id', $user_id)
->eq(Project::TABLE.'.is_everybody_allowed', 1)
->closeOr()
->join(self::TABLE, 'project_id', 'id') ->join(self::TABLE, 'project_id', 'id')
->findAllByColumn('projects.id'); ->findAllByColumn('projects.id');
} }
@ -348,7 +354,10 @@ class ProjectPermission extends Base
{ {
return $this->db return $this->db
->table(Project::TABLE) ->table(Project::TABLE)
->eq('user_id', $user_id) ->beginOr()
->eq(self::TABLE.'.user_id', $user_id)
->eq(Project::TABLE.'.is_everybody_allowed', 1)
->closeOr()
->eq(Project::TABLE.'.is_active', Project::ACTIVE) ->eq(Project::TABLE.'.is_active', Project::ACTIVE)
->join(self::TABLE, 'project_id', 'id') ->join(self::TABLE, 'project_id', 'id')
->findAllByColumn('projects.id'); ->findAllByColumn('projects.id');
@ -365,7 +374,10 @@ class ProjectPermission extends Base
{ {
return $this->db return $this->db
->hashtable(Project::TABLE) ->hashtable(Project::TABLE)
->eq('user_id', $user_id) ->beginOr()
->eq(self::TABLE.'.user_id', $user_id)
->eq(Project::TABLE.'.is_everybody_allowed', 1)
->closeOr()
->eq(Project::TABLE.'.is_active', Project::ACTIVE) ->eq(Project::TABLE.'.is_active', Project::ACTIVE)
->join(self::TABLE, 'project_id', 'id') ->join(self::TABLE, 'project_id', 'id')
->getAll('projects.id', 'name'); ->getAll('projects.id', 'name');

View file

@ -19,7 +19,7 @@ class Subtask extends Base
* *
* @var string * @var string
*/ */
const TABLE = 'task_has_subtasks'; const TABLE = 'subtasks';
/** /**
* Task "done" status * Task "done" status
@ -98,6 +98,7 @@ class Subtask extends Base
Subtask::TABLE.'.*', Subtask::TABLE.'.*',
Task::TABLE.'.project_id', Task::TABLE.'.project_id',
Task::TABLE.'.color_id', Task::TABLE.'.color_id',
Task::TABLE.'.title AS task_name',
Project::TABLE.'.name AS project_name' Project::TABLE.'.name AS project_name'
) )
->eq('user_id', $user_id) ->eq('user_id', $user_id)
@ -122,7 +123,7 @@ class Subtask extends Base
->eq('task_id', $task_id) ->eq('task_id', $task_id)
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name') ->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
->join(User::TABLE, 'id', 'user_id') ->join(User::TABLE, 'id', 'user_id')
->asc(self::TABLE.'.id') ->asc(self::TABLE.'.position')
->filter(array($this, 'addStatusName')) ->filter(array($this, 'addStatusName'))
->findAll(); ->findAll();
} }
@ -163,6 +164,22 @@ class Subtask extends Base
$this->resetFields($values, array('time_estimated', 'time_spent')); $this->resetFields($values, array('time_estimated', 'time_spent'));
} }
/**
* Get the position of the last column for a given project
*
* @access public
* @param integer $task_id Task id
* @return integer
*/
public function getLastPosition($task_id)
{
return (int) $this->db
->table(self::TABLE)
->eq('task_id', $task_id)
->desc('position')
->findOneColumn('position');
}
/** /**
* Create a new subtask * Create a new subtask
* *
@ -173,6 +190,8 @@ class Subtask extends Base
public function create(array $values) public function create(array $values)
{ {
$this->prepare($values); $this->prepare($values);
$values['position'] = $this->getLastPosition($values['task_id']) + 1;
$subtask_id = $this->persist(self::TABLE, $values); $subtask_id = $this->persist(self::TABLE, $values);
if ($subtask_id) { if ($subtask_id) {
@ -208,6 +227,64 @@ class Subtask extends Base
return $result; return $result;
} }
/**
* Move a subtask down, increment the position value
*
* @access public
* @param integer $task_id
* @param integer $subtask_id
* @return boolean
*/
public function moveDown($task_id, $subtask_id)
{
$subtasks = $this->db->hashtable(self::TABLE)->eq('task_id', $task_id)->asc('position')->getAll('id', 'position');
$positions = array_flip($subtasks);
if (isset($subtasks[$subtask_id]) && $subtasks[$subtask_id] < count($subtasks)) {
$position = ++$subtasks[$subtask_id];
$subtasks[$positions[$position]]--;
$this->db->startTransaction();
$this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position));
$this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $subtasks[$positions[$position]]));
$this->db->closeTransaction();
return true;
}
return false;
}
/**
* Move a subtask up, decrement the position value
*
* @access public
* @param integer $task_id
* @param integer $subtask_id
* @return boolean
*/
public function moveUp($task_id, $subtask_id)
{
$subtasks = $this->db->hashtable(self::TABLE)->eq('task_id', $task_id)->asc('position')->getAll('id', 'position');
$positions = array_flip($subtasks);
if (isset($subtasks[$subtask_id]) && $subtasks[$subtask_id] > 1) {
$position = --$subtasks[$subtask_id];
$subtasks[$positions[$position]]++;
$this->db->startTransaction();
$this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position));
$this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $subtasks[$positions[$position]]));
$this->db->closeTransaction();
return true;
}
return false;
}
/** /**
* Change the status of subtask * Change the status of subtask
* *
@ -286,9 +363,9 @@ class Subtask extends Base
return $this->db->transaction(function ($db) use ($src_task_id, $dst_task_id) { return $this->db->transaction(function ($db) use ($src_task_id, $dst_task_id) {
$subtasks = $db->table(Subtask::TABLE) $subtasks = $db->table(Subtask::TABLE)
->columns('title', 'time_estimated') ->columns('title', 'time_estimated', 'position')
->eq('task_id', $src_task_id) ->eq('task_id', $src_task_id)
->asc('id') // Explicit sorting for postgresql ->asc('position')
->findAll(); ->findAll();
foreach ($subtasks as &$subtask) { foreach ($subtasks as &$subtask) {
@ -380,7 +457,7 @@ class Subtask extends Base
return array( return array(
new Validators\Integer('id', t('The subtask id must be an integer')), 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\Integer('task_id', t('The task id must be an integer')),
new Validators\MaxLength('title', t('The maximum length is %d characters', 100), 100), 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('user_id', t('The user id must be an integer')),
new Validators\Integer('status', t('The status 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_estimated', t('The time must be a numeric value')),

View file

@ -0,0 +1,124 @@
<?php
namespace Model;
use DateTime;
use DateInterval;
/**
* Subtask Forecast
*
* @package model
* @author Frederic Guillot
*/
class SubtaskForecast extends Base
{
/**
* Get not completed subtasks with an estimate sorted by postition
*
* @access public
* @param integer $user_id
* @return array
*/
public function getSubtasks($user_id)
{
return $this->db
->table(Subtask::TABLE)
->columns(Subtask::TABLE.'.id', Task::TABLE.'.project_id', Subtask::TABLE.'.task_id', Subtask::TABLE.'.title', Subtask::TABLE.'.time_estimated')
->join(Task::TABLE, 'id', 'task_id')
->asc(Task::TABLE.'.position')
->asc(Subtask::TABLE.'.position')
->gt(Subtask::TABLE.'.time_estimated', 0)
->eq(Subtask::TABLE.'.status', Subtask::STATUS_TODO)
->eq(Subtask::TABLE.'.user_id', $user_id)
->findAll();
}
/**
* Get the start date for the forecast
*
* @access public
* @param integer $user_id
* @return array
*/
public function getStartDate($user_id)
{
$subtask = $this->db->table(Subtask::TABLE)
->columns(Subtask::TABLE.'.time_estimated', SubtaskTimeTracking::TABLE.'.start')
->eq(SubtaskTimeTracking::TABLE.'.user_id', $user_id)
->eq(SubtaskTimeTracking::TABLE.'.end', 0)
->status('status', Subtask::STATUS_INPROGRESS)
->join(SubtaskTimeTracking::TABLE, 'subtask_id', 'id')
->findOne();
if ($subtask && $subtask['time_estimated'] && $subtask['start']) {
return date('Y-m-d H:i', $subtask['start'] + $subtask['time_estimated'] * 3600);
}
return date('Y-m-d H:i');
}
/**
* Get all calendar events according to the user timetable and the subtasks estimates
*
* @access public
* @param integer $user_id
* @param string $end End date of the calendar
* @return array
*/
public function getCalendarEvents($user_id, $end)
{
$events = array();
$start_date = new DateTime($this->getStartDate($user_id));
$timetable = $this->timetable->calculate($user_id, $start_date, new DateTime($end));
$subtasks = $this->getSubtasks($user_id);
$total = count($subtasks);
$offset = 0;
foreach ($timetable as $slot) {
$interval = $this->dateParser->getHours($slot[0], $slot[1]);
$start = $slot[0]->getTimestamp();
if ($slot[0] < $start_date) {
if (! $this->dateParser->withinDateRange($start_date, $slot[0], $slot[1])) {
continue;
}
$interval = $this->dateParser->getHours(new DateTime, $slot[1]);
$start = time();
}
while ($offset < $total) {
$event = array(
'id' => $subtasks[$offset]['id'].'-'.$subtasks[$offset]['task_id'].'-'.$offset,
'subtask_id' => $subtasks[$offset]['id'],
'title' => t('#%d', $subtasks[$offset]['task_id']).' '.$subtasks[$offset]['title'],
'url' => $this->helper->url('task', 'show', array('task_id' => $subtasks[$offset]['task_id'], 'project_id' => $subtasks[$offset]['project_id'])),
'editable' => false,
'start' => date('Y-m-d\TH:i:s', $start),
);
if ($subtasks[$offset]['time_estimated'] <= $interval) {
$start += $subtasks[$offset]['time_estimated'] * 3600;
$interval -= $subtasks[$offset]['time_estimated'];
$offset++;
$event['end'] = date('Y-m-d\TH:i:s', $start);
$events[] = $event;
}
else {
$subtasks[$offset]['time_estimated'] -= $interval;
$event['end'] = $slot[1]->format('Y-m-d\TH:i:s');
$events[] = $event;
break;
}
}
}
return $events;
}
}

View file

@ -2,6 +2,8 @@
namespace Model; namespace Model;
use DateTime;
/** /**
* Subtask timesheet * Subtask timesheet
* *
@ -33,6 +35,7 @@ class SubtaskTimeTracking extends Base
self::TABLE.'.subtask_id', self::TABLE.'.subtask_id',
self::TABLE.'.end', self::TABLE.'.end',
self::TABLE.'.start', self::TABLE.'.start',
self::TABLE.'.time_spent',
Subtask::TABLE.'.task_id', Subtask::TABLE.'.task_id',
Subtask::TABLE.'.title AS subtask_title', Subtask::TABLE.'.title AS subtask_title',
Task::TABLE.'.title AS task_title', Task::TABLE.'.title AS task_title',
@ -60,6 +63,7 @@ class SubtaskTimeTracking extends Base
self::TABLE.'.subtask_id', self::TABLE.'.subtask_id',
self::TABLE.'.end', self::TABLE.'.end',
self::TABLE.'.start', self::TABLE.'.start',
self::TABLE.'.time_spent',
self::TABLE.'.user_id', self::TABLE.'.user_id',
Subtask::TABLE.'.task_id', Subtask::TABLE.'.task_id',
Subtask::TABLE.'.title AS subtask_title', Subtask::TABLE.'.title AS subtask_title',
@ -89,6 +93,7 @@ class SubtaskTimeTracking extends Base
self::TABLE.'.subtask_id', self::TABLE.'.subtask_id',
self::TABLE.'.end', self::TABLE.'.end',
self::TABLE.'.start', self::TABLE.'.start',
self::TABLE.'.time_spent',
self::TABLE.'.user_id', self::TABLE.'.user_id',
Subtask::TABLE.'.task_id', Subtask::TABLE.'.task_id',
Subtask::TABLE.'.title AS subtask_title', Subtask::TABLE.'.title AS subtask_title',
@ -133,6 +138,8 @@ class SubtaskTimeTracking extends Base
->addCondition($this->getCalendarCondition($start, $end)) ->addCondition($this->getCalendarCondition($start, $end))
->findAll(); ->findAll();
$result = $this->timetable->calculateEventsIntersect($user_id, $result, $start, $end);
return $this->toCalendarEvents($result); return $this->toCalendarEvents($result);
} }
@ -235,7 +242,11 @@ class SubtaskTimeTracking extends Base
*/ */
public function logEndTime($subtask_id, $user_id) public function logEndTime($subtask_id, $user_id)
{ {
$this->updateSubtaskTimeSpent($subtask_id, $user_id); $time_spent = $this->getTimeSpent($subtask_id, $user_id);
if ($time_spent > 0) {
$this->updateSubtaskTimeSpent($subtask_id, $time_spent);
}
return $this->db return $this->db
->table(self::TABLE) ->table(self::TABLE)
@ -243,7 +254,56 @@ class SubtaskTimeTracking extends Base
->eq('user_id', $user_id) ->eq('user_id', $user_id)
->eq('end', 0) ->eq('end', 0)
->update(array( ->update(array(
'end' => time() 'end' => time(),
'time_spent' => $time_spent,
));
}
/**
* Calculate the time spent when the clock is stopped
*
* @access public
* @param integer $subtask_id
* @param integer $user_id
* @return float
*/
public function getTimeSpent($subtask_id, $user_id)
{
$start_time = $this->db
->table(self::TABLE)
->eq('subtask_id', $subtask_id)
->eq('user_id', $user_id)
->eq('end', 0)
->findOneColumn('start');
if ($start_time) {
$start = new DateTime;
$start->setTimestamp($start_time);
return $this->timetable->calculateEffectiveDuration($user_id, $start, new DateTime);
}
return 0;
}
/**
* Update subtask time spent
*
* @access public
* @param integer $subtask_id
* @param float $time_spent
* @return bool
*/
public function updateSubtaskTimeSpent($subtask_id, $time_spent)
{
$subtask = $this->subtask->getById($subtask_id);
// Fire the event subtask.update
return $this->subtask->update(array(
'id' => $subtask['id'],
'time_spent' => $subtask['time_spent'] + $time_spent,
'task_id' => $subtask['task_id'],
)); ));
} }
@ -289,31 +349,4 @@ class SubtaskTimeTracking extends Base
) )
->findOne(); ->findOne();
} }
/**
* Update subtask time spent based on the punch clock table
*
* @access public
* @param integer $subtask_id
* @param integer $user_id
* @return bool
*/
public function updateSubtaskTimeSpent($subtask_id, $user_id)
{
$start_time = $this->db
->table(self::TABLE)
->eq('subtask_id', $subtask_id)
->eq('user_id', $user_id)
->eq('end', 0)
->findOneColumn('start');
$subtask = $this->subtask->getById($subtask_id);
return $start_time &&
$this->subtask->update(array( // Fire the event subtask.update
'id' => $subtask['id'],
'time_spent' => $subtask['time_spent'] + round((time() - $start_time) / 3600, 1),
'task_id' => $subtask['task_id'],
));
}
} }

View file

@ -74,6 +74,22 @@ class Swimlane extends Base
->findOneColumn('id'); ->findOneColumn('id');
} }
/**
* Get a swimlane by the project and the name
*
* @access public
* @param integer $project_id Project id
* @param string $name Swimlane name
* @return array
*/
public function getByName($project_id, $name)
{
return $this->db->table(self::TABLE)
->eq('project_id', $project_id)
->eq('name', $name)
->findAll();
}
/** /**
* Get default swimlane properties * Get default swimlane properties
* *
@ -83,10 +99,16 @@ class Swimlane extends Base
*/ */
public function getDefault($project_id) public function getDefault($project_id)
{ {
return $this->db->table(Project::TABLE) $result = $this->db->table(Project::TABLE)
->eq('id', $project_id) ->eq('id', $project_id)
->columns('id', 'default_swimlane', 'show_default_swimlane') ->columns('id', 'default_swimlane', 'show_default_swimlane')
->findOne(); ->findOne();
if ($result['default_swimlane'] === 'Default swimlane') {
$result['default_swimlane'] = t($result['default_swimlane']);
}
return $result;
} }
/** /**
@ -150,6 +172,11 @@ class Swimlane extends Base
->findOneColumn('default_swimlane'); ->findOneColumn('default_swimlane');
if ($default_swimlane) { if ($default_swimlane) {
if ($default_swimlane === 'Default swimlane') {
$default_swimlane = t($default_swimlane);
}
array_unshift($swimlanes, array('id' => 0, 'name' => $default_swimlane)); array_unshift($swimlanes, array('id' => 0, 'name' => $default_swimlane));
} }
@ -167,14 +194,17 @@ class Swimlane extends Base
public function getList($project_id, $prepend = false) public function getList($project_id, $prepend = false)
{ {
$swimlanes = array(); $swimlanes = array();
$swimlanes[] = $this->db->table(Project::TABLE)->eq('id', $project_id)->findOneColumn('default_swimlane'); $default = $this->db->table(Project::TABLE)->eq('id', $project_id)->eq('show_default_swimlane', 1)->findOneColumn('default_swimlane');
$swimlanes = array_merge( if ($prepend) {
$swimlanes, $swimlanes[-1] = t('All swimlanes');
$this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->orderBy('name', 'asc')->getAll('id', 'name') }
);
return $prepend ? array(-1 => t('All swimlanes')) + $swimlanes : $swimlanes; if (! empty($default)) {
$swimlanes[0] = $default === 'Default swimlane' ? t($default) : $default;
}
return $swimlanes + $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->orderBy('position', 'asc')->getAll('id', 'name');
} }
/** /**

View file

@ -80,10 +80,10 @@ class TaskFinder extends Base
return $this->db return $this->db
->table(Task::TABLE) ->table(Task::TABLE)
->columns( ->columns(
'(SELECT count(*) FROM comments WHERE task_id=tasks.id) AS nb_comments', '(SELECT count(*) FROM '.Comment::TABLE.' WHERE task_id=tasks.id) AS nb_comments',
'(SELECT count(*) FROM task_has_files WHERE task_id=tasks.id) AS nb_files', '(SELECT count(*) FROM '.File::TABLE.' WHERE task_id=tasks.id) AS nb_files',
'(SELECT count(*) FROM task_has_subtasks WHERE task_id=tasks.id) AS nb_subtasks', '(SELECT count(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id) AS nb_subtasks',
'(SELECT count(*) FROM task_has_subtasks WHERE task_id=tasks.id AND status=2) AS nb_completed_subtasks', '(SELECT count(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks',
'(SELECT count(*) FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id) AS nb_links', '(SELECT count(*) FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id) AS nb_links',
'tasks.id', 'tasks.id',
'tasks.reference', 'tasks.reference',

View file

@ -4,6 +4,7 @@ namespace Model;
use SimpleValidator\Validator; use SimpleValidator\Validator;
use SimpleValidator\Validators; use SimpleValidator\Validators;
use PicoDb\Table;
/** /**
* TaskLink model * TaskLink model
@ -57,6 +58,7 @@ class TaskLink extends Base
->join(Link::TABLE, 'id', 'link_id') ->join(Link::TABLE, 'id', 'link_id')
->join(Task::TABLE, 'id', 'opposite_task_id') ->join(Task::TABLE, 'id', 'opposite_task_id')
->join(Board::TABLE, 'id', 'column_id', Task::TABLE) ->join(Board::TABLE, 'id', 'column_id', Task::TABLE)
->orderBy(Link::TABLE.'.id ASC, '.Board::TABLE.'.position ASC, '.Task::TABLE.'.is_active DESC, '.Task::TABLE.'.id', Table::SORT_ASC)
->findAll(); ->findAll();
} }

View file

@ -21,9 +21,10 @@ class TaskPosition extends Base
* @param integer $column_id Column id * @param integer $column_id Column id
* @param integer $position Position (must be >= 1) * @param integer $position Position (must be >= 1)
* @param integer $swimlane_id Swimlane id * @param integer $swimlane_id Swimlane id
* @param boolean $fire_events Fire events
* @return boolean * @return boolean
*/ */
public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0) public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0, $fire_events = true)
{ {
$original_task = $this->taskFinder->getById($task_id); $original_task = $this->taskFinder->getById($task_id);
@ -35,8 +36,10 @@ class TaskPosition extends Base
$this->calculateAndSave($project_id, 0, $column_id, 1, $original_task['swimlane_id']); $this->calculateAndSave($project_id, 0, $column_id, 1, $original_task['swimlane_id']);
} }
if ($fire_events) {
$this->fireEvents($original_task, $column_id, $position, $swimlane_id); $this->fireEvents($original_task, $column_id, $position, $swimlane_id);
} }
}
return $result; return $result;
} }
@ -140,6 +143,9 @@ class TaskPosition extends Base
'position' => $new_position, 'position' => $new_position,
'column_id' => $new_column_id, 'column_id' => $new_column_id,
'swimlane_id' => $new_swimlane_id, 'swimlane_id' => $new_swimlane_id,
'src_column_id' => $task['column_id'],
'dst_column_id' => $new_column_id,
'date_moved' => $task['date_moved'],
); );
if ($task['swimlane_id'] != $new_swimlane_id) { if ($task['swimlane_id'] != $new_swimlane_id) {

View file

@ -0,0 +1,356 @@
<?php
namespace Model;
use DateTime;
use DateInterval;
/**
* Timetable
*
* @package model
* @author Frederic Guillot
*/
class Timetable extends Base
{
/**
* User time slots
*
* @access private
* @var array
*/
private $day;
private $week;
private $overtime;
private $timeoff;
/**
* Get a set of events by using the intersection between the timetable and the time tracking data
*
* @access public
* @param integer $user_id
* @param array $events Time tracking data
* @param string $start ISO8601 date
* @param string $end ISO8601 date
* @return array
*/
public function calculateEventsIntersect($user_id, array $events, $start, $end)
{
$start_dt = new DateTime($start);
$start_dt->setTime(0, 0);
$end_dt = new DateTime($end);
$end_dt->setTime(23, 59);
$timetable = $this->calculate($user_id, $start_dt, $end_dt);
// The user has no timetable
if (empty($this->week)) {
return $events;
}
$results = array();
foreach ($events as $event) {
$results = array_merge($results, $this->calculateEventIntersect($event, $timetable));
}
return $results;
}
/**
* Get a serie of events based on the timetable and the provided event
*
* @access public
* @param array $event
* @param array $timetable
* @return array
*/
public function calculateEventIntersect(array $event, array $timetable)
{
$events = array();
foreach ($timetable as $slot) {
$start_ts = $slot[0]->getTimestamp();
$end_ts = $slot[1]->getTimestamp();
if ($start_ts > $event['end']) {
break;
}
if ($event['start'] <= $start_ts) {
$event['start'] = $start_ts;
}
if ($event['start'] >= $start_ts && $event['start'] <= $end_ts) {
if ($event['end'] >= $end_ts) {
$events[] = array_merge($event, array('end' => $end_ts));
}
else {
$events[] = $event;
break;
}
}
}
return $events;
}
/**
* Calculate effective worked hours by taking into consideration the timetable
*
* @access public
* @param integer $user_id
* @param \DateTime $start
* @param \DateTime $end
* @return float
*/
public function calculateEffectiveDuration($user_id, DateTime $start, DateTime $end)
{
$end_timetable = clone($end);
$end_timetable->setTime(23, 59);
$timetable = $this->calculate($user_id, $start, $end_timetable);
$found_start = false;
$hours = 0;
// The user has no timetable
if (empty($this->week)) {
return $this->dateParser->getHours($start, $end);
}
foreach ($timetable as $slot) {
$isStartSlot = $this->dateParser->withinDateRange($start, $slot[0], $slot[1]);
$isEndSlot = $this->dateParser->withinDateRange($end, $slot[0], $slot[1]);
// Start and end are within the same time slot
if ($isStartSlot && $isEndSlot) {
return $this->dateParser->getHours($start, $end);
}
// We found the start slot
if (! $found_start && $isStartSlot) {
$found_start = true;
$hours = $this->dateParser->getHours($start, $slot[1]);
}
else if ($found_start) {
// We found the end slot
if ($isEndSlot) {
$hours += $this->dateParser->getHours($slot[0], $end);
break;
}
else {
// Sum hours of the intermediate time slots
$hours += $this->dateParser->getHours($slot[0], $slot[1]);
}
}
}
// The start date was not found in regular hours so we get the nearest time slot
if (! empty($timetable) && ! $found_start) {
$slot = $this->findClosestTimeSlot($start, $timetable);
if ($start < $slot[0]) {
return $this->calculateEffectiveDuration($user_id, $slot[0], $end);
}
}
return $hours;
}
/**
* Find the nearest time slot
*
* @access public
* @param DateTime $date
* @param array $timetable
* @return array
*/
public function findClosestTimeSlot(DateTime $date, array $timetable)
{
$values = array();
foreach ($timetable as $slot) {
$t1 = abs($slot[0]->getTimestamp() - $date->getTimestamp());
$t2 = abs($slot[1]->getTimestamp() - $date->getTimestamp());
$values[] = min($t1, $t2);
}
asort($values);
return $timetable[key($values)];
}
/**
* Get the timetable for a user for a given date range
*
* @access public
* @param integer $user_id
* @param \DateTime $start
* @param \DateTime $end
* @return array
*/
public function calculate($user_id, DateTime $start, DateTime $end)
{
$timetable = array();
$this->day = $this->timetableDay->getByUser($user_id);
$this->week = $this->timetableWeek->getByUser($user_id);
$this->overtime = $this->timetableExtra->getByUserAndDate($user_id, $start->format('Y-m-d'), $end->format('Y-m-d'));
$this->timeoff = $this->timetableOff->getByUserAndDate($user_id, $start->format('Y-m-d'), $end->format('Y-m-d'));
for ($today = clone($start); $today <= $end; $today->add(new DateInterval('P1D'))) {
$week_day = $today->format('N');
$timetable = array_merge($timetable, $this->getWeekSlots($today, $week_day));
$timetable = array_merge($timetable, $this->getOvertimeSlots($today, $week_day));
}
return $timetable;
}
/**
* Return worked time slots for the given day
*
* @access public
* @param \DateTime $today
* @param string $week_day
* @return array
*/
public function getWeekSlots(DateTime $today, $week_day)
{
$slots = array();
$dayoff = $this->getDayOff($today);
if (! empty($dayoff) && $dayoff['all_day'] == 1) {
return array();
}
foreach ($this->week as $slot) {
if ($week_day == $slot['day']) {
$slots = array_merge($slots, $this->getDayWorkSlots($slot, $dayoff, $today));
}
}
return $slots;
}
/**
* Get the overtime time slots for the given day
*
* @access public
* @param \DateTime $today
* @param string $week_day
* @return array
*/
public function getOvertimeSlots(DateTime $today, $week_day)
{
$slots = array();
foreach ($this->overtime as $slot) {
$day = new DateTime($slot['date']);
if ($week_day == $day->format('N')) {
if ($slot['all_day'] == 1) {
$slots = array_merge($slots, $this->getDaySlots($today));
}
else {
$slots[] = $this->getTimeSlot($slot, $day);
}
}
}
return $slots;
}
/**
* Get worked time slots and remove time off
*
* @access public
* @param array $slot
* @param array $dayoff
* @param \DateTime $today
* @return array
*/
public function getDayWorkSlots(array $slot, array $dayoff, DateTime $today)
{
$slots = array();
if (! empty($dayoff) && $dayoff['start'] < $slot['end']) {
if ($dayoff['start'] > $slot['start']) {
$slots[] = $this->getTimeSlot(array('end' => $dayoff['start']) + $slot, $today);
}
if ($dayoff['end'] < $slot['end']) {
$slots[] = $this->getTimeSlot(array('start' => $dayoff['end']) + $slot, $today);
}
}
else {
$slots[] = $this->getTimeSlot($slot, $today);
}
return $slots;
}
/**
* Get regular day work time slots
*
* @access public
* @param \DateTime $today
* @return array
*/
public function getDaySlots(DateTime $today)
{
$slots = array();
foreach ($this->day as $day) {
$slots[] = $this->getTimeSlot($day, $today);
}
return $slots;
}
/**
* Get the start and end time slot for a given day
*
* @access public
* @param array $slot
* @param \DateTime $today
* @return array
*/
public function getTimeSlot(array $slot, DateTime $today)
{
$date = $today->format('Y-m-d');
return array(
new DateTime($date.' '.$slot['start']),
new DateTime($date.' '.$slot['end']),
);
}
/**
* Return day off time slot
*
* @access public
* @param \DateTime $today
* @return array
*/
public function getDayOff(DateTime $today)
{
foreach ($this->timeoff as $day) {
if ($day['date'] === $today->format('Y-m-d')) {
return $day;
}
}
return array();
}
}

View file

@ -0,0 +1,87 @@
<?php
namespace Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Timetable Workweek
*
* @package model
* @author Frederic Guillot
*/
class TimetableDay extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'timetable_day';
/**
* Get the timetable for a given user
*
* @access public
* @param integer $user_id User id
* @return array
*/
public function getByUser($user_id)
{
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('start')->findAll();
}
/**
* Add a new time slot in the database
*
* @access public
* @param integer $user_id User id
* @param string $start Start hour (24h format)
* @param string $end End hour (24h format)
* @return boolean|integer
*/
public function create($user_id, $start, $end)
{
$values = array(
'user_id' => $user_id,
'start' => $start,
'end' => $end,
);
return $this->persist(self::TABLE, $values);
}
/**
* Remove a specific time slot
*
* @access public
* @param integer $slot_id
* @return boolean
*/
public function remove($slot_id)
{
return $this->db->table(self::TABLE)->eq('id', $slot_id)->remove();
}
/**
* 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, array(
new Validators\Required('user_id', t('Field required')),
new Validators\Required('start', t('Field required')),
new Validators\Required('end', t('Field required')),
));
return array(
$v->execute(),
$v->getErrors()
);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Timetable over-time
*
* @package model
* @author Frederic Guillot
*/
class TimetableExtra extends TimetableOff
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'timetable_extra';
}

View file

@ -0,0 +1,125 @@
<?php
namespace Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Timetable time off
*
* @package model
* @author Frederic Guillot
*/
class TimetableOff extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'timetable_off';
/**
* Get query to fetch everything (pagination)
*
* @access public
* @param integer $user_id User id
* @return \PicoDb\Table
*/
public function getUserQuery($user_id)
{
return $this->db->table(static::TABLE)->eq('user_id', $user_id);
}
/**
* Get the timetable for a given user
*
* @access public
* @param integer $user_id User id
* @return array
*/
public function getByUser($user_id)
{
return $this->db->table(static::TABLE)->eq('user_id', $user_id)->desc('date')->asc('start')->findAll();
}
/**
* Get the timetable for a given user
*
* @access public
* @param integer $user_id User id
* @param string $start_date
* @param string $end_date
* @return array
*/
public function getByUserAndDate($user_id, $start_date, $end_date)
{
return $this->db->table(static::TABLE)
->eq('user_id', $user_id)
->gte('date', $start_date)
->lte('date', $end_date)
->desc('date')
->asc('start')
->findAll();
}
/**
* Add a new time slot in the database
*
* @access public
* @param integer $user_id User id
* @param string $date Day (ISO8601 format)
* @param boolean $all_day All day flag
* @param float $start Start hour (24h format)
* @param float $end End hour (24h format)
* @param string $comment
* @return boolean|integer
*/
public function create($user_id, $date, $all_day, $start = '', $end = '', $comment = '')
{
$values = array(
'user_id' => $user_id,
'date' => $date,
'all_day' => (int) $all_day, // Postgres fix
'start' => $all_day ? '' : $start,
'end' => $all_day ? '' : $end,
'comment' => $comment,
);
return $this->persist(static::TABLE, $values);
}
/**
* Remove a specific time slot
*
* @access public
* @param integer $slot_id
* @return boolean
*/
public function remove($slot_id)
{
return $this->db->table(static::TABLE)->eq('id', $slot_id)->remove();
}
/**
* 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, array(
new Validators\Required('user_id', t('Field required')),
new Validators\Required('date', t('Field required')),
new Validators\Numeric('all_day', t('This value must be numeric')),
));
return array(
$v->execute(),
$v->getErrors()
);
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Timetable Workweek
*
* @package model
* @author Frederic Guillot
*/
class TimetableWeek extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'timetable_week';
/**
* Get the timetable for a given user
*
* @access public
* @param integer $user_id User id
* @return array
*/
public function getByUser($user_id)
{
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('day')->asc('start')->findAll();
}
/**
* Add a new time slot in the database
*
* @access public
* @param integer $user_id User id
* @param string $day Day of the week (ISO-8601)
* @param string $start Start hour (24h format)
* @param string $end End hour (24h format)
* @return boolean|integer
*/
public function create($user_id, $day, $start, $end)
{
$values = array(
'user_id' => $user_id,
'day' => $day,
'start' => $start,
'end' => $end,
);
return $this->persist(self::TABLE, $values);
}
/**
* Remove a specific time slot
*
* @access public
* @param integer $slot_id
* @return boolean
*/
public function remove($slot_id)
{
return $this->db->table(self::TABLE)->eq('id', $slot_id)->remove();
}
/**
* 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, array(
new Validators\Required('user_id', t('Field required')),
new Validators\Required('day', t('Field required')),
new Validators\Numeric('day', t('This value must be numeric')),
new Validators\Required('start', t('Field required')),
new Validators\Required('end', t('Field required')),
));
return array(
$v->execute(),
$v->getErrors()
);
}
}

View file

@ -0,0 +1,170 @@
<?php
namespace Model;
/**
* Transition model
*
* @package model
* @author Frederic Guillot
*/
class Transition extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'transitions';
/**
* Save transition event
*
* @access public
* @param integer $user_id
* @param array $task
* @return boolean
*/
public function save($user_id, array $task)
{
return $this->db->table(self::TABLE)->insert(array(
'user_id' => $user_id,
'project_id' => $task['project_id'],
'task_id' => $task['task_id'],
'src_column_id' => $task['src_column_id'],
'dst_column_id' => $task['dst_column_id'],
'date' => time(),
'time_spent' => time() - $task['date_moved']
));
}
/**
* Get all transitions by task
*
* @access public
* @param integer $task_id
* @return array
*/
public function getAllByTask($task_id)
{
return $this->db->table(self::TABLE)
->columns(
'src.title as src_column',
'dst.title as dst_column',
User::TABLE.'.name',
User::TABLE.'.username',
self::TABLE.'.user_id',
self::TABLE.'.date',
self::TABLE.'.time_spent'
)
->eq('task_id', $task_id)
->desc('date')
->join(User::TABLE, 'id', 'user_id')
->join(Board::TABLE.' as src', 'id', 'src_column_id', self::TABLE, 'src')
->join(Board::TABLE.' as dst', 'id', 'dst_column_id', self::TABLE, 'dst')
->findAll();
}
/**
* Get all transitions by project
*
* @access public
* @param integer $project_id
* @param mixed $from Start date (timestamp or user formatted date)
* @param mixed $to End date (timestamp or user formatted date)
* @return array
*/
public function getAllByProjectAndDate($project_id, $from, $to)
{
if (! is_numeric($from)) {
$from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from));
}
if (! is_numeric($to)) {
$to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to)));
}
return $this->db->table(self::TABLE)
->columns(
Task::TABLE.'.id',
Task::TABLE.'.title',
'src.title as src_column',
'dst.title as dst_column',
User::TABLE.'.name',
User::TABLE.'.username',
self::TABLE.'.user_id',
self::TABLE.'.date',
self::TABLE.'.time_spent'
)
->gte('date', $from)
->lte('date', $to)
->eq(self::TABLE.'.project_id', $project_id)
->desc('date')
->join(Task::TABLE, 'id', 'task_id')
->join(User::TABLE, 'id', 'user_id')
->join(Board::TABLE.' as src', 'id', 'src_column_id', self::TABLE, 'src')
->join(Board::TABLE.' as dst', 'id', 'dst_column_id', self::TABLE, 'dst')
->findAll();
}
/**
* Get project export
*
* @access public
* @param integer $project_id Project id
* @param mixed $from Start date (timestamp or user formatted date)
* @param mixed $to End date (timestamp or user formatted date)
* @return array
*/
public function export($project_id, $from, $to)
{
$results = array($this->getColumns());
$transitions = $this->getAllByProjectAndDate($project_id, $from, $to);
foreach ($transitions as $transition) {
$results[] = $this->format($transition);
}
return $results;
}
/**
* Get column titles
*
* @access public
* @return string[]
*/
public function getColumns()
{
return array(
e('Id'),
e('Task Title'),
e('Source column'),
e('Destination column'),
e('Executer'),
e('Date'),
e('Time spent'),
);
}
/**
* Format the output of a transition array
*
* @access public
* @param array $transition
* @return array
*/
public function format(array $transition)
{
$values = array();
$values[] = $transition['id'];
$values[] = $transition['title'];
$values[] = $transition['src_column'];
$values[] = $transition['dst_column'];
$values[] = $transition['name'] ?: $transition['username'];
$values[] = date('Y-m-d H:i', $transition['date']);
$values[] = round($transition['time_spent'] / 3600, 2);
return $values;
}
}

View file

@ -60,7 +60,8 @@ class User extends Base
'is_ldap_user', 'is_ldap_user',
'notifications_enabled', 'notifications_enabled',
'google_id', 'google_id',
'github_id' 'github_id',
'twofactor_activated'
); );
} }

View file

@ -28,14 +28,41 @@ class UserSession extends Base
unset($user['password']); unset($user['password']);
} }
if (isset($user['twofactor_secret'])) {
unset($user['twofactor_secret']);
}
$user['id'] = (int) $user['id']; $user['id'] = (int) $user['id'];
$user['default_project_id'] = (int) $user['default_project_id']; $user['default_project_id'] = (int) $user['default_project_id'];
$user['is_admin'] = (bool) $user['is_admin']; $user['is_admin'] = (bool) $user['is_admin'];
$user['is_ldap_user'] = (bool) $user['is_ldap_user']; $user['is_ldap_user'] = (bool) $user['is_ldap_user'];
$user['twofactor_activated'] = (bool) $user['twofactor_activated'];
$this->session['user'] = $user; $this->session['user'] = $user;
} }
/**
* Return true if the user has validated the 2FA key
*
* @access public
* @return bool
*/
public function check2FA()
{
return isset($this->session['2fa_validated']) && $this->session['2fa_validated'] === true;
}
/**
* Return true if the user has 2FA enabled
*
* @access public
* @return bool
*/
public function has2FA()
{
return isset($this->session['user']['twofactor_activated']) && $this->session['user']['twofactor_activated'] === true;
}
/** /**
* Return true if the logged user is admin * Return true if the logged user is admin
* *

View file

@ -10,27 +10,6 @@ namespace Model;
*/ */
class Webhook extends Base class Webhook extends Base
{ {
/**
* HTTP connection timeout in seconds
*
* @var integer
*/
const HTTP_TIMEOUT = 1;
/**
* Number of maximum redirections for the HTTP client
*
* @var integer
*/
const HTTP_MAX_REDIRECTS = 3;
/**
* HTTP client user agent
*
* @var string
*/
const HTTP_USER_AGENT = 'Kanboard Webhook';
/** /**
* Call the external URL * Call the external URL
* *
@ -42,22 +21,6 @@ class Webhook extends Base
{ {
$token = $this->config->get('webhook_token'); $token = $this->config->get('webhook_token');
$headers = array(
'Connection: close',
'User-Agent: '.self::HTTP_USER_AGENT,
);
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'protocol_version' => 1.1,
'timeout' => self::HTTP_TIMEOUT,
'max_redirects' => self::HTTP_MAX_REDIRECTS,
'header' => implode("\r\n", $headers),
'content' => json_encode($task)
)
));
if (strpos($url, '?') !== false) { if (strpos($url, '?') !== false) {
$url .= '&token='.$token; $url .= '&token='.$token;
} }
@ -65,6 +28,6 @@ class Webhook extends Base
$url .= '?token='.$token; $url .= '?token='.$token;
} }
@file_get_contents($url, false, $context); return $this->httpClient->post($url, $task);
} }
} }

View file

@ -6,7 +6,190 @@ use PDO;
use Core\Security; use Core\Security;
use Model\Link; use Model\Link;
const VERSION = 46; const VERSION = 61;
function version_61($pdo)
{
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated TINYINT(1) DEFAULT 0');
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret CHAR(16)');
}
function version_60($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_gravatar', '0'));
}
function version_59($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_hipchat', '0'));
$rq->execute(array('integration_hipchat_api_url', 'https://api.hipchat.com'));
$rq->execute(array('integration_hipchat_room_id', ''));
$rq->execute(array('integration_hipchat_room_token', ''));
}
function version_58($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_slack_webhook', '0'));
$rq->execute(array('integration_slack_webhook_url', ''));
}
function version_57($pdo)
{
$pdo->exec('CREATE TABLE currencies (`currency` CHAR(3) NOT NULL UNIQUE, `rate` FLOAT DEFAULT 0) ENGINE=InnoDB CHARSET=utf8');
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('application_currency', 'USD'));
}
function version_56($pdo)
{
$pdo->exec('CREATE TABLE transitions (
`id` INT NOT NULL AUTO_INCREMENT,
`user_id` INT NOT NULL,
`project_id` INT NOT NULL,
`task_id` INT NOT NULL,
`src_column_id` INT NOT NULL,
`dst_column_id` INT NOT NULL,
`date` INT NOT NULL,
`time_spent` INT DEFAULT 0,
FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE,
FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
PRIMARY KEY(id)
) ENGINE=InnoDB CHARSET=utf8');
$pdo->exec("CREATE INDEX transitions_task_index ON transitions(task_id)");
$pdo->exec("CREATE INDEX transitions_project_index ON transitions(project_id)");
$pdo->exec("CREATE INDEX transitions_user_index ON transitions(user_id)");
}
function version_55($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('subtask_forecast', '0'));
}
function version_54($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('application_stylesheet', ''));
}
function version_53($pdo)
{
$pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent FLOAT DEFAULT 0");
}
function version_52($pdo)
{
$pdo->exec('CREATE TABLE budget_lines (
`id` INT NOT NULL AUTO_INCREMENT,
`project_id` INT NOT NULL,
`amount` FLOAT NOT NULL,
`date` VARCHAR(10) NOT NULL,
`comment` TEXT,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
PRIMARY KEY(id)
) ENGINE=InnoDB CHARSET=utf8');
}
function version_51($pdo)
{
$pdo->exec('CREATE TABLE timetable_day (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
start VARCHAR(5) NOT NULL,
end VARCHAR(5) NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY(id)
) ENGINE=InnoDB CHARSET=utf8');
$pdo->exec('CREATE TABLE timetable_week (
id INT NOT NULL AUTO_INCREMENT,
user_id INTEGER NOT NULL,
day INT NOT NULL,
start VARCHAR(5) NOT NULL,
end VARCHAR(5) NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY(id)
) ENGINE=InnoDB CHARSET=utf8');
$pdo->exec('CREATE TABLE timetable_off (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
date VARCHAR(10) NOT NULL,
all_day TINYINT(1) DEFAULT 0,
start VARCHAR(5) DEFAULT 0,
end VARCHAR(5) DEFAULT 0,
comment TEXT,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY(id)
) ENGINE=InnoDB CHARSET=utf8');
$pdo->exec('CREATE TABLE timetable_extra (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
date VARCHAR(10) NOT NULL,
all_day TINYINT(1) DEFAULT 0,
start VARCHAR(5) DEFAULT 0,
end VARCHAR(5) DEFAULT 0,
comment TEXT,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY(id)
) ENGINE=InnoDB CHARSET=utf8');
}
function version_50($pdo)
{
$pdo->exec("CREATE TABLE hourly_rates (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
rate FLOAT DEFAULT 0,
date_effective INTEGER NOT NULL,
currency CHAR(3) NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY(id)
) ENGINE=InnoDB CHARSET=utf8");
}
function version_49($pdo)
{
$pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
$task_id = 0;
$position = 1;
$urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
$rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
$rq->execute();
foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
if ($task_id != $subtask['task_id']) {
$position = 1;
$task_id = $subtask['task_id'];
}
$urq->execute(array($position, $subtask['id']));
$position++;
}
}
function version_48($pdo)
{
$pdo->exec('RENAME TABLE task_has_files TO files');
$pdo->exec('RENAME TABLE task_has_subtasks TO subtasks');
}
function version_47($pdo)
{
$pdo->exec('ALTER TABLE projects ADD COLUMN description TEXT');
}
function version_46($pdo) function version_46($pdo)
{ {

View file

@ -6,7 +6,183 @@ use PDO;
use Core\Security; use Core\Security;
use Model\Link; use Model\Link;
const VERSION = 27; const VERSION = 42;
function version_42($pdo)
{
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated BOOLEAN DEFAULT \'0\'');
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret CHAR(16)');
}
function version_41($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_gravatar', '0'));
}
function version_40($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_hipchat', '0'));
$rq->execute(array('integration_hipchat_api_url', 'https://api.hipchat.com'));
$rq->execute(array('integration_hipchat_room_id', ''));
$rq->execute(array('integration_hipchat_room_token', ''));
}
function version_39($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_slack_webhook', '0'));
$rq->execute(array('integration_slack_webhook_url', ''));
}
function version_38($pdo)
{
$pdo->exec('CREATE TABLE currencies ("currency" CHAR(3) NOT NULL UNIQUE, "rate" REAL DEFAULT 0)');
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('application_currency', 'USD'));
}
function version_37($pdo)
{
$pdo->exec('CREATE TABLE transitions (
"id" SERIAL PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"project_id" INTEGER NOT NULL,
"task_id" INTEGER NOT NULL,
"src_column_id" INTEGER NOT NULL,
"dst_column_id" INTEGER NOT NULL,
"date" INTEGER NOT NULL,
"time_spent" INTEGER DEFAULT 0,
FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE,
FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
)');
$pdo->exec("CREATE INDEX transitions_task_index ON transitions(task_id)");
$pdo->exec("CREATE INDEX transitions_project_index ON transitions(project_id)");
$pdo->exec("CREATE INDEX transitions_user_index ON transitions(user_id)");
}
function version_36($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('subtask_forecast', '0'));
}
function version_35($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('application_stylesheet', ''));
}
function version_34($pdo)
{
$pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent REAL DEFAULT 0");
}
function version_33($pdo)
{
$pdo->exec('CREATE TABLE budget_lines (
"id" SERIAL PRIMARY KEY,
"project_id" INTEGER NOT NULL,
"amount" REAL NOT NULL,
"date" VARCHAR(10) NOT NULL,
"comment" TEXT,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
)');
}
function version_32($pdo)
{
$pdo->exec('CREATE TABLE timetable_day (
"id" SERIAL PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"start" VARCHAR(5) NOT NULL,
"end" VARCHAR(5) NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)');
$pdo->exec('CREATE TABLE timetable_week (
"id" SERIAL PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"day" INTEGER NOT NULL,
"start" VARCHAR(5) NOT NULL,
"end" VARCHAR(5) NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)');
$pdo->exec('CREATE TABLE timetable_off (
"id" SERIAL PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"date" VARCHAR(10) NOT NULL,
"all_day" BOOLEAN DEFAULT \'0\',
"start" VARCHAR(5) DEFAULT 0,
"end" VARCHAR(5) DEFAULT 0,
"comment" TEXT,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)');
$pdo->exec('CREATE TABLE timetable_extra (
"id" SERIAL PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"date" VARCHAR(10) NOT NULL,
"all_day" BOOLEAN DEFAULT \'0\',
"start" VARCHAR(5) DEFAULT 0,
"end" VARCHAR(5) DEFAULT 0,
"comment" TEXT,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)');
}
function version_31($pdo)
{
$pdo->exec("CREATE TABLE hourly_rates (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
rate REAL DEFAULT 0,
date_effective INTEGER NOT NULL,
currency CHAR(3) NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)");
}
function version_30($pdo)
{
$pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
$task_id = 0;
$position = 1;
$urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
$rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
$rq->execute();
foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
if ($task_id != $subtask['task_id']) {
$position = 1;
$task_id = $subtask['task_id'];
}
$urq->execute(array($position, $subtask['id']));
$position++;
}
}
function version_29($pdo)
{
$pdo->exec('ALTER TABLE task_has_files RENAME TO files');
$pdo->exec('ALTER TABLE task_has_subtasks RENAME TO subtasks');
}
function version_28($pdo)
{
$pdo->exec('ALTER TABLE projects ADD COLUMN description TEXT');
}
function version_27($pdo) function version_27($pdo)
{ {

View file

@ -6,7 +6,185 @@ use Core\Security;
use PDO; use PDO;
use Model\Link; use Model\Link;
const VERSION = 45; const VERSION = 60;
function version_60($pdo)
{
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated INTEGER DEFAULT 0');
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret TEXT');
}
function version_59($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_gravatar', '0'));
}
function version_58($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_hipchat', '0'));
$rq->execute(array('integration_hipchat_api_url', 'https://api.hipchat.com'));
$rq->execute(array('integration_hipchat_room_id', ''));
$rq->execute(array('integration_hipchat_room_token', ''));
}
function version_57($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('integration_slack_webhook', '0'));
$rq->execute(array('integration_slack_webhook_url', ''));
}
function version_56($pdo)
{
$pdo->exec('CREATE TABLE currencies ("currency" TEXT NOT NULL UNIQUE, "rate" REAL DEFAULT 0)');
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('application_currency', 'USD'));
}
function version_55($pdo)
{
$pdo->exec('CREATE TABLE transitions (
"id" INTEGER PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"project_id" INTEGER NOT NULL,
"task_id" INTEGER NOT NULL,
"src_column_id" INTEGER NOT NULL,
"dst_column_id" INTEGER NOT NULL,
"date" INTEGER NOT NULL,
"time_spent" INTEGER DEFAULT 0,
FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE,
FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
)');
$pdo->exec("CREATE INDEX transitions_task_index ON transitions(task_id)");
$pdo->exec("CREATE INDEX transitions_project_index ON transitions(project_id)");
$pdo->exec("CREATE INDEX transitions_user_index ON transitions(user_id)");
}
function version_54($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('subtask_forecast', '0'));
}
function version_53($pdo)
{
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('application_stylesheet', ''));
}
function version_52($pdo)
{
$pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent REAL DEFAULT 0");
}
function version_51($pdo)
{
$pdo->exec('CREATE TABLE budget_lines (
"id" INTEGER PRIMARY KEY,
"project_id" INTEGER NOT NULL,
"amount" REAL NOT NULL,
"date" TEXT NOT NULL,
"comment" TEXT,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
)');
}
function version_50($pdo)
{
$pdo->exec('CREATE TABLE timetable_day (
"id" INTEGER PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"start" TEXT NOT NULL,
"end" TEXT NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)');
$pdo->exec('CREATE TABLE timetable_week (
"id" INTEGER PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"day" INTEGER NOT NULL,
"start" TEXT NOT NULL,
"end" TEXT NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)');
$pdo->exec('CREATE TABLE timetable_off (
"id" INTEGER PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"date" TEXT NOT NULL,
"all_day" INTEGER DEFAULT 0,
"start" TEXT DEFAULT 0,
"end" TEXT DEFAULT 0,
"comment" TEXT,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)');
$pdo->exec('CREATE TABLE timetable_extra (
"id" INTEGER PRIMARY KEY,
"user_id" INTEGER NOT NULL,
"date" TEXT NOT NULL,
"all_day" INTEGER DEFAULT 0,
"start" TEXT DEFAULT 0,
"end" TEXT DEFAULT 0,
"comment" TEXT,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)');
}
function version_49($pdo)
{
$pdo->exec("CREATE TABLE hourly_rates (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
rate REAL DEFAULT 0,
date_effective INTEGER NOT NULL,
currency TEXT NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
)");
}
function version_48($pdo)
{
$pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
// Migrate all subtasks position
$task_id = 0;
$position = 1;
$urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
$rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
$rq->execute();
foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
if ($task_id != $subtask['task_id']) {
$position = 1;
$task_id = $subtask['task_id'];
}
$urq->execute(array($position, $subtask['id']));
$position++;
}
}
function version_47($pdo)
{
$pdo->exec('ALTER TABLE task_has_files RENAME TO files');
$pdo->exec('ALTER TABLE task_has_subtasks RENAME TO subtasks');
}
function version_46($pdo)
{
$pdo->exec('ALTER TABLE projects ADD COLUMN description TEXT');
}
function version_45($pdo) function version_45($pdo)
{ {

View file

@ -17,12 +17,15 @@ class ClassProvider implements ServiceProviderInterface
'Action', 'Action',
'Authentication', 'Authentication',
'Board', 'Board',
'Budget',
'Category', 'Category',
'Color', 'Color',
'Comment', 'Comment',
'Config', 'Config',
'Currency',
'DateParser', 'DateParser',
'File', 'File',
'HourlyRate',
'LastLogin', 'LastLogin',
'Link', 'Link',
'Notification', 'Notification',
@ -34,6 +37,7 @@ class ClassProvider implements ServiceProviderInterface
'ProjectPermission', 'ProjectPermission',
'Subtask', 'Subtask',
'SubtaskExport', 'SubtaskExport',
'SubtaskForecast',
'SubtaskTimeTracking', 'SubtaskTimeTracking',
'Swimlane', 'Swimlane',
'Task', 'Task',
@ -48,7 +52,12 @@ class ClassProvider implements ServiceProviderInterface
'TaskPosition', 'TaskPosition',
'TaskStatus', 'TaskStatus',
'TaskValidator', 'TaskValidator',
'TimeTracking', 'Timetable',
'TimetableDay',
'TimetableWeek',
'TimetableOff',
'TimetableExtra',
'Transition',
'User', 'User',
'UserSession', 'UserSession',
'Webhook', 'Webhook',
@ -60,11 +69,14 @@ class ClassProvider implements ServiceProviderInterface
'MemoryCache', 'MemoryCache',
'FileCache', 'FileCache',
'Request', 'Request',
'HttpClient',
), ),
'Integration' => array( 'Integration' => array(
'GitlabWebhook', 'GitlabWebhook',
'GithubWebhook', 'GithubWebhook',
'BitbucketWebhook', 'BitbucketWebhook',
'Hipchat',
'SlackWebhook',
) )
); );

View file

@ -79,6 +79,7 @@ class DatabaseProvider implements ServiceProviderInterface
'password' => DB_PASSWORD, 'password' => DB_PASSWORD,
'database' => DB_NAME, 'database' => DB_NAME,
'charset' => 'utf8', 'charset' => 'utf8',
'port' => DB_PORT,
)); ));
} }
@ -97,6 +98,7 @@ class DatabaseProvider implements ServiceProviderInterface
'username' => DB_USERNAME, 'username' => DB_USERNAME,
'password' => DB_PASSWORD, 'password' => DB_PASSWORD,
'database' => DB_NAME, 'database' => DB_NAME,
'port' => DB_PORT,
)); ));
} }
} }

View file

@ -14,6 +14,7 @@ use Subscriber\ProjectModificationDateSubscriber;
use Subscriber\WebhookSubscriber; use Subscriber\WebhookSubscriber;
use Subscriber\SubtaskTimesheetSubscriber; use Subscriber\SubtaskTimesheetSubscriber;
use Subscriber\TaskMovedDateSubscriber; use Subscriber\TaskMovedDateSubscriber;
use Subscriber\TransitionSubscriber;
class EventDispatcherProvider implements ServiceProviderInterface class EventDispatcherProvider implements ServiceProviderInterface
{ {
@ -29,6 +30,7 @@ class EventDispatcherProvider implements ServiceProviderInterface
$container['dispatcher']->addSubscriber(new NotificationSubscriber($container)); $container['dispatcher']->addSubscriber(new NotificationSubscriber($container));
$container['dispatcher']->addSubscriber(new SubtaskTimesheetSubscriber($container)); $container['dispatcher']->addSubscriber(new SubtaskTimesheetSubscriber($container));
$container['dispatcher']->addSubscriber(new TaskMovedDateSubscriber($container)); $container['dispatcher']->addSubscriber(new TaskMovedDateSubscriber($container));
$container['dispatcher']->addSubscriber(new TransitionSubscriber($container));
// Automatic actions // Automatic actions
$container['action']->attachEvents(); $container['action']->attachEvents();

View file

@ -10,6 +10,8 @@ use Pimple\Container;
* @package subscriber * @package subscriber
* @author Frederic Guillot * @author Frederic Guillot
* *
* @property \Integration\SlackWebhook $slackWebhook
* @property \Integration\Hipchat $hipchat
* @property \Model\Board $board * @property \Model\Board $board
* @property \Model\Config $config * @property \Model\Config $config
* @property \Model\Comment $comment * @property \Model\Comment $comment

View file

@ -47,12 +47,15 @@ class NotificationSubscriber extends Base implements EventSubscriberInterface
public function execute(GenericEvent $event, $event_name) public function execute(GenericEvent $event, $event_name)
{ {
$values = $this->getTemplateData($event); $values = $this->getTemplateData($event);
if (isset($values['task']['project_id'])) {
$users = $this->notification->getUsersList($values['task']['project_id']); $users = $this->notification->getUsersList($values['task']['project_id']);
if ($users) { if (! empty($users)) {
$this->notification->sendEmails($this->templates[$event_name], $users, $values); $this->notification->sendEmails($this->templates[$event_name], $users, $values);
} }
} }
}
public function getTemplateData(GenericEvent $event) public function getTemplateData(GenericEvent $event)
{ {
@ -64,11 +67,11 @@ class NotificationSubscriber extends Base implements EventSubscriberInterface
break; break;
case 'Event\SubtaskEvent': case 'Event\SubtaskEvent':
$values['subtask'] = $this->subtask->getById($event['id'], true); $values['subtask'] = $this->subtask->getById($event['id'], true);
$values['task'] = $this->taskFinder->getDetails($event['task_id']); $values['task'] = $this->taskFinder->getDetails($values['subtask']['task_id']);
break; break;
case 'Event\FileEvent': case 'Event\FileEvent':
$values['file'] = $event->getAll(); $values['file'] = $event->getAll();
$values['task'] = $this->taskFinder->getDetails($event['task_id']); $values['task'] = $this->taskFinder->getDetails($values['file']['task_id']);
break; break;
case 'Event\CommentEvent': case 'Event\CommentEvent':
$values['comment'] = $this->comment->getById($event['id']); $values['comment'] = $this->comment->getById($event['id']);

View file

@ -41,6 +41,33 @@ class ProjectActivitySubscriber extends Base implements EventSubscriberInterface
$event_name, $event_name,
$values $values
); );
$this->sendSlackNotification($event_name, $values);
$this->sendHipchatNotification($event_name, $values);
}
}
private function sendSlackNotification($event_name, array $values)
{
if ($this->config->get('integration_slack_webhook') == 1) {
$this->slackWebhook->notify(
$values['task']['project_id'],
$values['task']['id'],
$event_name,
$values
);
}
}
private function sendHipchatNotification($event_name, array $values)
{
if ($this->config->get('integration_hipchat') == 1) {
$this->hipchat->notify(
$values['task']['project_id'],
$values['task']['id'],
$event_name,
$values
);
} }
} }

View file

@ -0,0 +1,26 @@
<?php
namespace Subscriber;
use Event\TaskEvent;
use Model\Task;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class TransitionSubscriber extends Base implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
Task::EVENT_MOVE_COLUMN => array('execute', 0),
);
}
public function execute(TaskEvent $event)
{
$user_id = $this->userSession->getId();
if (! empty($user_id)) {
$this->transition->save($user_id, $event->getAll());
}
}
}

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