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

Update to kanboard v1.0.27

This commit is contained in:
mbugeia 2016-04-27 20:50:42 +02:00
parent 00a82fdf05
commit fff59a2e84
570 changed files with 9329 additions and 8452 deletions

View file

@ -1,11 +1,56 @@
Version 1.0.27
--------------
New features:
* Added Markdown editor
* Added user avatars with pluggable system
- Default is a letter based avatar
- Gravatar
- Avatar Image upload
* Added Korean translation
Improvements:
* Added more logging for LDAP client
* Improve schema migration process
* Improve notification configuration form
* Handle state in OAuth2 client
* Allow to use the original template in overridden templates
* Unification of the project header
* Refactoring of Javascript code
* Improve comments design
* Improve task summary sections
* Put back the action sidebar in task view
* Added support for multiple placeholders for LDAP_USER_FILTER
* Added local file link provider
* Show configuration in settings page
* Added "?" to display list of keyboard shortcuts
* Added new keyboard shortcuts for task view
* Always display project name and task title in task views
* Improve automatic action creation
* Move notifications to the bottom of the screen
* Added the possibility to import automatic actions from another project
* Added Ajax loading icon for submit buttons
* Added support for HTTP header "X-Forwarded-Proto: https"
Bug fixes:
* Fix bad unique constraints in Mysql table user_has_notifications
* Force integer type for aggregated metrics (Burndown chart concat values instead of summing)
* Fixes cycle time calculation when the start date is defined in the future
* Access allowed to any tasks from the shared public board by changing the URL parameters
* Fix invalid user filter for API procedure createLdapUser()
* Ambiguous column name with very old version of Sqlite
Version 1.0.26 Version 1.0.26
-------------- --------------
Breaking changes: Breaking changes:
* API procedures: * API procedures:
- "moveColumnUp" and "moveColumnDown" are replace by "changeColumnPosition" - "moveColumnUp" and "moveColumnDown" are replaced by "changeColumnPosition"
- "moveSwimlaneUp" and "moveSwimlaneDown" are replace by "changeSwimlanePosition" - "moveSwimlaneUp" and "moveSwimlaneDown" are replaced by "changeSwimlanePosition"
New features: New features:

View file

@ -132,6 +132,7 @@ abstract class Base extends \Kanboard\Core\Base
* Set project id * Set project id
* *
* @access public * @access public
* @param integer $project_id
* @return Base * @return Base
*/ */
public function setProjectId($project_id) public function setProjectId($project_id)
@ -154,10 +155,10 @@ abstract class Base extends \Kanboard\Core\Base
/** /**
* Set an user defined parameter * Set an user defined parameter
* *
* @access public * @access public
* @param string $name Parameter name * @param string $name Parameter name
* @param mixed $value Value * @param mixed $value Value
* @param Base * @return Base
*/ */
public function setParam($name, $value) public function setParam($name, $value)
{ {
@ -271,6 +272,7 @@ abstract class Base extends \Kanboard\Core\Base
* @access public * @access public
* @param string $event * @param string $event
* @param string $description * @param string $description
* @return Base
*/ */
public function addEvent($event, $description = '') public function addEvent($event, $description = '')
{ {

View file

@ -85,20 +85,22 @@ class AverageLeadCycleTimeAnalytic extends Base
*/ */
private function calculateCycleTime(array &$task) private function calculateCycleTime(array &$task)
{ {
if (empty($task['date_started'])) { $end = (int) $task['date_completed'] ?: time();
return 0; $start = (int) $task['date_started'];
// Start date can be in the future when defined with the Gantt chart
if ($start > 0 && $end > $start) {
return $end - $start;
} }
$end = $task['date_completed'] ?: time(); return 0;
$start = $task['date_started'];
return $end - $start;
} }
/** /**
* Get the 1000 last created tasks * Get the 1000 last created tasks
* *
* @access private * @access private
* @param integer $project_id
* @return array * @return array
*/ */
private function getTasks($project_id) private function getTasks($project_id)

View file

@ -126,6 +126,7 @@ class AverageTimeSpentColumnAnalytic extends Base
* *
* @access private * @access private
* @param array $task * @param array $task
* @return integer
*/ */
private function getTaskTimeSpentInCurrentColumn(array &$task) private function getTaskTimeSpentInCurrentColumn(array &$task)
{ {

View file

@ -31,6 +31,7 @@ class Auth extends Base
} elseif ($this->isAppAuthenticated($username, $password)) { } elseif ($this->isAppAuthenticated($username, $password)) {
$this->checkProcedurePermission(false, $method); $this->checkProcedurePermission(false, $method);
} else { } else {
$this->logger->error('API authentication failure for '.$username);
throw new AuthenticationFailure('Wrong credentials'); throw new AuthenticationFailure('Wrong credentials');
} }
} }

View file

@ -66,12 +66,29 @@ class User extends \Kanboard\Core\Base
return $valid ? $this->user->create($values) : false; return $valid ? $this->user->create($values) : false;
} }
/**
* Create LDAP user in the database
*
* Only "anonymous" and "proxy" LDAP authentication are supported by this method
*
* User information will be fetched from the LDAP server
*
* @access public
* @param string $username
* @return bool|int
*/
public function createLdapUser($username) public function createLdapUser($username)
{ {
if (LDAP_BIND_TYPE === 'user') {
$this->logger->error('LDAP authentication "user" is not supported by this API call');
return false;
}
try { try {
$ldap = LdapClient::connect(); $ldap = LdapClient::connect();
$user = LdapUser::getUser($ldap, sprintf(LDAP_USER_FILTER, $username)); $ldap->setLogger($this->logger);
$user = LdapUser::getUser($ldap, $username);
if ($user === null) { if ($user === null) {
$this->logger->info('User not found in LDAP server'); $this->logger->info('User not found in LDAP server');

View file

@ -63,10 +63,12 @@ class LdapAuth extends Base implements PasswordAuthenticationProviderInterface
try { try {
$client = LdapClient::connect($this->getLdapUsername(), $this->getLdapPassword()); $client = LdapClient::connect($this->getLdapUsername(), $this->getLdapPassword());
$client->setLogger($this->logger);
$user = LdapUser::getUser($client, $this->username); $user = LdapUser::getUser($client, $this->username);
if ($user === null) { if ($user === null) {
$this->logger->info('User not found in LDAP server'); $this->logger->info('User ('.$this->username.') not found in LDAP server');
return false; return false;
} }
@ -74,6 +76,8 @@ class LdapAuth extends Base implements PasswordAuthenticationProviderInterface
throw new LogicException('Username not found in LDAP profile, check the parameter LDAP_USER_ATTRIBUTE_USERNAME'); throw new LogicException('Username not found in LDAP profile, check the parameter LDAP_USER_ATTRIBUTE_USERNAME');
} }
$this->logger->info('Authenticate user: '.$user->getDn());
if ($client->authenticate($user->getDn(), $this->password)) { if ($client->authenticate($user->getDn(), $this->password)) {
$this->userInfo = $user; $this->userInfo = $user;
return true; return true;

View file

@ -11,18 +11,18 @@ use Symfony\Component\Console\Command\Command;
* @package console * @package console
* @author Frederic Guillot * @author Frederic Guillot
* *
* @property \Kanboard\Export\SubtaskExport $subtaskExport
* @property \Kanboard\Export\TaskExport $taskExport
* @property \Kanboard\Export\TransitionExport $transitionExport
* @property \Kanboard\Model\Notification $notification * @property \Kanboard\Model\Notification $notification
* @property \Kanboard\Model\Project $project * @property \Kanboard\Model\Project $project
* @property \Kanboard\Model\ProjectPermission $projectPermission * @property \Kanboard\Model\ProjectPermission $projectPermission
* @property \Kanboard\Model\ProjectAnalytic $projectAnalytic
* @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats * @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats
* @property \Kanboard\Model\ProjectDailyStats $projectDailyStats * @property \Kanboard\Model\ProjectDailyStats $projectDailyStats
* @property \Kanboard\Model\SubtaskExport $subtaskExport
* @property \Kanboard\Model\OverdueNotification $overdueNotification
* @property \Kanboard\Model\Task $task * @property \Kanboard\Model\Task $task
* @property \Kanboard\Model\TaskExport $taskExport
* @property \Kanboard\Model\TaskFinder $taskFinder * @property \Kanboard\Model\TaskFinder $taskFinder
* @property \Kanboard\Model\Transition $transition * @property \Kanboard\Model\UserNotification $userNotification
* @property \Kanboard\Model\UserNotificationFilter $userNotificationFilter
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
*/ */
abstract class Base extends Command abstract class Base extends Command

View file

@ -2,6 +2,7 @@
namespace Kanboard\Console; namespace Kanboard\Console;
use Kanboard\Model\Task;
use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@ -19,7 +20,7 @@ class TaskOverdueNotification extends Base
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$tasks = $this->overdueNotification->sendOverdueTaskNotifications(); $tasks = $this->sendOverdueTaskNotifications();
if ($input->getOption('show')) { if ($input->getOption('show')) {
$this->showTable($output, $tasks); $this->showTable($output, $tasks);
@ -47,4 +48,69 @@ class TaskOverdueNotification extends Base
->setRows($rows) ->setRows($rows)
->render(); ->render();
} }
/**
* Send overdue tasks
*
* @access public
*/
public function sendOverdueTaskNotifications()
{
$tasks = $this->taskFinder->getOverdueTasks();
foreach ($this->groupByColumn($tasks, 'project_id') as $project_id => $project_tasks) {
$users = $this->userNotification->getUsersWithNotificationEnabled($project_id);
foreach ($users as $user) {
$this->sendUserOverdueTaskNotifications($user, $project_tasks);
}
}
return $tasks;
}
/**
* Send overdue tasks for a given user
*
* @access public
* @param array $user
* @param array $tasks
*/
public function sendUserOverdueTaskNotifications(array $user, array $tasks)
{
$user_tasks = array();
foreach ($tasks as $task) {
if ($this->userNotificationFilter->shouldReceiveNotification($user, array('task' => $task))) {
$user_tasks[] = $task;
}
}
if (! empty($user_tasks)) {
$this->userNotification->sendUserNotification(
$user,
Task::EVENT_OVERDUE,
array('tasks' => $user_tasks, 'project_name' => $tasks[0]['project_name'])
);
}
}
/**
* Group a collection of records by a column
*
* @access public
* @param array $collection
* @param string $column
* @return array
*/
public function groupByColumn(array $collection, $column)
{
$result = array();
foreach ($collection as $item) {
$result[$item[$column]][] = $item;
}
return $result;
}
} }

View file

@ -21,7 +21,7 @@ class TransitionExport extends Base
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$data = $this->transition->export( $data = $this->transitionExport->export(
$input->getArgument('project_id'), $input->getArgument('project_id'),
$input->getArgument('start_date'), $input->getArgument('start_date'),
$input->getArgument('end_date') $input->getArgument('end_date')

View file

@ -3,7 +3,7 @@
namespace Kanboard\Controller; namespace Kanboard\Controller;
/** /**
* Automatic actions management * Automatic Actions
* *
* @package controller * @package controller
* @author Frederic Guillot * @author Frederic Guillot
@ -37,98 +37,6 @@ class Action extends Base
))); )));
} }
/**
* Choose the event according to the action (step 2)
*
* @access public
*/
public function event()
{
$project = $this->getProject();
$values = $this->request->getValues();
if (empty($values['action_name']) || empty($values['project_id'])) {
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
}
$this->response->html($this->helper->layout->project('action/event', array(
'values' => $values,
'project' => $project,
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
'title' => t('Automatic actions')
)));
}
/**
* Define action parameters (step 3)
*
* @access public
*/
public function params()
{
$project = $this->getProject();
$values = $this->request->getValues();
if (empty($values['action_name']) || empty($values['project_id']) || empty($values['event_name'])) {
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
}
$action = $this->actionManager->getAction($values['action_name']);
$action_params = $action->getActionRequiredParameters();
if (empty($action_params)) {
$this->doCreation($project, $values + array('params' => array()));
}
$projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
unset($projects_list[$project['id']]);
$this->response->html($this->helper->layout->project('action/params', array(
'values' => $values,
'action_params' => $action_params,
'columns_list' => $this->column->getList($project['id']),
'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']),
'projects_list' => $projects_list,
'colors_list' => $this->color->getList(),
'categories_list' => $this->category->getList($project['id']),
'links_list' => $this->link->getList(0, false),
'project' => $project,
'title' => t('Automatic actions')
)));
}
/**
* Create a new action (last step)
*
* @access public
*/
public function create()
{
$this->doCreation($this->getProject(), $this->request->getValues());
}
/**
* Save the action
*
* @access private
* @param array $project Project properties
* @param array $values Form values
*/
private function doCreation(array $project, array $values)
{
list($valid, ) = $this->actionValidator->validateCreation($values);
if ($valid) {
if ($this->action->create($values) !== false) {
$this->flash->success(t('Your automatic action have been created successfully.'));
} else {
$this->flash->failure(t('Unable to create your automatic action.'));
}
}
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
}
/** /**
* Confirmation dialog before removing an action * Confirmation dialog before removing an action
* *

View file

@ -0,0 +1,121 @@
<?php
namespace Kanboard\Controller;
/**
* Action Creation
*
* @package controller
* @author Frederic Guillot
*/
class ActionCreation extends Base
{
/**
* Show the form (step 1)
*
* @access public
*/
public function create()
{
$project = $this->getProject();
$this->response->html($this->template->render('action_creation/create', array(
'project' => $project,
'values' => array('project_id' => $project['id']),
'available_actions' => $this->actionManager->getAvailableActions(),
)));
}
/**
* Choose the event according to the action (step 2)
*
* @access public
*/
public function event()
{
$project = $this->getProject();
$values = $this->request->getValues();
if (empty($values['action_name']) || empty($values['project_id'])) {
return $this->create();
}
$this->response->html($this->template->render('action_creation/event', array(
'values' => $values,
'project' => $project,
'available_actions' => $this->actionManager->getAvailableActions(),
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
)));
}
/**
* Define action parameters (step 3)
*
* @access public
*/
public function params()
{
$project = $this->getProject();
$values = $this->request->getValues();
if (empty($values['action_name']) || empty($values['project_id']) || empty($values['event_name'])) {
return $this->create();
}
$action = $this->actionManager->getAction($values['action_name']);
$action_params = $action->getActionRequiredParameters();
if (empty($action_params)) {
$this->doCreation($project, $values + array('params' => array()));
}
$projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
unset($projects_list[$project['id']]);
$this->response->html($this->template->render('action_creation/params', array(
'values' => $values,
'action_params' => $action_params,
'columns_list' => $this->column->getList($project['id']),
'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']),
'projects_list' => $projects_list,
'colors_list' => $this->color->getList(),
'categories_list' => $this->category->getList($project['id']),
'links_list' => $this->link->getList(0, false),
'project' => $project,
'available_actions' => $this->actionManager->getAvailableActions(),
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
)));
}
/**
* Save the action (last step)
*
* @access public
*/
public function save()
{
$this->doCreation($this->getProject(), $this->request->getValues());
}
/**
* Common method to save the action
*
* @access private
* @param array $project Project properties
* @param array $values Form values
*/
private function doCreation(array $project, array $values)
{
list($valid, ) = $this->actionValidator->validateCreation($values);
if ($valid) {
if ($this->action->create($values) !== false) {
$this->flash->success(t('Your automatic action have been created successfully.'));
} else {
$this->flash->failure(t('Unable to create your automatic action.'));
}
}
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Kanboard\Controller;
/**
* Duplicate automatic action from another project
*
* @package controller
* @author Frederic Guillot
*/
class ActionProject extends Base
{
public function project()
{
$project = $this->getProject();
$projects = $this->projectUserRole->getProjectsByUser($this->userSession->getId());
unset($projects[$project['id']]);
$this->response->html($this->template->render('action_project/project', array(
'project' => $project,
'projects_list' => $projects,
)));
}
public function save()
{
$project = $this->getProject();
$src_project_id = $this->request->getValue('src_project_id');
if ($this->action->duplicate($src_project_id, $project['id'])) {
$this->flash->success(t('Actions duplicated successfully.'));
} else {
$this->flash->failure(t('Unable to duplicate actions.'));
}
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
}
}

View file

@ -38,6 +38,7 @@ class Activity extends Base
$this->response->html($this->helper->layout->task('activity/task', array( $this->response->html($this->helper->layout->task('activity/task', array(
'title' => $task['title'], 'title' => $task['title'],
'task' => $task, 'task' => $task,
'project' => $this->project->getById($task['project_id']),
'events' => $this->projectActivity->getTask($task['id']), 'events' => $this->projectActivity->getTask($task['id']),
))); )));
} }

View file

@ -44,8 +44,7 @@ class Analytic extends Base
public function compareHours() public function compareHours()
{ {
$project = $this->getProject(); $project = $this->getProject();
$params = $this->getProjectFilters('analytic', 'compareHours'); $query = $this->taskFilter->create()->filterByProject($project['id'])->getQuery();
$query = $this->taskFilter->create()->filterByProject($params['project']['id'])->getQuery();
$paginator = $this->paginator $paginator = $this->paginator
->setUrl('analytic', 'compareHours', array('project_id' => $project['id'])) ->setUrl('analytic', 'compareHours', array('project_id' => $project['id']))

View file

@ -2,6 +2,7 @@
namespace Kanboard\Controller; namespace Kanboard\Controller;
use Kanboard\Model\Project as ProjectModel;
use Kanboard\Model\Subtask as SubtaskModel; use Kanboard\Model\Subtask as SubtaskModel;
/** /**
@ -19,13 +20,14 @@ class App extends Base
* @param integer $user_id * @param integer $user_id
* @param string $action * @param string $action
* @param integer $max * @param integer $max
* @return \Kanboard\Core\Paginator
*/ */
private function getProjectPaginator($user_id, $action, $max) private function getProjectPaginator($user_id, $action, $max)
{ {
return $this->paginator return $this->paginator
->setUrl('app', $action, array('pagination' => 'projects', 'user_id' => $user_id)) ->setUrl('app', $action, array('pagination' => 'projects', 'user_id' => $user_id))
->setMax($max) ->setMax($max)
->setOrder('name') ->setOrder(ProjectModel::TABLE.'.name')
->setQuery($this->project->getQueryColumnStats($this->projectPermission->getActiveProjectIds($user_id))) ->setQuery($this->project->getQueryColumnStats($this->projectPermission->getActiveProjectIds($user_id)))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects'); ->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
} }
@ -37,6 +39,7 @@ class App extends Base
* @param integer $user_id * @param integer $user_id
* @param string $action * @param string $action
* @param integer $max * @param integer $max
* @return \Kanboard\Core\Paginator
*/ */
private function getTaskPaginator($user_id, $action, $max) private function getTaskPaginator($user_id, $action, $max)
{ {
@ -55,6 +58,7 @@ class App extends Base
* @param integer $user_id * @param integer $user_id
* @param string $action * @param string $action
* @param integer $max * @param integer $max
* @return \Kanboard\Core\Paginator
*/ */
private function getSubtaskPaginator($user_id, $action, $max) private function getSubtaskPaginator($user_id, $action, $max)
{ {

View file

@ -14,6 +14,8 @@ class Auth extends Base
* Display the form login * Display the form login
* *
* @access public * @access public
* @param array $values
* @param array $errors
*/ */
public function login(array $values = array(), array $errors = array()) public function login(array $values = array(), array $errors = array())
{ {

View file

@ -0,0 +1,92 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
use Kanboard\Core\Thumbnail;
/**
* Avatar File Controller
*
* @package controller
* @author Frederic Guillot
*/
class AvatarFile extends Base
{
/**
* Display avatar page
*/
public function show()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('avatar_file/show', array(
'user' => $user,
)));
}
/**
* Upload Avatar
*/
public function upload()
{
$user = $this->getUser();
if (! $this->avatarFile->uploadFile($user['id'], $this->request->getFileInfo('avatar'))) {
$this->flash->failure(t('Unable to upload the file.'));
}
$this->response->redirect($this->helper->url->to('AvatarFile', 'show', array('user_id' => $user['id'])));
}
/**
* Remove Avatar image
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
$this->avatarFile->remove($user['id']);
$this->response->redirect($this->helper->url->to('AvatarFile', 'show', array('user_id' => $user['id'])));
}
/**
* Show Avatar image (public)
*/
public function image()
{
$user_id = $this->request->getIntegerParam('user_id');
$size = $this->request->getStringParam('size', 48);
$filename = $this->avatarFile->getFilename($user_id);
$etag = md5($filename.$size);
$this->response->cache(365 * 86400, $etag);
$this->response->contentType('image/jpeg');
if ($this->request->getHeader('If-None-Match') !== '"'.$etag.'"') {
$this->render($filename, $size);
} else {
$this->response->status(304);
}
}
/**
* Render thumbnail from object storage
*
* @access private
* @param string $filename
* @param integer $size
*/
private function render($filename, $size)
{
try {
$blob = $this->objectStorage->get($filename);
Thumbnail::createFromString($blob)
->resize($size, $size)
->toOutput();
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
}
}

View file

@ -287,60 +287,4 @@ abstract class Base extends \Kanboard\Core\Base
return $subtask; return $subtask;
} }
/**
* Common method to get project filters
*
* @access protected
* @param string $controller
* @param string $action
* @return array
*/
protected function getProjectFilters($controller, $action)
{
$project = $this->getProject();
$search = $this->request->getStringParam('search', $this->userSession->getFilters($project['id']));
$board_selector = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
unset($board_selector[$project['id']]);
$filters = array(
'controller' => $controller,
'action' => $action,
'project_id' => $project['id'],
'search' => urldecode($search),
);
$this->userSession->setFilters($project['id'], $filters['search']);
return array(
'project' => $project,
'board_selector' => $board_selector,
'filters' => $filters,
'title' => $project['name'],
'description' => $this->getProjectDescription($project),
);
}
/**
* Get project description
*
* @access protected
* @param array &$project
* @return string
*/
protected function getProjectDescription(array &$project)
{
if ($project['owner_id'] > 0) {
$description = t('Project owner: ').'**'.$this->template->e($project['owner_name'] ?: $project['owner_username']).'**'.PHP_EOL.PHP_EOL;
if (! empty($project['description'])) {
$description .= '***'.PHP_EOL.PHP_EOL;
$description .= $project['description'];
}
} else {
$description = $project['description'];
}
return $description;
}
} }

View file

@ -47,16 +47,17 @@ class Board extends Base
*/ */
public function show() public function show()
{ {
$params = $this->getProjectFilters('board', 'show'); $project = $this->getProject();
$search = $this->helper->projectHeader->getSearchQuery($project);
$this->response->html($this->helper->layout->app('board/view_private', array( $this->response->html($this->helper->layout->app('board/view_private', array(
'categories_list' => $this->category->getList($params['project']['id'], false), 'swimlanes' => $this->taskFilter->search($search)->getBoard($project['id']),
'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false), 'project' => $project,
'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()), 'title' => $project['name'],
'swimlanes' => $this->taskFilter->search($params['filters']['search'])->getBoard($params['project']['id']), 'description' => $this->helper->projectHeader->getDescription($project),
'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'),
) + $params)); )));
} }
/** /**

View file

@ -77,6 +77,7 @@ class BoardTooltip extends Base
$task = $this->getTask(); $task = $this->getTask();
$this->response->html($this->template->render('board/tooltip_comments', array( $this->response->html($this->template->render('board/tooltip_comments', array(
'task' => $task,
'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting()) 'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting())
))); )));
} }

View file

@ -20,9 +20,14 @@ class Calendar extends Base
*/ */
public function show() public function show()
{ {
$project = $this->getProject();
$this->response->html($this->helper->layout->app('calendar/show', array( $this->response->html($this->helper->layout->app('calendar/show', array(
'project' => $project,
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'check_interval' => $this->config->get('board_private_refresh_interval'), 'check_interval' => $this->config->get('board_private_refresh_interval'),
) + $this->getProjectFilters('calendar', 'show'))); )));
} }
/** /**

View file

@ -76,6 +76,8 @@ class Column extends Base
* Display a form to edit a column * Display a form to edit a column
* *
* @access public * @access public
* @param array $values
* @param array $errors
*/ */
public function edit(array $values = array(), array $errors = array()) public function edit(array $values = array(), array $errors = array())
{ {

View file

@ -47,11 +47,10 @@ class Comment extends Base
); );
} }
$this->response->html($this->helper->layout->task('comment/create', array( $this->response->html($this->template->render('comment/create', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
'task' => $task, 'task' => $task,
'title' => t('Add a comment'),
))); )));
} }
@ -90,7 +89,7 @@ class Comment extends Base
$task = $this->getTask(); $task = $this->getTask();
$comment = $this->getComment(); $comment = $this->getComment();
$this->response->html($this->helper->layout->task('comment/edit', array( $this->response->html($this->template->render('comment/edit', array(
'values' => empty($values) ? $comment : $values, 'values' => empty($values) ? $comment : $values,
'errors' => $errors, 'errors' => $errors,
'comment' => $comment, 'comment' => $comment,
@ -135,7 +134,7 @@ class Comment extends Base
$task = $this->getTask(); $task = $this->getTask();
$comment = $this->getComment(); $comment = $this->getComment();
$this->response->html($this->helper->layout->task('comment/remove', array( $this->response->html($this->template->render('comment/remove', array(
'comment' => $comment, 'comment' => $comment,
'task' => $task, 'task' => $task,
'title' => t('Remove a comment') 'title' => t('Remove a comment')

View file

@ -61,6 +61,8 @@ class Config extends Base
{ {
$this->response->html($this->helper->layout->config('config/about', array( $this->response->html($this->helper->layout->config('config/about', array(
'db_size' => $this->config->getDatabaseSize(), 'db_size' => $this->config->getDatabaseSize(),
'db_version' => $this->db->getDriver()->getDatabaseVersion(),
'user_agent' => $this->request->getServerVariable('HTTP_USER_AGENT'),
'title' => t('Settings').' &gt; '.t('About'), 'title' => t('Settings').' &gt; '.t('About'),
))); )));
} }

View file

@ -5,53 +5,88 @@ namespace Kanboard\Controller;
use Parsedown; use Parsedown;
/** /**
* Documentation controller * Documentation Viewer
* *
* @package controller * @package controller
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Doc extends Base class Doc extends Base
{ {
private function readFile($filename) public function show()
{
$page = $this->request->getStringParam('file', 'index');
if (!preg_match('/^[a-z0-9\-]+/', $page)) {
$page = 'index';
}
if ($this->config->getCurrentLanguage() === 'fr_FR') {
$filename = __DIR__.'/../../doc/fr/' . $page . '.markdown';
} else {
$filename = __DIR__ . '/../../doc/' . $page . '.markdown';
}
if (!file_exists($filename)) {
$filename = __DIR__.'/../../doc/index.markdown';
}
$this->response->html($this->helper->layout->app('doc/show', $this->render($filename)));
}
/**
* Display keyboard shortcut
*/
public function shortcuts()
{
$this->response->html($this->template->render('config/keyboard_shortcuts'));
}
/**
* Prepare Markdown file
*
* @access private
* @param string $filename
* @return array
*/
private function render($filename)
{ {
$url = $this->helper->url;
$data = file_get_contents($filename); $data = file_get_contents($filename);
$content = preg_replace_callback('/\((.*.markdown)\)/', array($this, 'replaceMarkdownUrl'), $data);
$content = preg_replace_callback('/\((screenshots.*\.png)\)/', array($this, 'replaceImageUrl'), $content);
list($title, ) = explode("\n", $data, 2); list($title, ) = explode("\n", $data, 2);
$replaceUrl = function (array $matches) use ($url) {
return '('.$url->to('doc', 'show', array('file' => str_replace('.markdown', '', $matches[1]))).')';
};
$content = preg_replace_callback('/\((.*.markdown)\)/', $replaceUrl, $data);
return array( return array(
'content' => Parsedown::instance()->text($content), 'content' => Parsedown::instance()->text($content),
'title' => $title !== 'Documentation' ? t('Documentation: %s', $title) : $title, 'title' => $title !== 'Documentation' ? t('Documentation: %s', $title) : $title,
); );
} }
public function show() /**
* Regex callback to replace Markdown links
*
* @access public
* @param array $matches
* @return string
*/
public function replaceMarkdownUrl(array $matches)
{ {
$page = $this->request->getStringParam('file', 'index'); return '('.$this->helper->url->to('doc', 'show', array('file' => str_replace('.markdown', '', $matches[1]))).')';
}
if (! preg_match('/^[a-z0-9\-]+/', $page)) {
$page = 'index';
}
$filenames = array(__DIR__.'/../../doc/'.$page.'.markdown');
$filename = __DIR__.'/../../doc/index.markdown';
/**
* Regex callback to replace image links
*
* @access public
* @param array $matches
* @return string
*/
public function replaceImageUrl(array $matches)
{
if ($this->config->getCurrentLanguage() === 'fr_FR') { if ($this->config->getCurrentLanguage() === 'fr_FR') {
array_unshift($filenames, __DIR__.'/../../doc/fr/'.$page.'.markdown'); return '('.$this->helper->url->base().'doc/fr/'.$matches[1].')';
} }
foreach ($filenames as $file) { return '('.$this->helper->url->base().'doc/'.$matches[1].')';
if (file_exists($file)) {
$filename = $file;
break;
}
}
$this->response->html($this->helper->layout->app('doc/show', $this->readFile($filename)));
} }
} }

View file

@ -80,6 +80,6 @@ class Export extends Base
*/ */
public function transitions() public function transitions()
{ {
$this->common('transition', 'export', t('Transitions'), 'transitions', t('Task transitions export')); $this->common('transitionExport', 'export', t('Transitions'), 'transitions', t('Task transitions export'));
} }
} }

View file

@ -66,9 +66,16 @@ class FileViewer extends Base
*/ */
public function image() public function image()
{ {
$file = $this->getFile();
$etag = md5($file['path']);
$this->response->contentType($this->helper->file->getImageMimeType($file['name']));
$this->response->cache(5 * 86400, $etag);
if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') {
return $this->response->status(304);
}
try { try {
$file = $this->getFile();
$this->response->contentType($this->helper->file->getImageMimeType($file['name']));
$this->objectStorage->output($file['path']); $this->objectStorage->output($file['path']);
} catch (ObjectStorageException $e) { } catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage()); $this->logger->error($e->getMessage());
@ -82,12 +89,21 @@ class FileViewer extends Base
*/ */
public function thumbnail() public function thumbnail()
{ {
$file = $this->getFile();
$model = $file['model'];
$filename = $this->$model->getThumbnailPath($file['path']);
$etag = md5($filename);
$this->response->cache(5 * 86400, $etag);
$this->response->contentType('image/jpeg'); $this->response->contentType('image/jpeg');
if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') {
return $this->response->status(304);
}
try { try {
$file = $this->getFile();
$model = $file['model']; $this->objectStorage->output($filename);
$this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
} catch (ObjectStorageException $e) { } catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage()); $this->logger->error($e->getMessage());

View file

@ -54,8 +54,9 @@ class Gantt extends Base
*/ */
public function project() public function project()
{ {
$params = $this->getProjectFilters('gantt', 'project'); $project = $this->getProject();
$filter = $this->taskFilterGanttFormatter->search($params['filters']['search'])->filterByProject($params['project']['id']); $search = $this->helper->projectHeader->getSearchQuery($project);
$filter = $this->taskFilterGanttFormatter->search($search)->filterByProject($project['id']);
$sorting = $this->request->getStringParam('sorting', 'board'); $sorting = $this->request->getStringParam('sorting', 'board');
if ($sorting === 'date') { if ($sorting === 'date') {
@ -64,8 +65,10 @@ class Gantt extends Base
$filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position'); $filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position');
} }
$this->response->html($this->helper->layout->app('gantt/project', $params + array( $this->response->html($this->helper->layout->app('gantt/project', array(
'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false), 'project' => $project,
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'sorting' => $sorting, 'sorting' => $sorting,
'tasks' => $filter->format(), 'tasks' => $filter->format(),
))); )));

View file

@ -19,22 +19,23 @@ class Listing extends Base
*/ */
public function show() public function show()
{ {
$params = $this->getProjectFilters('listing', 'show'); $project = $this->getProject();
$query = $this->taskFilter->search($params['filters']['search'])->filterByProject($params['project']['id'])->getQuery(); $search = $this->helper->projectHeader->getSearchQuery($project);
$query = $this->taskFilter->search($search)->filterByProject($project['id'])->getQuery();
$paginator = $this->paginator $paginator = $this->paginator
->setUrl('listing', 'show', array('project_id' => $params['project']['id'])) ->setUrl('listing', 'show', array('project_id' => $project['id']))
->setMax(30) ->setMax(30)
->setOrder(TaskModel::TABLE.'.id') ->setOrder(TaskModel::TABLE.'.id')
->setDirection('DESC') ->setDirection('DESC')
->setQuery($query) ->setQuery($query)
->calculate(); ->calculate();
$this->response->html($this->helper->layout->app('listing/show', $params + array( $this->response->html($this->helper->layout->app('listing/show', array(
'project' => $project,
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'paginator' => $paginator, 'paginator' => $paginator,
'categories_list' => $this->category->getList($params['project']['id'], false),
'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()),
))); )));
} }
} }

View file

@ -2,6 +2,8 @@
namespace Kanboard\Controller; namespace Kanboard\Controller;
use Kanboard\Core\Security\OAuthAuthenticationProviderInterface;
/** /**
* OAuth controller * OAuth controller
* *
@ -10,6 +12,72 @@ namespace Kanboard\Controller;
*/ */
class Oauth extends Base class Oauth extends Base
{ {
/**
* Redirect to the provider if no code received
*
* @access private
* @param string $provider
*/
protected function step1($provider)
{
$code = $this->request->getStringParam('code');
$state = $this->request->getStringParam('state');
if (! empty($code)) {
$this->step2($provider, $code, $state);
} else {
$this->response->redirect($this->authenticationManager->getProvider($provider)->getService()->getAuthorizationUrl());
}
}
/**
* Link or authenticate the user
*
* @access protected
* @param string $providerName
* @param string $code
* @param string $state
*/
protected function step2($providerName, $code, $state)
{
$provider = $this->authenticationManager->getProvider($providerName);
$provider->setCode($code);
$hasValidState = $provider->getService()->isValidateState($state);
if ($this->userSession->isLogged()) {
if ($hasValidState) {
$this->link($provider);
} else {
$this->flash->failure(t('The OAuth2 state parameter is invalid'));
$this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
}
} else {
if ($hasValidState) {
$this->authenticate($providerName);
} else {
$this->authenticationFailure(t('The OAuth2 state parameter is invalid'));
}
}
}
/**
* Link the account
*
* @access protected
* @param OAuthAuthenticationProviderInterface $provider
*/
protected function link(OAuthAuthenticationProviderInterface $provider)
{
if (! $provider->authenticate()) {
$this->flash->failure(t('External authentication failed'));
} else {
$this->userProfile->assign($this->userSession->getId(), $provider->getUser());
$this->flash->success(t('Your external account is linked to your profile successfully.'));
}
$this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
}
/** /**
* Unlink external account * Unlink external account
* *
@ -29,78 +97,34 @@ class Oauth extends Base
$this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId()))); $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
} }
/**
* Redirect to the provider if no code received
*
* @access private
* @param string $provider
*/
protected function step1($provider)
{
$code = $this->request->getStringParam('code');
if (! empty($code)) {
$this->step2($provider, $code);
} else {
$this->response->redirect($this->authenticationManager->getProvider($provider)->getService()->getAuthorizationUrl());
}
}
/**
* Link or authenticate the user
*
* @access protected
* @param string $provider
* @param string $code
*/
protected function step2($provider, $code)
{
$this->authenticationManager->getProvider($provider)->setCode($code);
if ($this->userSession->isLogged()) {
$this->link($provider);
}
$this->authenticate($provider);
}
/**
* Link the account
*
* @access protected
* @param string $provider
*/
protected function link($provider)
{
$authProvider = $this->authenticationManager->getProvider($provider);
if (! $authProvider->authenticate()) {
$this->flash->failure(t('External authentication failed'));
} else {
$this->userProfile->assign($this->userSession->getId(), $authProvider->getUser());
$this->flash->success(t('Your external account is linked to your profile successfully.'));
}
$this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
}
/** /**
* Authenticate the account * Authenticate the account
* *
* @access protected * @access protected
* @param string $provider * @param string $providerName
*/ */
protected function authenticate($provider) protected function authenticate($providerName)
{ {
if ($this->authenticationManager->oauthAuthentication($provider)) { if ($this->authenticationManager->oauthAuthentication($providerName)) {
$this->response->redirect($this->helper->url->to('app', 'index')); $this->response->redirect($this->helper->url->to('app', 'index'));
} else { } else {
$this->response->html($this->helper->layout->app('auth/index', array( $this->authenticationFailure(t('External authentication failed'));
'errors' => array('login' => t('External authentication failed')),
'values' => array(),
'no_layout' => true,
'title' => t('Login')
)));
} }
} }
/**
* Show login failure page
*
* @access protected
* @param string $message
*/
protected function authenticationFailure($message)
{
$this->response->html($this->helper->layout->app('auth/index', array(
'errors' => array('login' => $message),
'values' => array(),
'no_layout' => true,
'title' => t('Login')
)));
}
} }

View file

@ -67,7 +67,7 @@ class ProjectEdit extends Base
if ($valid) { if ($valid) {
if ($this->project->update($values)) { if ($this->project->update($values)) {
$this->flash->success(t('Project updated successfully.')); $this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('ProjectEdit', $redirect, array('project_id' => $project['id']))); $this->response->redirect($this->helper->url->to('ProjectEdit', $redirect, array('project_id' => $project['id'])), true);
} else { } else {
$this->flash->failure(t('Unable to update this project.')); $this->flash->failure(t('Unable to update this project.'));
} }

View file

@ -15,15 +15,18 @@ class ProjectOverview extends Base
*/ */
public function show() public function show()
{ {
$params = $this->getProjectFilters('ProjectOverview', 'show'); $project = $this->getProject();
$params['users'] = $this->projectUserRole->getAllUsersGroupedByRole($params['project']['id']); $this->project->getColumnStats($project);
$params['roles'] = $this->role->getProjectRoles();
$params['events'] = $this->projectActivity->getProject($params['project']['id'], 10);
$params['images'] = $this->projectFile->getAllImages($params['project']['id']);
$params['files'] = $this->projectFile->getAllDocuments($params['project']['id']);
$this->project->getColumnStats($params['project']); $this->response->html($this->helper->layout->app('project_overview/show', array(
'project' => $project,
$this->response->html($this->helper->layout->app('project_overview/show', $params)); 'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'users' => $this->projectUserRole->getAllUsersGroupedByRole($project['id']),
'roles' => $this->role->getProjectRoles(),
'events' => $this->projectActivity->getProject($project['id'], 10),
'images' => $this->projectFile->getAllImages($project['id']),
'files' => $this->projectFile->getAllDocuments($project['id']),
)));
} }
} }

View file

@ -10,22 +10,6 @@ namespace Kanboard\Controller;
*/ */
class Subtask extends Base class Subtask extends Base
{ {
/**
* Show list of subtasks
*/
public function show()
{
$task = $this->getTask();
$this->response->html($this->helper->layout->task('subtask/show', array(
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
'task' => $task,
'project' => $this->getProject(),
'subtasks' => $this->subtask->getAll($task['id']),
'editable' => true,
)));
}
/** /**
* Creation form * Creation form
* *
@ -42,7 +26,7 @@ class Subtask extends Base
); );
} }
$this->response->html($this->helper->layout->task('subtask/create', array( $this->response->html($this->template->render('subtask/create', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']), 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
@ -89,7 +73,7 @@ class Subtask extends Base
$task = $this->getTask(); $task = $this->getTask();
$subtask = $this->getSubTask(); $subtask = $this->getSubTask();
$this->response->html($this->helper->layout->task('subtask/edit', array( $this->response->html($this->template->render('subtask/edit', array(
'values' => empty($values) ? $subtask : $values, 'values' => empty($values) ? $subtask : $values,
'errors' => $errors, 'errors' => $errors,
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']), 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
@ -135,7 +119,7 @@ class Subtask extends Base
$task = $this->getTask(); $task = $this->getTask();
$subtask = $this->getSubtask(); $subtask = $this->getSubtask();
$this->response->html($this->helper->layout->task('subtask/remove', array( $this->response->html($this->template->render('subtask/remove', array(
'subtask' => $subtask, 'subtask' => $subtask,
'task' => $task, 'task' => $task,
))); )));

View file

@ -2,6 +2,8 @@
namespace Kanboard\Controller; namespace Kanboard\Controller;
use Kanboard\Core\DateParser;
/** /**
* Task controller * Task controller
* *
@ -21,13 +23,17 @@ class Task extends Base
// Token verification // Token verification
if (empty($project)) { if (empty($project)) {
$this->forbidden(true); return $this->forbidden(true);
} }
$task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id')); $task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id'));
if (empty($task)) { if (empty($task)) {
$this->notfound(true); return $this->notfound(true);
}
if ($task['project_id'] != $project['id']) {
return $this->forbidden(true);
} }
$this->response->html($this->helper->layout->app('task/public', array( $this->response->html($this->helper->layout->app('task/public', array(
@ -62,25 +68,19 @@ class Task extends Base
'time_spent' => $task['time_spent'] ?: '', 'time_spent' => $task['time_spent'] ?: '',
); );
$values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', 'm/d/Y H:i')); $values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', DateParser::DATE_TIME_FORMAT));
$this->response->html($this->helper->layout->task('task/show', array( $this->response->html($this->helper->layout->task('task/show', array(
'task' => $task,
'project' => $this->project->getById($task['project_id']), 'project' => $this->project->getById($task['project_id']),
'values' => $values,
'files' => $this->taskFile->getAllDocuments($task['id']), 'files' => $this->taskFile->getAllDocuments($task['id']),
'images' => $this->taskFile->getAllImages($task['id']), 'images' => $this->taskFile->getAllImages($task['id']),
'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting()), 'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting()),
'subtasks' => $subtasks, 'subtasks' => $subtasks,
'links' => $this->taskLink->getAllGroupedByLabel($task['id']), 'internal_links' => $this->taskLink->getAllGroupedByLabel($task['id']),
'task' => $task, 'external_links' => $this->taskExternalLink->getAll($task['id']),
'values' => $values,
'link_label_list' => $this->link->getList(0, false), 'link_label_list' => $this->link->getList(0, false),
'columns_list' => $this->column->getList($task['project_id']),
'colors_list' => $this->color->getList(),
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id'], true, false, false),
'title' => $task['project_name'].' &gt; '.$task['title'],
'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(),
))); )));
} }
@ -94,8 +94,8 @@ class Task extends Base
$task = $this->getTask(); $task = $this->getTask();
$this->response->html($this->helper->layout->task('task/analytics', array( $this->response->html($this->helper->layout->task('task/analytics', array(
'title' => $task['title'],
'task' => $task, 'task' => $task,
'project' => $this->project->getById($task['project_id']),
'lead_time' => $this->taskAnalytic->getLeadTime($task), 'lead_time' => $this->taskAnalytic->getLeadTime($task),
'cycle_time' => $this->taskAnalytic->getCycleTime($task), 'cycle_time' => $this->taskAnalytic->getCycleTime($task),
'time_spent_columns' => $this->taskAnalytic->getTimeSpentByColumn($task), 'time_spent_columns' => $this->taskAnalytic->getTimeSpentByColumn($task),
@ -121,6 +121,7 @@ class Task extends Base
$this->response->html($this->helper->layout->task('task/time_tracking_details', array( $this->response->html($this->helper->layout->task('task/time_tracking_details', array(
'task' => $task, 'task' => $task,
'project' => $this->project->getById($task['project_id']),
'subtask_paginator' => $subtask_paginator, 'subtask_paginator' => $subtask_paginator,
))); )));
} }
@ -136,6 +137,7 @@ class Task extends Base
$this->response->html($this->helper->layout->task('task/transitions', array( $this->response->html($this->helper->layout->task('task/transitions', array(
'task' => $task, 'task' => $task,
'project' => $this->project->getById($task['project_id']),
'transitions' => $this->transition->getAllByTask($task['id']), 'transitions' => $this->transition->getAllByTask($task['id']),
))); )));
} }
@ -165,7 +167,7 @@ class Task extends Base
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
} }
$this->response->html($this->helper->layout->task('task/remove', array( $this->response->html($this->template->render('task/remove', array(
'task' => $task, 'task' => $task,
))); )));
} }

View file

@ -12,22 +12,6 @@ use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
*/ */
class TaskExternalLink extends Base class TaskExternalLink extends Base
{ {
/**
* Creation form
*
* @access public
*/
public function show()
{
$task = $this->getTask();
$this->response->html($this->helper->layout->task('task_external_link/show', array(
'links' => $this->taskExternalLink->getAll($task['id']),
'task' => $task,
'title' => t('List of external links'),
)));
}
/** /**
* First creation form * First creation form
* *
@ -37,7 +21,7 @@ class TaskExternalLink extends Base
{ {
$task = $this->getTask(); $task = $this->getTask();
$this->response->html($this->helper->layout->task('task_external_link/find', array( $this->response->html($this->template->render('task_external_link/find', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
'task' => $task, 'task' => $task,
@ -60,7 +44,7 @@ class TaskExternalLink extends Base
$provider = $this->externalLinkManager->setUserInput($values)->find(); $provider = $this->externalLinkManager->setUserInput($values)->find();
$link = $provider->getLink(); $link = $provider->getLink();
$this->response->html($this->helper->layout->task('task_external_link/create', array( $this->response->html($this->template->render('task_external_link/create', array(
'values' => array( 'values' => array(
'title' => $link->getTitle(), 'title' => $link->getTitle(),
'url' => $link->getUrl(), 'url' => $link->getUrl(),
@ -90,7 +74,7 @@ class TaskExternalLink extends Base
if ($valid && $this->taskExternalLink->create($values)) { if ($valid && $this->taskExternalLink->create($values)) {
$this->flash->success(t('Link added successfully.')); $this->flash->success(t('Link added successfully.'));
return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
} }
$this->edit($values, $errors); $this->edit($values, $errors);
@ -116,7 +100,7 @@ class TaskExternalLink extends Base
$provider = $this->externalLinkManager->getProvider($values['link_type']); $provider = $this->externalLinkManager->getProvider($values['link_type']);
$this->response->html($this->helper->layout->task('task_external_link/edit', array( $this->response->html($this->template->render('task_external_link/edit', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
'task' => $task, 'task' => $task,
@ -137,7 +121,7 @@ class TaskExternalLink extends Base
if ($valid && $this->taskExternalLink->update($values)) { if ($valid && $this->taskExternalLink->update($values)) {
$this->flash->success(t('Link updated successfully.')); $this->flash->success(t('Link updated successfully.'));
return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
} }
$this->edit($values, $errors); $this->edit($values, $errors);
@ -158,7 +142,7 @@ class TaskExternalLink extends Base
return $this->notfound(); return $this->notfound();
} }
$this->response->html($this->helper->layout->task('task_external_link/remove', array( $this->response->html($this->template->render('task_external_link/remove', array(
'link' => $link, 'link' => $link,
'task' => $task, 'task' => $task,
))); )));
@ -180,6 +164,6 @@ class TaskExternalLink extends Base
$this->flash->failure(t('Unable to remove this link.')); $this->flash->failure(t('Unable to remove this link.'));
} }
$this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
} }
} }

View file

@ -10,22 +10,6 @@ namespace Kanboard\Controller;
*/ */
class TaskHelper extends Base class TaskHelper extends Base
{ {
/**
* Render Markdown text and reply with the HTML Code
*
* @access public
*/
public function preview()
{
$payload = $this->request->getJson();
if (empty($payload['text'])) {
$this->response->html('<p>'.t('Nothing to preview...').'</p>');
}
$this->response->html($this->helper->text->markdown($payload['text']));
}
/** /**
* Task autocompletion (Ajax) * Task autocompletion (Ajax)
* *

View file

@ -3,13 +3,13 @@
namespace Kanboard\Controller; namespace Kanboard\Controller;
/** /**
* TaskLink controller * TaskInternalLink Controller
* *
* @package controller * @package controller
* @author Olivier Maridat * @author Olivier Maridat
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Tasklink extends Base class TaskInternalLink extends Base
{ {
/** /**
* Get the current link * Get the current link
@ -28,25 +28,6 @@ class Tasklink extends Base
return $link; return $link;
} }
/**
* Show links
*
* @access public
*/
public function show()
{
$task = $this->getTask();
$project = $this->project->getById($task['project_id']);
$this->response->html($this->helper->layout->task('tasklink/show', array(
'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
'task' => $task,
'project' => $project,
'editable' => true,
'is_public' => false,
)));
}
/** /**
* Creation form * Creation form
* *
@ -56,12 +37,11 @@ class Tasklink extends Base
{ {
$task = $this->getTask(); $task = $this->getTask();
$this->response->html($this->helper->layout->task('tasklink/create', array( $this->response->html($this->template->render('task_internal_link/create', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
'task' => $task, 'task' => $task,
'labels' => $this->link->getList(0, false), 'labels' => $this->link->getList(0, false),
'title' => t('Add a new link')
))); )));
} }
@ -80,7 +60,7 @@ class Tasklink extends Base
if ($valid) { if ($valid) {
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->flash->success(t('Link added successfully.')); $this->flash->success(t('Link added successfully.'));
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links', true); return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
} }
$errors = array('title' => array(t('The exact same link already exists'))); $errors = array('title' => array(t('The exact same link already exists')));
@ -106,13 +86,12 @@ class Tasklink extends Base
$values['title'] = '#'.$opposite_task['id'].' - '.$opposite_task['title']; $values['title'] = '#'.$opposite_task['id'].' - '.$opposite_task['title'];
} }
$this->response->html($this->helper->layout->task('tasklink/edit', array( $this->response->html($this->template->render('task_internal_link/edit', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
'task_link' => $task_link, 'task_link' => $task_link,
'task' => $task, 'task' => $task,
'labels' => $this->link->getList(0, false), 'labels' => $this->link->getList(0, false)
'title' => t('Edit link')
))); )));
} }
@ -150,7 +129,7 @@ class Tasklink extends Base
$task = $this->getTask(); $task = $this->getTask();
$link = $this->getTaskLink(); $link = $this->getTaskLink();
$this->response->html($this->helper->layout->task('tasklink/remove', array( $this->response->html($this->template->render('task_internal_link/remove', array(
'link' => $link, 'link' => $link,
'task' => $task, 'task' => $task,
))); )));
@ -172,6 +151,6 @@ class Tasklink extends Base
$this->flash->failure(t('Unable to remove this link.')); $this->flash->failure(t('Unable to remove this link.'));
} }
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links'); $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
} }
} }

View file

@ -23,7 +23,7 @@ class TaskRecurrence extends Base
$values = $task; $values = $task;
} }
$this->response->html($this->helper->layout->task('task_recurrence/edit', array( $this->response->html($this->template->render('task_recurrence/edit', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
'task' => $task, 'task' => $task,

View file

@ -32,7 +32,7 @@ class Taskduplication extends Base
} }
} }
$this->response->html($this->helper->layout->task('task_duplication/duplicate', array( $this->response->html($this->template->render('task_duplication/duplicate', array(
'task' => $task, 'task' => $task,
))); )));
} }
@ -128,7 +128,7 @@ class Taskduplication extends Base
$users_list = array(); $users_list = array();
} }
$this->response->html($this->helper->layout->task($template, array( $this->response->html($this->template->render($template, array(
'values' => $values, 'values' => $values,
'task' => $task, 'task' => $task,
'projects_list' => $projects_list, 'projects_list' => $projects_list,

View file

@ -2,6 +2,8 @@
namespace Kanboard\Controller; namespace Kanboard\Controller;
use Kanboard\Core\DateParser;
/** /**
* Task Modification controller * Task Modification controller
* *
@ -35,7 +37,7 @@ class Taskmodification extends Base
$values = array('id' => $task['id'], 'description' => $task['description']); $values = array('id' => $task['id'], 'description' => $task['description']);
} }
$this->response->html($this->helper->layout->task('task_modification/edit_description', array( $this->response->html($this->template->render('task_modification/edit_description', array(
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,
'task' => $task, 'task' => $task,
@ -83,10 +85,10 @@ class Taskmodification extends Base
$values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values)); $values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values));
} }
$values = $this->dateParser->format($values, array('date_due'), $this->config->get('application_date_format', 'm/d/Y')); $values = $this->dateParser->format($values, array('date_due'), $this->config->get('application_date_format', DateParser::DATE_FORMAT));
$values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', 'm/d/Y H:i')); $values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', DateParser::DATE_TIME_FORMAT));
$this->response->html($this->helper->layout->task('task_modification/edit_task', array( $this->response->html($this->template->render('task_modification/edit_task', array(
'project' => $project, 'project' => $project,
'values' => $values, 'values' => $values,
'errors' => $errors, 'errors' => $errors,

View file

@ -55,7 +55,7 @@ class Taskstatus extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
} }
$this->response->html($this->helper->layout->task($template, array( $this->response->html($this->template->render($template, array(
'task' => $task, 'task' => $task,
))); )));
} }

View file

@ -31,7 +31,6 @@ use Pimple\Container;
* @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage * @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage
* @property \Kanboard\Core\Plugin\Hook $hook * @property \Kanboard\Core\Plugin\Hook $hook
* @property \Kanboard\Core\Plugin\Loader $pluginLoader * @property \Kanboard\Core\Plugin\Loader $pluginLoader
* @property \Kanboard\Core\Security\AccessMap $projectAccessMap
* @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager * @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager
* @property \Kanboard\Core\Security\AccessMap $applicationAccessMap * @property \Kanboard\Core\Security\AccessMap $applicationAccessMap
* @property \Kanboard\Core\Security\AccessMap $projectAccessMap * @property \Kanboard\Core\Security\AccessMap $projectAccessMap
@ -42,6 +41,7 @@ use Pimple\Container;
* @property \Kanboard\Core\Session\FlashMessage $flash * @property \Kanboard\Core\Session\FlashMessage $flash
* @property \Kanboard\Core\Session\SessionManager $sessionManager * @property \Kanboard\Core\Session\SessionManager $sessionManager
* @property \Kanboard\Core\Session\SessionStorage $sessionStorage * @property \Kanboard\Core\Session\SessionStorage $sessionStorage
* @property \Kanboard\Core\User\Avatar\AvatarManager $avatarManager
* @property \Kanboard\Core\User\GroupSync $groupSync * @property \Kanboard\Core\User\GroupSync $groupSync
* @property \Kanboard\Core\User\UserProfile $userProfile * @property \Kanboard\Core\User\UserProfile $userProfile
* @property \Kanboard\Core\User\UserSync $userSync * @property \Kanboard\Core\User\UserSync $userSync
@ -60,6 +60,7 @@ use Pimple\Container;
* @property \Kanboard\Formatter\GroupAutoCompleteFormatter $groupAutoCompleteFormatter * @property \Kanboard\Formatter\GroupAutoCompleteFormatter $groupAutoCompleteFormatter
* @property \Kanboard\Model\Action $action * @property \Kanboard\Model\Action $action
* @property \Kanboard\Model\ActionParameter $actionParameter * @property \Kanboard\Model\ActionParameter $actionParameter
* @property \Kanboard\Model\AvatarFile $avatarFile
* @property \Kanboard\Model\Board $board * @property \Kanboard\Model\Board $board
* @property \Kanboard\Model\Category $category * @property \Kanboard\Model\Category $category
* @property \Kanboard\Model\Color $color * @property \Kanboard\Model\Color $color
@ -75,11 +76,9 @@ use Pimple\Container;
* @property \Kanboard\Model\LastLogin $lastLogin * @property \Kanboard\Model\LastLogin $lastLogin
* @property \Kanboard\Model\Link $link * @property \Kanboard\Model\Link $link
* @property \Kanboard\Model\Notification $notification * @property \Kanboard\Model\Notification $notification
* @property \Kanboard\Model\OverdueNotification $overdueNotification
* @property \Kanboard\Model\PasswordReset $passwordReset * @property \Kanboard\Model\PasswordReset $passwordReset
* @property \Kanboard\Model\Project $project * @property \Kanboard\Model\Project $project
* @property \Kanboard\Model\ProjectActivity $projectActivity * @property \Kanboard\Model\ProjectActivity $projectActivity
* @property \Kanboard\Model\ProjectAnalytic $projectAnalytic
* @property \Kanboard\Model\ProjectDuplication $projectDuplication * @property \Kanboard\Model\ProjectDuplication $projectDuplication
* @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats * @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats
* @property \Kanboard\Model\ProjectDailyStats $projectDailyStats * @property \Kanboard\Model\ProjectDailyStats $projectDailyStats
@ -92,16 +91,13 @@ use Pimple\Container;
* @property \Kanboard\Model\ProjectNotificationType $projectNotificationType * @property \Kanboard\Model\ProjectNotificationType $projectNotificationType
* @property \Kanboard\Model\RememberMeSession $rememberMeSession * @property \Kanboard\Model\RememberMeSession $rememberMeSession
* @property \Kanboard\Model\Subtask $subtask * @property \Kanboard\Model\Subtask $subtask
* @property \Kanboard\Model\SubtaskExport $subtaskExport
* @property \Kanboard\Model\SubtaskTimeTracking $subtaskTimeTracking * @property \Kanboard\Model\SubtaskTimeTracking $subtaskTimeTracking
* @property \Kanboard\Model\Swimlane $swimlane * @property \Kanboard\Model\Swimlane $swimlane
* @property \Kanboard\Model\Task $task * @property \Kanboard\Model\Task $task
* @property \Kanboard\Model\TaskAnalytic $taskAnalytic * @property \Kanboard\Model\TaskAnalytic $taskAnalytic
* @property \Kanboard\Model\TaskCreation $taskCreation * @property \Kanboard\Model\TaskCreation $taskCreation
* @property \Kanboard\Model\TaskDuplication $taskDuplication * @property \Kanboard\Model\TaskDuplication $taskDuplication
* @property \Kanboard\Model\TaskExport $taskExport
* @property \Kanboard\Model\TaskExternalLink $taskExternalLink * @property \Kanboard\Model\TaskExternalLink $taskExternalLink
* @property \Kanboard\Model\TaskImport $taskImport
* @property \Kanboard\Model\TaskFinder $taskFinder * @property \Kanboard\Model\TaskFinder $taskFinder
* @property \Kanboard\Model\TaskFilter $taskFilter * @property \Kanboard\Model\TaskFilter $taskFilter
* @property \Kanboard\Model\TaskLink $taskLink * @property \Kanboard\Model\TaskLink $taskLink
@ -112,7 +108,6 @@ use Pimple\Container;
* @property \Kanboard\Model\TaskMetadata $taskMetadata * @property \Kanboard\Model\TaskMetadata $taskMetadata
* @property \Kanboard\Model\Transition $transition * @property \Kanboard\Model\Transition $transition
* @property \Kanboard\Model\User $user * @property \Kanboard\Model\User $user
* @property \Kanboard\Model\UserImport $userImport
* @property \Kanboard\Model\UserLocking $userLocking * @property \Kanboard\Model\UserLocking $userLocking
* @property \Kanboard\Model\UserMention $userMention * @property \Kanboard\Model\UserMention $userMention
* @property \Kanboard\Model\UserNotification $userNotification * @property \Kanboard\Model\UserNotification $userNotification
@ -120,12 +115,10 @@ use Pimple\Container;
* @property \Kanboard\Model\UserNotificationFilter $userNotificationFilter * @property \Kanboard\Model\UserNotificationFilter $userNotificationFilter
* @property \Kanboard\Model\UserUnreadNotification $userUnreadNotification * @property \Kanboard\Model\UserUnreadNotification $userUnreadNotification
* @property \Kanboard\Model\UserMetadata $userMetadata * @property \Kanboard\Model\UserMetadata $userMetadata
* @property \Kanboard\Model\Webhook $webhook
* @property \Kanboard\Validator\ActionValidator $actionValidator * @property \Kanboard\Validator\ActionValidator $actionValidator
* @property \Kanboard\Validator\AuthValidator $authValidator * @property \Kanboard\Validator\AuthValidator $authValidator
* @property \Kanboard\Validator\ColumnValidator $columnValidator * @property \Kanboard\Validator\ColumnValidator $columnValidator
* @property \Kanboard\Validator\CategoryValidator $categoryValidator * @property \Kanboard\Validator\CategoryValidator $categoryValidator
* @property \Kanboard\Validator\ColumnValidator $columnValidator
* @property \Kanboard\Validator\CommentValidator $commentValidator * @property \Kanboard\Validator\CommentValidator $commentValidator
* @property \Kanboard\Validator\CurrencyValidator $currencyValidator * @property \Kanboard\Validator\CurrencyValidator $currencyValidator
* @property \Kanboard\Validator\CustomFilterValidator $customFilterValidator * @property \Kanboard\Validator\CustomFilterValidator $customFilterValidator
@ -136,9 +129,14 @@ use Pimple\Container;
* @property \Kanboard\Validator\SubtaskValidator $subtaskValidator * @property \Kanboard\Validator\SubtaskValidator $subtaskValidator
* @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator * @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator
* @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator
* @property \Kanboard\Validator\TaskExternalLinkValidator $taskExternalLinkValidator * @property \Kanboard\Validator\ExternalLinkValidator $externalLinkValidator
* @property \Kanboard\Validator\TaskValidator $taskValidator * @property \Kanboard\Validator\TaskValidator $taskValidator
* @property \Kanboard\Validator\UserValidator $userValidator * @property \Kanboard\Validator\UserValidator $userValidator
* @property \Kanboard\Import\TaskImport $taskImport
* @property \Kanboard\Import\UserImport $userImport
* @property \Kanboard\Export\SubtaskExport $subtaskExport
* @property \Kanboard\Export\TaskExport $taskExport
* @property \Kanboard\Export\TransitionExport $transitionExport
* @property \Psr\Log\LoggerInterface $logger * @property \Psr\Log\LoggerInterface $logger
* @property \PicoDb\Database $db * @property \PicoDb\Database $db
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher

View file

@ -87,7 +87,8 @@ class Csv
* *
* @static * @static
* @access public * @access public
* @return integer * @param mixed $value
* @return int
*/ */
public static function getBooleanValue($value) public static function getBooleanValue($value)
{ {

View file

@ -12,6 +12,9 @@ use DateTime;
*/ */
class DateParser extends Base class DateParser extends Base
{ {
const DATE_FORMAT = 'm/d/Y';
const DATE_TIME_FORMAT = 'm/d/Y H:i';
/** /**
* List of time formats * List of time formats
* *
@ -201,7 +204,7 @@ class DateParser extends Base
} }
/** /**
* Get a timetstamp from an ISO date format * Get a timestamp from an ISO date format
* *
* @access public * @access public
* @param string $value * @param string $value

View file

@ -10,17 +10,20 @@ use Pimple\Container;
* @package core * @package core
* @author Frederic Guillot * @author Frederic Guillot
* *
* @property \Helper\App $app * @property \Kanboard\Helper\AppHelper $app
* @property \Helper\Asset $asset * @property \Kanboard\Helper\AssetHelper $asset
* @property \Helper\Dt $dt * @property \Kanboard\Helper\DateHelper $dt
* @property \Helper\File $file * @property \Kanboard\Helper\FileHelper $file
* @property \Helper\Form $form * @property \Kanboard\Helper\FormHelper $form
* @property \Helper\Subtask $subtask * @property \Kanboard\Helper\HookHelper $hook
* @property \Helper\Task $task * @property \Kanboard\Helper\ModelHelper $model
* @property \Helper\Text $text * @property \Kanboard\Helper\SubtaskHelper $subtask
* @property \Helper\Url $url * @property \Kanboard\Helper\TaskHelper $task
* @property \Helper\User $user * @property \Kanboard\Helper\TextHelper $text
* @property \Helper\Layout $layout * @property \Kanboard\Helper\UrlHelper $url
* @property \Kanboard\Helper\UserHelper $user
* @property \Kanboard\Helper\LayoutHelper $layout
* @property \Kanboard\Helper\ProjectHeaderHelper $projectHeader
*/ */
class Helper class Helper
{ {
@ -28,17 +31,17 @@ class Helper
* Helper instances * Helper instances
* *
* @access private * @access private
* @var array * @var \Pimple\Container
*/ */
private $helpers = array(); private $helpers;
/** /**
* Container instance * Container instance
* *
* @access protected * @access private
* @var \Pimple\Container * @var \Pimple\Container
*/ */
protected $container; private $container;
/** /**
* Constructor * Constructor
@ -49,33 +52,49 @@ class Helper
public function __construct(Container $container) public function __construct(Container $container)
{ {
$this->container = $container; $this->container = $container;
$this->helpers = new Container;
} }
/** /**
* Load automatically helpers * Expose helpers with magic getter
* *
* @access public * @access public
* @param string $name Helper name * @param string $helper
* @return mixed * @return mixed
*/ */
public function __get($name) public function __get($helper)
{ {
if (! isset($this->helpers[$name])) { return $this->getHelper($helper);
$class = '\Kanboard\Helper\\'.ucfirst($name);
$this->helpers[$name] = new $class($this->container);
}
return $this->helpers[$name];
} }
/** /**
* HTML escaping * Expose helpers with method
* *
* @param string $value Value to escape * @access public
* @return string * @param string $helper
* @return mixed
*/ */
public function e($value) public function getHelper($helper)
{ {
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false); return $this->helpers[$helper];
}
/**
* Register a new Helper
*
* @access public
* @param string $property
* @param string $className
* @return Helper
*/
public function register($property, $className)
{
$container = $this->container;
$this->helpers[$property] = function() use($className, $container) {
return new $className($container);
};
return $this;
} }
} }

View file

@ -12,14 +12,14 @@ use Kanboard\Core\Base;
*/ */
class OAuth2 extends Base class OAuth2 extends Base
{ {
private $clientId; protected $clientId;
private $secret; protected $secret;
private $callbackUrl; protected $callbackUrl;
private $authUrl; protected $authUrl;
private $tokenUrl; protected $tokenUrl;
private $scopes; protected $scopes;
private $tokenType; protected $tokenType;
private $accessToken; protected $accessToken;
/** /**
* Create OAuth2 service * Create OAuth2 service
@ -45,6 +45,33 @@ class OAuth2 extends Base
return $this; return $this;
} }
/**
* Generate OAuth2 state and return the token value
*
* @access public
* @return string
*/
public function getState()
{
if (! isset($this->sessionStorage->oauthState) || empty($this->sessionStorage->oauthState)) {
$this->sessionStorage->oauthState = $this->token->getToken();
}
return $this->sessionStorage->oauthState;
}
/**
* Check the validity of the state (CSRF token)
*
* @access public
* @param string $state
* @return bool
*/
public function isValidateState($state)
{
return $state === $this->getState();
}
/** /**
* Get authorization url * Get authorization url
* *
@ -58,6 +85,7 @@ class OAuth2 extends Base
'client_id' => $this->clientId, 'client_id' => $this->clientId,
'redirect_uri' => $this->callbackUrl, 'redirect_uri' => $this->callbackUrl,
'scope' => implode(' ', $this->scopes), 'scope' => implode(' ', $this->scopes),
'state' => $this->getState(),
); );
return $this->authUrl.'?'.http_build_query($params); return $this->authUrl.'?'.http_build_query($params);
@ -94,6 +122,7 @@ class OAuth2 extends Base
'client_secret' => $this->secret, 'client_secret' => $this->secret,
'redirect_uri' => $this->callbackUrl, 'redirect_uri' => $this->callbackUrl,
'grant_type' => 'authorization_code', 'grant_type' => 'authorization_code',
'state' => $this->getState(),
); );
$response = json_decode($this->httpClient->postForm($this->tokenUrl, $params, array('Accept: application/json')), true); $response = json_decode($this->httpClient->postForm($this->tokenUrl, $params, array('Accept: application/json')), true);

View file

@ -29,7 +29,12 @@ class Request extends Base
* Constructor * Constructor
* *
* @access public * @access public
* @param \Pimple\Container $container * @param \Pimple\Container $container
* @param array $server
* @param array $get
* @param array $post
* @param array $files
* @param array $cookies
*/ */
public function __construct(Container $container, array $server = array(), array $get = array(), array $post = array(), array $files = array(), array $cookies = array()) public function __construct(Container $container, array $server = array(), array $get = array(), array $post = array(), array $files = array(), array $cookies = array())
{ {
@ -211,7 +216,11 @@ class Request extends Base
*/ */
public function isHTTPS() public function isHTTPS()
{ {
return isset($this->server['HTTPS']) && $this->server['HTTPS'] !== '' && $this->server['HTTPS'] !== 'off'; if ($this->getServerVariable('HTTP_X_FORWARDED_PROTO') === 'https') {
return true;
}
return $this->getServerVariable('HTTPS') !== '' && $this->server['HTTPS'] !== 'off';
} }
/** /**

View file

@ -13,6 +13,24 @@ use Kanboard\Core\Csv;
*/ */
class Response extends Base class Response extends Base
{ {
/**
* Send headers to cache a resource
*
* @access public
* @param integer $duration
* @param string $etag
*/
public function cache($duration, $etag = '')
{
header('Pragma: cache');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $duration) . ' GMT');
header('Cache-Control: public, max-age=' . $duration);
if ($etag) {
header('ETag: "' . $etag . '"');
}
}
/** /**
* Send no cache headers * Send no cache headers
* *

View file

@ -3,6 +3,7 @@
namespace Kanboard\Core\Ldap; namespace Kanboard\Core\Ldap;
use LogicException; use LogicException;
use Psr\Log\LoggerInterface;
/** /**
* LDAP Client * LDAP Client
@ -20,6 +21,14 @@ class Client
*/ */
protected $ldap; protected $ldap;
/**
* Logger instance
*
* @access private
* @var LoggerInterface
*/
private $logger;
/** /**
* Establish LDAP connection * Establish LDAP connection
* *
@ -31,7 +40,7 @@ class Client
*/ */
public static function connect($username = null, $password = null) public static function connect($username = null, $password = null)
{ {
$client = new self; $client = new static;
$client->open($client->getLdapServer()); $client->open($client->getLdapServer());
$username = $username ?: $client->getLdapUsername(); $username = $username ?: $client->getLdapUsername();
$password = $password ?: $client->getLdapPassword(); $password = $password ?: $client->getLdapPassword();
@ -60,6 +69,7 @@ class Client
* Establish server connection * Establish server connection
* *
* @access public * @access public
* @throws ClientException
* @param string $server LDAP server hostname or IP * @param string $server LDAP server hostname or IP
* @param integer $port LDAP port * @param integer $port LDAP port
* @param boolean $tls Start TLS * @param boolean $tls Start TLS
@ -98,6 +108,7 @@ class Client
* Anonymous authentication * Anonymous authentication
* *
* @access public * @access public
* @throws ClientException
* @return boolean * @return boolean
*/ */
public function useAnonymousAuthentication() public function useAnonymousAuthentication()
@ -113,6 +124,7 @@ class Client
* Authentication with username/password * Authentication with username/password
* *
* @access public * @access public
* @throws ClientException
* @param string $bind_rdn * @param string $bind_rdn
* @param string $bind_password * @param string $bind_password
* @return boolean * @return boolean
@ -162,4 +174,39 @@ class Client
{ {
return LDAP_PASSWORD; return LDAP_PASSWORD;
} }
/**
* Set logger
*
* @access public
* @param LoggerInterface $logger
* @return Client
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
return $this;
}
/**
* Get logger
*
* @access public
* @return LoggerInterface
*/
public function getLogger()
{
return $this->logger;
}
/**
* Test if a logger is defined
*
* @access public
* @return boolean
*/
public function hasLogger()
{
return $this->logger !== null;
}
} }

View file

@ -48,6 +48,12 @@ class Query
*/ */
public function execute($baseDn, $filter, array $attributes) public function execute($baseDn, $filter, array $attributes)
{ {
if (DEBUG && $this->client->hasLogger()) {
$this->client->getLogger()->debug('BaseDN='.$baseDn);
$this->client->getLogger()->debug('Filter='.$filter);
$this->client->getLogger()->debug('Attributes='.implode(', ', $attributes));
}
$sr = ldap_search($this->client->getConnection(), $baseDn, $filter, $attributes); $sr = ldap_search($this->client->getConnection(), $baseDn, $filter, $attributes);
if ($sr === false) { if ($sr === false) {
return $this; return $this;
@ -78,7 +84,7 @@ class Query
* Get LDAP Entries * Get LDAP Entries
* *
* @access public * @access public
* @return Entities * @return Entries
*/ */
public function getEntries() public function getEntries()
{ {

View file

@ -44,8 +44,7 @@ class User
*/ */
public static function getUser(Client $client, $username) public static function getUser(Client $client, $username)
{ {
$className = get_called_class(); $self = new static(new Query($client));
$self = new $className(new Query($client));
return $self->find($self->getLdapUserPattern($username)); return $self->find($self->getLdapUserPattern($username));
} }
@ -211,14 +210,15 @@ class User
* *
* @access public * @access public
* @param string $username * @param string $username
* @param string $filter
* @return string * @return string
*/ */
public function getLdapUserPattern($username) public function getLdapUserPattern($username, $filter = LDAP_USER_FILTER)
{ {
if (! LDAP_USER_FILTER) { if (! $filter) {
throw new LogicException('LDAP user filter empty, check the parameter LDAP_USER_FILTER'); throw new LogicException('LDAP user filter empty, check the parameter LDAP_USER_FILTER');
} }
return sprintf(LDAP_USER_FILTER, $username); return str_replace('%s', $username, $filter);
} }
} }

View file

@ -41,7 +41,7 @@ class Client extends Base
* @param string $name * @param string $name
* @param string $subject * @param string $subject
* @param string $html * @param string $html
* @return EmailClient * @return Client
*/ */
public function send($email, $name, $subject, $html) public function send($email, $name, $subject, $html)
{ {
@ -70,7 +70,7 @@ class Client extends Base
* *
* @access public * @access public
* @param string $transport * @param string $transport
* @return EmailClientInterface * @return ClientInterface
*/ */
public function getTransport($transport) public function getTransport($transport)
{ {
@ -83,7 +83,7 @@ class Client extends Base
* @access public * @access public
* @param string $transport * @param string $transport
* @param string $class * @param string $class
* @return EmailClient * @return Client
*/ */
public function setTransport($transport, $class) public function setTransport($transport, $class)
{ {

View file

@ -33,6 +33,7 @@ class FileStorage implements ObjectStorageInterface
* Fetch object contents * Fetch object contents
* *
* @access public * @access public
* @throws ObjectStorageException
* @param string $key * @param string $key
* @return string * @return string
*/ */
@ -51,6 +52,7 @@ class FileStorage implements ObjectStorageInterface
* Save object * Save object
* *
* @access public * @access public
* @throws ObjectStorageException
* @param string $key * @param string $key
* @param string $blob * @param string $blob
*/ */
@ -67,6 +69,7 @@ class FileStorage implements ObjectStorageInterface
* Output directly object content * Output directly object content
* *
* @access public * @access public
* @throws ObjectStorageException
* @param string $key * @param string $key
*/ */
public function output($key) public function output($key)
@ -84,6 +87,7 @@ class FileStorage implements ObjectStorageInterface
* Move local file to object storage * Move local file to object storage
* *
* @access public * @access public
* @throws ObjectStorageException
* @param string $src_filename * @param string $src_filename
* @param string $key * @param string $key
* @return boolean * @return boolean
@ -136,6 +140,7 @@ class FileStorage implements ObjectStorageInterface
* Create object folder * Create object folder
* *
* @access private * @access private
* @throws ObjectStorageException
* @param string $key * @param string $key
*/ */
private function createFolder($key) private function createFolder($key)

View file

@ -40,6 +40,17 @@ abstract class Base extends \Kanboard\Core\Base
return array(); return array();
} }
/**
* Returns all helper classes that needs to be stored in the DI container
*
* @access public
* @return array
*/
public function getHelpers()
{
return array();
}
/** /**
* Listen on internal events * Listen on internal events
* *

View file

@ -55,6 +55,7 @@ class Loader extends \Kanboard\Core\Base
* Load plugin * Load plugin
* *
* @access public * @access public
* @throws LogicException
* @param string $plugin * @param string $plugin
*/ */
public function load($plugin) public function load($plugin)
@ -69,6 +70,8 @@ class Loader extends \Kanboard\Core\Base
Tool::buildDic($this->container, $instance->getClasses()); Tool::buildDic($this->container, $instance->getClasses());
Tool::buildDICHelpers($this->container, $instance->getHelpers());
$instance->initialize(); $instance->initialize();
$this->plugins[] = $instance; $this->plugins[] = $instance;
} }

View file

@ -39,7 +39,7 @@ class AccessMap
* *
* @access public * @access public
* @param string $role * @param string $role
* @return Acl * @return AccessMap
*/ */
public function setDefaultRole($role) public function setDefaultRole($role)
{ {
@ -53,7 +53,7 @@ class AccessMap
* @access public * @access public
* @param string $role * @param string $role
* @param array $subroles * @param array $subroles
* @return Acl * @return AccessMap
*/ */
public function setRoleHierarchy($role, array $subroles) public function setRoleHierarchy($role, array $subroles)
{ {
@ -113,7 +113,7 @@ class AccessMap
* @param string $controller Controller class name * @param string $controller Controller class name
* @param mixed $methods List of method name or just one method * @param mixed $methods List of method name or just one method
* @param string $role Lowest role required * @param string $role Lowest role required
* @return Acl * @return AccessMap
*/ */
public function add($controller, $methods, $role) public function add($controller, $methods, $role)
{ {
@ -135,7 +135,7 @@ class AccessMap
* @param string $controller * @param string $controller
* @param string $method * @param string $method
* @param string $role * @param string $role
* @return Acl * @return AccessMap
*/ */
private function addRule($controller, $method, $role) private function addRule($controller, $method, $role)
{ {
@ -157,7 +157,7 @@ class AccessMap
* @access public * @access public
* @param string $controller * @param string $controller
* @param string $method * @param string $method
* @return boolean * @return array
*/ */
public function getRoles($controller, $method) public function getRoles($controller, $method)
{ {

View file

@ -14,7 +14,7 @@ interface OAuthAuthenticationProviderInterface extends AuthenticationProviderInt
* Get user object * Get user object
* *
* @access public * @access public
* @return UserProviderInterface * @return \Kanboard\Core\User\UserProviderInterface
*/ */
public function getUser(); public function getUser();
@ -31,7 +31,7 @@ interface OAuthAuthenticationProviderInterface extends AuthenticationProviderInt
* Get configured OAuth2 service * Get configured OAuth2 service
* *
* @access public * @access public
* @return Kanboard\Core\Http\OAuth2 * @return \Kanboard\Core\Http\OAuth2
*/ */
public function getService(); public function getService();

View file

@ -14,7 +14,7 @@ interface PasswordAuthenticationProviderInterface extends AuthenticationProvider
* Get user object * Get user object
* *
* @access public * @access public
* @return UserProviderInterface * @return \Kanboard\Core\User\UserProviderInterface
*/ */
public function getUser(); public function getUser();

View file

@ -14,7 +14,7 @@ interface PreAuthenticationProviderInterface extends AuthenticationProviderInter
* Get user object * Get user object
* *
* @access public * @access public
* @return UserProviderInterface * @return \Kanboard\Core\User\UserProviderInterface
*/ */
public function getUser(); public function getUser();
} }

View file

@ -21,6 +21,7 @@ namespace Kanboard\Core\Session;
* @property bool $boardCollapsed * @property bool $boardCollapsed
* @property bool $twoFactorBeforeCodeCalled * @property bool $twoFactorBeforeCodeCalled
* @property string $twoFactorSecret * @property string $twoFactorSecret
* @property string $oauthState
*/ */
class SessionStorage class SessionStorage
{ {

View file

@ -3,13 +3,36 @@
namespace Kanboard\Core; namespace Kanboard\Core;
/** /**
* Template class * Template
* *
* @package core * @package core
* @author Frederic Guillot * @author Frederic Guillot
*
* @property \Kanboard\Helper\AppHelper $app
* @property \Kanboard\Helper\AssetHelper $asset
* @property \Kanboard\Helper\DateHelper $dt
* @property \Kanboard\Helper\FileHelper $file
* @property \Kanboard\Helper\FormHelper $form
* @property \Kanboard\Helper\HookHelper $hook
* @property \Kanboard\Helper\ModelHelper $model
* @property \Kanboard\Helper\SubtaskHelper $subtask
* @property \Kanboard\Helper\TaskHelper $task
* @property \Kanboard\Helper\TextHelper $text
* @property \Kanboard\Helper\UrlHelper $url
* @property \Kanboard\Helper\UserHelper $user
* @property \Kanboard\Helper\LayoutHelper $layout
* @property \Kanboard\Helper\ProjectHeaderHelper $projectHeader
*/ */
class Template extends Helper class Template
{ {
/**
* Helper object
*
* @access private
* @var Helper
*/
private $helper;
/** /**
* List of template overrides * List of template overrides
* *
@ -19,47 +42,26 @@ class Template extends Helper
private $overrides = array(); private $overrides = array();
/** /**
* Rendering start time * Template constructor
* *
* @access private * @access public
* @var float * @param Helper $helper
*/ */
private $startTime = 0; public function __construct(Helper $helper)
/**
* Total rendering time
*
* @access private
* @var float
*/
private $renderingTime = 0;
/**
* Method executed before the rendering
*
* @access protected
* @param string $template
*/
protected function beforeRender($template)
{ {
if (DEBUG) { $this->helper = $helper;
$this->startTime = microtime(true);
}
} }
/** /**
* Method executed after the rendering * Expose helpers with magic getter
* *
* @access protected * @access public
* @param string $template * @param string $helper
* @return mixed
*/ */
protected function afterRender($template) public function __get($helper)
{ {
if (DEBUG) { return $this->helper->getHelper($helper);
$duration = microtime(true) - $this->startTime;
$this->renderingTime += $duration;
$this->container['logger']->debug('Rendering '.$template.' in '.$duration.'s, total='.$this->renderingTime);
}
} }
/** /**
@ -76,33 +78,10 @@ class Template extends Helper
*/ */
public function render($__template_name, array $__template_args = array()) public function render($__template_name, array $__template_args = array())
{ {
$this->beforeRender($__template_name);
extract($__template_args); extract($__template_args);
ob_start(); ob_start();
include $this->getTemplateFile($__template_name); include $this->getTemplateFile($__template_name);
$html = ob_get_clean(); return ob_get_clean();
$this->afterRender($__template_name);
return $html;
}
/**
* Render a page layout
*
* @access public
* @param string $template_name Template name
* @param array $template_args Key/value map
* @param string $layout_name Layout name
* @return string
*/
public function layout($template_name, array $template_args = array(), $layout_name = 'layout')
{
return $this->render(
$layout_name,
$template_args + array('content_for_layout' => $this->render($template_name, $template_args))
);
} }
/** /**
@ -120,25 +99,26 @@ class Template extends Helper
/** /**
* Find template filename * Find template filename
* *
* Core template name: 'task/show' * Core template: 'task/show' or 'kanboard:task/show'
* Plugin template name: 'myplugin:task/show' * Plugin template: 'myplugin:task/show'
* *
* @access public * @access public
* @param string $template_name * @param string $template
* @return string * @return string
*/ */
public function getTemplateFile($template_name) public function getTemplateFile($template)
{ {
$template_name = isset($this->overrides[$template_name]) ? $this->overrides[$template_name] : $template_name; $plugin = '';
$template = isset($this->overrides[$template]) ? $this->overrides[$template] : $template;
if (strpos($template_name, ':') !== false) { if (strpos($template, ':') !== false) {
list($plugin, $template) = explode(':', $template_name); list($plugin, $template) = explode(':', $template);
$path = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'plugins';
$path .= DIRECTORY_SEPARATOR.ucfirst($plugin).DIRECTORY_SEPARATOR.'Template'.DIRECTORY_SEPARATOR.$template.'.php';
} else {
$path = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Template'.DIRECTORY_SEPARATOR.$template_name.'.php';
} }
return $path; if ($plugin !== 'kanboard' && $plugin !== '') {
return implode(DIRECTORY_SEPARATOR, array(__DIR__, '..', '..', 'plugins', ucfirst($plugin), 'Template', $template.'.php'));
}
return implode(DIRECTORY_SEPARATOR, array(__DIR__, '..', 'Template', $template.'.php'));
} }
} }

View file

@ -0,0 +1,172 @@
<?php
namespace Kanboard\Core;
/**
* Thumbnail Generator
*
* @package core
* @author Frederic Guillot
*/
class Thumbnail
{
protected $metadata = array();
protected $srcImage;
protected $dstImage;
/**
* Create a thumbnail from a local file
*
* @static
* @access public
* @param string $filename
* @return Thumbnail
*/
public static function createFromFile($filename)
{
$self = new static();
$self->fromFile($filename);
return $self;
}
/**
* Create a thumbnail from a string
*
* @static
* @access public
* @param string $blob
* @return Thumbnail
*/
public static function createFromString($blob)
{
$self = new static();
$self->fromString($blob);
return $self;
}
/**
* Load the local image file in memory with GD
*
* @access public
* @param string $filename
* @return Thumbnail
*/
public function fromFile($filename)
{
$this->metadata = getimagesize($filename);
$this->srcImage = imagecreatefromstring(file_get_contents($filename));
return $this;
}
/**
* Load the image blob in memory with GD
*
* @access public
* @param string $blob
* @return Thumbnail
*/
public function fromString($blob)
{
if (!function_exists('getimagesizefromstring')) {
$uri = 'data://application/octet-stream;base64,' . base64_encode($blob);
$this->metadata = getimagesize($uri);
} else {
$this->metadata = getimagesizefromstring($blob);
}
$this->srcImage = imagecreatefromstring($blob);
return $this;
}
/**
* Resize the image
*
* @access public
* @param int $width
* @param int $height
* @return Thumbnail
*/
public function resize($width = 250, $height = 100)
{
$srcWidth = $this->metadata[0];
$srcHeight = $this->metadata[1];
$dstX = 0;
$dstY = 0;
if ($width == 0 && $height == 0) {
$width = 100;
$height = 100;
}
if ($width > 0 && $height == 0) {
$dstWidth = $width;
$dstHeight = floor($srcHeight * ($width / $srcWidth));
$this->dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
} elseif ($width == 0 && $height > 0) {
$dstWidth = floor($srcWidth * ($height / $srcHeight));
$dstHeight = $height;
$this->dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
} else {
$srcRatio = $srcWidth / $srcHeight;
$resizeRatio = $width / $height;
if ($srcRatio <= $resizeRatio) {
$dstWidth = $width;
$dstHeight = floor($srcHeight * ($width / $srcWidth));
$dstY = ($dstHeight - $height) / 2 * (-1);
} else {
$dstWidth = floor($srcWidth * ($height / $srcHeight));
$dstHeight = $height;
$dstX = ($dstWidth - $width) / 2 * (-1);
}
$this->dstImage = imagecreatetruecolor($width, $height);
}
imagecopyresampled($this->dstImage, $this->srcImage, $dstX, $dstY, 0, 0, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
return $this;
}
/**
* Save the thumbnail to a local file
*
* @access public
* @param string $filename
* @return Thumbnail
*/
public function toFile($filename)
{
imagejpeg($this->dstImage, $filename);
imagedestroy($this->dstImage);
imagedestroy($this->srcImage);
return $this;
}
/**
* Return the thumbnail as a string
*
* @access public
* @return string
*/
public function toString()
{
ob_start();
imagejpeg($this->dstImage, null);
imagedestroy($this->dstImage);
imagedestroy($this->srcImage);
return ob_get_clean();
}
/**
* Output the thumbnail directly to the browser or stdout
*
* @access public
*/
public function toOutput()
{
imagejpeg($this->dstImage, null);
imagedestroy($this->dstImage);
imagedestroy($this->srcImage);
}
}

View file

@ -56,76 +56,23 @@ class Tool
} }
/** /**
* Generate a jpeg thumbnail from an image * Build dependency injection container for custom helpers from an array
* *
* @static * @static
* @access public * @access public
* @param string $src_file Source file image * @param Container $container
* @param string $dst_file Destination file image * @param array $namespaces
* @param integer $resize_width Desired image width * @return Container
* @param integer $resize_height Desired image height
*/ */
public static function generateThumbnail($src_file, $dst_file, $resize_width = 250, $resize_height = 100) public static function buildDICHelpers(Container $container, array $namespaces)
{ {
$metadata = getimagesize($src_file); foreach ($namespaces as $namespace => $classes) {
$src_width = $metadata[0]; foreach ($classes as $name) {
$src_height = $metadata[1]; $class = '\\Kanboard\\'.$namespace.'\\'.$name;
$dst_y = 0; $container['helper']->register($name, $class);
$dst_x = 0;
if (empty($metadata['mime'])) {
return;
}
if ($resize_width == 0 && $resize_height == 0) {
$resize_width = 100;
$resize_height = 100;
}
if ($resize_width > 0 && $resize_height == 0) {
$dst_width = $resize_width;
$dst_height = floor($src_height * ($resize_width / $src_width));
$dst_image = imagecreatetruecolor($dst_width, $dst_height);
} elseif ($resize_width == 0 && $resize_height > 0) {
$dst_width = floor($src_width * ($resize_height / $src_height));
$dst_height = $resize_height;
$dst_image = imagecreatetruecolor($dst_width, $dst_height);
} else {
$src_ratio = $src_width / $src_height;
$resize_ratio = $resize_width / $resize_height;
if ($src_ratio <= $resize_ratio) {
$dst_width = $resize_width;
$dst_height = floor($src_height * ($resize_width / $src_width));
$dst_y = ($dst_height - $resize_height) / 2 * (-1);
} else {
$dst_width = floor($src_width * ($resize_height / $src_height));
$dst_height = $resize_height;
$dst_x = ($dst_width - $resize_width) / 2 * (-1);
} }
$dst_image = imagecreatetruecolor($resize_width, $resize_height);
} }
switch ($metadata['mime']) { return $container;
case 'image/jpeg':
case 'image/jpg':
$src_image = imagecreatefromjpeg($src_file);
break;
case 'image/png':
$src_image = imagecreatefrompng($src_file);
break;
case 'image/gif':
$src_image = imagecreatefromgif($src_file);
break;
default:
return;
}
imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
imagejpeg($dst_image, $dst_file);
imagedestroy($dst_image);
} }
} }

View file

@ -0,0 +1,93 @@
<?php
namespace Kanboard\Core\User\Avatar;
/**
* Avatar Manager
*
* @package avatar
* @author Frederic Guillot
*/
class AvatarManager
{
/**
* Providers
*
* @access private
* @var AvatarProviderInterface[]
*/
private $providers = array();
/**
* Register a new Avatar provider
*
* @access public
* @param AvatarProviderInterface $provider
* @return $this
*/
public function register(AvatarProviderInterface $provider)
{
$this->providers[] = $provider;
return $this;
}
/**
* Render avatar HTML element
*
* @access public
* @param string $user_id
* @param string $username
* @param string $name
* @param string $email
* @param string $avatar_path
* @param int $size
* @return string
*/
public function render($user_id, $username, $name, $email, $avatar_path, $size)
{
$user = array(
'id' => $user_id,
'username' => $username,
'name' => $name,
'email' => $email,
'avatar_path' => $avatar_path,
);
krsort($this->providers);
foreach ($this->providers as $provider) {
if ($provider->isActive($user)) {
return $provider->render($user, $size);
}
}
return '';
}
/**
* Render default provider for unknown users (first provider registered)
*
* @access public
* @param integer $size
* @return string
*/
public function renderDefault($size)
{
if (count($this->providers) > 0) {
ksort($this->providers);
$provider = current($this->providers);
$user = array(
'id' => 0,
'username' => '',
'name' => '?',
'email' => '',
'avatar_path' => '',
);
return $provider->render($user, $size);
}
return '';
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Kanboard\Core\User\Avatar;
/**
* Avatar Provider Interface
*
* @package user
* @author Frederic Guillot
*/
interface AvatarProviderInterface
{
/**
* Render avatar html
*
* @access public
* @param array $user
* @param int $size
*/
public function render(array $user, $size);
/**
* Determine if the provider is active
*
* @access public
* @param array $user
* @return boolean
*/
public function isActive(array $user);
}

View file

@ -13,6 +13,19 @@ use Kanboard\Core\Security\Role;
*/ */
class UserSession extends Base class UserSession extends Base
{ {
/**
* Refresh current session if necessary
*
* @access public
* @param integer $user_id
*/
public function refresh($user_id)
{
if ($this->getId() == $user_id) {
$this->initialize($this->user->getById($user_id));
}
}
/** /**
* Update user session * Update user session
* *
@ -35,6 +48,17 @@ class UserSession extends Base
$this->sessionStorage->postAuthenticationValidated = false; $this->sessionStorage->postAuthenticationValidated = false;
} }
/**
* Get user properties
*
* @access public
* @return array
*/
public function getAll()
{
return $this->sessionStorage->user;
}
/** /**
* Get user application role * Get user application role
* *

View file

@ -1,11 +1,16 @@
<?php <?php
namespace Kanboard\Model; namespace Kanboard\Export;
use Kanboard\Core\Base;
use Kanboard\Model\Task;
use Kanboard\Model\Subtask;
use Kanboard\Model\User;
/** /**
* Subtask Export * Subtask Export
* *
* @package model * @package export
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class SubtaskExport extends Base class SubtaskExport extends Base

View file

@ -1,13 +1,16 @@
<?php <?php
namespace Kanboard\Model; namespace Kanboard\Export;
use Kanboard\Core\Base;
use Kanboard\Core\DateParser;
use Kanboard\Model\Task;
use PDO; use PDO;
/** /**
* Task Export model * Task Export
* *
* @package model * @package export
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskExport extends Base class TaskExport extends Base
@ -106,7 +109,7 @@ class TaskExport extends Base
$task['score'] = $task['score'] ?: 0; $task['score'] = $task['score'] ?: 0;
$task['swimlane_id'] = isset($swimlanes[$task['swimlane_id']]) ? $swimlanes[$task['swimlane_id']] : '?'; $task['swimlane_id'] = isset($swimlanes[$task['swimlane_id']]) ? $swimlanes[$task['swimlane_id']] : '?';
$task = $this->dateParser->format($task, array('date_due', 'date_modification', 'date_creation', 'date_started', 'date_completed'), 'Y-m-d'); $task = $this->dateParser->format($task, array('date_due', 'date_modification', 'date_creation', 'date_started', 'date_completed'), DateParser::DATE_FORMAT);
return $task; return $task;
} }

View file

@ -0,0 +1,77 @@
<?php
namespace Kanboard\Export;
use Kanboard\Core\Base;
use Kanboard\Core\DateParser;
/**
* Transition Export
*
* @package export
* @author Frederic Guillot
*/
class TransitionExport extends Base
{
/**
* 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->transition->getAllByProjectAndDate($project_id, $from, $to);
foreach ($transitions as $transition) {
$results[] = $this->format($transition);
}
return $results;
}
/**
* Get column titles
*
* @access protected
* @return string[]
*/
protected 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 protected
* @param array $transition
* @return array
*/
protected function format(array $transition)
{
$values = array(
(int) $transition['id'],
$transition['title'],
$transition['src_column'],
$transition['dst_column'],
$transition['name'] ?: $transition['username'],
date($this->config->get('application_datetime_format', DateParser::DATE_TIME_FORMAT), $transition['date']),
round($transition['time_spent'] / 3600, 2)
);
return $values;
}
}

View file

@ -85,7 +85,7 @@ class AttachmentLinkProvider extends BaseLinkProvider implements ExternalLinkPro
* Get the link found with the properties * Get the link found with the properties
* *
* @access public * @access public
* @return ExternalLinkInterface * @return \Kanboard\Core\ExternalLink\ExternalLinkInterface
*/ */
public function getLink() public function getLink()
{ {

View file

@ -0,0 +1,26 @@
<?php
namespace Kanboard\ExternalLink;
use Kanboard\Core\ExternalLink\ExternalLinkInterface;
/**
* File Link
*
* @package externalLink
* @author Frederic Guillot
*/
class FileLink extends BaseLink implements ExternalLinkInterface
{
/**
* Get link title
*
* @access public
* @return string
*/
public function getTitle()
{
$path = parse_url($this->url, PHP_URL_PATH);
return basename(str_replace('\\', '/', $path));
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace Kanboard\ExternalLink;
use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface;
/**
* File Link Provider
*
* @package externalLink
* @author Frederic Guillot
*/
class FileLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface
{
/**
* Get provider name
*
* @access public
* @return string
*/
public function getName()
{
return t('Local File');
}
/**
* Get link type
*
* @access public
* @return string
*/
public function getType()
{
return 'file';
}
/**
* Get a dictionary of supported dependency types by the provider
*
* @access public
* @return array
*/
public function getDependencies()
{
return array(
'related' => t('Related'),
);
}
/**
* Return true if the provider can parse correctly the user input
*
* @access public
* @return boolean
*/
public function match()
{
return strpos($this->userInput, 'file://') === 0;
}
/**
* Get the link found with the properties
*
* @access public
* @return \Kanboard\Core\ExternalLink\ExternalLinkInterface
*/
public function getLink()
{
$link = new FileLink($this->container);
$link->setUrl($this->userInput);
return $link;
}
}

View file

@ -65,7 +65,7 @@ class WebLinkProvider extends BaseLinkProvider implements ExternalLinkProviderIn
* Get the link found with the properties * Get the link found with the properties
* *
* @access public * @access public
* @return ExternalLinkInterface * @return \Kanboard\Core\ExternalLink\ExternalLinkInterface
*/ */
public function getLink() public function getLink()
{ {

View file

@ -5,12 +5,12 @@ namespace Kanboard\Helper;
use Kanboard\Core\Base; use Kanboard\Core\Base;
/** /**
* Application helpers * Application Helper
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class App extends Base class AppHelper extends Base
{ {
/** /**
* Get config variable * Get config variable
@ -116,11 +116,11 @@ class App extends Base
$failure_message = $this->flash->getMessage('failure'); $failure_message = $this->flash->getMessage('failure');
if (! empty($success_message)) { if (! empty($success_message)) {
return '<div class="alert alert-success alert-fade-out">'.$this->helper->e($success_message).'</div>'; return '<div class="alert alert-success alert-fade-out">'.$this->helper->text->e($success_message).'</div>';
} }
if (! empty($failure_message)) { if (! empty($failure_message)) {
return '<div class="alert alert-error">'.$this->helper->e($failure_message).'</div>'; return '<div class="alert alert-error">'.$this->helper->text->e($failure_message).'</div>';
} }
return ''; return '';

View file

@ -2,18 +2,21 @@
namespace Kanboard\Helper; namespace Kanboard\Helper;
use Kanboard\Core\Base;
/** /**
* Assets helpers * Asset Helper
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Asset extends \Kanboard\Core\Base class AssetHelper extends Base
{ {
/** /**
* Add a Javascript asset * Add a Javascript asset
* *
* @param string $filename Filename * @param string $filename Filename
* @param bool $async
* @return string * @return string
*/ */
public function js($filename, $async = false) public function js($filename, $async = false)

View file

@ -0,0 +1,68 @@
<?php
namespace Kanboard\Helper;
use Kanboard\Core\Base;
/**
* Avatar Helper
*
* @package helper
* @author Frederic Guillot
*/
class AvatarHelper extends Base
{
/**
* Render user avatar
*
* @access public
* @param string $user_id
* @param string $username
* @param string $name
* @param string $email
* @param string $avatar_path
* @param string $css
* @param int $size
* @return string
*/
public function render($user_id, $username, $name, $email, $avatar_path, $css = 'avatar-left', $size = 48)
{
if (empty($user_id) && empty($username)) {
$html = $this->avatarManager->renderDefault($size);
} else {
$html = $this->avatarManager->render($user_id, $username, $name, $email, $avatar_path, $size);
}
return '<div class="avatar avatar-'.$size.' '.$css.'">'.$html.'</div>';
}
/**
* Render small user avatar
*
* @access public
* @param string $user_id
* @param string $username
* @param string $name
* @param string $email
* @param string $avatar_path
* @param string $css
* @return string
*/
public function small($user_id, $username, $name, $email, $avatar_path, $css = '')
{
return $this->render($user_id, $username, $name, $email, $avatar_path, $css, 20);
}
/**
* Get a small avatar for the current user
*
* @access public
* @param string $css
* @return string
*/
public function currentUserSmall($css = '')
{
$user = $this->userSession->getAll();
return $this->small($user['id'], $user['username'], $user['name'], $user['email'], $user['avatar_path'], $css);
}
}

View file

@ -2,13 +2,15 @@
namespace Kanboard\Helper; namespace Kanboard\Helper;
use Kanboard\Core\Base;
/** /**
* Board Helper * Board Helper
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Board extends \Kanboard\Core\Base class BoardHelper extends Base
{ {
/** /**
* Return true if tasks are collapsed * Return true if tasks are collapsed

View file

@ -3,6 +3,7 @@
namespace Kanboard\Helper; namespace Kanboard\Helper;
use DateTime; use DateTime;
use Kanboard\Core\Base;
/** /**
* DateTime helpers * DateTime helpers
@ -10,7 +11,7 @@ use DateTime;
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Dt extends \Kanboard\Core\Base class DateHelper extends Base
{ {
/** /**
* Get formatted time * Get formatted time

View file

@ -2,13 +2,15 @@
namespace Kanboard\Helper; namespace Kanboard\Helper;
use Kanboard\Core\Base;
/** /**
* File helpers * File helpers
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class File extends \Kanboard\Core\Base class FileHelper extends Base
{ {
/** /**
* Get file icon * Get file icon

View file

@ -10,7 +10,7 @@ use Kanboard\Core\Base;
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Form extends Base class FormHelper extends Base
{ {
/** /**
* Hidden CSRF token field * Hidden CSRF token field
@ -40,11 +40,12 @@ class Form extends Base
* Display a select field * Display a select field
* *
* @access public * @access public
* @param string $name Field name * @param string $name Field name
* @param array $options Options * @param array $options Options
* @param array $values Form values * @param array $values Form values
* @param array $errors Form errors * @param array $errors Form errors
* @param string $class CSS class * @param array $attributes
* @param string $class CSS class
* @return string * @return string
*/ */
public function select($name, array $options, array $values = array(), array $errors = array(), array $attributes = array(), $class = '') public function select($name, array $options, array $values = array(), array $errors = array(), array $attributes = array(), $class = '')
@ -52,7 +53,7 @@ class Form extends Base
$html = '<select name="'.$name.'" id="form-'.$name.'" class="'.$class.'" '.implode(' ', $attributes).'>'; $html = '<select name="'.$name.'" id="form-'.$name.'" class="'.$class.'" '.implode(' ', $attributes).'>';
foreach ($options as $id => $value) { foreach ($options as $id => $value) {
$html .= '<option value="'.$this->helper->e($id).'"'; $html .= '<option value="'.$this->helper->text->e($id).'"';
if (isset($values->$name) && $id == $values->$name) { if (isset($values->$name) && $id == $values->$name) {
$html .= ' selected="selected"'; $html .= ' selected="selected"';
@ -61,7 +62,7 @@ class Form extends Base
$html .= ' selected="selected"'; $html .= ' selected="selected"';
} }
$html .= '>'.$this->helper->e($value).'</option>'; $html .= '>'.$this->helper->text->e($value).'</option>';
} }
$html .= '</select>'; $html .= '</select>';
@ -103,7 +104,7 @@ class Form extends Base
*/ */
public function radio($name, $label, $value, $selected = false, $class = '') public function radio($name, $label, $value, $selected = false, $class = '')
{ {
return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.$this->helper->e($value).'" '.($selected ? 'checked="checked"' : '').'> '.$this->helper->e($label).'</label>'; return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.$this->helper->text->e($value).'" '.($selected ? 'checked="checked"' : '').'> '.$this->helper->text->e($label).'</label>';
} }
/** /**
@ -139,7 +140,7 @@ class Form extends Base
*/ */
public function checkbox($name, $label, $value, $checked = false, $class = '') public function checkbox($name, $label, $value, $checked = false, $class = '')
{ {
return '<label><input type="checkbox" name="'.$name.'" class="'.$class.'" value="'.$this->helper->e($value).'" '.($checked ? 'checked="checked"' : '').'>&nbsp;'.$this->helper->e($label).'</label>'; return '<label><input type="checkbox" name="'.$name.'" class="'.$class.'" value="'.$this->helper->text->e($value).'" '.($checked ? 'checked="checked"' : '').'>&nbsp;'.$this->helper->text->e($label).'</label>';
} }
/** /**
@ -153,7 +154,7 @@ class Form extends Base
*/ */
public function label($label, $name, array $attributes = array()) public function label($label, $name, array $attributes = array())
{ {
return '<label for="form-'.$name.'" '.implode(' ', $attributes).'>'.$this->helper->e($label).'</label>'; return '<label for="form-'.$name.'" '.implode(' ', $attributes).'>'.$this->helper->text->e($label).'</label>';
} }
/** /**
@ -173,7 +174,7 @@ class Form extends Base
$html = '<textarea name="'.$name.'" id="form-'.$name.'" class="'.$class.'" '; $html = '<textarea name="'.$name.'" id="form-'.$name.'" class="'.$class.'" ';
$html .= implode(' ', $attributes).'>'; $html .= implode(' ', $attributes).'>';
$html .= isset($values->$name) ? $this->helper->e($values->$name) : isset($values[$name]) ? $values[$name] : ''; $html .= isset($values->$name) ? $this->helper->text->e($values->$name) : isset($values[$name]) ? $values[$name] : '';
$html .= '</textarea>'; $html .= '</textarea>';
$html .= $this->errorList($errors, $name); $html .= $this->errorList($errors, $name);
@ -334,7 +335,7 @@ class Form extends Base
$html .= '<ul class="form-errors">'; $html .= '<ul class="form-errors">';
foreach ($errors[$name] as $error) { foreach ($errors[$name] as $error) {
$html .= '<li>'.$this->helper->e($error).'</li>'; $html .= '<li>'.$this->helper->text->e($error).'</li>';
} }
$html .= '</ul>'; $html .= '</ul>';
@ -354,9 +355,9 @@ class Form extends Base
private function formValue($values, $name) private function formValue($values, $name)
{ {
if (isset($values->$name)) { if (isset($values->$name)) {
return 'value="'.$this->helper->e($values->$name).'"'; return 'value="'.$this->helper->text->e($values->$name).'"';
} }
return isset($values[$name]) ? 'value="'.$this->helper->e($values[$name]).'"' : ''; return isset($values[$name]) ? 'value="'.$this->helper->text->e($values[$name]).'"' : '';
} }
} }

View file

@ -2,13 +2,15 @@
namespace Kanboard\Helper; namespace Kanboard\Helper;
use Kanboard\Core\Base;
/** /**
* Template Hook helpers * Template Hook helpers
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Hook extends \Kanboard\Core\Base class HookHelper extends Base
{ {
/** /**
* Add assets JS or CSS * Add assets JS or CSS
@ -54,7 +56,7 @@ class Hook extends \Kanboard\Core\Base
* @access public * @access public
* @param string $hook * @param string $hook
* @param string $template * @param string $template
* @return \Helper\Hook * @return \Kanboard\Helper\Hook
*/ */
public function attach($hook, $template) public function attach($hook, $template)
{ {

View file

@ -5,12 +5,12 @@ namespace Kanboard\Helper;
use Kanboard\Core\Base; use Kanboard\Core\Base;
/** /**
* Layout helpers * Layout Helper
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Layout extends Base class LayoutHelper extends Base
{ {
/** /**
* Render a template without the layout if Ajax request * Render a template without the layout if Ajax request
@ -30,7 +30,7 @@ class Layout extends Base
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
} }
return $this->template->layout($template, $params); return $this->pageLayout($template, $params);
} }
/** /**
@ -60,7 +60,7 @@ class Layout extends Base
*/ */
public function task($template, array $params) public function task($template, array $params)
{ {
$params['title'] = $params['task']['title']; $params['title'] = $params['task']['project_name'];
return $this->subLayout('task/layout', 'task/sidebar', $template, $params); return $this->subLayout('task/layout', 'task/sidebar', $template, $params);
} }
@ -146,7 +146,24 @@ class Layout extends Base
} }
/** /**
* Common method to generate a sublayout * Render page layout
*
* @access public
* @param string $template Template name
* @param array $params Key/value dictionary
* @param string $layout Layout name
* @return string
*/
public function pageLayout($template, array $params = array(), $layout = 'layout')
{
return $this->template->render(
$layout,
$params + array('content_for_layout' => $this->template->render($template, $params))
);
}
/**
* Common method to generate a sub-layout
* *
* @access public * @access public
* @param string $sublayout * @param string $sublayout

View file

@ -0,0 +1,94 @@
<?php
namespace Kanboard\Helper;
use Kanboard\Core\Base;
/**
* Model Helper
*
* @package helper
* @author Frederic Guillot
*/
class ModelHelper extends Base
{
/**
* Remove keys from an array
*
* @access public
* @param array $values Input array
* @param string[] $keys List of keys to remove
*/
public function removeFields(array &$values, array $keys)
{
foreach ($keys as $key) {
if (array_key_exists($key, $values)) {
unset($values[$key]);
}
}
}
/**
* Remove keys from an array if empty
*
* @access public
* @param array $values Input array
* @param string[] $keys List of keys to remove
*/
public function removeEmptyFields(array &$values, array $keys)
{
foreach ($keys as $key) {
if (array_key_exists($key, $values) && empty($values[$key])) {
unset($values[$key]);
}
}
}
/**
* Force fields to be at 0 if empty
*
* @access public
* @param array $values Input array
* @param string[] $keys List of keys
*/
public function resetFields(array &$values, array $keys)
{
foreach ($keys as $key) {
if (isset($values[$key]) && empty($values[$key])) {
$values[$key] = 0;
}
}
}
/**
* Force some fields to be integer
*
* @access public
* @param array $values Input array
* @param string[] $keys List of keys
*/
public function convertIntegerFields(array &$values, array $keys)
{
foreach ($keys as $key) {
if (isset($values[$key])) {
$values[$key] = (int) $values[$key];
}
}
}
/**
* Force some fields to be null if empty
*
* @access public
* @param array $values Input array
* @param string[] $keys List of keys
*/
public function convertNullFields(array &$values, array $keys)
{
foreach ($keys as $key) {
if (array_key_exists($key, $values) && empty($values[$key])) {
$values[$key] = null;
}
}
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace Kanboard\Helper;
use Kanboard\Core\Base;
/**
* Project Header Helper
*
* @package helper
* @author Frederic Guillot
*/
class ProjectHeaderHelper extends Base
{
/**
* Get current search query
*
* @access public
* @param array $project
* @return string
*/
public function getSearchQuery(array $project)
{
$search = $this->request->getStringParam('search', $this->userSession->getFilters($project['id']));
$this->userSession->setFilters($project['id'], $search);
return urldecode($search);
}
/**
* Render project header (views switcher and search box)
*
* @access public
* @param array $project
* @param string $controller
* @param string $action
* @param bool $boardView
* @return string
*/
public function render(array $project, $controller, $action, $boardView = false)
{
$filters = array(
'controller' => $controller,
'action' => $action,
'project_id' => $project['id'],
'search' => $this->getSearchQuery($project),
);
return $this->template->render('project_header/header', array(
'project' => $project,
'filters' => $filters,
'categories_list' => $this->category->getList($project['id'], false),
'users_list' => $this->projectUserRole->getAssignableUsersList($project['id'], false),
'custom_filters_list' => $this->customFilter->getAll($project['id'], $this->userSession->getId()),
'board_view' => $boardView,
));
}
/**
* Get project description
*
* @access public
* @param array &$project
* @return string
*/
public function getDescription(array &$project)
{
if ($project['owner_id'] > 0) {
$description = t('Project owner: ').'**'.$this->helper->text->e($project['owner_name'] ?: $project['owner_username']).'**'.PHP_EOL.PHP_EOL;
if (! empty($project['description'])) {
$description .= '***'.PHP_EOL.PHP_EOL;
$description .= $project['description'];
}
} else {
$description = $project['description'];
}
return $description;
}
}

View file

@ -2,13 +2,15 @@
namespace Kanboard\Helper; namespace Kanboard\Helper;
use Kanboard\Core\Base;
/** /**
* Subtask helpers * Subtask helpers
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Subtask extends \Kanboard\Core\Base class SubtaskHelper extends Base
{ {
public function getTitle(array $subtask) public function getTitle(array $subtask)
{ {
@ -20,7 +22,7 @@ class Subtask extends \Kanboard\Core\Base
$html = '<i class="fa fa-check-square-o fa-fw"></i>'; $html = '<i class="fa fa-check-square-o fa-fw"></i>';
} }
return $html.$this->helper->e($subtask['title']); return $html.$this->helper->text->e($subtask['title']);
} }
/** /**

View file

@ -10,7 +10,7 @@ use Kanboard\Core\Base;
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Task extends Base class TaskHelper extends Base
{ {
/** /**
* Local cache for project columns * Local cache for project columns

View file

@ -11,8 +11,19 @@ use Kanboard\Core\Base;
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Text extends Base class TextHelper extends Base
{ {
/**
* HTML escaping
*
* @param string $value Value to escape
* @return string
*/
public function e($value)
{
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
}
/** /**
* Markdown transformation * Markdown transformation
* *
@ -88,7 +99,7 @@ class Text extends Base
public function in($id, array $listing, $default_value = '?') public function in($id, array $listing, $default_value = '?')
{ {
if (isset($listing[$id])) { if (isset($listing[$id])) {
return $this->helper->e($listing[$id]); return $this->helper->text->e($listing[$id]);
} }
return $default_value; return $default_value;

View file

@ -5,12 +5,12 @@ namespace Kanboard\Helper;
use Kanboard\Core\Base; use Kanboard\Core\Base;
/** /**
* Url helpers * Url Helper
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class Url extends Base class UrlHelper extends Base
{ {
private $base = ''; private $base = '';
private $directory = ''; private $directory = '';
@ -29,17 +29,37 @@ class Url extends Base
} }
/** /**
* HTML Link tag * Button Link Element
* *
* @access public * @access public
* @param string $label Link label * @param string $icon Font-Awesome icon
* @param string $controller Controller name * @param string $label Link label
* @param string $action Action name * @param string $controller Controller name
* @param array $params Url parameters * @param string $action Action name
* @param boolean $csrf Add a CSRF token * @param array $params Url parameters
* @param string $class CSS class attribute * @param string $class CSS class attribute
* @param boolean $new_tab Open the link in a new tab * @return string
* @param string $anchor Link Anchor */
public function button($icon, $label, $controller, $action, array $params = array(), $class = '')
{
$icon = '<i class="fa '.$icon.' fa-fw"></i> ';
$class = 'btn '.$class;
return $this->link($icon.$label, $controller, $action, $params, false, $class);
}
/**
* Link element
*
* @access public
* @param string $label Link label
* @param string $controller Controller name
* @param string $action Action name
* @param array $params Url parameters
* @param boolean $csrf Add a CSRF token
* @param string $class CSS class attribute
* @param string $title
* @param boolean $new_tab Open the link in a new tab
* @param string $anchor Link Anchor
* @return string * @return string
*/ */
public function link($label, $controller, $action, array $params = array(), $csrf = false, $class = '', $title = '', $new_tab = false, $anchor = '') public function link($label, $controller, $action, array $params = array(), $csrf = false, $class = '', $title = '', $new_tab = false, $anchor = '')

View file

@ -2,13 +2,15 @@
namespace Kanboard\Helper; namespace Kanboard\Helper;
use Kanboard\Core\Base;
/** /**
* User helpers * User helpers
* *
* @package helper * @package helper
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class User extends \Kanboard\Core\Base class UserHelper extends Base
{ {
/** /**
* Return true if the logged user as unread notifications * Return true if the logged user as unread notifications
@ -32,7 +34,7 @@ class User extends \Kanboard\Core\Base
{ {
$initials = ''; $initials = '';
foreach (explode(' ', $name) as $string) { foreach (explode(' ', $name, 2) as $string) {
$initials .= mb_substr($string, 0, 1); $initials .= mb_substr($string, 0, 1);
} }
@ -154,23 +156,6 @@ class User extends \Kanboard\Core\Base
*/ */
public function getFullname(array $user = array()) public function getFullname(array $user = array())
{ {
return $this->user->getFullname(empty($user) ? $this->sessionStorage->user : $user); return $this->user->getFullname(empty($user) ? $this->userSession->getAll() : $user);
}
/**
* 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->helper->e($alt).'" title="'.$this->helper->e($alt).'">';
}
return '';
} }
} }

View file

@ -1,7 +1,8 @@
<?php <?php
namespace Kanboard\Model; namespace Kanboard\Import;
use Kanboard\Core\Base;
use Kanboard\Core\Csv; use Kanboard\Core\Csv;
use SimpleValidator\Validator; use SimpleValidator\Validator;
use SimpleValidator\Validators; use SimpleValidator\Validators;
@ -9,7 +10,7 @@ use SimpleValidator\Validators;
/** /**
* Task Import * Task Import
* *
* @package model * @package import
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskImport extends Base class TaskImport extends Base
@ -126,7 +127,7 @@ class TaskImport extends Base
$values['date_due'] = $this->dateParser->getTimestampFromIsoFormat($row['date_due']); $values['date_due'] = $this->dateParser->getTimestampFromIsoFormat($row['date_due']);
} }
$this->removeEmptyFields( $this->helper->model->removeEmptyFields(
$values, $values,
array('owner_id', 'creator_id', 'color_id', 'column_id', 'category_id', 'swimlane_id', 'date_due') array('owner_id', 'creator_id', 'color_id', 'column_id', 'category_id', 'swimlane_id', 'date_due')
); );

View file

@ -1,16 +1,18 @@
<?php <?php
namespace Kanboard\Model; namespace Kanboard\Import;
use Kanboard\Model\User;
use SimpleValidator\Validator; use SimpleValidator\Validator;
use SimpleValidator\Validators; use SimpleValidator\Validators;
use Kanboard\Core\Security\Role; use Kanboard\Core\Security\Role;
use Kanboard\Core\Base;
use Kanboard\Core\Csv; use Kanboard\Core\Csv;
/** /**
* User Import * User Import
* *
* @package model * @package import
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class UserImport extends Base class UserImport extends Base
@ -91,7 +93,7 @@ class UserImport extends Base
unset($row['is_admin']); unset($row['is_admin']);
unset($row['is_manager']); unset($row['is_manager']);
$this->removeEmptyFields($row, array('password', 'email', 'name')); $this->helper->model->removeEmptyFields($row, array('password', 'email', 'name'));
return $row; return $row;
} }

View file

@ -8,7 +8,6 @@ return array(
'Edit' => 'Uredi', 'Edit' => 'Uredi',
'remove' => 'ukloni', 'remove' => 'ukloni',
'Remove' => 'Ukloni', 'Remove' => 'Ukloni',
'Update' => 'Ažuriraj',
'Yes' => 'Da', 'Yes' => 'Da',
'No' => 'Ne', 'No' => 'Ne',
'cancel' => 'odustani', 'cancel' => 'odustani',
@ -56,25 +55,23 @@ return array(
'Project' => 'Projekat', 'Project' => 'Projekat',
'Status' => 'Status', 'Status' => 'Status',
'Tasks' => 'Zadatak', 'Tasks' => 'Zadatak',
'Board' => 'Tabla', 'Board' => 'Ploča',
'Actions' => 'Akcije', 'Actions' => 'Akcije',
'Inactive' => 'Neaktivan', 'Inactive' => 'Neaktivan',
'Active' => 'Aktivan', 'Active' => 'Aktivan',
'Add this column' => 'Dodaj kolonu',
'%d tasks on the board' => '%d zadataka na tabli', '%d tasks on the board' => '%d zadataka na tabli',
'%d tasks in total' => '%d zadataka ukupno', '%d tasks in total' => '%d zadataka ukupno',
'Unable to update this board.' => 'Nemogu da ažuriram ovu tablu.', 'Unable to update this board.' => 'Nemogu da ažuriram ovu ploču.',
'Edit board' => 'Izmijeni tablu', 'Edit board' => 'Izmijeni ploču',
'Disable' => 'Onemogući', 'Disable' => 'Onemogući',
'Enable' => 'Omogući', 'Enable' => 'Omogući',
'New project' => 'Novi projekat', 'New project' => 'Novi projekat',
'Do you really want to remove this project: "%s"?' => 'Da li želiš da ukloniš projekat: "%s"?', 'Do you really want to remove this project: "%s"?' => 'Da li želiš da ukloniš projekat: "%s"?',
'Remove project' => 'Ukloni projekat', 'Remove project' => 'Ukloni projekat',
'Edit the board for "%s"' => 'Uredi tablu za "%s"', 'Edit the board for "%s"' => 'Uredi ploču za "%s"',
'All projects' => 'Svi projekti', 'All projects' => 'Svi projekti',
'Add a new column' => 'Dodaj novu kolonu', 'Add a new column' => 'Dodaj novu kolonu',
'Title' => 'Naslov', 'Title' => 'Naslov',
'Nobody assigned' => 'Niko nije dodijeljen',
'Assigned to %s' => 'Dodijeljen korisniku %s', 'Assigned to %s' => 'Dodijeljen korisniku %s',
'Remove a column' => 'Ukloni kolonu', 'Remove a column' => 'Ukloni kolonu',
'Remove a column from a board' => 'Ukloni kolonu sa table', 'Remove a column from a board' => 'Ukloni kolonu sa table',
@ -100,7 +97,7 @@ return array(
'New task' => 'Novi zadatak', 'New task' => 'Novi zadatak',
'Open a task' => 'Otvori zadatak', 'Open a task' => 'Otvori zadatak',
'Do you really want to open this task: "%s"?' => 'Da li zaista želiš da otvoriš zadatak: "%s"?', 'Do you really want to open this task: "%s"?' => 'Da li zaista želiš da otvoriš zadatak: "%s"?',
'Back to the board' => 'Nazad na tablu', 'Back to the board' => 'Nazad na ploču',
'There is nobody assigned' => 'Niko nije dodijeljen!', 'There is nobody assigned' => 'Niko nije dodijeljen!',
'Column on the board:' => 'Kolona na tabli:', 'Column on the board:' => 'Kolona na tabli:',
'Close this task' => 'Zatvori ovaj zadatak', 'Close this task' => 'Zatvori ovaj zadatak',
@ -148,7 +145,7 @@ return array(
'Unable to update your user.' => 'Nije moguće ažuriranje korisnika.', 'Unable to update your user.' => 'Nije moguće ažuriranje korisnika.',
'User removed successfully.' => 'Korisnik uspješno uklonjen.', 'User removed successfully.' => 'Korisnik uspješno uklonjen.',
'Unable to remove this user.' => 'Nije moguće uklanjanje korisnika.', 'Unable to remove this user.' => 'Nije moguće uklanjanje korisnika.',
'Board updated successfully.' => 'Tabla uspješno ažurirana.', 'Board updated successfully.' => 'Ploča je uspješno ažurirana.',
'Ready' => 'Spreman', 'Ready' => 'Spreman',
'Backlog' => 'Zaliha', 'Backlog' => 'Zaliha',
'Work in progress' => 'U izradi', 'Work in progress' => 'U izradi',
@ -168,7 +165,6 @@ return array(
'Task count' => 'Broj zadataka', 'Task count' => 'Broj zadataka',
'User' => 'Korisnik', 'User' => 'Korisnik',
'Comments' => 'Komentari', 'Comments' => 'Komentari',
'Write your text in Markdown' => 'Pisanje teksta pomoću Markdown',
'Leave a comment' => 'Ostavi komentar', 'Leave a comment' => 'Ostavi komentar',
'Comment is required' => 'Komentar je obavezan', 'Comment is required' => 'Komentar je obavezan',
'Leave a description' => 'Dodaj opis', 'Leave a description' => 'Dodaj opis',
@ -184,7 +180,6 @@ return array(
'Unable to remove this action.' => 'Nije moguće obrisati akciju', 'Unable to remove this action.' => 'Nije moguće obrisati akciju',
'Action removed successfully.' => 'Akcija obrisana', 'Action removed successfully.' => 'Akcija obrisana',
'Automatic actions for the project "%s"' => 'Akcije za automatizaciju projekta "%s"', 'Automatic actions for the project "%s"' => 'Akcije za automatizaciju projekta "%s"',
'Defined actions' => 'Definisane akcje',
'Add an action' => 'dodaj akcju', 'Add an action' => 'dodaj akcju',
'Event name' => 'Naziv događaja', 'Event name' => 'Naziv događaja',
'Action name' => 'Naziv akcije', 'Action name' => 'Naziv akcije',
@ -194,7 +189,6 @@ return array(
'When the selected event occurs execute the corresponding action.' => 'Na izabrani događaj izvrši odgovarajuću akciju', 'When the selected event occurs execute the corresponding action.' => 'Na izabrani događaj izvrši odgovarajuću akciju',
'Next step' => 'Slijedeći korak', 'Next step' => 'Slijedeći korak',
'Define action parameters' => 'Definiši parametre akcije', '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"?', 'Do you really want to remove this action: "%s"?' => 'Da li da obrišem akciju "%s"?',
'Remove an automatic action' => 'Obriši automatsku akciju', 'Remove an automatic action' => 'Obriši automatsku akciju',
'Assign the task to a specific user' => 'Dodijeli zadatak određenom korisniku', 'Assign the task to a specific user' => 'Dodijeli zadatak određenom korisniku',
@ -333,10 +327,10 @@ return array(
'Time tracking:' => 'Praćenje vremena:', 'Time tracking:' => 'Praćenje vremena:',
'New sub-task' => 'Novi pod-zadatak', 'New sub-task' => 'Novi pod-zadatak',
'New attachment added "%s"' => 'Ubačen novi prilog "%s"', 'New attachment added "%s"' => 'Ubačen novi prilog "%s"',
'Comment updated' => 'Komentar ažuriran',
'New comment posted by %s' => '%s ostavio novi komentar', 'New comment posted by %s' => '%s ostavio novi komentar',
'New attachment' => 'Novi prilog', 'New attachment' => 'Novi prilog',
'New comment' => 'Novi komentar', 'New comment' => 'Novi komentar',
'Comment updated' => 'Komentar ažuriran',
'New subtask' => 'Novi pod-zadatak', 'New subtask' => 'Novi pod-zadatak',
'Subtask updated' => 'Pod-zadatak ažuriran', 'Subtask updated' => 'Pod-zadatak ažuriran',
'Task updated' => 'Zadatak ažuriran', 'Task updated' => 'Zadatak ažuriran',
@ -404,7 +398,7 @@ return array(
'%s moved the task #%d to the position %d in the column "%s"' => '%s premjestio zadatak #%d na poziciju %d u koloni "%s"', '%s moved the task #%d to the position %d in the column "%s"' => '%s premjestio zadatak #%d na poziciju %d u koloni "%s"',
'Activity' => 'Aktivnosti', 'Activity' => 'Aktivnosti',
'Default values are "%s"' => 'Podrazumijevane vrijednosti su: "%s"', 'Default values are "%s"' => 'Podrazumijevane vrijednosti su: "%s"',
'Default columns for new projects (Comma-separated)' => 'Podrazumijevane kolone za novi projekat (Odvojeni zarezom)', 'Default columns for new projects (Comma-separated)' => 'Podrazumijevane kolone za novi projekat (Odvojene zarezom)',
'Task assignee change' => 'Promijena izvršioca zadatka', 'Task assignee change' => 'Promijena izvršioca zadatka',
'%s change the assignee of the task #%d to %s' => '%s zamijeni izvršioca za zadatak #%d u %s', '%s change the assignee of the task #%d to %s' => '%s zamijeni izvršioca za zadatak #%d u %s',
'%s changed the assignee of the task %s to %s' => '%s promijenio izvršioca za zadatak %s u %s', '%s changed the assignee of the task %s to %s' => '%s promijenio izvršioca za zadatak %s u %s',
@ -416,7 +410,7 @@ return array(
'Reference' => 'Referenca', 'Reference' => 'Referenca',
'Label' => 'Etiketa', 'Label' => 'Etiketa',
'Database' => 'Baza', 'Database' => 'Baza',
'About' => 'O', 'About' => 'O Kanboardu',
'Database driver:' => 'Database driver:', 'Database driver:' => 'Database driver:',
'Board settings' => 'Postavke table', 'Board settings' => 'Postavke table',
'URL and token' => 'URL i token', 'URL and token' => 'URL i token',
@ -424,23 +418,22 @@ return array(
'URL for task creation:' => 'URL za kreiranje zadataka', 'URL for task creation:' => 'URL za kreiranje zadataka',
'Reset token' => 'Resetuj token', 'Reset token' => 'Resetuj token',
'API endpoint:' => 'API endpoint', 'API endpoint:' => 'API endpoint',
'Refresh interval for private board' => 'Interval osvježavanja privatnih tabli', 'Refresh interval for private board' => 'Interval osvježavanja privatnih ploča',
'Refresh interval for public board' => 'Interval osvježavanja javnih tabli', 'Refresh interval for public board' => 'Interval osvježavanja javnih ploča',
'Task highlight period' => 'Period naznačavanja zadatka', 'Task highlight period' => 'Period naznačavanja zadatka',
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Period (u sekundama) u kom su se događale promjene na zadatku (0 je onemogućeno, 2 dana je uobičajeno)', 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Period (u sekundama) u kom su se događale promjene na zadatku (0 je onemogućeno, 2 dana je uobičajeno)',
'Frequency in second (60 seconds by default)' => 'Frekvencija u sekundama (60 sekundi je uobičajeno)', 'Frequency in second (60 seconds by default)' => 'Frekvencija u sekundama (60 sekundi je uobičajeno)',
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvencija u sekundama (0 je onemogućeno u budućnosti, 10 sekundi je uobičajeno)', 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvencija u sekundama (0 je onemogućeno u budućnosti, 10 sekundi je uobičajeno)',
'Application URL' => 'URL aplikacje', 'Application URL' => 'URL aplikacije',
'Token regenerated.' => 'Token regenerisan.', 'Token regenerated.' => 'Token regenerisan.',
'Date format' => 'Format datuma', 'Date format' => 'Format datuma',
'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO je uvek prihvatljiv, primjer: "%s", "%s"', 'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO je uvijek prihvatljiv, primjer: "%s", "%s"',
'New private project' => 'Novi privatni projekat', 'New private project' => 'Novi privatni projekat',
'This project is private' => 'Ovaj projekat je privatan', 'This project is private' => 'Ovaj projekat je privatan',
'Type here to create a new sub-task' => 'Piši ovdje za kreiranje novog pod-zadatka',
'Add' => 'Dodaj', 'Add' => 'Dodaj',
'Start date' => 'Datum početka', 'Start date' => 'Datum početka',
'Time estimated' => 'Procijenjeno vrijeme', 'Time estimated' => 'Procijenjeno vrijeme',
'There is nothing assigned to you.' => 'Ništa vam nije dodijeljeno', 'There is nothing assigned to you.' => 'Ništa ti nije dodijeljeno',
'My tasks' => 'Moji zadaci', 'My tasks' => 'Moji zadaci',
'Activity stream' => 'Spisak aktivnosti', 'Activity stream' => 'Spisak aktivnosti',
'Dashboard' => 'Panel', 'Dashboard' => 'Panel',
@ -487,9 +480,6 @@ return array(
'Daily project summary export for "%s"' => 'Izvoz zbirnog pregleda po danima za "%s"', 'Daily project summary export for "%s"' => 'Izvoz zbirnog pregleda po danima za "%s"',
'Exports' => 'Izvozi', 'Exports' => 'Izvozi',
'This export contains the number of tasks per column grouped per day.' => 'Ovaj izvoz sadržava broj zadataka po koloni grupisanih po danima.', 'This export contains the number of tasks per column grouped per day.' => 'Ovaj izvoz sadržava broj zadataka po koloni grupisanih po danima.',
'Nothing to preview...' => 'Ništa za pokazati...',
'Preview' => 'Pregled',
'Write' => 'Piši',
'Active swimlanes' => 'Aktivne swimline trake', 'Active swimlanes' => 'Aktivne swimline trake',
'Add a new swimlane' => 'Dodaj novu swimline traku', 'Add a new swimlane' => 'Dodaj novu swimline traku',
'Change default swimlane' => 'Preimenuj podrazumijevanu swimline traku', 'Change default swimlane' => 'Preimenuj podrazumijevanu swimline traku',
@ -543,7 +533,6 @@ return array(
'Task age in days' => 'Trajanje zadatka u danima', 'Task age in days' => 'Trajanje zadatka u danima',
'Days in this column' => 'Dani u ovoj koloni', 'Days in this column' => 'Dani u ovoj koloni',
'%dd' => '%dd', '%dd' => '%dd',
'Add a link' => 'Dodaj vezu',
'Add a new link' => 'Dodaj novu vezu', 'Add a new link' => 'Dodaj novu vezu',
'Do you really want to remove this link: "%s"?' => 'Da li zaista želite ukloniti ovu vezu: "%s"?', 'Do you really want to remove this link: "%s"?' => 'Da li zaista želite ukloniti ovu vezu: "%s"?',
'Do you really want to remove this link with task #%d?' => 'Da li zaista želite ukloniti ovu vezu sa zadatkom #%d?', 'Do you really want to remove this link with task #%d?' => 'Da li zaista želite ukloniti ovu vezu sa zadatkom #%d?',
@ -598,7 +587,7 @@ return array(
'CHF - Swiss Francs' => 'CHF - Švicarski franak', 'CHF - Swiss Francs' => 'CHF - Švicarski franak',
'Custom Stylesheet' => 'Prilagođeni stil', 'Custom Stylesheet' => 'Prilagođeni stil',
'download' => 'preuzmi', 'download' => 'preuzmi',
'EUR - Euro' => 'EUR - Evro', 'EUR - Euro' => 'EUR - Euro',
'GBP - British Pound' => 'GBP - Britanska funta', 'GBP - British Pound' => 'GBP - Britanska funta',
'INR - Indian Rupee' => 'INR - Indijski rupi', 'INR - Indian Rupee' => 'INR - Indijski rupi',
'JPY - Japanese Yen' => 'JPY - Japanski jen', 'JPY - Japanese Yen' => 'JPY - Japanski jen',
@ -619,7 +608,7 @@ return array(
'Rate' => 'Stopa', 'Rate' => 'Stopa',
'Change reference currency' => 'Promijeni referencu valute', 'Change reference currency' => 'Promijeni referencu valute',
'Add a new currency rate' => 'Dodaj novu stopu valute', 'Add a new currency rate' => 'Dodaj novu stopu valute',
'Reference currency' => 'Referenca valute', 'Reference currency' => 'Referentna valuta',
'The currency rate have been added successfully.' => 'Stopa valute je uspješno dodana.', 'The currency rate have been added successfully.' => 'Stopa valute je uspješno dodana.',
'Unable to add this currency rate.' => 'Nemoguće dodati stopu valute.', 'Unable to add this currency rate.' => 'Nemoguće dodati stopu valute.',
'Webhook URL' => 'Webhook URL', 'Webhook URL' => 'Webhook URL',
@ -734,7 +723,7 @@ return array(
'Time spent changed: %sh' => 'Utrošeno vrijeme je promijenjeno: %sh', 'Time spent changed: %sh' => 'Utrošeno vrijeme je promijenjeno: %sh',
'Time estimated changed: %sh' => 'Očekivano vrijeme je promijenjeno: %sh', 'Time estimated changed: %sh' => 'Očekivano vrijeme je promijenjeno: %sh',
'The field "%s" have been updated' => 'Polje "%s" je ažurirano', 'The field "%s" have been updated' => 'Polje "%s" je ažurirano',
'The description have been modified' => 'Promijenjen opis', 'The description has been modified:' => 'Promijenjen opis:',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Da li zaista želiš zatvoriti zadatak "%s" kao i sve pod-zadatke?', 'Do you really want to close the task "%s" as well as all subtasks?' => 'Da li zaista želiš zatvoriti zadatak "%s" kao i sve pod-zadatke?',
'I want to receive notifications for:' => 'Želim dobijati obavještenja za:', 'I want to receive notifications for:' => 'Želim dobijati obavještenja za:',
'All tasks' => 'Sve zadatke', 'All tasks' => 'Sve zadatke',
@ -753,8 +742,6 @@ return array(
'My activity stream' => 'Tok mojih aktivnosti', 'My activity stream' => 'Tok mojih aktivnosti',
'My calendar' => 'Moj kalendar', 'My calendar' => 'Moj kalendar',
'Search tasks' => 'Pretraga zadataka', 'Search tasks' => 'Pretraga zadataka',
'Back to the calendar' => 'Vrati na kalendar',
'Filters' => 'Filteri',
'Reset filters' => 'Vrati filtere na početno', 'Reset filters' => 'Vrati filtere na početno',
'My tasks due tomorrow' => 'Moji zadaci koje treba završiti sutra', 'My tasks due tomorrow' => 'Moji zadaci koje treba završiti sutra',
'Tasks due today' => 'Zadaci koje treba završiti danas', 'Tasks due today' => 'Zadaci koje treba završiti danas',
@ -765,8 +752,8 @@ return array(
'Not assigned' => 'Bez izvršioca', 'Not assigned' => 'Bez izvršioca',
'View advanced search syntax' => 'Vidi naprednu sintaksu pretrage', 'View advanced search syntax' => 'Vidi naprednu sintaksu pretrage',
'Overview' => 'Opšti pregled', 'Overview' => 'Opšti pregled',
'Board/Calendar/List view' => 'Pregle Table/Kalendara/Liste', 'Board/Calendar/List view' => 'Pregled Ploče/Kalendara/Liste',
'Switch to the board view' => 'Promijeni da vidim tablu', 'Switch to the board view' => 'Promijeni da vidim ploču',
'Switch to the calendar view' => 'Promijeni da vidim kalendar', 'Switch to the calendar view' => 'Promijeni da vidim kalendar',
'Switch to the list view' => 'Promijeni da vidim listu', 'Switch to the list view' => 'Promijeni da vidim listu',
'Go to the search/filter box' => 'Idi na kutiju s pretragom/filterima', 'Go to the search/filter box' => 'Idi na kutiju s pretragom/filterima',
@ -850,11 +837,10 @@ return array(
'Gantt chart for all projects' => 'Gantogram za sve projekte', 'Gantt chart for all projects' => 'Gantogram za sve projekte',
'Projects list' => 'Lista projekata', 'Projects list' => 'Lista projekata',
'Gantt chart for this project' => 'Gantogram za ovaj projekat', 'Gantt chart for this project' => 'Gantogram za ovaj projekat',
'Project board' => 'Tabla projekta', 'Project board' => 'Ploča projekta',
'End date:' => 'Datum završetka:', 'End date:' => 'Datum završetka:',
'There is no start date or end date for this project.' => 'Nema početnog ili krajnjeg datuma za ovaj projekat.', 'There is no start date or end date for this project.' => 'Nema početnog ili krajnjeg datuma za ovaj projekat.',
'Projects Gantt chart' => 'Gantogram projekata', 'Projects Gantt chart' => 'Gantogram projekata',
'Link type' => 'Tip veze',
'Change task color when using a specific task link' => 'Promijeni boju zadatka kada se koristi određena veza na zadatku', 'Change task color when using a specific task link' => 'Promijeni boju zadatka kada se koristi određena veza na zadatku',
'Task link creation or modification' => 'Veza na zadatku je napravljena ili izmijenjena', 'Task link creation or modification' => 'Veza na zadatku je napravljena ili izmijenjena',
'Milestone' => 'Prekretnica', 'Milestone' => 'Prekretnica',
@ -895,18 +881,17 @@ return array(
'Assignee changed on task #%d' => 'Promijenjen izvršilac na zadatku #%d', 'Assignee changed on task #%d' => 'Promijenjen izvršilac na zadatku #%d',
'%d overdue tasks' => '%d zadataka kasni', '%d overdue tasks' => '%d zadataka kasni',
'Task #%d is overdue' => 'Zadatak #%d kasni', 'Task #%d is overdue' => 'Zadatak #%d kasni',
'No new notifications.' => 'Nema novi obavještenja.', 'No new notifications.' => 'Nema novih obavještenja.',
'Mark all as read' => 'Označi sve kao pročitano', 'Mark all as read' => 'Označi sve kao pročitano',
'Mark as read' => 'Označi kao pročitano', 'Mark as read' => 'Označi kao pročitano',
'Total number of tasks in this column across all swimlanes' => 'Ukupan broj zadataka u ovoj koloni u svim swimline trakama', 'Total number of tasks in this column across all swimlanes' => 'Ukupan broj zadataka u ovoj koloni u svim swimline trakama',
'Collapse swimlane' => 'Skupi swimline trake', 'Collapse swimlane' => 'Skupi swimline trake',
'Expand swimlane' => 'Proširi swimline trake', 'Expand swimlane' => 'Proširi swimline trake',
'Add a new filter' => 'Dodaj novi filter', 'Add a new filter' => 'Dodaj novi filter',
'Share with all project members' => 'Podijeli s svim članovima projekta', 'Share with all project members' => 'Podijeli sa svim članovima projekta',
'Shared' => 'Podijeljeno', 'Shared' => 'Podijeljeno',
'Owner' => 'Vlasnik', 'Owner' => 'Vlasnik',
'Unread notifications' => 'Nepročitana obavještenja', 'Unread notifications' => 'Nepročitana obavještenja',
'My filters' => 'Moji filteri',
'Notification methods:' => 'Metode obavještenja:', 'Notification methods:' => 'Metode obavještenja:',
'Import tasks from CSV file' => 'Uvezi zadatke putem CSV fajla', 'Import tasks from CSV file' => 'Uvezi zadatke putem CSV fajla',
'Unable to read your file' => 'Nemoguće pročitati fajl', 'Unable to read your file' => 'Nemoguće pročitati fajl',
@ -936,216 +921,236 @@ return array(
'Your file must use the predefined CSV format' => 'File mora biti predefinisani CSV format', 'Your file must use the predefined CSV format' => 'File mora biti predefinisani CSV format',
'Your file must be encoded in UTF-8' => 'File mora biti u UTF-8 kodu', 'Your file must be encoded in UTF-8' => 'File mora biti u UTF-8 kodu',
'The first row must be the header' => 'Prvi red mora biti zaglavlje', 'The first row must be the header' => 'Prvi red mora biti zaglavlje',
'Duplicates are not verified for you' => 'Dipliciranje nisu potvrđena', 'Duplicates are not verified for you' => 'Dupliciranja neće biti provjeravana (to ćeš morati uraditi ručno)',
'The due date must use the ISO format: YYYY-MM-DD' => 'Datum do kog se treba izvršiti mora biti u ISO formatu: GGGG-MM-DD', 'The due date must use the ISO format: YYYY-MM-DD' => 'Datum do kog se treba izvršiti mora biti u ISO formatu: GGGG-MM-DD',
'Download CSV template' => 'Preuzmi CSV šablon', 'Download CSV template' => 'Preuzmi CSV šablon',
'No external integration registered.' => 'Nema registrovanih vanjskih integracija.', 'No external integration registered.' => 'Nema registrovanih vanjskih integracija.',
'Duplicates are not imported' => 'Duplikati nisu uvezeni', 'Duplicates are not imported' => 'Duplikati nisu uvezeni',
'Usernames must be lowercase and unique' => 'Korisničko ime mora biti malim slovima i jedinstveno', 'Usernames must be lowercase and unique' => 'Korisničko ime mora biti malim slovima i jedinstveno',
'Passwords will be encrypted if present' => 'Šifra će biti kriptovana', 'Passwords will be encrypted if present' => 'Šifra će biti kriptovana',
// '%s attached a new file to the task %s' => '', '%s attached a new file to the task %s' => '%s je dodano novi fajl u zadatak %s',
'Link type' => 'Tip veze',
'Assign automatically a category based on a link' => 'Automatsko pridruživanje kategorije bazirano na vezi', 'Assign automatically a category based on a link' => 'Automatsko pridruživanje kategorije bazirano na vezi',
'BAM - Konvertible Mark' => 'BAM - Konvertibilna marka', 'BAM - Konvertible Mark' => 'BAM - Konvertibilna marka',
// 'Assignee Username' => '', 'Assignee Username' => 'Pridruži korisničko ime',
// 'Assignee Name' => '', 'Assignee Name' => 'Pridruži ime',
// 'Groups' => '', 'Groups' => 'Grupe',
// 'Members of %s' => '', 'Members of %s' => 'Članovi %s',
// 'New group' => '', 'New group' => 'Nova grupa',
// 'Group created successfully.' => '', 'Group created successfully.' => 'Grupa uspješno kreirana.',
// 'Unable to create your group.' => '', 'Unable to create your group.' => 'Nemoguće kreirati grupu.',
// 'Edit group' => '', 'Edit group' => 'Uredi grupu',
// 'Group updated successfully.' => '', 'Group updated successfully.' => 'Grupa uspješno ažurirana.',
// 'Unable to update your group.' => '', 'Unable to update your group.' => 'Nemoguće ažurirati grupu',
// 'Add group member to "%s"' => '', 'Add group member to "%s"' => 'Dodaj člana grupe u ""%s"',
// 'Group member added successfully.' => '', 'Group member added successfully.' => 'Uspješno dodan član grupe.',
// 'Unable to add group member.' => '', 'Unable to add group member.' => 'Nemoguće dodati člana grupe.',
// 'Remove user from group "%s"' => '', 'Remove user from group "%s"' => 'Ukloni korisnika iz grupe "%s"',
// 'User removed successfully from this group.' => '', 'User removed successfully from this group.' => 'Korisnik uspješno uklonjen iz grupe.',
// 'Unable to remove this user from the group.' => '', 'Unable to remove this user from the group.' => 'Nemoguće ukloniti korisnika iz grupe.',
// 'Remove group' => '', 'Remove group' => 'Ukloni grupu',
// 'Group removed successfully.' => '', 'Group removed successfully.' => 'Grupa uspješno uklonjena.',
// 'Unable to remove this group.' => '', 'Unable to remove this group.' => 'Nemoguće ukloniti grupu.',
// 'Project Permissions' => '', 'Project Permissions' => 'Prava na projektu',
// 'Manager' => '', 'Manager' => 'Menadžer',
// 'Project Manager' => '', 'Project Manager' => 'Menadžer projekta',
// 'Project Member' => '', 'Project Member' => 'Član projekta',
// 'Project Viewer' => '', 'Project Viewer' => 'Preglednik projekta',
// 'Your account is locked for %d minutes' => '', 'Your account is locked for %d minutes' => 'Tvoj korisnički račun je zaključan za narednih %d minuta',
// 'Invalid captcha' => '', 'Invalid captcha' => 'Pogrešna captcha',
// 'The name must be unique' => '', 'The name must be unique' => 'Ime mora biti jedinstveno',
// 'View all groups' => '', 'View all groups' => 'Pregledaj sve grupe',
// 'View group members' => '', 'View group members' => 'Pregledaj članove grupe',
// 'There is no user available.' => '', 'There is no user available.' => 'Trenutno nema dostupnih korisnika.',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Da li zaista želiš ukloniti korisnika "%s" iz grupe "%s"?',
// 'There is no group.' => '', 'There is no group.' => 'Trenutno nema grupa.',
// 'External Id' => '', 'External Id' => 'Vanjski Id',
// 'Add group member' => '', 'Add group member' => 'Dodaj člana grupe',
// 'Do you really want to remove this group: "%s"?' => '', 'Do you really want to remove this group: "%s"?' => 'Da li zaista želiš ukloniti ovu grupu: "%s"?',
// 'There is no user in this group.' => '', 'There is no user in this group.' => 'Trenutno nema korisnika u grupi.',
// 'Remove this user' => '', 'Remove this user' => 'Ukloni ovog korisnika',
// 'Permissions' => '', 'Permissions' => 'Prava',
// 'Allowed Users' => '', 'Allowed Users' => 'Dozvoljeni korisnici',
// 'No user have been allowed specifically.' => '', 'No user have been allowed specifically.' => 'Nema korisnika sa specijalnim dozvolama.',
// 'Role' => '', 'Role' => 'Uloge',
// 'Enter user name...' => '', 'Enter user name...' => 'Unesi korisničko ime...',
// 'Allowed Groups' => '', 'Allowed Groups' => 'Dozvoljene grupe',
// 'No group have been allowed specifically.' => '', 'No group have been allowed specifically.' => 'Nema grupa sa specijalnim dozvolama.',
// 'Group' => '', 'Group' => 'Grupa',
// 'Group Name' => '', 'Group Name' => 'Ime grupe',
// 'Enter group name...' => '', 'Enter group name...' => 'Unesi ime grupe...',
// 'Role:' => '', 'Role:' => 'Uloga:',
'Project members' => 'Članovi projekta', 'Project members' => 'Članovi projekta',
// 'Compare hours for "%s"' => '', 'Compare hours for "%s"' => 'Poredi sate za "%s"',
// '%s mentioned you in the task #%d' => '', '%s mentioned you in the task #%d' => '%s te spomenuo u zadatku #%d',
// '%s mentioned you in a comment on the task #%d' => '', '%s mentioned you in a comment on the task #%d' => '%s te spomenuo u komentaru zadatka #%d',
// 'You were mentioned in the task #%d' => '', 'You were mentioned in the task #%d' => 'Spomenut si u zadatku #%d',
// 'You were mentioned in a comment on the task #%d' => '', 'You were mentioned in a comment on the task #%d' => 'Spomenut si u komentaru zadatka #%d',
// 'Mentioned' => '', 'Mentioned' => 'Spominjanja',
// 'Compare Estimated Time vs Actual Time' => '', 'Compare Estimated Time vs Actual Time' => 'Poređenje očekivanog i aktuelnog vremena',
// 'Estimated hours: ' => '', 'Estimated hours: ' => 'Očekivani sati:',
// 'Actual hours: ' => '', 'Actual hours: ' => 'Aktuelni sati:',
// 'Hours Spent' => '', 'Hours Spent' => 'Utrošeni sati:',
// 'Hours Estimated' => '', 'Hours Estimated' => 'Očekivani sati',
// 'Estimated Time' => '', 'Estimated Time' => 'Očekivano vrijeme',
// 'Actual Time' => '', 'Actual Time' => 'Aktuelno vrijeme',
// 'Estimated vs actual time' => '', 'Estimated vs actual time' => 'Očekivano nasuprot aktuelnog vremena',
// 'RUB - Russian Ruble' => '', 'RUB - Russian Ruble' => 'RUB - Ruski rubij',
// 'Assign the task to the person who does the action when the column is changed' => '', 'Assign the task to the person who does the action when the column is changed' => 'Dodijeli zadatak osobi koja izvrši akciju promjene kolone',
// 'Close a task in a specific column' => '', 'Close a task in a specific column' => 'Zatvori zadatak u određenoj koloni',
// 'Time-based One-time Password Algorithm' => '', 'Time-based One-time Password Algorithm' => 'Vremenski bazirani One-time Algoritam šifri',
// 'Two-Factor Provider: ' => '', 'Two-Factor Provider: ' => 'Two-Factor provajder',
// 'Disable two-factor authentication' => '', 'Disable two-factor authentication' => 'Onemogući two-factor autentifikaciju',
// 'Enable two-factor authentication' => '', 'Enable two-factor authentication' => 'Omogući two-factor autentifikaciju',
// 'There is no integration registered at the moment.' => '', 'There is no integration registered at the moment.' => 'Trenutno nema registrovanih integracija.',
// 'Password Reset for Kanboard' => '', 'Password Reset for Kanboard' => 'Promjena šifre za Kanboard',
// 'Forgot password?' => '', 'Forgot password?' => 'Ne mogu da se sjetim šifre?',
// 'Enable "Forget Password"' => '', 'Enable "Forget Password"' => 'Omogući "Ne mogu da se sjetim šifre"',
// 'Password Reset' => '', 'Password Reset' => 'Promijena šifre',
// 'New password' => '', 'New password' => 'Nova šifra',
// 'Change Password' => '', 'Change Password' => 'Promijeni šifru',
// 'To reset your password click on this link:' => '', 'To reset your password click on this link:' => 'Da bi promijenio šifru klikni na ovaj link:',
// 'Last Password Reset' => '', 'Last Password Reset' => 'Posljednja promjena šifre',
// 'The password has never been reinitialized.' => '', 'The password has never been reinitialized.' => 'Šifra nije nikada promijenjena.',
// 'Creation' => '', 'Creation' => 'Napravljena',
// 'Expiration' => '', 'Expiration' => 'Ističe',
// 'Password reset history' => '', 'Password reset history' => 'Hitorija promjena šifri',
// 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Svi zadaci kolone "%s" i swimline-a "%s" uspješno su zatvoreni.',
// 'Do you really want to close all tasks of this column?' => '', 'Do you really want to close all tasks of this column?' => 'Da li zaista želiš zatvoriti sve zadatke ove kolone?',
// '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d zadatak(ci) u koloni "%s" i swimline-u "%s" će biti zatvoreni.',
// 'Close all tasks of this column' => '', 'Close all tasks of this column' => 'Zatvori sve zadatke ove kolone',
// 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nema posebnog plugin-a za obavještenja. Još uvijek možeš koristiti individualne postavke obavještenja na svom profilu.',
// 'My dashboard' => '', 'My dashboard' => 'Moj panel',
// 'My profile' => '', 'My profile' => 'Moj profil',
// 'Project owner: ' => '', 'Project owner: ' => 'Vlasnik projekta:',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identifikator projekta je opcionalan i mora biti alfanumerički, na primjer: MOJPROJEKAT.',
// 'Project owner' => '', 'Project owner' => 'Vlasnik projekta',
// 'Those dates are useful for the project Gantt chart.' => '', 'Those dates are useful for the project Gantt chart.' => 'Ovi datumi su korisni za pravljenje Gantt dijagrama za projekat.',
// 'Private projects do not have users and groups management.' => '', 'Private projects do not have users and groups management.' => 'Privatni projekti ne mogu imati korisnike ili grupe korisnika.',
// 'There is no project member.' => '', 'There is no project member.' => 'Nema članova projekta.',
// 'Priority' => '', 'Priority' => 'Prioritet',
// 'Task priority' => '', 'Task priority' => 'Prioritet zadatka',
// 'General' => '', 'General' => 'Opšte',
// 'Dates' => '', 'Dates' => 'Datumi',
// 'Default priority' => '', 'Default priority' => 'Podrazumijevani prioritet',
// 'Lowest priority' => '', 'Lowest priority' => 'Najmanji prioritet',
// 'Highest priority' => '', 'Highest priority' => 'Najveći prioritet',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '', 'If you put zero to the low and high priority, this feature will be disabled.' => 'Ako upišeš nulu za najmanji i najveći prioritet, ova opcija će biti onemogućena.',
// 'Close a task when there is no activity' => '', 'Close a task when there is no activity' => 'Zatvori zadatak kada nema aktivnosti',
// 'Duration in days' => '', 'Duration in days' => 'Dužina trajanja u danima',
// 'Send email when there is no activity on a task' => '', 'Send email when there is no activity on a task' => 'Pošalji email kada nema aktivnosti na zadatku',
// 'List of external links' => '', 'Unable to fetch link information.' => 'Ne mogu da pribavim informacije o vezi.',
// 'Unable to fetch link information.' => '', 'Daily background job for tasks' => 'Dnevni pozadinski poslovi na zadacima',
// 'Daily background job for tasks' => '', 'Auto' => 'Automatski',
// 'Auto' => '', 'Related' => 'Povezani',
// 'Related' => '', 'Attachment' => 'Dodijeljeni',
// 'Attachment' => '', 'Title not found' => 'Bez naslova',
// 'Title not found' => '', 'Web Link' => 'Web veza',
// 'Web Link' => '', 'External links' => 'Vanjska veza',
// 'External links' => '', 'Add external link' => 'Dodaj vanjsku vezu',
// 'Add external link' => '', 'Type' => 'Vrsta',
// 'Type' => '', 'Dependency' => 'Zavisnost',
// 'Dependency' => '', 'Add internal link' => 'Dodaj unutrašnju vezu',
// 'Add internal link' => '', 'Add a new external link' => 'Dodaj novu vanjsku vezu',
// 'Add a new external link' => '', 'Edit external link' => 'Uredi vanjsku vezu',
// 'Edit external link' => '', 'External link' => 'Vanjska veza',
// 'External link' => '', 'Copy and paste your link here...' => 'Kopiraj i zalijepi svoju vezu ovdje...',
// 'Copy and paste your link here...' => '', 'URL' => 'URL',
// 'URL' => '', 'Internal links' => 'Unutrašnje veze',
// 'There is no external link for the moment.' => '', 'Assign to me' => 'Dodijeli meni',
// 'Internal links' => '', 'Me' => 'Za mene',
// 'There is no internal link for the moment.' => '', 'Do not duplicate anything' => 'Ništa ne dupliciraj',
// 'Assign to me' => '', 'Projects management' => 'Menadžment projekata',
// 'Me' => '', 'Users management' => 'Menadžment korisnika',
// 'Do not duplicate anything' => '', 'Groups management' => 'Menadžment grupa',
// 'Projects management' => '', 'Create from another project' => 'Napravi iz drugog projekta',
// 'Users management' => '', 'open' => 'otvoreno',
// 'Groups management' => '', 'closed' => 'zatvoreno',
// 'Create from another project' => '', 'Priority:' => 'Prioritet:',
// 'There is no subtask at the moment.' => '', 'Reference:' => 'Preporuka:',
// 'open' => '', 'Complexity:' => 'Složenost:',
// 'closed' => '', 'Swimlane:' => 'Swimline:',
// 'Priority:' => '', 'Column:' => 'Kolona:',
// 'Reference:' => '', 'Position:' => 'Pozicija:',
// 'Complexity:' => '', 'Creator:' => 'Kreator:',
// 'Swimlane:' => '', 'Time estimated:' => 'Očekivano vrijeme:',
// 'Column:' => '', '%s hours' => '%s sati',
// 'Position:' => '', 'Time spent:' => 'Utrošeno vrijeme:',
// 'Creator:' => '', 'Created:' => 'Kreirao:',
// 'Time estimated:' => '', 'Modified:' => 'Uredio:',
// '%s hours' => '', 'Completed:' => 'Završio:',
// 'Time spent:' => '', 'Started:' => 'Početo:',
// 'Created:' => '', 'Moved:' => 'Pomjereno:',
// 'Modified:' => '', 'Task #%d' => 'Zadatak #%d',
// 'Completed:' => '', 'Date and time format' => 'Format za datum i vrijeme',
// 'Started:' => '', 'Time format' => 'Format za vrijeme',
// 'Moved:' => '', 'Start date: ' => 'Početni datum:',
// 'Task #%d' => '', 'End date: ' => 'Krajnji datum:',
// 'Sub-tasks' => '', 'New due date: ' => 'Novi datum očekivanja:',
// 'Date and time format' => '', 'Start date changed: ' => 'Početni datum promijenjen:',
// 'Time format' => '', 'Disable private projects' => 'Onemogući privatne projekte',
// 'Start date: ' => '', 'Do you really want to remove this custom filter: "%s"?' => 'Da li zaista želiš ukloniti ovaj prilagođeni filter "%s"?',
// 'End date: ' => '', 'Remove a custom filter' => 'Ukloni prilagođeni filter',
// 'New due date: ' => '', 'User activated successfully.' => 'Korisnik uspješno aktiviran.',
// 'Start date changed: ' => '', 'Unable to enable this user.' => 'Nemoguće omogućiti ovog korisnika.',
// 'Disable private projects' => '', 'User disabled successfully.' => 'Korisnik uspješno onemogućen.',
// 'Do you really want to remove this custom filter: "%s"?' => '', 'Unable to disable this user.' => 'Nemoguće onemogućiti ovog korisnika.',
// 'Remove a custom filter' => '', 'All files have been uploaded successfully.' => 'Svi fajlovi su uspješno dodani.',
// 'User activated successfully.' => '', 'View uploaded files' => 'Pregled dodanih fajlova',
// 'Unable to enable this user.' => '', 'The maximum allowed file size is %sB.' => 'Maksimalna dozvoljena veličina fajla je %sB.',
// 'User disabled successfully.' => '', 'Choose files again' => 'Izaberi ponovo fajlove',
// 'Unable to disable this user.' => '', 'Drag and drop your files here' => 'Povuci i spusti svoje fajlove ovdje',
// 'All files have been uploaded successfully.' => '', 'choose files' => 'izaberi fajlove',
// 'View uploaded files' => '', 'View profile' => 'Pregledaj profil',
// 'The maximum allowed file size is %sB.' => '', 'Two Factor' => 'Dva faktora',
// 'Choose files again' => '', 'Disable user' => 'Onemogući korisnika',
// 'Drag and drop your files here' => '', 'Do you really want to disable this user: "%s"?' => 'Da li zaista želiš onemogućiti ovog korisnika: "%s"?',
// 'choose files' => '', 'Enable user' => 'Omogući korisnika',
// 'View profile' => '', 'Do you really want to enable this user: "%s"?' => 'Da li zaista želiš omogućiti ovog korisnika: "%s"?',
// 'Two Factor' => '', 'Download' => 'Preuzeto',
// 'Disable user' => '', 'Uploaded: %s' => 'Dodano: %s',
// 'Do you really want to disable this user: "%s"?' => '', 'Size: %s' => 'Veličina: %s',
// 'Enable user' => '', 'Uploaded by %s' => 'Dodao %s',
// 'Do you really want to enable this user: "%s"?' => '', 'Filename' => 'Ime fajla',
// 'Download' => '', 'Size' => 'Veličina',
// 'Uploaded: %s' => '', 'Column created successfully.' => 'Kolona uspješno napravljena.',
// 'Size: %s' => '', 'Another column with the same name exists in the project' => 'Već postoji kolona s istim imenom u ovom projektu.',
// 'Uploaded by %s' => '', 'Default filters' => 'Podrazumijevani filteri',
// 'Filename' => '', 'Your board doesn\'t have any column!' => 'Vaš panel nema ni jednu kolonu!',
// 'Size' => '', 'Change column position' => 'Promijeni poziciju kolone',
// 'Column created successfully.' => '', 'Switch to the project overview' => 'Promijeni u pregled projekta',
// 'Another column with the same name exists in the project' => '', 'User filters' => 'Korisnički filteri',
// 'Default filters' => '', 'Category filters' => 'Kategorija filtera',
// 'Your board doesn\'t have any column!' => '', 'Upload a file' => 'Dodaj fajl',
// 'Change column position' => '', 'View file' => 'Pregled fajla',
// 'Switch to the project overview' => '', 'Last activity' => 'Posljednja aktivnost',
// 'User filters' => '', 'Change subtask position' => 'Promijeni poziciju pod-zadatka',
// 'Category filters' => '', 'This value must be greater than %d' => 'Ova vrijednost mora biti veća od %d',
// 'Upload a file' => '', 'Another swimlane with the same name exists in the project' => 'Već postoji swimline sa istim imenom u ovom projektu',
// 'There is no attachment at the moment.' => '', 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Na primjer: http://example.kanboard.net/ (koristi se za pravljenje apsolutnog URL-a)',
// 'View file' => '', 'Actions duplicated successfully.' => 'Akcije uspješno duplicirane.',
// 'Last activity' => '', 'Unable to duplicate actions.' => 'Nemoguće duplicirati akcije.',
// 'Change subtask position' => '', 'Add a new action' => 'Dodaj novu akciju',
// 'This value must be greater than %d' => '', 'Import from another project' => 'Uvezi iz drugog projekta',
// 'Another swimlane with the same name exists in the project' => '', 'There is no action at the moment.' => 'Trenutno nema akcija.',
// 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '', 'Import actions from another project' => 'Uvezi akcije iz drugog projekta',
'There is no available project.' => 'Trenutno nema dostupnih projekata.',
// 'Local File' => '',
// 'Configuration' => '',
// 'PHP version:' => '',
// 'PHP SAPI:' => '',
// 'OS version:' => '',
// 'Database version:' => '',
// 'Browser:' => '',
// 'Task view' => '',
// 'Edit task' => '',
// 'Edit description' => '',
// 'New internal link' => '',
// 'Display list of keyboard shortcuts' => '',
// 'Menu' => '',
// 'Set start date' => '',
// 'Avatar' => '',
// 'Upload my avatar image' => '',
// 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '',
); );

View file

@ -8,7 +8,6 @@ return array(
'Edit' => 'Editovat', 'Edit' => 'Editovat',
'remove' => 'odstranit', 'remove' => 'odstranit',
'Remove' => 'Odstranit', 'Remove' => 'Odstranit',
'Update' => 'Akualizovat',
'Yes' => 'Ano', 'Yes' => 'Ano',
'No' => 'Ne', 'No' => 'Ne',
'cancel' => 'Zrušit', 'cancel' => 'Zrušit',
@ -60,7 +59,6 @@ return array(
'Actions' => 'Akce', 'Actions' => 'Akce',
'Inactive' => 'Neaktivní', 'Inactive' => 'Neaktivní',
'Active' => 'Aktivní', 'Active' => 'Aktivní',
'Add this column' => 'Přidat sloupec',
'%d tasks on the board' => '%d úkolů na nástěnce', '%d tasks on the board' => '%d úkolů na nástěnce',
'%d tasks in total' => '%d úkolů celkem', '%d tasks in total' => '%d úkolů celkem',
'Unable to update this board.' => 'Nástěnku není možné aktualizovat', 'Unable to update this board.' => 'Nástěnku není možné aktualizovat',
@ -74,7 +72,6 @@ return array(
'All projects' => 'Všechny projekty', 'All projects' => 'Všechny projekty',
'Add a new column' => 'Přidat nový sloupec', 'Add a new column' => 'Přidat nový sloupec',
'Title' => 'Název', 'Title' => 'Název',
'Nobody assigned' => 'Nepřiřazena žádná osoba',
'Assigned to %s' => 'Přiřazeno uživateli: %s', 'Assigned to %s' => 'Přiřazeno uživateli: %s',
'Remove a column' => 'Vyjmout sloupec', 'Remove a column' => 'Vyjmout sloupec',
'Remove a column from a board' => 'Vyjmout sloupec z nástěnky', 'Remove a column from a board' => 'Vyjmout sloupec z nástěnky',
@ -168,7 +165,6 @@ return array(
'Task count' => 'Počet úkolů', 'Task count' => 'Počet úkolů',
'User' => 'Uživatel', 'User' => 'Uživatel',
'Comments' => 'Komentáře', 'Comments' => 'Komentáře',
'Write your text in Markdown' => 'Můžete použít i Markdown-syntaxi',
'Leave a comment' => 'Zanechte komentář', 'Leave a comment' => 'Zanechte komentář',
'Comment is required' => 'Komentář je vyžadován', 'Comment is required' => 'Komentář je vyžadován',
'Leave a description' => 'Vložte popis', 'Leave a description' => 'Vložte popis',
@ -184,7 +180,6 @@ return array(
'Unable to remove this action.' => 'Tuto akci nelze odstranit.', 'Unable to remove this action.' => 'Tuto akci nelze odstranit.',
'Action removed successfully.' => 'Akce byla úspěšně odstraněna.', 'Action removed successfully.' => 'Akce byla úspěšně odstraněna.',
'Automatic actions for the project "%s"' => 'Automaticky vykonávané akce pro projekt "%s"', 'Automatic actions for the project "%s"' => 'Automaticky vykonávané akce pro projekt "%s"',
'Defined actions' => 'Definované akce',
'Add an action' => 'Přidat akci', 'Add an action' => 'Přidat akci',
'Event name' => 'Název události', 'Event name' => 'Název události',
'Action name' => 'Název akce', 'Action name' => 'Název akce',
@ -194,7 +189,6 @@ return array(
'When the selected event occurs execute the corresponding action.' => 'Kdykoliv se vybraná událost objeví, vykonat odpovídající akci.', 'When the selected event occurs execute the corresponding action.' => 'Kdykoliv se vybraná událost objeví, vykonat odpovídající akci.',
'Next step' => 'Další krok', 'Next step' => 'Další krok',
'Define action parameters' => 'Definovat parametry akce', 'Define action parameters' => 'Definovat parametry akce',
'Save this action' => 'Uložit akci',
'Do you really want to remove this action: "%s"?' => 'Skutečně chcete odebrat tuto akci: "%s"?', 'Do you really want to remove this action: "%s"?' => 'Skutečně chcete odebrat tuto akci: "%s"?',
'Remove an automatic action' => 'Odebrat automaticky prováděnou akci', 'Remove an automatic action' => 'Odebrat automaticky prováděnou akci',
'Assign the task to a specific user' => 'Přiřadit tento úkol konkrétnímu uživateli', 'Assign the task to a specific user' => 'Přiřadit tento úkol konkrétnímu uživateli',
@ -333,10 +327,10 @@ return array(
'Time tracking:' => 'Sledování času:', 'Time tracking:' => 'Sledování času:',
'New sub-task' => 'Nový dílčí úkol', 'New sub-task' => 'Nový dílčí úkol',
'New attachment added "%s"' => 'Byla přidána nová příloha "%s".', 'New attachment added "%s"' => 'Byla přidána nová příloha "%s".',
'Comment updated' => 'Komentář byl aktualizován.',
'New comment posted by %s' => 'Nový komentář publikovaný uživatelem %s', 'New comment posted by %s' => 'Nový komentář publikovaný uživatelem %s',
'New attachment' => 'Nová příloha', 'New attachment' => 'Nová příloha',
'New comment' => 'Nový komentář', 'New comment' => 'Nový komentář',
'Comment updated' => 'Komentář byl aktualizován.',
'New subtask' => 'Nový dílčí úkol', 'New subtask' => 'Nový dílčí úkol',
'Subtask updated' => 'Dílčí úkol byl aktualizován', 'Subtask updated' => 'Dílčí úkol byl aktualizován',
'Task updated' => 'Úkol byl aktualizován', 'Task updated' => 'Úkol byl aktualizován',
@ -436,7 +430,6 @@ return array(
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formát je vždy akceptován, například: "%s" a "%s"', 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formát je vždy akceptován, například: "%s" a "%s"',
'New private project' => 'Nový soukromý projekt', 'New private project' => 'Nový soukromý projekt',
'This project is private' => 'Tento projekt je soukromuý', 'This project is private' => 'Tento projekt je soukromuý',
'Type here to create a new sub-task' => 'Uveďte zde pro vytvoření nového dílčího úkolu',
'Add' => 'Přidat', 'Add' => 'Přidat',
'Start date' => 'Počáteční datum', 'Start date' => 'Počáteční datum',
'Time estimated' => 'Odhadovaný čas', 'Time estimated' => 'Odhadovaný čas',
@ -487,9 +480,6 @@ return array(
'Daily project summary export for "%s"' => 'Export denních přehledů pro "%s"', 'Daily project summary export for "%s"' => 'Export denních přehledů pro "%s"',
'Exports' => 'Exporty', 'Exports' => 'Exporty',
'This export contains the number of tasks per column grouped per day.' => 'Tento export obsahuje počet úkolů pro jednotlivé sloupce seskupených podle dní.', 'This export contains the number of tasks per column grouped per day.' => 'Tento export obsahuje počet úkolů pro jednotlivé sloupce seskupených podle dní.',
'Nothing to preview...' => 'Žádná položka k zobrazení ...',
'Preview' => 'Náhled',
'Write' => 'Režim psaní',
'Active swimlanes' => 'Aktive Swimlane', 'Active swimlanes' => 'Aktive Swimlane',
'Add a new swimlane' => 'Přidat nový řádek', 'Add a new swimlane' => 'Přidat nový řádek',
'Change default swimlane' => 'Standard Swimlane ändern', 'Change default swimlane' => 'Standard Swimlane ändern',
@ -543,7 +533,6 @@ return array(
'Task age in days' => 'Doba trvání úkolu ve dnech', 'Task age in days' => 'Doba trvání úkolu ve dnech',
'Days in this column' => 'Dní v tomto sloupci', 'Days in this column' => 'Dní v tomto sloupci',
'%dd' => '%d d', '%dd' => '%d d',
'Add a link' => 'Přidat odkaz',
'Add a new link' => 'Přidat nový odkaz', 'Add a new link' => 'Přidat nový odkaz',
'Do you really want to remove this link: "%s"?' => 'Die Verbindung "%s" wirklich löschen?', 'Do you really want to remove this link: "%s"?' => 'Die Verbindung "%s" wirklich löschen?',
'Do you really want to remove this link with task #%d?' => 'Die Verbindung mit der Aufgabe #%d wirklich löschen?', 'Do you really want to remove this link with task #%d?' => 'Die Verbindung mit der Aufgabe #%d wirklich löschen?',
@ -734,7 +723,7 @@ return array(
'Time spent changed: %sh' => 'Verbrauchte Zeit geändert: %sh', 'Time spent changed: %sh' => 'Verbrauchte Zeit geändert: %sh',
'Time estimated changed: %sh' => 'Geschätzte Zeit geändert: %sh', 'Time estimated changed: %sh' => 'Geschätzte Zeit geändert: %sh',
'The field "%s" have been updated' => 'Das Feld "%s" wurde verändert', 'The field "%s" have been updated' => 'Das Feld "%s" wurde verändert',
'The description have been modified' => 'Die Beschreibung wurde geändert', 'The description has been modified:' => 'Die Beschreibung wurde geändert:',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Soll die Aufgabe "%s" wirklich geschlossen werden? (einschließlich Teilaufgaben)', 'Do you really want to close the task "%s" as well as all subtasks?' => 'Soll die Aufgabe "%s" wirklich geschlossen werden? (einschließlich Teilaufgaben)',
'I want to receive notifications for:' => 'Chci dostávat upozornění na:', 'I want to receive notifications for:' => 'Chci dostávat upozornění na:',
'All tasks' => 'Všechny úkoly', 'All tasks' => 'Všechny úkoly',
@ -753,8 +742,6 @@ return array(
'My activity stream' => 'Přehled mých aktivit', 'My activity stream' => 'Přehled mých aktivit',
'My calendar' => 'Můj kalendář', 'My calendar' => 'Můj kalendář',
'Search tasks' => 'Hledání úkolů', 'Search tasks' => 'Hledání úkolů',
'Back to the calendar' => 'Zpět do kalendáře',
'Filters' => 'Filtry',
'Reset filters' => 'Resetovat filtry', 'Reset filters' => 'Resetovat filtry',
'My tasks due tomorrow' => 'Moje zítřejší úkoly', 'My tasks due tomorrow' => 'Moje zítřejší úkoly',
'Tasks due today' => 'Dnešní úkoly', 'Tasks due today' => 'Dnešní úkoly',
@ -854,7 +841,6 @@ return array(
// 'End date:' => '', // 'End date:' => '',
// 'There is no start date or end date for this project.' => '', // 'There is no start date or end date for this project.' => '',
// 'Projects Gantt chart' => '', // 'Projects Gantt chart' => '',
// 'Link type' => '',
// 'Change task color when using a specific task link' => '', // 'Change task color when using a specific task link' => '',
// 'Task link creation or modification' => '', // 'Task link creation or modification' => '',
// 'Milestone' => '', // 'Milestone' => '',
@ -906,7 +892,6 @@ return array(
// 'Shared' => '', // 'Shared' => '',
// 'Owner' => '', // 'Owner' => '',
// 'Unread notifications' => '', // 'Unread notifications' => '',
// 'My filters' => '',
// 'Notification methods:' => '', // 'Notification methods:' => '',
// 'Import tasks from CSV file' => '', // 'Import tasks from CSV file' => '',
// 'Unable to read your file' => '', // 'Unable to read your file' => '',
@ -944,6 +929,7 @@ return array(
// 'Usernames must be lowercase and unique' => '', // 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '', // 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '', // '%s attached a new file to the task %s' => '',
// 'Link type' => '',
// 'Assign automatically a category based on a link' => '', // 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertible Mark' => '', // 'BAM - Konvertible Mark' => '',
// 'Assignee Username' => '', // 'Assignee Username' => '',
@ -1053,7 +1039,6 @@ return array(
// 'Close a task when there is no activity' => '', // 'Close a task when there is no activity' => '',
// 'Duration in days' => '', // 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '', // 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '', // 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '', // 'Daily background job for tasks' => '',
// 'Auto' => '', // 'Auto' => '',
@ -1071,9 +1056,7 @@ return array(
// 'External link' => '', // 'External link' => '',
// 'Copy and paste your link here...' => '', // 'Copy and paste your link here...' => '',
// 'URL' => '', // 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '', // 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
// 'Assign to me' => '', // 'Assign to me' => '',
// 'Me' => '', // 'Me' => '',
// 'Do not duplicate anything' => '', // 'Do not duplicate anything' => '',
@ -1081,7 +1064,6 @@ return array(
// 'Users management' => '', // 'Users management' => '',
// 'Groups management' => '', // 'Groups management' => '',
// 'Create from another project' => '', // 'Create from another project' => '',
// 'There is no subtask at the moment.' => '',
// 'open' => '', // 'open' => '',
// 'closed' => '', // 'closed' => '',
// 'Priority:' => '', // 'Priority:' => '',
@ -1100,7 +1082,6 @@ return array(
// 'Started:' => '', // 'Started:' => '',
// 'Moved:' => '', // 'Moved:' => '',
// 'Task #%d' => '', // 'Task #%d' => '',
// 'Sub-tasks' => '',
// 'Date and time format' => '', // 'Date and time format' => '',
// 'Time format' => '', // 'Time format' => '',
// 'Start date: ' => '', // 'Start date: ' => '',
@ -1141,11 +1122,35 @@ return array(
// 'User filters' => '', // 'User filters' => '',
// 'Category filters' => '', // 'Category filters' => '',
// 'Upload a file' => '', // 'Upload a file' => '',
// 'There is no attachment at the moment.' => '',
// 'View file' => '', // 'View file' => '',
// 'Last activity' => '', // 'Last activity' => '',
// 'Change subtask position' => '', // 'Change subtask position' => '',
// 'This value must be greater than %d' => '', // 'This value must be greater than %d' => '',
// 'Another swimlane with the same name exists in the project' => '', // 'Another swimlane with the same name exists in the project' => '',
// 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '', // 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '',
// 'Actions duplicated successfully.' => '',
// 'Unable to duplicate actions.' => '',
// 'Add a new action' => '',
// 'Import from another project' => '',
// 'There is no action at the moment.' => '',
// 'Import actions from another project' => '',
// 'There is no available project.' => '',
// 'Local File' => '',
// 'Configuration' => '',
// 'PHP version:' => '',
// 'PHP SAPI:' => '',
// 'OS version:' => '',
// 'Database version:' => '',
// 'Browser:' => '',
// 'Task view' => '',
// 'Edit task' => '',
// 'Edit description' => '',
// 'New internal link' => '',
// 'Display list of keyboard shortcuts' => '',
// 'Menu' => '',
// 'Set start date' => '',
// 'Avatar' => '',
// 'Upload my avatar image' => '',
// 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '',
); );

View file

@ -8,7 +8,6 @@ return array(
'Edit' => 'Rediger', 'Edit' => 'Rediger',
'remove' => 'fjern', 'remove' => 'fjern',
'Remove' => 'Fjern', 'Remove' => 'Fjern',
'Update' => 'Opdater',
'Yes' => 'Ja', 'Yes' => 'Ja',
'No' => 'Nej', 'No' => 'Nej',
'cancel' => 'annuller', 'cancel' => 'annuller',
@ -60,7 +59,6 @@ return array(
'Actions' => 'Handlinger', 'Actions' => 'Handlinger',
'Inactive' => 'Inaktiv', 'Inactive' => 'Inaktiv',
'Active' => 'Aktiv', 'Active' => 'Aktiv',
'Add this column' => 'Tilføj denne kolonne',
'%d tasks on the board' => '%d Opgaver på boardet', '%d tasks on the board' => '%d Opgaver på boardet',
'%d tasks in total' => '%d Opgaver i alt', '%d tasks in total' => '%d Opgaver i alt',
'Unable to update this board.' => 'Ikke muligt at opdatere dette board', 'Unable to update this board.' => 'Ikke muligt at opdatere dette board',
@ -74,7 +72,6 @@ return array(
'All projects' => 'Alle Projekter', 'All projects' => 'Alle Projekter',
'Add a new column' => 'Tilføj en ny kolonne', 'Add a new column' => 'Tilføj en ny kolonne',
'Title' => 'Titel', 'Title' => 'Titel',
'Nobody assigned' => 'Ingen ansvarlig',
'Assigned to %s' => 'Ansvarlig: %s', 'Assigned to %s' => 'Ansvarlig: %s',
'Remove a column' => 'Fjern en kolonne', 'Remove a column' => 'Fjern en kolonne',
'Remove a column from a board' => 'Fjern en kolonne fra et board', 'Remove a column from a board' => 'Fjern en kolonne fra et board',
@ -168,7 +165,6 @@ return array(
// 'Task count' => '', // 'Task count' => '',
'User' => 'Bruger', 'User' => 'Bruger',
'Comments' => 'Kommentarer', 'Comments' => 'Kommentarer',
'Write your text in Markdown' => 'Skriv din tekst i markdown',
'Leave a comment' => 'Efterlad en kommentar', 'Leave a comment' => 'Efterlad en kommentar',
'Comment is required' => 'Kommentar er krævet', 'Comment is required' => 'Kommentar er krævet',
'Leave a description' => 'Efterlad en beskrivelse...', 'Leave a description' => 'Efterlad en beskrivelse...',
@ -184,7 +180,6 @@ return array(
'Unable to remove this action.' => 'Handlingen kunne ikke fjernes.', 'Unable to remove this action.' => 'Handlingen kunne ikke fjernes.',
'Action removed successfully.' => 'Handlingen er fjernet.', 'Action removed successfully.' => 'Handlingen er fjernet.',
'Automatic actions for the project "%s"' => 'Automatiske handlinger for projektet "%s"', 'Automatic actions for the project "%s"' => 'Automatiske handlinger for projektet "%s"',
'Defined actions' => 'Defineret handlinger',
'Add an action' => 'Tilføj en handling', 'Add an action' => 'Tilføj en handling',
'Event name' => 'Begivenhed', 'Event name' => 'Begivenhed',
'Action name' => 'Handling', 'Action name' => 'Handling',
@ -194,7 +189,6 @@ return array(
'When the selected event occurs execute the corresponding action.' => 'Når den valgte begivenhed opstår, udfør den tilsvarende handling.', 'When the selected event occurs execute the corresponding action.' => 'Når den valgte begivenhed opstår, udfør den tilsvarende handling.',
'Next step' => 'Næste', 'Next step' => 'Næste',
'Define action parameters' => 'Definer Handlingsparametre', 'Define action parameters' => 'Definer Handlingsparametre',
'Save this action' => 'Gem denne handling',
'Do you really want to remove this action: "%s"?' => 'Vil du virkelig slette denne handling: "%s"?', 'Do you really want to remove this action: "%s"?' => 'Vil du virkelig slette denne handling: "%s"?',
'Remove an automatic action' => 'Fjern en automatisk handling', 'Remove an automatic action' => 'Fjern en automatisk handling',
'Assign the task to a specific user' => 'Tildel opgaven til en bestem bruger', 'Assign the task to a specific user' => 'Tildel opgaven til en bestem bruger',
@ -333,10 +327,10 @@ return array(
'Time tracking:' => 'Tidsmåling:', 'Time tracking:' => 'Tidsmåling:',
'New sub-task' => 'Ny under-opgave', 'New sub-task' => 'Ny under-opgave',
'New attachment added "%s"' => 'Ny vedhæftning tilføjet "%s"', 'New attachment added "%s"' => 'Ny vedhæftning tilføjet "%s"',
'Comment updated' => 'Kommentar opdateret',
'New comment posted by %s' => 'Ny kommentar af %s', 'New comment posted by %s' => 'Ny kommentar af %s',
// 'New attachment' => '', // 'New attachment' => '',
// 'New comment' => '', // 'New comment' => '',
'Comment updated' => 'Kommentar opdateret',
// 'New subtask' => '', // 'New subtask' => '',
// 'Subtask updated' => '', // 'Subtask updated' => '',
// 'Task updated' => '', // 'Task updated' => '',
@ -436,7 +430,6 @@ return array(
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format er altid accepteret, eksempelvis: "%s" og "%s"', 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format er altid accepteret, eksempelvis: "%s" og "%s"',
'New private project' => 'Nyt privat projekt', 'New private project' => 'Nyt privat projekt',
'This project is private' => 'Dette projekt er privat', 'This project is private' => 'Dette projekt er privat',
'Type here to create a new sub-task' => 'Skriv her for at tilføje en ny under-opgave',
'Add' => 'Tilføj', 'Add' => 'Tilføj',
'Start date' => 'Start dato', 'Start date' => 'Start dato',
'Time estimated' => 'Tid estimeret', 'Time estimated' => 'Tid estimeret',
@ -487,9 +480,6 @@ return array(
// 'Daily project summary export for "%s"' => '', // 'Daily project summary export for "%s"' => '',
// 'Exports' => '', // 'Exports' => '',
// 'This export contains the number of tasks per column grouped per day.' => '', // 'This export contains the number of tasks per column grouped per day.' => '',
// 'Nothing to preview...' => '',
// 'Preview' => '',
// 'Write' => '',
// 'Active swimlanes' => '', // 'Active swimlanes' => '',
// 'Add a new swimlane' => '', // 'Add a new swimlane' => '',
// 'Change default swimlane' => '', // 'Change default swimlane' => '',
@ -543,7 +533,6 @@ 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 new link' => '', // 'Add a new link' => '',
// 'Do you really want to remove this link: "%s"?' => '', // 'Do you really want to remove this link: "%s"?' => '',
// 'Do you really want to remove this link with task #%d?' => '', // 'Do you really want to remove this link with task #%d?' => '',
@ -734,7 +723,7 @@ return array(
// 'Time spent changed: %sh' => '', // 'Time spent changed: %sh' => '',
// 'Time estimated changed: %sh' => '', // 'Time estimated changed: %sh' => '',
// 'The field "%s" have been updated' => '', // 'The field "%s" have been updated' => '',
// 'The description have been modified' => '', // 'The description has been modified:' => '',
// 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '',
// 'I want to receive notifications for:' => '', // 'I want to receive notifications for:' => '',
// 'All tasks' => '', // 'All tasks' => '',
@ -753,8 +742,6 @@ return array(
// 'My activity stream' => '', // 'My activity stream' => '',
// 'My calendar' => '', // 'My calendar' => '',
// 'Search tasks' => '', // 'Search tasks' => '',
// 'Back to the calendar' => '',
// 'Filters' => '',
// 'Reset filters' => '', // 'Reset filters' => '',
// 'My tasks due tomorrow' => '', // 'My tasks due tomorrow' => '',
// 'Tasks due today' => '', // 'Tasks due today' => '',
@ -854,7 +841,6 @@ return array(
// 'End date:' => '', // 'End date:' => '',
// 'There is no start date or end date for this project.' => '', // 'There is no start date or end date for this project.' => '',
// 'Projects Gantt chart' => '', // 'Projects Gantt chart' => '',
// 'Link type' => '',
// 'Change task color when using a specific task link' => '', // 'Change task color when using a specific task link' => '',
// 'Task link creation or modification' => '', // 'Task link creation or modification' => '',
// 'Milestone' => '', // 'Milestone' => '',
@ -906,7 +892,6 @@ return array(
// 'Shared' => '', // 'Shared' => '',
// 'Owner' => '', // 'Owner' => '',
// 'Unread notifications' => '', // 'Unread notifications' => '',
// 'My filters' => '',
// 'Notification methods:' => '', // 'Notification methods:' => '',
// 'Import tasks from CSV file' => '', // 'Import tasks from CSV file' => '',
// 'Unable to read your file' => '', // 'Unable to read your file' => '',
@ -944,6 +929,7 @@ return array(
// 'Usernames must be lowercase and unique' => '', // 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '', // 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '', // '%s attached a new file to the task %s' => '',
// 'Link type' => '',
// 'Assign automatically a category based on a link' => '', // 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertible Mark' => '', // 'BAM - Konvertible Mark' => '',
// 'Assignee Username' => '', // 'Assignee Username' => '',
@ -1053,7 +1039,6 @@ return array(
// 'Close a task when there is no activity' => '', // 'Close a task when there is no activity' => '',
// 'Duration in days' => '', // 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '', // 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '', // 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '', // 'Daily background job for tasks' => '',
// 'Auto' => '', // 'Auto' => '',
@ -1071,9 +1056,7 @@ return array(
// 'External link' => '', // 'External link' => '',
// 'Copy and paste your link here...' => '', // 'Copy and paste your link here...' => '',
// 'URL' => '', // 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '', // 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
// 'Assign to me' => '', // 'Assign to me' => '',
// 'Me' => '', // 'Me' => '',
// 'Do not duplicate anything' => '', // 'Do not duplicate anything' => '',
@ -1081,7 +1064,6 @@ return array(
// 'Users management' => '', // 'Users management' => '',
// 'Groups management' => '', // 'Groups management' => '',
// 'Create from another project' => '', // 'Create from another project' => '',
// 'There is no subtask at the moment.' => '',
// 'open' => '', // 'open' => '',
// 'closed' => '', // 'closed' => '',
// 'Priority:' => '', // 'Priority:' => '',
@ -1100,7 +1082,6 @@ return array(
// 'Started:' => '', // 'Started:' => '',
// 'Moved:' => '', // 'Moved:' => '',
// 'Task #%d' => '', // 'Task #%d' => '',
// 'Sub-tasks' => '',
// 'Date and time format' => '', // 'Date and time format' => '',
// 'Time format' => '', // 'Time format' => '',
// 'Start date: ' => '', // 'Start date: ' => '',
@ -1141,11 +1122,35 @@ return array(
// 'User filters' => '', // 'User filters' => '',
// 'Category filters' => '', // 'Category filters' => '',
// 'Upload a file' => '', // 'Upload a file' => '',
// 'There is no attachment at the moment.' => '',
// 'View file' => '', // 'View file' => '',
// 'Last activity' => '', // 'Last activity' => '',
// 'Change subtask position' => '', // 'Change subtask position' => '',
// 'This value must be greater than %d' => '', // 'This value must be greater than %d' => '',
// 'Another swimlane with the same name exists in the project' => '', // 'Another swimlane with the same name exists in the project' => '',
// 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '', // 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '',
// 'Actions duplicated successfully.' => '',
// 'Unable to duplicate actions.' => '',
// 'Add a new action' => '',
// 'Import from another project' => '',
// 'There is no action at the moment.' => '',
// 'Import actions from another project' => '',
// 'There is no available project.' => '',
// 'Local File' => '',
// 'Configuration' => '',
// 'PHP version:' => '',
// 'PHP SAPI:' => '',
// 'OS version:' => '',
// 'Database version:' => '',
// 'Browser:' => '',
// 'Task view' => '',
// 'Edit task' => '',
// 'Edit description' => '',
// 'New internal link' => '',
// 'Display list of keyboard shortcuts' => '',
// 'Menu' => '',
// 'Set start date' => '',
// 'Avatar' => '',
// 'Upload my avatar image' => '',
// 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '',
); );

View file

@ -8,7 +8,6 @@ return array(
'Edit' => 'Bearbeiten', 'Edit' => 'Bearbeiten',
'remove' => 'Entfernen', 'remove' => 'Entfernen',
'Remove' => 'Entfernen', 'Remove' => 'Entfernen',
'Update' => 'Aktualisieren',
'Yes' => 'Ja', 'Yes' => 'Ja',
'No' => 'Nein', 'No' => 'Nein',
'cancel' => 'Abbrechen', 'cancel' => 'Abbrechen',
@ -60,7 +59,6 @@ return array(
'Actions' => 'Aktionen', 'Actions' => 'Aktionen',
'Inactive' => 'Inaktiv', 'Inactive' => 'Inaktiv',
'Active' => 'Aktiv', 'Active' => 'Aktiv',
'Add this column' => 'Diese Spalte hinzufügen',
'%d tasks on the board' => '%d Aufgaben auf dieser Pinnwand', '%d tasks on the board' => '%d Aufgaben auf dieser Pinnwand',
'%d tasks in total' => '%d Aufgaben insgesamt', '%d tasks in total' => '%d Aufgaben insgesamt',
'Unable to update this board.' => 'Ändern dieser Pinnwand nicht möglich.', 'Unable to update this board.' => 'Ändern dieser Pinnwand nicht möglich.',
@ -74,7 +72,6 @@ return array(
'All projects' => 'Alle Projekte', 'All projects' => 'Alle Projekte',
'Add a new column' => 'Neue Spalte hinzufügen', 'Add a new column' => 'Neue Spalte hinzufügen',
'Title' => 'Titel', 'Title' => 'Titel',
'Nobody assigned' => 'Nicht zugeordnet',
'Assigned to %s' => 'Zuständig: %s', 'Assigned to %s' => 'Zuständig: %s',
'Remove a column' => 'Spalte löschen', 'Remove a column' => 'Spalte löschen',
'Remove a column from a board' => 'Spalte einer Pinnwand löschen', 'Remove a column from a board' => 'Spalte einer Pinnwand löschen',
@ -168,7 +165,6 @@ return array(
'Task count' => 'Aufgabenanzahl', 'Task count' => 'Aufgabenanzahl',
'User' => 'Benutzer', 'User' => 'Benutzer',
'Comments' => 'Kommentare', 'Comments' => 'Kommentare',
'Write your text in Markdown' => 'Schreibe deinen Text in Markdown-Syntax',
'Leave a comment' => 'Kommentar eingeben', 'Leave a comment' => 'Kommentar eingeben',
'Comment is required' => 'Ein Kommentar wird benötigt', 'Comment is required' => 'Ein Kommentar wird benötigt',
'Leave a description' => 'Beschreibung eingeben', 'Leave a description' => 'Beschreibung eingeben',
@ -184,7 +180,6 @@ return array(
'Unable to remove this action.' => 'Löschen der Aktion nicht möglich.', 'Unable to remove this action.' => 'Löschen der Aktion nicht möglich.',
'Action removed successfully.' => 'Aktion erfolgreich gelöscht.', 'Action removed successfully.' => 'Aktion erfolgreich gelöscht.',
'Automatic actions for the project "%s"' => 'Automatische Aktionen für das Projekt "%s"', 'Automatic actions for the project "%s"' => 'Automatische Aktionen für das Projekt "%s"',
'Defined actions' => 'Definierte Aktionen',
'Add an action' => 'Aktion hinzufügen', 'Add an action' => 'Aktion hinzufügen',
'Event name' => 'Ereignisname', 'Event name' => 'Ereignisname',
'Action name' => 'Aktionsname', 'Action name' => 'Aktionsname',
@ -194,7 +189,6 @@ return array(
'When the selected event occurs execute the corresponding action.' => 'Wenn das gewählte Ereignis eintritt, führe die zugehörige Aktion aus.', 'When the selected event occurs execute the corresponding action.' => 'Wenn das gewählte Ereignis eintritt, führe die zugehörige Aktion aus.',
'Next step' => 'Weiter', 'Next step' => 'Weiter',
'Define action parameters' => 'Aktionsparameter definieren', 'Define action parameters' => 'Aktionsparameter definieren',
'Save this action' => 'Aktion speichern',
'Do you really want to remove this action: "%s"?' => 'Soll diese Aktion wirklich gelöscht werden: "%s"?', 'Do you really want to remove this action: "%s"?' => 'Soll diese Aktion wirklich gelöscht werden: "%s"?',
'Remove an automatic action' => 'Löschen einer automatischen Aktion', 'Remove an automatic action' => 'Löschen einer automatischen Aktion',
'Assign the task to a specific user' => 'Aufgabe einem Benutzer zuordnen', 'Assign the task to a specific user' => 'Aufgabe einem Benutzer zuordnen',
@ -333,10 +327,10 @@ return array(
'Time tracking:' => 'Zeittracking', 'Time tracking:' => 'Zeittracking',
'New sub-task' => 'Neue Teilaufgabe', 'New sub-task' => 'Neue Teilaufgabe',
'New attachment added "%s"' => 'Neuer Anhang "%s" wurde hinzugefügt.', 'New attachment added "%s"' => 'Neuer Anhang "%s" wurde hinzugefügt.',
'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',
'New attachment' => 'Neuer Anhang', 'New attachment' => 'Neuer Anhang',
'New comment' => 'Neuer Kommentar', 'New comment' => 'Neuer Kommentar',
'Comment updated' => 'Kommentar wurde aktualisiert',
'New subtask' => 'Neue Teilaufgabe', 'New subtask' => 'Neue Teilaufgabe',
'Subtask updated' => 'Teilaufgabe aktualisiert', 'Subtask updated' => 'Teilaufgabe aktualisiert',
'Task updated' => 'Aufgabe aktualisiert', 'Task updated' => 'Aufgabe aktualisiert',
@ -436,7 +430,6 @@ return array(
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO Format wird immer akzeptiert, z.B.: "%s" und "%s"', 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO Format wird immer akzeptiert, z.B.: "%s" und "%s"',
'New private project' => 'Neues privates Projekt', 'New private project' => 'Neues privates Projekt',
'This project is private' => 'Dieses Projekt ist privat', 'This project is private' => 'Dieses Projekt ist privat',
'Type here to create a new sub-task' => 'Hier tippen, um eine neue Teilaufgabe zu erstellen',
'Add' => 'Hinzufügen', 'Add' => 'Hinzufügen',
'Start date' => 'Startdatum', 'Start date' => 'Startdatum',
'Time estimated' => 'Geschätzte Zeit', 'Time estimated' => 'Geschätzte Zeit',
@ -487,9 +480,6 @@ return array(
'Daily project summary export for "%s"' => 'Export der täglichen Projektzusammenfassung für "%s"', 'Daily project summary export for "%s"' => 'Export der täglichen Projektzusammenfassung für "%s"',
'Exports' => 'Exporte', 'Exports' => 'Exporte',
'This export contains the number of tasks per column grouped per day.' => 'Dieser Export enthält die Anzahl der Aufgaben pro Spalte nach Tagen gruppiert.', 'This export contains the number of tasks per column grouped per day.' => 'Dieser Export enthält die Anzahl der Aufgaben pro Spalte nach Tagen gruppiert.',
'Nothing to preview...' => 'Nichts in der Vorschau anzuzeigen ...',
'Preview' => 'Vorschau',
'Write' => 'Ändern',
'Active swimlanes' => 'Aktive Swimlane', 'Active swimlanes' => 'Aktive Swimlane',
'Add a new swimlane' => 'Eine neue Swimlane hinzufügen', 'Add a new swimlane' => 'Eine neue Swimlane hinzufügen',
'Change default swimlane' => 'Standard-Swimlane ändern', 'Change default swimlane' => 'Standard-Swimlane ändern',
@ -543,7 +533,6 @@ return array(
'Task age in days' => 'Aufgabenalter in Tagen', 'Task age in days' => 'Aufgabenalter in Tagen',
'Days in this column' => 'Tage in dieser Spalte', 'Days in this column' => 'Tage in dieser Spalte',
'%dd' => '%dT', '%dd' => '%dT',
'Add a link' => 'Verbindung hinzufügen',
'Add a new link' => 'Neue Verbindung hinzufügen', 'Add a new link' => 'Neue Verbindung hinzufügen',
'Do you really want to remove this link: "%s"?' => 'Die Verbindung "%s" wirklich löschen?', 'Do you really want to remove this link: "%s"?' => 'Die Verbindung "%s" wirklich löschen?',
'Do you really want to remove this link with task #%d?' => 'Die Verbindung mit der Aufgabe #%d wirklich löschen?', 'Do you really want to remove this link with task #%d?' => 'Die Verbindung mit der Aufgabe #%d wirklich löschen?',
@ -641,7 +630,7 @@ return array(
'Burndown chart' => 'Burndown-Diagramm', 'Burndown chart' => 'Burndown-Diagramm',
'This chart show the task complexity over the time (Work Remaining).' => 'Dieses Diagramm zeigt die Aufgabenkomplexität über den Faktor Zeit (Verbleibende Arbeit).', 'This chart show the task complexity over the time (Work Remaining).' => 'Dieses Diagramm zeigt die Aufgabenkomplexität über den Faktor Zeit (Verbleibende Arbeit).',
'Screenshot taken %s' => 'Screenshot aufgenommen %s ', 'Screenshot taken %s' => 'Screenshot aufgenommen %s ',
'Add a screenshot' => 'Füge einen Screenshot hinzu', 'Add a screenshot' => 'Screenshot hinzufügen',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Nimm einen Screenshot auf und drücke STRG+V oder ⌘+V um ihn hier einzufügen.', 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Nimm einen Screenshot auf und drücke STRG+V oder ⌘+V um ihn hier einzufügen.',
'Screenshot uploaded successfully.' => 'Screenshot erfolgreich hochgeladen.', 'Screenshot uploaded successfully.' => 'Screenshot erfolgreich hochgeladen.',
'SEK - Swedish Krona' => 'SEK - Schwedische Kronen', 'SEK - Swedish Krona' => 'SEK - Schwedische Kronen',
@ -734,7 +723,7 @@ return array(
'Time spent changed: %sh' => 'Verbrauchte Zeit geändert: %sh', 'Time spent changed: %sh' => 'Verbrauchte Zeit geändert: %sh',
'Time estimated changed: %sh' => 'Geschätzte Zeit geändert: %sh', 'Time estimated changed: %sh' => 'Geschätzte Zeit geändert: %sh',
'The field "%s" have been updated' => 'Das Feld "%s" wurde verändert', 'The field "%s" have been updated' => 'Das Feld "%s" wurde verändert',
'The description have been modified' => 'Die Beschreibung wurde geändert', 'The description has been modified:' => 'Die Beschreibung wurde geändert:',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Soll die Aufgabe "%s" wirklich geschlossen werden? (einschließlich Teilaufgaben)', 'Do you really want to close the task "%s" as well as all subtasks?' => 'Soll die Aufgabe "%s" wirklich geschlossen werden? (einschließlich Teilaufgaben)',
'I want to receive notifications for:' => 'Ich möchte Benachrichtigungen erhalten für:', 'I want to receive notifications for:' => 'Ich möchte Benachrichtigungen erhalten für:',
'All tasks' => 'Alle Aufgaben', 'All tasks' => 'Alle Aufgaben',
@ -753,8 +742,6 @@ return array(
'My activity stream' => 'Aktivitätsstream', 'My activity stream' => 'Aktivitätsstream',
'My calendar' => 'Mein Kalender', 'My calendar' => 'Mein Kalender',
'Search tasks' => 'Suche nach Aufgaben', 'Search tasks' => 'Suche nach Aufgaben',
'Back to the calendar' => 'Zurück zum Kalender',
'Filters' => 'Filter',
'Reset filters' => 'Filter zurücksetzen', 'Reset filters' => 'Filter zurücksetzen',
'My tasks due tomorrow' => 'Meine morgen fälligen Aufgaben', 'My tasks due tomorrow' => 'Meine morgen fälligen Aufgaben',
'Tasks due today' => 'Heute fällige Aufgaben', 'Tasks due today' => 'Heute fällige Aufgaben',
@ -854,7 +841,6 @@ return array(
'End date:' => 'Endedatum:', 'End date:' => 'Endedatum:',
'There is no start date or end date for this project.' => 'Es gibt kein Startdatum oder Endedatum für dieses Projekt', 'There is no start date or end date for this project.' => 'Es gibt kein Startdatum oder Endedatum für dieses Projekt',
'Projects Gantt chart' => 'Projekt Gantt Diagramm', 'Projects Gantt chart' => 'Projekt Gantt Diagramm',
'Link type' => 'Verbindungstyp',
'Change task color when using a specific task link' => 'Aufgabefarbe ändern bei bestimmter Aufgabenverbindung', 'Change task color when using a specific task link' => 'Aufgabefarbe ändern bei bestimmter Aufgabenverbindung',
'Task link creation or modification' => 'Aufgabenverbindung erstellen oder bearbeiten', 'Task link creation or modification' => 'Aufgabenverbindung erstellen oder bearbeiten',
'Milestone' => 'Meilenstein', 'Milestone' => 'Meilenstein',
@ -906,7 +892,6 @@ return array(
'Shared' => 'Geteilt', 'Shared' => 'Geteilt',
'Owner' => 'Eigentümer', 'Owner' => 'Eigentümer',
'Unread notifications' => 'Ungelesene Benachrichtigungen', 'Unread notifications' => 'Ungelesene Benachrichtigungen',
'My filters' => 'Meine Filter',
'Notification methods:' => 'Benachrichtigungs-Methoden:', 'Notification methods:' => 'Benachrichtigungs-Methoden:',
'Import tasks from CSV file' => 'Importiere Aufgaben aus CSV Datei', 'Import tasks from CSV file' => 'Importiere Aufgaben aus CSV Datei',
'Unable to read your file' => 'Die Datei kann nicht gelesen werden', 'Unable to read your file' => 'Die Datei kann nicht gelesen werden',
@ -930,7 +915,7 @@ return array(
'change sorting' => 'Sortierung ändern', 'change sorting' => 'Sortierung ändern',
'Tasks Importation' => 'Aufgaben Import', 'Tasks Importation' => 'Aufgaben Import',
'Delimiter' => 'Trennzeichen', 'Delimiter' => 'Trennzeichen',
'Enclosure' => 'Anlage', 'Enclosure' => 'Textbegrenzer',
'CSV File' => 'CSV Datei', 'CSV File' => 'CSV Datei',
'Instructions' => 'Anweisungen', 'Instructions' => 'Anweisungen',
'Your file must use the predefined CSV format' => 'Ihre Datei muss das vorgegebene CSV Format haben', 'Your file must use the predefined CSV format' => 'Ihre Datei muss das vorgegebene CSV Format haben',
@ -944,6 +929,7 @@ return array(
'Usernames must be lowercase and unique' => 'Benutzernamen müssen in Kleinbuschstaben und eindeutig sein', 'Usernames must be lowercase and unique' => 'Benutzernamen müssen in Kleinbuschstaben und eindeutig sein',
'Passwords will be encrypted if present' => 'Passwörter werden verschlüsselt wenn vorhanden', 'Passwords will be encrypted if present' => 'Passwörter werden verschlüsselt wenn vorhanden',
'%s attached a new file to the task %s' => '%s hat eine neue Datei zur Aufgabe %s hinzufgefügt', '%s attached a new file to the task %s' => '%s hat eine neue Datei zur Aufgabe %s hinzufgefügt',
'Link type' => 'Verbindungstyp',
'Assign automatically a category based on a link' => 'Linkbasiert eine Kategorie automatisch zuordnen', 'Assign automatically a category based on a link' => 'Linkbasiert eine Kategorie automatisch zuordnen',
'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark',
'Assignee Username' => 'Benutzername des Zuständigen', 'Assignee Username' => 'Benutzername des Zuständigen',
@ -1053,7 +1039,6 @@ return array(
'Close a task when there is no activity' => 'Schliesse eine Aufgabe, wenn keine Aktivitäten vorhanden sind', 'Close a task when there is no activity' => 'Schliesse eine Aufgabe, wenn keine Aktivitäten vorhanden sind',
'Duration in days' => 'Dauer in Tagen', 'Duration in days' => 'Dauer in Tagen',
'Send email when there is no activity on a task' => 'Versende eine Email, wenn keine Aktivitäten an einer Aufgabe vorhanden sind', 'Send email when there is no activity on a task' => 'Versende eine Email, wenn keine Aktivitäten an einer Aufgabe vorhanden sind',
'List of external links' => 'Liste der externen Verbindungen',
'Unable to fetch link information.' => 'Kann keine Informationen über Verbindungen holen', 'Unable to fetch link information.' => 'Kann keine Informationen über Verbindungen holen',
'Daily background job for tasks' => 'Tägliche Hintergrundarbeit für Aufgaben', 'Daily background job for tasks' => 'Tägliche Hintergrundarbeit für Aufgaben',
'Auto' => 'Auto', 'Auto' => 'Auto',
@ -1065,15 +1050,13 @@ return array(
'Add external link' => 'Externe Verbindung hinzufügen', 'Add external link' => 'Externe Verbindung hinzufügen',
'Type' => 'Typ', 'Type' => 'Typ',
'Dependency' => 'Abhängigkeit', 'Dependency' => 'Abhängigkeit',
'Add internal link' => 'Füge interne Verbindung hinzu', 'Add internal link' => 'Interne Verbindung hinzufügen',
'Add a new external link' => 'Füge eine neue externe Verbindung hinzu', 'Add a new external link' => 'Füge eine neue externe Verbindung hinzu',
'Edit external link' => 'Externe Verbindung bearbeiten', 'Edit external link' => 'Externe Verbindung bearbeiten',
'External link' => 'Externe Verbindung', 'External link' => 'Externe Verbindung',
'Copy and paste your link here...' => 'Kopieren Sie Ihren Link hier...', 'Copy and paste your link here...' => 'Kopieren Sie Ihren Link hier...',
'URL' => 'URL', 'URL' => 'URL',
'There is no external link for the moment.' => 'Es gibt im Moment keine externe Verbindung.',
'Internal links' => 'Interne Verbindungen', 'Internal links' => 'Interne Verbindungen',
'There is no internal link for the moment.' => 'Es gibt im Moment keine interne Verbindung.',
'Assign to me' => 'Mir zuweisen', 'Assign to me' => 'Mir zuweisen',
'Me' => 'Mich', 'Me' => 'Mich',
'Do not duplicate anything' => 'Nichts duplizieren', 'Do not duplicate anything' => 'Nichts duplizieren',
@ -1081,7 +1064,6 @@ return array(
'Users management' => 'Benutzermanagement', 'Users management' => 'Benutzermanagement',
'Groups management' => 'Gruppenmanagement', 'Groups management' => 'Gruppenmanagement',
'Create from another project' => 'Von einem anderen Projekt erstellen', 'Create from another project' => 'Von einem anderen Projekt erstellen',
'There is no subtask at the moment.' => 'Es gibt im Moment keine Teilaufgabe',
'open' => 'offen', 'open' => 'offen',
'closed' => 'geschlossen', 'closed' => 'geschlossen',
'Priority:' => 'Priorität:', 'Priority:' => 'Priorität:',
@ -1100,7 +1082,6 @@ return array(
'Started:' => 'Gestarted:', 'Started:' => 'Gestarted:',
'Moved:' => 'Verschoben:', 'Moved:' => 'Verschoben:',
'Task #%d' => 'Aufgabe #%d', 'Task #%d' => 'Aufgabe #%d',
'Sub-tasks' => 'Teilaufgaben',
'Date and time format' => 'Datums- und Zeitformat', 'Date and time format' => 'Datums- und Zeitformat',
'Time format' => 'Zeitformat', 'Time format' => 'Zeitformat',
'Start date: ' => 'Anfangsdatum:', 'Start date: ' => 'Anfangsdatum:',
@ -1141,11 +1122,35 @@ return array(
'User filters' => 'Benutzer-Filter', 'User filters' => 'Benutzer-Filter',
'Category filters' => 'Kategorie-Filter', 'Category filters' => 'Kategorie-Filter',
'Upload a file' => 'Eine Datei hochladen', 'Upload a file' => 'Eine Datei hochladen',
'There is no attachment at the moment.' => 'Es gibt zur Zeit keine Anhänge',
'View file' => 'Datei ansehen', 'View file' => 'Datei ansehen',
'Last activity' => 'Letzte Aktivität', 'Last activity' => 'Letzte Aktivität',
'Change subtask position' => 'Position der Unteraufgabe ändern', 'Change subtask position' => 'Position der Unteraufgabe ändern',
'This value must be greater than %d' => 'Dieser Wert muss größer als %d sein', 'This value must be greater than %d' => 'Dieser Wert muss größer als %d sein',
'Another swimlane with the same name exists in the project' => 'Es gibt bereits eine Swimlane mit diesem Namen im Projekt', 'Another swimlane with the same name exists in the project' => 'Es gibt bereits eine Swimlane mit diesem Namen im Projekt',
'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Beispiel: http://example.kanboard.net (wird zum Erstellen absoluter URLs genutzt)', 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Beispiel: http://example.kanboard.net (wird zum Erstellen absoluter URLs genutzt)',
'Actions duplicated successfully.' => 'Aktionen erfolgreich dupliziert',
'Unable to duplicate actions.' => 'Aktionen können nicht dupliziert werden.',
'Add a new action' => 'Neue Aktion hinzufügen',
'Import from another project' => 'Von einem anderen Projekt importieren',
'There is no action at the moment.' => 'Es gibt zur Zeit keine Aktionen.',
'Import actions from another project' => 'Aktionen von einem anderen Projekt importieren',
'There is no available project.' => 'Es ist kein Projekt verfügbar.',
// 'Local File' => '',
// 'Configuration' => '',
// 'PHP version:' => '',
// 'PHP SAPI:' => '',
// 'OS version:' => '',
// 'Database version:' => '',
// 'Browser:' => '',
// 'Task view' => '',
// 'Edit task' => '',
// 'Edit description' => '',
// 'New internal link' => '',
// 'Display list of keyboard shortcuts' => '',
// 'Menu' => '',
// 'Set start date' => '',
// 'Avatar' => '',
// 'Upload my avatar image' => '',
// 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '',
); );

View file

@ -8,7 +8,6 @@ return array(
'Edit' => 'Διόρθωση', 'Edit' => 'Διόρθωση',
'remove' => 'αφαιρετής', 'remove' => 'αφαιρετής',
'Remove' => 'Αφαίρεση', 'Remove' => 'Αφαίρεση',
'Update' => 'Ενημέρωση',
'Yes' => 'Ναι', 'Yes' => 'Ναι',
'No' => 'Όχι', 'No' => 'Όχι',
'cancel' => 'ακύρωση', 'cancel' => 'ακύρωση',
@ -18,24 +17,24 @@ return array(
'Green' => 'Πράσινο', 'Green' => 'Πράσινο',
'Purple' => 'Βιολετί', 'Purple' => 'Βιολετί',
'Red' => 'Κόκκινο', 'Red' => 'Κόκκινο',
'Orange' => 'Ποστοκαλί', 'Orange' => 'Πορτοκαλί',
'Grey' => 'Γκρίζο', 'Grey' => 'Γκρίζο',
'Brown' => 'Καφέ', 'Brown' => 'Καφέ',
'Deep Orange' => 'Βαθύ πορτοκαλί', 'Deep Orange' => 'Βαθύ πορτοκαλί',
'Dark Grey' => 'Βαθύ γκρί', 'Dark Grey' => 'Βαθύ γκρί',
'Pink' => 'Ρόζ', 'Pink' => 'Ρόζ',
'Teal' => 'Τουρκουάζ', 'Teal' => 'Τυρκουάζ',
'Cyan' => 'Γαλάζιο', 'Cyan' => 'Γαλάζιο',
'Lime' => 'Λεμονί', 'Lime' => 'Λεμονί',
'Light Green' => 'Ανοιχτό πράσινο', 'Light Green' => 'Ανοιχτό πράσινο',
'Amber' => 'Κεχριμπαρί', 'Amber' => 'Κεχριμπαρί',
'Save' => 'Αποθήκευση', 'Save' => 'Αποθήκευση',
'Login' => 'Είσοδος', 'Login' => 'Είσοδος',
'Official website:' => 'Επίσημο web site :', 'Official website:' => 'Επίσημο web site:',
'Unassigned' => 'Μη εκχωρημένο', 'Unassigned' => 'Μη εκχωρημένο',
'View this task' => 'Προβολή της εργασίας', 'View this task' => 'Προβολή της εργασίας',
'Remove user' => 'Αφαίρεση χρήστη', 'Remove user' => 'Αφαίρεση χρήστη',
'Do you really want to remove this user: "%s"?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό τον χρήστη : « %s » ?', 'Do you really want to remove this user: "%s"?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό τον χρήστη: « %s » ?',
'New user' => 'Νέος Χρήστης', 'New user' => 'Νέος Χρήστης',
'All users' => 'Όλοι οι χρήστες', 'All users' => 'Όλοι οι χρήστες',
'Username' => 'Όνομα χρήστη', 'Username' => 'Όνομα χρήστη',
@ -60,7 +59,6 @@ return array(
'Actions' => 'Ενέργειες', 'Actions' => 'Ενέργειες',
'Inactive' => 'Ανενεργός', 'Inactive' => 'Ανενεργός',
'Active' => 'Ενεργός', 'Active' => 'Ενεργός',
'Add this column' => 'Προσθήκη αυτής της στήλης',
'%d tasks on the board' => '%d εργασίες στον κεντρικό πίνακα έργου', '%d tasks on the board' => '%d εργασίες στον κεντρικό πίνακα έργου',
'%d tasks in total' => '%d εργασιών στο σύνολο', '%d tasks in total' => '%d εργασιών στο σύνολο',
'Unable to update this board.' => 'Αδύνατη η ενημέρωση αυτού του πίνακα', 'Unable to update this board.' => 'Αδύνατη η ενημέρωση αυτού του πίνακα',
@ -68,24 +66,23 @@ 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' => 'Αφαίρεση του έργου',
'Edit the board for "%s"' => 'Διόρθωση πίνακα από « %s »', 'Edit the board for "%s"' => 'Διόρθωση πίνακα από « %s »',
'All projects' => 'Όλα τα έργα', 'All projects' => 'Όλα τα έργα',
'Add a new column' => 'Πρόσθήκη στήλης', 'Add a new column' => 'Πρόσθήκη στήλης',
'Title' => 'Τίτλος', 'Title' => 'Τίτλος',
'Nobody assigned' => 'Δεν έχει ανατεθεί',
'Assigned to %s' => 'Ανατιθεμένο στον %s', 'Assigned to %s' => 'Ανατιθεμένο στον %s',
'Remove a column' => 'Αφαίρεση στήλης', 'Remove a column' => 'Αφαίρεση στήλης',
'Remove a column from a board' => 'Αφαίρεση στήλης από τον πίνακα', 'Remove a column from a board' => 'Αφαίρεση στήλης από τον πίνακα',
'Unable to remove this column.' => 'Αδύνατη η αφαίρεση της στήλης', 'Unable to remove this column.' => 'Αδύνατη η αφαίρεση της στήλης',
'Do you really want to remove this column: "%s"?' => 'Θέλετε να αφαιρέσετε τη στήλη : « %s » ?', 'Do you really want to remove this column: "%s"?' => 'Θέλετε να αφαιρέσετε τη στήλη: « %s » ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Αυτή η ενέργεια θα ΑΦΑΙΡΕΣΕΙ ΟΛΕΣ ΤΙΣ ΕΡΓΑΣΙΕΣ που είναι σχετικές με τη στήλη!!', 'This action will REMOVE ALL TASKS associated to this column!' => 'Αυτή η ενέργεια θα ΑΦΑΙΡΕΣΕΙ ΟΛΕΣ ΤΙΣ ΕΡΓΑΣΙΕΣ που είναι σχετικές με τη στήλη!!',
'Settings' => 'Προτιμήσεις', 'Settings' => 'Προτιμήσεις',
'Application settings' => 'Παραμετροποίηση εφαρμογής', 'Application settings' => 'Παραμετροποίηση εφαρμογής',
'Language' => 'Γλώσσα', 'Language' => 'Γλώσσα',
'Webhook token:' => 'Διακριτικό ασφαλείας (token) webhooks :', 'Webhook token:' => 'Διακριτικό ασφαλείας (token) webhooks:',
'API token:' => 'Διακριτικό ασφαλείας (token) API :', 'API token:' => 'Διακριτικό ασφαλείας (token) API:',
'Database size:' => 'Μέγεθος βάσης δεδομένων :', 'Database size:' => 'Μέγεθος βάσης δεδομένων :',
'Download the database' => 'Κατεβάστε τη βάση δεδομένων', 'Download the database' => 'Κατεβάστε τη βάση δεδομένων',
'Optimize the database' => 'Βελτιστοποίηση της βάσης δεδομένων', 'Optimize the database' => 'Βελτιστοποίηση της βάσης δεδομένων',
@ -95,7 +92,7 @@ return array(
'Edit a task' => 'Διόρθωση εργασίας', 'Edit a task' => 'Διόρθωση εργασίας',
'Column' => 'Στήλη', 'Column' => 'Στήλη',
'Color' => 'Χρώμα', 'Color' => 'Χρώμα',
'Assignee' => 'Ανατεθιμένα', 'Assignee' => 'Ανατεθιμένο στον χρήστη',
'Create another task' => 'Δημιουργία και άλλης εργασίας', 'Create another task' => 'Δημιουργία και άλλης εργασίας',
'New task' => 'Νέα εργασία', 'New task' => 'Νέα εργασία',
'Open a task' => 'Άνοιγμα εργασίας', 'Open a task' => 'Άνοιγμα εργασίας',
@ -168,7 +165,6 @@ return array(
'Task count' => 'Αρίθμηση εργασιών', 'Task count' => 'Αρίθμηση εργασιών',
'User' => 'Χρήστης', 'User' => 'Χρήστης',
'Comments' => 'Σχόλια', 'Comments' => 'Σχόλια',
'Write your text in Markdown' => 'Δυνατότητα γραφής και σε Markdown',
'Leave a comment' => 'Αφήστε ένα σχόλιο', 'Leave a comment' => 'Αφήστε ένα σχόλιο',
'Comment is required' => 'Το σχόλιο απαιτείται', 'Comment is required' => 'Το σχόλιο απαιτείται',
'Leave a description' => 'Αφήστε μια περιγραφή', 'Leave a description' => 'Αφήστε μια περιγραφή',
@ -184,7 +180,6 @@ return array(
'Unable to remove this action.' => 'Δεν είναι δυνατή η αφαίρεση αυτής της ενέργειας', 'Unable to remove this action.' => 'Δεν είναι δυνατή η αφαίρεση αυτής της ενέργειας',
'Action removed successfully.' => 'Η ενέργεια αφαιρέθηκε με επιτυχία.', 'Action removed successfully.' => 'Η ενέργεια αφαιρέθηκε με επιτυχία.',
'Automatic actions for the project "%s"' => 'Αυτόματες ενέργειες για το έργο « %s »', 'Automatic actions for the project "%s"' => 'Αυτόματες ενέργειες για το έργο « %s »',
'Defined actions' => 'Ορισμένες ενέργειες',
'Add an action' => 'Προσθήκη ενέργειας', 'Add an action' => 'Προσθήκη ενέργειας',
'Event name' => 'Ονομασία συμβάντος', 'Event name' => 'Ονομασία συμβάντος',
'Action name' => 'Ονομασία ενέργειας', 'Action name' => 'Ονομασία ενέργειας',
@ -194,7 +189,6 @@ return array(
'When the selected event occurs execute the corresponding action.' => 'Όταν εμφανίζεται το επιλεγμένο συμβάν εκτελέστε την αντίστοιχη ενέργεια.', 'When the selected event occurs execute the corresponding action.' => 'Όταν εμφανίζεται το επιλεγμένο συμβάν εκτελέστε την αντίστοιχη ενέργεια.',
'Next step' => 'Επόμενο βήμα', 'Next step' => 'Επόμενο βήμα',
'Define action parameters' => 'Ορισμός παραμέτρων ενέργειας', 'Define action parameters' => 'Ορισμός παραμέτρων ενέργειας',
'Save this action' => 'Αποθήκευση ενέργειας',
'Do you really want to remove this action: "%s"?' => 'Αφαίρεση της ενέργειας: « %s » ?', 'Do you really want to remove this action: "%s"?' => 'Αφαίρεση της ενέργειας: « %s » ?',
'Remove an automatic action' => 'Αφαίρεση της αυτόματης ενέργειας', 'Remove an automatic action' => 'Αφαίρεση της αυτόματης ενέργειας',
'Assign the task to a specific user' => 'Ανάθεση της εργασίας σε συγκεκριμένο χρήστη', 'Assign the task to a specific user' => 'Ανάθεση της εργασίας σε συγκεκριμένο χρήστη',
@ -228,7 +222,7 @@ return array(
'Persistent connections' => 'Μόνιμες συνδέσεις', 'Persistent connections' => 'Μόνιμες συνδέσεις',
'No session.' => 'Καμμία συνεδρία', 'No session.' => 'Καμμία συνεδρία',
'Expiration date' => 'Ημερομηνία λήξης', 'Expiration date' => 'Ημερομηνία λήξης',
'Remember Me' => 'Remember Me', 'Remember Me' => 'Να με θυμάσαι',
'Creation date' => 'Ημερομηνία δημιουργίας', 'Creation date' => 'Ημερομηνία δημιουργίας',
'Everybody' => 'Όλα', 'Everybody' => 'Όλα',
'Open' => 'Ανοικτά', 'Open' => 'Ανοικτά',
@ -236,7 +230,7 @@ return array(
'Search' => 'Αναζήτηση', 'Search' => 'Αναζήτηση',
'Nothing found.' => 'Δεν βρέθηκε.', 'Nothing found.' => 'Δεν βρέθηκε.',
'Due date' => 'Μέχρι την ημερομηνία', 'Due date' => 'Μέχρι την ημερομηνία',
'Others formats accepted: %s and %s' => 'Άλλες δεκτές μορφοποιήσεις : %s και %s', 'Others formats accepted: %s and %s' => 'Άλλες δεκτές μορφοποιήσεις: %s και %s',
'Description' => 'Περιγραφή', 'Description' => 'Περιγραφή',
'%d comments' => '%d σχόλια', '%d comments' => '%d σχόλια',
'%d comment' => '%d σχόλιο', '%d comment' => '%d σχόλιο',
@ -254,7 +248,7 @@ return array(
'Assign automatically a category based on a color' => 'Αυτόματη εκχώρηση μιας κατηγορίας με βάση το χρώμα', 'Assign automatically a category based on a color' => 'Αυτόματη εκχώρηση μιας κατηγορίας με βάση το χρώμα',
'Task creation or modification' => 'Δημιουργία ή τροποποίηση εργασιών', 'Task creation or modification' => 'Δημιουργία ή τροποποίηση εργασιών',
'Category' => 'Κατηγορία', 'Category' => 'Κατηγορία',
'Category:' => 'Κατηγορία :', 'Category:' => 'Κατηγορία:',
'Categories' => 'Κατηγορίες', 'Categories' => 'Κατηγορίες',
'Category not found.' => 'Η κατηγορία δεν βρέθηκε', 'Category not found.' => 'Η κατηγορία δεν βρέθηκε',
'Your category have been created successfully.' => 'Η κατηγορία δημιουργήθηκε.', 'Your category have been created successfully.' => 'Η κατηγορία δημιουργήθηκε.',
@ -283,7 +277,7 @@ return array(
'Edit a comment' => 'Διόρθωση σχολίου', 'Edit a comment' => 'Διόρθωση σχολίου',
'Summary' => 'Περίληψη', 'Summary' => 'Περίληψη',
'Time tracking' => 'Παρακολούθηση χρόνου', 'Time tracking' => 'Παρακολούθηση χρόνου',
'Estimate:' => 'Κατ\' εκτίμηση :', 'Estimate:' => 'Κατ\' εκτίμηση:',
'Spent:' => 'Ξοδεύτηκε :', 'Spent:' => 'Ξοδεύτηκε :',
'Do you really want to remove this sub-task?' => 'Διαγραφή της υπο-εργασίας ?', 'Do you really want to remove this sub-task?' => 'Διαγραφή της υπο-εργασίας ?',
'Remaining:' => 'Απομένει :', 'Remaining:' => 'Απομένει :',
@ -292,7 +286,7 @@ return array(
'estimated' => 'κατ\' εκτίμηση', 'estimated' => 'κατ\' εκτίμηση',
'Sub-Tasks' => 'Υπο-Εργασίες', 'Sub-Tasks' => 'Υπο-Εργασίες',
'Add a sub-task' => 'Προσθήκη υπο-εργασίας', 'Add a sub-task' => 'Προσθήκη υπο-εργασίας',
'Original estimate' => 'Original estimate', 'Original estimate' => 'Αρχική πρόβλεψη χρόνου',
'Create another sub-task' => 'Δημιουργία κι άλλης υπο-εργασίας', 'Create another sub-task' => 'Δημιουργία κι άλλης υπο-εργασίας',
'Time spent' => 'Χρόνος που ξοδεύτηκε', 'Time spent' => 'Χρόνος που ξοδεύτηκε',
'Edit a sub-task' => 'Διόρθωση υπο-εργασίας', 'Edit a sub-task' => 'Διόρθωση υπο-εργασίας',
@ -323,22 +317,22 @@ return array(
'Project cloned successfully.' => 'Το έργο κλωνοποιήθηκε με επιτυχία.', 'Project cloned successfully.' => 'Το έργο κλωνοποιήθηκε με επιτυχία.',
'Unable to clone this project.' => 'Αδύνατο να κλωνοποιηθεί το έργο.', 'Unable to clone this project.' => 'Αδύνατο να κλωνοποιηθεί το έργο.',
'Enable email notifications' => 'Ενεργοποίηση ειδοποιήσεων ηλεκτρονικού ταχυδρομείου', 'Enable email notifications' => 'Ενεργοποίηση ειδοποιήσεων ηλεκτρονικού ταχυδρομείου',
'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 έχει κλείσει.',
'Sub-task updated' => 'Η υπο-εργασία ενημερώθηκε', 'Sub-task updated' => 'Η υπο-εργασία ενημερώθηκε',
'Title:' => 'Τίτλος :', 'Title:' => 'Τίτλος:',
'Status:' => 'Κατάσταση :', 'Status:' => 'Κατάσταση:',
'Assignee:' => 'Εντολοδόχος :', 'Assignee:' => 'Εντολοδόχος:',
'Time tracking:' => 'Παρακολούθηση του χρόνου :', 'Time tracking:' => 'Παρακολούθηση του χρόνου:',
'New sub-task' => 'Νέα υπο-εργασία', 'New sub-task' => 'Νέα υπο-εργασία',
'New attachment added "%s"' => 'Νέα επικόλληση προστέθηκε « %s »', 'New attachment added "%s"' => 'Νέα επικόλληση προστέθηκε « %s »',
'Comment updated' => 'Το σχόλιο ενημερώθηκε',
'New comment posted by %s' => 'Νέο σχόλιο από τον χρήστη « %s »', 'New comment posted by %s' => 'Νέο σχόλιο από τον χρήστη « %s »',
'New attachment' => 'New attachment', 'New attachment' => 'New attachment',
'New comment' => 'Νέο σχόλιο', 'New comment' => 'Νέο σχόλιο',
'Comment updated' => 'Το σχόλιο ενημερώθηκε',
'New subtask' => 'Νέα υπο-εργασία', 'New subtask' => 'Νέα υπο-εργασία',
'Subtask updated' => 'Υπο-Εργασία ενημερώθηκε', 'Subtask updated' => 'Η Υπο-Εργασία ενημερώθηκε',
'Task updated' => 'Η εργασία ενημερώθηκε', 'Task updated' => 'Η εργασία ενημερώθηκε',
'Task closed' => 'Η εργασία έκλεισε', 'Task closed' => 'Η εργασία έκλεισε',
'Task opened' => 'Η εργασία άνοιξε', 'Task opened' => 'Η εργασία άνοιξε',
@ -349,8 +343,8 @@ return array(
'Disable public access' => 'Απενεργοποίηση δημόσιας πρόσβασης', 'Disable public access' => 'Απενεργοποίηση δημόσιας πρόσβασης',
'Enable public access' => 'Ενεργοποίηση δημόσιας πρόσβασης', 'Enable public access' => 'Ενεργοποίηση δημόσιας πρόσβασης',
'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 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' => 'Μεταφορά σε άλλο έργο',
@ -362,12 +356,12 @@ return array(
'Remote' => 'Απομακρυσμένη', 'Remote' => 'Απομακρυσμένη',
'Enabled' => 'Ενεργή', 'Enabled' => 'Ενεργή',
'Disabled' => 'Απενεργοποιημένη', 'Disabled' => 'Απενεργοποιημένη',
'Username:' => 'Username :', 'Username:' => 'Username:',
'Name:' => 'Όνομα :', 'Name:' => 'Όνομα:',
'Email:' => 'Email :', 'Email:' => 'Email:',
'Notifications:' => 'Ειδοποιήσεις :', 'Notifications:' => 'Ειδοποιήσεις:',
'Notifications' => 'Ειδοποιήσεις', 'Notifications' => 'Ειδοποιήσεις',
'Account type:' => 'Τύπος λογαριασμού :', 'Account type:' => 'Τύπος λογαριασμού:',
'Edit profile' => 'Επεξεργασία προφίλ', 'Edit profile' => 'Επεξεργασία προφίλ',
'Change password' => 'Αλλαγή password', 'Change password' => 'Αλλαγή password',
'Password modification' => 'Τροποποίηση password ', 'Password modification' => 'Τροποποίηση password ',
@ -394,8 +388,8 @@ return array(
'RSS feed' => 'RSS feed', 'RSS feed' => 'RSS feed',
'%s updated a comment on the task #%d' => '%s ενημέρωσε ένα σχόλιο στην εργασία n°%d', '%s updated a comment on the task #%d' => '%s ενημέρωσε ένα σχόλιο στην εργασία n°%d',
'%s commented on the task #%d' => '%s σχολίασε την εργασία n°%d', '%s commented on the task #%d' => '%s σχολίασε την εργασία n°%d',
'%s updated a subtask for the task #%d' => '%s ενημέρωσε μια υπο-εργασία στην εργασία n °%d', '%s updated a subtask for the task #%d' => '%s ενημερώθηκε μια υπο-εργασία στην εργασία n °%d',
'%s created a subtask for the task #%d' => '%s δημιούργησε μια υπο-εργασία στην εργασία n°%d', '%s created a subtask for the task #%d' => '%s δημιουργήθηκε μια υπο-εργασία στην εργασία n°%d',
'%s updated the task #%d' => '%s ενημέρωσε την εργασία n°%d', '%s updated the task #%d' => '%s ενημέρωσε την εργασία n°%d',
'%s created the task #%d' => '%s δημιούργησε την εργασία n°%d', '%s created the task #%d' => '%s δημιούργησε την εργασία n°%d',
'%s closed the task #%d' => '%s έκλεισε την εργασία n°%d', '%s closed the task #%d' => '%s έκλεισε την εργασία n°%d',
@ -417,13 +411,13 @@ return array(
'Label' => 'Label', 'Label' => 'Label',
'Database' => 'Database', 'Database' => 'Database',
'About' => 'About', 'About' => 'About',
'Database driver:' => 'Database driver :', 'Database driver:' => 'Database driver:',
'Board settings' => 'Board settings', 'Board settings' => 'Board settings',
'URL and token' => 'URL / token', 'URL and token' => 'URL / token',
'Webhook settings' => 'Webhook settings', 'Webhook settings' => 'Webhook settings',
'URL for task creation:' => 'URL για δημιουργία εργασίας: :', 'URL for task creation:' => 'URL για δημιουργία εργασίας:',
'Reset token' => 'Reset token', 'Reset token' => 'Reset token',
'API endpoint:' => 'URL API :', 'API endpoint:' => 'URL API:',
'Refresh interval for private board' => 'Ανανέωση interval στο private board', 'Refresh interval for private board' => 'Ανανέωση interval στο private board',
'Refresh interval for public board' => 'Ανανέωση interval στο public board', 'Refresh interval for public board' => 'Ανανέωση interval στο public board',
'Task highlight period' => 'Περίοδος εργασίας', 'Task highlight period' => 'Περίοδος εργασίας',
@ -433,10 +427,9 @@ return array(
'Application URL' => 'Application URL', 'Application URL' => 'Application URL',
'Token regenerated.' => 'Token regenerated.', 'Token regenerated.' => 'Token regenerated.',
'Date format' => 'Μορφή ημερομηνίας', 'Date format' => 'Μορφή ημερομηνίας',
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format είναι πάντα αποδεκτό, π.χ. : « %s » και « %s »', 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format είναι πάντα αποδεκτό, π.χ.: « %s » και « %s »',
'New private project' => 'Νέο ιδιωτικό έργο', 'New private project' => 'Νέο ιδιωτικό έργο',
'This project is private' => 'Αυτό το έργο είναι ιδιωτικό', 'This project is private' => 'Αυτό το έργο είναι ιδιωτικό',
'Type here to create a new sub-task' => 'Πληκτρολογήστε εδώ για να δημιουργήσετε μια νέα υπο-εργασία',
'Add' => 'Προσθήκη', 'Add' => 'Προσθήκη',
'Start date' => 'Ημερομηνία έναρξης', 'Start date' => 'Ημερομηνία έναρξης',
'Time estimated' => 'Εκτιμώμενος χρόνος', 'Time estimated' => 'Εκτιμώμενος χρόνος',
@ -487,21 +480,18 @@ return array(
'Daily project summary export for "%s"' => 'Εξαγωγή της καθημερινής περίληψης του έργου « %s »', 'Daily project summary export for "%s"' => 'Εξαγωγή της καθημερινής περίληψης του έργου « %s »',
'Exports' => 'Εξαγωγές', 'Exports' => 'Εξαγωγές',
'This export contains the number of tasks per column grouped per day.' => 'Αυτή η κατάσταση περιέχει τον αριθμό των εργασιών ανά στήλη ομαδοποιημένα ανά ημέρα.', 'This export contains the number of tasks per column grouped per day.' => 'Αυτή η κατάσταση περιέχει τον αριθμό των εργασιών ανά στήλη ομαδοποιημένα ανά ημέρα.',
'Nothing to preview...' => 'Τίποτα για προεπισκόπηση...', 'Active swimlanes' => 'Ενεργές λωρίδες',
'Preview' => 'Προεπισκόπηση', 'Add a new swimlane' => 'Προσθήκη λωρίδας',
'Write' => 'Write', 'Change default swimlane' => 'Αλλαγή της εξ\' ορισμού λωρίδας',
'Active swimlanes' => 'Ενεργά swimlanes', 'Default swimlane' => 'Εξ\' ορισμού λωρίδα',
'Add a new swimlane' => 'Πρόσθεσε ένα νέο λωρίδα',
'Change default swimlane' => 'Αλλαγή του default λωρίδα',
'Default swimlane' => 'Default λωρίδα',
'Do you really want to remove this swimlane: "%s"?' => 'Σίγουρα θέλετε να αφαιρέσετε τη λωρίδα : « %s » ?', 'Do you really want to remove this swimlane: "%s"?' => 'Σίγουρα θέλετε να αφαιρέσετε τη λωρίδα : « %s » ?',
'Inactive swimlanes' => 'Λωρίδες ανενεργές', 'Inactive swimlanes' => 'Ανενεργές Λωρίδες',
'Remove a swimlane' => 'Αφαιρέστε μια λωρίδα', 'Remove a swimlane' => 'Αφαίρεση λωρίδας',
'Show default swimlane' => 'Εμφάνιση προεπιλεγμένων λωρίδων', 'Show default swimlane' => 'Εμφάνιση προεπιλεγμένων λωρίδων',
'Swimlane modification for the project "%s"' => 'Τροποποίηση λωρίδας για το έργο « %s »', 'Swimlane modification for the project "%s"' => 'Τροποποίηση λωρίδας για το έργο « %s »',
'Swimlane not found.' => 'Η λωρίδα δεν βρέθηκε.', 'Swimlane not found.' => 'Η λωρίδα δεν βρέθηκε.',
'Swimlane removed successfully.' => 'Η λωρίδα αφαιρέθηκε με επιτυχία.', 'Swimlane removed successfully.' => 'Η λωρίδα αφαιρέθηκε με επιτυχία.',
'Swimlanes' => 'Swimlanes', 'Swimlanes' => 'Λωρίδες',
'Swimlane updated successfully.' => 'Η λωρίδα ενημερώθηκε με επιτυχία.', 'Swimlane updated successfully.' => 'Η λωρίδα ενημερώθηκε με επιτυχία.',
'The default swimlane have been updated successfully.' => 'Η προεπιλεγμένη λωρίδα ενημερώθηκε με επιτυχία.', 'The default swimlane have been updated successfully.' => 'Η προεπιλεγμένη λωρίδα ενημερώθηκε με επιτυχία.',
'Unable to remove this swimlane.' => 'Αδύνατο να αφαιρεθεί η λωρίδα.', 'Unable to remove this swimlane.' => 'Αδύνατο να αφαιρεθεί η λωρίδα.',
@ -518,8 +508,8 @@ return array(
'Task Title' => 'Τίτλος εργασίας', 'Task Title' => 'Τίτλος εργασίας',
'Untitled' => 'Χωρίς τίτλο', 'Untitled' => 'Χωρίς τίτλο',
'Application default' => 'Προεπιλογή από την εφαρμογή', 'Application default' => 'Προεπιλογή από την εφαρμογή',
'Language:' => 'Γλώσσα :', 'Language:' => 'Γλώσσα:',
'Timezone:' => 'Timezone :', 'Timezone:' => 'Timezone:',
'All columns' => 'Όλες οι στήλες', 'All columns' => 'Όλες οι στήλες',
'Calendar' => 'Ημερολόγιο', 'Calendar' => 'Ημερολόγιο',
'Next' => 'Επόμενο', 'Next' => 'Επόμενο',
@ -535,7 +525,7 @@ return array(
'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?' => 'Ποιά κομμάτια του έργου θέλετε να αντιγράψετε ?',
'Disallow login form' => 'Απαγόρευση φόρμας σύνδεσης', 'Disallow login form' => 'Απαγόρευση φόρμας σύνδεσης',
'Start' => 'Εκκίνηση', 'Start' => 'Εκκίνηση',
@ -543,7 +533,6 @@ return array(
'Task age in days' => 'Χρόνος εργασίας σε μέρες', 'Task age in days' => 'Χρόνος εργασίας σε μέρες',
'Days in this column' => 'Μέρες σε αυτή την στήλη', 'Days in this column' => 'Μέρες σε αυτή την στήλη',
'%dd' => '%dημ', '%dd' => '%dημ',
'Add a link' => 'Προσθήκη ενός link',
'Add a new link' => 'Προσθήκη ενός νέου link', 'Add a new link' => 'Προσθήκη ενός νέου link',
'Do you really want to remove this link: "%s"?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό το link : « %s » ?', 'Do you really want to remove this link: "%s"?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό το link : « %s » ?',
'Do you really want to remove this link with task #%d?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό το link του έργου n°%d ?', 'Do you really want to remove this link with task #%d?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό το link του έργου n°%d ?',
@ -587,10 +576,10 @@ return array(
'Keyboard shortcuts' => 'Συντομεύσεις πληκτρολογίου', 'Keyboard shortcuts' => 'Συντομεύσεις πληκτρολογίου',
'Open board switcher' => 'Άνοιγμα μεταγωγέα κεντρικού πίνακα', 'Open board switcher' => 'Άνοιγμα μεταγωγέα κεντρικού πίνακα',
'Application' => 'Εφαρμογή', 'Application' => 'Εφαρμογή',
'Compact view' => 'Συμπηκνωμένη προβολή', 'Compact view' => 'Συμπυκνωμένη προβολή',
'Horizontal scrolling' => 'Οριζόντια ολίσθηση', 'Horizontal scrolling' => 'Οριζόντια ολίσθηση',
'Compact/wide view' => 'Συμπηκνωμένη/Ευρεία Προβολή', 'Compact/wide view' => 'Συμπυκνωμένη/Ευρεία Προβολή',
'No results match:' => 'Δεν ταιριάζει κανένα αποτέλεσμα :', 'No results match:' => 'Δεν ταιριάζει κανένα αποτέλεσμα:',
'Currency' => 'Νόμισμα', 'Currency' => 'Νόμισμα',
'Private project' => 'Ιδιωτικό έργο', 'Private project' => 'Ιδιωτικό έργο',
'AUD - Australian Dollar' => 'AUD - Australian Dollar', 'AUD - Australian Dollar' => 'AUD - Australian Dollar',
@ -633,7 +622,7 @@ return array(
'Two factor authentication' => 'Κωδικός ελέγχου ταυτότητας δύο παραγόντων', 'Two factor authentication' => 'Κωδικός ελέγχου ταυτότητας δύο παραγόντων',
'This QR code contains the key URI: ' => 'Αυτό το QR code περιέχει το url : ', 'This QR code contains the key URI: ' => 'Αυτό το QR code περιέχει το url : ',
'Check my code' => 'Έλεγχος του κωδικού μου', 'Check my code' => 'Έλεγχος του κωδικού μου',
'Secret key: ' => 'Μυστικό κλειδί : ', 'Secret key: ' => 'Μυστικό κλειδί: ',
'Test your device' => 'Ελέγξτε τη συσκευή σας', 'Test your device' => 'Ελέγξτε τη συσκευή σας',
'Assign a color when the task is moved to a specific column' => 'Αντιστοίχιση χρώματος όταν η εργασία κινείται σε μια συγκεκριμένη στήλη', 'Assign a color when the task is moved to a specific column' => 'Αντιστοίχιση χρώματος όταν η εργασία κινείται σε μια συγκεκριμένη στήλη',
'%s via Kanboard' => '%s via Kanboard', '%s via Kanboard' => '%s via Kanboard',
@ -719,22 +708,22 @@ return array(
'Project activities for %s' => 'Ενέργειες για το έργο « %s »', 'Project activities for %s' => 'Ενέργειες για το έργο « %s »',
'view the board on Kanboard' => 'δείτε τον πίνακα στο Kanboard', 'view the board on Kanboard' => 'δείτε τον πίνακα στο Kanboard',
'The task have been moved to the first swimlane' => 'Η εργασία αυτή έχει μετακινηθεί στην πρώτη λωρίδα', 'The task have been moved to the first swimlane' => 'Η εργασία αυτή έχει μετακινηθεί στην πρώτη λωρίδα',
'The task have been moved to another swimlane:' => 'Η εργασία αυτή έχει μετακινηθεί σε άλλη λωρίδα :', 'The task have been moved to another swimlane:' => 'Η εργασία αυτή έχει μετακινηθεί σε άλλη λωρίδα:',
'Overdue tasks for the project "%s"' => 'Εκπρόθεσμες εργασίες για το έργο « %s »', 'Overdue tasks for the project "%s"' => 'Εκπρόθεσμες εργασίες για το έργο « %s »',
'New title: %s' => 'Νέος τίτλος : %s', 'New title: %s' => 'Νέος τίτλος: %s',
'The task is not assigned anymore' => 'Η εργασία δεν έχει ανατεθεί πλέον', 'The task is not assigned anymore' => 'Η εργασία δεν έχει ανατεθεί πλέον',
'New assignee: %s' => 'Καινούργια ανάθεση : %s', 'New assignee: %s' => 'Καινούργια ανάθεση: %s',
'There is no category now' => 'Δεν υπάρχει κατηγορία τώρα', 'There is no category now' => 'Δεν υπάρχει κατηγορία τώρα',
'New category: %s' => 'Νέα κατηγορία : %s', 'New category: %s' => 'Νέα κατηγορία: %s',
'New color: %s' => 'Νέο χρώμα : %s', 'New color: %s' => 'Νέο χρώμα: %s',
'New complexity: %d' => 'Νέα πολυπλοκότητα : %d', 'New complexity: %d' => 'Νέα πολυπλοκότητα: %d',
'The due date have been removed' => 'Η ημερομηνία καθηκόντων έχει αφαιρεθεί', 'The due date have been removed' => 'Η ημερομηνία καθηκόντων έχει αφαιρεθεί',
'There is no description anymore' => 'Δεν υπάρχει περιγραφή πλέον', 'There is no description anymore' => 'Δεν υπάρχει περιγραφή πλέον',
'Recurrence settings have been modified' => 'Οι ρυθμίσεις επανάληψης έχουν τροποποιηθεί', 'Recurrence settings have been modified' => 'Οι ρυθμίσεις επανάληψης έχουν τροποποιηθεί',
'Time spent changed: %sh' => 'Ο χρόνος που πέρασε έχει αλλάξει : %sh', 'Time spent changed: %sh' => 'Ο χρόνος που πέρασε έχει αλλάξει: %sh',
'Time estimated changed: %sh' => 'Ο εκτιμώμενος χρόνος άλλαξε : %sh', 'Time estimated changed: %sh' => 'Ο εκτιμώμενος χρόνος άλλαξε: %sh',
'The field "%s" have been updated' => 'Το πεδίο « %s » έχει ενημερωθεί', 'The field "%s" have been updated' => 'Το πεδίο « %s » έχει ενημερωθεί',
'The description have been modified' => 'Η περιγραφή έχει ενημερωθεί', 'The description has been modified:' => 'Η περιγραφή έχει ενημερωθεί',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Σίγουρα θέλετε να κλείσετε την εργασία « %s » και την υπο-εργασία ?', 'Do you really want to close the task "%s" as well as all subtasks?' => 'Σίγουρα θέλετε να κλείσετε την εργασία « %s » και την υπο-εργασία ?',
'I want to receive notifications for:' => 'Επιθυμώ να λαμβάνω ενημερώσεις για :', 'I want to receive notifications for:' => 'Επιθυμώ να λαμβάνω ενημερώσεις για :',
'All tasks' => 'Όλες οι εργασίες', 'All tasks' => 'Όλες οι εργασίες',
@ -753,8 +742,6 @@ return array(
'My activity stream' => 'Η ροή δραστηριοτήτων μου', 'My activity stream' => 'Η ροή δραστηριοτήτων μου',
'My calendar' => 'Το ημερολόγιο μου', 'My calendar' => 'Το ημερολόγιο μου',
'Search tasks' => 'Αναζήτηση εργασιών', 'Search tasks' => 'Αναζήτηση εργασιών',
'Back to the calendar' => 'Πίσω στο ημερολόγιο',
'Filters' => 'Φίλτρα',
'Reset filters' => 'Επαναφορά φίλτρων', 'Reset filters' => 'Επαναφορά φίλτρων',
'My tasks due tomorrow' => 'Οι εργασίες καθηκόντων μου αύριο', 'My tasks due tomorrow' => 'Οι εργασίες καθηκόντων μου αύριο',
'Tasks due today' => 'Οι εργασίες καθηκόντων μου αύριο', 'Tasks due today' => 'Οι εργασίες καθηκόντων μου αύριο',
@ -772,26 +759,26 @@ return array(
'Go to the search/filter box' => 'Μετάβαση στο πλαίσιο αναζήτησης / φίλτρο', 'Go to the search/filter box' => 'Μετάβαση στο πλαίσιο αναζήτησης / φίλτρο',
'There is no activity yet.' => 'Δεν υπάρχει καμία δραστηριότητα ακόμα.', 'There is no activity yet.' => 'Δεν υπάρχει καμία δραστηριότητα ακόμα.',
'No tasks found.' => 'Δεν βρέθηκαν εργασίες.', 'No tasks found.' => 'Δεν βρέθηκαν εργασίες.',
'Keyboard shortcut: "%s"' => 'Συντόμευση πληκτρολογίου : « %s »', 'Keyboard shortcut: "%s"' => 'Συντόμευση πληκτρολογίου: « %s »',
'List' => 'Λίστα', 'List' => 'Λίστα',
'Filter' => 'Φίλτρο', 'Filter' => 'Φίλτρο',
'Advanced search' => 'Προχωρημένη Αναζήτηση', 'Advanced search' => 'Προχωρημένη Αναζήτηση',
'Example of query: ' => 'Παράδειγμα ερωτήματος : ', 'Example of query: ' => 'Παράδειγμα ερωτήματος: ',
'Search by project: ' => 'Αναζήτηση με βάση το έργο : ', 'Search by project: ' => 'Αναζήτηση με βάση το έργο: ',
'Search by column: ' => 'Αναζήτηση με βάση την στήλη : ', 'Search by column: ' => 'Αναζήτηση με βάση την στήλη: ',
'Search by assignee: ' => 'Αναζήτηση με βάση τον δικαιοδόχο : ', 'Search by assignee: ' => 'Αναζήτηση με βάση τον δικαιοδόχο: ',
'Search by color: ' => 'Αναζήτηση βάση χρώματος : ', 'Search by color: ' => 'Αναζήτηση βάση χρώματος: ',
'Search by category: ' => 'Αναζήτηση βάση κατηγορίας : ', 'Search by category: ' => 'Αναζήτηση βάση κατηγορίας: ',
'Search by description: ' => 'Αναζήτηση βάση περιγραφής : ', 'Search by description: ' => 'Αναζήτηση βάση περιγραφής: ',
'Search by due date: ' => 'Αναζήτηση βάση ημέρας λήξης : ', 'Search by due date: ' => 'Αναζήτηση βάση ημέρας λήξης: ',
'Lead and Cycle time for "%s"' => 'Lead & cycle time για « %s »', 'Lead and Cycle time for "%s"' => 'Lead & cycle time για « %s »',
'Average time spent into each column for "%s"' => 'Μέσος χρόνος παραμονής σε κάθε στήλη για « %s »', 'Average time spent into each column for "%s"' => 'Μέσος χρόνος παραμονής σε κάθε στήλη για « %s »',
'Average time spent into each column' => 'Μέσος χρόνος παραμονής σε κάθε στήλη', 'Average time spent into each column' => 'Μέσος χρόνος παραμονής σε κάθε στήλη',
'Average time spent' => 'Μέσος χρόνος που δαπανήθηκε', 'Average time spent' => 'Μέσος χρόνος που δαπανήθηκε',
'This chart show the average time spent into each column for the last %d tasks.' => 'Αυτό το γράφημα δείχνει ότι ο μέσος χρόνος που δαπανάται σε κάθε στήλη για τις τελευταίες %d εργασίες', 'This chart show the average time spent into each column for the last %d tasks.' => 'Αυτό το γράφημα δείχνει ότι ο μέσος χρόνος που δαπανάται σε κάθε στήλη για τις τελευταίες %d εργασίες',
'Average Lead and Cycle time' => 'Average Lead & Cycle time', 'Average Lead and Cycle time' => 'Average Lead & Cycle time',
'Average lead time: ' => 'Average lead time : ', 'Average lead time: ' => 'Average lead time: ',
'Average cycle time: ' => 'Average cycle time : ', 'Average cycle time: ' => 'Average cycle time: ',
'Cycle Time' => 'Cycle time', 'Cycle Time' => 'Cycle time',
'Lead Time' => 'Lead time', 'Lead Time' => 'Lead time',
'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Αυτό το γράφημα δείχνει το average lead and cycle time για τις τελευταίες %d εργασίες κατά τη διάρκεια του χρόνου.', 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Αυτό το γράφημα δείχνει το average lead and cycle time για τις τελευταίες %d εργασίες κατά τη διάρκεια του χρόνου.',
@ -815,23 +802,23 @@ return array(
'There is no destination project available.' => 'Δεν υπάρχει διαθέσιμο κανένα έργο προορισμού.', 'There is no destination project available.' => 'Δεν υπάρχει διαθέσιμο κανένα έργο προορισμού.',
'Trigger automatically subtask time tracking' => 'Αυτόματη ενεργοποίηση της παρακολούθησης χρόνου σε υπο-εργασίες', 'Trigger automatically subtask time tracking' => 'Αυτόματη ενεργοποίηση της παρακολούθησης χρόνου σε υπο-εργασίες',
'Include closed tasks in the cumulative flow diagram' => 'Να συμπεριλαμβάνονται οι κλειστές εργασίες στο συσσωρευτικό διάγραμμα ροής', 'Include closed tasks in the cumulative flow diagram' => 'Να συμπεριλαμβάνονται οι κλειστές εργασίες στο συσσωρευτικό διάγραμμα ροής',
'Current swimlane: %s' => 'Τρέχουσα λωρίδα : %s', 'Current swimlane: %s' => 'Τρέχουσα λωρίδα: %s',
'Current column: %s' => 'Τρέχουσα στήλη : %s', 'Current column: %s' => 'Τρέχουσα στήλη: %s',
'Current category: %s' => 'Τρέχουσα κατηγορία : %s', 'Current category: %s' => 'Τρέχουσα κατηγορία: %s',
'no category' => 'Καμμία κατηγορία', 'no category' => 'Καμμία κατηγορία',
'Current assignee: %s' => 'Τρέχον εκδοχέας : %s', 'Current assignee: %s' => 'Τρέχον εκδοχέας: %s',
'not assigned' => 'δεν έχει εκχωρηθεί', 'not assigned' => 'δεν έχει εκχωρηθεί',
'Author:' => 'Συγγραφέας :', 'Author:' => 'Συγγραφέας:',
'contributors' => 'συνεισφέροντες', 'contributors' => 'συνεισφέροντες',
'License:' => 'Άδεια :', 'License:' => 'Άδεια:',
'License' => 'Άδεια', 'License' => 'Άδεια',
'Enter the text below' => 'Πληκτρολογήστε το παρακάτω κείμενο', 'Enter the text below' => 'Πληκτρολογήστε το παρακάτω κείμενο',
'Gantt chart for %s' => 'Gantt διάγραμμα για %s', 'Gantt chart for %s' => 'Gantt διάγραμμα για %s',
'Sort by position' => 'Ταξινόμηση κατά Θέση', 'Sort by position' => 'Ταξινόμηση κατά Θέση',
'Sort by date' => 'Ταξινόμηση κατά ημέρα', 'Sort by date' => 'Ταξινόμηση κατά ημέρα',
'Add task' => 'Προσθήκη εργασίας', 'Add task' => 'Προσθήκη εργασίας',
'Start date:' => 'Ημέρα εκκίνησης :', 'Start date:' => 'Ημερομηνία εκκίνησης :',
'Due date:' => 'Ημέρα καθηκόντων :', 'Due date:' => 'Ημερομηνία λήξης:',
'There is no start date or due date for this task.' => 'Δεν υπάρχει ημερομηνία έναρξης ή ημερομηνία λήξης καθηκόντων για το έργο αυτό.', 'There is no start date or due date for this task.' => 'Δεν υπάρχει ημερομηνία έναρξης ή ημερομηνία λήξης καθηκόντων για το έργο αυτό.',
'Moving or resizing a task will change the start and due date of the task.' => 'Μετακίνηση ή αλλαγή μεγέθους μιας εργασίας θα αλλάξει την ώρα έναρξης και ημερομηνία λήξης της εργασίας.', 'Moving or resizing a task will change the start and due date of the task.' => 'Μετακίνηση ή αλλαγή μεγέθους μιας εργασίας θα αλλάξει την ώρα έναρξης και ημερομηνία λήξης της εργασίας.',
'There is no task in your project.' => 'Δεν υπάρχει καμία εργασία στο έργο σας.', 'There is no task in your project.' => 'Δεν υπάρχει καμία εργασία στο έργο σας.',
@ -854,11 +841,10 @@ return array(
'End date:' => 'Ημερομηνία λήξης :', 'End date:' => 'Ημερομηνία λήξης :',
'There is no start date or end date for this project.' => 'Δεν υπάρχει ημερομηνία έναρξης ή λήξης για το έργο αυτό.', 'There is no start date or end date for this project.' => 'Δεν υπάρχει ημερομηνία έναρξης ή λήξης για το έργο αυτό.',
'Projects Gantt chart' => 'Διάγραμμα Gantt έργων', 'Projects Gantt chart' => 'Διάγραμμα Gantt έργων',
'Link type' => 'Τύπος συνδέσμου',
'Change task color when using a specific task link' => 'Αλλαγή χρώματος εργασίας χρησιμοποιώντας συγκεκριμένο σύνδεσμο εργασίας', 'Change task color when using a specific task link' => 'Αλλαγή χρώματος εργασίας χρησιμοποιώντας συγκεκριμένο σύνδεσμο εργασίας',
'Task link creation or modification' => 'Σύνδεσμος δημιουργίας ή τροποποίησης εργασίας', 'Task link creation or modification' => 'Σύνδεσμος δημιουργίας ή τροποποίησης εργασίας',
'Milestone' => 'Ορόσημο', 'Milestone' => 'Ορόσημο',
'Documentation: %s' => 'Τεκμηρίωση : %s', 'Documentation: %s' => 'Τεκμηρίωση: %s',
'Switch to the Gantt chart view' => 'Μεταφορά σε προβολή διαγράμματος Gantt', 'Switch to the Gantt chart view' => 'Μεταφορά σε προβολή διαγράμματος Gantt',
'Reset the search/filter box' => 'Αρχικοποίηση του πεδίου αναζήτησης/φιλτραρίσματος', 'Reset the search/filter box' => 'Αρχικοποίηση του πεδίου αναζήτησης/φιλτραρίσματος',
'Documentation' => 'Τεκμηρίωση', 'Documentation' => 'Τεκμηρίωση',
@ -880,12 +866,12 @@ return array(
'Your custom filter have been updated successfully.' => 'Το παρεμετροποιημένο από τον χρήστη φίλτρο, διορθώθηκε με επιτυχία.', 'Your custom filter have been updated successfully.' => 'Το παρεμετροποιημένο από τον χρήστη φίλτρο, διορθώθηκε με επιτυχία.',
'Unable to update custom filter.' => 'Δεν είναι δυνατή η ενημέρωση του παρεμετροποιημένου από τον χρήστη φίλτρου.', 'Unable to update custom filter.' => 'Δεν είναι δυνατή η ενημέρωση του παρεμετροποιημένου από τον χρήστη φίλτρου.',
'Web' => 'Web', 'Web' => 'Web',
'New attachment on task #%d: %s' => 'Νέο συνημμένο για την εργασία n°%d : %s', 'New attachment on task #%d: %s' => 'Νέο συνημμένο για την εργασία n°%d: %s',
'New comment on task #%d' => 'Νέο σχόλιο για την εργασία n°%d', 'New comment on task #%d' => 'Νέο σχόλιο για την εργασία n°%d',
'Comment updated on task #%d' => 'Ενημέρωση σχολίου για την εργασία n°%d', 'Comment updated on task #%d' => 'Ενημέρωση σχολίου για την εργασία n°%d',
'New subtask on task #%d' => 'Νέα υπο-εργασία για την εργασία n°%d', 'New subtask on task #%d' => 'Νέα υπο-εργασία για την εργασία n°%d',
'Subtask updated on task #%d' => 'Ενημέρωση υπό-εργασίας για την εργασία n°%d', 'Subtask updated on task #%d' => 'Ενημέρωση υπό-εργασίας για την εργασία n°%d',
'New task #%d: %s' => 'Νέα εργασία n°%d : %s', 'New task #%d: %s' => 'Νέα εργασία n°%d: %s',
'Task updated #%d' => 'Η εργασία n°%d ενημερώθηκε με επιτυχία', 'Task updated #%d' => 'Η εργασία n°%d ενημερώθηκε με επιτυχία',
'Task #%d closed' => 'Η εργασία n°%d έκλεισε', 'Task #%d closed' => 'Η εργασία n°%d έκλεισε',
'Task #%d opened' => 'Η εργασία n°%d άνοιξε', 'Task #%d opened' => 'Η εργασία n°%d άνοιξε',
@ -906,8 +892,7 @@ return array(
'Shared' => 'Διαμοιρασμένα', 'Shared' => 'Διαμοιρασμένα',
'Owner' => 'Ιδιοκτήτης', 'Owner' => 'Ιδιοκτήτης',
'Unread notifications' => 'Αδιάβαστες ειδοποιήσεις', 'Unread notifications' => 'Αδιάβαστες ειδοποιήσεις',
'My filters' => 'Τα φίλτρα μου', 'Notification methods:' => 'Μέθοδοι ειδοποίησης:',
'Notification methods:' => 'Μέθοδοι ειδοποίησης :',
'Import tasks from CSV file' => 'Εισαγωγή εργασιών μέσω αρχείου CSV', 'Import tasks from CSV file' => 'Εισαγωγή εργασιών μέσω αρχείου CSV',
'Unable to read your file' => 'Δεν είναι δυνατή η ανάγνωση του αρχείου', 'Unable to read your file' => 'Δεν είναι δυνατή η ανάγνωση του αρχείου',
'%d task(s) have been imported successfully.' => '%d η(οι) εργασία(ες) εισήχθησαν με επιτυχία.', '%d task(s) have been imported successfully.' => '%d η(οι) εργασία(ες) εισήχθησαν με επιτυχία.',
@ -937,13 +922,14 @@ return array(
'Your file must be encoded in UTF-8' => 'Το αρχείο σας πρέπει να έχει κωδικοποίηση χαρακτήρων UTF-8', 'Your file must be encoded in UTF-8' => 'Το αρχείο σας πρέπει να έχει κωδικοποίηση χαρακτήρων UTF-8',
'The first row must be the header' => 'Η πρώτη γραμμή πρέπει να είναι η κεφαλίδα', 'The first row must be the header' => 'Η πρώτη γραμμή πρέπει να είναι η κεφαλίδα',
'Duplicates are not verified for you' => 'Οι Διπλοεγγραφές δεν ελέγχονται', 'Duplicates are not verified for you' => 'Οι Διπλοεγγραφές δεν ελέγχονται',
'The due date must use the ISO format: YYYY-MM-DD' => 'Η ημερομηνία λήξης πρέπει να χρησιμοποιεί τη μορφοποίηση ISO : EEEE-MM-HH ή στα αγγλικά YYYY-MM-DD', 'The due date must use the ISO format: YYYY-MM-DD' => 'Η ημερομηνία λήξης πρέπει να χρησιμοποιεί τη μορφοποίηση ISO: EEEE-MM-HH ή στα αγγλικά YYYY-MM-DD',
'Download CSV template' => 'Κατέβασμα πρότυπου αρχείου CSV', 'Download CSV template' => 'Κατέβασμα πρότυπου αρχείου CSV',
'No external integration registered.' => 'No external integration registered.', 'No external integration registered.' => 'No external integration registered.',
'Duplicates are not imported' => 'Διπλοεγγραφές δεν εισήχθησαν', 'Duplicates are not imported' => 'Διπλοεγγραφές δεν εισήχθησαν',
'Usernames must be lowercase and unique' => 'Οι ονομασίες χρηστών πρέπει να είναι σε μικρά γράμματα (lowercase) και μοναδικά', 'Usernames must be lowercase and unique' => 'Οι ονομασίες χρηστών πρέπει να είναι σε μικρά γράμματα (lowercase) και μοναδικά',
'Passwords will be encrypted if present' => 'Οι κωδικοί πρόσβασης κρυπτογραφούνται, αν υπάρχουν', 'Passwords will be encrypted if present' => 'Οι κωδικοί πρόσβασης κρυπτογραφούνται, αν υπάρχουν',
'%s attached a new file to the task %s' => '%s νέο συνημμένο αρχείο της εργασίας %s', '%s attached a new file to the task %s' => '%s νέο συνημμένο αρχείο της εργασίας %s',
'Link type' => 'Τύπος συνδέσμου',
'Assign automatically a category based on a link' => 'Ανατίθεται αυτόματα κατηγορία, βασισμένη στον σύνδεσμο', 'Assign automatically a category based on a link' => 'Ανατίθεται αυτόματα κατηγορία, βασισμένη στον σύνδεσμο',
'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark',
'Assignee Username' => 'Δικαιοδόχο όνομα χρήστη', 'Assignee Username' => 'Δικαιοδόχο όνομα χρήστη',
@ -980,7 +966,7 @@ return array(
'There is no group.' => 'Δεν υπάρχει ομάδα.', 'There is no group.' => 'Δεν υπάρχει ομάδα.',
'External Id' => 'Εξωτερικό αναγνωριστικό', 'External Id' => 'Εξωτερικό αναγνωριστικό',
'Add group member' => 'Προσθήκη μέλους ομάδας', 'Add group member' => 'Προσθήκη μέλους ομάδας',
'Do you really want to remove this group: "%s"?' => 'Αφαίρεση της ομάδας : « %s » ?', 'Do you really want to remove this group: "%s"?' => 'Αφαίρεση της ομάδας: « %s » ?',
'There is no user in this group.' => 'Δεν υπάρχει χρήστης σε αυτήν την ομάδα', 'There is no user in this group.' => 'Δεν υπάρχει χρήστης σε αυτήν την ομάδα',
'Remove this user' => 'Αφαίρεση χρήστη', 'Remove this user' => 'Αφαίρεση χρήστη',
'Permissions' => 'Επιτρέψεις', 'Permissions' => 'Επιτρέψεις',
@ -993,7 +979,7 @@ return array(
'Group' => 'Ομάδα', 'Group' => 'Ομάδα',
'Group Name' => 'Ονομασία ομάδας', 'Group Name' => 'Ονομασία ομάδας',
'Enter group name...' => 'Εισαγωγή ονομασίας ομάδας...', 'Enter group name...' => 'Εισαγωγή ονομασίας ομάδας...',
'Role:' => 'Ρόλος :', 'Role:' => 'Ρόλος:',
'Project members' => 'Μέλη έργου', 'Project members' => 'Μέλη έργου',
'Compare hours for "%s"' => 'Σύγκριση ωρών για « %s »', 'Compare hours for "%s"' => 'Σύγκριση ωρών για « %s »',
'%s mentioned you in the task #%d' => '%s αναφέρονται σε εσάς, στη εργασία n°%d', '%s mentioned you in the task #%d' => '%s αναφέρονται σε εσάς, στη εργασία n°%d',
@ -1002,8 +988,8 @@ return array(
'You were mentioned in a comment on the task #%d' => 'Αναφέρεστε σε σχόλιο, στην εργασία n°%d', 'You were mentioned in a comment on the task #%d' => 'Αναφέρεστε σε σχόλιο, στην εργασία n°%d',
'Mentioned' => 'Αναφέρεται', 'Mentioned' => 'Αναφέρεται',
'Compare Estimated Time vs Actual Time' => 'Σύγκριση προβλεπόμενου χρόνου vs πραγματικού χρόνου', 'Compare Estimated Time vs Actual Time' => 'Σύγκριση προβλεπόμενου χρόνου vs πραγματικού χρόνου',
'Estimated hours: ' => 'Προβλεπόμενες ώρες : ', 'Estimated hours: ' => 'Προβλεπόμενες ώρες: ',
'Actual hours: ' => 'Πραγματικές ώρες : ', 'Actual hours: ' => 'Πραγματικές ώρες: ',
'Hours Spent' => 'Δαπανόμενες ώρες', 'Hours Spent' => 'Δαπανόμενες ώρες',
'Hours Estimated' => 'Προβλεπόμενες ώρες', 'Hours Estimated' => 'Προβλεπόμενες ώρες',
'Estimated Time' => 'Προβλεπόμενος χρόνος', 'Estimated Time' => 'Προβλεπόμενος χρόνος',
@ -1013,9 +999,9 @@ return array(
'Assign the task to the person who does the action when the column is changed' => 'Ανάθεση της εργασίας στο άτομο κάνει την ενέργεια όταν η στήλη αλλάζει', 'Assign the task to the person who does the action when the column is changed' => 'Ανάθεση της εργασίας στο άτομο κάνει την ενέργεια όταν η στήλη αλλάζει',
'Close a task in a specific column' => 'Κλείσιμο εργασίας σε συγκεκριμένη στήλη', 'Close a task in a specific column' => 'Κλείσιμο εργασίας σε συγκεκριμένη στήλη',
'Time-based One-time Password Algorithm' => 'Time-based One-time Password Algorithm', 'Time-based One-time Password Algorithm' => 'Time-based One-time Password Algorithm',
'Two-Factor Provider: ' => 'Two-Factor Provider : ', 'Two-Factor Provider: ' => 'Two-Factor Provider: ',
'Disable two-factor authentication' => 'Disable two-factor authentication', 'Disable two-factor authentication' => 'Απενεργοποίηση two-factor authentication',
'Enable two-factor authentication' => 'Enable two-factor authentication', 'Enable two-factor authentication' => 'Ενεργοποίηση two-factor authentication',
'There is no integration registered at the moment.' => 'There is no integration registered at the moment.', 'There is no integration registered at the moment.' => 'There is no integration registered at the moment.',
'Password Reset for Kanboard' => 'Αρχικοποίηση κωδικών πρόσβασης για την εφαρμογή Kanboard', 'Password Reset for Kanboard' => 'Αρχικοποίηση κωδικών πρόσβασης για την εφαρμογή Kanboard',
'Forgot password?' => 'Ξεχάσατε τον κωδικό πρόσβασης ?', 'Forgot password?' => 'Ξεχάσατε τον κωδικό πρόσβασης ?',
@ -1023,7 +1009,7 @@ return array(
'Password Reset' => 'Αρχικοποίηση κωδικού πρόσβασης', 'Password Reset' => 'Αρχικοποίηση κωδικού πρόσβασης',
'New password' => 'Νέος κωδικός πρόσβασης', 'New password' => 'Νέος κωδικός πρόσβασης',
'Change Password' => 'Αλλαγή κωδικού πρόσβασης', 'Change Password' => 'Αλλαγή κωδικού πρόσβασης',
'To reset your password click on this link:' => 'Για να αρχικοποιηθεί ο κωδικός πρόσβασης σας πατήστε σε αυτόν τον σύνδεσμο :', 'To reset your password click on this link:' => 'Για να αρχικοποιηθεί ο κωδικός πρόσβασης σας πατήστε σε αυτόν τον σύνδεσμο:',
'Last Password Reset' => 'Αρχικοποίηση τελευταίου κωδικού πρόσβασης', 'Last Password Reset' => 'Αρχικοποίηση τελευταίου κωδικού πρόσβασης',
'The password has never been reinitialized.' => 'Ο κωδικός πρόσβασης δεν μπορεί να αρχικοποιηθεί για δεύτερη φορά.', 'The password has never been reinitialized.' => 'Ο κωδικός πρόσβασης δεν μπορεί να αρχικοποιηθεί για δεύτερη φορά.',
'Creation' => 'Δημιουργία', 'Creation' => 'Δημιουργία',
@ -1036,116 +1022,135 @@ return array(
'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Κανένα plugin δεν έχει καταχωρηθεί με τη μέθοδο της κοινοποίησης του έργου. Μπορείτε ακόμα να διαμορφώσετε τις μεμονωμένες κοινοποιήσεις στο προφίλ χρήστη σας.', 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Κανένα plugin δεν έχει καταχωρηθεί με τη μέθοδο της κοινοποίησης του έργου. Μπορείτε ακόμα να διαμορφώσετε τις μεμονωμένες κοινοποιήσεις στο προφίλ χρήστη σας.',
'My dashboard' => 'Το κεντρικό ταμπλό μου', 'My dashboard' => 'Το κεντρικό ταμπλό μου',
'My profile' => 'Το προφίλ μου', 'My profile' => 'Το προφίλ μου',
// 'Project owner: ' => '', 'Project owner: ' => 'Ιδιοκτήτης έργου: ',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Το αναγνωριστικό έργου είναι προαιρετικό και πρέπει να είναι αλφαριθμητικό, για παράδειγμα: MYPROJECT',
// 'Project owner' => '', 'Project owner' => 'Ιδιοκτήτης έργου',
// 'Those dates are useful for the project Gantt chart.' => '', 'Those dates are useful for the project Gantt chart.' => 'Οι ημερομηνίες αυτές είανι χρήσιμες για το διάγραμμα Gantt του έργου',
// 'Private projects do not have users and groups management.' => '', 'Private projects do not have users and groups management.' => 'Τα ιδιωτικά έργα δεν έχουν χρήστες και διαχείριση ομάδων',
// 'There is no project member.' => '', 'There is no project member.' => 'Δεν υπάρχει μέλος στο έργο',
// 'Priority' => '', 'Priority' => 'Προτεραιότητα',
// 'Task priority' => '', 'Task priority' => 'Προτεραιότητα εργασίας',
// 'General' => '', 'General' => 'Γενικά',
// 'Dates' => '', 'Dates' => 'Ημερομηνίες',
// 'Default priority' => '', 'Default priority' => 'Εξ ορισμού προτεραιότητα',
// 'Lowest priority' => '', 'Lowest priority' => 'η χαμηλότερη προτεραιότητα',
// 'Highest priority' => '', 'Highest priority' => 'η υψηλότερη προτεραιότητα',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '', 'If you put zero to the low and high priority, this feature will be disabled.' => 'Αν βάλετε μηδέν στη χαμηλή και στην υψηλή προτεραιότητα, το χαρακτηριστικό αυτό απενεργοποιείται.',
// 'Close a task when there is no activity' => '', 'Close a task when there is no activity' => 'Κλείσιμο εργασίας όταν δεν υπάρχει δραστηριότητα',
// 'Duration in days' => '', 'Duration in days' => 'Διάρκεια σε ημέρες',
// 'Send email when there is no activity on a task' => '', 'Send email when there is no activity on a task' => 'Αποστολή email όταν δεν υπάρχει δραστηριότητα σε εργασία',
// 'List of external links' => '', 'Unable to fetch link information.' => 'Δεν είναι δυνατή η ανάλυση της πληροφορίας συνδεσμου',
// 'Unable to fetch link information.' => '', 'Daily background job for tasks' => 'Ημερήσια παρασκηνιακή δουλειά για τις εργασίες',
// 'Daily background job for tasks' => '', 'Auto' => 'Αυτόματο',
// 'Auto' => '', 'Related' => 'Σχετίζεται',
// 'Related' => '', 'Attachment' => 'Συνημμένο',
// 'Attachment' => '', 'Title not found' => 'Ο τίτλος δεν βρέθηκε',
// 'Title not found' => '', 'Web Link' => 'Σύνδεσμος web',
// 'Web Link' => '', 'External links' => 'Εξωτερικοί σύνδεσμοι',
// 'External links' => '', 'Add external link' => 'Προσθήκη εξωτερικού συνδέσμου',
// 'Add external link' => '', 'Type' => 'Τύπος',
// 'Type' => '', 'Dependency' => 'Εξάρτηση',
// 'Dependency' => '', 'Add internal link' => 'Προσθήκη εσωτερικού συνδέσμου',
// 'Add internal link' => '', 'Add a new external link' => 'Προσθήκη νέου εξωτερικού συνδέσμου',
// 'Add a new external link' => '', 'Edit external link' => 'Διόρθωση εξωτερικού συνδέσμου',
// 'Edit external link' => '', 'External link' => 'Εξωτερικός σύνδεσμος',
// 'External link' => '', 'Copy and paste your link here...' => 'Κάντε αντιγραφή και επικόλληση εδώ',
// 'Copy and paste your link here...' => '', 'URL' => 'URL',
// 'URL' => '', 'Internal links' => 'Εσωτερικοί σύνδεσμοι',
// 'There is no external link for the moment.' => '', 'Assign to me' => 'Αναττίθεται σε εμένα',
// 'Internal links' => '', 'Me' => 'Σε μένα',
// 'There is no internal link for the moment.' => '', 'Do not duplicate anything' => 'Να μην γίνει κλωνοποίηση από άλλο έργο',
// 'Assign to me' => '', 'Projects management' => 'Διαχείριση έργων',
// 'Me' => '', 'Users management' => 'Διαχείριση χρηστών',
// 'Do not duplicate anything' => '', 'Groups management' => 'Διαχείριση ομάδων',
// 'Projects management' => '', 'Create from another project' => 'Δημιουργία από άλλο έργο',
// 'Users management' => '', 'open' => 'Ανοικτό',
// 'Groups management' => '', 'closed' => 'Κλειστό',
// 'Create from another project' => '', 'Priority:' => 'Προτεραιότητα:',
// 'There is no subtask at the moment.' => '', 'Reference:' => 'Αναφορά:',
// 'open' => '', 'Complexity:' => 'Πολυπλοκότητα:',
// 'closed' => '', 'Swimlane:' => 'Λωρίδα:',
// 'Priority:' => '', 'Column:' => 'Στήλη:',
// 'Reference:' => '', 'Position:' => 'Θέση:',
// 'Complexity:' => '', 'Creator:' => 'Δημιουργός:',
// 'Swimlane:' => '', 'Time estimated:' => 'Προβλεπόμενη ώρα:',
// 'Column:' => '', '%s hours' => '%s ώρες',
// 'Position:' => '', 'Time spent:' => 'χρόνος που καταναλώθηκε:',
// 'Creator:' => '', 'Created:' => 'Δημιουργήθηκε:',
// 'Time estimated:' => '', 'Modified:' => 'Διορθώθηκε:',
// '%s hours' => '', 'Completed:' => 'Ολοκληρώθηκε:',
// 'Time spent:' => '', 'Started:' => 'Ξεκίνησε:',
// 'Created:' => '', 'Moved:' => 'Μετακινήθηκε:',
// 'Modified:' => '', 'Task #%d' => 'Εργασία #%d',
// 'Completed:' => '', 'Date and time format' => 'Μορφή ημερομηνίας και ώρας',
// 'Started:' => '', 'Time format' => 'Μορφή ώρας',
// 'Moved:' => '', 'Start date: ' => 'Ημερομηνία έναρξης: ',
// 'Task #%d' => '', 'End date: ' => 'Ημερομηνία λήξης: ',
// 'Sub-tasks' => '', 'New due date: ' => 'Νέα ημερομηνία λήξης: ',
// 'Date and time format' => '', 'Start date changed: ' => 'Αλλαγμένη ημερομηνία έναρξης: ',
// 'Time format' => '', 'Disable private projects' => 'Απενεργοποίηση ιδιωτικών έργων',
// 'Start date: ' => '', 'Do you really want to remove this custom filter: "%s"?' => 'Αφαίρεση αυτού του οριζόμενου από το χρήστη φίλτρου %s ?',
// 'End date: ' => '', 'Remove a custom filter' => 'Αφαίρεση του οριζόμενου από το χρήστη φίλτρου',
// 'New due date: ' => '', 'User activated successfully.' => 'Ο χρήστης ενεργοποιήθηκε με επιτυχία',
// 'Start date changed: ' => '', 'Unable to enable this user.' => 'Δεν είναι δυνατή η ενεργοποίηση του χρήστη',
// 'Disable private projects' => '', 'User disabled successfully.' => 'Η απενεργοποίηση του χρήστη έγινε με επιτυχία',
// 'Do you really want to remove this custom filter: "%s"?' => '', 'Unable to disable this user.' => 'Δεν είναι δυνατή η απενεργοποίηση του χρήστη',
// 'Remove a custom filter' => '', 'All files have been uploaded successfully.' => 'Όλα τα αρχεία ανέβηκαν με επιτυχία',
// 'User activated successfully.' => '', 'View uploaded files' => 'Προβολή ανεβασμένων αρχείων',
// 'Unable to enable this user.' => '', 'The maximum allowed file size is %sB.' => 'Το μέγιστο μέγεθος αρχείου που επιτρέπεται είναι %sB.',
// 'User disabled successfully.' => '', 'Choose files again' => 'Επιλογή κι άλλων αρχείων',
// 'Unable to disable this user.' => '', 'Drag and drop your files here' => 'Σύρετε τα αρχεία σας εδώ',
// 'All files have been uploaded successfully.' => '', 'choose files' => 'Επιλογή αρχείων',
// 'View uploaded files' => '', 'View profile' => 'Προβολή προφίλ',
// 'The maximum allowed file size is %sB.' => '', 'Two Factor' => 'Δύο παραγόντων',
// 'Choose files again' => '', 'Disable user' => 'Απενεργοποίηση χρήστη',
// 'Drag and drop your files here' => '', 'Do you really want to disable this user: "%s"?' => 'Απενεργοποίηση του χρήστη: "%s";',
// 'choose files' => '', 'Enable user' => 'Ενεργοποίηση χρήστη',
// 'View profile' => '', 'Do you really want to enable this user: "%s"?' => 'Ενεργοποίηση του χρήστη "%s";',
// 'Two Factor' => '', 'Download' => 'Κατέβασμα',
// 'Disable user' => '', 'Uploaded: %s' => 'Ανέβηκε το αρχείο: %s',
// 'Do you really want to disable this user: "%s"?' => '', 'Size: %s' => 'Μέγεθος: %s',
// 'Enable user' => '', 'Uploaded by %s' => 'Ανέβασμα από τον χρήστη: %s',
// 'Do you really want to enable this user: "%s"?' => '', 'Filename' => 'Όνομα αρχείου',
// 'Download' => '', 'Size' => 'Μέγεθος',
// 'Uploaded: %s' => '', 'Column created successfully.' => 'Η στήλη δημιουργήθηκε με επιτυχία.',
// 'Size: %s' => '', 'Another column with the same name exists in the project' => 'Μια άλλη στήλη με το ίδιο όνομα υπάρχει στο έργο',
// 'Uploaded by %s' => '', 'Default filters' => 'Εξ\' ορισμού φίλτρα',
// 'Filename' => '', 'Your board doesn\'t have any column!' => 'Το ταμπλό δεν έχει καμία στήλη!!=',
// 'Size' => '', 'Change column position' => 'Αλλαγή θέσης στήλης',
// 'Column created successfully.' => '', 'Switch to the project overview' => 'Αλλαγή προβολής σε επισκόπηση έργου',
// 'Another column with the same name exists in the project' => '', 'User filters' => 'Φίλτρα οριζόμενα από τον χρήστη',
// 'Default filters' => '', 'Category filters' => 'Κατηγορία φίλτρων',
// 'Your board doesn\'t have any column!' => '', 'Upload a file' => 'Ανέβασμα αρχείου',
// 'Change column position' => '', 'View file' => 'Προβολή αρχείου',
// 'Switch to the project overview' => '', 'Last activity' => 'Τελευταία δραστηριότητα',
// 'User filters' => '', 'Change subtask position' => 'Αλλαγή θέσης υπο-εργασίας',
// 'Category filters' => '', 'This value must be greater than %d' => 'Η τιμή πρέπει να είναι μεγαλύτερη από %d',
// 'Upload a file' => '', 'Another swimlane with the same name exists in the project' => 'Μια άλλη λωρίδα, με το ίδιο όνομα υπάρχει στο έργο',
// 'There is no attachment at the moment.' => '', 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Παράδειγμα: http://example.kanboard.net/ (χρησιμοποιείται για τη δημιουργία απόλυτων URLs)',
// 'View file' => '', // 'Actions duplicated successfully.' => '',
// 'Last activity' => '', // 'Unable to duplicate actions.' => '',
// 'Change subtask position' => '', // 'Add a new action' => '',
// 'This value must be greater than %d' => '', // 'Import from another project' => '',
// 'Another swimlane with the same name exists in the project' => '', // 'There is no action at the moment.' => '',
// 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '', // 'Import actions from another project' => '',
// 'There is no available project.' => '',
// 'Local File' => '',
// 'Configuration' => '',
// 'PHP version:' => '',
// 'PHP SAPI:' => '',
// 'OS version:' => '',
// 'Database version:' => '',
// 'Browser:' => '',
// 'Task view' => '',
// 'Edit task' => '',
// 'Edit description' => '',
// 'New internal link' => '',
// 'Display list of keyboard shortcuts' => '',
// 'Menu' => '',
// 'Set start date' => '',
// 'Avatar' => '',
// 'Upload my avatar image' => '',
// 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '',
); );

View file

@ -8,7 +8,6 @@ return array(
'Edit' => 'Modificar', 'Edit' => 'Modificar',
'remove' => 'suprimir', 'remove' => 'suprimir',
'Remove' => 'Suprimir', 'Remove' => 'Suprimir',
'Update' => 'Actualizar',
'Yes' => 'Sí', 'Yes' => 'Sí',
'No' => 'No', 'No' => 'No',
'cancel' => 'cancelar', 'cancel' => 'cancelar',
@ -60,7 +59,6 @@ return array(
'Actions' => 'Acciones', 'Actions' => 'Acciones',
'Inactive' => 'Inactivo', 'Inactive' => 'Inactivo',
'Active' => 'Activo', 'Active' => 'Activo',
'Add this column' => 'Añadir esta columna',
'%d tasks on the board' => '%d tareas en el tablero', '%d tasks on the board' => '%d tareas en el tablero',
'%d tasks in total' => '%d tareas en total', '%d tasks in total' => '%d tareas en total',
'Unable to update this board.' => 'No se puede actualizar este tablero.', 'Unable to update this board.' => 'No se puede actualizar este tablero.',
@ -74,7 +72,6 @@ return array(
'All projects' => 'Todos los proyectos', 'All projects' => 'Todos los proyectos',
'Add a new column' => 'Añadir una nueva columna', 'Add a new column' => 'Añadir una nueva columna',
'Title' => 'Título', 'Title' => 'Título',
'Nobody assigned' => 'Nadie asignado',
'Assigned to %s' => 'Asignada a %s', 'Assigned to %s' => 'Asignada a %s',
'Remove a column' => 'Suprimir esta columna', 'Remove a column' => 'Suprimir esta columna',
'Remove a column from a board' => 'Suprimir una columna de un tablero', 'Remove a column from a board' => 'Suprimir una columna de un tablero',
@ -168,7 +165,6 @@ return array(
'Task count' => 'Contador de tareas', 'Task count' => 'Contador de tareas',
'User' => 'Usuario', 'User' => 'Usuario',
'Comments' => 'Comentarios', 'Comments' => 'Comentarios',
'Write your text in Markdown' => 'Redacta el texto en Markdown',
'Leave a comment' => 'Dejar un comentario', 'Leave a comment' => 'Dejar un comentario',
'Comment is required' => 'El comentario es obligatorio', 'Comment is required' => 'El comentario es obligatorio',
'Leave a description' => 'Dejar una descripción', 'Leave a description' => 'Dejar una descripción',
@ -184,7 +180,6 @@ return array(
'Unable to remove this action.' => 'No se puede suprimir esta accción.', 'Unable to remove this action.' => 'No se puede suprimir esta accción.',
'Action removed successfully.' => 'La acción ha sido borrada correctamente.', 'Action removed successfully.' => 'La acción ha sido borrada correctamente.',
'Automatic actions for the project "%s"' => 'Acciones automatizadas para este proyecto « %s »', 'Automatic actions for the project "%s"' => 'Acciones automatizadas para este proyecto « %s »',
'Defined actions' => 'Acciones definidas',
'Add an action' => 'Agregar una acción', 'Add an action' => 'Agregar una acción',
'Event name' => 'Nombre del evento', 'Event name' => 'Nombre del evento',
'Action name' => 'Nombre de la acción', 'Action name' => 'Nombre de la acción',
@ -194,7 +189,6 @@ return array(
'When the selected event occurs execute the corresponding action.' => 'Cuando tiene lugar el evento seleccionado, ejecutar la acción correspondiente.', 'When the selected event occurs execute the corresponding action.' => 'Cuando tiene lugar el evento seleccionado, ejecutar la acción correspondiente.',
'Next step' => 'Etapa siguiente', 'Next step' => 'Etapa siguiente',
'Define action parameters' => 'Definición de los parametros de la acción', 'Define action parameters' => 'Definición de los parametros de la acción',
'Save this action' => 'Guardar esta acción',
'Do you really want to remove this action: "%s"?' => '¿Realmente desea suprimir esta acción « %s » ?', 'Do you really want to remove this action: "%s"?' => '¿Realmente desea suprimir esta acción « %s » ?',
'Remove an automatic action' => 'Suprimir una acción automatizada', 'Remove an automatic action' => 'Suprimir una acción automatizada',
'Assign the task to a specific user' => 'Asignar una tarea a un usuario especifico', 'Assign the task to a specific user' => 'Asignar una tarea a un usuario especifico',
@ -333,10 +327,10 @@ return array(
'Time tracking:' => 'Control de tiempo:', 'Time tracking:' => 'Control de tiempo:',
'New sub-task' => 'Nueva subtarea', 'New sub-task' => 'Nueva subtarea',
'New attachment added "%s"' => 'Nuevo adjunto agregado "%s"', 'New attachment added "%s"' => 'Nuevo adjunto agregado "%s"',
'Comment updated' => 'Comentario actualizado',
'New comment posted by %s' => 'Nuevo comentario agregado por %s', 'New comment posted by %s' => 'Nuevo comentario agregado por %s',
'New attachment' => 'Nuevo adjunto', 'New attachment' => 'Nuevo adjunto',
'New comment' => 'Nuevo comentario', 'New comment' => 'Nuevo comentario',
'Comment updated' => 'Comentario actualizado',
'New subtask' => 'Nueva subtarea', 'New subtask' => 'Nueva subtarea',
'Subtask updated' => 'Subtarea actualizada', 'Subtask updated' => 'Subtarea actualizada',
'Task updated' => 'Tarea actualizada', 'Task updated' => 'Tarea actualizada',
@ -436,7 +430,6 @@ return array(
'ISO format is always accepted, example: "%s" and "%s"' => 'El formato ISO siempre es aceptado, ejemplo: "%s" y "%s"', 'ISO format is always accepted, example: "%s" and "%s"' => 'El formato ISO siempre es aceptado, ejemplo: "%s" y "%s"',
'New private project' => 'Nuevo proyecto privado', 'New private project' => 'Nuevo proyecto privado',
'This project is private' => 'Este proyecto es privado', 'This project is private' => 'Este proyecto es privado',
'Type here to create a new sub-task' => 'Escriba aquí para crear una nueva sub-tarea',
'Add' => 'Añadir', 'Add' => 'Añadir',
'Start date' => 'Fecha de inicio', 'Start date' => 'Fecha de inicio',
'Time estimated' => 'Tiempo estimado', 'Time estimated' => 'Tiempo estimado',
@ -487,9 +480,6 @@ return array(
'Daily project summary export for "%s"' => 'Exportar sumario diario del proyecto para "%s"', 'Daily project summary export for "%s"' => 'Exportar sumario diario del proyecto para "%s"',
'Exports' => 'Exportaciones', 'Exports' => 'Exportaciones',
'This export contains the number of tasks per column grouped per day.' => 'Esta exportación contiene el número de tereas por columna agrupada por día.', 'This export contains the number of tasks per column grouped per day.' => 'Esta exportación contiene el número de tereas por columna agrupada por día.',
'Nothing to preview...' => 'Nada que previsualizar...',
'Preview' => 'Previsualizar',
'Write' => 'Grabar',
'Active swimlanes' => 'Calles activas', 'Active swimlanes' => 'Calles activas',
'Add a new swimlane' => 'Añadir nueva calle', 'Add a new swimlane' => 'Añadir nueva calle',
'Change default swimlane' => 'Cambiar la calle por defecto', 'Change default swimlane' => 'Cambiar la calle por defecto',
@ -543,7 +533,6 @@ return array(
'Task age in days' => 'Edad de la tarea en días', 'Task age in days' => 'Edad de la tarea en días',
'Days in this column' => 'Días en esta columna', 'Days in this column' => 'Días en esta columna',
'%dd' => '%dd', '%dd' => '%dd',
'Add a link' => 'Añadir enlace',
'Add a new link' => 'Añadir nuevo enlace', 'Add a new link' => 'Añadir nuevo enlace',
'Do you really want to remove this link: "%s"?' => '¿Realmente quiere quitar este enlace: "%s"?', 'Do you really want to remove this link: "%s"?' => '¿Realmente quiere quitar este enlace: "%s"?',
'Do you really want to remove this link with task #%d?' => '¿Realmente quiere quitar este enlace con esta tarea: #%d?', 'Do you really want to remove this link with task #%d?' => '¿Realmente quiere quitar este enlace con esta tarea: #%d?',
@ -734,7 +723,7 @@ return array(
'Time spent changed: %sh' => 'Se ha cambiado el tiempo empleado: %sh', 'Time spent changed: %sh' => 'Se ha cambiado el tiempo empleado: %sh',
'Time estimated changed: %sh' => 'Se ha cambiado el tiempo estimado: %sh', 'Time estimated changed: %sh' => 'Se ha cambiado el tiempo estimado: %sh',
'The field "%s" have been updated' => 'Se ha actualizado el campo "%s"', 'The field "%s" have been updated' => 'Se ha actualizado el campo "%s"',
'The description have been modified' => 'Se ha modificado la descripción', 'The description has been modified:' => 'Se ha modificado la descripción',
'Do you really want to close the task "%s" as well as all subtasks?' => '¿De verdad que quiere cerra la tarea "%s" así como todas las subtareas?', 'Do you really want to close the task "%s" as well as all subtasks?' => '¿De verdad que quiere cerra la tarea "%s" así como todas las subtareas?',
'I want to receive notifications for:' => 'Deseo recibir notificaciones para:', 'I want to receive notifications for:' => 'Deseo recibir notificaciones para:',
'All tasks' => 'Todas las tareas', 'All tasks' => 'Todas las tareas',
@ -753,8 +742,6 @@ return array(
'My activity stream' => 'Mi flujo de actividad', 'My activity stream' => 'Mi flujo de actividad',
'My calendar' => 'Mi calendario', 'My calendar' => 'Mi calendario',
'Search tasks' => 'Buscar tareas', 'Search tasks' => 'Buscar tareas',
'Back to the calendar' => 'Volver al calendario',
'Filters' => 'Filtros',
'Reset filters' => 'Limpiar filtros', 'Reset filters' => 'Limpiar filtros',
'My tasks due tomorrow' => 'Mis tareas a entregar mañana', 'My tasks due tomorrow' => 'Mis tareas a entregar mañana',
'Tasks due today' => 'Tareas a antregar hoy', 'Tasks due today' => 'Tareas a antregar hoy',
@ -854,7 +841,6 @@ return array(
'End date:' => 'Fecha final', 'End date:' => 'Fecha final',
'There is no start date or end date for this project.' => 'No existe fecha de inicio o de fin para este proyecto.', 'There is no start date or end date for this project.' => 'No existe fecha de inicio o de fin para este proyecto.',
'Projects Gantt chart' => 'Diagramas de Gantt de los proyectos', 'Projects Gantt chart' => 'Diagramas de Gantt de los proyectos',
'Link type' => 'Tipo de enlace',
'Change task color when using a specific task link' => 'Cambiar colo de la tarea al usar un enlace específico a tarea', 'Change task color when using a specific task link' => 'Cambiar colo de la tarea al usar un enlace específico a tarea',
'Task link creation or modification' => 'Creación o modificación de enlace a tarea', 'Task link creation or modification' => 'Creación o modificación de enlace a tarea',
'Milestone' => 'Hito', 'Milestone' => 'Hito',
@ -906,7 +892,6 @@ return array(
'Shared' => 'Compartido', 'Shared' => 'Compartido',
'Owner' => 'Dueño', 'Owner' => 'Dueño',
'Unread notifications' => 'Notificaciones sin leer', 'Unread notifications' => 'Notificaciones sin leer',
'My filters' => 'Mis filtros',
'Notification methods:' => 'Métodos de notificación', 'Notification methods:' => 'Métodos de notificación',
'Import tasks from CSV file' => 'Importar tareas desde archivo CSV', 'Import tasks from CSV file' => 'Importar tareas desde archivo CSV',
'Unable to read your file' => 'No es posible leer el archivo', 'Unable to read your file' => 'No es posible leer el archivo',
@ -930,7 +915,7 @@ return array(
'change sorting' => 'Cambiar orden', 'change sorting' => 'Cambiar orden',
'Tasks Importation' => 'Importación de tareas', 'Tasks Importation' => 'Importación de tareas',
'Delimiter' => 'Delimitador', 'Delimiter' => 'Delimitador',
// 'Enclosure' => '', 'Enclosure' => 'Recinto',
'CSV File' => 'Archivo CSV', 'CSV File' => 'Archivo CSV',
'Instructions' => 'Indicaciones', 'Instructions' => 'Indicaciones',
'Your file must use the predefined CSV format' => 'Su archivo debe utilizar el formato CSV predeterminado', 'Your file must use the predefined CSV format' => 'Su archivo debe utilizar el formato CSV predeterminado',
@ -944,6 +929,7 @@ return array(
'Usernames must be lowercase and unique' => 'Los nombres de usuario deben ser únicos y contener sólo minúsculas', 'Usernames must be lowercase and unique' => 'Los nombres de usuario deben ser únicos y contener sólo minúsculas',
'Passwords will be encrypted if present' => 'Las contraseñas serán cifradas si es que existen', 'Passwords will be encrypted if present' => 'Las contraseñas serán cifradas si es que existen',
'%s attached a new file to the task %s' => '%s adjuntó un nuevo archivo a la tarea %s', '%s attached a new file to the task %s' => '%s adjuntó un nuevo archivo a la tarea %s',
'Link type' => 'Tipo de enlace',
'Assign automatically a category based on a link' => 'Asignar una categoría automáticamente basado en un enlace', 'Assign automatically a category based on a link' => 'Asignar una categoría automáticamente basado en un enlace',
'BAM - Konvertible Mark' => 'BAM - marco convertible', 'BAM - Konvertible Mark' => 'BAM - marco convertible',
'Assignee Username' => 'Nombre de usuario del concesionario', 'Assignee Username' => 'Nombre de usuario del concesionario',
@ -995,19 +981,19 @@ return array(
'Enter group name...' => 'Ingresa el nombre del grupo...', 'Enter group name...' => 'Ingresa el nombre del grupo...',
'Role:' => 'Rol:', 'Role:' => 'Rol:',
'Project members' => 'Miembros del proyecto', 'Project members' => 'Miembros del proyecto',
// 'Compare hours for "%s"' => '', 'Compare hours for "%s"' => 'Compara horas con "%s"',
'%s mentioned you in the task #%d' => '%s te mencionó en la tarea #%d', '%s mentioned you in the task #%d' => '%s te mencionó en la tarea #%d',
'%s mentioned you in a comment on the task #%d' => '%s te mencionó en un comentario en la tarea #%d', '%s mentioned you in a comment on the task #%d' => '%s te mencionó en un comentario en la tarea #%d',
'You were mentioned in the task #%d' => 'Te mencionaron en la tarea #%d', 'You were mentioned in the task #%d' => 'Te mencionaron en la tarea #%d',
'You were mentioned in a comment on the task #%d' => 'Te mencionaron en un comentario en la tarea #%d', 'You were mentioned in a comment on the task #%d' => 'Te mencionaron en un comentario en la tarea #%d',
'Mentioned' => 'Mencionado', 'Mentioned' => 'Mencionado',
// 'Compare Estimated Time vs Actual Time' => '', 'Compare Estimated Time vs Actual Time' => 'Comparar Tiempo Estimado vs Tiempo Actual',
// 'Estimated hours: ' => '', 'Estimated hours: ' => 'Horas estimadas: ',
// 'Actual hours: ' => '', 'Actual hours: ' => 'Horas actuales: ',
// 'Hours Spent' => '', 'Hours Spent' => 'Horas gastadas',
// 'Hours Estimated' => '', 'Hours Estimated' => 'Hora Estimada',
// 'Estimated Time' => '', 'Estimated Time' => 'Tiempo Estimado',
// 'Actual Time' => '', 'Actual Time' => 'Tiempo Actual',
'Estimated vs actual time' => 'Tiempo estimado vs real', 'Estimated vs actual time' => 'Tiempo estimado vs real',
'RUB - Russian Ruble' => 'RUB - rublo ruso', 'RUB - Russian Ruble' => 'RUB - rublo ruso',
'Assign the task to the person who does the action when the column is changed' => 'Asignar la tarea a la persona que haga la acción al cambiar de columna', 'Assign the task to the person who does the action when the column is changed' => 'Asignar la tarea a la persona que haga la acción al cambiar de columna',
@ -1025,7 +1011,7 @@ return array(
'Change Password' => 'Cambiar contraseña', 'Change Password' => 'Cambiar contraseña',
'To reset your password click on this link:' => 'Para cambiar tu contraseña haz clic en el siguiente enlace:', 'To reset your password click on this link:' => 'Para cambiar tu contraseña haz clic en el siguiente enlace:',
'Last Password Reset' => 'Último cambio de contraseña', 'Last Password Reset' => 'Último cambio de contraseña',
// 'The password has never been reinitialized.' => '', 'The password has never been reinitialized.' => 'La contraseña nunca se ha reinicializado.',
'Creation' => 'Creación', 'Creation' => 'Creación',
'Expiration' => 'Vencimiento', 'Expiration' => 'Vencimiento',
'Password reset history' => 'Historial de restablecimiento de contraseña', 'Password reset history' => 'Historial de restablecimiento de contraseña',
@ -1053,7 +1039,6 @@ return array(
'Close a task when there is no activity' => 'Cerrar tarea cuando no haya actividad', 'Close a task when there is no activity' => 'Cerrar tarea cuando no haya actividad',
'Duration in days' => 'Duración en días', 'Duration in days' => 'Duración en días',
'Send email when there is no activity on a task' => 'Enviar correo cuando no haya actividad en una tarea', 'Send email when there is no activity on a task' => 'Enviar correo cuando no haya actividad en una tarea',
'List of external links' => 'Lista de enlaces externos',
'Unable to fetch link information.' => 'No es posible obtener información sobre el enlace', 'Unable to fetch link information.' => 'No es posible obtener información sobre el enlace',
'Daily background job for tasks' => 'Tarea de fondo diaria para las tareas', 'Daily background job for tasks' => 'Tarea de fondo diaria para las tareas',
'Auto' => 'Automático', 'Auto' => 'Automático',
@ -1071,9 +1056,7 @@ return array(
'External link' => 'Enlace externo', 'External link' => 'Enlace externo',
'Copy and paste your link here...' => 'Copia y pega tu enlace aquí...', 'Copy and paste your link here...' => 'Copia y pega tu enlace aquí...',
'URL' => 'URL', 'URL' => 'URL',
'There is no external link for the moment.' => 'No existe un enlace externo por el momento',
'Internal links' => 'Enlaces internos', 'Internal links' => 'Enlaces internos',
'There is no internal link for the moment.' => 'No existe un enlace interno por el momento',
'Assign to me' => 'Asignar a mí', 'Assign to me' => 'Asignar a mí',
'Me' => 'Yo', 'Me' => 'Yo',
'Do not duplicate anything' => 'No duplicar nada', 'Do not duplicate anything' => 'No duplicar nada',
@ -1081,26 +1064,24 @@ return array(
'Users management' => 'Administración de usuarios', 'Users management' => 'Administración de usuarios',
'Groups management' => 'Administración de grupos', 'Groups management' => 'Administración de grupos',
'Create from another project' => 'Crear de otro proyecto', 'Create from another project' => 'Crear de otro proyecto',
'There is no subtask at the moment.' => 'No existe subtarea por el momento',
'open' => 'abierto', 'open' => 'abierto',
'closed' => 'cerrado', 'closed' => 'cerrado',
'Priority:' => 'Prioridad', 'Priority:' => 'Prioridad',
'Reference:' => 'Referencia', 'Reference:' => 'Referencia',
// 'Complexity:' => 'Complejidad', 'Complexity:' => 'Complejidad:',
// 'Swimlane:' => 'Swimlane', 'Swimlane:' => 'Swimlane:',
// 'Column:' => 'Columna', 'Column:' => 'Columna:',
// 'Position:' => 'Posición', 'Position:' => 'Posición:',
// 'Creator:' => 'Creador', 'Creator:' => 'Creador:',
// 'Time estimated:' => '', 'Time estimated:' => 'Tiempo estimado:',
'%s hours' => '%s horas', '%s hours' => '%s horas',
// 'Time spent:' => '', 'Time spent:' => 'Tiempo gastado:',
'Created:' => 'Creado', 'Created:' => 'Creado',
'Modified:' => 'Modificado', 'Modified:' => 'Modificado',
'Completed:' => 'Terminado', 'Completed:' => 'Terminado',
'Started:' => 'Iniciado', 'Started:' => 'Iniciado',
'Moved:' => 'Movido', 'Moved:' => 'Movido',
'Task #%d' => 'Tarea #%d', 'Task #%d' => 'Tarea #%d',
'Sub-tasks' => 'Subtareas',
'Date and time format' => 'Formato de hora y fecha', 'Date and time format' => 'Formato de hora y fecha',
'Time format' => 'Formato de hora', 'Time format' => 'Formato de hora',
'Start date: ' => 'Fecha de inicio', 'Start date: ' => 'Fecha de inicio',
@ -1135,17 +1116,41 @@ return array(
'Column created successfully.' => 'Columna creada exitosamente', 'Column created successfully.' => 'Columna creada exitosamente',
'Another column with the same name exists in the project' => 'Ya existe una columna con el mismo nombre en el proyecto', 'Another column with the same name exists in the project' => 'Ya existe una columna con el mismo nombre en el proyecto',
'Default filters' => 'Filtros predeterminados', 'Default filters' => 'Filtros predeterminados',
'Your board doesn\'t have any column!' => '¡Tu tablero no tiene ninguna columna', 'Your board doesn\'t have any column!' => '¡Tu tablero no tiene ninguna columna!',
'Change column position' => 'Cambiar posición de la columna', 'Change column position' => 'Cambiar posición de la columna',
'Switch to the project overview' => 'Cambiar a vista general del proyecto', 'Switch to the project overview' => 'Cambiar a vista general del proyecto',
'User filters' => 'Usar filtros', 'User filters' => 'Usar filtros',
'Category filters' => 'Categoría y filtros', 'Category filters' => 'Categoría y filtros',
'Upload a file' => 'Subir archivo', 'Upload a file' => 'Subir archivo',
'There is no attachment at the moment.' => 'No existe ningún adjunto por el momento',
'View file' => 'Ver archivo', 'View file' => 'Ver archivo',
'Last activity' => 'Última actividad', 'Last activity' => 'Última actividad',
'Change subtask position' => 'Cambiar posición de la subtarea', 'Change subtask position' => 'Cambiar posición de la subtarea',
'This value must be greater than %d' => 'Este valor debe ser mayor a %d', 'This value must be greater than %d' => 'Este valor debe ser mayor a %d',
'Another swimlane with the same name exists in the project' => 'Ya existe otro swimlane con el mismo nombre en el proyecto', 'Another swimlane with the same name exists in the project' => 'Ya existe otro swimlane con el mismo nombre en el proyecto',
'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Ejemplo: http://ejemplo.kanboard.net/ (Usado para generar URLs absolutas)', 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Ejemplo: http://ejemplo.kanboard.net/ (Usado para generar URLs absolutas)',
'Actions duplicated successfully.' => 'Acción duplicada con exito.',
'Unable to duplicate actions.' => 'No se ha podido duplicar la acción.',
'Add a new action' => 'Añadir una nueva acción',
'Import from another project' => 'Importar de otro proyecto',
'There is no action at the moment.' => 'No hay ninguna acción en este momento.',
'Import actions from another project' => 'Importar acciones de otro proyecto',
'There is no available project.' => 'No hay proyectos disponibles.',
// 'Local File' => '',
// 'Configuration' => '',
// 'PHP version:' => '',
// 'PHP SAPI:' => '',
// 'OS version:' => '',
// 'Database version:' => '',
// 'Browser:' => '',
// 'Task view' => '',
// 'Edit task' => '',
// 'Edit description' => '',
// 'New internal link' => '',
// 'Display list of keyboard shortcuts' => '',
// 'Menu' => '',
// 'Set start date' => '',
// 'Avatar' => '',
// 'Upload my avatar image' => '',
// 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '',
); );

View file

@ -8,7 +8,6 @@ return array(
'Edit' => 'Muokkaa', 'Edit' => 'Muokkaa',
'remove' => 'poista', 'remove' => 'poista',
'Remove' => 'Poista', 'Remove' => 'Poista',
'Update' => 'Päivitä',
'Yes' => 'Kyllä', 'Yes' => 'Kyllä',
'No' => 'Ei', 'No' => 'Ei',
'cancel' => 'peruuta', 'cancel' => 'peruuta',
@ -60,7 +59,6 @@ return array(
'Actions' => 'Toiminnot', 'Actions' => 'Toiminnot',
'Inactive' => 'Ei aktiivinen', 'Inactive' => 'Ei aktiivinen',
'Active' => 'Aktiivinen', 'Active' => 'Aktiivinen',
'Add this column' => 'Lisää tämä sarake',
'%d tasks on the board' => '%d tehtävää taululla', '%d tasks on the board' => '%d tehtävää taululla',
'%d tasks in total' => '%d tehtävää yhteensä', '%d tasks in total' => '%d tehtävää yhteensä',
'Unable to update this board.' => 'Taulun muuttaminen ei onnistunut.', 'Unable to update this board.' => 'Taulun muuttaminen ei onnistunut.',
@ -74,7 +72,6 @@ return array(
'All projects' => 'Kaikki projektit', 'All projects' => 'Kaikki projektit',
'Add a new column' => 'Lisää uusi sarake', 'Add a new column' => 'Lisää uusi sarake',
'Title' => 'Nimi', 'Title' => 'Nimi',
'Nobody assigned' => 'Ei suorittajaa',
'Assigned to %s' => 'Tekijä: %s', 'Assigned to %s' => 'Tekijä: %s',
'Remove a column' => 'Poista sarake', 'Remove a column' => 'Poista sarake',
'Remove a column from a board' => 'Poista sarake taulusta', 'Remove a column from a board' => 'Poista sarake taulusta',
@ -168,7 +165,6 @@ return array(
'Task count' => 'Tehtävien määrä', 'Task count' => 'Tehtävien määrä',
'User' => 'Käyttäjät', 'User' => 'Käyttäjät',
'Comments' => 'Kommentit', 'Comments' => 'Kommentit',
'Write your text in Markdown' => 'Kirjoita kommenttisi Markdownilla',
'Leave a comment' => 'Lisää kommentti', 'Leave a comment' => 'Lisää kommentti',
'Comment is required' => 'Kommentti vaaditaan', 'Comment is required' => 'Kommentti vaaditaan',
'Leave a description' => 'Lisää kuvaus', 'Leave a description' => 'Lisää kuvaus',
@ -184,7 +180,6 @@ return array(
'Unable to remove this action.' => 'Toiminnon poistaminen epäonnistui.', 'Unable to remove this action.' => 'Toiminnon poistaminen epäonnistui.',
'Action removed successfully.' => 'Toiminto poistettiin onnistuneesti.', 'Action removed successfully.' => 'Toiminto poistettiin onnistuneesti.',
'Automatic actions for the project "%s"' => 'Automaattiset toiminnot projektille "%s"', 'Automatic actions for the project "%s"' => 'Automaattiset toiminnot projektille "%s"',
'Defined actions' => 'Määritellyt toiminnot',
// 'Add an action' => '', // 'Add an action' => '',
'Event name' => 'Tapahtuman nimi', 'Event name' => 'Tapahtuman nimi',
'Action name' => 'Toiminnon nimi', 'Action name' => 'Toiminnon nimi',
@ -194,7 +189,6 @@ return array(
'When the selected event occurs execute the corresponding action.' => 'Kun valittu tapahtuma tapahtuu, suorita vastaava toiminto.', 'When the selected event occurs execute the corresponding action.' => 'Kun valittu tapahtuma tapahtuu, suorita vastaava toiminto.',
'Next step' => 'Seuraava vaihe', 'Next step' => 'Seuraava vaihe',
'Define action parameters' => 'Määrittele toiminnon parametrit', 'Define action parameters' => 'Määrittele toiminnon parametrit',
'Save this action' => 'Tallenna toiminto',
'Do you really want to remove this action: "%s"?' => 'Oletko varma että haluat poistaa toiminnon "%s"?', 'Do you really want to remove this action: "%s"?' => 'Oletko varma että haluat poistaa toiminnon "%s"?',
'Remove an automatic action' => 'Poista automaattintn toiminto', 'Remove an automatic action' => 'Poista automaattintn toiminto',
'Assign the task to a specific user' => 'Osoita tehtävä käyttäjälle', 'Assign the task to a specific user' => 'Osoita tehtävä käyttäjälle',
@ -333,10 +327,10 @@ return array(
'Time tracking:' => 'Ajan seuranta:', 'Time tracking:' => 'Ajan seuranta:',
'New sub-task' => 'Uusi alitehtävä', 'New sub-task' => 'Uusi alitehtävä',
'New attachment added "%s"' => 'Uusi liite lisätty "%s"', 'New attachment added "%s"' => 'Uusi liite lisätty "%s"',
'Comment updated' => 'Kommentti päivitetty',
'New comment posted by %s' => '%s lisäsi uuden kommentin', 'New comment posted by %s' => '%s lisäsi uuden kommentin',
// 'New attachment' => '', // 'New attachment' => '',
// 'New comment' => '', // 'New comment' => '',
'Comment updated' => 'Kommentti päivitetty',
// 'New subtask' => '', // 'New subtask' => '',
// 'Subtask updated' => '', // 'Subtask updated' => '',
// 'Task updated' => '', // 'Task updated' => '',
@ -436,7 +430,6 @@ return array(
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO-muoto on aina hyväksytty, esimerkiksi %s ja %s', 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO-muoto on aina hyväksytty, esimerkiksi %s ja %s',
'New private project' => 'Uusi yksityinen projekti', 'New private project' => 'Uusi yksityinen projekti',
'This project is private' => 'Tämä projekti on yksityinen', 'This project is private' => 'Tämä projekti on yksityinen',
'Type here to create a new sub-task' => 'Kirjoita tähän luodaksesi uuden alitehtävän',
'Add' => 'Lisää', 'Add' => 'Lisää',
'Start date' => 'Aloituspäivä', 'Start date' => 'Aloituspäivä',
'Time estimated' => 'Arvioitu aika', 'Time estimated' => 'Arvioitu aika',
@ -487,9 +480,6 @@ return array(
'Daily project summary export for "%s"' => 'Päivittäisen yhteenvedon vienti kohteeseen "%s"', 'Daily project summary export for "%s"' => 'Päivittäisen yhteenvedon vienti kohteeseen "%s"',
'Exports' => 'Viennit', 'Exports' => 'Viennit',
'This export contains the number of tasks per column grouped per day.' => 'Tämä tiedosto sisältää tehtäviä sarakkeisiin päiväkohtaisesti ryhmilteltyinä', 'This export contains the number of tasks per column grouped per day.' => 'Tämä tiedosto sisältää tehtäviä sarakkeisiin päiväkohtaisesti ryhmilteltyinä',
'Nothing to preview...' => 'Ei esikatselua...',
'Preview' => 'Ei esikatselua',
'Write' => 'Kirjoita',
'Active swimlanes' => 'Aktiiviset kaistat', 'Active swimlanes' => 'Aktiiviset kaistat',
'Add a new swimlane' => 'Lisää uusi kaista', 'Add a new swimlane' => 'Lisää uusi kaista',
'Change default swimlane' => 'Vaihda oletuskaistaa', 'Change default swimlane' => 'Vaihda oletuskaistaa',
@ -543,7 +533,6 @@ 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 new link' => '', // 'Add a new link' => '',
// 'Do you really want to remove this link: "%s"?' => '', // 'Do you really want to remove this link: "%s"?' => '',
// 'Do you really want to remove this link with task #%d?' => '', // 'Do you really want to remove this link with task #%d?' => '',
@ -734,7 +723,7 @@ return array(
// 'Time spent changed: %sh' => '', // 'Time spent changed: %sh' => '',
// 'Time estimated changed: %sh' => '', // 'Time estimated changed: %sh' => '',
// 'The field "%s" have been updated' => '', // 'The field "%s" have been updated' => '',
// 'The description have been modified' => '', // 'The description has been modified:' => '',
// 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '',
// 'I want to receive notifications for:' => '', // 'I want to receive notifications for:' => '',
// 'All tasks' => '', // 'All tasks' => '',
@ -753,8 +742,6 @@ return array(
// 'My activity stream' => '', // 'My activity stream' => '',
// 'My calendar' => '', // 'My calendar' => '',
// 'Search tasks' => '', // 'Search tasks' => '',
// 'Back to the calendar' => '',
// 'Filters' => '',
// 'Reset filters' => '', // 'Reset filters' => '',
// 'My tasks due tomorrow' => '', // 'My tasks due tomorrow' => '',
// 'Tasks due today' => '', // 'Tasks due today' => '',
@ -854,7 +841,6 @@ return array(
// 'End date:' => '', // 'End date:' => '',
// 'There is no start date or end date for this project.' => '', // 'There is no start date or end date for this project.' => '',
// 'Projects Gantt chart' => '', // 'Projects Gantt chart' => '',
// 'Link type' => '',
// 'Change task color when using a specific task link' => '', // 'Change task color when using a specific task link' => '',
// 'Task link creation or modification' => '', // 'Task link creation or modification' => '',
// 'Milestone' => '', // 'Milestone' => '',
@ -906,7 +892,6 @@ return array(
// 'Shared' => '', // 'Shared' => '',
// 'Owner' => '', // 'Owner' => '',
// 'Unread notifications' => '', // 'Unread notifications' => '',
// 'My filters' => '',
// 'Notification methods:' => '', // 'Notification methods:' => '',
// 'Import tasks from CSV file' => '', // 'Import tasks from CSV file' => '',
// 'Unable to read your file' => '', // 'Unable to read your file' => '',
@ -944,6 +929,7 @@ return array(
// 'Usernames must be lowercase and unique' => '', // 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '', // 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '', // '%s attached a new file to the task %s' => '',
// 'Link type' => '',
// 'Assign automatically a category based on a link' => '', // 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertible Mark' => '', // 'BAM - Konvertible Mark' => '',
// 'Assignee Username' => '', // 'Assignee Username' => '',
@ -1053,7 +1039,6 @@ return array(
// 'Close a task when there is no activity' => '', // 'Close a task when there is no activity' => '',
// 'Duration in days' => '', // 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '', // 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '', // 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '', // 'Daily background job for tasks' => '',
// 'Auto' => '', // 'Auto' => '',
@ -1071,9 +1056,7 @@ return array(
// 'External link' => '', // 'External link' => '',
// 'Copy and paste your link here...' => '', // 'Copy and paste your link here...' => '',
// 'URL' => '', // 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '', // 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
// 'Assign to me' => '', // 'Assign to me' => '',
// 'Me' => '', // 'Me' => '',
// 'Do not duplicate anything' => '', // 'Do not duplicate anything' => '',
@ -1081,7 +1064,6 @@ return array(
// 'Users management' => '', // 'Users management' => '',
// 'Groups management' => '', // 'Groups management' => '',
// 'Create from another project' => '', // 'Create from another project' => '',
// 'There is no subtask at the moment.' => '',
// 'open' => '', // 'open' => '',
// 'closed' => '', // 'closed' => '',
// 'Priority:' => '', // 'Priority:' => '',
@ -1100,7 +1082,6 @@ return array(
// 'Started:' => '', // 'Started:' => '',
// 'Moved:' => '', // 'Moved:' => '',
// 'Task #%d' => '', // 'Task #%d' => '',
// 'Sub-tasks' => '',
// 'Date and time format' => '', // 'Date and time format' => '',
// 'Time format' => '', // 'Time format' => '',
// 'Start date: ' => '', // 'Start date: ' => '',
@ -1141,11 +1122,35 @@ return array(
// 'User filters' => '', // 'User filters' => '',
// 'Category filters' => '', // 'Category filters' => '',
// 'Upload a file' => '', // 'Upload a file' => '',
// 'There is no attachment at the moment.' => '',
// 'View file' => '', // 'View file' => '',
// 'Last activity' => '', // 'Last activity' => '',
// 'Change subtask position' => '', // 'Change subtask position' => '',
// 'This value must be greater than %d' => '', // 'This value must be greater than %d' => '',
// 'Another swimlane with the same name exists in the project' => '', // 'Another swimlane with the same name exists in the project' => '',
// 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '', // 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '',
// 'Actions duplicated successfully.' => '',
// 'Unable to duplicate actions.' => '',
// 'Add a new action' => '',
// 'Import from another project' => '',
// 'There is no action at the moment.' => '',
// 'Import actions from another project' => '',
// 'There is no available project.' => '',
// 'Local File' => '',
// 'Configuration' => '',
// 'PHP version:' => '',
// 'PHP SAPI:' => '',
// 'OS version:' => '',
// 'Database version:' => '',
// 'Browser:' => '',
// 'Task view' => '',
// 'Edit task' => '',
// 'Edit description' => '',
// 'New internal link' => '',
// 'Display list of keyboard shortcuts' => '',
// 'Menu' => '',
// 'Set start date' => '',
// 'Avatar' => '',
// 'Upload my avatar image' => '',
// 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '',
); );

View file

@ -8,7 +8,6 @@ return array(
'Edit' => 'Modifier', 'Edit' => 'Modifier',
'remove' => 'supprimer', 'remove' => 'supprimer',
'Remove' => 'Supprimer', 'Remove' => 'Supprimer',
'Update' => 'Mettre à jour',
'Yes' => 'Oui', 'Yes' => 'Oui',
'No' => 'Non', 'No' => 'Non',
'cancel' => 'annuler', 'cancel' => 'annuler',
@ -60,7 +59,6 @@ return array(
'Actions' => 'Actions', 'Actions' => 'Actions',
'Inactive' => 'Inactif', 'Inactive' => 'Inactif',
'Active' => 'Actif', 'Active' => 'Actif',
'Add this column' => 'Ajouter cette colonne',
'%d tasks on the board' => '%d tâches sur le tableau', '%d tasks on the board' => '%d tâches sur le tableau',
'%d tasks in total' => '%d tâches au total', '%d tasks in total' => '%d tâches au total',
'Unable to update this board.' => 'Impossible de mettre à jour ce tableau.', 'Unable to update this board.' => 'Impossible de mettre à jour ce tableau.',
@ -74,7 +72,6 @@ return array(
'All projects' => 'Tous les projets', 'All projects' => 'Tous les projets',
'Add a new column' => 'Ajouter une nouvelle colonne', 'Add a new column' => 'Ajouter une nouvelle colonne',
'Title' => 'Titre', 'Title' => 'Titre',
'Nobody assigned' => 'Personne assignée',
'Assigned to %s' => 'Assigné à %s', 'Assigned to %s' => 'Assigné à %s',
'Remove a column' => 'Supprimer une colonne', 'Remove a column' => 'Supprimer une colonne',
'Remove a column from a board' => 'Supprimer une colonne d\'un tableau', 'Remove a column from a board' => 'Supprimer une colonne d\'un tableau',
@ -95,7 +92,7 @@ return array(
'Edit a task' => 'Modifier une tâche', 'Edit a task' => 'Modifier une tâche',
'Column' => 'Colonne', 'Column' => 'Colonne',
'Color' => 'Couleur', 'Color' => 'Couleur',
'Assignee' => 'Personne assigné', 'Assignee' => 'Personne assignée',
'Create another task' => 'Créer une autre tâche', 'Create another task' => 'Créer une autre tâche',
'New task' => 'Nouvelle tâche', 'New task' => 'Nouvelle tâche',
'Open a task' => 'Ouvrir une tâche', 'Open a task' => 'Ouvrir une tâche',
@ -168,7 +165,6 @@ return array(
'Task count' => 'Nombre de tâches', 'Task count' => 'Nombre de tâches',
'User' => 'Utilisateur', 'User' => 'Utilisateur',
'Comments' => 'Commentaires', 'Comments' => 'Commentaires',
'Write your text in Markdown' => 'Écrivez votre texte en Markdown',
'Leave a comment' => 'Laissez un commentaire', 'Leave a comment' => 'Laissez un commentaire',
'Comment is required' => 'Le commentaire est obligatoire', 'Comment is required' => 'Le commentaire est obligatoire',
'Leave a description' => 'Laissez une description', 'Leave a description' => 'Laissez une description',
@ -184,7 +180,6 @@ return array(
'Unable to remove this action.' => 'Impossible de supprimer cette action', 'Unable to remove this action.' => 'Impossible de supprimer cette action',
'Action removed successfully.' => 'Action supprimée avec succès.', 'Action removed successfully.' => 'Action supprimée avec succès.',
'Automatic actions for the project "%s"' => 'Actions automatisées pour le projet « %s »', 'Automatic actions for the project "%s"' => 'Actions automatisées pour le projet « %s »',
'Defined actions' => 'Actions définies',
'Add an action' => 'Ajouter une action', 'Add an action' => 'Ajouter une action',
'Event name' => 'Nom de l\'événement', 'Event name' => 'Nom de l\'événement',
'Action name' => 'Nom de l\'action', 'Action name' => 'Nom de l\'action',
@ -194,7 +189,6 @@ return array(
'When the selected event occurs execute the corresponding action.' => 'Lorsque l\'événement sélectionné se déclenche, exécuter l\'action correspondante.', 'When the selected event occurs execute the corresponding action.' => 'Lorsque l\'événement sélectionné se déclenche, exécuter l\'action correspondante.',
'Next step' => 'Étape suivante', 'Next step' => 'Étape suivante',
'Define action parameters' => 'Définition des paramètres de l\'action', 'Define action parameters' => 'Définition des paramètres de l\'action',
'Save this action' => 'Sauvegarder cette action',
'Do you really want to remove this action: "%s"?' => 'Voulez-vous vraiment supprimer cette action « %s » ?', 'Do you really want to remove this action: "%s"?' => 'Voulez-vous vraiment supprimer cette action « %s » ?',
'Remove an automatic action' => 'Supprimer une action automatisée', 'Remove an automatic action' => 'Supprimer une action automatisée',
'Assign the task to a specific user' => 'Assigner la tâche à un utilisateur spécifique', 'Assign the task to a specific user' => 'Assigner la tâche à un utilisateur spécifique',
@ -333,14 +327,12 @@ return array(
'Time tracking:' => 'Gestion du temps :', 'Time tracking:' => 'Gestion du temps :',
'New sub-task' => 'Nouvelle sous-tâche', 'New sub-task' => 'Nouvelle sous-tâche',
'New attachment added "%s"' => 'Nouvelle pièce-jointe ajoutée « %s »', 'New attachment added "%s"' => 'Nouvelle pièce-jointe ajoutée « %s »',
'Comment updated' => 'Commentaire ajouté',
'New comment posted by %s' => 'Nouveau commentaire ajouté par « %s »', 'New comment posted by %s' => 'Nouveau commentaire ajouté par « %s »',
'New attachment' => 'Nouveau document', 'New attachment' => 'Nouveau document',
'New comment' => 'Nouveau commentaire', 'New comment' => 'Nouveau commentaire',
'Comment updated' => 'Commentaire mis à jour', 'Comment updated' => 'Commentaire mis à jour',
'New subtask' => 'Nouvelle sous-tâche', 'New subtask' => 'Nouvelle sous-tâche',
'Subtask updated' => 'Sous-tâche mise à jour', 'Subtask updated' => 'Sous-tâche mise à jour',
'New task' => 'Nouvelle tâche',
'Task updated' => 'Tâche mise à jour', 'Task updated' => 'Tâche mise à jour',
'Task closed' => 'Tâche fermée', 'Task closed' => 'Tâche fermée',
'Task opened' => 'Tâche ouverte', 'Task opened' => 'Tâche ouverte',
@ -438,7 +430,6 @@ return array(
'ISO format is always accepted, example: "%s" and "%s"' => 'Le format ISO est toujours accepté, exemple : « %s » et « %s »', 'ISO format is always accepted, example: "%s" and "%s"' => 'Le format ISO est toujours accepté, exemple : « %s » et « %s »',
'New private project' => 'Nouveau projet privé', 'New private project' => 'Nouveau projet privé',
'This project is private' => 'Ce projet est privé', 'This project is private' => 'Ce projet est privé',
'Type here to create a new sub-task' => 'Créer une sous-tâche en écrivant le titre ici',
'Add' => 'Ajouter', 'Add' => 'Ajouter',
'Start date' => 'Date de début', 'Start date' => 'Date de début',
'Time estimated' => 'Temps estimé', 'Time estimated' => 'Temps estimé',
@ -489,9 +480,6 @@ return array(
'Daily project summary export for "%s"' => 'Export du résumé quotidien du projet pour « %s »', 'Daily project summary export for "%s"' => 'Export du résumé quotidien du projet pour « %s »',
'Exports' => 'Exports', 'Exports' => 'Exports',
'This export contains the number of tasks per column grouped per day.' => 'Cet export contient le nombre de tâches par colonne groupé par jour.', 'This export contains the number of tasks per column grouped per day.' => 'Cet export contient le nombre de tâches par colonne groupé par jour.',
'Nothing to preview...' => 'Rien à prévisualiser...',
'Preview' => 'Prévisualiser',
'Write' => 'Écrire',
'Active swimlanes' => 'Swimlanes actives', 'Active swimlanes' => 'Swimlanes actives',
'Add a new swimlane' => 'Ajouter une nouvelle swimlane', 'Add a new swimlane' => 'Ajouter une nouvelle swimlane',
'Change default swimlane' => 'Modifier la swimlane par défaut', 'Change default swimlane' => 'Modifier la swimlane par défaut',
@ -545,7 +533,6 @@ return array(
'Task age in days' => 'Âge de la tâche en jours', 'Task age in days' => 'Âge de la tâche en jours',
'Days in this column' => 'Jours dans cette colonne', 'Days in this column' => 'Jours dans cette colonne',
'%dd' => '%dj', '%dd' => '%dj',
'Add a link' => 'Ajouter un lien',
'Add a new link' => 'Ajouter un nouveau lien', 'Add a new link' => 'Ajouter un nouveau lien',
'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien : « %s » ?', 'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien : « %s » ?',
'Do you really want to remove this link with task #%d?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche n°%d ?', 'Do you really want to remove this link with task #%d?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche n°%d ?',
@ -736,7 +723,7 @@ return array(
'Time spent changed: %sh' => 'Le temps passé a été changé : %sh', 'Time spent changed: %sh' => 'Le temps passé a été changé : %sh',
'Time estimated changed: %sh' => 'Le temps estimé a été changé : %sh', 'Time estimated changed: %sh' => 'Le temps estimé a été changé : %sh',
'The field "%s" have been updated' => 'Le champ « %s » a été mis à jour', 'The field "%s" have been updated' => 'Le champ « %s » a été mis à jour',
'The description have been modified' => 'La description a été modifiée', 'The description has been modified:' => 'La description a été modifiée',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Voulez-vous vraiment fermer la tâche « %s » ainsi que toutes ses sous-tâches ?', 'Do you really want to close the task "%s" as well as all subtasks?' => 'Voulez-vous vraiment fermer la tâche « %s » ainsi que toutes ses sous-tâches ?',
'I want to receive notifications for:' => 'Je veux reçevoir les notifications pour :', 'I want to receive notifications for:' => 'Je veux reçevoir les notifications pour :',
'All tasks' => 'Toutes les Tâches', 'All tasks' => 'Toutes les Tâches',
@ -755,8 +742,6 @@ return array(
'My activity stream' => 'Mon flux d\'activité', 'My activity stream' => 'Mon flux d\'activité',
'My calendar' => 'Mon agenda', 'My calendar' => 'Mon agenda',
'Search tasks' => 'Rechercher des tâches', 'Search tasks' => 'Rechercher des tâches',
'Back to the calendar' => 'Retour au calendrier',
'Filters' => 'Filtres',
'Reset filters' => 'Réinitialiser les filtres', 'Reset filters' => 'Réinitialiser les filtres',
'My tasks due tomorrow' => 'Mes tâches qui arrivent à échéance demain', 'My tasks due tomorrow' => 'Mes tâches qui arrivent à échéance demain',
'Tasks due today' => 'Tâches qui arrivent à échéance aujourd\'hui', 'Tasks due today' => 'Tâches qui arrivent à échéance aujourd\'hui',
@ -856,7 +841,6 @@ return array(
'End date:' => 'Date de fin :', 'End date:' => 'Date de fin :',
'There is no start date or end date for this project.' => 'Il n\'y a pas de date de début ou de date de fin pour ce projet.', 'There is no start date or end date for this project.' => 'Il n\'y a pas de date de début ou de date de fin pour ce projet.',
'Projects Gantt chart' => 'Diagramme de Gantt des projets', 'Projects Gantt chart' => 'Diagramme de Gantt des projets',
'Link type' => 'Type de lien',
'Change task color when using a specific task link' => 'Changer la couleur de la tâche lorsqu\'un lien spécifique est utilisé', 'Change task color when using a specific task link' => 'Changer la couleur de la tâche lorsqu\'un lien spécifique est utilisé',
'Task link creation or modification' => 'Création ou modification d\'un lien sur une tâche', 'Task link creation or modification' => 'Création ou modification d\'un lien sur une tâche',
'Milestone' => 'Étape importante', 'Milestone' => 'Étape importante',
@ -908,7 +892,6 @@ return array(
'Shared' => 'Partagé', 'Shared' => 'Partagé',
'Owner' => 'Propriétaire', 'Owner' => 'Propriétaire',
'Unread notifications' => 'Notifications non lus', 'Unread notifications' => 'Notifications non lus',
'My filters' => 'Mes filtres',
'Notification methods:' => 'Méthodes de notifications :', 'Notification methods:' => 'Méthodes de notifications :',
'Import tasks from CSV file' => 'Importer les tâches depuis un fichier CSV', 'Import tasks from CSV file' => 'Importer les tâches depuis un fichier CSV',
'Unable to read your file' => 'Impossible de lire votre fichier', 'Unable to read your file' => 'Impossible de lire votre fichier',
@ -1038,7 +1021,7 @@ return array(
'Close all tasks of this column' => 'Fermer toutes les tâches de cette colonne', 'Close all tasks of this column' => 'Fermer toutes les tâches de cette colonne',
'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Aucun plugin n\'a enregistré une méthode de notification de projet. Vous pouvez toujours configurer les notifications individuelles dans votre profil d\'utilisateur.', 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Aucun plugin n\'a enregistré une méthode de notification de projet. Vous pouvez toujours configurer les notifications individuelles dans votre profil d\'utilisateur.',
'My dashboard' => 'Mon tableau de bord', 'My dashboard' => 'Mon tableau de bord',
'My profile' => 'Mon profile', 'My profile' => 'Mon profil',
'Project owner: ' => 'Responsable du projet : ', 'Project owner: ' => 'Responsable du projet : ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'L\'identifiant du projet est optionnel et doit être alphanumérique, example: MONPROJET.', 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'L\'identifiant du projet est optionnel et doit être alphanumérique, example: MONPROJET.',
'Project owner' => 'Responsable du projet', 'Project owner' => 'Responsable du projet',
@ -1056,7 +1039,6 @@ return array(
'Close a task when there is no activity' => 'Fermer une tâche sans activité', 'Close a task when there is no activity' => 'Fermer une tâche sans activité',
'Duration in days' => 'Durée en jours', 'Duration in days' => 'Durée en jours',
'Send email when there is no activity on a task' => 'Envoyer un email lorsqu\'il n\'y a pas d\'activité sur une tâche', 'Send email when there is no activity on a task' => 'Envoyer un email lorsqu\'il n\'y a pas d\'activité sur une tâche',
'List of external links' => 'Liste des liens externes',
'Unable to fetch link information.' => 'Impossible de récupérer les informations sur le lien.', 'Unable to fetch link information.' => 'Impossible de récupérer les informations sur le lien.',
'Daily background job for tasks' => 'Tâche planifiée quotidienne pour les tâches', 'Daily background job for tasks' => 'Tâche planifiée quotidienne pour les tâches',
'Auto' => 'Auto', 'Auto' => 'Auto',
@ -1074,9 +1056,7 @@ return array(
'External link' => 'Lien externe', 'External link' => 'Lien externe',
'Copy and paste your link here...' => 'Copier-coller vôtre lien ici...', 'Copy and paste your link here...' => 'Copier-coller vôtre lien ici...',
'URL' => 'URL', 'URL' => 'URL',
'There is no external link for the moment.' => 'Il n\'y a pas de lien externe pour le moment.',
'Internal links' => 'Liens internes', 'Internal links' => 'Liens internes',
'There is no internal link for the moment.' => 'Il n\'y a pas de lien interne pour le moment.',
'Assign to me' => 'Assigner à moi', 'Assign to me' => 'Assigner à moi',
'Me' => 'Moi', 'Me' => 'Moi',
'Do not duplicate anything' => 'Ne rien dupliquer', 'Do not duplicate anything' => 'Ne rien dupliquer',
@ -1084,7 +1064,6 @@ return array(
'Users management' => 'Gestion des utilisateurs', 'Users management' => 'Gestion des utilisateurs',
'Groups management' => 'Gestion des groupes', 'Groups management' => 'Gestion des groupes',
'Create from another project' => 'Créer depuis un autre projet', 'Create from another project' => 'Créer depuis un autre projet',
'There is no subtask at the moment.' => 'Il n\'y a aucune sous-tâche pour le moment.',
'open' => 'ouvert', 'open' => 'ouvert',
'closed' => 'fermé', 'closed' => 'fermé',
'Priority:' => 'Priorité :', 'Priority:' => 'Priorité :',
@ -1103,7 +1082,6 @@ return array(
'Started:' => 'Commençé le :', 'Started:' => 'Commençé le :',
'Moved:' => 'Déplacé le : ', 'Moved:' => 'Déplacé le : ',
'Task #%d' => 'Tâche n°%d', 'Task #%d' => 'Tâche n°%d',
'Sub-tasks' => 'Sous-tâches',
'Date and time format' => 'Format de la date et de l\'heure', 'Date and time format' => 'Format de la date et de l\'heure',
'Time format' => 'Format de l\'heure', 'Time format' => 'Format de l\'heure',
'Start date: ' => 'Date de début : ', 'Start date: ' => 'Date de début : ',
@ -1123,7 +1101,7 @@ return array(
'Choose files again' => 'Choisir de nouveau des fichiers', 'Choose files again' => 'Choisir de nouveau des fichiers',
'Drag and drop your files here' => 'Glissez-déposez vos fichiers ici', 'Drag and drop your files here' => 'Glissez-déposez vos fichiers ici',
'choose files' => 'choisissez des fichiers', 'choose files' => 'choisissez des fichiers',
'View profile' => 'Voir le profile', 'View profile' => 'Voir le profil',
'Two Factor' => 'Deux-Facteurs', 'Two Factor' => 'Deux-Facteurs',
'Disable user' => 'Désactiver l\'utilisateur', 'Disable user' => 'Désactiver l\'utilisateur',
'Do you really want to disable this user: "%s"?' => 'Voulez-vous vraiment désactiver cet utilisateur : « %s » ?', 'Do you really want to disable this user: "%s"?' => 'Voulez-vous vraiment désactiver cet utilisateur : « %s » ?',
@ -1144,11 +1122,35 @@ return array(
'User filters' => 'Filtres des utilisateurs', 'User filters' => 'Filtres des utilisateurs',
'Category filters' => 'Filtres des catégories', 'Category filters' => 'Filtres des catégories',
'Upload a file' => 'Uploader un fichier', 'Upload a file' => 'Uploader un fichier',
'There is no attachment at the moment.' => 'Il n\'y a aucune pièce-jointe pour le moment.',
'View file' => 'Voir le fichier', 'View file' => 'Voir le fichier',
'Last activity' => 'Dernières activités', 'Last activity' => 'Dernières activités',
'Change subtask position' => 'Changer la position de la sous-tâche', 'Change subtask position' => 'Changer la position de la sous-tâche',
'This value must be greater than %d' => 'Cette valeur doit être plus grande que %d', 'This value must be greater than %d' => 'Cette valeur doit être plus grande que %d',
'Another swimlane with the same name exists in the project' => 'Une autre swimlane existe avec le même nom dans le projet', 'Another swimlane with the same name exists in the project' => 'Une autre swimlane existe avec le même nom dans le projet',
'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Exemple : http://exemple.kanboard.net/ (utilisé pour générer les URLs absolues)', 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Exemple : http://exemple.kanboard.net/ (utilisé pour générer les URLs absolues)',
'Actions duplicated successfully.' => 'Actions dupliquées avec succès.',
'Unable to duplicate actions.' => 'Impossible de dupliquer les actions.',
'Add a new action' => 'Ajouter une nouvelle action',
'Import from another project' => 'Importer depuis un autre projet',
'There is no action at the moment.' => 'Il n\'y a aucune action pour le moment.',
'Import actions from another project' => 'Importer les actions depuis un autre projet',
'There is no available project.' => 'Il n\'y a pas de projet disponible.',
'Local File' => 'Fichier local',
'Configuration' => 'Configuration',
'PHP version:' => 'Version de PHP :',
'PHP SAPI:' => 'PHP SAPI :',
'OS version:' => 'Version du système d\'exploitation :',
'Database version:' => 'Version de la base de donnée :',
'Browser:' => 'Navigateur web :',
'Task view' => 'Vue détaillée d\'une tâche',
'Edit task' => 'Modifier la tâche',
'Edit description' => 'Modifier la description',
'New internal link' => 'Nouveau lien interne',
'Display list of keyboard shortcuts' => 'Afficher la liste des raccourcis claviers',
'Menu' => 'Menu',
'Set start date' => 'Définir la date de début',
'Avatar' => 'Avatar',
'Upload my avatar image' => 'Uploader mon image d\'avatar',
'Remove my image' => 'Supprimer mon image',
'The OAuth2 state parameter is invalid' => 'Le paramètre "state" de OAuth2 est invalide',
); );

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