diff options
Diffstat (limited to 'app')
190 files changed, 5412 insertions, 3016 deletions
diff --git a/app/Action/CommentCreation.php b/app/Action/CommentCreation.php index 5dfd1c89..4029c875 100644 --- a/app/Action/CommentCreation.php +++ b/app/Action/CommentCreation.php @@ -28,6 +28,7 @@ class CommentCreation extends Base BitbucketWebhook::EVENT_ISSUE_COMMENT, BitbucketWebhook::EVENT_COMMIT, GitlabWebhook::EVENT_COMMIT, + GitlabWebhook::EVENT_ISSUE_COMMENT, ); } diff --git a/app/Api/Project.php b/app/Api/Project.php index 2451cd9c..4e4e10b8 100644 --- a/app/Api/Project.php +++ b/app/Api/Project.php @@ -12,17 +12,17 @@ class Project extends Base { public function getProjectById($project_id) { - return $this->project->getById($project_id); + return $this->formatProject($this->project->getById($project_id)); } public function getProjectByName($name) { - return $this->project->getByName($name); + return $this->formatProject($this->project->getByName($name)); } public function getAllProjects() { - return $this->project->getAll(); + return $this->formatProjects($this->project->getAll()); } public function removeProject($project_id) @@ -82,4 +82,28 @@ class Project extends Base list($valid,) = $this->project->validateModification($values); return $valid && $this->project->update($values); } + + private function formatProject($project) + { + if (! empty($project)) { + $project['url'] = array( + 'board' => $this->helper->url->to('board', 'show', array('project_id' => $project['id']), '', true), + 'calendar' => $this->helper->url->to('calendar', 'show', array('project_id' => $project['id']), '', true), + 'list' => $this->helper->url->to('listing', 'show', array('project_id' => $project['id']), '', true), + ); + } + + return $project; + } + + private function formatProjects($projects) + { + if (! empty($projects)) { + foreach ($projects as &$project) { + $project = $this->formatProject($project); + } + } + + return $projects; + } } diff --git a/app/Api/Task.php b/app/Api/Task.php index e06c012b..3b8c1ec8 100644 --- a/app/Api/Task.php +++ b/app/Api/Task.php @@ -14,17 +14,17 @@ class Task extends Base { public function getTask($task_id) { - return $this->taskFinder->getById($task_id); + return $this->formatTask($this->taskFinder->getById($task_id)); } public function getTaskByReference($project_id, $reference) { - return $this->taskFinder->getByReference($project_id, $reference); + return $this->formatTask($this->taskFinder->getByReference($project_id, $reference)); } public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN) { - return $this->taskFinder->getAll($project_id, $status_id); + return $this->formatTasks($this->taskFinder->getAll($project_id, $status_id)); } public function getOverdueTasks() @@ -115,4 +115,24 @@ class Task extends Base list($valid) = $this->taskValidator->validateApiModification($values); return $valid && $this->taskModification->update($values); } + + private function formatTask($task) + { + if (! empty($task)) { + $task['url'] = $this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), '', true); + } + + return $task; + } + + private function formatTasks($tasks) + { + if (! empty($tasks)) { + foreach ($tasks as &$task) { + $task = $this->formatTask($task); + } + } + + return $tasks; + } } diff --git a/app/Auth/GitHub.php b/app/Auth/GitHub.php deleted file mode 100644 index 816cc9c1..00000000 --- a/app/Auth/GitHub.php +++ /dev/null @@ -1,163 +0,0 @@ -<?php - -namespace Auth; - -use Event\AuthEvent; -use OAuth\Common\Storage\Session; -use OAuth\Common\Consumer\Credentials; -use OAuth\Common\Http\Uri\UriFactory; -use OAuth\ServiceFactory; -use OAuth\Common\Http\Exception\TokenResponseException; - -/** - * GitHub backend - * - * @package auth - */ -class GitHub extends Base -{ - /** - * Backend name - * - * @var string - */ - const AUTH_NAME = 'Github'; - - /** - * Authenticate a GitHub user - * - * @access public - * @param string $github_id GitHub user id - * @return boolean - */ - public function authenticate($github_id) - { - $user = $this->user->getByGitHubId($github_id); - - if (! empty($user)) { - $this->userSession->refresh($user); - $this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id'])); - return true; - } - - return false; - } - - /** - * Unlink a GitHub account for a given user - * - * @access public - * @param integer $user_id User id - * @return boolean - */ - public function unlink($user_id) - { - return $this->user->update(array( - 'id' => $user_id, - 'github_id' => '', - )); - } - - /** - * Update the user table based on the GitHub profile information - * - * @access public - * @param integer $user_id User id - * @param array $profile GitHub profile - * @return boolean - * @todo Don't overwrite existing email/name with empty GitHub data - */ - public function updateUser($user_id, array $profile) - { - return $this->user->update(array( - 'id' => $user_id, - 'github_id' => $profile['id'], - 'email' => $profile['email'], - 'name' => $profile['name'], - )); - } - - /** - * Get the GitHub service instance - * - * @access public - * @return \OAuth\OAuth2\Service\GitHub - */ - public function getService() - { - $uriFactory = new UriFactory(); - $currentUri = $uriFactory->createFromSuperGlobalArray($_SERVER); - $currentUri->setQuery('controller=user&action=gitHub'); - - $storage = new Session(false); - - $credentials = new Credentials( - GITHUB_CLIENT_ID, - GITHUB_CLIENT_SECRET, - $currentUri->getAbsoluteUri() - ); - - $serviceFactory = new ServiceFactory(); - - return $serviceFactory->createService( - 'gitHub', - $credentials, - $storage, - array('') - ); - } - - /** - * Get the authorization URL - * - * @access public - * @return \OAuth\Common\Http\Uri\Uri - */ - public function getAuthorizationUrl() - { - return $this->getService()->getAuthorizationUri(); - } - - /** - * Get GitHub profile information from the API - * - * @access public - * @param string $code GitHub authorization code - * @return bool|array - */ - public function getGitHubProfile($code) - { - try { - $gitHubService = $this->getService(); - $gitHubService->requestAccessToken($code); - - return json_decode($gitHubService->request('user'), true); - } - catch (TokenResponseException $e) { - return false; - } - } - - /** - * Revokes this user's GitHub tokens for Kanboard - * - * @access public - * @return bool|array - * @todo Currently this simply removes all our tokens for this user, ideally it should - * restrict itself to the one in question - */ - public function revokeGitHubAccess() - { - try { - $gitHubService = $this->getService(); - - $basicAuthHeader = array('Authorization' => 'Basic ' . - base64_encode(GITHUB_CLIENT_ID.':'.GITHUB_CLIENT_SECRET)); - - return json_decode($gitHubService->request('/applications/'.GITHUB_CLIENT_ID.'/tokens', 'DELETE', null, $basicAuthHeader), true); - } - catch (TokenResponseException $e) { - return false; - } - } -} diff --git a/app/Auth/Github.php b/app/Auth/Github.php new file mode 100644 index 00000000..44bcc6c8 --- /dev/null +++ b/app/Auth/Github.php @@ -0,0 +1,122 @@ +<?php + +namespace Auth; + +use Event\AuthEvent; + +/** + * Github backend + * + * @package auth + */ +class Github extends Base +{ + /** + * Backend name + * + * @var string + */ + const AUTH_NAME = 'Github'; + + /** + * OAuth2 instance + * + * @access private + * @var \Core\OAuth2 + */ + private $service; + + /** + * Authenticate a Github user + * + * @access public + * @param string $github_id Github user id + * @return boolean + */ + public function authenticate($github_id) + { + $user = $this->user->getByGithubId($github_id); + + if (! empty($user)) { + $this->userSession->refresh($user); + $this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id'])); + return true; + } + + return false; + } + + /** + * Unlink a Github account for a given user + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function unlink($user_id) + { + return $this->user->update(array( + 'id' => $user_id, + 'github_id' => '', + )); + } + + /** + * Update the user table based on the Github profile information + * + * @access public + * @param integer $user_id User id + * @param array $profile Github profile + * @return boolean + */ + public function updateUser($user_id, array $profile) + { + $user = $this->user->getById($user_id); + + return $this->user->update(array( + 'id' => $user_id, + 'github_id' => $profile['id'], + 'email' => $profile['email'] ?: $user['email'], + 'name' => $profile['name'] ?: $user['name'], + )); + } + + /** + * Get OAuth2 configured service + * + * @access public + * @return \Core\OAuth2 + */ + public function getService() + { + if (empty($this->service)) { + $this->service = $this->oauth->createService( + GITHUB_CLIENT_ID, + GITHUB_CLIENT_SECRET, + $this->helper->url->to('oauth', 'github', array(), '', true), + 'https://github.com/login/oauth/authorize', + 'https://github.com/login/oauth/access_token', + array() + ); + } + + return $this->service; + } + + /** + * Get Github profile + * + * @access public + * @param string $code + * @return array + */ + public function getProfile($code) + { + $this->getService()->getAccessToken($code); + + return $this->httpClient->getJson( + 'https://api.github.com/user', + array($this->getService()->getAuthorizationHeader()) + ); + } +} diff --git a/app/Auth/Google.php b/app/Auth/Google.php index 9a977037..972dd748 100644 --- a/app/Auth/Google.php +++ b/app/Auth/Google.php @@ -3,11 +3,6 @@ namespace Auth; use Event\AuthEvent; -use OAuth\Common\Storage\Session; -use OAuth\Common\Consumer\Credentials; -use OAuth\Common\Http\Uri\UriFactory; -use OAuth\ServiceFactory; -use OAuth\Common\Http\Exception\TokenResponseException; /** * Google backend @@ -25,6 +20,14 @@ class Google extends Base const AUTH_NAME = 'Google'; /** + * OAuth2 instance + * + * @access private + * @var \Core\OAuth2 + */ + private $service; + + /** * Authenticate a Google user * * @access public @@ -69,72 +72,52 @@ class Google extends Base */ public function updateUser($user_id, array $profile) { + $user = $this->user->getById($user_id); + return $this->user->update(array( 'id' => $user_id, 'google_id' => $profile['id'], - 'email' => $profile['email'], - 'name' => $profile['name'], + 'email' => $profile['email'] ?: $user['email'], + 'name' => $profile['name'] ?: $user['name'], )); } /** - * Get the Google service instance + * Get OAuth2 configured service * * @access public - * @return \OAuth\OAuth2\Service\Google + * @return \Core\OAuth2 */ public function getService() { - $uriFactory = new UriFactory(); - $currentUri = $uriFactory->createFromSuperGlobalArray($_SERVER); - $currentUri->setQuery('controller=user&action=google'); - - $storage = new Session(false); - - $credentials = new Credentials( - GOOGLE_CLIENT_ID, - GOOGLE_CLIENT_SECRET, - $currentUri->getAbsoluteUri() - ); - - $serviceFactory = new ServiceFactory(); - - return $serviceFactory->createService( - 'google', - $credentials, - $storage, - array('userinfo_email', 'userinfo_profile') - ); - } + if (empty($this->service)) { + $this->service = $this->oauth->createService( + GOOGLE_CLIENT_ID, + GOOGLE_CLIENT_SECRET, + $this->helper->url->to('oauth', 'google', array(), '', true), + 'https://accounts.google.com/o/oauth2/auth', + 'https://accounts.google.com/o/oauth2/token', + array('https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile') + ); + } - /** - * Get the authorization URL - * - * @access public - * @return \OAuth\Common\Http\Uri\Uri - */ - public function getAuthorizationUrl() - { - return $this->getService()->getAuthorizationUri(); + return $this->service; } /** - * Get Google profile information from the API + * Get Google profile * * @access public - * @param string $code Google authorization code - * @return bool|array + * @param string $code + * @return array */ - public function getGoogleProfile($code) + public function getProfile($code) { - try { + $this->getService()->getAccessToken($code); - $googleService = $this->getService(); - $googleService->requestAccessToken($code); - return json_decode($googleService->request('https://www.googleapis.com/oauth2/v1/userinfo'), true); - } - catch (TokenResponseException $e) { - return false; - } + return $this->httpClient->getJson( + 'https://www.googleapis.com/oauth2/v1/userinfo', + array($this->getService()->getAuthorizationHeader()) + ); } } diff --git a/app/Auth/Ldap.php b/app/Auth/Ldap.php index 3ee6ec9b..c1459b4e 100644 --- a/app/Auth/Ldap.php +++ b/app/Auth/Ldap.php @@ -46,7 +46,7 @@ class Ldap extends Base else { // We create automatically a new user - if ($this->createUser($username, $result['name'], $result['email'])) { + if (LDAP_ACCOUNT_CREATION && $this->createUser($username, $result['name'], $result['email'])) { $user = $this->user->getByUsername($username); } else { diff --git a/app/Auth/RememberMe.php b/app/Auth/RememberMe.php index e8b20f37..54e60422 100644 --- a/app/Auth/RememberMe.php +++ b/app/Auth/RememberMe.php @@ -119,31 +119,6 @@ class RememberMe extends Base } /** - * Update the database and the cookie with a new sequence - * - * @access public - */ - public function refresh() - { - $credentials = $this->readCookie(); - - if ($credentials !== false) { - - $record = $this->find($credentials['token'], $credentials['sequence']); - - if ($record) { - - // Update the sequence - $this->writeCookie( - $record['token'], - $this->update($record['token']), - $record['expiration'] - ); - } - } - } - - /** * Remove a session record * * @access public @@ -197,9 +172,10 @@ class RememberMe extends Base $this->cleanup($user_id); - $this->db - ->table(self::TABLE) - ->insert(array( + $this + ->db + ->table(self::TABLE) + ->insert(array( 'user_id' => $user_id, 'ip' => $ip, 'user_agent' => $user_agent, @@ -207,7 +183,7 @@ class RememberMe extends Base 'sequence' => $sequence, 'expiration' => $expiration, 'date_creation' => time(), - )); + )); return array( 'token' => $token, @@ -306,7 +282,7 @@ class RememberMe extends Base self::COOKIE_NAME, $this->encodeCookie($token, $sequence), $expiration, - BASE_URL_DIRECTORY, + $this->helper->url->dir(), null, Request::isHTTPS(), true @@ -339,7 +315,7 @@ class RememberMe extends Base self::COOKIE_NAME, '', time() - 3600, - BASE_URL_DIRECTORY, + $this->helper->url->dir(), null, Request::isHTTPS(), true diff --git a/app/Console/Base.php b/app/Console/Base.php index 07243080..86da1465 100644 --- a/app/Console/Base.php +++ b/app/Console/Base.php @@ -11,16 +11,17 @@ use Symfony\Component\Console\Command\Command; * @package console * @author Frederic Guillot * - * @property \Model\Notification $notification - * @property \Model\Project $project - * @property \Model\ProjectPermission $projectPermission - * @property \Model\ProjectAnalytic $projectAnalytic - * @property \Model\ProjectDailySummary $projectDailySummary - * @property \Model\SubtaskExport $subtaskExport - * @property \Model\Task $task - * @property \Model\TaskExport $taskExport - * @property \Model\TaskFinder $taskFinder - * @property \Model\Transition $transition + * @property \Model\Notification $notification + * @property \Model\Project $project + * @property \Model\ProjectPermission $projectPermission + * @property \Model\ProjectAnalytic $projectAnalytic + * @property \Model\ProjectDailyColumnStats $projectDailyColumnStats + * @property \Model\ProjectDailyStats $projectDailyStats + * @property \Model\SubtaskExport $subtaskExport + * @property \Model\Task $task + * @property \Model\TaskExport $taskExport + * @property \Model\TaskFinder $taskFinder + * @property \Model\Transition $transition */ abstract class Base extends Command { diff --git a/app/Console/ProjectDailySummaryExport.php b/app/Console/ProjectDailyColumnStatsExport.php index 07841d52..b9830662 100644 --- a/app/Console/ProjectDailySummaryExport.php +++ b/app/Console/ProjectDailyColumnStatsExport.php @@ -7,13 +7,13 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class ProjectDailySummaryExport extends Base +class ProjectDailyColumnStatsExport extends Base { protected function configure() { $this - ->setName('export:daily-project-summary') - ->setDescription('Daily project summary CSV export (number of tasks per column and per day)') + ->setName('export:daily-project-column-stats') + ->setDescription('Daily project column stats CSV export (number of tasks per column and per day)') ->addArgument('project_id', InputArgument::REQUIRED, 'Project id') ->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)') ->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)'); @@ -21,7 +21,7 @@ class ProjectDailySummaryExport extends Base protected function execute(InputInterface $input, OutputInterface $output) { - $data = $this->projectDailySummary->getAggregatedMetrics( + $data = $this->projectDailyColumnStats->getAggregatedMetrics( $input->getArgument('project_id'), $input->getArgument('start_date'), $input->getArgument('end_date') diff --git a/app/Console/ProjectDailySummaryCalculation.php b/app/Console/ProjectDailyStatsCalculation.php index b2ada1b6..4b77c556 100644 --- a/app/Console/ProjectDailySummaryCalculation.php +++ b/app/Console/ProjectDailyStatsCalculation.php @@ -6,13 +6,13 @@ use Model\Project; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class ProjectDailySummaryCalculation extends Base +class ProjectDailyStatsCalculation extends Base { protected function configure() { $this - ->setName('projects:daily-summary') - ->setDescription('Calculate daily summary data for all projects'); + ->setName('projects:daily-stats') + ->setDescription('Calculate daily statistics for all projects'); } protected function execute(InputInterface $input, OutputInterface $output) @@ -21,7 +21,8 @@ class ProjectDailySummaryCalculation extends Base foreach ($projects as $project) { $output->writeln('Run calculation for '.$project['name']); - $this->projectDailySummary->updateTotals($project['id'], date('Y-m-d')); + $this->projectDailyColumnStats->updateTotals($project['id'], date('Y-m-d')); + $this->projectDailyStats->updateTotals($project['id'], date('Y-m-d')); } } } diff --git a/app/Controller/Action.php b/app/Controller/Action.php index cd24453a..140c47d3 100644 --- a/app/Controller/Action.php +++ b/app/Controller/Action.php @@ -46,7 +46,7 @@ class Action extends Base $values = $this->request->getValues(); if (empty($values['action_name']) || empty($values['project_id'])) { - $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } $this->response->html($this->projectLayout('action/event', array( @@ -68,7 +68,7 @@ class Action extends Base $values = $this->request->getValues(); if (empty($values['action_name']) || empty($values['project_id']) || empty($values['event_name'])) { - $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } $action = $this->action->load($values['action_name'], $values['project_id'], $values['event_name']); @@ -117,7 +117,7 @@ class Action extends Base if ($valid) { - if ($this->action->create($values)) { + if ($this->action->create($values) !== false) { $this->session->flash(t('Your automatic action have been created successfully.')); } else { @@ -125,7 +125,7 @@ class Action extends Base } } - $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } /** @@ -163,6 +163,6 @@ class Action extends Base $this->session->flashError(t('Unable to remove this action.')); } - $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } } diff --git a/app/Controller/Activity.php b/app/Controller/Activity.php new file mode 100644 index 00000000..234e4be4 --- /dev/null +++ b/app/Controller/Activity.php @@ -0,0 +1,45 @@ +<?php + +namespace Controller; + +/** + * Activity stream + * + * @package controller + * @author Frederic Guillot + */ +class Activity extends Base +{ + /** + * Activity page for a project + * + * @access public + */ + public function project() + { + $project = $this->getProject(); + + $this->response->html($this->template->layout('activity/project', array( + 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'events' => $this->projectActivity->getProject($project['id']), + 'project' => $project, + 'title' => t('%s\'s activity', $project['name']) + ))); + } + + /** + * Display task activities + * + * @access public + */ + public function task() + { + $task = $this->getTask(); + + $this->response->html($this->taskLayout('activity/task', array( + 'title' => $task['title'], + 'task' => $task, + 'events' => $this->projectActivity->getTask($task['id']), + ))); + } +} diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php index 2413ba92..ca2146ed 100644 --- a/app/Controller/Analytic.php +++ b/app/Controller/Analytic.php @@ -3,7 +3,7 @@ namespace Controller; /** - * Project Anaytic controller + * Project Analytic controller * * @package controller * @author Frederic Guillot @@ -27,6 +27,56 @@ class Analytic extends Base } /** + * Show average Lead and Cycle time + * + * @access public + */ + public function leadAndCycleTime() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $this->projectDailyStats->updateTotals($project['id'], date('Y-m-d')); + + $from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week'))); + $to = $this->request->getStringParam('to', date('Y-m-d')); + + if (! empty($values)) { + $from = $values['from']; + $to = $values['to']; + } + + $this->response->html($this->layout('analytic/lead_cycle_time', array( + 'values' => array( + 'from' => $from, + 'to' => $to, + ), + 'project' => $project, + 'average' => $this->projectAnalytic->getAverageLeadAndCycleTime($project['id']), + 'metrics' => $this->projectDailyStats->getRawMetrics($project['id'], $from, $to), + 'date_format' => $this->config->get('application_date_format'), + 'date_formats' => $this->dateParser->getAvailableFormats(), + 'title' => t('Lead and Cycle time for "%s"', $project['name']), + ))); + } + + /** + * Show average time spent by column + * + * @access public + */ + public function averageTimeByColumn() + { + $project = $this->getProject(); + + $this->response->html($this->layout('analytic/avg_time_columns', array( + 'project' => $project, + 'metrics' => $this->projectAnalytic->getAverageTimeSpentByColumn($project['id']), + 'title' => t('Average time spent into each column for "%s"', $project['name']), + ))); + } + + /** * Show tasks distribution graph * * @access public @@ -88,6 +138,8 @@ class Analytic extends Base $project = $this->getProject(); $values = $this->request->getValues(); + $this->projectDailyColumnStats->updateTotals($project['id'], date('Y-m-d')); + $from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week'))); $to = $this->request->getStringParam('to', date('Y-m-d')); @@ -96,7 +148,7 @@ class Analytic extends Base $to = $values['to']; } - $display_graph = $this->projectDailySummary->countDays($project['id'], $from, $to) >= 2; + $display_graph = $this->projectDailyColumnStats->countDays($project['id'], $from, $to) >= 2; $this->response->html($this->layout($template, array( 'values' => array( @@ -104,7 +156,7 @@ class Analytic extends Base 'to' => $to, ), 'display_graph' => $display_graph, - 'metrics' => $display_graph ? $this->projectDailySummary->getAggregatedMetrics($project['id'], $from, $to, $column) : array(), + 'metrics' => $display_graph ? $this->projectDailyColumnStats->getAggregatedMetrics($project['id'], $from, $to, $column) : array(), 'project' => $project, 'date_format' => $this->config->get('application_date_format'), 'date_formats' => $this->dateParser->getAvailableFormats(), diff --git a/app/Controller/Auth.php b/app/Controller/Auth.php index 24e6e242..e8889b7f 100644 --- a/app/Controller/Auth.php +++ b/app/Controller/Auth.php @@ -25,7 +25,6 @@ class Auth extends Base 'errors' => $errors, 'values' => $values, 'no_layout' => true, - 'redirect_query' => $this->request->getStringParam('redirect_query'), 'title' => t('Login') ))); } @@ -37,14 +36,15 @@ class Auth extends Base */ public function check() { - $redirect_query = $this->request->getStringParam('redirect_query'); $values = $this->request->getValues(); list($valid, $errors) = $this->authentication->validateForm($values); if ($valid) { - if ($redirect_query !== '') { - $this->response->redirect('?'.urldecode($redirect_query)); + if (! empty($this->session['login_redirect']) && ! filter_var($this->session['login_redirect'], FILTER_VALIDATE_URL)) { + $redirect = $this->session['login_redirect']; + unset($this->session['login_redirect']); + $this->response->redirect($redirect); } $this->response->redirect($this->helper->url->to('app', 'index')); diff --git a/app/Controller/Base.php b/app/Controller/Base.php index b7ee431f..f68c4755 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -67,6 +67,7 @@ abstract class Base extends \Core\Base $this->container['logger']->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries)); $this->container['logger']->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT'])); + $this->container['logger']->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage())); $this->container['logger']->debug('END_REQUEST='.$_SERVER['REQUEST_URI']); } } @@ -101,7 +102,7 @@ abstract class Base extends \Core\Base public function beforeAction($controller, $action) { // Start the session - $this->session->open(BASE_URL_DIRECTORY); + $this->session->open($this->helper->url->dir()); $this->sendHeaders($action); $this->container['dispatcher']->dispatch('session.bootstrap', new Event); @@ -127,7 +128,8 @@ abstract class Base extends \Core\Base $this->response->text('Not Authorized', 401); } - $this->response->redirect($this->helper->url->to('auth', 'login', array('redirect_query' => urlencode($this->request->getQueryString())))); + $this->session['login_redirect'] = $this->request->getUri(); + $this->response->redirect($this->helper->url->to('auth', 'login')); } } @@ -223,17 +225,6 @@ abstract class Base extends \Core\Base } /** - * Redirection when there is no project in the database - * - * @access protected - */ - protected function redirectNoProject() - { - $this->session->flash(t('There is no active project, the first step is to create a new project.')); - $this->response->redirect('?controller=project&action=create'); - } - - /** * Common layout for task views * * @access protected @@ -301,7 +292,7 @@ abstract class Base extends \Core\Base if (empty($project)) { $this->session->flashError(t('Project not found.')); - $this->response->redirect('?controller=project'); + $this->response->redirect($this->helper->url->to('project', 'index')); } return $project; @@ -327,4 +318,33 @@ abstract class Base extends \Core\Base return $user; } + + /** + * Common method to get project filters + * + * @access protected + */ + protected function getProjectFilters($controller, $action) + { + $project = $this->getProject(); + $search = $this->request->getStringParam('search', $this->userSession->getFilters($project['id'])); + $board_selector = $this->projectPermission->getAllowedProjects($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'], + ); + } } diff --git a/app/Controller/Board.php b/app/Controller/Board.php index 0f38f910..50d9c62e 100644 --- a/app/Controller/Board.php +++ b/app/Controller/Board.php @@ -27,7 +27,7 @@ class Board extends Base } // Display the board with a specific layout - $this->response->html($this->template->layout('board/public', array( + $this->response->html($this->template->layout('board/public_view', array( 'project' => $project, 'swimlanes' => $this->board->getBoard($project['id']), 'title' => $project['name'], @@ -44,28 +44,17 @@ class Board extends Base * Show a board for a given project * * @access public - * @param integer $project_id Default project id */ - public function show($project_id = 0) + public function show() { - $project = $this->getProject($project_id); - $projects = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params = $this->getProjectFilters('board', 'show'); - $board_selector = $projects; - unset($board_selector[$project['id']]); - - $this->response->html($this->template->layout('board/index', array( - 'users' => $this->projectPermission->getMemberList($project['id'], true, true), - 'projects' => $projects, - 'project' => $project, - 'swimlanes' => $this->board->getBoard($project['id']), - 'categories_listing' => $this->category->getList($project['id'], true, true), - 'title' => $project['name'], - 'description' => $project['description'], - 'board_selector' => $board_selector, + $this->response->html($this->template->layout('board/private_view', array( + 'swimlanes' => $this->taskFilter->search($params['filters']['search'])->getBoard($params['project']['id']), + 'description' => $params['project']['description'], 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), 'board_highlight_period' => $this->config->get('board_highlight_period'), - ))); + ) + $params)); } /** @@ -99,15 +88,7 @@ class Board extends Base return $this->response->status(400); } - $this->response->html( - $this->template->render('board/show', array( - 'project' => $this->project->getById($project_id), - 'swimlanes' => $this->board->getBoard($project_id), - 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), - 'board_highlight_period' => $this->config->get('board_highlight_period'), - )), - 201 - ); + $this->response->html($this->renderBoard($project_id), 201); } /** @@ -132,14 +113,7 @@ class Board extends Base return $this->response->status(304); } - $this->response->html( - $this->template->render('board/show', array( - 'project' => $this->project->getById($project_id), - 'swimlanes' => $this->board->getBoard($project_id), - 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), - 'board_highlight_period' => $this->config->get('board_highlight_period'), - )) - ); + $this->response->html($this->renderBoard($project_id)); } /** @@ -150,7 +124,7 @@ class Board extends Base public function tasklinks() { $task = $this->getTask(); - $this->response->html($this->template->render('board/tasklinks', array( + $this->response->html($this->template->render('board/tooltip_tasklinks', array( 'links' => $this->taskLink->getAll($task['id']), 'task' => $task, ))); @@ -164,7 +138,7 @@ class Board extends Base public function subtasks() { $task = $this->getTask(); - $this->response->html($this->template->render('board/subtasks', array( + $this->response->html($this->template->render('board/tooltip_subtasks', array( 'subtasks' => $this->subtask->getAll($task['id']), 'task' => $task, ))); @@ -179,7 +153,7 @@ class Board extends Base { $task = $this->getTask(); - $this->response->html($this->template->render('board/files', array( + $this->response->html($this->template->render('board/tooltip_files', array( 'files' => $this->file->getAllDocuments($task['id']), 'images' => $this->file->getAllImages($task['id']), 'task' => $task, @@ -195,7 +169,7 @@ class Board extends Base { $task = $this->getTask(); - $this->response->html($this->template->render('board/comments', array( + $this->response->html($this->template->render('board/tooltip_comments', array( 'comments' => $this->comment->getAll($task['id']) ))); } @@ -209,7 +183,7 @@ class Board extends Base { $task = $this->getTask(); - $this->response->html($this->template->render('board/description', array( + $this->response->html($this->template->render('board/tooltip_description', array( 'task' => $task ))); } @@ -224,7 +198,7 @@ class Board extends Base $task = $this->getTask(); $project = $this->project->getById($task['project_id']); - $this->response->html($this->template->render('board/assignee', array( + $this->response->html($this->template->render('board/popover_assignee', array( 'values' => $task, 'users_list' => $this->projectPermission->getMemberList($project['id']), 'project' => $project, @@ -262,7 +236,7 @@ class Board extends Base $task = $this->getTask(); $project = $this->project->getById($task['project_id']); - $this->response->html($this->template->render('board/category', array( + $this->response->html($this->template->render('board/popover_category', array( 'values' => $task, 'categories_list' => $this->category->getList($project['id']), 'project' => $project, @@ -321,4 +295,57 @@ class Board extends Base 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), ))); } + + /** + * Enable collapsed mode + * + * @access public + */ + public function collapse() + { + $this->changeDisplayMode(true); + } + + /** + * Enable expanded mode + * + * @access public + */ + public function expand() + { + $this->changeDisplayMode(false); + } + + /** + * Change display mode + * + * @access private + */ + private function changeDisplayMode($mode) + { + $project_id = $this->request->getIntegerParam('project_id'); + $this->userSession->setBoardDisplayMode($project_id, $mode); + + if ($this->request->isAjax()) { + $this->response->html($this->renderBoard($project_id)); + } + else { + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project_id))); + } + } + + /** + * Render board + * + * @access private + */ + private function renderBoard($project_id) + { + return $this->template->render('board/table_container', array( + 'project' => $this->project->getById($project_id), + 'swimlanes' => $this->taskFilter->search($this->userSession->getFilters($project_id))->getBoard($project_id), + 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->config->get('board_highlight_period'), + )); + } } diff --git a/app/Controller/Calendar.php b/app/Controller/Calendar.php index 41642a59..8a24d705 100644 --- a/app/Controller/Calendar.php +++ b/app/Controller/Calendar.php @@ -20,20 +20,9 @@ class Calendar extends Base */ public function show() { - $project = $this->getProject(); - $this->response->html($this->template->layout('calendar/show', array( 'check_interval' => $this->config->get('board_private_refresh_interval'), - 'users_list' => $this->projectPermission->getMemberList($project['id'], true, true), - 'categories_list' => $this->category->getList($project['id'], true, true), - 'columns_list' => $this->board->getColumnsList($project['id'], true), - 'swimlanes_list' => $this->swimlane->getList($project['id'], true), - 'colors_list' => $this->color->getList(true), - 'status_list' => $this->taskStatus->getList(true), - 'project' => $project, - 'title' => t('Calendar for "%s"', $project['name']), - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), - ))); + ) + $this->getProjectFilters('calendar', 'show'))); } /** @@ -49,14 +38,8 @@ class Calendar extends Base // Common filter $filter = $this->taskFilter - ->create() - ->filterByProject($project_id) - ->filterByCategory($this->request->getIntegerParam('category_id', -1)) - ->filterByOwner($this->request->getIntegerParam('owner_id', -1)) - ->filterByColumn($this->request->getIntegerParam('column_id', -1)) - ->filterBySwimlane($this->request->getIntegerParam('swimlane_id', -1)) - ->filterByColor($this->request->getStringParam('color_id')) - ->filterByStatus($this->request->getIntegerParam('is_active', -1)); + ->search($this->userSession->getFilters($project_id)) + ->filterByProject($project_id); // Tasks if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') { diff --git a/app/Controller/Category.php b/app/Controller/Category.php index 515cc9c8..e8d83f2d 100644 --- a/app/Controller/Category.php +++ b/app/Controller/Category.php @@ -23,7 +23,7 @@ class Category extends Base if (empty($category)) { $this->session->flashError(t('Category not found.')); - $this->response->redirect('?controller=category&action=index&project_id='.$project_id); + $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project_id))); } return $category; @@ -63,7 +63,7 @@ class Category extends Base if ($this->category->create($values)) { $this->session->flash(t('Your category have been created successfully.')); - $this->response->redirect('?controller=category&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to create your category.')); @@ -107,7 +107,7 @@ class Category extends Base if ($this->category->update($values)) { $this->session->flash(t('Your category have been updated successfully.')); - $this->response->redirect('?controller=category&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to update your category.')); @@ -151,6 +151,6 @@ class Category extends Base $this->session->flashError(t('Unable to remove this category.')); } - $this->response->redirect('?controller=category&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id']))); } } diff --git a/app/Controller/Comment.php b/app/Controller/Comment.php index a5f6b1f8..7b9d4aee 100644 --- a/app/Controller/Comment.php +++ b/app/Controller/Comment.php @@ -90,10 +90,10 @@ class Comment extends Base } if ($ajax) { - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#comments'); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments')); } $this->create($values, $errors); @@ -140,7 +140,7 @@ class Comment extends Base $this->session->flashError(t('Unable to update your comment.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#comment-'.$comment['id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), 'comment-'.$comment['id']); } $this->edit($values, $errors); @@ -181,6 +181,6 @@ class Comment extends Base $this->session->flashError(t('Unable to remove this comment.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#comments'); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), 'comments'); } } diff --git a/app/Controller/Config.php b/app/Controller/Config.php index 19bc2767..206237c0 100644 --- a/app/Controller/Config.php +++ b/app/Controller/Config.php @@ -60,7 +60,7 @@ class Config extends Base $this->session->flashError(t('Unable to save your settings.')); } - $this->response->redirect('?controller=config&action='.$redirect); + $this->response->redirect($this->helper->url->to('config', $redirect)); } } @@ -104,6 +104,7 @@ class Config extends Base $this->common('project'); $this->response->html($this->layout('config/project', array( + 'colors' => $this->color->getList(), 'default_columns' => implode(', ', $this->board->getDefaultColumns()), 'title' => t('Settings').' > '.t('Project settings'), ))); @@ -199,7 +200,7 @@ class Config extends Base $this->checkCSRFParam(); $this->config->optimizeDatabase(); $this->session->flash(t('Database optimization done.')); - $this->response->redirect('?controller=config'); + $this->response->redirect($this->helper->url->to('config', 'index')); } /** @@ -215,6 +216,6 @@ class Config extends Base $this->config->regenerateToken($type.'_token'); $this->session->flash(t('Token regenerated.')); - $this->response->redirect('?controller=config&action='.$type); + $this->response->redirect($this->helper->url->to('config', $type)); } } diff --git a/app/Controller/Export.php b/app/Controller/Export.php index 117fb5ee..8b558c0a 100644 --- a/app/Controller/Export.php +++ b/app/Controller/Export.php @@ -70,7 +70,7 @@ class Export extends Base */ public function summary() { - $this->common('projectDailySummary', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export')); + $this->common('ProjectDailyColumnStats', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export')); } /** diff --git a/app/Controller/Ical.php b/app/Controller/Ical.php index 8a7ed8b5..0129915e 100644 --- a/app/Controller/Ical.php +++ b/app/Controller/Ical.php @@ -78,8 +78,8 @@ class Ical extends Base */ private function renderCalendar(TaskFilter $filter, iCalendar $calendar) { - $start = $this->request->getStringParam('start', strtotime('-1 month')); - $end = $this->request->getStringParam('end', strtotime('+2 months')); + $start = $this->request->getStringParam('start', strtotime('-2 month')); + $end = $this->request->getStringParam('end', strtotime('+6 months')); // Tasks if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') { diff --git a/app/Controller/Listing.php b/app/Controller/Listing.php new file mode 100644 index 00000000..2c197e3e --- /dev/null +++ b/app/Controller/Listing.php @@ -0,0 +1,37 @@ +<?php + +namespace Controller; + +use Model\Task as TaskModel; + +/** + * List view controller + * + * @package controller + * @author Frederic Guillot + */ +class Listing extends Base +{ + /** + * Show list view for projects + * + * @access public + */ + public function show() + { + $params = $this->getProjectFilters('listing', 'show'); + $query = $this->taskFilter->search($params['filters']['search'])->filterByProject($params['project']['id'])->getQuery(); + + $paginator = $this->paginator + ->setUrl('listing', 'show', array('project_id' => $params['project']['id'])) + ->setMax(30) + ->setOrder(TaskModel::TABLE.'.id') + ->setDirection('DESC') + ->setQuery($query) + ->calculate(); + + $this->response->html($this->template->layout('listing/show', $params + array( + 'paginator' => $paginator, + ))); + } +} diff --git a/app/Controller/Oauth.php b/app/Controller/Oauth.php new file mode 100644 index 00000000..8ba5b252 --- /dev/null +++ b/app/Controller/Oauth.php @@ -0,0 +1,123 @@ +<?php + +namespace Controller; + +/** + * OAuth controller + * + * @package controller + * @author Frederic Guillot + */ +class Oauth extends Base +{ + /** + * Link or authenticate a Google account + * + * @access public + */ + public function google() + { + $this->step1('google'); + } + + /** + * Link or authenticate a Github account + * + * @access public + */ + public function github() + { + $this->step1('github'); + } + + /** + * Unlink external account + * + * @access public + */ + public function unlink($backend = '') + { + $backend = $this->request->getStringParam('backend', $backend); + $this->checkCSRFParam(); + + if ($this->authentication->backend($backend)->unlink($this->userSession->getId())) { + $this->session->flash(t('Your external account is not linked anymore to your profile.')); + } + else { + $this->session->flashError(t('Unable to unlink your external account.')); + } + + $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 + */ + private function step1($backend) + { + $code = $this->request->getStringParam('code'); + + if (! empty($code)) { + $this->step2($backend, $code); + } + else { + $this->response->redirect($this->authentication->backend($backend)->getService()->getAuthorizationUrl()); + } + } + + /** + * Link or authenticate the user + * + * @access private + */ + private function step2($backend, $code) + { + $profile = $this->authentication->backend($backend)->getProfile($code); + + if ($this->userSession->isLogged()) { + $this->link($backend, $profile); + } + + $this->authenticate($backend, $profile); + } + + /** + * Link the account + * + * @access private + */ + private function link($backend, $profile) + { + if (empty($profile)) { + $this->session->flashError(t('External authentication failed')); + } + else { + $this->session->flash(t('Your external account is linked to your profile successfully.')); + $this->authentication->backend($backend)->updateUser($this->userSession->getId(), $profile); + } + + $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId()))); + } + + /** + * Authenticate the account + * + * @access private + */ + private function authenticate($backend, $profile) + { + if (! empty($profile) && $this->authentication->backend($backend)->authenticate($profile['id'])) { + $this->response->redirect($this->helper->url->to('app', 'index')); + } + else { + $this->response->html($this->template->layout('auth/index', array( + 'errors' => array('login' => t('External authentication failed')), + 'values' => array(), + 'no_layout' => true, + 'title' => t('Login') + ))); + } + } +} diff --git a/app/Controller/Project.php b/app/Controller/Project.php index faebac38..45bc2a46 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -73,11 +73,12 @@ class Project extends Base if ($this->project->{$switch.'PublicAccess'}($project['id'])) { $this->session->flash(t('Project updated successfully.')); - } else { + } + else { $this->session->flashError(t('Unable to update this project.')); } - $this->response->redirect('?controller=project&action=share&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'share', array('project_id' => $project['id']))); } $this->response->html($this->projectLayout('project/share', array( @@ -150,7 +151,7 @@ class Project extends Base if ($this->project->update($values)) { $this->session->flash(t('Project updated successfully.')); - $this->response->redirect('?controller=project&action=edit&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'edit', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to update this project.')); @@ -197,7 +198,7 @@ class Project extends Base } } - $this->response->redirect('?controller=project&action=users&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $project['id']))); } /** @@ -220,7 +221,7 @@ class Project extends Base } } - $this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']); + $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); } /** @@ -250,7 +251,7 @@ class Project extends Base } } - $this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']); + $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); } /** @@ -279,7 +280,7 @@ class Project extends Base } } - $this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']); + $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); } /** @@ -301,7 +302,7 @@ class Project extends Base $this->session->flashError(t('Unable to remove this project.')); } - $this->response->redirect('?controller=project'); + $this->response->redirect($this->helper->url->to('project', 'index')); } $this->response->html($this->projectLayout('project/remove', array( @@ -329,7 +330,7 @@ class Project extends Base $this->session->flashError(t('Unable to clone this project.')); } - $this->response->redirect('?controller=project'); + $this->response->redirect($this->helper->url->to('project', 'index')); } $this->response->html($this->projectLayout('project/duplicate', array( @@ -357,7 +358,7 @@ class Project extends Base $this->session->flashError(t('Unable to disable this project.')); } - $this->response->redirect('?controller=project&action=show&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id']))); } $this->response->html($this->projectLayout('project/disable', array( @@ -385,7 +386,7 @@ class Project extends Base $this->session->flashError(t('Unable to activate this project.')); } - $this->response->redirect('?controller=project&action=show&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id']))); } $this->response->html($this->projectLayout('project/enable', array( @@ -428,7 +429,7 @@ class Project extends Base if ($project_id > 0) { $this->session->flash(t('Your project have been created successfully.')); - $this->response->redirect('?controller=project&action=show&project_id='.$project_id); + $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id))); } $this->session->flashError(t('Unable to create your project.')); diff --git a/app/Controller/Projectinfo.php b/app/Controller/Projectinfo.php deleted file mode 100644 index 22b9861c..00000000 --- a/app/Controller/Projectinfo.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php - -namespace Controller; - -/** - * Project Info controller (ActivityStream + completed tasks) - * - * @package controller - * @author Frederic Guillot - */ -class Projectinfo extends Base -{ - /** - * Activity page for a project - * - * @access public - */ - public function activity() - { - $project = $this->getProject(); - - $this->response->html($this->template->layout('projectinfo/activity', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), - 'events' => $this->projectActivity->getProject($project['id']), - 'project' => $project, - 'title' => t('%s\'s activity', $project['name']) - ))); - } - - /** - * Task search for a given project - * - * @access public - */ - public function search() - { - $project = $this->getProject(); - $search = $this->request->getStringParam('search'); - $nb_tasks = 0; - - $paginator = $this->paginator - ->setUrl('projectinfo', 'search', array('search' => $search, 'project_id' => $project['id'])) - ->setMax(30) - ->setOrder('tasks.id') - ->setDirection('DESC'); - - if ($search !== '') { - $paginator->setQuery($this->taskFilter->search($search)->filterByProject($project['id'])->getQuery()) - ->calculate(); - - $nb_tasks = $paginator->getTotal(); - } - - $this->response->html($this->template->layout('projectinfo/search', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), - 'values' => array( - 'search' => $search, - 'controller' => 'projectinfo', - 'action' => 'search', - 'project_id' => $project['id'], - ), - 'paginator' => $paginator, - 'project' => $project, - 'columns' => $this->board->getColumnsList($project['id']), - 'categories' => $this->category->getList($project['id'], false), - 'title' => t('Search in the project "%s"', $project['name']).($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '') - ))); - } - - /** - * List of completed tasks for a given project - * - * @access public - */ - public function tasks() - { - $project = $this->getProject(); - $paginator = $this->paginator - ->setUrl('projectinfo', 'tasks', array('project_id' => $project['id'])) - ->setMax(30) - ->setOrder('tasks.id') - ->setDirection('DESC') - ->setQuery($this->taskFinder->getClosedTaskQuery($project['id'])) - ->calculate(); - - $this->response->html($this->template->layout('projectinfo/tasks', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), - 'project' => $project, - 'columns' => $this->board->getColumnsList($project['id']), - 'categories' => $this->category->getList($project['id'], false), - 'paginator' => $paginator, - 'title' => t('Completed tasks for "%s"', $project['name']).' ('.$paginator->getTotal().')' - ))); - } -} diff --git a/app/Controller/Search.php b/app/Controller/Search.php index 519f9ce4..f6dc7a32 100644 --- a/app/Controller/Search.php +++ b/app/Controller/Search.php @@ -13,7 +13,7 @@ class Search extends Base public function index() { $projects = $this->projectPermission->getAllowedProjects($this->userSession->getId()); - $search = $this->request->getStringParam('search'); + $search = urldecode($this->request->getStringParam('search')); $nb_tasks = 0; $paginator = $this->paginator diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php index 6ee94333..87f3fcb4 100644 --- a/app/Controller/Subtask.php +++ b/app/Controller/Subtask.php @@ -75,10 +75,10 @@ class Subtask extends Base } if (isset($values['another_subtask']) && $values['another_subtask'] == 1) { - $this->response->redirect('?controller=subtask&action=create&task_id='.$task['id'].'&another_subtask=1&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('subtask', 'create', array('project_id' => $task['project_id'], 'task_id' => $task['id'], 'another_subtask' => 1))); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); } $this->create($values, $errors); @@ -126,7 +126,7 @@ class Subtask extends Base $this->session->flashError(t('Unable to update your sub-task.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); } $this->edit($values, $errors); @@ -166,7 +166,7 @@ class Subtask extends Base $this->session->flashError(t('Unable to remove this sub-task.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); } /** @@ -256,7 +256,7 @@ class Subtask extends Base case 'dashboard': $this->response->redirect($this->helper->url->to('app', 'index')); default: - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'subtasks')); } } @@ -275,6 +275,6 @@ class Subtask extends Base $method = $direction === 'up' ? 'moveUp' : 'moveDown'; $this->subtask->$method($task_id, $subtask_id); - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id)).'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id), 'subtasks')); } } diff --git a/app/Controller/Swimlane.php b/app/Controller/Swimlane.php index c6862d47..054fa4ba 100644 --- a/app/Controller/Swimlane.php +++ b/app/Controller/Swimlane.php @@ -25,7 +25,7 @@ class Swimlane extends Base if (empty($swimlane)) { $this->session->flashError(t('Swimlane not found.')); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project_id); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project_id))); } return $swimlane; @@ -67,7 +67,7 @@ class Swimlane extends Base if ($this->swimlane->create($project['id'], $values['name'])) { $this->session->flash(t('Your swimlane have been created successfully.')); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to create your swimlane.')); @@ -93,7 +93,7 @@ class Swimlane extends Base if ($this->swimlane->updateDefault($values)) { $this->session->flash(t('The default swimlane have been updated successfully.')); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to update this swimlane.')); @@ -137,7 +137,7 @@ class Swimlane extends Base if ($this->swimlane->rename($values['id'], $values['name'])) { $this->session->flash(t('Swimlane updated successfully.')); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to update this swimlane.')); @@ -181,7 +181,7 @@ class Swimlane extends Base $this->session->flashError(t('Unable to remove this swimlane.')); } - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } /** @@ -201,7 +201,7 @@ class Swimlane extends Base $this->session->flashError(t('Unable to update this swimlane.')); } - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } /** @@ -221,7 +221,7 @@ class Swimlane extends Base $this->session->flashError(t('Unable to update this swimlane.')); } - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } /** @@ -236,7 +236,7 @@ class Swimlane extends Base $swimlane_id = $this->request->getIntegerParam('swimlane_id'); $this->swimlane->moveUp($project['id'], $swimlane_id); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } /** @@ -251,6 +251,6 @@ class Swimlane extends Base $swimlane_id = $this->request->getIntegerParam('swimlane_id'); $this->swimlane->moveDown($project['id'], $swimlane_id); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } } diff --git a/app/Controller/Task.php b/app/Controller/Task.php index dc83f7b1..0770fcd1 100644 --- a/app/Controller/Task.php +++ b/app/Controller/Task.php @@ -2,8 +2,6 @@ namespace Controller; -use Model\Project as ProjectModel; - /** * Task controller * @@ -64,7 +62,7 @@ class Task extends Base 'time_spent' => $task['time_spent'] ?: '', ); - $this->dateParser->format($values, array('date_started')); + $this->dateParser->format($values, array('date_started'), 'Y-m-d H:i'); $this->response->html($this->taskLayout('task/show', array( 'project' => $this->project->getById($task['project_id']), @@ -78,6 +76,7 @@ class Task extends Base 'link_label_list' => $this->link->getList(0, false), 'columns_list' => $this->board->getColumnsList($task['project_id']), 'colors_list' => $this->color->getList(), + 'users_list' => $this->projectPermission->getMemberList($task['project_id'], true, false, false), 'date_format' => $this->config->get('application_date_format'), 'date_formats' => $this->dateParser->getAvailableFormats(), 'title' => $task['project_name'].' > '.$task['title'], @@ -88,249 +87,58 @@ class Task extends Base } /** - * Display task activities + * Display task analytics * * @access public */ - public function activites() + public function analytics() { $task = $this->getTask(); - $this->response->html($this->taskLayout('task/activity', array( + $this->response->html($this->taskLayout('task/analytics', array( 'title' => $task['title'], 'task' => $task, - 'ajax' => $this->request->isAjax(), - 'events' => $this->projectActivity->getTask($task['id']), + 'lead_time' => $this->taskAnalytic->getLeadTime($task), + 'cycle_time' => $this->taskAnalytic->getCycleTime($task), + 'time_spent_columns' => $this->taskAnalytic->getTimeSpentByColumn($task), ))); } /** - * Display a form to create a new task - * - * @access public - */ - public function create(array $values = array(), array $errors = array()) - { - $project = $this->getProject(); - $method = $this->request->isAjax() ? 'render' : 'layout'; - $swimlanes_list = $this->swimlane->getList($project['id'], false, true); - - if (empty($values)) { - - $values = array( - 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)), - 'column_id' => $this->request->getIntegerParam('column_id'), - 'color_id' => $this->request->getStringParam('color_id'), - 'owner_id' => $this->request->getIntegerParam('owner_id'), - 'another_task' => $this->request->getIntegerParam('another_task'), - ); - } - - $this->response->html($this->template->$method('task/new', array( - 'ajax' => $this->request->isAjax(), - 'errors' => $errors, - 'values' => $values + array('project_id' => $project['id']), - 'projects_list' => $this->project->getListByStatus(ProjectModel::ACTIVE), - 'columns_list' => $this->board->getColumnsList($project['id']), - 'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true), - 'colors_list' => $this->color->getList(), - 'categories_list' => $this->category->getList($project['id']), - 'swimlanes_list' => $swimlanes_list, - 'date_format' => $this->config->get('application_date_format'), - 'date_formats' => $this->dateParser->getAvailableFormats(), - 'title' => $project['name'].' > '.t('New task') - ))); - } - - /** - * Validate and save a new task - * - * @access public - */ - public function save() - { - $project = $this->getProject(); - $values = $this->request->getValues(); - $values['creator_id'] = $this->userSession->getId(); - - list($valid, $errors) = $this->taskValidator->validateCreation($values); - - if ($valid) { - - if ($this->taskCreation->create($values)) { - $this->session->flash(t('Task created successfully.')); - - if (isset($values['another_task']) && $values['another_task'] == 1) { - unset($values['title']); - unset($values['description']); - $this->response->redirect('?controller=task&action=create&'.http_build_query($values)); - } - else { - $this->response->redirect('?controller=board&action=show&project_id='.$project['id']); - } - } - else { - $this->session->flashError(t('Unable to create your task.')); - } - } - - $this->create($values, $errors); - } - - /** - * Display a form to edit a task - * - * @access public - */ - public function edit(array $values = array(), array $errors = array()) - { - $task = $this->getTask(); - $ajax = $this->request->isAjax(); - - if (empty($values)) { - $values = $task; - } - - $this->dateParser->format($values, array('date_due')); - - $params = array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, - 'users_list' => $this->projectPermission->getMemberList($task['project_id']), - 'colors_list' => $this->color->getList(), - 'categories_list' => $this->category->getList($task['project_id']), - 'date_format' => $this->config->get('application_date_format'), - 'date_formats' => $this->dateParser->getAvailableFormats(), - 'ajax' => $ajax, - ); - - if ($ajax) { - $this->response->html($this->template->render('task/edit', $params)); - } - else { - $this->response->html($this->taskLayout('task/edit', $params)); - } - } - - /** - * Validate and update a task - * - * @access public - */ - public function update() - { - $task = $this->getTask(); - $values = $this->request->getValues(); - - list($valid, $errors) = $this->taskValidator->validateModification($values); - - if ($valid) { - - if ($this->taskModification->update($values)) { - $this->session->flash(t('Task updated successfully.')); - - if ($this->request->getIntegerParam('ajax')) { - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); - } - else { - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); - } - } - else { - $this->session->flashError(t('Unable to update your task.')); - } - } - - $this->edit($values, $errors); - } - - /** - * Update time tracking information - * - * @access public - */ - public function time() - { - $task = $this->getTask(); - $values = $this->request->getValues(); - - list($valid,) = $this->taskValidator->validateTimeModification($values); - - if ($valid && $this->taskModification->update($values)) { - $this->session->flash(t('Task updated successfully.')); - } - else { - $this->session->flashError(t('Unable to update your task.')); - } - - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); - } - - /** - * Hide a task + * Display the time tracking details * * @access public */ - public function close() + public function timetracking() { $task = $this->getTask(); - $redirect = $this->request->getStringParam('redirect'); - - if ($this->request->getStringParam('confirmation') === 'yes') { - - $this->checkCSRFParam(); - - if ($this->taskStatus->close($task['id'])) { - $this->session->flash(t('Task closed successfully.')); - } else { - $this->session->flashError(t('Unable to close this task.')); - } - if ($redirect === 'board') { - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); - } - - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); - } - - if ($this->request->isAjax()) { - $this->response->html($this->template->render('task/close', array( - 'task' => $task, - 'redirect' => $redirect, - ))); - } + $subtask_paginator = $this->paginator + ->setUrl('task', 'timesheet', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'pagination' => 'subtasks')) + ->setMax(15) + ->setOrder('start') + ->setDirection('DESC') + ->setQuery($this->subtaskTimeTracking->getTaskQuery($task['id'])) + ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); - $this->response->html($this->taskLayout('task/close', array( + $this->response->html($this->taskLayout('task/time_tracking_details', array( 'task' => $task, - 'redirect' => $redirect, + 'subtask_paginator' => $subtask_paginator, ))); } /** - * Open a task + * Display the task transitions * * @access public */ - public function open() + public function transitions() { $task = $this->getTask(); - if ($this->request->getStringParam('confirmation') === 'yes') { - - $this->checkCSRFParam(); - - if ($this->taskStatus->open($task['id'])) { - $this->session->flash(t('Task opened successfully.')); - } else { - $this->session->flashError(t('Unable to open this task.')); - } - - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); - } - - $this->response->html($this->taskLayout('task/open', array( + $this->response->html($this->taskLayout('task/transitions', array( 'task' => $task, + 'transitions' => $this->transition->getAllByTask($task['id']), ))); } @@ -357,265 +165,11 @@ class Task extends Base $this->session->flashError(t('Unable to remove this task.')); } - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); } $this->response->html($this->taskLayout('task/remove', array( 'task' => $task, ))); } - - /** - * Duplicate a task - * - * @access public - */ - public function duplicate() - { - $task = $this->getTask(); - - if ($this->request->getStringParam('confirmation') === 'yes') { - - $this->checkCSRFParam(); - $task_id = $this->taskDuplication->duplicate($task['id']); - - if ($task_id) { - $this->session->flash(t('Task created successfully.')); - $this->response->redirect('?controller=task&action=show&task_id='.$task_id.'&project_id='.$task['project_id']); - } else { - $this->session->flashError(t('Unable to create this task.')); - $this->response->redirect('?controller=task&action=duplicate&task_id='.$task['id'].'&project_id='.$task['project_id']); - } - } - - $this->response->html($this->taskLayout('task/duplicate', array( - 'task' => $task, - ))); - } - - /** - * Edit description form - * - * @access public - */ - public function description() - { - $task = $this->getTask(); - $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); - - if ($this->request->isPost()) { - - $values = $this->request->getValues(); - - list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values); - - if ($valid) { - - if ($this->taskModification->update($values)) { - $this->session->flash(t('Task updated successfully.')); - } - else { - $this->session->flashError(t('Unable to update your task.')); - } - - if ($ajax) { - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); - } - else { - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); - } - } - } - else { - $values = $task; - $errors = array(); - } - - $params = array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, - 'ajax' => $ajax, - ); - - if ($ajax) { - $this->response->html($this->template->render('task/edit_description', $params)); - } - else { - $this->response->html($this->taskLayout('task/edit_description', $params)); - } - } - - /** - * Edit recurrence form - * - * @access public - */ - public function recurrence() - { - $task = $this->getTask(); - $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); - - if ($this->request->isPost()) { - - $values = $this->request->getValues(); - - list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values); - - if ($valid) { - - if ($this->taskModification->update($values)) { - $this->session->flash(t('Task updated successfully.')); - } - else { - $this->session->flashError(t('Unable to update your task.')); - } - - if ($ajax) { - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); - } - else { - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); - } - } - } - else { - $values = $task; - $errors = array(); - } - - $params = array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, - 'ajax' => $ajax, - 'recurrence_status_list' => $this->task->getRecurrenceStatusList(), - 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), - 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), - 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), - ); - - if ($ajax) { - $this->response->html($this->template->render('task/edit_recurrence', $params)); - } - else { - $this->response->html($this->taskLayout('task/edit_recurrence', $params)); - } - } - - /** - * Move a task to another project - * - * @access public - */ - public function move() - { - $task = $this->getTask(); - $values = $task; - $errors = array(); - $projects_list = $this->projectPermission->getActiveMemberProjects($this->userSession->getId()); - - unset($projects_list[$task['project_id']]); - - if ($this->request->isPost()) { - - $values = $this->request->getValues(); - list($valid, $errors) = $this->taskValidator->validateProjectModification($values); - - if ($valid) { - - if ($this->taskDuplication->moveToProject($task['id'], $values['project_id'])) { - $this->session->flash(t('Task updated successfully.')); - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$values['project_id']); - } - else { - $this->session->flashError(t('Unable to update your task.')); - } - } - } - - $this->response->html($this->taskLayout('task/move_project', array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, - 'projects_list' => $projects_list, - ))); - } - - /** - * Duplicate a task to another project - * - * @access public - */ - public function copy() - { - $task = $this->getTask(); - $values = $task; - $errors = array(); - $projects_list = $this->projectPermission->getActiveMemberProjects($this->userSession->getId()); - - unset($projects_list[$task['project_id']]); - - if ($this->request->isPost()) { - - $values = $this->request->getValues(); - list($valid, $errors) = $this->taskValidator->validateProjectModification($values); - - if ($valid) { - $task_id = $this->taskDuplication->duplicateToProject($task['id'], $values['project_id']); - if ($task_id) { - $this->session->flash(t('Task created successfully.')); - $this->response->redirect('?controller=task&action=show&task_id='.$task_id.'&project_id='.$values['project_id']); - } - else { - $this->session->flashError(t('Unable to create your task.')); - } - } - } - - $this->response->html($this->taskLayout('task/duplicate_project', array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, - 'projects_list' => $projects_list, - ))); - } - - /** - * Display the time tracking details - * - * @access public - */ - public function timesheet() - { - $task = $this->getTask(); - - $subtask_paginator = $this->paginator - ->setUrl('task', 'timesheet', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'pagination' => 'subtasks')) - ->setMax(15) - ->setOrder('start') - ->setDirection('DESC') - ->setQuery($this->subtaskTimeTracking->getTaskQuery($task['id'])) - ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); - - $this->response->html($this->taskLayout('task/time_tracking', array( - 'task' => $task, - 'subtask_paginator' => $subtask_paginator, - ))); - } - - /** - * Display the task transitions - * - * @access public - */ - public function transitions() - { - $task = $this->getTask(); - - $this->response->html($this->taskLayout('task/transitions', array( - 'task' => $task, - 'transitions' => $this->transition->getAllByTask($task['id']), - ))); - } } diff --git a/app/Controller/Taskcreation.php b/app/Controller/Taskcreation.php new file mode 100644 index 00000000..7c841e10 --- /dev/null +++ b/app/Controller/Taskcreation.php @@ -0,0 +1,86 @@ +<?php + +namespace Controller; + +use Model\Project as ProjectModel; + +/** + * Task Creation controller + * + * @package controller + * @author Frederic Guillot + */ +class Taskcreation extends Base +{ + /** + * Display a form to create a new task + * + * @access public + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $method = $this->request->isAjax() ? 'render' : 'layout'; + $swimlanes_list = $this->swimlane->getList($project['id'], false, true); + + if (empty($values)) { + + $values = array( + 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)), + 'column_id' => $this->request->getIntegerParam('column_id'), + 'color_id' => $this->request->getStringParam('color_id', $this->color->getDefaultColor()), + 'owner_id' => $this->request->getIntegerParam('owner_id'), + 'another_task' => $this->request->getIntegerParam('another_task'), + ); + } + + $this->response->html($this->template->$method('task_creation/form', array( + 'ajax' => $this->request->isAjax(), + 'errors' => $errors, + 'values' => $values + array('project_id' => $project['id']), + 'projects_list' => $this->project->getListByStatus(ProjectModel::ACTIVE), + 'columns_list' => $this->board->getColumnsList($project['id']), + 'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true), + 'colors_list' => $this->color->getList(), + 'categories_list' => $this->category->getList($project['id']), + 'swimlanes_list' => $swimlanes_list, + 'date_format' => $this->config->get('application_date_format'), + 'date_formats' => $this->dateParser->getAvailableFormats(), + 'title' => $project['name'].' > '.t('New task') + ))); + } + + /** + * Validate and save a new task + * + * @access public + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateCreation($values); + + if ($valid) { + + if ($this->taskCreation->create($values)) { + $this->session->flash(t('Task created successfully.')); + + if (isset($values['another_task']) && $values['another_task'] == 1) { + unset($values['title']); + unset($values['description']); + $this->response->redirect($this->helper->url->to('taskcreation', 'create', $values)); + } + else { + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id']))); + } + } + else { + $this->session->flashError(t('Unable to create your task.')); + } + } + + $this->create($values, $errors); + } +} diff --git a/app/Controller/Taskduplication.php b/app/Controller/Taskduplication.php new file mode 100644 index 00000000..91291b0d --- /dev/null +++ b/app/Controller/Taskduplication.php @@ -0,0 +1,143 @@ +<?php + +namespace Controller; + +/** + * Task Duplication controller + * + * @package controller + * @author Frederic Guillot + */ +class Taskduplication extends Base +{ + /** + * Duplicate a task + * + * @access public + */ + public function duplicate() + { + $task = $this->getTask(); + + if ($this->request->getStringParam('confirmation') === 'yes') { + + $this->checkCSRFParam(); + $task_id = $this->taskDuplication->duplicate($task['id']); + + if ($task_id > 0) { + $this->session->flash(t('Task created successfully.')); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } else { + $this->session->flashError(t('Unable to create this task.')); + $this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } + } + + $this->response->html($this->taskLayout('task_duplication/duplicate', array( + 'task' => $task, + ))); + } + + /** + * Move a task to another project + * + * @access public + */ + public function move() + { + $task = $this->getTask(); + + if ($this->request->isPost()) { + + $values = $this->request->getValues(); + list($valid, $errors) = $this->taskValidator->validateProjectModification($values); + + if ($valid && $this->taskDuplication->moveToProject($task['id'], + $values['project_id'], + $values['swimlane_id'], + $values['column_id'], + $values['category_id'], + $values['owner_id'])) { + + $this->session->flash(t('Task updated successfully.')); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task['id']))); + } + + $this->session->flashError(t('Unable to update your task.')); + } + + $this->chooseDestination($task, 'task_duplication/move'); + } + + /** + * Duplicate a task to another project + * + * @access public + */ + public function copy() + { + $task = $this->getTask(); + + if ($this->request->isPost()) { + + $values = $this->request->getValues(); + list($valid, $errors) = $this->taskValidator->validateProjectModification($values); + + if ($valid && $this->taskDuplication->duplicateToProject($task['id'], + $values['project_id'], + $values['swimlane_id'], + $values['column_id'], + $values['category_id'], + $values['owner_id'])) { + + $this->session->flash(t('Task created successfully.')); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } + + $this->session->flashError(t('Unable to create your task.')); + } + + $this->chooseDestination($task, 'task_duplication/copy'); + } + + /** + * Choose destination when move/copy task to another project + * + * @access private + */ + private function chooseDestination(array $task, $template) + { + $values = array(); + $projects_list = $this->projectPermission->getActiveMemberProjects($this->userSession->getId()); + + unset($projects_list[$task['project_id']]); + + if (! empty($projects_list)) { + $dst_project_id = $this->request->getIntegerParam('dst_project_id', key($projects_list)); + + $swimlanes_list = $this->swimlane->getList($dst_project_id, false, true); + $columns_list = $this->board->getColumnsList($dst_project_id); + $categories_list = $this->category->getList($dst_project_id); + $users_list = $this->projectPermission->getMemberList($dst_project_id); + + $values = $this->taskDuplication->checkDestinationProjectValues($task); + $values['project_id'] = $dst_project_id; + } + else { + $swimlanes_list = array(); + $columns_list = array(); + $categories_list = array(); + $users_list = array(); + } + + $this->response->html($this->taskLayout($template, array( + 'values' => $values, + 'task' => $task, + 'projects_list' => $projects_list, + 'swimlanes_list' => $swimlanes_list, + 'columns_list' => $columns_list, + 'categories_list' => $categories_list, + 'users_list' => $users_list, + ))); + } +} diff --git a/app/Controller/Taskmodification.php b/app/Controller/Taskmodification.php new file mode 100644 index 00000000..56d2b9f9 --- /dev/null +++ b/app/Controller/Taskmodification.php @@ -0,0 +1,212 @@ +<?php + +namespace Controller; + +/** + * Task Modification controller + * + * @package controller + * @author Frederic Guillot + */ +class Taskmodification extends Base +{ + /** + * Set automatically the start date + * + * @access public + */ + public function start() + { + $task = $this->getTask(); + $this->taskModification->update(array('id' => $task['id'], 'date_started' => time())); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } + + /** + * Update time tracking information + * + * @access public + */ + public function time() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + + list($valid,) = $this->taskValidator->validateTimeModification($values); + + if ($valid && $this->taskModification->update($values)) { + $this->session->flash(t('Task updated successfully.')); + } + else { + $this->session->flashError(t('Unable to update your task.')); + } + + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } + + /** + * Edit description form + * + * @access public + */ + public function description() + { + $task = $this->getTask(); + $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); + + if ($this->request->isPost()) { + + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values); + + if ($valid) { + + if ($this->taskModification->update($values)) { + $this->session->flash(t('Task updated successfully.')); + } + else { + $this->session->flashError(t('Unable to update your task.')); + } + + if ($ajax) { + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); + } + else { + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } + } + } + else { + $values = $task; + $errors = array(); + } + + $params = array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'ajax' => $ajax, + ); + + if ($ajax) { + $this->response->html($this->template->render('task_modification/edit_description', $params)); + } + else { + $this->response->html($this->taskLayout('task_modification/edit_description', $params)); + } + } + + /** + * Display a form to edit a task + * + * @access public + */ + public function edit(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + $ajax = $this->request->isAjax(); + + if (empty($values)) { + $values = $task; + } + + $this->dateParser->format($values, array('date_due')); + + $params = array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'users_list' => $this->projectPermission->getMemberList($task['project_id']), + 'colors_list' => $this->color->getList(), + 'categories_list' => $this->category->getList($task['project_id']), + 'date_format' => $this->config->get('application_date_format'), + 'date_formats' => $this->dateParser->getAvailableFormats(), + 'ajax' => $ajax, + ); + + if ($ajax) { + $this->response->html($this->template->render('task_modification/edit_task', $params)); + } + else { + $this->response->html($this->taskLayout('task_modification/edit_task', $params)); + } + } + + /** + * Validate and update a task + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateModification($values); + + if ($valid) { + + if ($this->taskModification->update($values)) { + $this->session->flash(t('Task updated successfully.')); + + if ($this->request->getIntegerParam('ajax')) { + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); + } + else { + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } + } + else { + $this->session->flashError(t('Unable to update your task.')); + } + } + + $this->edit($values, $errors); + } + + /** + * Edit recurrence form + * + * @access public + */ + public function recurrence() + { + $task = $this->getTask(); + + if ($this->request->isPost()) { + + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values); + + if ($valid) { + + if ($this->taskModification->update($values)) { + $this->session->flash(t('Task updated successfully.')); + } + else { + $this->session->flashError(t('Unable to update your task.')); + } + + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } + } + else { + $values = $task; + $errors = array(); + } + + $params = array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'recurrence_status_list' => $this->task->getRecurrenceStatusList(), + 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), + 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), + 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), + ); + + $this->response->html($this->taskLayout('task_modification/edit_recurrence', $params)); + } +} diff --git a/app/Controller/Taskstatus.php b/app/Controller/Taskstatus.php new file mode 100644 index 00000000..a47d9da3 --- /dev/null +++ b/app/Controller/Taskstatus.php @@ -0,0 +1,79 @@ +<?php + +namespace Controller; + +/** + * Task Status controller + * + * @package controller + * @author Frederic Guillot + */ +class Taskstatus extends Base +{ + /** + * Close a task + * + * @access public + */ + public function close() + { + $task = $this->getTask(); + $redirect = $this->request->getStringParam('redirect'); + + if ($this->request->getStringParam('confirmation') === 'yes') { + + $this->checkCSRFParam(); + + if ($this->taskStatus->close($task['id'])) { + $this->session->flash(t('Task closed successfully.')); + } else { + $this->session->flashError(t('Unable to close this task.')); + } + + if ($redirect === 'board') { + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); + } + + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); + } + + if ($this->request->isAjax()) { + $this->response->html($this->template->render('task_status/close', array( + 'task' => $task, + 'redirect' => $redirect, + ))); + } + + $this->response->html($this->taskLayout('task_status/close', array( + 'task' => $task, + 'redirect' => $redirect, + ))); + } + + /** + * Open a task + * + * @access public + */ + public function open() + { + $task = $this->getTask(); + + if ($this->request->getStringParam('confirmation') === 'yes') { + + $this->checkCSRFParam(); + + if ($this->taskStatus->open($task['id'])) { + $this->session->flash(t('Task opened successfully.')); + } else { + $this->session->flashError(t('Unable to open this task.')); + } + + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + } + + $this->response->html($this->taskLayout('task_status/open', array( + 'task' => $task, + ))); + } +} diff --git a/app/Controller/User.php b/app/Controller/User.php index 119041e5..10a3a931 100644 --- a/app/Controller/User.php +++ b/app/Controller/User.php @@ -60,7 +60,9 @@ class User extends Base */ public function create(array $values = array(), array $errors = array()) { - $this->response->html($this->template->layout('user/new', array( + $is_remote = $this->request->getIntegerParam('remote') == 1 || (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1); + + $this->response->html($this->template->layout($is_remote ? 'user/create_remote' : 'user/create_local', array( 'timezones' => $this->config->getTimezones(true), 'languages' => $this->config->getLanguages(true), 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), @@ -178,7 +180,7 @@ class User extends Base $this->checkCSRFParam(); $user = $this->getUser(); $this->authentication->backend('rememberMe')->remove($this->request->getIntegerParam('id')); - $this->response->redirect('?controller=user&action=sessions&user_id='.$user['id']); + $this->response->redirect($this->helper->url->to('user', 'session', array('user_id' => $user['id']))); } /** @@ -194,7 +196,7 @@ class User extends Base $values = $this->request->getValues(); $this->notification->saveSettings($user['id'], $values); $this->session->flash(t('User updated successfully.')); - $this->response->redirect('?controller=user&action=notifications&user_id='.$user['id']); + $this->response->redirect($this->helper->url->to('user', 'notifications', array('user_id' => $user['id']))); } $this->response->html($this->layout('user/notifications', array( @@ -272,7 +274,7 @@ class User extends Base $this->session->flashError(t('Unable to change the password.')); } - $this->response->redirect('?controller=user&action=show&user_id='.$user['id']); + $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id']))); } } @@ -298,7 +300,7 @@ class User extends Base if ($this->request->isPost()) { - $values = $this->request->getValues() + array('disable_login_form' => 0); + $values = $this->request->getValues(); if ($this->userSession->isAdmin()) { $values += array('is_admin' => 0); @@ -321,7 +323,7 @@ class User extends Base $this->session->flashError(t('Unable to update your user.')); } - $this->response->redirect('?controller=user&action=show&user_id='.$user['id']); + $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id']))); } } @@ -335,157 +337,67 @@ class User extends Base } /** - * Remove a user + * Display a form to edit authentication * * @access public */ - public function remove() + public function authentication() { $user = $this->getUser(); + $values = $user; + $errors = array(); - if ($this->request->getStringParam('confirmation') === 'yes') { - - $this->checkCSRFParam(); - - if ($this->user->remove($user['id'])) { - $this->session->flash(t('User removed successfully.')); - } else { - $this->session->flashError(t('Unable to remove this user.')); - } - - $this->response->redirect('?controller=user'); - } - - $this->response->html($this->layout('user/remove', array( - 'user' => $user, - ))); - } - - /** - * Google authentication - * - * @access public - */ - public function google() - { - $code = $this->request->getStringParam('code'); - - if ($code) { - - $profile = $this->authentication->backend('google')->getGoogleProfile($code); + unset($values['password']); - if (is_array($profile)) { + if ($this->request->isPost()) { - // If the user is already logged, link the account otherwise authenticate - if ($this->userSession->isLogged()) { + $values = $this->request->getValues() + array('disable_login_form' => 0, 'is_ldap_user' => 0); + list($valid, $errors) = $this->user->validateModification($values); - if ($this->authentication->backend('google')->updateUser($this->userSession->getId(), $profile)) { - $this->session->flash(t('Your Google Account is linked to your profile successfully.')); - } - else { - $this->session->flashError(t('Unable to link your Google Account.')); - } + if ($valid) { - $this->response->redirect('?controller=user&action=external&user_id='.$this->userSession->getId()); - } - else if ($this->authentication->backend('google')->authenticate($profile['id'])) { - $this->response->redirect('?controller=app'); + if ($this->user->update($values)) { + $this->session->flash(t('User updated successfully.')); } else { - $this->response->html($this->template->layout('auth/index', array( - 'errors' => array('login' => t('Google authentication failed')), - 'values' => array(), - 'no_layout' => true, - 'redirect_query' => '', - 'title' => t('Login') - ))); + $this->session->flashError(t('Unable to update your user.')); } - } - } - - $this->response->redirect($this->authentication->backend('google')->getAuthorizationUrl()); - } - /** - * Unlink a Google account - * - * @access public - */ - public function unlinkGoogle() - { - $this->checkCSRFParam(); - if ($this->authentication->backend('google')->unlink($this->userSession->getId())) { - $this->session->flash(t('Your Google Account is not linked anymore to your profile.')); - } - else { - $this->session->flashError(t('Unable to unlink your Google Account.')); + $this->response->redirect($this->helper->url->to('user', 'authentication', array('user_id' => $user['id']))); + } } - $this->response->redirect('?controller=user&action=external&user_id='.$this->userSession->getId()); + $this->response->html($this->layout('user/authentication', array( + 'values' => $values, + 'errors' => $errors, + 'user' => $user, + ))); } /** - * GitHub authentication + * Remove a user * * @access public */ - public function github() + public function remove() { - $code = $this->request->getStringParam('code'); - - if ($code) { - $profile = $this->authentication->backend('gitHub')->getGitHubProfile($code); - - if (is_array($profile)) { + $user = $this->getUser(); - // If the user is already logged, link the account otherwise authenticate - if ($this->userSession->isLogged()) { + if ($this->request->getStringParam('confirmation') === 'yes') { - if ($this->authentication->backend('gitHub')->updateUser($this->userSession->getId(), $profile)) { - $this->session->flash(t('Your GitHub account was successfully linked to your profile.')); - } - else { - $this->session->flashError(t('Unable to link your GitHub Account.')); - } + $this->checkCSRFParam(); - $this->response->redirect('?controller=user&action=external&user_id='.$this->userSession->getId()); - } - else if ($this->authentication->backend('gitHub')->authenticate($profile['id'])) { - $this->response->redirect('?controller=app'); - } - else { - $this->response->html($this->template->layout('auth/index', array( - 'errors' => array('login' => t('GitHub authentication failed')), - 'values' => array(), - 'no_layout' => true, - 'redirect_query' => '', - 'title' => t('Login') - ))); - } + if ($this->user->remove($user['id'])) { + $this->session->flash(t('User removed successfully.')); + } else { + $this->session->flashError(t('Unable to remove this user.')); } - } - - $this->response->redirect($this->authentication->backend('gitHub')->getAuthorizationUrl()); - } - /** - * Unlink a GitHub account - * - * @access public - */ - public function unlinkGithub() - { - $this->checkCSRFParam(); - - $this->authentication->backend('gitHub')->revokeGitHubAccess(); - - if ($this->authentication->backend('gitHub')->unlink($this->userSession->getId())) { - $this->session->flash(t('Your GitHub account is no longer linked to your profile.')); - } - else { - $this->session->flashError(t('Unable to unlink your GitHub Account.')); + $this->response->redirect($this->helper->url->to('user', 'index')); } - $this->response->redirect('?controller=user&action=external&user_id='.$this->userSession->getId()); + $this->response->html($this->layout('user/remove', array( + 'user' => $user, + ))); } } diff --git a/app/Core/Base.php b/app/Core/Base.php index d4d7faa3..14466d5c 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -48,7 +48,8 @@ use Pimple\Container; * @property \Model\ProjectActivity $projectActivity * @property \Model\ProjectAnalytic $projectAnalytic * @property \Model\ProjectDuplication $projectDuplication - * @property \Model\ProjectDailySummary $projectDailySummary + * @property \Model\ProjectDailyColumnStats $projectDailyColumnStats + * @property \Model\ProjectDailyStats $projectDailyStats * @property \Model\ProjectIntegration $projectIntegration * @property \Model\ProjectPermission $projectPermission * @property \Model\Subtask $subtask diff --git a/app/Core/Helper.php b/app/Core/Helper.php index 53084a7e..64eaed23 100644 --- a/app/Core/Helper.php +++ b/app/Core/Helper.php @@ -2,6 +2,8 @@ namespace Core; +use Pimple\Container; + /** * Helper base class * @@ -10,7 +12,7 @@ namespace Core; * * @property \Helper\App $app * @property \Helper\Asset $asset - * @property \Helper\Datetime $datetime + * @property \Helper\Dt $dt * @property \Helper\File $file * @property \Helper\Form $form * @property \Helper\Subtask $subtask @@ -19,16 +21,34 @@ namespace Core; * @property \Helper\Url $url * @property \Helper\User $user */ -class Helper extends Base +class Helper { /** * Helper instances * - * @static * @access private * @var array */ - private static $helpers = array(); + private $helpers = array(); + + /** + * Container instance + * + * @access protected + * @var \Pimple\Container + */ + protected $container; + + /** + * Constructor + * + * @access public + * @param \Pimple\Container $container + */ + public function __construct(Container $container) + { + $this->container = $container; + } /** * Load automatically helpers @@ -39,12 +59,12 @@ class Helper extends Base */ public function __get($name) { - if (! isset(self::$helpers[$name])) { + if (! isset($this->helpers[$name])) { $class = '\Helper\\'.ucfirst($name); - self::$helpers[$name] = new $class($this->container); + $this->helpers[$name] = new $class($this->container); } - return self::$helpers[$name]; + return $this->helpers[$name]; } /** diff --git a/app/Core/HttpClient.php b/app/Core/HttpClient.php index 805c1e5a..b808f756 100644 --- a/app/Core/HttpClient.php +++ b/app/Core/HttpClient.php @@ -32,6 +32,20 @@ class HttpClient extends Base const HTTP_USER_AGENT = 'Kanboard'; /** + * Send a GET HTTP request and parse JSON response + * + * @access public + * @param string $url + * @param string[] $headers + * @return array + */ + public function getJson($url, array $headers = array()) + { + $response = $this->doRequest('GET', $url, '', array_merge(array('Accept: application/json'), $headers)); + return json_decode($response, true) ?: array(); + } + + /** * Send a POST HTTP request encoded in JSON * * @access public @@ -43,6 +57,7 @@ class HttpClient extends Base public function postJson($url, array $data, array $headers = array()) { return $this->doRequest( + 'POST', $url, json_encode($data), array_merge(array('Content-type: application/json'), $headers) @@ -61,6 +76,7 @@ class HttpClient extends Base public function postForm($url, array $data, array $headers = array()) { return $this->doRequest( + 'POST', $url, http_build_query($data), array_merge(array('Content-type: application/x-www-form-urlencoded'), $headers) @@ -71,12 +87,13 @@ class HttpClient extends Base * Make the HTTP request * * @access private + * @param string $method * @param string $url * @param string $content * @param string[] $headers * @return string */ - private function doRequest($url, $content, array $headers) + private function doRequest($method, $url, $content, array $headers) { if (empty($url)) { return ''; @@ -86,7 +103,7 @@ class HttpClient extends Base $context = stream_context_create(array( 'http' => array( - 'method' => 'POST', + 'method' => $method, 'protocol_version' => 1.1, 'timeout' => self::HTTP_TIMEOUT, 'max_redirects' => self::HTTP_MAX_REDIRECTS, diff --git a/app/Core/Lexer.php b/app/Core/Lexer.php index d277f998..d7e6fde4 100644 --- a/app/Core/Lexer.php +++ b/app/Core/Lexer.php @@ -28,16 +28,23 @@ class Lexer "/^(assignee:)/" => 'T_ASSIGNEE', "/^(color:)/" => 'T_COLOR', "/^(due:)/" => 'T_DUE', + "/^(updated:)/" => 'T_UPDATED', + "/^(modified:)/" => 'T_UPDATED', + "/^(created:)/" => 'T_CREATED', "/^(status:)/" => 'T_STATUS', "/^(description:)/" => 'T_DESCRIPTION', "/^(category:)/" => 'T_CATEGORY', "/^(column:)/" => 'T_COLUMN', "/^(project:)/" => 'T_PROJECT', + "/^(swimlane:)/" => 'T_SWIMLANE', + "/^(ref:)/" => 'T_REFERENCE', + "/^(reference:)/" => 'T_REFERENCE', "/^(\s+)/" => 'T_WHITESPACE', '/^([<=>]{0,2}[0-9]{4}-[0-9]{2}-[0-9]{2})/' => 'T_DATE', '/^(yesterday|tomorrow|today)/' => 'T_DATE', '/^("(.*?)")/' => 'T_STRING', "/^(\w+)/" => 'T_STRING', + "/^(#\d+)/" => 'T_STRING', ); /** @@ -113,6 +120,7 @@ class Lexer case 'T_CATEGORY': case 'T_COLUMN': case 'T_PROJECT': + case 'T_SWIMLANE': $next = next($tokens); if ($next !== false && $next['token'] === 'T_STRING') { @@ -123,7 +131,10 @@ class Lexer case 'T_STATUS': case 'T_DUE': + case 'T_UPDATED': + case 'T_CREATED': case 'T_DESCRIPTION': + case 'T_REFERENCE': $next = next($tokens); if ($next !== false && ($next['token'] === 'T_DATE' || $next['token'] === 'T_STRING')) { diff --git a/app/Core/OAuth2.php b/app/Core/OAuth2.php new file mode 100644 index 00000000..a7d04f33 --- /dev/null +++ b/app/Core/OAuth2.php @@ -0,0 +1,120 @@ +<?php + +namespace Core; + +/** + * OAuth2 client + * + * @package core + * @author Frederic Guillot + */ +class OAuth2 extends Base +{ + private $clientId; + private $secret; + private $callbackUrl; + private $authUrl; + private $tokenUrl; + private $scopes; + private $tokenType; + private $accessToken; + + /** + * Create OAuth2 service + * + * @access public + * @param string $clientId + * @param string $secret + * @param string $callbackUrl + * @param string $authUrl + * @param string $tokenUrl + * @param array $scopes + * @return OAuth2 + */ + public function createService($clientId, $secret, $callbackUrl, $authUrl, $tokenUrl, array $scopes) + { + $this->clientId = $clientId; + $this->secret = $secret; + $this->callbackUrl = $callbackUrl; + $this->authUrl = $authUrl; + $this->tokenUrl = $tokenUrl; + $this->scopes = $scopes; + + return $this; + } + + /** + * Get authorization url + * + * @access public + * @return string + */ + public function getAuthorizationUrl() + { + $params = array( + 'response_type' => 'code', + 'client_id' => $this->clientId, + 'redirect_uri' => $this->callbackUrl, + 'scope' => implode(' ', $this->scopes), + ); + + return $this->authUrl.'?'.http_build_query($params); + } + + /** + * Get authorization header + * + * @access public + * @return string + */ + public function getAuthorizationHeader() + { + if (strtolower($this->tokenType) === 'bearer') { + return 'Authorization: Bearer '.$this->accessToken; + } + + return ''; + } + + /** + * Get access token + * + * @access public + * @param string $code + * @return string + */ + public function getAccessToken($code) + { + if (empty($this->accessToken) && ! empty($code)) { + + $params = array( + 'code' => $code, + 'client_id' => $this->clientId, + 'client_secret' => $this->secret, + 'redirect_uri' => $this->callbackUrl, + 'grant_type' => 'authorization_code', + ); + + $response = json_decode($this->httpClient->postForm($this->tokenUrl, $params, array('Accept: application/json')), true); + + $this->tokenType = isset($response['token_type']) ? $response['token_type'] : ''; + $this->accessToken = isset($response['access_token']) ? $response['access_token'] : ''; + } + + return $this->accessToken; + } + + /** + * Set access token + * + * @access public + * @param string $token + * @param string $type + * @return string + */ + public function setAccessToken($token, $type = 'bearer') + { + $this->accessToken = $token; + $this->tokenType = $type; + } +} diff --git a/app/Core/Request.php b/app/Core/Request.php index b399a1f0..1eff66fa 100644 --- a/app/Core/Request.php +++ b/app/Core/Request.php @@ -163,6 +163,17 @@ class Request } /** + * Returns uri + * + * @access public + * @return string + */ + public function getUri() + { + return isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; + } + + /** * Get the user agent * * @static diff --git a/app/Core/Router.php b/app/Core/Router.php index 36c11a0a..ae989de5 100644 --- a/app/Core/Router.php +++ b/app/Core/Router.php @@ -2,53 +2,151 @@ namespace Core; -use Pimple\Container; - /** * Router class * * @package core * @author Frederic Guillot */ -class Router +class Router extends Base { /** - * Controller name + * Store routes for path lookup * * @access private - * @var string + * @var array */ - private $controller = ''; + private $paths = array(); /** - * Action name + * Store routes for url lookup * * @access private - * @var string + * @var array */ - private $action = ''; + private $urls = array(); /** - * Container instance + * Get the path to compare patterns * - * @access private - * @var \Pimple\Container + * @access public + * @param string $uri + * @param string $query_string + * @return string */ - private $container; + public function getPath($uri, $query_string = '') + { + $path = substr($uri, strlen($this->helper->url->dir())); + + if (! empty($query_string)) { + $path = substr($path, 0, - strlen($query_string) - 1); + } + + if ($path{0} === '/') { + $path = substr($path, 1); + } + + return $path; + } + + /** + * Add route + * + * @access public + * @param string $path + * @param string $controller + * @param string $action + * @param array $params + */ + public function addRoute($path, $controller, $action, array $params = array()) + { + $pattern = explode('/', $path); + + $this->paths[] = array( + 'pattern' => $pattern, + 'count' => count($pattern), + 'controller' => $controller, + 'action' => $action, + ); + + $this->urls[$controller][$action][] = array( + 'path' => $path, + 'params' => array_flip($params), + 'count' => count($params), + ); + } + + /** + * Find a route according to the given path + * + * @access public + * @param string $path + * @return array + */ + public function findRoute($path) + { + $parts = explode('/', $path); + $count = count($parts); + + foreach ($this->paths as $route) { + + if ($count === $route['count']) { + + $params = array(); + + for ($i = 0; $i < $count; $i++) { + + if ($route['pattern'][$i]{0} === ':') { + $params[substr($route['pattern'][$i], 1)] = $parts[$i]; + } + else if ($route['pattern'][$i] !== $parts[$i]) { + break; + } + } + + if ($i === $count) { + $_GET = array_merge($_GET, $params); + return array($route['controller'], $route['action']); + } + } + } + + return array('app', 'index'); + } /** - * Constructor + * Find route url * * @access public - * @param \Pimple\Container $container Container instance - * @param string $controller Controller name - * @param string $action Action name + * @param string $controller + * @param string $action + * @param array $params + * @return string */ - public function __construct(Container $container, $controller = '', $action = '') + public function findUrl($controller, $action, array $params = array()) { - $this->container = $container; - $this->controller = empty($_GET['controller']) ? $controller : $_GET['controller']; - $this->action = empty($_GET['action']) ? $action : $_GET['action']; + if (! isset($this->urls[$controller][$action])) { + return ''; + } + + foreach ($this->urls[$controller][$action] as $pattern) { + + if (array_diff_key($params, $pattern['params']) === array()) { + $url = $pattern['path']; + $i = 0; + + foreach ($params as $variable => $value) { + $url = str_replace(':'.$variable, $value, $url); + $i++; + } + + if ($i === $pattern['count']) { + return $url; + } + } + } + + return ''; } /** @@ -65,15 +163,42 @@ class Router } /** - * Load a controller and execute the action + * Find controller/action from the route table or from get arguments * * @access public - * @param string $filename Controller filename - * @param string $class Class name - * @param string $method Method name + * @param string $uri + * @param string $query_string + * @return boolean + */ + public function dispatch($uri, $query_string = '') + { + if (! empty($_GET['controller']) && ! empty($_GET['action'])) { + $controller = $this->sanitize($_GET['controller'], 'app'); + $action = $this->sanitize($_GET['action'], 'index'); + } + else { + list($controller, $action) = $this->findRoute($this->getPath($uri, $query_string)); + } + + return $this->load( + __DIR__.'/../Controller/'.ucfirst($controller).'.php', + $controller, + '\Controller\\'.ucfirst($controller), + $action + ); + } + + /** + * Load a controller and execute the action + * + * @access private + * @param string $filename + * @param string $controller + * @param string $class + * @param string $method * @return bool */ - public function load($filename, $class, $method) + private function load($filename, $controller, $class, $method) { if (file_exists($filename)) { @@ -84,7 +209,7 @@ class Router } $instance = new $class($this->container); - $instance->beforeAction($this->controller, $this->action); + $instance->beforeAction($controller, $method); $instance->$method(); return true; @@ -92,20 +217,4 @@ class Router return false; } - - /** - * Find a route - * - * @access public - */ - public function execute() - { - $this->controller = $this->sanitize($this->controller, 'app'); - $this->action = $this->sanitize($this->action, 'index'); - $filename = __DIR__.'/../Controller/'.ucfirst($this->controller).'.php'; - - if (! $this->load($filename, '\Controller\\'.$this->controller, $this->action)) { - die('Page not found!'); - } - } } diff --git a/app/Core/Session.php b/app/Core/Session.php index c35014cd..0e5f7426 100644 --- a/app/Core/Session.php +++ b/app/Core/Session.php @@ -41,8 +41,6 @@ class Session implements ArrayAccess */ public function open($base_path = '/') { - $base_path = str_replace('\\', '/', $base_path); - // HttpOnly and secure flags for session cookie session_set_cookie_params( self::SESSION_LIFETIME, diff --git a/app/Helper/Asset.php b/app/Helper/Asset.php index 1b1e47c5..fd555e07 100644 --- a/app/Helper/Asset.php +++ b/app/Helper/Asset.php @@ -18,7 +18,7 @@ class Asset extends \Core\Base */ public function js($filename, $async = false) { - return '<script '.($async ? 'async' : '').' type="text/javascript" src="'.$filename.'?'.filemtime($filename).'"></script>'; + return '<script '.($async ? 'async' : '').' type="text/javascript" src="'.$this->helper->url->dir().$filename.'?'.filemtime($filename).'"></script>'; } /** @@ -31,7 +31,7 @@ class Asset extends \Core\Base */ public function css($filename, $is_file = true, $media = 'screen') { - return '<link rel="stylesheet" href="'.$filename.($is_file ? '?'.filemtime($filename) : '').'" media="'.$media.'">'; + return '<link rel="stylesheet" href="'.$this->helper->url->dir().$filename.($is_file ? '?'.filemtime($filename) : '').'" media="'.$media.'">'; } /** diff --git a/app/Helper/Board.php b/app/Helper/Board.php new file mode 100644 index 00000000..452a3b70 --- /dev/null +++ b/app/Helper/Board.php @@ -0,0 +1,24 @@ +<?php + +namespace Helper; + +/** + * Board Helper + * + * @package helper + * @author Frederic Guillot + */ +class Board extends \Core\Base +{ + /** + * Return true if tasks are collapsed + * + * @access public + * @param integer $project_id + * @return boolean + */ + public function isCollapsed($project_id) + { + return $this->userSession->isBoardCollapsed($project_id); + } +} diff --git a/app/Helper/Datetime.php b/app/Helper/Dt.php index 74ea9bdd..b338fdc8 100644 --- a/app/Helper/Datetime.php +++ b/app/Helper/Dt.php @@ -2,15 +2,35 @@ namespace Helper; +use DateTime; + /** * DateTime helpers * * @package helper * @author Frederic Guillot */ -class Datetime extends \Core\Base +class Dt extends \Core\Base { /** + * Get duration in seconds into human format + * + * @access public + * @param integer $seconds + * @return string + */ + public function duration($seconds) + { + if ($seconds == 0) { + return 0; + } + + $dtF = new DateTime("@0"); + $dtT = new DateTime("@$seconds"); + return $dtF->diff($dtT)->format('%a days, %h hours, %i minutes and %s seconds'); + } + + /** * Get the age of an item in quasi human readable format. * It's in this format: <1h , NNh, NNd * diff --git a/app/Helper/Url.php b/app/Helper/Url.php index e133f195..964e0762 100644 --- a/app/Helper/Url.php +++ b/app/Helper/Url.php @@ -13,6 +13,9 @@ use Core\Security; */ class Url extends \Core\Base { + private $base = ''; + private $directory = ''; + /** * HTML Link tag * @@ -33,7 +36,7 @@ class Url extends \Core\Base } /** - * Hyperlink + * HTML Hyperlink * * @access public * @param string $controller Controller name @@ -41,22 +44,12 @@ class Url extends \Core\Base * @param array $params Url parameters * @param boolean $csrf Add a CSRF token * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link * @return string */ - public function href($controller, $action, array $params = array(), $csrf = false, $anchor = '') + public function href($controller, $action, array $params = array(), $csrf = false, $anchor = '', $absolute = false) { - $values = array( - 'controller' => $controller, - 'action' => $action, - ); - - if ($csrf) { - $params['csrf_token'] = Security::getCSRFToken(); - } - - $values += $params; - - return '?'.http_build_query($values, '', '&').(empty($anchor) ? '' : '#'.$anchor); + return $this->build('&', $controller, $action, $params, $csrf, $anchor, $absolute); } /** @@ -66,18 +59,13 @@ class Url extends \Core\Base * @param string $controller Controller name * @param string $action Action name * @param array $params Url parameters + * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link * @return string */ - public function to($controller, $action, array $params = array()) + public function to($controller, $action, array $params = array(), $anchor = '', $absolute = false) { - $values = array( - 'controller' => $controller, - 'action' => $action, - ); - - $values += $params; - - return '?'.http_build_query($values, '', '&'); + return $this->build('&', $controller, $action, $params, false, $anchor, $absolute); } /** @@ -88,7 +76,28 @@ class Url extends \Core\Base */ public function base() { - return $this->config->get('application_url') ?: $this->server(); + if (empty($this->base)) { + $this->base = $this->config->get('application_url') ?: $this->server(); + } + + return $this->base; + } + + /** + * Get application base directory + * + * @access public + * @return string + */ + public function dir() + { + if (empty($this->directory) && isset($_SERVER['REQUEST_METHOD'])) { + $this->directory = str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])); + $this->directory = $this->directory !== '/' ? $this->directory.'/' : '/'; + $this->directory = str_replace('//', '/', $this->directory); + } + + return $this->directory; } /** @@ -99,13 +108,50 @@ class Url extends \Core\Base */ public function server() { - $self = str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])); + if (empty($_SERVER['SERVER_NAME'])) { + return 'http://localhost/'; + } $url = Request::isHTTPS() ? 'https://' : 'http://'; $url .= $_SERVER['SERVER_NAME']; $url .= $_SERVER['SERVER_PORT'] == 80 || $_SERVER['SERVER_PORT'] == 443 ? '' : ':'.$_SERVER['SERVER_PORT']; - $url .= $self !== '/' ? $self.'/' : '/'; + $url .= $this->dir() ?: '/'; return $url; } + + /** + * Build relative url + * + * @access private + * @param string $separator Querystring argument separator + * @param string $controller Controller name + * @param string $action Action name + * @param array $params Url parameters + * @param boolean $csrf Add a CSRF token + * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link + * @return string + */ + private function build($separator, $controller, $action, array $params = array(), $csrf = false, $anchor = '', $absolute = false) + { + $path = $this->router->findUrl($controller, $action, $params); + $qs = array(); + + if (empty($path)) { + $qs['controller'] = $controller; + $qs['action'] = $action; + $qs += $params; + } + + if ($csrf) { + $qs['csrf_token'] = Security::getCSRFToken(); + } + + if (! empty($qs)) { + $path .= '?'.http_build_query($qs, '', $separator); + } + + return ($absolute ? $this->base() : $this->dir()).$path.(empty($anchor) ? '' : '#'.$anchor); + } } diff --git a/app/Helper/User.php b/app/Helper/User.php index 1cad6042..c1fff8c6 100644 --- a/app/Helper/User.php +++ b/app/Helper/User.php @@ -11,6 +11,24 @@ namespace Helper; class User extends \Core\Base { /** + * Get initials from a user + * + * @access public + * @param string $name + * @return string + */ + public function getInitials($name) + { + $initials = ''; + + foreach (explode(' ', $name) as $string) { + $initials .= mb_substr($string, 0, 1); + } + + return mb_strtoupper($initials); + } + + /** * Get user id * * @access public diff --git a/app/Integration/GitlabWebhook.php b/app/Integration/GitlabWebhook.php index dce7413a..b8925daf 100644 --- a/app/Integration/GitlabWebhook.php +++ b/app/Integration/GitlabWebhook.php @@ -21,14 +21,16 @@ class GitlabWebhook extends \Core\Base const EVENT_ISSUE_OPENED = 'gitlab.webhook.issue.opened'; const EVENT_ISSUE_CLOSED = 'gitlab.webhook.issue.closed'; const EVENT_COMMIT = 'gitlab.webhook.commit'; + const EVENT_ISSUE_COMMENT = 'gitlab.webhook.issue.commented'; /** * Supported webhook events * * @var string */ - const TYPE_PUSH = 'push'; - const TYPE_ISSUE = 'issue'; + const TYPE_PUSH = 'push'; + const TYPE_ISSUE = 'issue'; + const TYPE_COMMENT = 'comment'; /** * Project id @@ -63,6 +65,8 @@ class GitlabWebhook extends \Core\Base return $this->handlePushEvent($payload); case self::TYPE_ISSUE; return $this->handleIssueEvent($payload); + case self::TYPE_COMMENT; + return $this->handleCommentEvent($payload); } return false; @@ -77,15 +81,20 @@ class GitlabWebhook extends \Core\Base */ public function getType(array $payload) { - if (isset($payload['object_kind']) && $payload['object_kind'] === 'issue') { - return self::TYPE_ISSUE; + if (empty($payload['object_kind'])) { + return ''; } - if (isset($payload['commits'])) { - return self::TYPE_PUSH; + switch ($payload['object_kind']) { + case 'issue': + return self::TYPE_ISSUE; + case 'note': + return self::TYPE_COMMENT; + case 'push': + return self::TYPE_PUSH; + default: + return ''; } - - return ''; } /** @@ -213,4 +222,46 @@ class GitlabWebhook extends \Core\Base return false; } + + /** + * Parse comment issue events + * + * @access public + * @param array $payload Event data + * @return boolean + */ + public function handleCommentEvent(array $payload) + { + if (! isset($payload['issue'])) { + return false; + } + + $task = $this->taskFinder->getByReference($this->project_id, $payload['issue']['id']); + + if (! empty($task)) { + + $user = $this->user->getByUsername($payload['user']['username']); + + if (! empty($user) && ! $this->projectPermission->isMember($this->project_id, $user['id'])) { + $user = array(); + } + + $event = array( + 'project_id' => $this->project_id, + 'reference' => $payload['object_attributes']['id'], + 'comment' => $payload['object_attributes']['note']."\n\n[".t('By @%s on Gitlab', $payload['user']['username']).']('.$payload['object_attributes']['url'].')', + 'user_id' => ! empty($user) ? $user['id'] : 0, + 'task_id' => $task['id'], + ); + + $this->container['dispatcher']->dispatch( + self::EVENT_ISSUE_COMMENT, + new GenericEvent($event) + ); + + return true; + } + + return false; + } } diff --git a/app/Integration/HipchatWebhook.php b/app/Integration/HipchatWebhook.php index f1be0f34..1d08e514 100644 --- a/app/Integration/HipchatWebhook.php +++ b/app/Integration/HipchatWebhook.php @@ -72,8 +72,7 @@ class HipchatWebhook extends \Core\Base $html .= $this->projectActivity->getTitle($event); if ($this->config->get('application_url')) { - $html .= '<br/><a href="'.$this->config->get('application_url'); - $html .= $this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id)).'">'; + $html .= '<br/><a href="'.$this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true).'">'; $html .= t('view the task on Kanboard').'</a>'; } diff --git a/app/Integration/Jabber.php b/app/Integration/Jabber.php index a1191662..3e403aab 100644 --- a/app/Integration/Jabber.php +++ b/app/Integration/Jabber.php @@ -81,8 +81,7 @@ class Jabber extends \Core\Base $payload = '['.$project['name'].'] '.str_replace('"', '"', $this->projectActivity->getTitle($event)).(isset($event['task']['title']) ? ' ('.$event['task']['title'].')' : ''); if ($this->config->get('application_url')) { - $payload .= ' '.$this->config->get('application_url'); - $payload .= $this->helper->url->to('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id)); + $payload .= ' '.$this->helper->url->to('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true); } $this->sendMessage($project_id, $payload); diff --git a/app/Integration/SlackWebhook.php b/app/Integration/SlackWebhook.php index 975ea21f..d238652f 100644 --- a/app/Integration/SlackWebhook.php +++ b/app/Integration/SlackWebhook.php @@ -40,6 +40,25 @@ class SlackWebhook extends \Core\Base } /** + * Get optional Slack channel + * + * @access public + * @param integer $project_id + * @return string + */ + public function getChannel($project_id) + { + $channel = $this->config->get('integration_slack_webhook_channel'); + + if (! empty($channel)) { + return $channel; + } + + $options = $this->projectIntegration->getParameters($project_id); + return $options['slack_webhook_channel']; + } + + /** * Send message to the incoming Slack webhook * * @access public @@ -64,11 +83,15 @@ class SlackWebhook extends \Core\Base ); if ($this->config->get('application_url')) { - $payload['text'] .= ' - <'.$this->config->get('application_url'); - $payload['text'] .= $this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id)); + $payload['text'] .= ' - <'.$this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true); $payload['text'] .= '|'.t('view the task on Kanboard').'>'; } + $channel = $this->getChannel($project_id); + if (! empty($channel)) { + $payload['channel'] = $channel; + } + $this->httpClient->postJson($this->getWebhookUrl($project_id), $payload); } } diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index ece8a97b..6916b84e 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Projektets navn er krævet', 'This project must be unique' => 'Projektets navn skal være unikt', 'The title is required' => 'Titel er krævet', - 'There is no active project, the first step is to create a new project.' => 'Der er ingen aktive projekter. Første step er at oprette et nyt projekt.', 'Settings saved successfully.' => 'Indstillinger gemt.', 'Unable to save your settings.' => 'Indstillinger kunne ikke gemmes.', 'Database optimization done.' => 'Databaseoptimeringen er fuldført.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Dato for oprettelse', 'Date completed' => 'Dato for fuldført', 'Id' => 'ID', - 'Completed tasks' => 'Fuldførte opgaver', - 'Completed tasks for "%s"' => 'Fuldførte opgaver for "%s"', '%d closed tasks' => '%d lukket opgavet', 'No task for this project' => 'Ingen opgaver i dette projekt', 'Public link' => 'Offentligt link', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Udløbsdato', 'Remember Me' => 'Husk mig', 'Creation date' => 'Oprettelsesdato', - 'Filter by user' => 'Filtrer efter bruger', - 'Filter by due date' => 'Filtrer efter forfaldsdato', 'Everybody' => 'Alle', 'Open' => 'Åben', 'Closed' => 'Lukket', 'Search' => 'Søg', 'Nothing found.' => 'Intet fundet.', - 'Search in the project "%s"' => 'Søg i projektet "%s"', 'Due date' => 'Forfaldsdato', 'Others formats accepted: %s and %s' => 'Andre acceptable formater: %s und %s', 'Description' => 'Beskrivelse', '%d comments' => '%d kommentarer', '%d comment' => '%d kommentar', 'Email address invalid' => 'Ugyldig email', - 'Your Google Account is not linked anymore to your profile.' => 'Din Google-konto er ikke længere forbundet til din profil.', - 'Unable to unlink your Google Account.' => 'Det var ikke muligt at fjerne din Google-konto.', - 'Google authentication failed' => 'Google autentificering mislykkedes', - 'Unable to link your Google Account.' => 'Det var ikke muligt at forbinde til din Google-konto.', - 'Your Google Account is linked to your profile successfully.' => 'Din Google-konto er forbundet til din profil.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-Mail', 'Link my Google Account' => 'Forbind min Google-konto', 'Unlink my Google Account' => 'Fjern forbindelsen til min Google-konto', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Kategorinavn', 'Add a new category' => 'Tilfæj en ny kategori', 'Do you really want to remove this category: "%s"?' => 'Vil du virkelig fjerne denne kategori: "%s"?', - 'Filter by category' => 'Filter efter kategori', 'All categories' => 'Alle kategorier', 'No category' => 'Ingen kategori', 'The name is required' => 'Navnet er krævet', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maksimum størrelse: ', 'Unable to upload the file.' => 'Filen kunne ikke uploades.', 'Display another project' => 'Vis et andet projekt...', - 'Your GitHub account was successfully linked to your profile.' => 'Din GitHub-konto er forbundet til din profil.', - 'Unable to link your GitHub Account.' => 'Det var ikke muligt er forbinde til din GitHub-konto.', - 'GitHub authentication failed' => 'GitHub autentificering mislykkedes', - 'Your GitHub account is no longer linked to your profile.' => 'Din GitHub-konto er ikke længere forbundet til din profil.', - 'Unable to unlink your GitHub Account.' => 'Det var ikke muligt at fjerne forbindelsen til din GitHub-konto.', - 'Login with my GitHub Account' => 'Login med min GitHub-konto', - 'Link my GitHub Account' => 'Forbind min GitHub-konto', - 'Unlink my GitHub Account' => 'Fjern forbindelsen til min GitHub-konto', + 'Login with my Github Account' => 'Login med min Github-konto', + 'Link my Github Account' => 'Forbind min Github-konto', + 'Unlink my Github Account' => 'Fjern forbindelsen til min Github-konto', 'Created by %s' => 'Oprettet af %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Sidst redigeret %d.%m.%Y - %H:%M', 'Tasks Export' => 'Opgave eksport', @@ -409,7 +396,7 @@ return array( 'Enabled' => 'Aktiv', 'Disabled' => 'Deaktiveret', 'Google account linked' => 'Google-konto forbundet', - 'Github account linked' => 'GitHub-konto forbundet', + 'Github account linked' => 'Github-konto forbundet', 'Username:' => 'Brugernavn', 'Name:' => 'Navn:', 'Email:' => 'Email:', @@ -423,7 +410,7 @@ return array( 'Password modification' => 'Adgangskode ændring', 'External authentications' => 'Ekstern autentificering', 'Google Account' => 'Google-konto', - 'Github Account' => 'GitHub-konto', + 'Github Account' => 'Github-konto', 'Never connected.' => 'Aldrig forbundet.', 'No account linked.' => 'Ingen kontoer forfundet.', 'Account linked.' => 'Konto forbundet.', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s opdateret en kommentar på opgaven %s', '%s commented the task %s' => '%s har kommenteret opgaven %s', '%s\'s activity' => '%s\'s aktvitet', - 'No activity.' => 'Ingen aktivitet', 'RSS feed' => 'RSS feed', '%s updated a comment on the task #%d' => '%s opdaterede en kommentar på opgaven #%d', '%s commented on the task #%d' => '%s kommenteret op opgaven #%d', @@ -605,14 +591,9 @@ return array( // 'Language:' => '', // 'Timezone:' => '', // 'All columns' => '', - // 'Calendar for "%s"' => '', - // 'Filter by column' => '', - // 'Filter by status' => '', // 'Calendar' => '', // 'Next' => '', // '#%d' => '', - // 'Filter by color' => '', - // 'Filter by swimlane' => '', // 'All swimlanes' => '', // 'All colors' => '', // 'All status' => '', @@ -627,14 +608,7 @@ return array( // 'Time Tracking' => '', // 'You already have one subtask in progress' => '', // 'Which parts of the project do you want to duplicate?' => '', - // 'Change dashboard view' => '', - // 'Show/hide activities' => '', - // 'Show/hide projects' => '', - // 'Show/hide subtasks' => '', - // 'Show/hide tasks' => '', - // 'Disable login form' => '', - // 'Show/hide calendar' => '', - // 'User calendar' => '', + // 'Disallow login form' => '', // 'Bitbucket commit received' => '', // 'Bitbucket webhooks' => '', // 'Help on Bitbucket webhooks' => '', @@ -688,9 +662,7 @@ return array( // 'Keyboard shortcuts' => '', // 'Open board switcher' => '', // 'Application' => '', - // 'Filter recently updated' => '', // 'since %B %e, %Y at %k:%M %p' => '', - // 'More filters' => '', // 'Compact view' => '', // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index 0bd4704f..1b381157 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -91,7 +91,7 @@ return array( 'Download the database' => 'Datenbank herunterladen', 'Optimize the database' => 'Datenbank optimieren', '(VACUUM command)' => '(VACUUM Befehl)', - '(Gzip compressed Sqlite file)' => '(Gzip-komprimierte Sqlite Datei)', + '(Gzip compressed Sqlite file)' => '(Gzip-komprimierte SQLite-Datei)', 'Close a task' => 'Aufgabe abschließen', 'Edit a task' => 'Aufgabe bearbeiten', 'Column' => 'Spalte', @@ -117,7 +117,7 @@ return array( 'The password is required' => 'Das Passwort wird benötigt', 'This value must be an integer' => 'Dieser Wert muss eine ganze Zahl sein', 'The username must be unique' => 'Der Benutzername muss eindeutig sein', - 'The user id is required' => 'Die Benutzer ID ist anzugeben', + 'The user id is required' => 'Die Benutzer-ID ist anzugeben', 'Passwords don\'t match' => 'Passwörter nicht gleich', 'The confirmation is required' => 'Die Bestätigung ist erforderlich', 'The project is required' => 'Das Projekt ist anzugeben', @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Der Projektname ist anzugeben', 'This project must be unique' => 'Der Projektname muss eindeutig sein', 'The title is required' => 'Der Titel ist anzugeben', - 'There is no active project, the first step is to create a new project.' => 'Es gibt kein aktives Projekt. Zunächst muss ein Projekt erstellt werden.', 'Settings saved successfully.' => 'Einstellungen erfolgreich gespeichert.', 'Unable to save your settings.' => 'Speichern der Einstellungen nicht möglich.', 'Database optimization done.' => 'Optimieren der Datenbank abgeschlossen.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Erstellt am', 'Date completed' => 'Abgeschlossen am', 'Id' => 'ID', - 'Completed tasks' => 'Abgeschlossene Aufgaben', - 'Completed tasks for "%s"' => 'Abgeschlossene Aufgaben für "%s"', '%d closed tasks' => '%d abgeschlossene Aufgaben', 'No task for this project' => 'Keine Aufgaben in diesem Projekt', 'Public link' => 'Öffentlicher Link', @@ -248,36 +245,32 @@ return array( 'Last logins' => 'Letzte Anmeldungen', 'Login date' => 'Anmeldedatum', 'Authentication method' => 'Authentisierungsmethode', - 'IP address' => 'IP Adresse', - 'User agent' => 'User Agent', + 'IP address' => 'IP-Adresse', + 'User agent' => 'User-Agent', 'Persistent connections' => 'Bestehende Verbindungen', 'No session.' => 'Keine Sitzung.', 'Expiration date' => 'Ablaufdatum', 'Remember Me' => 'Angemeldet bleiben', 'Creation date' => 'Erstellungsdatum', - 'Filter by user' => 'Benutzer filtern', - 'Filter by due date' => 'Fälligkeit filtern', 'Everybody' => 'Alle', 'Open' => 'Offen', 'Closed' => 'Abgeschlossen', 'Search' => 'Suchen', 'Nothing found.' => 'Nichts gefunden.', - 'Search in the project "%s"' => 'Suche in Projekt "%s"', 'Due date' => 'Fälligkeitsdatum', 'Others formats accepted: %s and %s' => 'Andere akzeptierte Formate: %s und %s', 'Description' => 'Beschreibung', '%d comments' => '%d Kommentare', '%d comment' => '%d Kommentar', 'Email address invalid' => 'Ungültige E-Mail-Adresse', - 'Your Google Account is not linked anymore to your profile.' => 'Google Account nicht mehr mit dem Profil verbunden.', - 'Unable to unlink your Google Account.' => 'Trennung der Verbindung zum Google Account nicht möglich.', - 'Google authentication failed' => 'Zugriff mit Google fehlgeschlagen', - 'Unable to link your Google Account.' => 'Verbindung mit diesem Google Account nicht möglich.', - 'Your Google Account is linked to your profile successfully.' => 'Der Google Account wurde erfolgreich verbunden.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-Mail', - 'Link my Google Account' => 'Verbinde meinen Google Account', - 'Unlink my Google Account' => 'Verbindung mit meinem Google Account trennen', - 'Login with my Google Account' => 'Anmelden mit meinem Google Account', + 'Link my Google Account' => 'Verbinde meinen Google-Account', + 'Unlink my Google Account' => 'Verbindung mit meinem Google-Account trennen', + 'Login with my Google Account' => 'Anmelden mit meinem Google-Account', 'Project not found.' => 'Das Projekt wurde nicht gefunden.', 'Task removed successfully.' => 'Aufgabe erfolgreich gelöscht.', 'Unable to remove this task.' => 'Löschen der Aufgabe nicht möglich.', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Kategoriename', 'Add a new category' => 'Neue Kategorie', 'Do you really want to remove this category: "%s"?' => 'Soll diese Kategorie wirklich gelöscht werden: "%s"?', - 'Filter by category' => 'Kategorie filtern', 'All categories' => 'Alle Kategorien', 'No category' => 'Keine Kategorie', 'The name is required' => 'Der Name ist erforderlich', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maximalgröße: ', 'Unable to upload the file.' => 'Hochladen der Datei nicht möglich.', 'Display another project' => 'Zu Projekt wechseln', - 'Your GitHub account was successfully linked to your profile.' => 'GitHub Account erfolgreich mit dem Profil verbunden.', - 'Unable to link your GitHub Account.' => 'Verbindung mit diesem GitHub Account nicht möglich.', - 'GitHub authentication failed' => 'Zugriff mit GitHub fehlgeschlagen', - 'Your GitHub account is no longer linked to your profile.' => 'GitHub Account nicht mehr mit dem Profil verbunden.', - 'Unable to unlink your GitHub Account.' => 'Trennung der Verbindung zum GitHub Account nicht möglich.', - 'Login with my GitHub Account' => 'Anmelden mit meinem GitHub Account', - 'Link my GitHub Account' => 'Mit meinem GitHub Account verbinden', - 'Unlink my GitHub Account' => 'Verbindung mit meinem GitHub Account trennen', + 'Login with my Github Account' => 'Anmelden mit meinem Github-Account', + 'Link my Github Account' => 'Mit meinem Github-Account verbinden', + 'Unlink my Github Account' => 'Verbindung mit meinem Github-Account trennen', 'Created by %s' => 'Erstellt durch %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Letzte Änderung am %d.%m.%Y um %H:%M', 'Tasks Export' => 'Aufgaben exportieren', @@ -366,8 +353,8 @@ return array( 'Clone' => 'duplizieren', 'Project cloned successfully.' => 'Projekt wurde dupliziert.', 'Unable to clone this project.' => 'Duplizieren dieses Projekts schlug fehl.', - 'Email notifications' => 'E-Mail Benachrichtigungen', - 'Enable email notifications' => 'E-Mail Benachrichtigungen einschalten', + 'Email notifications' => 'E-Mail-Benachrichtigungen', + 'Enable email notifications' => 'E-Mail-Benachrichtigungen einschalten', 'Task position:' => 'Position der Aufgabe', 'The task #%d have been opened.' => 'Die Aufgabe #%d wurde geöffnet.', 'The task #%d have been closed.' => 'Die Aufgabe #%d wurde geschlossen.', @@ -408,8 +395,8 @@ return array( 'Remote' => 'Remote', 'Enabled' => 'angeschaltet', 'Disabled' => 'abgeschaltet', - 'Google account linked' => 'Mit Googleaccount verbunden', - 'Github account linked' => 'Mit Githubaccount verbunden', + 'Google account linked' => 'Mit Google-Account verbunden', + 'Github account linked' => 'Mit Github-Account verbunden', 'Username:' => 'Benutzername', 'Name:' => 'Name', 'Email:' => 'E-Mail', @@ -422,8 +409,8 @@ return array( 'Change password' => 'Passwort ändern', 'Password modification' => 'Passwortänderung', 'External authentications' => 'Externe Authentisierungsmethoden', - 'Google Account' => 'Googleaccount', - 'Github Account' => 'Githubaccount', + 'Google Account' => 'Google-Account', + 'Github Account' => 'Github-Account', 'Never connected.' => 'Noch nie verbunden.', 'No account linked.' => 'Kein Account verbunden.', 'Account linked.' => 'Account verbunden', @@ -442,10 +429,9 @@ return array( '%s updated a subtask for the task %s' => '%s hat eine Teilaufgabe der Aufgabe %s verändert', 'Assigned to %s with an estimate of %s/%sh' => 'An %s zugewiesen mit einer Schätzung von %s/%s Stunden', 'Not assigned, estimate of %sh' => 'Nicht zugewiesen, Schätzung von %s Stunden', - '%s updated a comment on the task %s' => '%s hat einen Kommentat der Aufgabe %s aktualisiert', + '%s updated a comment on the task %s' => '%s hat einen Kommentar der Aufgabe %s aktualisiert', '%s commented the task %s' => '%s hat die Aufgabe %s kommentiert', '%s\'s activity' => '%s\'s Aktivität', - 'No activity.' => 'Keine Aktivität.', 'RSS feed' => 'RSS Feed', '%s updated a comment on the task #%d' => '%s hat einen Kommentar der Aufgabe #%d aktualisiert', '%s commented on the task #%d' => '%s hat die Aufgabe #%d kommentiert', @@ -482,17 +468,17 @@ return array( 'Database driver:' => 'Datenbanktreiber', 'Board settings' => 'Pinnwandeinstellungen', 'URL and token' => 'URL und Token', - 'Webhook settings' => 'Webhook Einstellungen', + 'Webhook settings' => 'Webhook-Einstellungen', 'URL for task creation:' => 'URL zur Aufgabenerstellung', 'Reset token' => 'Token zurücksetzen', - 'API endpoint:' => 'API Endpunkt', + 'API endpoint:' => 'API-Endpunkt', 'Refresh interval for private board' => 'Aktualisierungsintervall für private Pinnwände', 'Refresh interval for public board' => 'Aktualisierungsintervall für öffentliche Pinnwände', 'Task highlight period' => 'Aufgaben-Hervorhebungsdauer', 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Dauer (in Sekunden), wie lange eine Aufgabe als kürzlich verändert gilt (0 um diese Funktion zu deaktivieren, standardmäßig 2 Tage)', 'Frequency in second (60 seconds by default)' => 'Frequenz in Sekunden (standardmäßig 60 Sekunden)', 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequenz in Sekunden (0 um diese Funktion zu deaktivieren, standardmäßig 10 Sekunden)', - 'Application URL' => 'Applikations URL', + 'Application URL' => 'Applikations-URL', 'Example: http://example.kanboard.net/ (used by email notifications)' => 'Beispiel: http://example.kanboard.net/ (wird für E-Mail-Benachrichtigungen verwendet)', 'Token regenerated.' => 'Token wurde neu generiert.', 'Date format' => 'Datumsformat', @@ -515,10 +501,10 @@ return array( 'Everybody have access to this project.' => 'Jeder hat Zugriff zu diesem Projekt', 'Webhooks' => 'Webhooks', 'API' => 'API', - 'Github webhooks' => 'Github Webhook', - 'Help on Github webhooks' => 'Hilfe für Github Webhooks', + 'Github webhooks' => 'Github-Webhook', + 'Help on Github webhooks' => 'Hilfe für Github-Webhooks', 'Create a comment from an external provider' => 'Kommentar eines externen Providers hinzufügen', - 'Github issue comment created' => 'Github Fehler Kommentar hinzugefügt', + 'Github issue comment created' => 'Kommentar zum Github-Issue hinzugefügt', 'Project management' => 'Projektmanagement', 'My projects' => 'Meine Projekte', 'Columns' => 'Spalten', @@ -540,15 +526,15 @@ return array( 'Not enough data to show the graph.' => 'Nicht genügend Daten, um die Grafik zu zeigen.', 'Previous' => 'Vorherige', 'The id must be an integer' => 'Die Id muss eine ganze Zahl sein', - 'The project id must be an integer' => 'Der Projektid muss eine ganze Zahl sein', + 'The project id must be an integer' => 'Der Projekt-ID muss eine ganze Zahl sein', 'The status must be an integer' => 'Der Status muss eine ganze Zahl sein', - 'The subtask id is required' => 'Die Teilaufgabenid ist benötigt', - 'The subtask id must be an integer' => 'Die Teilaufgabenid muss eine ganze Zahl sein', - 'The task id is required' => 'Die Aufgabenid ist benötigt', - 'The task id must be an integer' => 'Die Aufgabenid muss eine ganze Zahl sein', - 'The user id must be an integer' => 'Die Userid muss eine ganze Zahl sein', + 'The subtask id is required' => 'Die Teilaufgaben-ID ist benötigt', + 'The subtask id must be an integer' => 'Die Teilaufgaben-ID muss eine ganze Zahl sein', + 'The task id is required' => 'Die Aufgaben-ID ist benötigt', + 'The task id must be an integer' => 'Die Aufgaben-ID muss eine ganze Zahl sein', + 'The user id must be an integer' => 'Die User-ID muss eine ganze Zahl sein', 'This value is required' => 'Dieser Wert ist erforderlich', - 'This value must be numeric' => 'Dieser Wert muss numerisch sein', + 'This value must be numeric' => 'Dieser Wert muss nummerisch sein', 'Unable to create this task.' => 'Diese Aufgabe kann nicht erstellt werden', 'Cumulative flow diagram' => 'Kumulatives Flussdiagramm', 'Cumulative flow diagram for "%s"' => 'Kumulatives Flussdiagramm für "%s"', @@ -562,82 +548,70 @@ return array( 'Write' => 'Ändern', 'Active swimlanes' => 'Aktive Swimlane', 'Add a new swimlane' => 'Eine neue Swimlane hinzufügen', - 'Change default swimlane' => 'Standard Swimlane ändern', - 'Default swimlane' => 'Standard Swimlane', + 'Change default swimlane' => 'Standard-Swimlane ändern', + 'Default swimlane' => 'Standard-Swimlane', 'Do you really want to remove this swimlane: "%s"?' => 'Diese Swimlane wirklich ändern: "%s"?', 'Inactive swimlanes' => 'Inaktive Swimlane', 'Set project manager' => 'zum Projektmanager machen', 'Set project member' => 'zum Projektmitglied machen', 'Remove a swimlane' => 'Swimlane entfernen', 'Rename' => 'umbenennen', - 'Show default swimlane' => 'Standard Swimlane anzeigen', - 'Swimlane modification for the project "%s"' => 'Swimlane Änderung für das Projekt "%s"', + 'Show default swimlane' => 'Standard-Swimlane anzeigen', + 'Swimlane modification for the project "%s"' => 'Swimlane-Änderung für das Projekt "%s"', 'Swimlane not found.' => 'Swimlane nicht gefunden', 'Swimlane removed successfully.' => 'Swimlane erfolgreich entfernt.', 'Swimlanes' => 'Swimlanes', 'Swimlane updated successfully.' => 'Swimlane erfolgreich geändert.', - 'The default swimlane have been updated successfully.' => 'Die standard Swimlane wurden erfolgreich aktualisiert. Die standard Swimlane wurden erfolgreich aktualisiert.', - 'Unable to create your swimlane.' => 'Es ist nicht möglich die Swimlane zu erstellen.', - 'Unable to remove this swimlane.' => 'Es ist nicht möglich die Swimlane zu entfernen.', - 'Unable to update this swimlane.' => 'Es ist nicht möglich die Swimöane zu ändern.', + 'The default swimlane have been updated successfully.' => 'Die Standard-Swimlane wurden erfolgreich aktualisiert. Die Standard-Swimlane wurden erfolgreich aktualisiert.', + 'Unable to create your swimlane.' => 'Es ist nicht möglich, Swimlane zu erstellen.', + 'Unable to remove this swimlane.' => 'Es ist nicht möglich, die Swimlane zu entfernen.', + 'Unable to update this swimlane.' => 'Es ist nicht möglich, die Swimlane zu ändern.', 'Your swimlane have been created successfully.' => 'Die Swimlane wurde erfolgreich angelegt.', 'Example: "Bug, Feature Request, Improvement"' => 'Beispiel: "Bug, Funktionswünsche, Verbesserung"', - 'Default categories for new projects (Comma-separated)' => 'Standard Kategorien für neue Projekte (Komma-getrennt)', - 'Gitlab commit received' => 'Gitlab commit erhalten', - 'Gitlab issue opened' => 'Gitlab Fehler eröffnet', - 'Gitlab issue closed' => 'Gitlab Fehler geschlossen', - 'Gitlab webhooks' => 'Gitlab Webhook', - 'Help on Gitlab webhooks' => 'Hilfe für Gitlab Webhooks', + 'Default categories for new projects (Comma-separated)' => 'Standard-Kategorien für neue Projekte (Komma-getrennt)', + 'Gitlab commit received' => 'Gitlab-Commit erhalten', + 'Gitlab issue opened' => 'Gitlab-Issue eröffnet', + 'Gitlab issue closed' => 'Gitlab-Issue geschlossen', + 'Gitlab webhooks' => 'Gitlab-Webhook', + 'Help on Gitlab webhooks' => 'Hilfe für Gitlab-Webhooks', 'Integrations' => 'Integration', - 'Integration with third-party services' => 'Integration von Fremdleistungen', + 'Integration with third-party services' => 'Integration von externen Diensten', 'Role for this project' => 'Rolle für dieses Projekt', 'Project manager' => 'Projektmanager', 'Project member' => 'Projektmitglied', 'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Ein Projektmanager kann die Projekteinstellungen ändern und hat mehr Rechte als ein normaler Benutzer.', - 'Gitlab Issue' => 'Gitlab Fehler', - 'Subtask Id' => 'Teilaufgaben Id', + 'Gitlab Issue' => 'Gitlab-Issue', + 'Subtask Id' => 'Teilaufgaben-ID', 'Subtasks' => 'Teilaufgaben', - 'Subtasks Export' => 'Teilaufgaben Export', - 'Subtasks exportation for "%s"' => 'Teilaufgaben Export für "%s"', - 'Task Title' => 'Aufgaben Titel', + 'Subtasks Export' => 'Export von Teilaufgaben', + 'Subtasks exportation for "%s"' => 'Export von Teilaufgaben für "%s"', + 'Task Title' => 'Aufgaben-Titel', 'Untitled' => 'unbetitelt', 'Application default' => 'Anwendungsstandard', 'Language:' => 'Sprache:', 'Timezone:' => 'Zeitzone:', 'All columns' => 'Alle Spalten', - 'Calendar for "%s"' => 'Kalender für "%s"', - 'Filter by column' => 'Spalte filtern', - 'Filter by status' => 'Status filtern', 'Calendar' => 'Kalender', 'Next' => 'Nächste', // '#%d' => '', - 'Filter by color' => 'Farbe filtern', - 'Filter by swimlane' => 'Swimlane filtern', 'All swimlanes' => 'Alle Swimlanes', 'All colors' => 'Alle Farben', 'All status' => 'Alle Status', 'Moved to column %s' => 'In Spalte %s verschoben', 'Change description' => 'Beschreibung ändern', - 'User dashboard' => 'Benutzer Dashboard', + 'User dashboard' => 'Benutzer-Dashboard', 'Allow only one subtask in progress at the same time for a user' => 'Erlaube nur eine Teilaufgabe pro Benutzer zu bearbeiten', 'Edit column "%s"' => 'Spalte "%s" bearbeiten', 'Select the new status of the subtask: "%s"' => 'Wähle einen neuen Status für Teilaufgabe: "%s"', 'Subtask timesheet' => 'Teilaufgaben Zeiterfassung', 'There is nothing to show.' => 'Es ist nichts zum Anzeigen vorhanden.', 'Time Tracking' => 'Zeiterfassung', - 'You already have one subtask in progress' => 'Bereits eine Teilaufgabe in bearbeitung', + 'You already have one subtask in progress' => 'Bereits eine Teilaufgabe in Bearbeitung', 'Which parts of the project do you want to duplicate?' => 'Welcher Teil des Projekts soll kopiert werden?', - 'Change dashboard view' => 'Dashboardansicht ändern', - 'Show/hide activities' => 'Aktivitäten anzeigen/verbergen', - 'Show/hide projects' => 'Projekte anzeigen/verbergen', - 'Show/hide subtasks' => 'Teilaufgaben anzeigen/verbergen', - 'Show/hide tasks' => 'Aufgaben anzeigen/verbergen', - 'Disable login form' => 'Anmeldeformular deaktivieren', - 'Show/hide calendar' => 'Kalender anzeigen/verbergen', - 'User calendar' => 'Benutzer Kalender', - 'Bitbucket commit received' => 'Bitbucket commit erhalten', - 'Bitbucket webhooks' => 'Bitbucket webhooks', - 'Help on Bitbucket webhooks' => 'Hilfe für Bitbucket webhooks', + // 'Disallow login form' => '', + 'Bitbucket commit received' => 'Bitbucket-Commit erhalten', + 'Bitbucket webhooks' => 'Bitbucket-Webhooks', + 'Help on Bitbucket webhooks' => 'Hilfe für Bitbucket-Webhooks', 'Start' => 'Start', 'End' => 'Ende', 'Task age in days' => 'Aufgabenalter in Tagen', @@ -657,7 +631,7 @@ return array( 'Link settings' => 'Verbindungseinstellungen', 'Opposite label' => 'Gegenteil', 'Remove a link' => 'Verbindung entfernen', - 'Task\'s links' => 'Aufgaben Verbindungen', + 'Task\'s links' => 'Aufgaben-Verbindungen', 'The labels must be different' => 'Die Beschriftung muss unterschiedlich sein', 'There is no link.' => 'Es gibt keine Verbindung', 'This label must be unique' => 'Die Beschriftung muss einzigartig sein', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Tastaturkürzel', 'Open board switcher' => 'Pinnwandauswahl öffnen', 'Application' => 'Anwendung', - 'Filter recently updated' => 'Zuletzt geänderte anzeigen', 'since %B %e, %Y at %k:%M %p' => 'seit %B %e, %Y um %k:%M %p', - 'More filters' => 'Mehr Filter', 'Compact view' => 'Kompaktansicht', 'Horizontal scrolling' => 'Horizontales Scrollen', 'Compact/wide view' => 'Kompakt/Breite-Ansicht', @@ -727,18 +699,18 @@ return array( 'Do you really want to remove this time slot?' => 'Soll diese Zeitfenster wirklich gelöscht werden?', 'Remove time slot' => 'Zeitfenster entfernen', 'Add new time slot' => 'Neues Zeitfenster hinzufügen', - 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Dieses Zeitfenster wird verwendet, wenn die Checkbox "gantägig" für Freizeit und Überstunden angeklickt ist.', + 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Dieses Zeitfenster wird verwendet, wenn die Checkbox "ganztägig" für Freizeit und Überstunden angeklickt ist.', 'Files' => 'Dateien', 'Images' => 'Bilder', 'Private project' => 'privates Projekt', 'Amount' => 'Betrag', - // 'AUD - Australian Dollar' => '', + 'AUD - Australian Dollar' => 'AUD - Australische Dollar', 'Budget' => 'Budget', 'Budget line' => 'Budgetlinie', 'Budget line removed successfully.' => 'Budgetlinie erfolgreich entfernt', 'Budget lines' => 'Budgetlinien', - // 'CAD - Canadian Dollar' => '', - // 'CHF - Swiss Francs' => '', + 'CAD - Canadian Dollar' => 'CAD - Kanadische Dollar', + 'CHF - Swiss Francs' => 'CHF - Schweizer Franken', 'Cost' => 'Kosten', 'Cost breakdown' => 'Kostenaufschlüsselung', 'Custom Stylesheet' => 'benutzerdefiniertes Stylesheet', @@ -746,9 +718,9 @@ return array( 'Do you really want to remove this budget line?' => 'Soll diese Budgetlinie wirklich entfernt werden?', 'EUR - Euro' => 'EUR - Euro', 'Expenses' => 'Kosten', - 'GBP - British Pound' => 'GBP - Britische Pfung', + 'GBP - British Pound' => 'GBP - Britische Pfund', 'INR - Indian Rupee' => 'INR - Indische Rupien', - 'JPY - Japanese Yen' => 'JPY - Japanischer Yen', + 'JPY - Japanese Yen' => 'JPY - Japanische Yen', 'New budget line' => 'Neue Budgetlinie', 'NZD - New Zealand Dollar' => 'NZD - Neuseeland-Dollar', 'Remove a budget line' => 'Budgetlinie entfernen', @@ -757,37 +729,37 @@ return array( 'The budget line have been created successfully.' => 'Die Budgetlinie wurde erfolgreich angelegt.', 'Unable to create the budget line.' => 'Budgetlinie konnte nicht erstellt werden.', 'Unable to remove this budget line.' => 'Budgetlinie konnte nicht gelöscht werden.', - 'USD - US Dollar' => 'USD - US Dollar', + 'USD - US Dollar' => 'USD - US-Dollar', 'Remaining' => 'Verbleibend', 'Destination column' => 'Zielspalte', 'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein User zugeordnet wurde.', 'Move the task to another column when assignee is cleared' => 'Aufgabe in eine andere Spalte verschieben, wenn die Zuordnung gelöscht wurde.', 'Source column' => 'Quellspalte', - 'Show subtask estimates (forecast of future work)' => 'Teilaufgaben Schätzungen anzeigen (Prognose)', + 'Show subtask estimates (forecast of future work)' => 'Teilaufgaben-Schätzungen anzeigen (Prognose)', 'Transitions' => 'Übergänge', 'Executer' => 'Ausführender', 'Time spent in the column' => 'Zeit in Spalte verbracht', - 'Task transitions' => 'Aufgaben Übergänge', - 'Task transitions export' => 'Aufgaben Übergänge exportieren', + 'Task transitions' => 'Aufgaben-Übergänge', + 'Task transitions export' => 'Aufgaben-Übergänge exportieren', 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Diese Auswertung enthält alle Spaltenbewegungen für jede Aufgabe mit Datum, Benutzer und Zeit vor jedem Wechsel.', 'Currency rates' => 'Währungskurse', 'Rate' => 'Kurse', 'Change reference currency' => 'Referenzwährung ändern', 'Add a new currency rate' => 'Neuen Währungskurs hinzufügen', - 'Currency rates are used to calculate project budget.' => 'Währungskurse werden verwendet um das Projektbudget zu berechnen.', + 'Currency rates are used to calculate project budget.' => 'Währungskurse werden verwendet, um das Projektbudget zu berechnen.', 'Reference currency' => 'Referenzwährung', 'The currency rate have been added successfully.' => 'Der Währungskurs wurde erfolgreich hinzugefügt.', 'Unable to add this currency rate.' => 'Währungskurs konnte nicht hinzugefügt werden', 'Send notifications to a Slack channel' => 'Benachrichtigung an einen Slack-Kanal senden', - 'Webhook URL' => 'Webhook URL', - 'Help on Slack integration' => 'Hilfe für Slack integration.', + 'Webhook URL' => 'Webhook-URL', + 'Help on Slack integration' => 'Hilfe für Slack-Integration.', '%s remove the assignee of the task %s' => '%s Zuordnung für die Aufgabe %s entfernen', 'Send notifications to Hipchat' => 'Sende Benachrichtigung an Hipchat', - 'API URL' => 'API URL', - 'Room API ID or name' => 'Raum API ID oder Name', - 'Room notification token' => 'Raum Benachrichtigungstoken', - 'Help on Hipchat integration' => 'Hilfe bei Hipchat Integration', - 'Enable Gravatar images' => 'Aktiviere Gravatar Bilder', + 'API URL' => 'API-URL', + 'Room API ID or name' => 'Raum-API-ID oder -Name', + 'Room notification token' => 'Raum-Benachrichtigungstoken', + 'Help on Hipchat integration' => 'Hilfe bei Hipchat-Integration', + 'Enable Gravatar images' => 'Aktiviere Gravatar-Bilder', 'Information' => 'Information', 'Check two factor authentication code' => 'Prüfe Zwei-Faktor-Authentifizierungscode', 'The two factor authentication code is not valid.' => 'Der Zwei-Faktor-Authentifizierungscode ist ungültig.', @@ -830,7 +802,7 @@ return array( 'Recurrent task is scheduled to be generated' => 'Wiederkehrende Aufgabe ist zur Generierung eingeplant', 'Recurring information' => 'Wiederkehrende Information', 'Score' => 'Wertung', - 'The identifier must be unique' => 'Der Schlüssel miss einzigartig sein', + 'The identifier must be unique' => 'Der Schlüssel muss einzigartig sein', 'This linked task id doesn\'t exists' => 'Die verbundene Aufgabe existiert nicht', 'This value must be alphanumeric' => 'Der Wert muss alphanumerisch sein', 'Edit recurrence' => 'Wiederholung bearbeiten', @@ -856,40 +828,40 @@ return array( 'When task is moved to last column' => 'Wenn Aufgabe in letzte Spalte verschoben wird', 'Year(s)' => 'Jahr(e)', // 'Jabber (XMPP)' => '', - // 'Send notifications to Jabber' => '', - // 'XMPP server address' => '', - // 'Jabber domain' => '', - // 'Jabber nickname' => '', - // 'Multi-user chat room' => '', - // 'Help on Jabber integration' => '', - // 'The server address must use this format: "tcp://hostname:5222"' => '', - 'Calendar settings' => 'Kalendar Einstellungen', + 'Send notifications to Jabber' => 'Benachrichtigungen an Jabber senden', + 'XMPP server address' => 'XMPP-Server-Adresse', + 'Jabber domain' => 'Jabber-Domain', + 'Jabber nickname' => 'Jabber-Nickname', + 'Multi-user chat room' => 'Multi-User-Chatroom', + 'Help on Jabber integration' => 'Hilfe zur Jabber-Integration', + 'The server address must use this format: "tcp://hostname:5222"' => 'Die Server-Adresse muss in diesem Format sein: "tcp://hostname:5222"', + 'Calendar settings' => 'Kalender-Einstellungen', 'Project calendar view' => 'Projekt-Kalendarsicht', 'Project settings' => 'Projekteinstellungen', 'Show subtasks based on the time tracking' => 'Zeige Teilaufgaben basierend auf Zeiterfassung', 'Show tasks based on the creation date' => 'Zeige Aufgaben basierend auf Erstelldatum', 'Show tasks based on the start date' => 'Zeige Aufgaben basierend auf Beginndatum', - 'Subtasks time tracking' => 'Teilaufgaben Zeiterfassung', - 'User calendar view' => 'Benutzer-Kalendarsicht', + 'Subtasks time tracking' => 'Teilaufgaben-Zeiterfassung', + 'User calendar view' => 'Benutzer-Kalendersicht', 'Automatically update the start date' => 'Beginndatum automatisch aktualisieren', - //'iCal feed' => '', + // 'iCal feed' => '', 'Preferences' => 'Einstellungen', 'Security' => 'Sicherheit', - 'Two factor authentication disabled' => 'Zweifaktorauthentifizierung deaktiviert', - 'Two factor authentication enabled' => 'Zweifaktorauthentifizierung aktiviert', - 'Unable to update this user.' => 'User kann nicht bearbeitet werden', + 'Two factor authentication disabled' => 'Zwei-Faktor-Authentifizierung deaktiviert', + 'Two factor authentication enabled' => 'Zwei-Faktor-Authentifizierung aktiviert', + 'Unable to update this user.' => 'Benutzer kann nicht bearbeitet werden', 'There is no user management for private projects.' => 'Es gibt keine Benutzerverwaltung für private Projekte', 'User that will receive the email' => 'Empfänger der E-Mail', - 'Email subject' => 'E-Mail Betreff', + 'Email subject' => 'E-Mail-Betreff', 'Date' => 'Datum', // 'By @%s on Bitbucket' => '', - // 'Bitbucket Issue' => '', + 'Bitbucket Issue' => 'Bitbucket-Issue', // 'Commit made by @%s on Bitbucket' => '', // 'Commit made by @%s on Github' => '', // 'By @%s on Github' => '', // 'Commit made by @%s on Gitlab' => '', - 'Add a comment log when moving the task between columns' => 'Kommentar hinzufügen, wenn Aufgabe in andere Spalte geschoben wird', - 'Move the task to another column when the category is changed' => 'Aufgabe in andere Spalte verschieben, wenn Kategorie geändert', + 'Add a comment log when moving the task between columns' => 'Kommentar hinzufügen, wenn Aufgabe in andere Spalte verschoben wird', + 'Move the task to another column when the category is changed' => 'Aufgabe in andere Spalte verschieben, wenn Kategorie geändert wird', 'Send a task by email to someone' => 'Aufgabe per E-Mail versenden', 'Reopen a task' => 'Aufgabe wieder öffnen', // 'Bitbucket issue opened' => '', @@ -906,22 +878,21 @@ return array( '%s moved the task #%d to the first swimlane' => '%s hat die Aufgabe #%d in die erste Swimlane verschoben', '%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben', // 'Swimlane' => '', - 'Budget overview' => 'Budget Übersicht', + 'Budget overview' => 'Budget-Übersicht', 'Type' => 'Typ', - 'There is not enough data to show something.' => 'Es gibt nicht genug Daten für die Anzeige', + 'There is not enough data to show something.' => 'Es gibt nicht genügend Daten für diese Anzeige', // 'Gravatar' => '', // 'Hipchat' => '', // 'Slack' => '', '%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben', '%s moved the task %s to the swimlane "%s"' => '%s hat die Aufgaben %s in die Swimlane "%s" verschoben', - 'This report contains all subtasks information for the given date range.' => 'Der Bericht beinhaltet alle Teilaufgaben im ausgewählten Zeitraum', - 'This report contains all tasks information for the given date range.' => 'Der Bericht beinhaltet alle Aufgaben im ausgewählten Zeitraum', + 'This report contains all subtasks information for the given date range.' => 'Der Bericht beinhaltet alle Teilaufgaben im gewählten Zeitraum', + 'This report contains all tasks information for the given date range.' => 'Der Bericht beinhaltet alle Aufgaben im gewählten Zeitraum', 'Project activities for %s' => 'Projektaktivitäten für %s', 'view the board on Kanboard' => 'Pinnwand in Kanboard anzeigen', 'The task have been moved to the first swimlane' => 'Die Aufgabe wurde in die erste Swimlane verschoben', 'The task have been moved to another swimlane:' => 'Die Aufgaben wurde in ene andere Swimlane verschoben', 'Overdue tasks for the project "%s"' => 'Überfällige Aufgaben für das Projekt "%s"', - 'There is no completed tasks at the moment.' => 'Es gibt keine abgeschlossenen Aufgaben', 'New title: %s' => 'Neuer Titel: %s', 'The task is not assigned anymore' => 'Die Aufgabe ist nicht mehr zugewiesen', 'New assignee: %s' => 'Neue Zuordnung: %s', @@ -930,7 +901,7 @@ return array( 'New color: %s' => 'Neue Farbe: %s', 'New complexity: %d' => 'Neue Komplexität: %d', 'The due date have been removed' => 'Das Ablaufdatum wurde entfernt', - 'There is no description anymore' => 'Es gibt keine BEschreibung mehr', + 'There is no description anymore' => 'Es gibt keine Beschreibung mehr', 'Recurrence settings have been modified' => 'Die Einstellungen für Wiederholung wurden geändert', 'Time spent changed: %sh' => 'Verbrauchte Zeit geändert: %sh', 'Time estimated changed: %sh' => 'Geschätzte Zeit geändert: %sh', @@ -938,24 +909,98 @@ return array( 'The description have 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)', // 'Swimlane: %s' => '', - 'Project calendar' => 'Projektkalendar', - 'I want to receive notifications for:' => 'Ich möchte Benachrichtigungn erhalten für:', + 'I want to receive notifications for:' => 'Ich möchte Benachrichtigungen erhalten für:', 'All tasks' => 'Alle Aufgaben', 'Only for tasks assigned to me' => 'nur mir zugeordnete Aufgane', 'Only for tasks created by me' => 'nur von mir erstellte Aufgaben', 'Only for tasks created by me and assigned to me' => 'nur mir zugeordnete und von mir erstellte Aufgaben', - //'%A' => '', - //'%b %e, %Y, %k:%M %p' => '', + // '%A' => '', + // '%b %e, %Y, %k:%M %p' => '', 'New due date: %B %e, %Y' => 'Neues Ablaufdatum: %B %e, %Y', 'Start date changed: %B %e, %Y' => 'Neues Beginndatum: %B %e, %Y', - //'%k:%M %p' => '', - //'%%Y-%%m-%%d' => '', + // '%k:%M %p' => '', + // '%%Y-%%m-%%d' => '', 'Total for all columns' => 'Gesamt für alle Spalten', 'You need at least 2 days of data to show the chart.' => 'Es werden mindestens 2 Tage zur Darstellung benötigt', - //'<15m' => '', - //'<30m' => '', + // '<15m' => '', + // '<30m' => '', 'Stop timer' => 'Stoppe Timer', 'Start timer' => 'Starte Timer', 'Add project member' => 'Projektmitglied hinzufügen', 'Enable notifications' => 'Benachrichtigung aktivieren', + 'My activity stream' => 'Aktivitätsstream', + 'My calendar' => 'Mein Kalender', + 'Search tasks' => 'Suche nach Aufgaben', + 'Back to the calendar' => 'Zurück zum Kalender', + 'Filters' => 'Filter', + 'Reset filters' => 'Filter zurücksetzen', + 'My tasks due tomorrow' => 'Meine morgen fälligen Aufgaben', + 'Tasks due today' => 'Heute fällige Aufgaben', + 'Tasks due tomorrow' => 'Morgen fällige Aufgaben', + 'Tasks due yesterday' => 'Gestern fällige Aufgaben', + 'Closed tasks' => 'Abgeschlossene Aufgaben', + 'Open tasks' => 'Offene Aufgaben', + 'Not assigned' => 'Nicht zugewiesen', + 'View advanced search syntax' => 'Zur erweiterten Suchsyntax', + 'Overview' => 'Überblick', + // '%b %e %Y' => '', + 'Board/Calendar/List view' => 'Board-/Kalender-/Listen-Ansicht', + 'Switch to the board view' => 'Zur Board-Ansicht', + 'Switch to the calendar view' => 'Zur Kalender-Ansicht', + 'Switch to the list view' => 'Zur Listen-Ansicht', + 'Go to the search/filter box' => 'Zum Such- und Filterfeld', + 'There is no activity yet.' => 'Es gibt bislang keine Aktivitäten.', + 'No tasks found.' => 'Keine Aufgaben gefunden.', + 'Keyboard shortcut: "%s"' => 'Tastaturkürzel: "%s"', + 'List' => 'Liste', + 'Filter' => 'Filter', + 'Advanced search' => 'Fortgeschrittene Suche', + 'Example of query: ' => 'Beispiel einer Abfrage: ', + 'Search by project: ' => 'Suche nach Projekt: ', + 'Search by column: ' => 'Suche nach Spalte: ', + 'Search by assignee: ' => 'Suche nach zugeordnetem Benutzer: ', + 'Search by color: ' => 'Suche nach Farbe: ', + 'Search by category: ' => 'Suche nach Kategorie: ', + 'Search by description: ' => 'Suche nach Beschreibung: ', + 'Search by due date: ' => 'Suche nach Fälligkeitsdatum: ', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index b6d89ac7..867cc3db 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -20,15 +20,15 @@ return array( 'Red' => 'Rojo', 'Orange' => 'Naranja', 'Grey' => 'Gris', - // 'Brown' => '', - // 'Deep Orange' => '', - // 'Dark Grey' => '', - // 'Pink' => '', - // 'Teal' => '', - // 'Cyan' => '', - // 'Lime' => '', - // 'Light Green' => '', - // 'Amber' => '', + 'Brown' => 'Marrón', + 'Deep Orange' => 'Naranja Oscuro', + 'Dark Grey' => 'Gris Oscuro', + 'Pink' => 'Rosa', + 'Teal' => 'Verde Azulado', + 'Cyan' => 'Cián', + 'Lime' => 'Lima', + 'Light Green' => 'Verde Claro', + 'Amber' => 'Ámbar', 'Save' => 'Guardar', 'Login' => 'Iniciar sesión (Ingresar)', 'Official website:' => 'Página web oficial :', @@ -48,7 +48,7 @@ return array( 'Access Forbidden' => 'Acceso denegado', 'Edit user' => 'Editar un usuario', 'Logout' => 'Salir', - 'Bad username or password' => 'Usuario o contraseña incorecto', + 'Bad username or password' => 'Usuario o contraseña incorrecto', 'Edit project' => 'Editar el proyecto', 'Name' => 'Nombre', 'Projects' => 'Proyectos', @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'El nombre del proyecto es obligatorio', 'This project must be unique' => 'El nombre del proyecto debe ser único', 'The title is required' => 'El titulo es obligatorio', - 'There is no active project, the first step is to create a new project.' => 'No hay proyectos activados, la primera etapa consiste en crear un nuevo proyecto.', 'Settings saved successfully.' => 'Parámetros guardados correctamente.', 'Unable to save your settings.' => 'No se pueden guardar sus parámetros.', 'Database optimization done.' => 'Optimización de la base de datos terminada.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Fecha de creación', 'Date completed' => 'Fecha de terminación', 'Id' => 'Identificador', - 'Completed tasks' => 'Tareas completadas', - 'Completed tasks for "%s"' => 'Tareas completadas por « %s »', '%d closed tasks' => '%d tareas completadas', 'No task for this project' => 'Ninguna tarea para este proyecto', 'Public link' => 'Vinculación pública', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Fecha de expiración', 'Remember Me' => 'Recuérdame', 'Creation date' => 'Fecha de creación', - 'Filter by user' => 'Filtrado mediante usuario', - 'Filter by due date' => 'Filtrado mediante fecha límite', 'Everybody' => 'Todo el mundo', 'Open' => 'Abierto', 'Closed' => 'Cerrado', 'Search' => 'Buscar', 'Nothing found.' => 'Nada hallado.', - 'Search in the project "%s"' => 'Buscar en el proyecto "%s"', 'Due date' => 'Fecha límite', 'Others formats accepted: %s and %s' => 'Otros formatos aceptados: %s y %s', 'Description' => 'Descripción', '%d comments' => '%d comentarios', '%d comment' => '%d comentario', 'Email address invalid' => 'Dirección de correo inválida', - 'Your Google Account is not linked anymore to your profile.' => 'Tu Cuenta en Google ya no se encuentra vinculada con tu perfil', - 'Unable to unlink your Google Account.' => 'No puedo desvincular tu Cuenta en Google.', - 'Google authentication failed' => 'Ha fallado tu autenticación en Google', - 'Unable to link your Google Account.' => 'No puedo vincular con tu Cuenta en Google.', - 'Your Google Account is linked to your profile successfully.' => 'Se ha vinculado correctamente tu Cuenta en Google con tu perfil.', + 'Your external account is not linked anymore to your profile.' => 'Su cuenta externa se ha desvinculado de su perfil.', + 'Unable to unlink your external account.' => 'Imposible desvincular cuenta externa.', + 'External authentication failed' => 'Falló la autenticación externa', + 'Your external account is linked to your profile successfully.' => 'Su cuenta externa se ha vinculado exitosamente con su perfil.', 'Email' => 'Correo', 'Link my Google Account' => 'Vincular con mi Cuenta en Google', 'Unlink my Google Account' => 'Desvincular de mi Cuenta en Google', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Nombre de Categoría', 'Add a new category' => 'Añadir una nueva categoría', 'Do you really want to remove this category: "%s"?' => '¿De verdad que quieres suprimir esta categoría: "%s"?', - 'Filter by category' => 'Filtrar mendiante categoría', 'All categories' => 'Todas las categorías', 'No category' => 'Sin categoría', 'The name is required' => 'El nombre es obligatorio', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Tamaño máximo', 'Unable to upload the file.' => 'No pude cargar el fichero.', 'Display another project' => 'Mostrar otro proyecto', - 'Your GitHub account was successfully linked to your profile.' => 'Tu cuenta de GitHub ha sido correctamente vinculada con tu perfil', - 'Unable to link your GitHub Account.' => 'Imposible vincular tu cuenta de GitHub', - 'GitHub authentication failed' => 'Falló la autenticación de GitHub', - 'Your GitHub account is no longer linked to your profile.' => 'Tu cuenta de GitHub ya no está vinculada a tu perfil', - 'Unable to unlink your GitHub Account.' => 'Imposible desvincular tu cuenta de GitHub', - 'Login with my GitHub Account' => 'Ingresar con mi cuenta de GitHub', - 'Link my GitHub Account' => 'Vincular mi cuenta de GitHub', - 'Unlink my GitHub Account' => 'Desvincular mi cuenta de GitHub', + 'Login with my Github Account' => 'Ingresar con mi cuenta de Github', + 'Link my Github Account' => 'Vincular mi cuenta de Github', + 'Unlink my Github Account' => 'Desvincular mi cuenta de Github', 'Created by %s' => 'Creado por %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Última modificación %B %e, %Y a las %k:%M %p', 'Tasks Export' => 'Exportar tareas', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s actualizó un comentario de la tarea %s', '%s commented the task %s' => '%s comentó la tarea %s', '%s\'s activity' => 'Actividad de %s', - 'No activity.' => 'Sin actividad', 'RSS feed' => 'Fichero RSS', '%s updated a comment on the task #%d' => '%s actualizó un comentario de la tarea #%d', '%s commented on the task #%d' => '%s comentó la tarea #%d', @@ -535,9 +521,9 @@ return array( 'User repartition' => 'Repartición de usuarios', 'User repartition for "%s"' => 'Repartición para "%s"', 'Clone this project' => 'Clonar este proyecto', - 'Column removed successfully.' => 'Columna removida correctamente', - 'Github Issue' => 'Problema Github', - 'Not enough data to show the graph.' => 'No hay suficiente información para mostrar el gráfico', + 'Column removed successfully.' => 'Columna eliminada correctamente', + 'Github Issue' => 'Problema con Github', + 'Not enough data to show the graph.' => 'No hay suficiente información para mostrar el gráfico.', 'Previous' => 'Anterior', 'The id must be an integer' => 'El id debe ser un entero', 'The project id must be an integer' => 'El id del proyecto debe ser un entero', @@ -571,9 +557,9 @@ return array( 'Remove a swimlane' => 'Remover un carril', 'Rename' => 'Renombrar', 'Show default swimlane' => 'Mostrar carril por defecto', - // 'Swimlane modification for the project "%s"' => '', + 'Swimlane modification for the project "%s"' => 'Modificación del carril para el proyecto "%s"', 'Swimlane not found.' => 'Carril no encontrado', - 'Swimlane removed successfully.' => 'Carril removido correctamente', + 'Swimlane removed successfully.' => 'Carril eliminado correctamente', 'Swimlanes' => 'Carriles', 'Swimlane updated successfully.' => 'Carril actualizado correctamente', 'The default swimlane have been updated successfully.' => 'El carril por defecto ha sido actualizado correctamente', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Idioma', 'Timezone:' => 'Zona horaria', 'All columns' => 'Todas las columnas', - 'Calendar for "%s"' => 'Calendario para "%s"', - 'Filter by column' => 'Filtrar por columna', - 'Filter by status' => 'Filtrar por estado', 'Calendar' => 'Calendario', 'Next' => 'Siguiente', // '#%d' => '', - 'Filter by color' => 'Filtrar por color', - 'Filter by swimlane' => 'Filtrar por carril', 'All swimlanes' => 'Todos los carriles', 'All colors' => 'Todos los colores', 'All status' => 'Todos los estados', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Seguimiento Temporal', 'You already have one subtask in progress' => 'Ya dispones de una subtarea en progreso', 'Which parts of the project do you want to duplicate?' => '¿Qué partes del proyecto deseas duplicar?', - 'Change dashboard view' => 'Cambiar vista de tablero', - 'Show/hide activities' => 'Mostrar/ocultar actividades', - 'Show/hide projects' => 'Mostrar/ocultar proyectos', - 'Show/hide subtasks' => 'Mostrar/Ocultar subtareas', - 'Show/hide tasks' => 'Mostrar/ocultar tareas', - 'Disable login form' => 'Desactivar formulario de ingreso', - 'Show/hide calendar' => 'Mostrar/ocultar calendario', - 'User calendar' => 'Calendario de usuario', + 'Disallow login form' => 'Deshabilitar formulario de ingreso', 'Bitbucket commit received' => 'Recibida envío desde Bitbucket', 'Bitbucket webhooks' => 'Disparadores Web (webhooks) de Bitbucket', 'Help on Bitbucket webhooks' => 'Ayuda sobre disparadores web (webhooks) de Bitbucket', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Atajos de teclado', 'Open board switcher' => 'Abrir conmutador de tablero', 'Application' => 'Aplicación', - 'Filter recently updated' => 'Filtro actualizado recientemente', 'since %B %e, %Y at %k:%M %p' => 'desde %B %e, %Y a las %k:%M %p', - 'More filters' => 'Más filtros', 'Compact view' => 'Compactar vista', 'Horizontal scrolling' => 'Desplazamiento horizontal', 'Compact/wide view' => 'Vista compacta/amplia', @@ -720,15 +692,15 @@ return array( 'Day timetable' => 'Horario diario', 'From' => 'Desde', 'To' => 'Hasta', - 'Time slot created successfully.' => 'Tiempo asignado creado con éxito.', + 'Time slot created successfully.' => 'Tiempo asignado creado correctamente.', 'Unable to save this time slot.' => 'No pude grabar este tiempo asignado.', - 'Time slot removed successfully.' => 'Tiempo asignado quitado con éxito.', + 'Time slot removed successfully.' => 'Tiempo asignado quitado correctamente.', 'Unable to remove this time slot.' => 'No pude quitar este tiempo asignado.', 'Do you really want to remove this time slot?' => '¿Realmente quieres quitar este tiempo asignado?', 'Remove time slot' => 'Quitar tiempo asignado', 'Add new time slot' => 'Añadir nuevo tiempo asignado', 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Este horario se usa cuando se marca la casilla "todos los días" para calendario de tiempo libre y horas extras.', - 'Files' => 'Ficheros', + 'Files' => 'Archivos', 'Images' => 'Imágenes', 'Private project' => 'Proyecto privado', 'Amount' => 'Cantidad', @@ -757,13 +729,13 @@ return array( 'The budget line have been created successfully.' => 'Se ha creado la línea de presupuesto con éxito.', 'Unable to create the budget line.' => 'No pude crear la línea de presupuesto.', 'Unable to remove this budget line.' => 'No pude quitar esta línea de presupuesto.', - 'USD - US Dollar' => 'USD - Dólar americano', + 'USD - US Dollar' => 'USD - Dólar Estadounidense', 'Remaining' => 'Restante', 'Destination column' => 'Columna destino', 'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columna al asignarse al usuario', 'Move the task to another column when assignee is cleared' => 'Mover la tarea a otra columna al quitar el asignado', 'Source column' => 'Columna fuente', - // 'Show subtask estimates (forecast of future work)' => '', + 'Show subtask estimates (forecast of future work)' => 'Mostrar estimado para la subtarea (pronóstico de trabajo futuro)', 'Transitions' => 'Transiciones', 'Executer' => 'Ejecutor', 'Time spent in the column' => 'Tiempo transcurrido en la columna', @@ -824,33 +796,33 @@ return array( 'Disable two factor authentication' => 'Desactivar la autenticación de dos factores', 'Do you really want to disable the two factor authentication for this user: "%s"?' => '¿Realmentes quieres desactuvar la autenticación de dos factores para este usuario: "%s?"', 'Edit link' => 'Editar enlace', - // 'Start to type task title...' => '', - // 'A task cannot be linked to itself' => '', - // 'The exact same link already exists' => '', - // 'Recurrent task is scheduled to be generated' => '', - // 'Recurring information' => '', - // 'Score' => '', - // 'The identifier must be unique' => '', - // 'This linked task id doesn\'t exists' => '', - // 'This value must be alphanumeric' => '', + 'Start to type task title...' => 'Empiece a escribir el título de la tarea...', + 'A task cannot be linked to itself' => 'Una tarea no puede se enlazada con sí misma', + 'The exact same link already exists' => 'El mismo enlace ya existe', + 'Recurrent task is scheduled to be generated' => 'Tarea recurrente programada para ser generada', + 'Recurring information' => 'Información recurrente', + 'Score' => 'Puntaje', + 'The identifier must be unique' => 'El identificador debe ser único', + 'This linked task id doesn\'t exists' => 'El id de tarea no existe', + 'This value must be alphanumeric' => 'Este valor debe ser alfanumérico', 'Edit recurrence' => 'Editar recurrencia', - // 'Generate recurrent task' => '', - // 'Trigger to generate recurrent task' => '', - // 'Factor to calculate new due date' => '', - // 'Timeframe to calculate new due date' => '', - // 'Base date to calculate new due date' => '', - // 'Action date' => '', - // 'Base date to calculate new due date: ' => '', - // 'This task has created this child task: ' => '', + 'Generate recurrent task' => 'Generar tarea recurrente', + 'Trigger to generate recurrent task' => 'Disparador para generar tarea recurrente', + 'Factor to calculate new due date' => 'Factor para calcular la nueva fecha de vencimiento', + 'Timeframe to calculate new due date' => 'Calendario para calcular la nueva fecha de vencimiento', + 'Base date to calculate new due date' => 'Fecha base para calcular la nueva fecha de vencimiento', + 'Action date' => 'Fecha de acción', + 'Base date to calculate new due date: ' => 'Fecha base para calcular la nueva fecha de vencimiento:', + 'This task has created this child task: ' => 'Esta tarea ha cerado esta tarea hija:', 'Day(s)' => 'Día(s)', - // 'Existing due date' => '', - // 'Factor to calculate new due date: ' => '', + 'Existing due date' => 'Fecha de vencimiento existente', + 'Factor to calculate new due date: ' => 'Factor para calcular la nueva fecha de vencimiento:', 'Month(s)' => 'Mes(es)', 'Recurrence' => 'Recurrencia', 'This task has been created by: ' => 'Esta tarea ha sido creada por: ', - // 'Recurrent task has been generated:' => '', - // 'Timeframe to calculate new due date: ' => '', - // 'Trigger to generate recurrent task: ' => '', + 'Recurrent task has been generated:' => 'Tarea recurrente generada:', + 'Timeframe to calculate new due date: ' => 'Calendario para calcular la nueva fecha de vencimiento:', + 'Trigger to generate recurrent task: ' => 'Disparador para generar tarea recurrente', 'When task is closed' => 'Cuando la tarea es cerrada', 'When task is moved from first column' => 'Cuando la tarea es movida a la primer columna', 'When task is moved to last column' => 'Cuando la tarea es movida a la última columna', @@ -861,8 +833,8 @@ return array( 'Jabber domain' => 'Dominio Jabber', 'Jabber nickname' => 'Apodo Jabber', 'Multi-user chat room' => 'Sala de chat multiusuario', - // 'Help on Jabber integration' => '', - // 'The server address must use this format: "tcp://hostname:5222"' => '', + 'Help on Jabber integration' => 'Ayuda para la integración con Jabber', + 'The server address must use this format: "tcp://hostname:5222"' => 'La dirección del servidor debe usar este formato: "tcp://hostname:5222"', 'Calendar settings' => 'Parámetros del Calendario', 'Project calendar view' => 'Vista de Calendario para el Proyecto', 'Project settings' => 'Parámetros del Proyecto', @@ -875,13 +847,13 @@ return array( 'iCal feed' => 'Fuente iCal', 'Preferences' => 'Preferencias', 'Security' => 'Seguridad', - // 'Two factor authentication disabled' => '', - // 'Two factor authentication enabled' => '', + 'Two factor authentication disabled' => 'Autenticación de dos factores deshabilitado', + 'Two factor authentication enabled' => 'Autenticación de dos factores habilitado', 'Unable to update this user.' => 'Imposible actualizar este usuario.', 'There is no user management for private projects.' => 'No hay gestión de usuarios para proyectos privados.', - // 'User that will receive the email' => '', - // 'Email subject' => '', - // 'Date' => '', + 'User that will receive the email' => 'Usuario que recibirá el correo', + 'Email subject' => 'Asunto del correo', + 'Date' => 'Fecha', // 'By @%s on Bitbucket' => '', // 'Bitbucket Issue' => '', // 'Commit made by @%s on Bitbucket' => '', @@ -889,25 +861,25 @@ return array( // 'By @%s on Github' => '', // 'Commit made by @%s on Gitlab' => '', // 'Add a comment log when moving the task between columns' => '', - // 'Move the task to another column when the category is changed' => '', - // 'Send a task by email to someone' => '', - // 'Reopen a task' => '', + 'Move the task to another column when the category is changed' => 'Mover la tarea a otra columna cuando cambia la categoría', + 'Send a task by email to someone' => 'Enviar una tarea a alguien por correo', + 'Reopen a task' => 'Reabrir tarea', // 'Bitbucket issue opened' => '', // 'Bitbucket issue closed' => '', // 'Bitbucket issue reopened' => '', // 'Bitbucket issue assignee change' => '', // 'Bitbucket issue comment created' => '', - // 'Column change' => '', - // 'Position change' => '', - // 'Swimlane change' => '', - // 'Assignee change' => '', - // '[%s] Overdue tasks' => '', - // 'Notification' => '', + 'Column change' => 'Cambio de columna', + 'Position change' => 'Cambio de posición', + 'Swimlane change' => 'Cambio de carril', + 'Assignee change' => 'Cambio de usuario asignado', + '[%s] Overdue tasks' => '[%s] Tareas vencidas', + 'Notification' => 'Notificación', // '%s moved the task #%d to the first swimlane' => '', // '%s moved the task #%d to the swimlane "%s"' => '', - // 'Swimlane' => '', + 'Swimlane' => 'Carril', // 'Budget overview' => '', - // 'Type' => '', + 'Type' => 'Tipo', // 'There is not enough data to show something.' => '', // 'Gravatar' => '', // 'Hipchat' => '', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + 'List' => 'Lista', + 'Filter' => 'Filtro', + 'Advanced search' => 'Búsqueda avanzada', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + 'Edit Authentication' => 'Editar autenticación', + // 'Google Id' => '', + // 'Github Id' => '', + 'Remote user' => 'Usuario remoto', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index 41ebc921..79124b15 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Projektin nimi on pakollinen', 'This project must be unique' => 'Projektin nimi täytyy olla uniikki', 'The title is required' => 'Otsikko vaaditaan', - 'There is no active project, the first step is to create a new project.' => 'Aktiivista projektia ei ole, ensimmäinen vaihe on luoda uusi projekti.', 'Settings saved successfully.' => 'Asetukset tallennettu onnistuneesti.', 'Unable to save your settings.' => 'Asetusten tallentaminen epäonnistui.', 'Database optimization done.' => 'Tietokannan optimointi suoritettu.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Luomispäivä', 'Date completed' => 'Valmistumispäivä', 'Id' => 'Id', - 'Completed tasks' => 'Valmiit tehtävät', - 'Completed tasks for "%s"' => 'Suoritetut tehtävät projektille %s', '%d closed tasks' => '%d suljettua tehtävää', 'No task for this project' => 'Ei tehtävää tälle projektille', 'Public link' => 'Julkinen linkki', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Vanhentumispäivä', 'Remember Me' => 'Muista minut', 'Creation date' => 'Luomispäivä', - 'Filter by user' => 'Rajaa käyttäjän mukaan', - 'Filter by due date' => 'Rajaa deadlinen mukaan', 'Everybody' => 'Kaikki', 'Open' => 'Avoin', 'Closed' => 'Suljettu', 'Search' => 'Etsi', 'Nothing found.' => 'Ei löytynyt.', - 'Search in the project "%s"' => 'Etsi projektista "%s"', 'Due date' => 'Deadline', 'Others formats accepted: %s and %s' => 'Muut hyväksytyt muodot: %s ja %s', 'Description' => 'Kuvaus', '%d comments' => '%d kommenttia', '%d comment' => '%d kommentti', 'Email address invalid' => 'Email ei kelpaa', - 'Your Google Account is not linked anymore to your profile.' => 'Google tunnustasi ei ole enää linkattu profiiliisi', - 'Unable to unlink your Google Account.' => 'Google tunnuksen linkkaamisen poistaminen epäonnistui.', - 'Google authentication failed' => 'Google autentikointi epäonnistui', - 'Unable to link your Google Account.' => 'Google tunnuksen linkkaaminen epäonnistui.', - 'Your Google Account is linked to your profile successfully.' => 'Google tunnuksesi linkitettiin profiiliisi onnistuneesti.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Sähköposti', 'Link my Google Account' => 'Linkitä Google-tili', 'Unlink my Google Account' => 'Poista Google-tilin linkitys', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Kategorian nimi', 'Add a new category' => 'Lisää uusi kategoria', 'Do you really want to remove this category: "%s"?' => 'Haluatko varmasti poistaa kategorian: "%s"?', - 'Filter by category' => 'Rajaa kategorian mukaan', 'All categories' => 'Kaikki kategoriat', 'No category' => 'Kategoriaa ei löydy', 'The name is required' => 'Nimi vaaditaan', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maksimikoko: ', 'Unable to upload the file.' => 'Tiedoston lataus epäonnistui.', 'Display another project' => 'Näytä toinen projekti', - 'Your GitHub account was successfully linked to your profile.' => 'Github-tilisi on onnistuneesti liitetty profiiliisi', - 'Unable to link your GitHub Account.' => 'Github-tilin liittäminen epäonnistui', - 'GitHub authentication failed' => 'Github-todennus epäonnistui', - 'Your GitHub account is no longer linked to your profile.' => 'Github-tiliäsi ei ole enää liitetty profiiliisi.', - 'Unable to unlink your GitHub Account.' => 'Github-tilisi liitoksen poisto epäonnistui', - 'Login with my GitHub Account' => 'Kirjaudu sisään Github-tililläni', - 'Link my GitHub Account' => 'Liitä Github-tilini', - 'Unlink my GitHub Account' => 'Poista liitos Github-tiliini', + 'Login with my Github Account' => 'Kirjaudu sisään Github-tililläni', + 'Link my Github Account' => 'Liitä Github-tilini', + 'Unlink my Github Account' => 'Poista liitos Github-tiliini', 'Created by %s' => 'Luonut: %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Viimeksi muokattu %B %e, %Y kello %H:%M', 'Tasks Export' => 'Tehtävien vienti', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s päivitti kommentia tehtävässä %s', '%s commented the task %s' => '%s kommentoi tehtävää %s', '%s\'s activity' => 'Henkilön %s toiminta', - 'No activity.' => 'Ei toimintaa.', 'RSS feed' => 'RSS-syöte', '%s updated a comment on the task #%d' => '%s päivitti kommenttia tehtävässä #%d', '%s commented on the task #%d' => '%s kommentoi tehtävää #%d', @@ -605,14 +591,9 @@ return array( // 'Language:' => '', // 'Timezone:' => '', // 'All columns' => '', - // 'Calendar for "%s"' => '', - // 'Filter by column' => '', - // 'Filter by status' => '', // 'Calendar' => '', // 'Next' => '', // '#%d' => '', - // 'Filter by color' => '', - // 'Filter by swimlane' => '', // 'All swimlanes' => '', // 'All colors' => '', // 'All status' => '', @@ -627,14 +608,7 @@ return array( // 'Time Tracking' => '', // 'You already have one subtask in progress' => '', // 'Which parts of the project do you want to duplicate?' => '', - // 'Change dashboard view' => '', - // 'Show/hide activities' => '', - // 'Show/hide projects' => '', - // 'Show/hide subtasks' => '', - // 'Show/hide tasks' => '', - // 'Disable login form' => '', - // 'Show/hide calendar' => '', - // 'User calendar' => '', + // 'Disallow login form' => '', // 'Bitbucket commit received' => '', // 'Bitbucket webhooks' => '', // 'Help on Bitbucket webhooks' => '', @@ -688,9 +662,7 @@ return array( // 'Keyboard shortcuts' => '', // 'Open board switcher' => '', // 'Application' => '', - // 'Filter recently updated' => '', // 'since %B %e, %Y at %k:%M %p' => '', - // 'More filters' => '', // 'Compact view' => '', // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index c96fecb9..81159fcf 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Le nom du projet est obligatoire', 'This project must be unique' => 'Le nom du projet doit être unique', 'The title is required' => 'Le titre est obligatoire', - 'There is no active project, the first step is to create a new project.' => 'Il n\'y a aucun projet actif, la première étape est de créer un nouveau projet.', 'Settings saved successfully.' => 'Paramètres sauvegardés avec succès.', 'Unable to save your settings.' => 'Impossible de sauvegarder vos réglages.', 'Database optimization done.' => 'Optmisation de la base de données terminée.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Date de création', 'Date completed' => 'Date de clôture', 'Id' => 'Identifiant', - 'Completed tasks' => 'Tâches terminées', - 'Completed tasks for "%s"' => 'Tâches terminées pour « %s »', '%d closed tasks' => '%d tâches terminées', 'No task for this project' => 'Aucune tâche pour ce projet', 'Public link' => 'Lien public', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Date d\'expiration', 'Remember Me' => 'Connexion automatique', 'Creation date' => 'Date de création', - 'Filter by user' => 'Filtrer par utilisateur', - 'Filter by due date' => 'Avec une date d\'échéance', 'Everybody' => 'Tout le monde', 'Open' => 'Ouvert', 'Closed' => 'Fermé', 'Search' => 'Rechercher', 'Nothing found.' => 'Rien trouvé.', - 'Search in the project "%s"' => 'Rechercher dans le projet « %s »', 'Due date' => 'Date d\'échéance', 'Others formats accepted: %s and %s' => 'Autres formats acceptés : %s et %s', 'Description' => 'Description', '%d comments' => '%d commentaires', '%d comment' => '%d commentaire', 'Email address invalid' => 'Adresse email invalide', - 'Your Google Account is not linked anymore to your profile.' => 'Votre compte Google n\'est plus relié à votre profile.', - 'Unable to unlink your Google Account.' => 'Impossible de supprimer votre compte Google.', - 'Google authentication failed' => 'Authentification Google échouée', - 'Unable to link your Google Account.' => 'Impossible de lier votre compte Google.', - 'Your Google Account is linked to your profile successfully.' => 'Votre compte Google est désormais lié à votre profile.', + 'Your external account is not linked anymore to your profile.' => 'Votre compte externe n\'est plus relié à votre profile.', + 'Unable to unlink your external account.' => 'Impossible de supprimer votre compte externe.', + 'External authentication failed' => 'Authentification externe échouée', + 'Your external account is linked to your profile successfully.' => 'Votre compte externe est désormais lié à votre profile.', 'Email' => 'Email', 'Link my Google Account' => 'Lier mon compte Google', 'Unlink my Google Account' => 'Ne plus utiliser mon compte Google', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Nom de la catégorie', 'Add a new category' => 'Ajouter une nouvelle catégorie', 'Do you really want to remove this category: "%s"?' => 'Voulez-vous vraiment supprimer cette catégorie « %s » ?', - 'Filter by category' => 'Filtrer par catégorie', 'All categories' => 'Toutes les catégories', 'No category' => 'Aucune catégorie', 'The name is required' => 'Le nom est requis', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Taille maximum : ', 'Unable to upload the file.' => 'Impossible de transférer le fichier.', 'Display another project' => 'Afficher un autre projet', - 'Your GitHub account was successfully linked to your profile.' => 'Votre compte Github est désormais lié avec votre profile.', - 'Unable to link your GitHub Account.' => 'Impossible de lier votre compte Github.', - 'GitHub authentication failed' => 'L\'authentification avec Github à échouée', - 'Your GitHub account is no longer linked to your profile.' => 'Votre compte Github n\'est plus relié avec votre profile.', - 'Unable to unlink your GitHub Account.' => 'Impossible de déconnecter votre compte Github.', - 'Login with my GitHub Account' => 'Se connecter avec mon compte Github', - 'Link my GitHub Account' => 'Lier mon compte Github', - 'Unlink my GitHub Account' => 'Ne plus utiliser mon compte Github', + 'Login with my Github Account' => 'Se connecter avec mon compte Github', + 'Link my Github Account' => 'Lier mon compte Github', + 'Unlink my Github Account' => 'Ne plus utiliser mon compte Github', 'Created by %s' => 'Créé par %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Modifié le %d/%m/%Y à %H:%M', 'Tasks Export' => 'Exportation des tâches', @@ -447,7 +434,6 @@ return array( '%s updated a comment on the task %s' => '%s a mis à jour un commentaire appartenant à la tâche %s', '%s commented the task %s' => '%s a ajouté un commentaire sur la tâche %s', '%s\'s activity' => 'Activité du projet %s', - 'No activity.' => 'Aucune activité.', 'RSS feed' => 'Flux RSS', '%s updated a comment on the task #%d' => '%s a mis à jour un commentaire sur la tâche n°%d', '%s commented on the task #%d' => '%s a ajouté un commentaire sur la tâche n°%d', @@ -607,14 +593,9 @@ return array( 'Language:' => 'Langue :', 'Timezone:' => 'Fuseau horaire :', 'All columns' => 'Toutes les colonnes', - 'Calendar for "%s"' => 'Agenda pour le projet « %s »', - 'Filter by column' => 'Filtrer par colonne', - 'Filter by status' => 'Filtrer par status', 'Calendar' => 'Agenda', 'Next' => 'Suivant', '#%d' => 'n˚%d', - 'Filter by color' => 'Filtrer par couleur', - 'Filter by swimlane' => 'Filtrer par swimlanes', 'All swimlanes' => 'Toutes les swimlanes', 'All colors' => 'Toutes les couleurs', 'All status' => 'Tous les états', @@ -629,14 +610,7 @@ return array( 'Time Tracking' => 'Feuille de temps', 'You already have one subtask in progress' => 'Vous avez déjà une sous-tâche en progrès', 'Which parts of the project do you want to duplicate?' => 'Quelles parties du projet voulez-vous dupliquer ?', - 'Change dashboard view' => 'Changer la vue du tableau de bord', - 'Show/hide activities' => 'Afficher/cacher les activités', - 'Show/hide projects' => 'Afficher/cacher les projets', - 'Show/hide subtasks' => 'Afficher/cacher les sous-tâches', - 'Show/hide tasks' => 'Afficher/cacher les tâches', - 'Disable login form' => 'Désactiver le formulaire d\'authentification', - 'Show/hide calendar' => 'Afficher/cacher le calendrier', - 'User calendar' => 'Calendrier de l\'utilisateur', + 'Disallow login form' => 'Interdir le formulaire d\'authentification', 'Bitbucket commit received' => 'Commit reçu via Bitbucket', 'Bitbucket webhooks' => 'Webhook Bitbucket', 'Help on Bitbucket webhooks' => 'Aide sur les webhooks Bitbucket', @@ -690,9 +664,7 @@ return array( 'Keyboard shortcuts' => 'Raccourcis clavier', 'Open board switcher' => 'Ouvrir le sélecteur de tableau', 'Application' => 'Application', - 'Filter recently updated' => 'Récemment modifié', 'since %B %e, %Y at %k:%M %p' => 'depuis le %d/%m/%Y à %H:%M', - 'More filters' => 'Plus de filtres', 'Compact view' => 'Vue compacte', 'Horizontal scrolling' => 'Défilement horizontal', 'Compact/wide view' => 'Basculer entre la vue compacte et étendue', @@ -919,11 +891,10 @@ return array( 'This report contains all subtasks information for the given date range.' => 'Ce rapport contient les informations de toutes les sous-tâches pour la période selectionnée.', 'This report contains all tasks information for the given date range.' => 'Ce rapport contient les informations de toutes les tâches pour la période selectionnée.', 'Project activities for %s' => 'Activité des projets pour « %s »', - 'view the board on Kanboard' => 'voir la tableau sur Kanboard', + 'view the board on Kanboard' => 'voir le tableau sur Kanboard', 'The task have been moved to the first swimlane' => 'La tâche a été déplacée dans la première swimlane', 'The task have been moved to another swimlane:' => 'La tâche a été déplacée dans une autre swimlane :', 'Overdue tasks for the project "%s"' => 'Tâches en retard pour le projet « %s »', - 'There is no completed tasks at the moment.' => 'Il n\'y a aucune tâche terminée pour le moment.', 'New title: %s' => 'Nouveau titre : %s', 'The task is not assigned anymore' => 'La tâche n\'est plus assignée maintenant', 'New assignee: %s' => 'Nouvel assigné : %s', @@ -940,7 +911,6 @@ return array( 'The description have 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 ?', 'Swimlane: %s' => 'Swimlane : %s', - 'Project calendar' => 'Agenda du projet', 'I want to receive notifications for:' => 'Je veux reçevoir les notifications pour :', 'All tasks' => 'Toutes les Tâches', 'Only for tasks assigned to me' => 'Seulement les tâches qui me sont assignées', @@ -960,4 +930,79 @@ return array( 'Start timer' => 'Démarrer le chrono', 'Add project member' => 'Ajouter un membre au projet', 'Enable notifications' => 'Activer les notifications', + 'My activity stream' => 'Mon flux d\'activité', + 'My calendar' => 'Mon agenda', + 'Search tasks' => 'Rechercher des tâches', + 'Back to the calendar' => 'Retour au calendrier', + 'Filters' => 'Filtres', + 'Reset filters' => 'Réinitialiser les filtres', + '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 tomorrow' => 'Tâches qui arrivent à échéance demain', + 'Tasks due yesterday' => 'Tâches qui sont arrivées à échéance hier', + 'Closed tasks' => 'Tâches fermées', + 'Open tasks' => 'Tâches ouvertes', + 'Not assigned' => 'Non assignées', + 'View advanced search syntax' => 'Voir la syntaxe pour la recherche avancée', + 'Overview' => 'Vue d\'ensemble', + '%b %e %Y' => '%b %e %Y', + 'Board/Calendar/List view' => 'Vue Tableau/Calendrier/Liste', + 'Switch to the board view' => 'Basculer vers le tableau', + 'Switch to the calendar view' => 'Basculer vers le calendrier', + 'Switch to the list view' => 'Basculer vers la vue en liste', + 'Go to the search/filter box' => 'Aller au champ de recherche', + 'There is no activity yet.' => 'Il n\'y a pas encore d\'activité.', + 'No tasks found.' => 'Aucune tâche trouvée.', + 'Keyboard shortcut: "%s"' => 'Raccourci clavier : « %s »', + 'List' => 'Liste', + 'Filter' => 'Filtre', + 'Advanced search' => 'Recherche avancée', + 'Example of query: ' => 'Exemple de requête : ', + 'Search by project: ' => 'Rechercher par projet : ', + 'Search by column: ' => 'Rechercher par colonne : ', + 'Search by assignee: ' => 'Rechercher par assigné : ', + 'Search by color: ' => 'Rechercher par couleur : ', + 'Search by category: ' => 'Rechercher par catégorie : ', + 'Search by description: ' => 'Rechercher par description : ', + 'Search by due date: ' => 'Rechercher par date d\'échéance : ', + 'Lead and Cycle time for "%s"' => 'Lead et cycle time pour « %s »', + 'Average time spent into each column for "%s"' => 'Temps passé moyen dans chaque colonne pour « %s »', + 'Average time spent into each column' => 'Temps moyen passé dans chaque colonne', + 'Average time spent' => 'Temps moyen passé', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Ce graphique montre le temps passé moyen dans chaque colonne pour les %d dernières tâches.', + 'Average Lead and Cycle time' => 'Durée moyenne du lead et cycle time', + 'Average lead time: ' => 'Lead time moyen : ', + 'Average cycle time: ' => 'Cycle time moyen : ', + 'Cycle Time' => 'Cycle time', + 'Lead Time' => 'Lead time', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Ce graphique montre la durée moyenne du lead et cycle time pour les %d dernières tâches.', + 'Average time into each column' => 'Temps moyen dans chaque colonne', + 'Lead and cycle time' => 'Lead et cycle time', + 'Google Authentication' => 'Authentification Google', + 'Help on Google authentication' => 'Aide sur l\'authentification Google', + 'Github Authentication' => 'Authentification Github', + 'Help on Github authentication' => 'Aide sur l\'authentification Github', + 'Channel/Group/User (Optional)' => 'Cannal/Groupe/Utilisateur (Optionnel)', + 'Lead time: ' => 'Lead time : ', + 'Cycle time: ' => 'Temps de cycle : ', + 'Time spent into each column' => 'Temps passé dans chaque colonne', + 'The lead time is the duration between the task creation and the completion.' => 'Le lead time est la durée entre la création de la tâche et sa complétion.', + 'The cycle time is the duration between the start date and the completion.' => 'Le cycle time est la durée entre la date de début et la complétion.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tâche n\'est pas fermée, l\'heure courante est utilisée à la place de la date de complétion.', + 'Set automatically the start date' => 'Définir automatiquement la date de début', + 'Edit Authentication' => 'Modifier l\'authentification', + 'Google Id' => 'Identifiant Google', + 'Github Id' => 'Identifiant Github', + 'Remote user' => 'Utilisateur distant', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Les utilisateurs distants ne stockent pas leur mot de passe dans la base de données de Kanboard, exemples : comptes LDAP, Github ou Google.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si vous cochez la case « Interdir le formulaire d\'authentification », les identifiants entrés dans le formulaire d\'authentification seront ignorés.', + 'By @%s on Gitlab' => 'Par @%s sur Gitlab', + 'Gitlab issue comment created' => 'Commentaire créé sur un ticket Gitlab', + 'New remote user' => 'Créer un utilisateur distant', + 'New local user' => 'Créer un utilisateur local', + 'Default task color' => 'Couleur par défaut des tâches', + 'Hide sidebar' => 'Cacher la barre latérale', + 'Expand sidebar' => 'Déplier la barre latérale', + 'This feature does not work with all browsers.' => 'Cette fonctionnalité n\'est pas compatible avec tous les navigateurs', + 'There is no destination project available.' => 'Il n\'y a pas de projet de destination disponible.', ); diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index 9ca5c3fd..cd2bca0a 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'A projekt nevét meg kell adni', 'This project must be unique' => 'A projekt nevének egyedinek kell lennie', 'The title is required' => 'A címet meg kell adni', - 'There is no active project, the first step is to create a new project.' => 'Nincs aktív projekt. Először létre kell hozni egy projektet.', 'Settings saved successfully.' => 'A beállítások sikeresen mentve.', 'Unable to save your settings.' => 'A beállítások mentése sikertelen.', 'Database optimization done.' => 'Adatbázis optimalizálás kész.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Létrehozás időpontja', 'Date completed' => 'Befejezés időpontja', 'Id' => 'ID', - 'Completed tasks' => 'Elvégzett feladatok', - 'Completed tasks for "%s"' => 'Elvégzett feladatok: %s', '%d closed tasks' => '%d lezárt feladat', 'No task for this project' => 'Nincs feladat ebben a projektben', 'Public link' => 'Nyilvános link', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Lejárati dátum', 'Remember Me' => 'Emlékezz rám', 'Creation date' => 'Létrehozás dátuma', - 'Filter by user' => 'Szűrés felhasználó szerint', - 'Filter by due date' => 'Szűrés határidő szerint', 'Everybody' => 'Minden felhasználó', 'Open' => 'Nyitott', 'Closed' => 'Lezárt', 'Search' => 'Keresés', 'Nothing found.' => 'Nincs találat.', - 'Search in the project "%s"' => 'Keresés a projektben: "%s"', 'Due date' => 'Határidő', 'Others formats accepted: %s and %s' => 'Egyéb érvényes formátumok: "%s" és "%s"', 'Description' => 'Leírás', '%d comments' => '%d megjegyzés', '%d comment' => '%d megjegyzés', 'Email address invalid' => 'Érvénytelen e-mail cím', - 'Your Google Account is not linked anymore to your profile.' => 'Google Fiók már nincs a profilhoz kapcsolva.', - 'Unable to unlink your Google Account.' => 'Leválasztás a Google fiókról nem lehetséges.', - 'Google authentication failed' => 'Google azonosítás sikertelen', - 'Unable to link your Google Account.' => 'A Google profilhoz kapcsolás sikertelen.', - 'Your Google Account is linked to your profile successfully.' => 'Google fiókkal sikeresen összekapcsolva.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-mail', 'Link my Google Account' => 'Kapcsold össze a Google fiókkal', 'Unlink my Google Account' => 'Válaszd le a Google fiókomat', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Kategória neve', 'Add a new category' => 'Új kategória', 'Do you really want to remove this category: "%s"?' => 'Valóban törölni akarja ezt a kategóriát: "%s"?', - 'Filter by category' => 'Szűrés kategória szerint', 'All categories' => 'Minden kategória', 'No category' => 'Nincs kategória', 'The name is required' => 'A név megadása kötelező', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maximális méret: ', 'Unable to upload the file.' => 'Fájl feltöltése nem lehetséges.', 'Display another project' => 'Másik projekt megjelenítése', - 'Your GitHub account was successfully linked to your profile.' => 'GitHub fiók sikeresen csatolva a profilhoz.', - 'Unable to link your GitHub Account.' => 'Nem lehet csatolni a GitHub fiókot.', - 'GitHub authentication failed' => 'GitHub azonosítás sikertelen', - 'Your GitHub account is no longer linked to your profile.' => 'GitHub fiók már nincs profilhoz kapcsolva.', - 'Unable to unlink your GitHub Account.' => 'GitHub fiók leválasztása nem lehetséges.', - 'Login with my GitHub Account' => 'Jelentkezzen be GitHub fiókkal', - 'Link my GitHub Account' => 'GitHub fiók csatolása', - 'Unlink my GitHub Account' => 'GitHub fiók leválasztása', + 'Login with my Github Account' => 'Jelentkezzen be Github fiókkal', + 'Link my Github Account' => 'Github fiók csatolása', + 'Unlink my Github Account' => 'Github fiók leválasztása', 'Created by %s' => 'Készítette: %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Utolsó módosítás: %Y. %m. %d. %H:%M', 'Tasks Export' => 'Feladatok exportálása', @@ -409,7 +396,7 @@ return array( 'Enabled' => 'Engedélyezve', 'Disabled' => 'Letiltva', 'Google account linked' => 'Google fiók összekapcsolva', - 'Github account linked' => 'GitHub fiók összekapcsolva', + 'Github account linked' => 'Github fiók összekapcsolva', 'Username:' => 'Felhasználónév:', 'Name:' => 'Név:', 'Email:' => 'E-mail:', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s frissítette a megjegyzését a feladatban %s', '%s commented the task %s' => '%s megjegyzést fűzött a feladathoz %s', '%s\'s activity' => 'Tevékenységek: %s', - 'No activity.' => 'Nincs tevékenység.', 'RSS feed' => 'RSS feed', '%s updated a comment on the task #%d' => '%s frissített egy megjegyzést a feladatban #%d', '%s commented on the task #%d' => '%s megjegyzést tett a feladathoz #%d', @@ -465,12 +451,12 @@ return array( '%s changed the assignee of the task %s to %s' => '%s a felelőst %s módosította: %s', 'New password for the user "%s"' => 'Felhasználó új jelszava: %s', 'Choose an event' => 'Válasszon eseményt', - 'Github commit received' => 'GitHub commit érkezett', - 'Github issue opened' => 'GitHub issue nyitás', - 'Github issue closed' => 'GitHub issue zárás', - 'Github issue reopened' => 'GitHub issue újranyitva', - 'Github issue assignee change' => 'GitHub issue felelős változás', - 'Github issue label change' => 'GitHub issue címke változás', + 'Github commit received' => 'Github commit érkezett', + 'Github issue opened' => 'Github issue nyitás', + 'Github issue closed' => 'Github issue zárás', + 'Github issue reopened' => 'Github issue újranyitva', + 'Github issue assignee change' => 'Github issue felelős változás', + 'Github issue label change' => 'Github issue címke változás', 'Create a task from an external provider' => 'Feladat létrehozása külsős számára', 'Change the assignee based on an external username' => 'Felelős módosítása külső felhasználónév alapján', 'Change the category based on an external label' => 'Kategória módosítása külső címke alapján', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Nyelv:', 'Timezone:' => 'Időzóna:', 'All columns' => 'Minden oszlop', - 'Calendar for "%s"' => 'Naptár: %s', - 'Filter by column' => 'Szűrés oszlop szerint', - 'Filter by status' => 'Szűrés állapot szerint', 'Calendar' => 'Naptár', 'Next' => 'Következő', '#%d' => '#%d', - 'Filter by color' => 'Szűrés szín szerint', - 'Filter by swimlane' => 'Szűrés folyamat szerint', 'All swimlanes' => 'Minden folyamat', 'All colors' => 'Minden szín', 'All status' => 'Minden állapot', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Idő követés', 'You already have one subtask in progress' => 'Már van egy folyamatban levő részfeladata', 'Which parts of the project do you want to duplicate?' => 'A projekt mely részeit szeretné másolni?', - 'Change dashboard view' => 'Vezérlőpult megjelenés változtatás', - 'Show/hide activities' => 'Tevékenységek megjelenítése/elrejtése', - 'Show/hide projects' => 'Projektek megjelenítése/elrejtése', - 'Show/hide subtasks' => 'Részfeladatok megjelenítése/elrejtése', - 'Show/hide tasks' => 'Feladatok megjelenítése/elrejtése', - 'Disable login form' => 'Bejelentkező képernyő tiltása', - 'Show/hide calendar' => 'Naptár megjelenítés/elrejtés', - 'User calendar' => 'Naptár', + // 'Disallow login form' => '', 'Bitbucket commit received' => 'Bitbucket commit érkezett', 'Bitbucket webhooks' => 'Bitbucket webhooks', 'Help on Bitbucket webhooks' => 'Bitbucket webhooks súgó', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Billentyű kombinációk', 'Open board switcher' => 'Tábla választó lenyitása', 'Application' => 'Alkalmazás', - 'Filter recently updated' => 'Szűrés az utolsó módosítás ideje szerint', 'since %B %e, %Y at %k:%M %p' => '%Y. %m. %d. %H:%M óta', - 'More filters' => 'További szűrők', 'Compact view' => 'Kompakt nézet', 'Horizontal scrolling' => 'Vízszintes görgetés', 'Compact/wide view' => 'Kompakt/széles nézet', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index e602208d..353630c3 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Si richiede il nome del progetto', 'This project must be unique' => 'Il nome del progetto deve essere unico', 'The title is required' => 'Si richiede un titolo', - 'There is no active project, the first step is to create a new project.' => 'Non ci sono progetti attivi, il primo passo consiste in creare un nuovo progetto.', 'Settings saved successfully.' => 'Impostazioni salvate correttamente.', 'Unable to save your settings.' => 'Non si possono salvare le impostazioni.', 'Database optimization done.' => 'Ottimizzazione della base dati conclusa.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Data di creazione', 'Date completed' => 'Data di termine', 'Id' => 'Identificatore', - 'Completed tasks' => 'Compiti fatti', - 'Completed tasks for "%s"' => 'Compiti fatti da « %s »', '%d closed tasks' => '%d compiti chiusi', 'No task for this project' => 'Nessun compito per questo progetto', 'Public link' => 'Link pubblico', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Data di scadenza', 'Remember Me' => 'Ricordami', 'Creation date' => 'Data di creazione', - 'Filter by user' => 'Filtrato mediante utente', - 'Filter by due date' => 'Filtrare attraverso data di scadenza', 'Everybody' => 'Tutti', 'Open' => 'Aperto', 'Closed' => 'Chiuso', 'Search' => 'Cercare', 'Nothing found.' => 'Non si è trovato nulla.', - 'Search in the project "%s"' => 'Cercare nel progetto "%s"', 'Due date' => 'Data di scadenza', 'Others formats accepted: %s and %s' => 'Altri formati accettati: %s y %s', 'Description' => 'Descrizione', '%d comments' => '%d commenti', '%d comment' => '%d commento', 'Email address invalid' => 'Indirizzo e-mail sbagliato', - 'Your Google Account is not linked anymore to your profile.' => 'Il suo account Google non è più collegato al suo profilo', - 'Unable to unlink your Google Account.' => 'Non si può svincolare l\'account di Google.', - 'Google authentication failed' => 'Autenticazione con Google non riuscita', - 'Unable to link your Google Account.' => 'Non si può collegare il tuo account di Google.', - 'Your Google Account is linked to your profile successfully.' => 'Il tuo account di Google è stato collegato correttamente al tuo profilo.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-mail', 'Link my Google Account' => 'Collegare il mio Account di Google', 'Unlink my Google Account' => 'Scollegare il mio account di Google', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Nome di categoria', 'Add a new category' => 'Aggiungere una nuova categoria', 'Do you really want to remove this category: "%s"?' => 'Vuoi veramente cancellare questa categoria: "%s"?', - 'Filter by category' => 'Filtrare attraverso categoria', 'All categories' => 'Tutte le categorie', 'No category' => 'Senza categoria', 'The name is required' => 'Si richiede un nome', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Dimensioni massime', 'Unable to upload the file.' => 'Non si può caricare il file.', 'Display another project' => 'Mostrare un altro progetto', - 'Your GitHub account was successfully linked to your profile.' => 'Il suo account di Github è stato collegato correttamente col tuo profilo.', - 'Unable to link your GitHub Account.' => 'Non si può collegarre il tuo account di Github.', - 'GitHub authentication failed' => 'Autenticazione con GitHub non riuscita', - 'Your GitHub account is no longer linked to your profile.' => 'Il tuo account di Github non è più collegato al tuo profilo.', - 'Unable to unlink your GitHub Account.' => 'Non si può collegare il tuo account di Github.', - 'Login with my GitHub Account' => 'Entrare col tuo account di Github', - 'Link my GitHub Account' => 'Collegare il mio account Github', - 'Unlink my GitHub Account' => 'Scollegare il mio account di Github', + 'Login with my Github Account' => 'Entrare col tuo account di Github', + 'Link my Github Account' => 'Collegare il mio account Github', + 'Unlink my Github Account' => 'Scollegare il mio account di Github', 'Created by %s' => 'Creato da %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Ultima modifica il %d/%m/%Y alle %H:%M', 'Tasks Export' => 'Esportazione di compiti', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s ha aggiornato un commento del compito %s', '%s commented the task %s' => '%s ha commentato il compito %s', '%s\'s activity' => 'Attività di %s', - 'No activity.' => 'Nessuna attività.', 'RSS feed' => 'Feed RSS', '%s updated a comment on the task #%d' => '%s ha aggiornato un commento del compito #%d', '%s commented on the task #%d' => '%s ha commentato il compito #%d', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Lingua', 'Timezone:' => 'Fuso Orario', 'All columns' => 'Tutte le colonne', - 'Calendar for "%s"' => 'Calendario per "%s"', - 'Filter by column' => 'Filtra per colonna', - 'Filter by status' => 'Filtra per status', 'Calendar' => 'Calendario', 'Next' => 'Prossimo', // '#%d' => '', - 'Filter by color' => 'Filtra per colore', - 'Filter by swimlane' => 'Filtra per corsia', 'All swimlanes' => 'Tutte le corsie', 'All colors' => 'Tutti i colori', 'All status' => 'Tutti gli stati', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Gestione del tempo', 'You already have one subtask in progress' => 'Hai già un sotto-compito in progresso', 'Which parts of the project do you want to duplicate?' => 'Quali parti del progetto vuoi duplicare?', - 'Change dashboard view' => 'Cambia la vista della bacheca', - 'Show/hide activities' => 'Mostra/nascondi attività', - 'Show/hide projects' => 'Mostra/nascondi progetti', - 'Show/hide subtasks' => 'Mostra/nascondi sotto-compiti', - 'Show/hide tasks' => 'Mostra/nascondi compiti', - 'Disable login form' => 'Disabilita form di login', - 'Show/hide calendar' => 'Mostra/nascondi calendario', - 'User calendar' => 'Calendario utente', + // 'Disallow login form' => '', 'Bitbucket commit received' => 'Commit ricevuto da Bitbucket', 'Bitbucket webhooks' => 'Webhooks di Bitbucket', 'Help on Bitbucket webhooks' => 'Guida ai Webhooks di Bitbucket', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Scorciatoie da tastiera', 'Open board switcher' => 'Apri il selezionatore di bacheche', 'Application' => 'Applicazione', - 'Filter recently updated' => 'Filtri recentemente aggiornati', 'since %B %e, %Y at %k:%M %p' => 'dal %B %e, %Y alle %k:%M %p', - 'More filters' => 'Più filtri', 'Compact view' => 'Vista compatta', 'Horizontal scrolling' => 'Scrolling orizzontale', 'Compact/wide view' => 'Vista compatta/estesa', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index 9397b9b9..636df9a5 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'プロジェクト名が必要です', 'This project must be unique' => 'プロジェクト名がすでに使われています', 'The title is required' => 'タイトルが必要です', - 'There is no active project, the first step is to create a new project.' => '有効なプロジェクトがありません。まず新しいプロジェクトを作ります。', 'Settings saved successfully.' => '設定を保存しました。', 'Unable to save your settings.' => '設定の保存に失敗しました。', 'Database optimization done.' => 'データベースの最適化が終わりました。', @@ -165,8 +164,6 @@ return array( 'Date created' => '作成日', 'Date completed' => '完了日', 'Id' => 'ID', - 'Completed tasks' => '完了したタスク', - 'Completed tasks for "%s"' => '「%s」の完了したタスク', '%d closed tasks' => '%d 個のクローズしたタスク', 'No task for this project' => 'このプロジェクトにタスクがありません', 'Public link' => '公開アクセス用リンク', @@ -255,25 +252,21 @@ return array( 'Expiration date' => '有効期限', 'Remember Me' => '次回から自動的にログインする', 'Creation date' => '作成日', - 'Filter by user' => 'ユーザでフィルタリング', - 'Filter by due date' => '期限でフィルタリング', 'Everybody' => '全員', 'Open' => 'オープン', 'Closed' => 'クローズ', 'Search' => '検索', 'Nothing found.' => '結果なし。', - 'Search in the project "%s"' => 'プロジェクト「%s」で検索', 'Due date' => '期限', 'Others formats accepted: %s and %s' => '他の書式: %s または %s', 'Description' => '説明', '%d comments' => '%d 個のコメント', '%d comment' => '%d 個のコメント', 'Email address invalid' => 'メールアドレスが正しくありません', - 'Your Google Account is not linked anymore to your profile.' => 'Google アカウントとのリンクが解除されました', - 'Unable to unlink your Google Account.' => 'Google アカウントとのリンク解除に失敗しました', - 'Google authentication failed' => 'Google の認証に失敗しました', - 'Unable to link your Google Account.' => 'Google アカウントとのリンクに失敗しました。', - 'Your Google Account is linked to your profile successfully.' => 'Google アカウントとリンクしました', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Email', 'Link my Google Account' => 'Google アカウントをリンクする', 'Unlink my Google Account' => 'Google アカウントのリンクを解除する', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'カテゴリ名', 'Add a new category' => 'カテゴリの追加', 'Do you really want to remove this category: "%s"?' => 'カテゴリ「%s」を削除しますか?', - 'Filter by category' => 'カテゴリでフィルタリング', 'All categories' => 'すべてのカテゴリ', 'No category' => 'カテゴリなし', 'The name is required' => '名前を入力してください', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => '最大: ', 'Unable to upload the file.' => 'ファイルのアップロードに失敗しました。', 'Display another project' => '別のプロジェクトを表示', - 'Your GitHub account was successfully linked to your profile.' => 'GitHub アカウントとリンクしました。', - 'Unable to link your GitHub Account.' => 'GitHub アカウントとリンクできませんでした。', - 'GitHub authentication failed' => 'GitHub アカウントの認証に失敗しました。', - 'Your GitHub account is no longer linked to your profile.' => 'GitHub アカウントへのリンクが解除されました。', - 'Unable to unlink your GitHub Account.' => 'GitHub アカウントのリンク解除に失敗しました。', - 'Login with my GitHub Account' => 'Github アカウントでログインする', - 'Link my GitHub Account' => 'Github アカウントをリンクする', - 'Unlink my GitHub Account' => 'Github アカウントとのリンクを解除する', + 'Login with my Github Account' => 'Github アカウントでログインする', + 'Link my Github Account' => 'Github アカウントをリンクする', + 'Unlink my Github Account' => 'Github アカウントとのリンクを解除する', 'Created by %s' => '%s が作成', 'Last modified on %B %e, %Y at %k:%M %p' => ' %Y/%m/%d %H:%M に変更', 'Tasks Export' => 'タスクの出力', @@ -409,7 +396,7 @@ return array( 'Enabled' => '有効', 'Disabled' => '無効', 'Google account linked' => 'Google アカウントがリンク', - 'Github account linked' => 'GitHub のアカウントがリンク', + 'Github account linked' => 'Github のアカウントがリンク', 'Username:' => 'ユーザ名:', 'Name:' => '名前:', 'Email:' => 'Email:', @@ -423,7 +410,7 @@ return array( 'Password modification' => 'パスワードの変更', 'External authentications' => '外部認証', 'Google Account' => 'Google アカウント', - 'Github Account' => 'GitHub アカウント', + 'Github Account' => 'Github アカウント', 'Never connected.' => '未接続。', 'No account linked.' => 'アカウントがリンクしていません。', 'Account linked.' => 'アカウントがリンクしました。', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s がタスク %s のコメントを更新しました', '%s commented the task %s' => '%s がタスク %s にコメントしました', '%s\'s activity' => '%s のアクティビティ', - 'No activity.' => 'アクティビティなし。', 'RSS feed' => 'RSS フィード', '%s updated a comment on the task #%d' => '%s がタスク #%d のコメントを更新しました', '%s commented on the task #%d' => '%s がタスク #%d にコメントしました', @@ -605,14 +591,9 @@ return array( 'Language:' => '言語:', 'Timezone:' => 'タイムゾーン:', 'All columns' => '全てのカラム', - 'Calendar for "%s"' => '「%s」のカレンダー', - 'Filter by column' => 'カラムでフィルタ', - 'Filter by status' => 'ステータスでフィルタ', 'Calendar' => 'カレンダー', 'Next' => '次へ', '#%d' => '#%d', - 'Filter by color' => '色でフィルタ', - 'Filter by swimlane' => 'スイムレーンでフィルタ', 'All swimlanes' => '全てのスイムレーン', 'All colors' => '全ての色', 'All status' => '全てのステータス', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'タイムトラッキング', 'You already have one subtask in progress' => 'すでに進行中のサブタスクがあります。', 'Which parts of the project do you want to duplicate?' => 'プロジェクトの何を複製しますか?', - 'Change dashboard view' => 'ダッシュボードビューを変更', - 'Show/hide activities' => 'アクティビティの表示・非表示', - 'Show/hide projects' => 'プロジェクトの表示・非表示', - 'Show/hide subtasks' => 'サブタスクの表示・非表示', - 'Show/hide tasks' => 'タスクの表示・非表示', - 'Disable login form' => 'ログインフォームの無効化', - 'Show/hide calendar' => 'カレンダーの表示・非表示', - 'User calendar' => 'ユーザカレンダー', + // 'Disallow login form' => '', 'Bitbucket commit received' => 'Bitbucket コミットを受信しました', 'Bitbucket webhooks' => 'Bitbucket Webhooks', 'Help on Bitbucket webhooks' => 'Bitbucket Webhooks のヘルプ', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'キーボードショートカット', 'Open board switcher' => 'ボード切り替えを開く', 'Application' => 'アプリケーション', - 'Filter recently updated' => 'フィルタがアップデートされました', 'since %B %e, %Y at %k:%M %p' => '%Y/%m/%d %k:%M から', - 'More filters' => '他のフィルタ', 'Compact view' => 'コンパクトビュー', 'Horizontal scrolling' => '縦スクロール', 'Compact/wide view' => 'コンパクト/ワイドビュー', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php index 9e4302f4..c0a6a032 100644 --- a/app/Locale/nl_NL/translations.php +++ b/app/Locale/nl_NL/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'De projectnaam is verplicht', 'This project must be unique' => 'Dit project moet uniek zijn', 'The title is required' => 'De titel is verplicht', - 'There is no active project, the first step is to create a new project.' => 'Er is geen actief project, de eerste stap is een nieuw project aanmaken.', 'Settings saved successfully.' => 'Instellingen succesvol opgeslagen.', 'Unable to save your settings.' => 'Instellingen opslaan niet gelukt.', 'Database optimization done.' => 'Database optimaliseren voltooid.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Datum aangemaakt', 'Date completed' => 'Datum voltooid', 'Id' => 'Id', - 'Completed tasks' => 'Voltooide taken', - 'Completed tasks for "%s"' => 'Vooltooide taken voor « %s »', '%d closed tasks' => '%d gesloten taken', 'No task for this project' => 'Geen taken voor dit project', 'Public link' => 'Publieke link', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Verloopdatum', 'Remember Me' => 'Onthoud mij', 'Creation date' => 'Aanmaakdatum', - 'Filter by user' => 'Filter op gebruiker', - 'Filter by due date' => 'Filter op vervaldatum', 'Everybody' => 'Iedereen', 'Open' => 'Open', 'Closed' => 'Gesloten', 'Search' => 'Zoek', 'Nothing found.' => 'Niets gevonden.', - 'Search in the project "%s"' => 'Zoek in project « %s »', 'Due date' => 'Vervaldatum', 'Others formats accepted: %s and %s' => 'Andere toegestane formaten : %s en %s', 'Description' => 'Omschrijving', '%d comments' => '%d commentaren', '%d comment' => '%d commentaar', 'Email address invalid' => 'Ongeldig emailadres', - 'Your Google Account is not linked anymore to your profile.' => 'Uw Google Account is niet meer aan uw profiel gelinkt.', - 'Unable to unlink your Google Account.' => 'Verwijderen link met Google Account niet gelukt.', - 'Google authentication failed' => 'Google authenticatie niet gelukt', - 'Unable to link your Google Account.' => 'Linken met Google Account niet gelukt', - 'Your Google Account is linked to your profile successfully.' => 'Linken met Google Account succesvol.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Email', 'Link my Google Account' => 'Link mijn Google Account', 'Unlink my Google Account' => 'Link met Google Account verwijderen', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Categorie naam', 'Add a new category' => 'Categorie toevoegen', 'Do you really want to remove this category: "%s"?' => 'Weet u zeker dat u deze categorie wil verwijderen: « %s » ?', - 'Filter by category' => 'Filter op categorie', 'All categories' => 'Alle categorieën', 'No category' => 'Geen categorie', 'The name is required' => 'De naam is verplicht', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maximale grootte : ', 'Unable to upload the file.' => 'Uploaden van bestand niet gelukt.', 'Display another project' => 'Een ander project weergeven', - 'Your GitHub account was successfully linked to your profile.' => 'Uw Github Account is succesvol gelinkt aan uw profiel.', - 'Unable to link your GitHub Account.' => 'Linken van uw Github Account niet gelukt.', - 'GitHub authentication failed' => 'Github Authenticatie niet gelukt', - 'Your GitHub account is no longer linked to your profile.' => 'Uw Github Account is niet langer gelinkt aan uw profiel.', - 'Unable to unlink your GitHub Account.' => 'Verwijdern van de link met uw Github Account niet gelukt.', - 'Login with my GitHub Account' => 'Login met mijn Github Account', - 'Link my GitHub Account' => 'Link met mijn Github', - 'Unlink my GitHub Account' => 'Link met mijn Github verwijderen', + 'Login with my Github Account' => 'Login met mijn Github Account', + 'Link my Github Account' => 'Link met mijn Github', + 'Unlink my Github Account' => 'Link met mijn Github verwijderen', 'Created by %s' => 'Aangemaakt door %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Laatst gewijzigd op %d/%m/%Y à %H:%M', 'Tasks Export' => 'Taken exporteren', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s heeft een commentaar aangepast voor taak %s', '%s commented the task %s' => '%s heeft een commentaar geplaatst voor taak %s', '%s\'s activity' => 'Activiteiten van %s', - 'No activity.' => 'Geen activiteiten.', 'RSS feed' => 'RSS feed', '%s updated a comment on the task #%d' => '%s heeft een commentaar aangepast voor taak %d', '%s commented on the task #%d' => '%s heeft commentaar geplaatst voor taak %d', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Taal :', 'Timezone:' => 'Tijdzone :', 'All columns' => 'Alle kolommen', - 'Calendar for "%s"' => 'Agenda voor « %s »', - 'Filter by column' => 'Filter op kolom', - 'Filter by status' => 'Filter op status', 'Calendar' => 'Agenda', 'Next' => 'Volgende', '#%d' => '%d', - 'Filter by color' => 'Filter op kleur', - 'Filter by swimlane' => 'Filter op swimlane', 'All swimlanes' => 'Alle swimlanes', 'All colors' => 'Alle kleuren', 'All status' => 'Alle statussen', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Tijdschrijven', 'You already have one subtask in progress' => 'U heeft al een subtaak in behandeling', 'Which parts of the project do you want to duplicate?' => 'Welke onderdelen van het project wilt u dupliceren?', - 'Change dashboard view' => 'Pas dashboard aan', - 'Show/hide activities' => 'Toon/verberg activiteiten', - 'Show/hide projects' => 'Toon/verberg projecten', - 'Show/hide subtasks' => 'Toon/verberg subtaken', - 'Show/hide tasks' => 'Toon/verberg taken', - 'Disable login form' => 'Schakel login scherm uit', - 'Show/hide calendar' => 'Toon/verberg agenda', - 'User calendar' => 'Agenda gebruiker', + // 'Disallow login form' => '', 'Bitbucket commit received' => 'Bitbucket commit ontvangen', 'Bitbucket webhooks' => 'Bitbucket webhooks', 'Help on Bitbucket webhooks' => 'Help bij Bitbucket webhooks', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Keyboard snelkoppelingen', 'Open board switcher' => 'Open bord switcher', 'Application' => 'Applicatie', - 'Filter recently updated' => 'Filter recent aangepast', 'since %B %e, %Y at %k:%M %p' => 'sinds %d/%m/%Y à %H:%M', - 'More filters' => 'Meer filters', // 'Compact view' => '', // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index 85ba2535..9c4558d3 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Nazwa projektu jest wymagana', 'This project must be unique' => 'Projekt musi być unikalny', 'The title is required' => 'Tutył jest wymagany', - 'There is no active project, the first step is to create a new project.' => 'Brak aktywnych projektów. Pierwszym krokiem jest utworzenie nowego projektu.', 'Settings saved successfully.' => 'Ustawienia zapisane.', 'Unable to save your settings.' => 'Nie udało się zapisać ustawień.', 'Database optimization done.' => 'Optymalizacja bazy danych zakończona.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Data utworzenia', 'Date completed' => 'Data zakończenia', 'Id' => 'Id', - 'Completed tasks' => 'Ukończone zadania', - 'Completed tasks for "%s"' => 'Zadania zakończone dla "%s"', '%d closed tasks' => '%d zamkniętych zadań', 'No task for this project' => 'Brak zadań dla tego projektu', 'Public link' => 'Link publiczny', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Data zakończenia', 'Remember Me' => 'Pamiętaj mnie', 'Creation date' => 'Data utworzenia', - 'Filter by user' => 'Filtruj według użytkowników', - 'Filter by due date' => 'Filtruj według terminów', 'Everybody' => 'Wszyscy', 'Open' => 'Otwarto', 'Closed' => 'Zamknięto', 'Search' => 'Szukaj', 'Nothing found.' => 'Nic nie znaleziono', - 'Search in the project "%s"' => 'Szukaj w projekcie "%s"', 'Due date' => 'Termin', 'Others formats accepted: %s and %s' => 'Inne akceptowane formaty: %s and %s', 'Description' => 'Opis', '%d comments' => '%d Komentarzy', '%d comment' => '%d Komentarz', 'Email address invalid' => 'Błędny adres email', - 'Your Google Account is not linked anymore to your profile.' => 'Twoje konto Google nie jest już połączone', - 'Unable to unlink your Google Account.' => 'Nie można odłączyć konta Google', - 'Google authentication failed' => 'Autentykacja Google nieudana', - 'Unable to link your Google Account.' => 'Nie można podłączyć konta Google', - 'Your Google Account is linked to your profile successfully.' => 'Podłączanie konta Google ukończone pomyślnie', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Email', 'Link my Google Account' => 'Połącz z kontem Google', 'Unlink my Google Account' => 'Rozłącz z kontem Google', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Nazwa kategorii', 'Add a new category' => 'Utwórz nową kategorię', 'Do you really want to remove this category: "%s"?' => 'Czy na pewno chcesz usunąć kategorię: "%s"?', - 'Filter by category' => 'Filtruj według kategorii', 'All categories' => 'Wszystkie kategorie', 'No category' => 'Brak kategorii', 'The name is required' => 'Nazwa jest wymagana', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maksymalny rozmiar: ', 'Unable to upload the file.' => 'Nie można wczytać pliku.', 'Display another project' => 'Wyświetl inny projekt', - 'Your GitHub account was successfully linked to your profile.' => 'Konto Github podłączone pomyślnie.', - 'Unable to link your GitHub Account.' => 'Nie można połączyć z kontem Github.', - 'GitHub authentication failed' => 'Autentykacja Github nieudana', - 'Your GitHub account is no longer linked to your profile.' => 'Konto Github nie jest już podłączone do twojego profilu.', - 'Unable to unlink your GitHub Account.' => 'Nie można odłączyć konta Github.', - 'Login with my GitHub Account' => 'Zaloguj przy użyciu konta Github', - 'Link my GitHub Account' => 'Podłącz konto Github', - 'Unlink my GitHub Account' => 'Odłącz konto Github', + 'Login with my Github Account' => 'Zaloguj przy użyciu konta Github', + 'Link my Github Account' => 'Podłącz konto Github', + 'Unlink my Github Account' => 'Odłącz konto Github', 'Created by %s' => 'Utworzone przez %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Ostatnio zmienione %e %B %Y o %k:%M', 'Tasks Export' => 'Eksport zadań', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s zaktualizował komentarz do zadania %s', '%s commented the task %s' => '%s skomentował zadanie %s', '%s\'s activity' => 'Aktywność %s', - 'No activity.' => 'Brak aktywności.', 'RSS feed' => 'Kanał RSS', '%s updated a comment on the task #%d' => '%s zaktualizował komentarz do zadania #%d', '%s commented on the task #%d' => '%s skomentował zadanie #%d', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Język:', 'Timezone:' => 'Strefa czasowa:', 'All columns' => 'Wszystkie kolumny', - 'Calendar for "%s"' => 'Kalendarz dla "%s"', - 'Filter by column' => 'Filtruj według kolumn', - 'Filter by status' => 'Filtruj według statusu', 'Calendar' => 'Kalendarz', 'Next' => 'Następny', '#%d' => 'nr %d', - 'Filter by color' => 'Filtruj według koloru', - 'Filter by swimlane' => 'Filtruj według procesu', 'All swimlanes' => 'Wszystkie procesy', 'All colors' => 'Wszystkie kolory', 'All status' => 'Wszystkie statusy', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Śledzenie czasu', 'You already have one subtask in progress' => 'Masz już zadanie o statusie "w trakcie"', 'Which parts of the project do you want to duplicate?' => 'Które elementy projektu chcesz zduplikować?', - 'Change dashboard view' => 'Zmień elementy panelu', - 'Show/hide activities' => 'Pokaż/Ukryj aktywność', - 'Show/hide projects' => 'Pokaż/Ukryj projekty', - 'Show/hide subtasks' => 'Pokaż/Ukryj pod-zadania', - 'Show/hide tasks' => 'Pokaż/Ukryj zadania', - 'Disable login form' => 'Wyłącz formularz logowania', - 'Show/hide calendar' => 'Pokaż/Ukryj kalendarz', - 'User calendar' => 'Kalendarz użytkownika', + // 'Disallow login form' => '', // 'Bitbucket commit received' => '', // 'Bitbucket webhooks' => '', // 'Help on Bitbucket webhooks' => '', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Skróty klawiszowe', 'Open board switcher' => 'Przełącz tablice', 'Application' => 'Aplikacja', - 'Filter recently updated' => 'Filtruj ostatnio zmieniane', 'since %B %e, %Y at %k:%M %p' => 'od %e %B %Y o %k:%M', - 'More filters' => 'Więcej filtrów', 'Compact view' => 'Widok kompaktowy', 'Horizontal scrolling' => 'Przewijanie poziome', 'Compact/wide view' => 'Pełny/Kompaktowy widok', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index 771958f1..b31f815c 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -20,15 +20,15 @@ return array( 'Red' => 'Vermelho', 'Orange' => 'Laranja', 'Grey' => 'Cinza', - // 'Brown' => '', - // 'Deep Orange' => '', - // 'Dark Grey' => '', - // 'Pink' => '', - // 'Teal' => '', - // 'Cyan' => '', - // 'Lime' => '', - // 'Light Green' => '', - // 'Amber' => '', + 'Brown' => 'Marrom', + 'Deep Orange' => 'Laranja escuro', + 'Dark Grey' => 'Cinza escuro', + 'Pink' => 'Roza', + 'Teal' => 'Turquesa', + 'Cyan' => 'Azul intenso', + 'Lime' => 'Verde limão', + 'Light Green' => 'Verde claro', + 'Amber' => 'Âmbar', 'Save' => 'Salvar', 'Login' => 'Login', 'Official website:' => 'Site oficial:', @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'O nome do projeto é obrigatório', 'This project must be unique' => 'Este projeto deve ser único', 'The title is required' => 'O título é obrigatório', - 'There is no active project, the first step is to create a new project.' => 'Não há projeto ativo. O primeiro passo é criar um novo projeto.', 'Settings saved successfully.' => 'Configurações salvas com sucesso.', 'Unable to save your settings.' => 'Não é possível salvar suas configurações.', 'Database optimization done.' => 'Otimização do banco de dados finalizada.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Data de criação', 'Date completed' => 'Data da finalização', 'Id' => 'Id', - 'Completed tasks' => 'Tarefas completadas', - 'Completed tasks for "%s"' => 'Tarefas completadas por "%s"', '%d closed tasks' => '%d tarefas finalizadas', 'No task for this project' => 'Não há tarefa para este projeto', 'Public link' => 'Link público', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Data de expiração', 'Remember Me' => 'Lembre-se de mim', 'Creation date' => 'Data de criação', - 'Filter by user' => 'Filtrar por usuário', - 'Filter by due date' => 'Filtrar por data de vencimento', 'Everybody' => 'Todos', 'Open' => 'Abrir', 'Closed' => 'Finalizado', 'Search' => 'Pesquisar', 'Nothing found.' => 'Nada foi encontrado.', - 'Search in the project "%s"' => 'Pesquisar no projeto "%s"', 'Due date' => 'Data de vencimento', 'Others formats accepted: %s and %s' => 'Outros formatos permitidos: %s e %s', 'Description' => 'Descrição', '%d comments' => '%d comentários', '%d comment' => '%d comentário', 'Email address invalid' => 'Endereço de e-mail inválido', - 'Your Google Account is not linked anymore to your profile.' => 'Sua conta do Google não está mais associada ao seu perfil.', - 'Unable to unlink your Google Account.' => 'Não foi possível desassociar a sua Conta do Google.', - 'Google authentication failed' => 'Autenticação do Google falhou.', - 'Unable to link your Google Account.' => 'Não foi possível associar a sua Conta do Google.', - 'Your Google Account is linked to your profile successfully.' => 'Sua Conta do Google foi associada ao seu perfil com sucesso.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-mail', 'Link my Google Account' => 'Vincular minha Conta do Google', 'Unlink my Google Account' => 'Desvincular minha Conta do Google', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Nome da Categoria', 'Add a new category' => 'Adicionar uma nova categoria', 'Do you really want to remove this category: "%s"?' => 'Você realmente deseja remover esta categoria: "%s"', - 'Filter by category' => 'Filtrar por categoria', 'All categories' => 'Todas as categorias', 'No category' => 'Nenhum categoria', 'The name is required' => 'O nome é obrigatório', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Tamanho máximo:', 'Unable to upload the file.' => 'Não foi possível carregar o arquivo.', 'Display another project' => 'Exibir outro projeto', - 'Your GitHub account was successfully linked to your profile.' => 'A sua Conta do GitHub foi associada com sucesso ao seu perfil.', - 'Unable to link your GitHub Account.' => 'Não foi possível associar sua Conta do GitHub.', - 'GitHub authentication failed' => 'Autenticação do GitHub falhou', - 'Your GitHub account is no longer linked to your profile.' => 'A sua Conta do GitHub não está mais associada ao seu perfil.', - 'Unable to unlink your GitHub Account.' => 'Não foi possível desassociar a sua Conta do GitHub.', - 'Login with my GitHub Account' => 'Entrar com minha Conta do GitHub', - 'Link my GitHub Account' => 'Associar à minha Conta do GitHub', - 'Unlink my GitHub Account' => 'Desassociar a minha Conta do GitHub', + 'Login with my Github Account' => 'Entrar com minha Conta do Github', + 'Link my Github Account' => 'Associar à minha Conta do Github', + 'Unlink my Github Account' => 'Desassociar a minha Conta do Github', 'Created by %s' => 'Criado por %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Última modificação em %B %e, %Y às %k: %M %p', 'Tasks Export' => 'Exportar Tarefas', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s atualizou o comentário na tarefa %s', '%s commented the task %s' => '%s comentou a tarefa %s', '%s\'s activity' => 'Atividades de%s', - 'No activity.' => 'Sem atividade.', 'RSS feed' => 'Feed RSS', '%s updated a comment on the task #%d' => '%s atualizou um comentário sobre a tarefa #%d', '%s commented on the task #%d' => '%s comentou sobre a tarefa #%d', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Idioma', 'Timezone:' => 'Fuso horário', 'All columns' => 'Todas as colunas', - 'Calendar for "%s"' => 'Calendário para "%s"', - 'Filter by column' => 'Filtrar por coluna', - 'Filter by status' => 'Filtrar por status', 'Calendar' => 'Calendário', 'Next' => 'Próximo', // '#%d' => '', - 'Filter by color' => 'Filtrar por cor', - 'Filter by swimlane' => 'Filtrar por swimlane', 'All swimlanes' => 'Todos os swimlane', 'All colors' => 'Todas as cores', 'All status' => 'Todos os status', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Gestão de tempo', 'You already have one subtask in progress' => 'Você já tem um subtarefa em andamento', 'Which parts of the project do you want to duplicate?' => 'Quais as partes do projeto você deseja duplicar?', - 'Change dashboard view' => 'Alterar a vista do Painel de Controle', - 'Show/hide activities' => 'Mostrar / ocultar as atividades', - 'Show/hide projects' => 'Mostrar / ocultar os projetos', - 'Show/hide subtasks' => 'Mostrar / ocultar as subtarefas', - 'Show/hide tasks' => 'Mostrar / ocultar as tarefas', - 'Disable login form' => 'Desativar o formulário de login', - 'Show/hide calendar' => 'Mostrar / ocultar calendário', - 'User calendar' => 'Calendário do usuário', + // 'Disallow login form' => '', 'Bitbucket commit received' => '"Commit" recebido via Bitbucket', 'Bitbucket webhooks' => 'Webhook Bitbucket', 'Help on Bitbucket webhooks' => 'Ajuda sobre os webhooks Bitbucket', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Atalhos de teclado', 'Open board switcher' => 'Abrir o comutador de painel', 'Application' => 'Aplicação', - 'Filter recently updated' => 'Filtro recentemente atualizado', 'since %B %e, %Y at %k:%M %p' => 'desde o %d/%m/%Y às %H:%M', - 'More filters' => 'Mais filtros', 'Compact view' => 'Vista reduzida', 'Horizontal scrolling' => 'Rolagem horizontal', 'Compact/wide view' => 'Alternar entre a vista compacta e ampliada', @@ -878,84 +850,157 @@ return array( 'Two factor authentication disabled' => 'Autenticação à fator duplo desativado', 'Two factor authentication enabled' => 'Autenticação à fator duplo activado', 'Unable to update this user.' => 'Impossível de atualizar esse usuário.', - // 'There is no user management for private projects.' => '', - // 'User that will receive the email' => '', - // 'Email subject' => '', - // 'Date' => '', - // 'By @%s on Bitbucket' => '', - // 'Bitbucket Issue' => '', - // 'Commit made by @%s on Bitbucket' => '', - // 'Commit made by @%s on Github' => '', - // 'By @%s on Github' => '', - // 'Commit made by @%s on Gitlab' => '', - // 'Add a comment log when moving the task between columns' => '', - // 'Move the task to another column when the category is changed' => '', - // 'Send a task by email to someone' => '', - // 'Reopen a task' => '', - // 'Bitbucket issue opened' => '', - // 'Bitbucket issue closed' => '', - // 'Bitbucket issue reopened' => '', - // 'Bitbucket issue assignee change' => '', - // 'Bitbucket issue comment created' => '', - // 'Column change' => '', - // 'Position change' => '', - // 'Swimlane change' => '', - // 'Assignee change' => '', - // '[%s] Overdue tasks' => '', - // 'Notification' => '', - // '%s moved the task #%d to the first swimlane' => '', - // '%s moved the task #%d to the swimlane "%s"' => '', - // 'Swimlane' => '', - // 'Budget overview' => '', - // 'Type' => '', - // 'There is not enough data to show something.' => '', - // 'Gravatar' => '', - // 'Hipchat' => '', - // 'Slack' => '', - // '%s moved the task %s to the first swimlane' => '', - // '%s moved the task %s to the swimlane "%s"' => '', - // 'This report contains all subtasks information for the given date range.' => '', - // 'This report contains all tasks information for the given date range.' => '', - // 'Project activities for %s' => '', - // 'view the board on Kanboard' => '', - // 'The task have been moved to the first swimlane' => '', - // 'The task have been moved to another swimlane:' => '', - // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', - // 'New title: %s' => '', - // 'The task is not assigned anymore' => '', - // 'New assignee: %s' => '', - // 'There is no category now' => '', - // 'New category: %s' => '', - // 'New color: %s' => '', - // 'New complexity: %d' => '', - // 'The due date have been removed' => '', - // 'There is no description anymore' => '', - // 'Recurrence settings have been modified' => '', - // 'Time spent changed: %sh' => '', - // 'Time estimated changed: %sh' => '', - // 'The field "%s" have been updated' => '', - // 'The description have been modified' => '', - // 'Do you really want to close the task "%s" as well as all subtasks?' => '', - // 'Swimlane: %s' => '', - // 'Project calendar' => '', - // 'I want to receive notifications for:' => '', - // 'All tasks' => '', - // 'Only for tasks assigned to me' => '', - // 'Only for tasks created by me' => '', - // 'Only for tasks created by me and assigned to me' => '', - // '%A' => '', - // '%b %e, %Y, %k:%M %p' => '', - // 'New due date: %B %e, %Y' => '', - // 'Start date changed: %B %e, %Y' => '', - // '%k:%M %p' => '', - // '%%Y-%%m-%%d' => '', - // 'Total for all columns' => '', - // 'You need at least 2 days of data to show the chart.' => '', - // '<15m' => '', - // '<30m' => '', - // 'Stop timer' => '', - // 'Start timer' => '', - // 'Add project member' => '', - // 'Enable notifications' => '', + 'There is no user management for private projects.' => 'Não há gerenciamento de usuários para projetos privados.', + 'User that will receive the email' => 'O usuário que vai receber o e-mail', + 'Email subject' => 'Assunto do e-mail', + 'Date' => 'Data', + 'By @%s on Bitbucket' => 'Por @%s no Bitbucket', + 'Bitbucket Issue' => 'Bitbucket Issue', + 'Commit made by @%s on Bitbucket' => 'Commit feito por @%s no Bitbucket', + 'Commit made by @%s on Github' => 'Commit feito por @%s no Github', + 'By @%s on Github' => 'Por @%s no Github', + 'Commit made by @%s on Gitlab' => 'Commit feito por @%s no Gitlab', + 'Add a comment log when moving the task between columns' => 'Adicionar um comentário de log quando uma tarefa é movida para uma outra coluna', + 'Move the task to another column when the category is changed' => 'Mover uma tarefa para outra coluna quando a categoria mudou', + 'Send a task by email to someone' => 'Enviar uma tarefa por e-mail a alguém', + 'Reopen a task' => 'Reabrir uma tarefa', + 'Bitbucket issue opened' => 'Bitbucket issue opened', + 'Bitbucket issue closed' => 'Bitbucket issue closed', + 'Bitbucket issue reopened' => 'Bitbucket issue reopened', + 'Bitbucket issue assignee change' => 'Bitbucket issue assignee change', + 'Bitbucket issue comment created' => 'Bitbucket issue comment created', + 'Column change' => 'Mudança de coluna', + 'Position change' => 'Mudança de posição', + 'Swimlane change' => 'Mudança de swimlane', + 'Assignee change' => 'Mudança do designado', + '[%s] Overdue tasks' => '[%s] Tarefas atrasadas', + 'Notification' => 'Notificação', + '%s moved the task #%d to the first swimlane' => '%s moveu a tarefa n° %d no primeiro swimlane', + '%s moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa n° %d no swimlane "%s"', + 'Swimlane' => 'Swimlane', + 'Budget overview' => 'Visão geral do orçamento', + 'Type' => 'Tipo', + 'There is not enough data to show something.' => 'Não há dados suficientes para mostrar alguma coisa.', + 'Gravatar' => 'Gravatar', + 'Hipchat' => 'Hipchat', + 'Slack' => 'Slack', + '%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s no primeiro swimlane', + '%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s no swimlane "%s"', + 'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as sub-tarefas para o período selecionado.', + 'This report contains all tasks information for the given date range.' => 'Este relatório contém informações de todas as tarefas para o período selecionado.', + 'Project activities for %s' => 'Atividade do projeto "%s"', + 'view the board on Kanboard' => 'ver o painel no Kanboard', + 'The task have been moved to the first swimlane' => 'A tarefa foi movida para o primeiro Swimlane', + 'The task have been moved to another swimlane:' => 'A tarefa foi movida para outro Swimlane', + 'Overdue tasks for the project "%s"' => 'Tarefas atrasadas para o projeto "%s"', + 'New title: %s' => 'Novo título: %s', + 'The task is not assigned anymore' => 'Agora a tarefa não está mais atribuída', + 'New assignee: %s' => 'Novo designado: %s', + 'There is no category now' => 'Agora não tem mais categoria', + 'New category: %s' => 'Nova categoria: %s', + 'New color: %s' => 'Nova cor: %s', + 'New complexity: %d' => 'Nova complexidade: %d', + 'The due date have been removed' => 'A data limite foi retirada', + 'There is no description anymore' => 'Agora não tem mais descrição', + 'Recurrence settings have been modified' => 'As configurações da recorrência foram modificadas', + 'Time spent changed: %sh' => 'O tempo despendido foi mudado: %sh', + 'Time estimated changed: %sh' => 'O tempo estimado foi mudado/ %sh', + 'The field "%s" have been updated' => 'O campo "%s" foi atualizada', + 'The description have been modified' => 'A descrição foi modificada', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Você realmente quer fechar a tarefa "%s" e todas as suas sub-tarefas?', + 'Swimlane: %s' => 'Swimlane: %s', + 'I want to receive notifications for:' => 'Eu quero receber as notificações para:', + 'All tasks' => 'Todas as tarefas', + 'Only for tasks assigned to me' => 'Somente as tarefas atribuídas a mim', + 'Only for tasks created by me' => 'Apenas as tarefas que eu criei', + 'Only for tasks created by me and assigned to me' => 'Apenas as tarefas que eu criei e aquelas atribuídas a mim', + '%A' => '%A', + '%b %e, %Y, %k:%M %p' => '%d/%m/%Y %H:%M', + 'New due date: %B %e, %Y' => 'Nova data limite: %d/%m/%Y', + 'Start date changed: %B %e, %Y' => 'Data de início alterada: %d/%m/%Y', + '%k:%M %p' => '%H:%M', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Total para todas as colunas', + 'You need at least 2 days of data to show the chart.' => 'Você precisa de pelo menos 2 dias de dados para visualizar o gráfico.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Stop timer', + 'Start timer' => 'Start timer', + 'Add project member' => 'Adicionar um membro ao projeto', + 'Enable notifications' => 'Ativar as notificações', + 'My activity stream' => 'Meu feed de atividade', + 'My calendar' => 'Minha agenda', + 'Search tasks' => 'Pesquisar tarefas', + 'Back to the calendar' => 'Voltar ao calendário', + 'Filters' => 'Filtros', + 'Reset filters' => 'Redefinir os filtros', + 'My tasks due tomorrow' => 'Minhas tarefas que expiram amanhã', + 'Tasks due today' => 'Tarefas que expiram hoje', + 'Tasks due tomorrow' => 'Tarefas que expiram amanhã', + 'Tasks due yesterday' => 'Tarefas que expiraram ontem', + 'Closed tasks' => 'Tarefas fechadas', + 'Open tasks' => 'Tarefas abertas', + 'Not assigned' => 'Não designada', + 'View advanced search syntax' => 'Ver a sintaxe para pesquisa avançada', + 'Overview' => 'Visão global', + '%b %e %Y' => '%b %e %Y', + 'Board/Calendar/List view' => 'Vista Painel/Calendário/Lista', + 'Switch to the board view' => 'Mudar para o modo Painel', + 'Switch to the calendar view' => 'Mudar par o modo Calendário', + 'Switch to the list view' => 'Mudar par o modo Lista', + 'Go to the search/filter box' => 'Ir para o campo de pesquisa', + 'There is no activity yet.' => 'Não há nenhuma atividade ainda.', + 'No tasks found.' => 'Nenhuma tarefa encontrada', + 'Keyboard shortcut: "%s"' => 'Tecla de atalho: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtro', + 'Advanced search' => 'Pesquisa avançada', + 'Example of query: ' => 'Exemplo de consulta: ', + 'Search by project: ' => 'Pesquisar por projet: ', + 'Search by column: ' => 'Pesquisar por coluna: ', + 'Search by assignee: ' => 'Pesquisar por designado: ', + 'Search by color: ' => 'Pesquisar por cor: ', + 'Search by category: ' => 'Pesquisar por categoria: ', + 'Search by description: ' => 'Pesquisar por descrição: ', + 'Search by due date: ' => 'Pesquisar por data de expiração: ', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index 69ebbf61..9ce2ea6e 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Требуется имя проекта', 'This project must be unique' => 'Проект должен быть уникальным', 'The title is required' => 'Требуется заголовок', - 'There is no active project, the first step is to create a new project.' => 'Нет активного проекта, сначала создайте новый проект.', 'Settings saved successfully.' => 'Параметры успешно сохранены.', 'Unable to save your settings.' => 'Невозможно сохранить параметры.', 'Database optimization done.' => 'База данных оптимизирована.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Дата создания', 'Date completed' => 'Дата завершения', 'Id' => 'ID', - 'Completed tasks' => 'Завершенные задачи', - 'Completed tasks for "%s"' => 'Завершенные задачи для « %s »', '%d closed tasks' => '%d завершенных задач', 'No task for this project' => 'Нет задач для этого проекта', 'Public link' => 'Ссылка для просмотра', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Дата окончания', 'Remember Me' => 'Запомнить меня', 'Creation date' => 'Дата создания', - 'Filter by user' => 'Фильтр по пользователям', - 'Filter by due date' => 'Фильтр по дате', 'Everybody' => 'Все', 'Open' => 'Открытый', 'Closed' => 'Закрытый', 'Search' => 'Поиск', 'Nothing found.' => 'Ничего не найдено.', - 'Search in the project "%s"' => 'Искать в проекте « %s »', 'Due date' => 'Срок', 'Others formats accepted: %s and %s' => 'Другой формат приемлем: %s и %s', 'Description' => 'Описание', '%d comments' => '%d комментариев', '%d comment' => '%d комментарий', 'Email address invalid' => 'Некорректный e-mail адрес', - 'Your Google Account is not linked anymore to your profile.' => 'Ваш аккаунт в Google больше не привязан к вашему профилю.', - 'Unable to unlink your Google Account.' => 'Не удалось отвязать ваш профиль от Google.', - 'Google authentication failed' => 'Аутентификация Google не удалась', - 'Unable to link your Google Account.' => 'Не удалось привязать ваш профиль к Google.', - 'Your Google Account is linked to your profile successfully.' => 'Ваш профиль успешно привязан к Google.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-mail', 'Link my Google Account' => 'Привязать мой профиль к Google', 'Unlink my Google Account' => 'Отвязать мой профиль от Google', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Название категории', 'Add a new category' => 'Добавить новую категорию', 'Do you really want to remove this category: "%s"?' => 'Вы точно хотите удалить категорию « %s » ?', - 'Filter by category' => 'Фильтр по категориям', 'All categories' => 'Все категории', 'No category' => 'Нет категории', 'The name is required' => 'Требуется название', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Максимальный размер: ', 'Unable to upload the file.' => 'Не удалось загрузить файл.', 'Display another project' => 'Показать другой проект', - 'Your GitHub account was successfully linked to your profile.' => 'Ваш GitHub привязан к вашему профилю.', - 'Unable to link your GitHub Account.' => 'Не удалось привязать ваш профиль к GitHub.', - 'GitHub authentication failed' => 'Аутентификация в GitHub не удалась', - 'Your GitHub account is no longer linked to your profile.' => 'Ваш GitHub отвязан от вашего профиля.', - 'Unable to unlink your GitHub Account.' => 'Не удалось отвязать ваш профиль от GitHub.', - 'Login with my GitHub Account' => 'Аутентификация через GitHub', - 'Link my GitHub Account' => 'Привязать мой профиль к GitHub', - 'Unlink my GitHub Account' => 'Отвязать мой профиль от GitHub', + 'Login with my Github Account' => 'Аутентификация через Github', + 'Link my Github Account' => 'Привязать мой профиль к Github', + 'Unlink my Github Account' => 'Отвязать мой профиль от Github', 'Created by %s' => 'Создано %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Последнее изменение %d/%m/%Y в %H:%M', 'Tasks Export' => 'Экспорт задач', @@ -409,7 +396,7 @@ return array( 'Enabled' => 'Включен', 'Disabled' => 'Выключены', 'Google account linked' => 'Профиль Google связан', - 'Github account linked' => 'Профиль GitHub связан', + 'Github account linked' => 'Профиль Github связан', 'Username:' => 'Имя пользователя:', 'Name:' => 'Имя:', 'Email:' => 'E-mail:', @@ -423,7 +410,7 @@ return array( 'Password modification' => 'Изменение пароля', 'External authentications' => 'Внешняя аутентификация', 'Google Account' => 'Профиль Google', - 'Github Account' => 'Профиль GitHub', + 'Github Account' => 'Профиль Github', 'Never connected.' => 'Ранее не соединялось.', 'No account linked.' => 'Нет связанных профилей.', 'Account linked.' => 'Профиль связан.', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s обновил комментарий к задаче %s', '%s commented the task %s' => '%s прокомментировал задачу %s', '%s\'s activity' => '%s активность', - 'No activity.' => 'Нет активности', 'RSS feed' => 'RSS лента', '%s updated a comment on the task #%d' => '%s обновил комментарий задачи #%d', '%s commented on the task #%d' => '%s прокомментировал задачу #%d', @@ -465,12 +451,12 @@ return array( '%s changed the assignee of the task %s to %s' => '%s сменил назначенного для задачи %s на %s', 'New password for the user "%s"' => 'Новый пароль для пользователя "%s"', 'Choose an event' => 'Выберите событие', - 'Github commit received' => 'GitHub: коммит получен', - 'Github issue opened' => 'GitHub: новая проблема', - 'Github issue closed' => 'GitHub: проблема закрыта', - 'Github issue reopened' => 'GitHub: проблема переоткрыта', - 'Github issue assignee change' => 'GitHub: сменить ответственного за проблему', - 'Github issue label change' => 'GitHub: ярлык проблемы изменен', + 'Github commit received' => 'Github: коммит получен', + 'Github issue opened' => 'Github: новая проблема', + 'Github issue closed' => 'Github: проблема закрыта', + 'Github issue reopened' => 'Github: проблема переоткрыта', + 'Github issue assignee change' => 'Github: сменить ответственного за проблему', + 'Github issue label change' => 'Github: ярлык проблемы изменен', 'Create a task from an external provider' => 'Создать задачу из внешнего источника', 'Change the assignee based on an external username' => 'Изменить назначенного основываясь на внешнем имени пользователя', 'Change the category based on an external label' => 'Изменить категорию основываясь на внешнем ярлыке', @@ -515,8 +501,8 @@ return array( 'Everybody have access to this project.' => 'Любой может получить доступ к этому проекту.', 'Webhooks' => 'Webhooks', 'API' => 'API', - 'Github webhooks' => 'GitHub webhooks', - 'Help on Github webhooks' => 'Помощь по GitHub webhooks', + 'Github webhooks' => 'Github webhooks', + 'Help on Github webhooks' => 'Помощь по Github webhooks', 'Create a comment from an external provider' => 'Создать комментарий из внешнего источника', 'Github issue comment created' => 'Github issue комментарий создан', 'Project management' => 'Управление проектом', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Язык:', 'Timezone:' => 'Временная зона:', 'All columns' => 'Все колонки', - 'Calendar for "%s"' => 'Календарь для "%s"', - 'Filter by column' => 'Фильтр по колонке', - 'Filter by status' => 'Фильтр по статусу', 'Calendar' => 'Календарь', 'Next' => 'Следующий', // '#%d' => '', - 'Filter by color' => 'Фильтрация по цвету', - 'Filter by swimlane' => 'Фильтрация по дорожкам', 'All swimlanes' => 'Все дорожки', 'All colors' => 'Все цвета', 'All status' => 'Все статусы', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Учет времени', 'You already have one subtask in progress' => 'У вас уже есть одна задача в разработке', 'Which parts of the project do you want to duplicate?' => 'Какие части проекта должны быть дублированы?', - 'Change dashboard view' => 'Изменить отображение панели мониторинга', - 'Show/hide activities' => 'Показать/скрыть активность', - 'Show/hide projects' => 'Показать/скрыть проекты', - 'Show/hide subtasks' => 'Показать/скрыть подзадачи', - 'Show/hide tasks' => 'Показать/скрыть задачи', - 'Disable login form' => 'Выключить форму авторизации', - 'Show/hide calendar' => 'Показать/скрыть календарь', - 'User calendar' => 'Пользовательский календарь', + // 'Disallow login form' => '', // 'Bitbucket commit received' => '', 'Bitbucket webhooks' => 'BitBucket webhooks', 'Help on Bitbucket webhooks' => 'Помощь по BitBucket webhooks', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Горячие клавиши', 'Open board switcher' => 'Открыть переключатель доски', 'Application' => 'Приложение', - 'Filter recently updated' => 'Сортировать по дате обновления', // 'since %B %e, %Y at %k:%M %p' => '', - 'More filters' => 'Дополнительные фильтры', 'Compact view' => 'Компактный вид', 'Horizontal scrolling' => 'Широкий вид', 'Compact/wide view' => 'Компактный/широкий вид', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php index 8cc6f05d..7f90af2d 100644 --- a/app/Locale/sr_Latn_RS/translations.php +++ b/app/Locale/sr_Latn_RS/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Naziv projekta je obavezan', 'This project must be unique' => 'Projekat mora biti jedinstven', 'The title is required' => 'Naslov je obavezan', - 'There is no active project, the first step is to create a new project.' => 'Nema aktivnih projekata. Potrebno je prvo napraviti novi projekat.', 'Settings saved successfully.' => 'Podešavanja uspešno snimljena.', 'Unable to save your settings.' => 'Nemoguće snimanje podešavanja.', 'Database optimization done.' => 'Optimizacija baze je završena.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Kreiran dana', 'Date completed' => 'Završen dana', 'Id' => 'Id', - 'Completed tasks' => 'Zatvoreni zadaci', - 'Completed tasks for "%s"' => 'zatvoreni zadaci za "%s"', '%d closed tasks' => '%d zatvorenih zadataka', 'No task for this project' => 'Nema dodeljenih zadataka ovom projektu', 'Public link' => 'Javni link', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Ističe', 'Remember Me' => 'Zapamti me', 'Creation date' => 'Datum kreiranja', - 'Filter by user' => 'Po korisniku', - 'Filter by due date' => 'Po terminu', 'Everybody' => 'Svi', 'Open' => 'Otvoreni', 'Closed' => 'Zatvoreni', 'Search' => 'Traži', 'Nothing found.' => 'Ništa nije pronađeno', - 'Search in the project "%s"' => 'Traži u prijektu "%s"', 'Due date' => 'Termin', 'Others formats accepted: %s and %s' => 'Ostali formati: %s i %s', 'Description' => 'Opis', '%d comments' => '%d Komentara', '%d comment' => '%d Komentar', 'Email address invalid' => 'Pogrešan e-mail', - 'Your Google Account is not linked anymore to your profile.' => 'Tvoj google nalog više nije povezan sa profilom', - 'Unable to unlink your Google Account.' => 'Neuspešno ukidanje veze od Google naloga', - 'Google authentication failed' => 'Neuspešna Google autentikacija', - 'Unable to link your Google Account.' => 'Neuspešno povezivanje sa Google nalogom', - 'Your Google Account is linked to your profile successfully.' => 'Vaš Google nalog je uspešno povezan sa vašim profilom', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-mail', 'Link my Google Account' => 'Poveži sa Google nalogom', 'Unlink my Google Account' => 'Ukini vezu sa Google nalogom', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Naziv kategorije', 'Add a new category' => 'Dodaj novu kategoriju', 'Do you really want to remove this category: "%s"?' => 'Da li zaista želiš da ukloniš kategoriju: "%s"?', - 'Filter by category' => 'Po kategoriji', 'All categories' => 'Sve kategorije', 'No category' => 'Bez kategorije', 'The name is required' => 'Naziv je obavezan', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maksimalna veličina: ', 'Unable to upload the file.' => 'Nije moguće snimiti fajl.', 'Display another project' => 'Prikaži drugi projekat', - 'Your GitHub account was successfully linked to your profile.' => 'Konto Github podłączone pomyślnie.', - 'Unable to link your GitHub Account.' => 'Nie można połączyć z kontem Github.', - 'GitHub authentication failed' => 'Autentykacja Github nieudana', - 'Your GitHub account is no longer linked to your profile.' => 'Konto Github nie jest już podłączone do twojego profilu.', - 'Unable to unlink your GitHub Account.' => 'Nie można odłączyć konta Github.', - 'Login with my GitHub Account' => 'Zaloguj przy użyciu konta Github', - 'Link my GitHub Account' => 'Podłącz konto Github', - 'Unlink my GitHub Account' => 'Odłącz konto Github', + 'Login with my Github Account' => 'Zaloguj przy użyciu konta Github', + 'Link my Github Account' => 'Podłącz konto Github', + 'Unlink my Github Account' => 'Odłącz konto Github', 'Created by %s' => 'Kreirao %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Poslednja izmena %e %B %Y o %k:%M', 'Tasks Export' => 'Izvoz zadataka', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s izmenjen komentar zadatka %s', '%s commented the task %s' => '%s komentarisao zadatak %s', '%s\'s activity' => 'Aktivnosti %s', - 'No activity.' => 'Bez aktivnosti.', 'RSS feed' => 'RSS kanal', '%s updated a comment on the task #%d' => '%s izmenjen komentar zadatka #%d', '%s commented on the task #%d' => '%s komentarisao zadatak #%d', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Jezik:', 'Timezone:' => 'Vremenska zona:', 'All columns' => 'Sve kolone', - 'Calendar for "%s"' => 'Kalendar za "%s"', - 'Filter by column' => 'Po koloni', - 'Filter by status' => 'Po statusu', 'Calendar' => 'Kalendar', 'Next' => 'Sledeći', // '#%d' => '', - 'Filter by color' => 'Po boji', - 'Filter by swimlane' => 'Po razdelniku', 'All swimlanes' => 'Svi razdelniki', 'All colors' => 'Sve boje', 'All status' => 'Svi statusi', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Praćenje vremena', // 'You already have one subtask in progress' => '', 'Which parts of the project do you want to duplicate?' => 'Koje delove projekta želite da kopirate', - // 'Change dashboard view' => '', - // 'Show/hide activities' => '', - // 'Show/hide projects' => '', - // 'Show/hide subtasks' => '', - // 'Show/hide tasks' => '', - // 'Disable login form' => '', - // 'Show/hide calendar' => '', - // 'User calendar' => '', + // 'Disallow login form' => '', // 'Bitbucket commit received' => '', // 'Bitbucket webhooks' => '', // 'Help on Bitbucket webhooks' => '', @@ -688,9 +662,7 @@ return array( // 'Keyboard shortcuts' => '', // 'Open board switcher' => '', // 'Application' => '', - // 'Filter recently updated' => '', // 'since %B %e, %Y at %k:%M %p' => '', - // 'More filters' => '', // 'Compact view' => '', // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index f54a5178..67e07192 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -20,15 +20,15 @@ return array( 'Red' => 'Röd', 'Orange' => 'Orange', 'Grey' => 'Grå', - // 'Brown' => '', - // 'Deep Orange' => '', - // 'Dark Grey' => '', - // 'Pink' => '', - // 'Teal' => '', - // 'Cyan' => '', - // 'Lime' => '', - // 'Light Green' => '', - // 'Amber' => '', + 'Brown' => 'Brun', + 'Deep Orange' => 'Mörkorange', + 'Dark Grey' => 'Mörkgrå', + 'Pink' => 'Rosa', + 'Teal' => 'Grönblå', + 'Cyan' => 'Cyan', + 'Lime' => 'Lime', + 'Light Green' => 'Ljusgrön', + 'Amber' => 'Bärnsten', 'Save' => 'Spara', 'Login' => 'Login', 'Official website:' => 'Officiell webbsida:', @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Ett projektnamn måste anges', 'This project must be unique' => 'Detta projekt måste vara unikt', 'The title is required' => 'En titel måste anges.', - 'There is no active project, the first step is to create a new project.' => 'Inget projekt är aktiverat, första steget är att skapa ett nytt projekt', 'Settings saved successfully.' => 'Inställningarna har sparats.', 'Unable to save your settings.' => 'Kunde inte spara dina ändringar', 'Database optimization done.' => 'Databasen har optimerats.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Skapat datum', 'Date completed' => 'Slutfört datum', 'Id' => 'ID', - 'Completed tasks' => 'Slutförda uppgifter', - 'Completed tasks for "%s"' => 'Slutföra uppgifter för "%s"', '%d closed tasks' => '%d stängda uppgifter', 'No task for this project' => 'Inga uppgifter i detta projekt', 'Public link' => 'Publik länk', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Förfallodatum', 'Remember Me' => 'Kom ihåg mig', 'Creation date' => 'Skapatdatum', - 'Filter by user' => 'Filtrera på användare', - 'Filter by due date' => 'Filtrera på slutdatum', 'Everybody' => 'Alla', 'Open' => 'Öppen', 'Closed' => 'Stängd', 'Search' => 'Sök', 'Nothing found.' => 'Inget kunde hittas.', - 'Search in the project "%s"' => 'Sök i projektet "%s"', 'Due date' => 'Måldatum', 'Others formats accepted: %s and %s' => 'Andra format som accepteras: %s and %s', 'Description' => 'Beskrivning', '%d comments' => '%d kommentarer', '%d comment' => '%d kommentar', 'Email address invalid' => 'Epost-adressen ogiltig', - 'Your Google Account is not linked anymore to your profile.' => 'Ditt Google-konto är inte längre länkat till din profil', - 'Unable to unlink your Google Account.' => 'Kunde inte ta bort länken till ditt Google-konto.', - 'Google authentication failed' => 'Autentiseringen för Google misslyckades', - 'Unable to link your Google Account.' => 'Kunde inte länka till ditt Google-konto', - 'Your Google Account is linked to your profile successfully.' => 'Ditt Google-konto har kopplats till din profil.', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Epost', 'Link my Google Account' => 'Länka till mitt Google-konto', 'Unlink my Google Account' => 'Ta bort länken till mitt Google-konto', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Kategorinamn', 'Add a new category' => 'Lägg till en kategori', 'Do you really want to remove this category: "%s"?' => 'Vill du verkligen ta bort denna kategori: "%s"?', - 'Filter by category' => 'Filtrera på kategori', 'All categories' => 'Alla kategorier', 'No category' => 'Ingen kategori', 'The name is required' => 'Namnet måste anges', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maxstorlek: ', 'Unable to upload the file.' => 'Kunde inte ladda upp filen.', 'Display another project' => 'Visa ett annat projekt', - 'Your GitHub account was successfully linked to your profile.' => 'Ditt GitHub-konto har anslutits till din profil.', - 'Unable to link your GitHub Account.' => 'Kunde inte ansluta ditt GitHub-konto.', - 'GitHub authentication failed' => 'GitHub-verifiering misslyckades', - 'Your GitHub account is no longer linked to your profile.' => 'Ditt GitHub-konto är inte längre anslutet till din profil.', - 'Unable to unlink your GitHub Account.' => 'Kunde inte koppla ifrån ditt GitHub-konto.', - 'Login with my GitHub Account' => 'Logga in med mitt GitHub-konto', - 'Link my GitHub Account' => 'Anslut mitt GitHub-konto', - 'Unlink my GitHub Account' => 'Koppla ifrån mitt GitHub-konto', + 'Login with my Github Account' => 'Logga in med mitt Github-konto', + 'Link my Github Account' => 'Anslut mitt Github-konto', + 'Unlink my Github Account' => 'Koppla ifrån mitt Github-konto', 'Created by %s' => 'Skapad av %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Senaste ändring %Y-%m-%d kl %H:%M', 'Tasks Export' => 'Exportera uppgifter', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s uppdaterade en kommentar till uppgiften %s', '%s commented the task %s' => '%s kommenterade uppgiften %s', '%s\'s activity' => '%s\'s aktivitet', - 'No activity.' => 'Ingen aktivitet.', 'RSS feed' => 'RSS flöde', '%s updated a comment on the task #%d' => '%s uppdaterade en kommentar på uppgiften #%d', '%s commented on the task #%d' => '%s kommenterade uppgiften #%d', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Språk', 'Timezone:' => 'Tidszon', 'All columns' => 'Alla kolumner', - 'Calendar for "%s"' => 'Kalender för "%s"', - 'Filter by column' => 'Filtrera på kolumn', - 'Filter by status' => 'Filtrera på status', 'Calendar' => 'Kalender', 'Next' => 'Nästa', '#%d' => '#%d', - 'Filter by color' => 'Filtrera på färg', - 'Filter by swimlane' => 'Filtrera på swimlane', 'All swimlanes' => 'Alla swimlanes', 'All colors' => 'Alla färger', 'All status' => 'Alla status', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Tidsbevakning', 'You already have one subtask in progress' => 'Du har redan en deluppgift igång', 'Which parts of the project do you want to duplicate?' => 'Vilka delar av projektet vill du duplicera?', - 'Change dashboard view' => 'Ändra dashboard vy', - 'Show/hide activities' => 'Visa/dölj aktiviteter', - 'Show/hide projects' => 'Visa/dölj projekt', - 'Show/hide subtasks' => 'Visa/dölj deluppgifter', - 'Show/hide tasks' => 'Visa/dölj uppgifter', - 'Disable login form' => 'Inaktivera loginformuläret', - 'Show/hide calendar' => 'Visa/dölj kalender', - 'User calendar' => 'Användarkalender', + // 'Disallow login form' => '', 'Bitbucket commit received' => 'Bitbucket bidrag mottaget', 'Bitbucket webhooks' => 'Bitbucket webhooks', 'Help on Bitbucket webhooks' => 'Hjälp för Bitbucket webhooks', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Tangentbordsgenvägar', 'Open board switcher' => 'Växling av öppen tavla', 'Application' => 'Applikation', - 'Filter recently updated' => 'Filter som uppdaterats nyligen', 'since %B %e, %Y at %k:%M %p' => 'sedan %B %e, %Y at %k:%M %p', - 'More filters' => 'Fler filter', 'Compact view' => 'Kompakt vy', 'Horizontal scrolling' => 'Horisontell scroll', 'Compact/wide view' => 'Kompakt/bred vy', @@ -763,7 +735,7 @@ return array( 'Move the task to another column when assigned to a user' => 'Flytta uppgiften till en annan kolumn när den tilldelats en användare', 'Move the task to another column when assignee is cleared' => 'Flytta uppgiften till en annan kolumn när tilldelningen tas bort.', 'Source column' => 'Källkolumn', - // 'Show subtask estimates (forecast of future work)' => '', + 'Show subtask estimates (forecast of future work)' => 'Visa uppskattningar för deluppgifter (prognos för framtida arbete)', 'Transitions' => 'Övergångar', 'Executer' => 'Verkställare', 'Time spent in the column' => 'Tid i kolumnen.', @@ -808,154 +780,227 @@ return array( 'Burndown chart for "%s"' => 'Burndown diagram för "%s"', 'Burndown chart' => 'Burndown diagram', 'This chart show the task complexity over the time (Work Remaining).' => 'Diagrammet visar uppgiftens svårighet över tid (återstående arbete).', - // 'Screenshot taken %s' => '', - // 'Add a screenshot' => '', - // 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '', - // 'Screenshot uploaded successfully.' => '', + 'Screenshot taken %s' => 'Skärmdump tagen %s', + 'Add a screenshot' => 'Lägg till en skärmdump', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Ta en skärmdump och tryck CTRL+V för att klistra in här.', + 'Screenshot uploaded successfully.' => 'Skärmdumpen laddades upp.', 'SEK - Swedish Krona' => 'SEK - Svensk Krona', - // 'The project identifier is an optional alphanumeric code used to identify your project.' => '', - // 'Identifier' => '', - // 'Postmark (incoming emails)' => '', - // 'Help on Postmark integration' => '', - // 'Mailgun (incoming emails)' => '', - // 'Help on Mailgun integration' => '', + 'The project identifier is an optional alphanumeric code used to identify your project.' => 'Projektidentifieraren är en valbar alfanumerisk kod som används för att identifiera ditt projekt.', + 'Identifier' => 'Identifierare', + 'Postmark (incoming emails)' => 'Postmark (inkommande e-post)', + 'Help on Postmark integration' => 'Hjälp för Postmark integration', + 'Mailgun (incoming emails)' => 'Mailgrun (inkommande e-post)', + 'Help on Mailgun integration' => 'Hjälp för Mailgrun integration', // 'Sendgrid (incoming emails)' => '', - // 'Help on Sendgrid integration' => '', - // 'Disable two factor authentication' => '', - // 'Do you really want to disable the two factor authentication for this user: "%s"?' => '', - // 'Edit link' => '', - // 'Start to type task title...' => '', - // 'A task cannot be linked to itself' => '', - // 'The exact same link already exists' => '', - // 'Recurrent task is scheduled to be generated' => '', - // 'Recurring information' => '', - // 'Score' => '', - // 'The identifier must be unique' => '', - // 'This linked task id doesn\'t exists' => '', - // 'This value must be alphanumeric' => '', - // 'Edit recurrence' => '', - // 'Generate recurrent task' => '', - // 'Trigger to generate recurrent task' => '', - // 'Factor to calculate new due date' => '', - // 'Timeframe to calculate new due date' => '', - // 'Base date to calculate new due date' => '', - // 'Action date' => '', - // 'Base date to calculate new due date: ' => '', - // 'This task has created this child task: ' => '', - // 'Day(s)' => '', - // 'Existing due date' => '', - // 'Factor to calculate new due date: ' => '', - // 'Month(s)' => '', - // 'Recurrence' => '', - // 'This task has been created by: ' => '', - // 'Recurrent task has been generated:' => '', - // 'Timeframe to calculate new due date: ' => '', - // 'Trigger to generate recurrent task: ' => '', - // 'When task is closed' => '', - // 'When task is moved from first column' => '', - // 'When task is moved to last column' => '', - // 'Year(s)' => '', - // 'Jabber (XMPP)' => '', - // 'Send notifications to Jabber' => '', - // 'XMPP server address' => '', - // 'Jabber domain' => '', - // 'Jabber nickname' => '', - // 'Multi-user chat room' => '', - // 'Help on Jabber integration' => '', - // 'The server address must use this format: "tcp://hostname:5222"' => '', - // 'Calendar settings' => '', - // 'Project calendar view' => '', - // 'Project settings' => '', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - // 'User calendar view' => '', - // 'Automatically update the start date' => '', - // 'iCal feed' => '', - // 'Preferences' => '', - // 'Security' => '', - // 'Two factor authentication disabled' => '', - // 'Two factor authentication enabled' => '', - // 'Unable to update this user.' => '', - // 'There is no user management for private projects.' => '', - // 'User that will receive the email' => '', - // 'Email subject' => '', - // 'Date' => '', - // 'By @%s on Bitbucket' => '', - // 'Bitbucket Issue' => '', - // 'Commit made by @%s on Bitbucket' => '', - // 'Commit made by @%s on Github' => '', - // 'By @%s on Github' => '', - // 'Commit made by @%s on Gitlab' => '', - // 'Add a comment log when moving the task between columns' => '', - // 'Move the task to another column when the category is changed' => '', - // 'Send a task by email to someone' => '', - // 'Reopen a task' => '', - // 'Bitbucket issue opened' => '', - // 'Bitbucket issue closed' => '', - // 'Bitbucket issue reopened' => '', - // 'Bitbucket issue assignee change' => '', - // 'Bitbucket issue comment created' => '', - // 'Column change' => '', - // 'Position change' => '', - // 'Swimlane change' => '', - // 'Assignee change' => '', - // '[%s] Overdue tasks' => '', - // 'Notification' => '', - // '%s moved the task #%d to the first swimlane' => '', - // '%s moved the task #%d to the swimlane "%s"' => '', - // 'Swimlane' => '', - // 'Budget overview' => '', - // 'Type' => '', - // 'There is not enough data to show something.' => '', - // 'Gravatar' => '', - // 'Hipchat' => '', - // 'Slack' => '', - // '%s moved the task %s to the first swimlane' => '', - // '%s moved the task %s to the swimlane "%s"' => '', - // 'This report contains all subtasks information for the given date range.' => '', - // 'This report contains all tasks information for the given date range.' => '', - // 'Project activities for %s' => '', - // 'view the board on Kanboard' => '', - // 'The task have been moved to the first swimlane' => '', - // 'The task have been moved to another swimlane:' => '', - // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', - // 'New title: %s' => '', - // 'The task is not assigned anymore' => '', - // 'New assignee: %s' => '', - // 'There is no category now' => '', - // 'New category: %s' => '', - // 'New color: %s' => '', - // 'New complexity: %d' => '', - // 'The due date have been removed' => '', - // 'There is no description anymore' => '', - // 'Recurrence settings have been modified' => '', - // 'Time spent changed: %sh' => '', - // 'Time estimated changed: %sh' => '', - // 'The field "%s" have been updated' => '', - // 'The description have been modified' => '', - // 'Do you really want to close the task "%s" as well as all subtasks?' => '', - // 'Swimlane: %s' => '', - // 'Project calendar' => '', - // 'I want to receive notifications for:' => '', - // 'All tasks' => '', - // 'Only for tasks assigned to me' => '', - // 'Only for tasks created by me' => '', - // 'Only for tasks created by me and assigned to me' => '', - // '%A' => '', - // '%b %e, %Y, %k:%M %p' => '', - // 'New due date: %B %e, %Y' => '', - // 'Start date changed: %B %e, %Y' => '', - // '%k:%M %p' => '', - // '%%Y-%%m-%%d' => '', - // 'Total for all columns' => '', - // 'You need at least 2 days of data to show the chart.' => '', - // '<15m' => '', - // '<30m' => '', - // 'Stop timer' => '', - // 'Start timer' => '', - // 'Add project member' => '', - // 'Enable notifications' => '', + 'Help on Sendgrid integration' => 'Hjälp för Sendgrid integration', + 'Disable two factor authentication' => 'Inaktivera två-faktors autentisering', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Vill du verkligen inaktivera två-faktors autentisering för denna användare: "%s"?', + 'Edit link' => 'Ändra länk', + 'Start to type task title...' => 'Börja skriv uppgiftstitel...', + 'A task cannot be linked to itself' => 'En uppgift kan inte länkas till sig själv', + 'The exact same link already exists' => 'Länken existerar redan', + 'Recurrent task is scheduled to be generated' => 'Återkommande uppgift är schemalagd att genereras', + 'Recurring information' => 'Återkommande information', + 'Score' => 'Poäng', + 'The identifier must be unique' => 'Identifieraren måste vara unik', + 'This linked task id doesn\'t exists' => 'Denna länkade uppgifts id existerar inte', + 'This value must be alphanumeric' => 'Värdet måste vara alfanumeriskt', + 'Edit recurrence' => 'Ändra återkommande', + 'Generate recurrent task' => 'Generera återkommande uppgift', + 'Trigger to generate recurrent task' => 'Aktivera att generera återkommande uppgift', + 'Factor to calculate new due date' => 'Faktor för att beräkna nytt datum', + 'Timeframe to calculate new due date' => 'Tidsram för att beräkna nytt datum', + 'Base date to calculate new due date' => 'Basdatum för att beräkna nytt datum', + 'Action date' => 'Händelsedatum', + 'Base date to calculate new due date: ' => 'Basdatum för att beräkna nytt förfallodatum: ', + 'This task has created this child task: ' => 'Uppgiften har skapat denna underliggande uppgift: ', + 'Day(s)' => 'Dag(ar)', + 'Existing due date' => 'Existerande förfallodatum', + 'Factor to calculate new due date: ' => 'Faktor för att beräkna nytt förfallodatum: ', + 'Month(s)' => 'Månad(er)', + 'Recurrence' => 'Återkommande', + 'This task has been created by: ' => 'Uppgiften har skapats av: ', + 'Recurrent task has been generated:' => 'Återkommande uppgift har genererats:', + 'Timeframe to calculate new due date: ' => 'Tidsram för att beräkna nytt förfallodatum: ', + 'Trigger to generate recurrent task: ' => 'Aktivera att generera återkommande uppgift: ', + 'When task is closed' => 'När uppgiften är stängd', + 'When task is moved from first column' => 'När uppgiften flyttas från första kolumnen', + 'When task is moved to last column' => 'När uppgiften flyttas till sista kolumnen', + 'Year(s)' => 'År', + 'Jabber (XMPP)' => 'Jabber (XMPP)', + 'Send notifications to Jabber' => 'Skicka notiser till Jabber', + 'XMPP server address' => 'XMPP serveradress', + 'Jabber domain' => 'Jabber domän', + 'Jabber nickname' => 'Jabber smeknamn', + 'Multi-user chat room' => 'Multi-user chatrum', + 'Help on Jabber integration' => 'Hjälp för Jabber integration', + 'The server address must use this format: "tcp://hostname:5222"' => 'Serveradressen måste använda detta format: "tcp://hostname:5222"', + 'Calendar settings' => 'Inställningar för kalendern', + 'Project calendar view' => 'Projektkalendervy', + 'Project settings' => 'Projektinställningar', + 'Show subtasks based on the time tracking' => 'Visa deluppgifter baserade på tidsspårning', + 'Show tasks based on the creation date' => 'Visa uppgifter baserade på skapat datum', + 'Show tasks based on the start date' => 'Visa uppgifter baserade på startdatum', + 'Subtasks time tracking' => 'Deluppgifter tidsspårning', + 'User calendar view' => 'Användarkalendervy', + 'Automatically update the start date' => 'Automatisk uppdatering av startdatum', + 'iCal feed' => 'iCal flöde', + 'Preferences' => 'Preferenser', + 'Security' => 'Säkerhet', + 'Two factor authentication disabled' => 'Tvåfaktorsverifiering inaktiverad', + 'Two factor authentication enabled' => 'Tvåfaktorsverifiering aktiverad', + 'Unable to update this user.' => 'Kunde inte uppdatera användaren.', + 'There is no user management for private projects.' => 'Det finns ingen användarhantering för privata projekt.', + 'User that will receive the email' => 'Användare som kommer att ta emot mailet', + 'Email subject' => 'E-post ämne', + 'Date' => 'Datum', + 'By @%s on Bitbucket' => 'Av @%s på Bitbucket', + 'Bitbucket Issue' => 'Bitbucket fråga', + 'Commit made by @%s on Bitbucket' => 'Bidrag gjort av @%s på Bitbucket', + 'Commit made by @%s on Github' => 'Bidrag gjort av @%s på Github', + 'By @%s on Github' => 'Av @%s på Github', + 'Commit made by @%s on Gitlab' => 'Bidrag gjort av @%s på Gitlab', + 'Add a comment log when moving the task between columns' => 'Lägg till en kommentarslogg när en uppgift flyttas mellan kolumner', + 'Move the task to another column when the category is changed' => 'Flyttas uppgiften till en annan kolumn när kategorin ändras', + 'Send a task by email to someone' => 'Skicka en uppgift med e-post till någon', + 'Reopen a task' => 'Återöppna en uppgift', + 'Bitbucket issue opened' => 'Bitbucketfråga öppnad', + 'Bitbucket issue closed' => 'Bitbucketfråga stängd', + 'Bitbucket issue reopened' => 'Bitbucketfråga återöppnad', + 'Bitbucket issue assignee change' => 'Bitbucketfråga tilldelningsändring', + 'Bitbucket issue comment created' => 'Bitbucketfråga kommentar skapad', + 'Column change' => 'Kolumnändring', + 'Position change' => 'Positionsändring', + 'Swimlane change' => 'Swimlaneändring', + 'Assignee change' => 'Tilldelningsändring', + '[%s] Overdue tasks' => '[%s] Försenade uppgifter', + 'Notification' => 'Notis', + '%s moved the task #%d to the first swimlane' => '%s flyttade uppgiften #%d till första swimlane', + '%s moved the task #%d to the swimlane "%s"' => '%s flyttade uppgiften #%d till swimlane "%s"', + 'Swimlane' => 'Swimlane', + 'Budget overview' => 'Budgetöversikt', + 'Type' => 'Typ', + 'There is not enough data to show something.' => 'Det finns inte tillräckligt mycket data för att visa något.', + 'Gravatar' => 'Gravatar', + 'Hipchat' => 'Hipchat', + 'Slack' => 'Slack', + '%s moved the task %s to the first swimlane' => '%s flyttade uppgiften %s till första swimlane', + '%s moved the task %s to the swimlane "%s"' => '%s flyttade uppgiften %s till swimlane "%s"', + 'This report contains all subtasks information for the given date range.' => 'Denna rapport innehåller all deluppgiftsinformation för det givna datumintervallet.', + 'This report contains all tasks information for the given date range.' => 'Denna rapport innehåller all uppgiftsinformation för det givna datumintervallet.', + 'Project activities for %s' => 'Projektaktiviteter för %s', + 'view the board on Kanboard' => 'visa tavlan på Kanboard', + 'The task have been moved to the first swimlane' => 'Uppgiften har flyttats till första swimlane', + 'The task have been moved to another swimlane:' => 'Uppgiften har flyttats till en annan swimlane:', + 'Overdue tasks for the project "%s"' => 'Försenade uppgifter för projektet "%s"', + 'New title: %s' => 'Ny titel: %s', + 'The task is not assigned anymore' => 'Uppgiften är inte länge tilldelad', + 'New assignee: %s' => 'Ny tilldelning: %s', + 'There is no category now' => 'Det finns ingen kategori nu', + 'New category: %s' => 'Ny kategori: %s', + 'New color: %s' => 'Ny färg: %s', + 'New complexity: %d' => 'Ny komplexitet: %d', + 'The due date have been removed' => 'Förfallodatumet har tagits bort', + 'There is no description anymore' => 'Det finns ingen beskrivning längre', + 'Recurrence settings have been modified' => 'Återkommande inställning har ändrats', + 'Time spent changed: %sh' => 'Spenderad tid har ändrats: %sh', + 'Time estimated changed: %sh' => 'Tidsuppskattning ändrad: %sh', + 'The field "%s" have been updated' => 'Fältet "%s" har uppdaterats', + 'The description have been modified' => 'Beskrivningen har modifierats', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Vill du verkligen stänga uppgiften "%s" och alla deluppgifter?', + 'Swimlane: %s' => 'Swimlane: %s', + 'I want to receive notifications for:' => 'Jag vill få notiser för:', + 'All tasks' => 'Alla uppgifter', + 'Only for tasks assigned to me' => 'Bara för uppgifter tilldelade mig', + 'Only for tasks created by me' => 'Bara för uppgifter skapade av mig', + 'Only for tasks created by me and assigned to me' => 'Bara för uppgifter skapade av mig och tilldelade till mig', + '%A' => '%A', + '%b %e, %Y, %k:%M %p' => '%b %e, %Y, %k:%M %p', + 'New due date: %B %e, %Y' => 'Nytt förfallodatum: %B %e, %Y', + 'Start date changed: %B %e, %Y' => 'Startdatum ändrat: %B %e, %Y', + '%k:%M %p' => '%k:%M %p', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Totalt för alla kolumner', + 'You need at least 2 days of data to show the chart.' => 'Du behöver minst två dagars data för att visa diagrammet.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Stoppa timer', + 'Start timer' => 'Starta timer', + 'Add project member' => 'Lägg till projektmedlem', + 'Enable notifications' => 'Aktivera notiser', + 'My activity stream' => 'Min aktivitetsström', + 'My calendar' => 'Min kalender', + 'Search tasks' => 'Sök uppgifter', + 'Back to the calendar' => 'Tillbaka till kalendern', + 'Filters' => 'Filter', + 'Reset filters' => 'Återställ filter', + 'My tasks due tomorrow' => 'Mina uppgifter förfaller imorgon', + 'Tasks due today' => 'Uppgifter förfaller idag', + 'Tasks due tomorrow' => 'Uppgifter förfaller imorgon', + 'Tasks due yesterday' => 'Uppgifter förföll igår', + 'Closed tasks' => 'Stängda uppgifter', + 'Open tasks' => 'Öppna uppgifter', + 'Not assigned' => 'Inte tilldelad', + 'View advanced search syntax' => 'Visa avancerad söksyntax', + 'Overview' => 'Översikts', + '%b %e %Y' => '%b %e %Y', + 'Board/Calendar/List view' => 'Tavla/Kalender/Listvy', + 'Switch to the board view' => 'Växla till tavelvy', + 'Switch to the calendar view' => 'Växla till kalendervy', + 'Switch to the list view' => 'Växla till listvy', + 'Go to the search/filter box' => 'Gå till sök/filter box', + 'There is no activity yet.' => 'Det finns ingen aktivitet ännu.', + 'No tasks found.' => 'Inga uppgifter hittades.', + 'Keyboard shortcut: "%s"' => 'Tangentbordsgenväg: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filter', + 'Advanced search' => 'Avancerad sök', + 'Example of query: ' => 'Exempel på fråga', + 'Search by project: ' => 'Sök efter projekt:', + 'Search by column: ' => 'Sök efter kolumn:', + 'Search by assignee: ' => 'Sök efter tilldelad:', + 'Search by color: ' => 'Sök efter färg:', + 'Search by category: ' => 'Sök efter kategori:', + 'Search by description: ' => 'Sök efter beskrivning', + 'Search by due date: ' => 'Sök efter förfallodatum', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index 6c21c41b..a44d0116 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'ต้องการชื่อโปรเจค', 'This project must be unique' => 'ชื่อโปรเจคต้องไม่ซ้ำ', 'The title is required' => 'ต้องการหัวเรื่อง', - 'There is no active project, the first step is to create a new project.' => 'ไม่มีโปรเจคที่ทำงานอยู่, ต้องการสร้างโปรเจคใหม่', 'Settings saved successfully.' => 'บันทึกการตั้งค่าเรียบร้อยแล้ว', 'Unable to save your settings.' => 'ไม่สามารถบันทึกการตั้งค่าได้', 'Database optimization done.' => 'ปรับปรุงฐานข้อมูลเรียบร้อยแล้ว', @@ -165,8 +164,6 @@ return array( 'Date created' => 'สร้างวันที่', 'Date completed' => 'เรียบร้อยวันที่', 'Id' => 'ไอดี', - 'Completed tasks' => 'งานที่เสร็จแล้ว', - 'Completed tasks for "%s"' => 'งานที่เสร็จแล้วสำหรับ « %s »', '%d closed tasks' => '%d งานที่ปิด', 'No task for this project' => 'ไม่มีงานสำหรับโปรเจคนี้', 'Public link' => 'ลิงค์สาธารณะ', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'หมดอายุวันที่', 'Remember Me' => 'จดจำฉัน', 'Creation date' => 'สร้างวันที่', - 'Filter by user' => 'กรองตามผู้ใช้', - 'Filter by due date' => 'กรองตามวันครบกำหนด', 'Everybody' => 'ทุกคน', 'Open' => 'เปิด', 'Closed' => 'ปิด', 'Search' => 'ค้นหา', 'Nothing found.' => 'ค้นหาไม่พบ.', - 'Search in the project "%s"' => 'ค้นหาในโปรเจค "%s"', 'Due date' => 'วันที่ครบกำหนด', 'Others formats accepted: %s and %s' => 'รูปแบบอื่นที่ได้รับการยอมรับ: %s และ %s', 'Description' => 'คำอธิบาย', '%d comments' => '%d ความคิดเห็น', '%d comment' => '%d ความคิดเห็น', 'Email address invalid' => 'อีเมลผิด', - 'Your Google Account is not linked anymore to your profile.' => 'กูเกิลแอคเคาท์ไม่ได้เชื่อมต่อกับประวัติของคุณ', - 'Unable to unlink your Google Account.' => 'ไม่สามารถยกเลิกการเชื่อมต่อกับกูเกิลแอคเคาท์', - 'Google authentication failed' => 'การยืนยันกับกูเกิลผิดพลาด', - 'Unable to link your Google Account.' => 'ไม่สามารถเชื่อมต่อกับกูเกิลแอคเคาท์', - 'Your Google Account is linked to your profile successfully.' => 'กูเกลิแอคเคาท์เชื่อมต่อกับประวัติของคุณเรียบร้อยแล้ว', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'อีเมล', 'Link my Google Account' => 'เชื่อมต่อกับกูเกิลแอคเคาท์', 'Unlink my Google Account' => 'ไม่เชื่อมต่อกับกูเกิลแอคเคาท์', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'ชื่อกลุ่ม', 'Add a new category' => 'เพิ่มกลุ่มใหม่', 'Do you really want to remove this category: "%s"?' => 'คุณต้องการลบกลุ่ม "%s" ใช่หรือไม่?', - 'Filter by category' => 'กรองตามกลุ่ม', 'All categories' => 'กลุ่มทั้งหมด', 'No category' => 'ไม่มีกลุ่ม', 'The name is required' => 'ต้องการชื่อ', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'ขนาดสูงสุด:', 'Unable to upload the file.' => 'ไม่สามารถอัพโหลดไฟล์ได้', 'Display another project' => 'แสดงโปรเจคอื่น', - 'Your GitHub account was successfully linked to your profile.' => 'กิทฮับแอคเคาท์เชื่อมต่อกับประวัติเรียบร้อยแล้ว', - 'Unable to link your GitHub Account.' => 'ไม่สามารถเชื่อมต่อกับกิทฮับแอคเคาท์ได้', - 'GitHub authentication failed' => 'การยืนยันกิทฮับผิดพลาด', - 'Your GitHub account is no longer linked to your profile.' => 'กิทฮับแอคเคาท์ไม่ได้มีการเชื่อมโยงไปยังโปรไฟล์ของคุณ', - 'Unable to unlink your GitHub Account.' => 'ไม่สามารถยกเลิกการเชื่อมต่อกิทฮับแอคเคาท์ได้', - 'Login with my GitHub Account' => 'เข้าใช้ด้วยกิทฮับแอคเคาท์', - 'Link my GitHub Account' => 'เชื่อมกับกิทฮับแอคเคาท์', - 'Unlink my GitHub Account' => 'ยกเลิกการเชื่อมกับกิทอับแอคเคาท์', + 'Login with my Github Account' => 'เข้าใช้ด้วยกิทฮับแอคเคาท์', + 'Link my Github Account' => 'เชื่อมกับกิทฮับแอคเคาท์', + 'Unlink my Github Account' => 'ยกเลิกการเชื่อมกับกิทอับแอคเคาท์', 'Created by %s' => 'สร้างโดย %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'แก้ไขล่าสุดวันที่ %B %e, %Y เวลา %k:%M %p', 'Tasks Export' => 'ส่งออกงาน', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s ปรับปรุงความคิดเห็นในงานแล้ว %s', '%s commented the task %s' => '%s แสดงความคิดเห็นของงานแล้ว %s', '%s\'s activity' => 'กิจกรรม %s', - 'No activity.' => 'ไม่มีกิจกรรม', 'RSS feed' => 'RSS feed', '%s updated a comment on the task #%d' => '%s ปรับปรุงความคิดเห็นบนงานแล้ว #%d', '%s commented on the task #%d' => '%s แสดงความคิดเห็นบนงานแล้ว #%d', @@ -605,14 +591,9 @@ return array( 'Language:' => 'ภาษา:', 'Timezone:' => 'เขตเวลา:', 'All columns' => 'คอลัมน์ทั้งหมด', - 'Calendar for "%s"' => 'ปฏิทินสำหรับ "%s"', - 'Filter by column' => 'กรองโดยคอลัมน์', - 'Filter by status' => 'กรองโดยสถานะ', 'Calendar' => 'ปฏิทิน', 'Next' => 'ต่อไป', // '#%d' => '', - 'Filter by color' => 'กรองโดยสี', - 'Filter by swimlane' => 'กรองโดยสวิมเลน', 'All swimlanes' => 'สวิมเลนทั้งหมด', 'All colors' => 'สีทั้งหมด', 'All status' => 'สถานะทั้งหมด', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'ติดตามเวลา', 'You already have one subtask in progress' => 'คุณมีหนึ่งงานย่อยที่กำลังทำงาน', // 'Which parts of the project do you want to duplicate?' => '', - 'Change dashboard view' => 'เปลี่ยนมุมมองแดชบอร์ด', - 'Show/hide activities' => 'แสดง/ซ่อน กิจกรรม', - 'Show/hide projects' => 'แสดง/ซ่อน โปรเจค', - 'Show/hide subtasks' => 'แสดง/ซ่อน งานย่อย', - 'Show/hide tasks' => 'แสดง/ซ่อน งาน', - // 'Disable login form' => '', - 'Show/hide calendar' => 'แสดง/ซ่อน ปฎิทิน', - 'User calendar' => 'ปฏิทินผู้ใช้', + // 'Disallow login form' => '', // 'Bitbucket commit received' => '', // 'Bitbucket webhooks' => '', // 'Help on Bitbucket webhooks' => '', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'คีย์ลัด', 'Open board switcher' => 'เปิดการสลับบอร์ด', 'Application' => 'แอพพลิเคชัน', - 'Filter recently updated' => 'ตัวกรองที่ปรับปรุงเร็วๆ นี้', 'since %B %e, %Y at %k:%M %p' => 'เริ่ม %B %e, %Y เวลา %k:%M %p', - 'More filters' => 'ตัวกรองเพิ่มเติม', 'Compact view' => 'มุมมองพอดี', 'Horizontal scrolling' => 'เลื่อนตามแนวนอน', 'Compact/wide view' => 'พอดี/กว้าง มุมมอง', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index a8440407..d394a67a 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => 'Proje adı gerekli', 'This project must be unique' => 'Bu projenin tekil olması gerekli', 'The title is required' => 'Başlık gerekli', - 'There is no active project, the first step is to create a new project.' => 'Aktif bir proje yok. İlk aşama yeni bir proje oluşturmak olmalı.', 'Settings saved successfully.' => 'Ayarlar başarıyla kaydedildi.', 'Unable to save your settings.' => 'Ayarlarınız kaydedilemedi.', 'Database optimization done.' => 'Veritabanı optimizasyonu tamamlandı.', @@ -165,8 +164,6 @@ return array( 'Date created' => 'Oluşturulma tarihi', 'Date completed' => 'Tamamlanma tarihi', 'Id' => 'Kod', - 'Completed tasks' => 'Tamamlanan görevler', - 'Completed tasks for "%s"' => '"%s" için tamamlanan görevler', '%d closed tasks' => '%d kapatılmış görevler', // 'No task for this project' => '', 'Public link' => 'Dışa açık link', @@ -255,25 +252,21 @@ return array( 'Expiration date' => 'Geçerlilik sonu', 'Remember Me' => 'Beni hatırla', 'Creation date' => 'Oluşturulma tarihi', - 'Filter by user' => 'Kullanıcıya göre filtrele', - 'Filter by due date' => 'Termine göre filtrele', 'Everybody' => 'Herkes', 'Open' => 'Açık', 'Closed' => 'Kapalı', 'Search' => 'Ara', 'Nothing found.' => 'Hiçbir şey bulunamadı', - 'Search in the project "%s"' => '"%s" Projesinde ara', 'Due date' => 'Termin', 'Others formats accepted: %s and %s' => 'Diğer kabul edilen formatlar: %s ve %s', 'Description' => 'Açıklama', '%d comments' => '%d yorumlar', '%d comment' => '%d yorum', 'Email address invalid' => 'E-Posta adresi geçersiz', - 'Your Google Account is not linked anymore to your profile.' => 'Google hesabınız artık profilinize bağlı değil', - 'Unable to unlink your Google Account.' => 'Google hesabınızla bağ koparılamadı', - 'Google authentication failed' => 'Google hesap doğrulaması başarısız', - 'Unable to link your Google Account.' => 'Google hesabınızla bağ oluşturulamadı', - 'Your Google Account is linked to your profile successfully.' => 'Google hesabınız profilinize başarıyla bağlandı', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-Posta', 'Link my Google Account' => 'Google hesabımla bağ oluştur', 'Unlink my Google Account' => 'Google hesabımla bağı kaldır', @@ -301,7 +294,6 @@ return array( 'Category Name' => 'Kategori adı', 'Add a new category' => 'Yeni kategori ekle', 'Do you really want to remove this category: "%s"?' => 'Bu kategoriyi silmek istediğinize emin misiniz: "%s"?', - 'Filter by category' => 'Kategoriye göre filtrele', 'All categories' => 'Tüm kategoriler', 'No category' => 'Kategori Yok', 'The name is required' => 'İsim gerekli', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => 'Maksimum boyutu', 'Unable to upload the file.' => 'Karşıya yükleme başarısız', 'Display another project' => 'Başka bir proje göster', - 'Your GitHub account was successfully linked to your profile.' => 'GitHub Hesabınız Profilinize bağlandı.', - 'Unable to link your GitHub Account.' => 'GitHub hesabınızla bağ oluşturulamadı.', - // 'GitHub authentication failed' => '', - // 'Your GitHub account is no longer linked to your profile.' => '', - // 'Unable to unlink your GitHub Account.' => '', - // 'Login with my GitHub Account' => '', - // 'Link my GitHub Account' => '', - // 'Unlink my GitHub Account' => '', + // 'Login with my Github Account' => '', + // 'Link my Github Account' => '', + // 'Unlink my Github Account' => '', 'Created by %s' => '%s tarafından oluşturuldu', 'Last modified on %B %e, %Y at %k:%M %p' => 'Son değişiklik tarihi %d.%m.%Y, saati %H:%M', 'Tasks Export' => 'Görevleri dışa aktar', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s kullanıcısı %s görevinde bir yorumu güncelledi', '%s commented the task %s' => '%s kullanıcısı %s görevine yorum ekledi', '%s\'s activity' => '%s\'in aktivitesi', - 'No activity.' => 'Aktivite yok.', 'RSS feed' => 'RSS kaynağı', '%s updated a comment on the task #%d' => '%s kullanıcısı #%d nolu görevde bir yorumu güncelledi', '%s commented on the task #%d' => '%s kullanıcısı #%d nolu göreve yorum ekledi', @@ -605,14 +591,9 @@ return array( 'Language:' => 'Dil:', 'Timezone:' => 'Saat dilimi:', 'All columns' => 'Tüm sütunlar', - 'Calendar for "%s"' => '"%s" için takvim', - 'Filter by column' => 'Sütuna göre filtrele', - 'Filter by status' => 'Duruma göre filtrele', 'Calendar' => 'Takvim', 'Next' => 'Sonraki', '#%d' => '#%d', - 'Filter by color' => 'Renklere göre filtrele', - 'Filter by swimlane' => 'Kulvara göre filtrele', 'All swimlanes' => 'Tüm Kulvarlar', 'All colors' => 'Tüm Renkler', 'All status' => 'Tüm Durumlar', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => 'Zaman takibi', 'You already have one subtask in progress' => 'Zaten işlemde olan bir alt görev var', 'Which parts of the project do you want to duplicate?' => 'Projenin hangi kısımlarının kopyasını oluşturmak istiyorsunuz?', - 'Change dashboard view' => 'Anasayfa görünümünü değiştir', - 'Show/hide activities' => 'Aktiviteleri göster/gizle', - 'Show/hide projects' => 'Projeleri göster/gizle', - 'Show/hide subtasks' => 'Alt görevleri göster/gizle', - 'Show/hide tasks' => 'Görevleri göster/gizle', - 'Disable login form' => 'Giriş formunu devre dışı bırak', - 'Show/hide calendar' => 'Takvimi göster/gizle', - 'User calendar' => 'Kullanıcı takvimi', + // 'Disallow login form' => '', 'Bitbucket commit received' => 'Bitbucket commit alındı', 'Bitbucket webhooks' => 'Bitbucket webhooks', 'Help on Bitbucket webhooks' => 'Bitbucket webhooks için yardım', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => 'Klavye kısayolları', 'Open board switcher' => 'Tablo seçim listesini aç', 'Application' => 'Uygulama', - 'Filter recently updated' => 'Son güncellenenleri göster', 'since %B %e, %Y at %k:%M %p' => '%B %e, %Y saat %k:%M %p\'den beri', - 'More filters' => 'Daha fazla filtre', 'Compact view' => 'Ekrana sığdır', 'Horizontal scrolling' => 'Geniş görünüm', 'Compact/wide view' => 'Ekrana sığdır / Geniş görünüm', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index f46a63d0..4de3aeaf 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -126,7 +126,6 @@ return array( 'The project name is required' => '需要指定项目名称', 'This project must be unique' => '项目名称必须唯一', 'The title is required' => '需要指定标题', - 'There is no active project, the first step is to create a new project.' => '尚无活跃项目,请首先创建一个新项目。', 'Settings saved successfully.' => '设置成功保存。', 'Unable to save your settings.' => '无法保存你的设置。', 'Database optimization done.' => '数据库优化完成。', @@ -165,8 +164,6 @@ return array( 'Date created' => '创建时间', 'Date completed' => '完成时间', 'Id' => '编号', - 'Completed tasks' => '已完成任务', - 'Completed tasks for "%s"' => '"%s"已经完成的任务', '%d closed tasks' => '%d个已关闭任务', 'No task for this project' => '该项目尚无任务', 'Public link' => '公开链接', @@ -255,25 +252,21 @@ return array( 'Expiration date' => '过期', 'Remember Me' => '记住我', 'Creation date' => '创建日期', - 'Filter by user' => '按用户过滤', - 'Filter by due date' => '按到期时间过滤', 'Everybody' => '所有人', 'Open' => '打开', 'Closed' => '关闭', 'Search' => '查找', 'Nothing found.' => '没找到。', - 'Search in the project "%s"' => '在项目"%s"中查找', 'Due date' => '到期时间', 'Others formats accepted: %s and %s' => '可以使用的其它格式:%s 和 %s', 'Description' => '描述', '%d comments' => '%d个评论', '%d comment' => '%d个评论', 'Email address invalid' => '电子邮件地址无效', - 'Your Google Account is not linked anymore to your profile.' => '您的google帐号不再与您的账户配置关联。', - 'Unable to unlink your Google Account.' => '无法去除您google帐号的关联', - 'Google authentication failed' => 'google验证失败', - 'Unable to link your Google Account.' => '无法关联您的google帐号。', - 'Your Google Account is linked to your profile successfully.' => '您的google帐号已成功与账户配置关联。', + // 'Your external account is not linked anymore to your profile.' => '', + // 'Unable to unlink your external account.' => '', + // 'External authentication failed' => '', + // 'Your external account is linked to your profile successfully.' => '', 'Email' => '电子邮件', 'Link my Google Account' => '关联我的google帐号', 'Unlink my Google Account' => '去除我的google帐号关联', @@ -301,7 +294,6 @@ return array( 'Category Name' => '分类名称', 'Add a new category' => '加入新分类', 'Do you really want to remove this category: "%s"?' => '确定要移除分类"%s"吗?', - 'Filter by category' => '按分类过滤', 'All categories' => '所有分类', 'No category' => '无分类', 'The name is required' => '必须要有名字', @@ -344,14 +336,9 @@ return array( 'Maximum size: ' => '大小上限:', 'Unable to upload the file.' => '无法上传文件', 'Display another project' => '显示其它项目', - 'Your GitHub account was successfully linked to your profile.' => 'GitHub账号已经成功链接到您的用户', - 'Unable to link your GitHub Account.' => '无法链接到GitHub账户', - 'GitHub authentication failed' => 'GitHub认证失败', - 'Your GitHub account is no longer linked to your profile.' => 'Github账号已经不再链接到您的用户', - 'Unable to unlink your GitHub Account.' => '无法链接GitHub账号', - 'Login with my GitHub Account' => '用Github账号登录', - 'Link my GitHub Account' => '链接GitHub账号', - 'Unlink my GitHub Account' => '取消GitHub账号链接', + 'Login with my Github Account' => '用Github账号登录', + 'Link my Github Account' => '链接Github账号', + 'Unlink my Github Account' => '取消Github账号链接', 'Created by %s' => '创建者:%s', 'Last modified on %B %e, %Y at %k:%M %p' => '最后修改:%Y/%m/%d/ %H:%M', 'Tasks Export' => '任务导出', @@ -445,7 +432,6 @@ return array( '%s updated a comment on the task %s' => '%s 更新了任务 %s的评论', '%s commented the task %s' => '%s 评论了任务 %s', '%s\'s activity' => '%s的动态', - 'No activity.' => '无动态', 'RSS feed' => 'RSS 链接', '%s updated a comment on the task #%d' => '%s 更新了任务 #%d 的评论', '%s commented on the task #%d' => '%s 评论了任务 #%d', @@ -605,14 +591,9 @@ return array( 'Language:' => '语言:', 'Timezone:' => '时区:', 'All columns' => '全部栏目', - 'Calendar for "%s"' => '"%s"的日程表', - 'Filter by column' => '按栏目过滤', - 'Filter by status' => '按状态过滤', 'Calendar' => '日程表', 'Next' => '前进', '#%d' => '#%d', - 'Filter by color' => '按颜色过滤', - 'Filter by swimlane' => '按泳道过滤', 'All swimlanes' => '全部泳道', 'All colors' => '全部颜色', 'All status' => '全部状态', @@ -627,14 +608,7 @@ return array( 'Time Tracking' => '时间记录', 'You already have one subtask in progress' => '你已经有了一个进行中的子任务', 'Which parts of the project do you want to duplicate?' => '要复制项目的哪些内容?', - 'Change dashboard view' => '修改仪表板视图', - 'Show/hide activities' => '显示/隐藏活动', - 'Show/hide projects' => '显示/隐藏项目', - 'Show/hide subtasks' => '显示/隐藏子任务', - 'Show/hide tasks' => '显示/隐藏任务', - 'Disable login form' => '禁用登录界面', - 'Show/hide calendar' => '显示/隐藏日程表', - 'User calendar' => '用户日程表', + // 'Disallow login form' => '', 'Bitbucket commit received' => '收到Bitbucket提交', 'Bitbucket webhooks' => 'Bitbucket网络钩子', 'Help on Bitbucket webhooks' => 'Bitbucket网络钩子帮助', @@ -688,9 +662,7 @@ return array( 'Keyboard shortcuts' => '键盘快捷方式', 'Open board switcher' => '打开面板切换器', 'Application' => '应用程序', - 'Filter recently updated' => '过滤最近的更新', // 'since %B %e, %Y at %k:%M %p' => '', - 'More filters' => '更多过滤', 'Compact view' => '紧凑视图', 'Horizontal scrolling' => '水平滚动', 'Compact/wide view' => '紧凑/宽视图', @@ -921,7 +893,6 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', - // 'There is no completed tasks at the moment.' => '', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -938,7 +909,6 @@ return array( // 'The description have been modified' => '', // 'Do you really want to close the task "%s" as well as all subtasks?' => '', // 'Swimlane: %s' => '', - // 'Project calendar' => '', // 'I want to receive notifications for:' => '', // 'All tasks' => '', // 'Only for tasks assigned to me' => '', @@ -958,4 +928,79 @@ return array( // 'Start timer' => '', // 'Add project member' => '', // 'Enable notifications' => '', + // 'My activity stream' => '', + // 'My calendar' => '', + // 'Search tasks' => '', + // 'Back to the calendar' => '', + // 'Filters' => '', + // 'Reset filters' => '', + // 'My tasks due tomorrow' => '', + // 'Tasks due today' => '', + // 'Tasks due tomorrow' => '', + // 'Tasks due yesterday' => '', + // 'Closed tasks' => '', + // 'Open tasks' => '', + // 'Not assigned' => '', + // 'View advanced search syntax' => '', + // 'Overview' => '', + // '%b %e %Y' => '', + // 'Board/Calendar/List view' => '', + // 'Switch to the board view' => '', + // 'Switch to the calendar view' => '', + // 'Switch to the list view' => '', + // 'Go to the search/filter box' => '', + // 'There is no activity yet.' => '', + // 'No tasks found.' => '', + // 'Keyboard shortcut: "%s"' => '', + // 'List' => '', + // 'Filter' => '', + // 'Advanced search' => '', + // 'Example of query: ' => '', + // 'Search by project: ' => '', + // 'Search by column: ' => '', + // 'Search by assignee: ' => '', + // 'Search by color: ' => '', + // 'Search by category: ' => '', + // 'Search by description: ' => '', + // 'Search by due date: ' => '', + // 'Lead and Cycle time for "%s"' => '', + // 'Average time spent into each column for "%s"' => '', + // 'Average time spent into each column' => '', + // 'Average time spent' => '', + // 'This chart show the average time spent into each column for the last %d tasks.' => '', + // 'Average Lead and Cycle time' => '', + // 'Average lead time: ' => '', + // 'Average cycle time: ' => '', + // 'Cycle Time' => '', + // 'Lead Time' => '', + // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', + // 'Average time into each column' => '', + // 'Lead and cycle time' => '', + // 'Google Authentication' => '', + // 'Help on Google authentication' => '', + // 'Github Authentication' => '', + // 'Help on Github authentication' => '', + // 'Channel/Group/User (Optional)' => '', + // 'Lead time: ' => '', + // 'Cycle time: ' => '', + // 'Time spent into each column' => '', + // 'The lead time is the duration between the task creation and the completion.' => '', + // 'The cycle time is the duration between the start date and the completion.' => '', + // 'If the task is not closed the current time is used instead of the completion date.' => '', + // 'Set automatically the start date' => '', + // 'Edit Authentication' => '', + // 'Google Id' => '', + // 'Github Id' => '', + // 'Remote user' => '', + // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', + // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', + // 'By @%s on Gitlab' => '', + // 'Gitlab issue comment created' => '', + // 'New remote user' => '', + // 'New local user' => '', + // 'Default task color' => '', + // 'Hide sidebar' => '', + // 'Expand sidebar' => '', + // 'This feature does not work with all browsers.' => '', + // 'There is no destination project available.' => '', ); diff --git a/app/Model/Acl.php b/app/Model/Acl.php index 91ed035b..95056de6 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -18,12 +18,12 @@ class Acl extends Base */ private $public_acl = array( 'auth' => array('login', 'check'), - 'user' => array('google', 'github'), 'task' => array('readonly'), 'board' => array('readonly'), 'webhook' => '*', 'ical' => '*', 'feed' => '*', + 'oauth' => array('google', 'github'), ); /** @@ -37,9 +37,14 @@ class Acl extends Base 'comment' => '*', 'file' => '*', 'project' => array('show'), - 'projectinfo' => array('tasks', 'search', 'activity'), + 'listing' => '*', + 'activity' => '*', 'subtask' => '*', 'task' => '*', + 'taskduplication' => '*', + 'taskcreation' => '*', + 'taskmodification' => '*', + 'taskstatus' => '*', 'tasklink' => '*', 'timer' => '*', 'calendar' => array('show', 'project'), @@ -69,7 +74,7 @@ class Acl extends Base * @var array */ private $admin_acl = array( - 'user' => array('index', 'create', 'save', 'remove'), + 'user' => array('index', 'create', 'save', 'remove', 'authentication'), 'config' => '*', 'link' => '*', 'project' => array('remove'), diff --git a/app/Model/Action.php b/app/Model/Action.php index d0607794..5e994c99 100644 --- a/app/Model/Action.php +++ b/app/Model/Action.php @@ -92,6 +92,7 @@ class Action extends Base GitlabWebhook::EVENT_COMMIT => t('Gitlab commit received'), GitlabWebhook::EVENT_ISSUE_OPENED => t('Gitlab issue opened'), GitlabWebhook::EVENT_ISSUE_CLOSED => t('Gitlab issue closed'), + GitlabWebhook::EVENT_ISSUE_COMMENT => t('Gitlab issue comment created'), BitbucketWebhook::EVENT_COMMIT => t('Bitbucket commit received'), BitbucketWebhook::EVENT_ISSUE_OPENED => t('Bitbucket issue opened'), BitbucketWebhook::EVENT_ISSUE_CLOSED => t('Bitbucket issue closed'), diff --git a/app/Model/Authentication.php b/app/Model/Authentication.php index 86c1c43f..31969b57 100644 --- a/app/Model/Authentication.php +++ b/app/Model/Authentication.php @@ -49,11 +49,6 @@ class Authentication extends Base return false; } - // We update each time the RememberMe cookie tokens - if ($this->backend('rememberMe')->hasCookie()) { - $this->backend('rememberMe')->refresh(); - } - return true; } diff --git a/app/Model/Board.php b/app/Model/Board.php index f6f968f4..bcf77b3e 100644 --- a/app/Model/Board.php +++ b/app/Model/Board.php @@ -237,10 +237,11 @@ class Board extends Base * Get all tasks sorted by columns and swimlanes * * @access public - * @param integer $project_id Project id + * @param integer $project_id + * @param callable $callback * @return array */ - public function getBoard($project_id) + public function getBoard($project_id, $callback = null) { $swimlanes = $this->swimlane->getSwimlanes($project_id); $columns = $this->getColumns($project_id); @@ -253,7 +254,11 @@ class Board extends Base $swimlanes[$i]['nb_tasks'] = 0; for ($j = 0; $j < $nb_columns; $j++) { - $swimlanes[$i]['columns'][$j]['tasks'] = $this->taskFinder->getTasksByColumnAndSwimlane($project_id, $columns[$j]['id'], $swimlanes[$i]['id']); + + $column_id = $columns[$j]['id']; + $swimlane_id = $swimlanes[$i]['id']; + + $swimlanes[$i]['columns'][$j]['tasks'] = $callback === null ? $this->taskFinder->getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id) : $callback($project_id, $column_id, $swimlane_id); $swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']); $swimlanes[$i]['columns'][$j]['score'] = $this->getColumnSum($swimlanes[$i]['columns'][$j]['tasks'], 'score'); $swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks']; diff --git a/app/Model/Color.php b/app/Model/Color.php index 1fd81b85..73e5d629 100644 --- a/app/Model/Color.php +++ b/app/Model/Color.php @@ -147,7 +147,7 @@ class Color extends Base */ public function getDefaultColor() { - return 'yellow'; // TODO: make this parameter configurable + return $this->config->get('default_color', 'yellow'); } /** diff --git a/app/Model/DateParser.php b/app/Model/DateParser.php index be79a92e..79a8385c 100644 --- a/app/Model/DateParser.php +++ b/app/Model/DateParser.php @@ -85,8 +85,7 @@ class DateParser extends Base */ public function getTimestamp($value) { - foreach ($this->getDateFormats() as $format) { - + foreach ($this->getAllFormats() as $format) { $timestamp = $this->getValidDate($value, $format); if ($timestamp !== 0) { @@ -98,6 +97,25 @@ class DateParser extends Base } /** + * Get all combinations of date/time formats + * + * @access public + * @return []string + */ + public function getAllFormats() + { + $formats = array(); + + foreach ($this->getDateFormats() as $date) { + foreach ($this->getTimeFormats() as $time) { + $formats[] = $date.' '.$time; + } + } + + return array_merge($formats, $this->getDateFormats()); + } + + /** * Return the list of supported date formats (for the parser) * * @access public @@ -113,6 +131,21 @@ class DateParser extends Base } /** + * Return the list of supported time formats (for the parser) + * + * @access public + * @return string[] + */ + public function getTimeFormats() + { + return array( + 'H:i', + 'g:i A', + 'g:iA', + ); + } + + /** * Return the list of available date formats (for the config page) * * @access public @@ -143,7 +176,7 @@ class DateParser extends Base * Get a timetstamp from an ISO date format * * @access public - * @param string $date Date format + * @param string $date * @return integer */ public function getTimestampFromIsoFormat($date) @@ -166,7 +199,6 @@ class DateParser extends Base } foreach ($fields as $field) { - if (! empty($values[$field])) { $values[$field] = date($format, $values[$field]); } @@ -180,15 +212,16 @@ class DateParser extends Base * Convert date (form input data) * * @access public - * @param array $values Database values - * @param string[] $fields Date fields + * @param array $values Database values + * @param string[] $fields Date fields + * @param boolean $keep_time Keep time or not */ - public function convert(array &$values, array $fields) + public function convert(array &$values, array $fields, $keep_time = false) { foreach ($fields as $field) { - if (! empty($values[$field]) && ! is_numeric($values[$field])) { - $values[$field] = $this->removeTimeFromTimestamp($this->getTimestamp($values[$field])); + $timestamp = $this->getTimestamp($values[$field]); + $values[$field] = $keep_time ? $timestamp : $this->removeTimeFromTimestamp($timestamp); } } } diff --git a/app/Model/Notification.php b/app/Model/Notification.php index 6a50f7ba..9628e344 100644 --- a/app/Model/Notification.php +++ b/app/Model/Notification.php @@ -37,7 +37,6 @@ class Notification extends Base public function sendOverdueTaskNotifications() { $tasks = $this->taskFinder->getOverdueTasks(); - $projects = array(); foreach ($this->groupByColumn($tasks, 'project_id') as $project_id => $project_tasks) { @@ -157,10 +156,9 @@ class Notification extends Base * * @access public * @param array $user - * @param array $event_data * @return boolean */ - public function filterNone(array $user, array $event_data) + public function filterNone(array $user) { return $user['notifications_filter'] == self::FILTER_NONE; } diff --git a/app/Model/ProjectAnalytic.php b/app/Model/ProjectAnalytic.php index a663f921..8ac22626 100644 --- a/app/Model/ProjectAnalytic.php +++ b/app/Model/ProjectAnalytic.php @@ -49,7 +49,7 @@ class ProjectAnalytic extends Base * Get users repartition * * @access public - * @param integer $project_id Project id + * @param integer $project_id * @return array */ public function getUserRepartition($project_id) @@ -87,4 +87,96 @@ class ProjectAnalytic extends Base return array_values($metrics); } + + /** + * Get the average lead and cycle time + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAverageLeadAndCycleTime($project_id) + { + $stats = array( + 'count' => 0, + 'total_lead_time' => 0, + 'total_cycle_time' => 0, + 'avg_lead_time' => 0, + 'avg_cycle_time' => 0, + ); + + $tasks = $this->db + ->table(Task::TABLE) + ->columns('date_completed', 'date_creation', 'date_started') + ->eq('project_id', $project_id) + ->desc('id') + ->limit(1000) + ->findAll(); + + foreach ($tasks as &$task) { + $stats['count']++; + $stats['total_lead_time'] += ($task['date_completed'] ?: time()) - $task['date_creation']; + $stats['total_cycle_time'] += empty($task['date_started']) ? 0 : ($task['date_completed'] ?: time()) - $task['date_started']; + } + + $stats['avg_lead_time'] = $stats['count'] > 0 ? (int) ($stats['total_lead_time'] / $stats['count']) : 0; + $stats['avg_cycle_time'] = $stats['count'] > 0 ? (int) ($stats['total_cycle_time'] / $stats['count']) : 0; + + return $stats; + } + + /** + * Get the average time spent into each column + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAverageTimeSpentByColumn($project_id) + { + $stats = array(); + $columns = $this->board->getColumnsList($project_id); + + // Get the time spent of the last move for each tasks + $tasks = $this->db + ->table(Task::TABLE) + ->columns('id', 'date_completed', 'date_moved', 'column_id') + ->eq('project_id', $project_id) + ->desc('id') + ->limit(1000) + ->findAll(); + + // Init values + foreach ($columns as $column_id => $column_title) { + $stats[$column_id] = array( + 'count' => 0, + 'time_spent' => 0, + 'average' => 0, + 'title' => $column_title, + ); + } + + // Get time spent foreach task/column and take into account the last move + foreach ($tasks as &$task) { + $sums = $this->transition->getTimeSpentByTask($task['id']); + + if (! isset($sums[$task['column_id']])) { + $sums[$task['column_id']] = 0; + } + + $sums[$task['column_id']] += ($task['date_completed'] ?: time()) - $task['date_moved']; + + foreach ($sums as $column_id => $time_spent) { + $stats[$column_id]['count']++; + $stats[$column_id]['time_spent'] += $time_spent; + } + } + + // Calculate average for each column + foreach ($columns as $column_id => $column_title) { + $stats[$column_id]['average'] = $stats[$column_id]['count'] > 0 ? (int) ($stats[$column_id]['time_spent'] / $stats[$column_id]['count']) : 0; + } + + return $stats; + } } diff --git a/app/Model/ProjectDailySummary.php b/app/Model/ProjectDailyColumnStats.php index 04dc5629..26e9d8b7 100644 --- a/app/Model/ProjectDailySummary.php +++ b/app/Model/ProjectDailyColumnStats.php @@ -3,22 +3,22 @@ namespace Model; /** - * Project daily summary + * Project Daily Column Stats * * @package model * @author Frederic Guillot */ -class ProjectDailySummary extends Base +class ProjectDailyColumnStats extends Base { /** * SQL table name * * @var string */ - const TABLE = 'project_daily_summaries'; + const TABLE = 'project_daily_column_stats'; /** - * Update daily totals for the project + * Update daily totals for the project and foreach column * * "total" is the number open of tasks in the column * "score" is the sum of tasks score in the column @@ -38,7 +38,7 @@ class ProjectDailySummary extends Base // This call will fail if the record already exists // (cross database driver hack for INSERT..ON DUPLICATE KEY UPDATE) - $db->table(ProjectDailySummary::TABLE)->insert(array( + $db->table(ProjectDailyColumnStats::TABLE)->insert(array( 'day' => $date, 'project_id' => $project_id, 'column_id' => $column_id, @@ -46,7 +46,7 @@ class ProjectDailySummary extends Base 'score' => 0, )); - $db->table(ProjectDailySummary::TABLE) + $db->table(ProjectDailyColumnStats::TABLE) ->eq('project_id', $project_id) ->eq('column_id', $column_id) ->eq('day', $date) @@ -95,19 +95,19 @@ class ProjectDailySummary extends Base */ public function getRawMetrics($project_id, $from, $to) { - return $this->db->table(ProjectDailySummary::TABLE) + return $this->db->table(ProjectDailyColumnStats::TABLE) ->columns( - ProjectDailySummary::TABLE.'.column_id', - ProjectDailySummary::TABLE.'.day', - ProjectDailySummary::TABLE.'.total', - ProjectDailySummary::TABLE.'.score', + ProjectDailyColumnStats::TABLE.'.column_id', + ProjectDailyColumnStats::TABLE.'.day', + ProjectDailyColumnStats::TABLE.'.total', + ProjectDailyColumnStats::TABLE.'.score', Board::TABLE.'.title AS column_title' ) ->join(Board::TABLE, 'id', 'column_id') - ->eq(ProjectDailySummary::TABLE.'.project_id', $project_id) + ->eq(ProjectDailyColumnStats::TABLE.'.project_id', $project_id) ->gte('day', $from) ->lte('day', $to) - ->asc(ProjectDailySummary::TABLE.'.day') + ->asc(ProjectDailyColumnStats::TABLE.'.day') ->findAll(); } @@ -122,17 +122,17 @@ class ProjectDailySummary extends Base */ public function getRawMetricsByDay($project_id, $from, $to) { - return $this->db->table(ProjectDailySummary::TABLE) + return $this->db->table(ProjectDailyColumnStats::TABLE) ->columns( - ProjectDailySummary::TABLE.'.day', - 'SUM('.ProjectDailySummary::TABLE.'.total) AS total', - 'SUM('.ProjectDailySummary::TABLE.'.score) AS score' + ProjectDailyColumnStats::TABLE.'.day', + 'SUM('.ProjectDailyColumnStats::TABLE.'.total) AS total', + 'SUM('.ProjectDailyColumnStats::TABLE.'.score) AS score' ) - ->eq(ProjectDailySummary::TABLE.'.project_id', $project_id) + ->eq(ProjectDailyColumnStats::TABLE.'.project_id', $project_id) ->gte('day', $from) ->lte('day', $to) - ->asc(ProjectDailySummary::TABLE.'.day') - ->groupBy(ProjectDailySummary::TABLE.'.day') + ->asc(ProjectDailyColumnStats::TABLE.'.day') + ->groupBy(ProjectDailyColumnStats::TABLE.'.day') ->findAll(); } @@ -160,7 +160,7 @@ class ProjectDailySummary extends Base $aggregates = array(); // Fetch metrics for the project - $records = $this->db->table(ProjectDailySummary::TABLE) + $records = $this->db->table(ProjectDailyColumnStats::TABLE) ->eq('project_id', $project_id) ->gte('day', $from) ->lte('day', $to) diff --git a/app/Model/ProjectDailyStats.php b/app/Model/ProjectDailyStats.php new file mode 100644 index 00000000..56a51730 --- /dev/null +++ b/app/Model/ProjectDailyStats.php @@ -0,0 +1,72 @@ +<?php + +namespace Model; + +/** + * Project Daily Stats + * + * @package model + * @author Frederic Guillot + */ +class ProjectDailyStats extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'project_daily_stats'; + + /** + * Update daily totals for the project + * + * @access public + * @param integer $project_id Project id + * @param string $date Record date (YYYY-MM-DD) + * @return boolean + */ + public function updateTotals($project_id, $date) + { + $lead_cycle_time = $this->projectAnalytic->getAverageLeadAndCycleTime($project_id); + + return $this->db->transaction(function($db) use ($project_id, $date, $lead_cycle_time) { + + // This call will fail if the record already exists + // (cross database driver hack for INSERT..ON DUPLICATE KEY UPDATE) + $db->table(ProjectDailyStats::TABLE)->insert(array( + 'day' => $date, + 'project_id' => $project_id, + 'avg_lead_time' => 0, + 'avg_cycle_time' => 0, + )); + + $db->table(ProjectDailyStats::TABLE) + ->eq('project_id', $project_id) + ->eq('day', $date) + ->update(array( + 'avg_lead_time' => $lead_cycle_time['avg_lead_time'], + 'avg_cycle_time' => $lead_cycle_time['avg_cycle_time'], + )); + }); + } + + /** + * Get raw metrics for the project within a data range + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return array + */ + public function getRawMetrics($project_id, $from, $to) + { + return $this->db->table(self::TABLE) + ->columns('day', 'avg_lead_time', 'avg_cycle_time') + ->eq(self::TABLE.'.project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->asc(self::TABLE.'.day') + ->findAll(); + } +} diff --git a/app/Model/TaskAnalytic.php b/app/Model/TaskAnalytic.php new file mode 100644 index 00000000..33a645c1 --- /dev/null +++ b/app/Model/TaskAnalytic.php @@ -0,0 +1,71 @@ +<?php + +namespace Model; + +/** + * Task Analytic + * + * @package model + * @author Frederic Guillot + */ +class TaskAnalytic extends Base +{ + /** + * Get the time between date_creation and date_completed or now if empty + * + * @access public + * @param array $task + * @return integer + */ + public function getLeadTime(array $task) + { + return ($task['date_completed'] ?: time()) - $task['date_creation']; + } + + /** + * Get the time between date_started and date_completed or now if empty + * + * @access public + * @param array $task + * @return integer + */ + public function getCycleTime(array $task) + { + if (empty($task['date_started'])) { + return 0; + } + + return ($task['date_completed'] ?: time()) - $task['date_started']; + } + + /** + * Get the average time spent in each column + * + * @access public + * @param array $task + * @return array + */ + public function getTimeSpentByColumn(array $task) + { + $result = array(); + $columns = $this->board->getColumnsList($task['project_id']); + $sums = $this->transition->getTimeSpentByTask($task['id']); + + foreach ($columns as $column_id => $column_title) { + + $time_spent = isset($sums[$column_id]) ? $sums[$column_id] : 0; + + if ($task['column_id'] == $column_id) { + $time_spent += ($task['date_completed'] ?: time()) - $task['date_moved']; + } + + $result[] = array( + 'id' => $column_id, + 'title' => $column_title, + 'time_spent' => $time_spent, + ); + } + + return $result; + } +} diff --git a/app/Model/TaskCreation.php b/app/Model/TaskCreation.php index 893cbc43..e530da13 100644 --- a/app/Model/TaskCreation.php +++ b/app/Model/TaskCreation.php @@ -43,9 +43,10 @@ class TaskCreation extends Base */ public function prepare(array &$values) { - $this->dateParser->convert($values, array('date_due', 'date_started')); + $this->dateParser->convert($values, array('date_due')); + $this->dateParser->convert($values, array('date_started'), true); $this->removeFields($values, array('another_task')); - $this->resetFields($values, array('owner_id', 'swimlane_id', 'date_due', 'score', 'category_id', 'time_estimated')); + $this->resetFields($values, array('creator_id', 'owner_id', 'swimlane_id', 'date_due', 'score', 'category_id', 'time_estimated')); if (empty($values['column_id'])) { $values['column_id'] = $this->board->getFirstColumn($values['project_id']); @@ -59,6 +60,10 @@ class TaskCreation extends Base $values['title'] = t('Untitled'); } + if ($this->userSession->isLogged()) { + $values['creator_id'] = $this->userSession->getId(); + } + $values['swimlane_id'] = empty($values['swimlane_id']) ? 0 : $values['swimlane_id']; $values['date_creation'] = time(); $values['date_modification'] = $values['date_creation']; diff --git a/app/Model/TaskDuplication.php b/app/Model/TaskDuplication.php index afcac4c7..8048f036 100755 --- a/app/Model/TaskDuplication.php +++ b/app/Model/TaskDuplication.php @@ -93,15 +93,22 @@ class TaskDuplication extends Base * Duplicate a task to another project * * @access public - * @param integer $task_id Task id - * @param integer $project_id Project id - * @return boolean|integer Duplicated task id + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return boolean|integer */ - public function duplicateToProject($task_id, $project_id) + public function duplicateToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) { $values = $this->copyFields($task_id); $values['project_id'] = $project_id; - $values['column_id'] = $this->board->getFirstColumn($project_id); + $values['column_id'] = $column_id !== null ? $column_id : $this->board->getFirstColumn($project_id); + $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $values['swimlane_id']; + $values['category_id'] = $category_id !== null ? $category_id : $values['category_id']; + $values['owner_id'] = $owner_id !== null ? $owner_id : $values['owner_id']; $this->checkDestinationProjectValues($values); @@ -112,22 +119,26 @@ class TaskDuplication extends Base * Move a task to another project * * @access public - * @param integer $task_id Task id - * @param integer $project_id Project id + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id * @return boolean */ - public function moveToProject($task_id, $project_id) + public function moveToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) { $task = $this->taskFinder->getById($task_id); $values = array(); $values['is_active'] = 1; $values['project_id'] = $project_id; - $values['column_id'] = $this->board->getFirstColumn($project_id); + $values['column_id'] = $column_id !== null ? $column_id : $this->board->getFirstColumn($project_id); $values['position'] = $this->taskFinder->countByColumnId($project_id, $values['column_id']) + 1; - $values['owner_id'] = $task['owner_id']; - $values['category_id'] = $task['category_id']; - $values['swimlane_id'] = $task['swimlane_id']; + $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $task['swimlane_id']; + $values['category_id'] = $category_id !== null ? $category_id : $task['category_id']; + $values['owner_id'] = $owner_id !== null ? $owner_id : $task['owner_id']; $this->checkDestinationProjectValues($values); @@ -144,10 +155,10 @@ class TaskDuplication extends Base /** * Check if the assignee and the category are available in the destination project * - * @access private + * @access public * @param array $values */ - private function checkDestinationProjectValues(&$values) + public function checkDestinationProjectValues(array &$values) { // Check if the assigned user is allowed for the destination project if ($values['owner_id'] > 0 && ! $this->projectPermission->isUserAllowed($values['project_id'], $values['owner_id'])) { @@ -169,6 +180,8 @@ class TaskDuplication extends Base $this->swimlane->getNameById($values['swimlane_id']) ); } + + return $values; } /** diff --git a/app/Model/TaskFilter.php b/app/Model/TaskFilter.php index 31080cb5..77ab1f3c 100644 --- a/app/Model/TaskFilter.php +++ b/app/Model/TaskFilter.php @@ -50,6 +50,12 @@ class TaskFilter extends Base case 'T_DUE': $this->filterByDueDate($value); break; + case 'T_UPDATED': + $this->filterByModificationDate($value); + break; + case 'T_CREATED': + $this->filterByCreationDate($value); + break; case 'T_TITLE': $this->filterByTitle($value); break; @@ -68,6 +74,12 @@ class TaskFilter extends Base case 'T_COLUMN': $this->filterByColumnName($value); break; + case 'T_REFERENCE': + $this->filterByReference($value); + break; + case 'T_SWIMLANE': + $this->filterBySwimlaneName($value); + break; } } @@ -98,6 +110,25 @@ class TaskFilter extends Base } /** + * Create a new subtask query + * + * @access public + * @return \PicoDb\Table + */ + public function createSubtaskQuery() + { + return $this->db->table(Subtask::TABLE) + ->columns( + Subtask::TABLE.'.user_id', + Subtask::TABLE.'.task_id', + User::TABLE.'.name', + User::TABLE.'.username' + ) + ->join(User::TABLE, 'id', 'user_id', Subtask::TABLE) + ->neq(Subtask::TABLE.'.status', Subtask::STATUS_DONE); + } + + /** * Clone the filter * * @access public @@ -115,7 +146,7 @@ class TaskFilter extends Base * Exclude a list of task_id * * @access public - * @param array $task_ids + * @param integer[] $task_ids * @return TaskFilter */ public function excludeTasks(array $task_ids) @@ -141,6 +172,22 @@ class TaskFilter extends Base } /** + * Filter by reference + * + * @access public + * @param string $reference + * @return TaskFilter + */ + public function filterByReference($reference) + { + if (! empty($reference)) { + $this->query->eq(Task::TABLE.'.reference', $reference); + } + + return $this; + } + + /** * Filter by title * * @access public @@ -154,7 +201,7 @@ class TaskFilter extends Base } /** - * Filter by title + * Filter by title or id if the string is like #123 or an integer * * @access public * @param string $title @@ -162,7 +209,16 @@ class TaskFilter extends Base */ public function filterByTitle($title) { - $this->query->ilike(Task::TABLE.'.title', '%'.$title.'%'); + if (strlen($title) > 1 && $title{0} === '#' && ctype_digit(substr($title, 1))) { + $this->query->eq(Task::TABLE.'.id', substr($title, 1)); + } + else if (ctype_digit($title)) { + $this->query->eq(Task::TABLE.'.id', $title); + } + else { + $this->query->ilike(Task::TABLE.'.title', '%'.$title.'%'); + } + return $this; } @@ -219,6 +275,30 @@ class TaskFilter extends Base } /** + * Filter by swimlane name + * + * @access public + * @param array $values List of swimlane name + * @return TaskFilter + */ + public function filterBySwimlaneName(array $values) + { + $this->query->beginOr(); + + foreach ($values as $swimlane) { + if ($swimlane === 'default') { + $this->query->eq(Task::TABLE.'.swimlane_id', 0); + } + else { + $this->query->ilike(Swimlane::TABLE.'.name', $swimlane); + $this->query->addCondition(Task::TABLE.'.swimlane_id=0 AND '.Project::TABLE.'.default_swimlane '.$this->db->getDriver()->getOperator('ILIKE')." '$swimlane'"); + } + } + + $this->query->closeOr(); + } + + /** * Filter by category id * * @access public @@ -285,7 +365,6 @@ class TaskFilter extends Base $this->query->beginOr(); foreach ($values as $assignee) { - switch ($assignee) { case 'me': $this->query->eq(Task::TABLE.'.owner_id', $this->userSession->getId()); @@ -299,7 +378,40 @@ class TaskFilter extends Base } } + $this->filterBySubtaskAssignee($values); + $this->query->closeOr(); + + return $this; + } + + /** + * Filter by subtask assignee names + * + * @access public + * @param array $values List of assignees + * @return TaskFilter + */ + public function filterBySubtaskAssignee(array $values) + { + $subtaskQuery = $this->createSubtaskQuery(); + $subtaskQuery->beginOr(); + + foreach ($values as $assignee) { + if ($assignee === 'me') { + $subtaskQuery->eq(Subtask::TABLE.'.user_id', $this->userSession->getId()); + } + else { + $subtaskQuery->ilike(User::TABLE.'.username', '%'.$assignee.'%'); + $subtaskQuery->ilike(User::TABLE.'.name', '%'.$assignee.'%'); + } + } + + $subtaskQuery->closeOr(); + + $this->query->in(Task::TABLE.'.id', $subtaskQuery->findAllByColumn('task_id')); + + return $this; } /** @@ -474,6 +586,22 @@ class TaskFilter extends Base * Filter by creation date * * @access public + * @param string $date ISO8601 date format + * @return TaskFilter + */ + public function filterByCreationDate($date) + { + if ($date === 'recently') { + return $this->filterRecentlyDate(Task::TABLE.'.date_creation'); + } + + return $this->filterWithOperator(Task::TABLE.'.date_creation', $date, true); + } + + /** + * Filter by creation date + * + * @access public * @param string $start * @param string $end * @return TaskFilter @@ -491,6 +619,22 @@ class TaskFilter extends Base } /** + * Filter by modification date + * + * @access public + * @param string $date ISO8601 date format + * @return TaskFilter + */ + public function filterByModificationDate($date) + { + if ($date === 'recently') { + return $this->filterRecentlyDate(Task::TABLE.'.date_modification'); + } + + return $this->filterWithOperator(Task::TABLE.'.date_modification', $date, true); + } + + /** * Get all results of the filter * * @access public @@ -513,6 +657,23 @@ class TaskFilter extends Base } /** + * Get swimlanes and tasks to display the board + * + * @access public + * @return array + */ + public function getBoard($project_id) + { + $tasks = $this->filterByProject($project_id)->query->asc(Task::TABLE.'.position')->findAll(); + + return $this->board->getBoard($project_id, function ($project_id, $column_id, $swimlane_id) use ($tasks) { + return array_filter($tasks, function(array $task) use ($column_id, $swimlane_id) { + return $task['column_id'] == $column_id && $task['swimlane_id'] == $swimlane_id; + }); + }); + } + + /** * Format the results to the ajax autocompletion * * @access public @@ -589,10 +750,10 @@ class TaskFilter extends Base * Transform results to ical events * * @access public - * @param string $start_column Column name for the start date - * @param string $end_column Column name for the end date - * @param Eluceo\iCal\Component\Calendar $vCalendar Calendar object - * @return Eluceo\iCal\Component\Calendar + * @param string $start_column Column name for the start date + * @param string $end_column Column name for the end date + * @param Calendar $vCalendar Calendar object + * @return Calendar */ public function addDateTimeIcalEvents($start_column, $end_column, Calendar $vCalendar = null) { @@ -622,9 +783,9 @@ class TaskFilter extends Base * Transform results to all day ical events * * @access public - * @param string $column Column name for the date - * @param Eluceo\iCal\Component\Calendar $vCalendar Calendar object - * @return Eluceo\iCal\Component\Calendar + * @param string $column Column name for the date + * @param Calendar $vCalendar Calendar object + * @return Calendar */ public function addAllDayIcalEvents($column = 'date_due', Calendar $vCalendar = null) { @@ -654,7 +815,7 @@ class TaskFilter extends Base * @access protected * @param array $task * @param string $uid - * @return Eluceo\iCal\Component\Event + * @return Event */ protected function getTaskIcalEvent(array &$task, $uid) { @@ -671,11 +832,11 @@ class TaskFilter extends Base $vEvent->setSummary(t('#%d', $task['id']).' '.$task['title']); $vEvent->setUrl($this->helper->url->base().$this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); - if (! empty($task['creator_id'])) { - $vEvent->setOrganizer('MAILTO:'.($task['creator_email'] ?: $task['creator_username'].'@kanboard.local')); + if (! empty($task['owner_id'])) { + $vEvent->setOrganizer('MAILTO:'.($task['assignee_email'] ?: $task['assignee_username'].'@kanboard.local')); } - if (! empty($task['owner_id'])) { + if (! empty($task['creator_id'])) { $attendees = new Attendees; $attendees->add('MAILTO:'.($task['creator_email'] ?: $task['creator_username'].'@kanboard.local')); $vEvent->setAttendees($attendees); @@ -703,7 +864,6 @@ class TaskFilter extends Base ); foreach ($operators as $operator => $method) { - if (strpos($value, $operator) === 0) { $value = substr($value, strlen($operator)); $this->query->$method($field, $is_date ? $this->dateParser->getTimestampFromIsoFormat($value) : $value); @@ -711,7 +871,32 @@ class TaskFilter extends Base } } - $this->query->eq($field, $is_date ? $this->dateParser->getTimestampFromIsoFormat($value) : $value); + if ($is_date) { + $timestamp = $this->dateParser->getTimestampFromIsoFormat($value); + $this->query->gte($field, $timestamp); + $this->query->lte($field, $timestamp + 86399); + } + else { + $this->query->eq($field, $value); + } + + return $this; + } + + /** + * Use the board_highlight_period for the "recently" keyword + * + * @access private + * @param string $field + * @return TaskFilter + */ + private function filterRecentlyDate($field) + { + $duration = $this->config->get('board_highlight_period', 0); + + if ($duration > 0) { + $this->query->gte($field, time() - $duration); + } return $this; } diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php index f061cef0..a16cb69f 100644 --- a/app/Model/TaskFinder.php +++ b/app/Model/TaskFinder.php @@ -13,20 +13,6 @@ use PDO; class TaskFinder extends Base { /** - * Get query for closed tasks - * - * @access public - * @param integer $project_id Project id - * @return \PicoDb\Table - */ - public function getClosedTaskQuery($project_id) - { - return $this->getExtendedQuery() - ->eq('project_id', $project_id) - ->eq('is_active', Task::STATUS_CLOSED); - } - - /** * Get query for assigned user tasks * * @access public @@ -77,6 +63,7 @@ class TaskFinder extends Base 'tasks.date_creation', 'tasks.date_modification', 'tasks.date_completed', + 'tasks.date_started', 'tasks.date_due', 'tasks.color_id', 'tasks.project_id', @@ -102,11 +89,14 @@ class TaskFinder extends Base Category::TABLE.'.name AS category_name', Category::TABLE.'.description AS category_description', Board::TABLE.'.title AS column_name', + Swimlane::TABLE.'.name AS swimlane_name', + Project::TABLE.'.default_swimlane', Project::TABLE.'.name AS project_name' ) ->join(User::TABLE, 'id', 'owner_id', Task::TABLE) ->join(Category::TABLE, 'id', 'category_id', Task::TABLE) ->join(Board::TABLE, 'id', 'column_id', Task::TABLE) + ->join(Swimlane::TABLE, 'id', 'swimlane_id', Task::TABLE) ->join(Project::TABLE, 'id', 'project_id', Task::TABLE); } @@ -142,8 +132,8 @@ class TaskFinder extends Base { return $this->db ->table(Task::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', $status_id) + ->eq(Task::TABLE.'.project_id', $project_id) + ->eq(Task::TABLE.'.is_active', $status_id) ->findAll(); } diff --git a/app/Model/TaskLink.php b/app/Model/TaskLink.php index 7d3a8918..3fdbd04b 100644 --- a/app/Model/TaskLink.php +++ b/app/Model/TaskLink.php @@ -4,7 +4,6 @@ namespace Model; use SimpleValidator\Validator; use SimpleValidator\Validators; -use PicoDb\Table; /** * TaskLink model diff --git a/app/Model/TaskModification.php b/app/Model/TaskModification.php index 4691ce81..b67106e1 100644 --- a/app/Model/TaskModification.php +++ b/app/Model/TaskModification.php @@ -83,7 +83,8 @@ class TaskModification extends Base */ public function prepare(array &$values) { - $this->dateParser->convert($values, array('date_due', 'date_started')); + $this->dateParser->convert($values, array('date_due')); + $this->dateParser->convert($values, array('date_started'), true); $this->removeFields($values, array('another_task', 'id')); $this->resetFields($values, array('date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent')); $this->convertIntegerFields($values, array('is_active', 'recurrence_status', 'recurrence_trigger', 'recurrence_factor', 'recurrence_timeframe', 'recurrence_basedate')); diff --git a/app/Model/TaskPosition.php b/app/Model/TaskPosition.php index 0c4beb2d..874633b1 100644 --- a/app/Model/TaskPosition.php +++ b/app/Model/TaskPosition.php @@ -26,104 +26,176 @@ class TaskPosition extends Base */ public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0, $fire_events = true) { - $original_task = $this->taskFinder->getById($task_id); + if ($position < 1) { + return false; + } - $result = $this->calculateAndSave($project_id, $task_id, $column_id, $position, $swimlane_id); + $task = $this->taskFinder->getById($task_id); - if ($result) { + // Ignore closed tasks + if ($task['is_active'] == Task::STATUS_CLOSED) { + return true; + } - if ($original_task['swimlane_id'] != $swimlane_id) { - $this->calculateAndSave($project_id, 0, $column_id, 1, $original_task['swimlane_id']); - } + $result = false; - if ($fire_events) { - $this->fireEvents($original_task, $column_id, $position, $swimlane_id); - } + if ($task['swimlane_id'] != $swimlane_id) { + $result = $this->saveSwimlaneChange($project_id, $task_id, $position, $task['column_id'], $column_id, $task['swimlane_id'], $swimlane_id); + } + else if ($task['column_id'] != $column_id) { + $result = $this->saveColumnChange($project_id, $task_id, $position, $swimlane_id, $task['column_id'], $column_id); + } + else if ($task['position'] != $position) { + $result = $this->savePositionChange($project_id, $task_id, $position, $column_id, $swimlane_id); + } + + if ($result && $fire_events) { + $this->fireEvents($task, $column_id, $position, $swimlane_id); } return $result; } /** - * Calculate the new position of all tasks + * Move a task to another swimlane * - * @access public - * @param integer $project_id Project id - * @param integer $task_id Task id - * @param integer $column_id Column id - * @param integer $position Position (must be >= 1) - * @param integer $swimlane_id Swimlane id - * @return array|boolean + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $original_column_id + * @param integer $new_column_id + * @param integer $original_swimlane_id + * @param integer $new_swimlane_id + * @return boolean */ - public function calculatePositions($project_id, $task_id, $column_id, $position, $swimlane_id = 0) + private function saveSwimlaneChange($project_id, $task_id, $position, $original_column_id, $new_column_id, $original_swimlane_id, $new_swimlane_id) { - // The position can't be lower than 1 - if ($position < 1) { - return false; - } + $this->db->startTransaction(); + $r1 = $this->saveTaskPositions($project_id, $task_id, 0, $original_column_id, $original_swimlane_id); + $r2 = $this->saveTaskPositions($project_id, $task_id, $position, $new_column_id, $new_swimlane_id); + $this->db->closeTransaction(); - $board = $this->db->table(Board::TABLE)->eq('project_id', $project_id)->asc('position')->findAllByColumn('id'); - $columns = array(); - - // For each column fetch all tasks ordered by position - foreach ($board as $board_column_id) { - - $columns[$board_column_id] = $this->db->table(Task::TABLE) - ->eq('is_active', 1) - ->eq('swimlane_id', $swimlane_id) - ->eq('project_id', $project_id) - ->eq('column_id', $board_column_id) - ->neq('id', $task_id) - ->asc('position') - ->asc('id') // Fix Postgresql unit test - ->findAllByColumn('id'); - } + return $r1 && $r2; + } - // The column must exists - if (! isset($columns[$column_id])) { - return false; - } + /** + * Move a task to another column + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $swimlane_id + * @param integer $original_column_id + * @param integer $new_column_id + * @return boolean + */ + private function saveColumnChange($project_id, $task_id, $position, $swimlane_id, $original_column_id, $new_column_id) + { + $this->db->startTransaction(); + $r1 = $this->saveTaskPositions($project_id, $task_id, 0, $original_column_id, $swimlane_id); + $r2 = $this->saveTaskPositions($project_id, $task_id, $position, $new_column_id, $swimlane_id); + $this->db->closeTransaction(); - // We put our task to the new position - if ($task_id) { - array_splice($columns[$column_id], $position - 1, 0, $task_id); - } + return $r1 && $r2; + } + + /** + * Move a task to another position in the same column + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id + * @return boolean + */ + private function savePositionChange($project_id, $task_id, $position, $column_id, $swimlane_id) + { + $this->db->startTransaction(); + $result = $this->saveTaskPositions($project_id, $task_id, $position, $column_id, $swimlane_id); + $this->db->closeTransaction(); - return $columns; + return $result; } /** - * Save task positions + * Save all task positions for one column * * @access private - * @param array $columns Sorted tasks - * @param integer $swimlane_id Swimlane id + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id * @return boolean */ - private function savePositions(array $columns, $swimlane_id) + private function saveTaskPositions($project_id, $task_id, $position, $column_id, $swimlane_id) { - return $this->db->transaction(function ($db) use ($columns, $swimlane_id) { + $tasks_ids = $this->db->table(Task::TABLE) + ->eq('is_active', 1) + ->eq('swimlane_id', $swimlane_id) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->neq('id', $task_id) + ->asc('position') + ->asc('id') + ->findAllByColumn('id'); + + $offset = 1; + + foreach ($tasks_ids as $current_task_id) { + + // Insert the new task + if ($position == $offset) { + if (! $this->saveTaskPosition($task_id, $offset, $column_id, $swimlane_id)) { + return false; + } + $offset++; + } - foreach ($columns as $column_id => $column) { + // Rewrite other tasks position + if (! $this->saveTaskPosition($current_task_id, $offset, $column_id, $swimlane_id)) { + return false; + } - $position = 1; + $offset++; + } - foreach ($column as $task_id) { + // Insert the new task at the bottom and normalize bad position + if ($position >= $offset && ! $this->saveTaskPosition($task_id, $offset, $column_id, $swimlane_id)) { + return false; + } - $result = $db->table(Task::TABLE)->eq('id', $task_id)->update(array( - 'position' => $position, - 'column_id' => $column_id, - 'swimlane_id' => $swimlane_id, - )); + return true; + } - if (! $result) { - return false; - } + /** + * Save new task position + * + * @access private + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id + * @return boolean + */ + private function saveTaskPosition($task_id, $position, $column_id, $swimlane_id) + { + $result = $this->db->table(Task::TABLE)->eq('id', $task_id)->update(array( + 'position' => $position, + 'column_id' => $column_id, + 'swimlane_id' => $swimlane_id, + )); + + if (! $result) { + $this->db->cancelTransaction(); + return false; + } - $position++; - } - } - }); + return true; } /** @@ -160,26 +232,4 @@ class TaskPosition extends Base $this->container['dispatcher']->dispatch(Task::EVENT_MOVE_POSITION, new TaskEvent($event_data)); } } - - /** - * Calculate the new position of all tasks - * - * @access private - * @param integer $project_id Project id - * @param integer $task_id Task id - * @param integer $column_id Column id - * @param integer $position Position (must be >= 1) - * @param integer $swimlane_id Swimlane id - * @return boolean - */ - private function calculateAndSave($project_id, $task_id, $column_id, $position, $swimlane_id) - { - $positions = $this->calculatePositions($project_id, $task_id, $column_id, $position, $swimlane_id); - - if ($positions === false || ! $this->savePositions($positions, $swimlane_id)) { - return false; - } - - return true; - } } diff --git a/app/Model/TaskValidator.php b/app/Model/TaskValidator.php index ec1383ad..95b8a26c 100644 --- a/app/Model/TaskValidator.php +++ b/app/Model/TaskValidator.php @@ -39,7 +39,7 @@ class TaskValidator extends Base new Validators\Integer('recurrence_status', t('This value must be an integer')), new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200), new Validators\Date('date_due', t('Invalid date'), $this->dateParser->getDateFormats()), - new Validators\Date('date_started', t('Invalid date'), $this->dateParser->getDateFormats()), + new Validators\Date('date_started', t('Invalid date'), $this->dateParser->getAllFormats()), new Validators\Numeric('time_spent', t('This value must be numeric')), new Validators\Numeric('time_estimated', t('This value must be numeric')), ); diff --git a/app/Model/Transition.php b/app/Model/Transition.php index cb759e4a..ac3fba54 100644 --- a/app/Model/Transition.php +++ b/app/Model/Transition.php @@ -39,6 +39,22 @@ class Transition extends Base } /** + * Get time spent by task for each column + * + * @access public + * @param integer $task_id + * @return array + */ + public function getTimeSpentByTask($task_id) + { + return $this->db + ->hashtable(self::TABLE) + ->groupBy('src_column_id') + ->eq('task_id', $task_id) + ->getAll('src_column_id', 'SUM(time_spent) AS time_spent'); + } + + /** * Get all transitions by task * * @access public diff --git a/app/Model/User.php b/app/Model/User.php index 4c32942c..b6804abc 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -122,13 +122,13 @@ class User extends Base } /** - * Get a specific user by the GitHub id + * Get a specific user by the Github id * * @access public - * @param string $github_id GitHub user id + * @param string $github_id Github user id * @return array|boolean */ - public function getByGitHubId($github_id) + public function getByGithubId($github_id) { if (empty($github_id)) { return false; @@ -377,6 +377,7 @@ class User extends Base new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'), new Validators\Email('email', t('Email address invalid')), new Validators\Integer('is_admin', t('This value must be an integer')), + new Validators\Integer('is_ldap_user', t('This value must be an integer')), ); } @@ -409,7 +410,12 @@ class User extends Base new Validators\Required('username', t('The username is required')), ); - $v = new Validator($values, array_merge($rules, $this->commonValidationRules(), $this->commonPasswordValidationRules())); + if (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) { + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + } + else { + $v = new Validator($values, array_merge($rules, $this->commonValidationRules(), $this->commonPasswordValidationRules())); + } return array( $v->execute(), diff --git a/app/Model/UserSession.php b/app/Model/UserSession.php index f1f2ffee..44a9c2a2 100644 --- a/app/Model/UserSession.php +++ b/app/Model/UserSession.php @@ -94,4 +94,52 @@ class UserSession extends Base { return ! empty($this->session['user']); } + + /** + * Get project filters from the session + * + * @access public + * @param integer $project_id + * @return string + */ + public function getFilters($project_id) + { + return ! empty($_SESSION['filters'][$project_id]) ? $_SESSION['filters'][$project_id] : 'status:open'; + } + + /** + * Save project filters in the session + * + * @access public + * @param integer $project_id + * @param string $filters + */ + public function setFilters($project_id, $filters) + { + $_SESSION['filters'][$project_id] = $filters; + } + + /** + * Is board collapsed or expanded + * + * @access public + * @param integer $project_id + * @return boolean + */ + public function isBoardCollapsed($project_id) + { + return ! empty($_SESSION['board_collapsed'][$project_id]) ? $_SESSION['board_collapsed'][$project_id] : false; + } + + /** + * Set board display mode + * + * @access public + * @param integer $project_id + * @param boolean $collapsed + */ + public function setBoardDisplayMode($project_id, $collapsed) + { + $_SESSION['board_collapsed'][$project_id] = $collapsed; + } } diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 0c932104..c62f3a72 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,37 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 77; +const VERSION = 80; + +function version_80($pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('default_color', 'yellow')"); +} + +function version_79($pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id INT NOT NULL AUTO_INCREMENT, + day CHAR(10) NOT NULL, + project_id INT NOT NULL, + avg_lead_time INT NOT NULL DEFAULT 0, + avg_cycle_time INT NOT NULL DEFAULT 0, + PRIMARY KEY(id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('RENAME TABLE project_daily_summaries TO project_daily_column_stats'); +} + +function version_78($pdo) +{ + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN slack_webhook_channel VARCHAR(255) DEFAULT ''"); + $pdo->exec("INSERT INTO settings VALUES ('integration_slack_webhook_channel', '')"); +} function version_77($pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index a3309068..b436db1b 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,7 +6,36 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 57; +const VERSION = 60; + +function version_60($pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('default_color', 'yellow')"); +} + +function version_59($pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id SERIAL PRIMARY KEY, + day CHAR(10) NOT NULL, + project_id INTEGER NOT NULL, + avg_lead_time INTEGER NOT NULL DEFAULT 0, + avg_cycle_time INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('ALTER TABLE project_daily_summaries RENAME TO project_daily_column_stats'); +} + +function version_58($pdo) +{ + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN slack_webhook_channel VARCHAR(255) DEFAULT ''"); + $pdo->exec("INSERT INTO settings VALUES ('integration_slack_webhook_channel', '')"); +} function version_57($pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index c3bbbac9..23097b55 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,36 @@ use Core\Security; use PDO; use Model\Link; -const VERSION = 73; +const VERSION = 76; + +function version_76($pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('default_color', 'yellow')"); +} + +function version_75($pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id INTEGER PRIMARY KEY, + day TEXT NOT NULL, + project_id INTEGER NOT NULL, + avg_lead_time INTEGER NOT NULL DEFAULT 0, + avg_cycle_time INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('ALTER TABLE project_daily_summaries RENAME TO project_daily_column_stats'); +} + +function version_74($pdo) +{ + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN slack_webhook_channel TEXT DEFAULT ''"); + $pdo->exec("INSERT INTO settings VALUES ('integration_slack_webhook_channel', '')"); +} function version_73($pdo) { diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index 1fa0d0ef..ef7aa575 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -3,6 +3,7 @@ namespace ServiceProvider; use Core\Paginator; +use Core\OAuth2; use Model\Config; use Model\Project; use Model\Webhook; @@ -33,7 +34,8 @@ class ClassProvider implements ServiceProviderInterface 'ProjectActivity', 'ProjectAnalytic', 'ProjectDuplication', - 'ProjectDailySummary', + 'ProjectDailyColumnStats', + 'ProjectDailyStats', 'ProjectIntegration', 'ProjectPermission', 'Subtask', @@ -42,6 +44,7 @@ class ClassProvider implements ServiceProviderInterface 'SubtaskTimeTracking', 'Swimlane', 'Task', + 'TaskAnalytic', 'TaskCreation', 'TaskDuplication', 'TaskExport', @@ -70,6 +73,7 @@ class ClassProvider implements ServiceProviderInterface 'Lexer', 'MemoryCache', 'Request', + 'Router', 'Session', 'Template', ), @@ -104,5 +108,9 @@ class ClassProvider implements ServiceProviderInterface $container['paginator'] = $container->factory(function ($c) { return new Paginator($c); }); + + $container['oauth'] = $container->factory(function ($c) { + return new OAuth2($c); + }); } } diff --git a/app/Subscriber/ProjectDailySummarySubscriber.php b/app/Subscriber/ProjectDailySummarySubscriber.php index 9e4f15b0..db180dea 100644 --- a/app/Subscriber/ProjectDailySummarySubscriber.php +++ b/app/Subscriber/ProjectDailySummarySubscriber.php @@ -22,7 +22,8 @@ class ProjectDailySummarySubscriber extends \Core\Base implements EventSubscribe public function execute(TaskEvent $event) { if (isset($event['project_id'])) { - $this->projectDailySummary->updateTotals($event['project_id'], date('Y-m-d')); + $this->projectDailyColumnStats->updateTotals($event['project_id'], date('Y-m-d')); + $this->projectDailyStats->updateTotals($event['project_id'], date('Y-m-d')); } } } diff --git a/app/Template/projectinfo/activity.php b/app/Template/activity/project.php index d458ea3d..480bbadd 100644 --- a/app/Template/projectinfo/activity.php +++ b/app/Template/activity/project.php @@ -12,10 +12,20 @@ </span> </li> <li> - <i class="fa fa-table fa-fw"></i> + <i class="fa fa-th fa-fw"></i> <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?> </li> <li> + <i class="fa fa-calendar fa-fw"></i> + <?= $this->url->link(t('Back to the calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?> + </li> + <?php if ($this->user->isManager($project['id'])): ?> + <li> + <i class="fa fa-cog fa-fw"></i> + <?= $this->url->link(t('Project settings'), 'project', 'show', array('project_id' => $project['id'])) ?> + </li> + <?php endif ?> + <li> <i class="fa fa-folder fa-fw"></i> <?= $this->url->link(t('All projects'), 'project', 'index') ?> </li> diff --git a/app/Template/task/activity.php b/app/Template/activity/task.php index cc4aad03..cc4aad03 100644 --- a/app/Template/task/activity.php +++ b/app/Template/activity/task.php diff --git a/app/Template/analytic/avg_time_columns.php b/app/Template/analytic/avg_time_columns.php new file mode 100644 index 00000000..e74e7950 --- /dev/null +++ b/app/Template/analytic/avg_time_columns.php @@ -0,0 +1,29 @@ +<div class="page-header"> + <h2><?= t('Average time spent into each column') ?></h2> +</div> + +<?php if (empty($metrics)): ?> + <p class="alert"><?= t('Not enough data to show the graph.') ?></p> +<?php else: ?> + <section id="analytic-avg-time-column"> + + <div id="chart" data-metrics='<?= json_encode($metrics) ?>' data-label="<?= t('Average time spent') ?>"></div> + + <table class="table-stripped"> + <tr> + <th><?= t('Column') ?></th> + <th><?= t('Average time spent') ?></th> + </tr> + <?php foreach ($metrics as $column): ?> + <tr> + <td><?= $this->e($column['title']) ?></td> + <td><?= $this->dt->duration($column['average']) ?></td> + </tr> + <?php endforeach ?> + </table> + + <p class="alert alert-info"> + <?= t('This chart show the average time spent into each column for the last %d tasks.', 1000) ?> + </p> + </section> +<?php endif ?> diff --git a/app/Template/analytic/layout.php b/app/Template/analytic/layout.php index 41c6a2ab..9d6bf77c 100644 --- a/app/Template/analytic/layout.php +++ b/app/Template/analytic/layout.php @@ -12,10 +12,20 @@ </span> </li> <li> - <i class="fa fa-table fa-fw"></i> + <i class="fa fa-th fa-fw"></i> <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?> </li> <li> + <i class="fa fa-calendar fa-fw"></i> + <?= $this->url->link(t('Back to the calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?> + </li> + <?php if ($this->user->isManager($project['id'])): ?> + <li> + <i class="fa fa-cog fa-fw"></i> + <?= $this->url->link(t('Project settings'), 'project', 'show', array('project_id' => $project['id'])) ?> + </li> + <?php endif ?> + <li> <i class="fa fa-folder fa-fw"></i> <?= $this->url->link(t('All projects'), 'project', 'index') ?> </li> diff --git a/app/Template/analytic/lead_cycle_time.php b/app/Template/analytic/lead_cycle_time.php new file mode 100644 index 00000000..d96bdcb8 --- /dev/null +++ b/app/Template/analytic/lead_cycle_time.php @@ -0,0 +1,42 @@ +<div class="page-header"> + <h2><?= t('Average Lead and Cycle time') ?></h2> +</div> + +<div class="listing"> + <ul> + <li><?= t('Average lead time: ').'<strong>'.$this->dt->duration($average['avg_lead_time']) ?></strong></li> + <li><?= t('Average cycle time: ').'<strong>'.$this->dt->duration($average['avg_cycle_time']) ?></strong></li> + </ul> +</div> + +<?php if (empty($metrics)): ?> + <p class="alert"><?= t('Not enough data to show the graph.') ?></p> +<?php else: ?> + <section id="analytic-lead-cycle-time"> + + <div id="chart" data-metrics='<?= json_encode($metrics) ?>' data-label-cycle="<?= t('Cycle Time') ?>" data-label-lead="<?= t('Lead Time') ?>"></div> + + <form method="post" class="form-inline" action="<?= $this->url->href('analytic', 'leadAndCycleTime', array('project_id' => $project['id'])) ?>" autocomplete="off"> + + <?= $this->form->csrf() ?> + + <div class="form-inline-group"> + <?= $this->form->label(t('Start Date'), 'from') ?> + <?= $this->form->text('from', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> + </div> + + <div class="form-inline-group"> + <?= $this->form->label(t('End Date'), 'to') ?> + <?= $this->form->text('to', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> + </div> + + <div class="form-inline-group"> + <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/> + </div> + </form> + + <p class="alert alert-info"> + <?= t('This chart show the average lead and cycle time for the last %d tasks over the time.', 1000) ?> + </p> + </section> +<?php endif ?> diff --git a/app/Template/analytic/sidebar.php b/app/Template/analytic/sidebar.php index 2d1a7c96..59cc1fa6 100644 --- a/app/Template/analytic/sidebar.php +++ b/app/Template/analytic/sidebar.php @@ -13,5 +13,13 @@ <li> <?= $this->url->link(t('Burndown chart'), 'analytic', 'burndown', array('project_id' => $project['id'])) ?> </li> + <li> + <?= $this->url->link(t('Average time into each column'), 'analytic', 'averageTimeByColumn', array('project_id' => $project['id'])) ?> + </li> + <li> + <?= $this->url->link(t('Lead and cycle time'), 'analytic', 'leadAndCycleTime', array('project_id' => $project['id'])) ?> + </li> </ul> + <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> + <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/Template/app/calendar.php b/app/Template/app/calendar.php index 6acee6ec..a154203b 100644 --- a/app/Template/app/calendar.php +++ b/app/Template/app/calendar.php @@ -1,6 +1,5 @@ -<div id="user-calendar" - data-check-url="<?= $this->url->href('calendar', 'user') ?>" - data-user-id="<?= $user['id'] ?>" +<div id="calendar" + data-check-url="<?= $this->url->href('calendar', 'user', array('user_id' => $user['id'])) ?>" data-save-url="<?= $this->url->href('calendar', 'save') ?>" > </div> diff --git a/app/Template/app/filters_helper.php b/app/Template/app/filters_helper.php new file mode 100644 index 00000000..b4e81130 --- /dev/null +++ b/app/Template/app/filters_helper.php @@ -0,0 +1,21 @@ +<div class="dropdown filters"> + <span> + <i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Filters') ?></a> + <ul> + <li><a href="#" class="filter-helper" data-filter="<?= isset($reset) ? $reset : '' ?>"><?= t('Reset filters') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open assignee:me"><?= t('My tasks') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open assignee:me due:tomorrow"><?= t('My tasks due tomorrow') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open due:today"><?= t('Tasks due today') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open due:tomorrow"><?= t('Tasks due tomorrow') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open due:yesterday"><?= t('Tasks due yesterday') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:closed"><?= t('Closed tasks') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open"><?= t('Open tasks') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open assignee:nobody"><?= t('Not assigned') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open category:none"><?= t('No category') ?></a></li> + <li> + <i class="fa fa-external-link"></i> + <a href="http://kanboard.net/documentation/search" target="_blank"><?= t('View advanced search syntax') ?></a> + </li> + </ul> + </span> +</div>
\ No newline at end of file diff --git a/app/Template/app/overview.php b/app/Template/app/overview.php index bd7d28db..1b160496 100644 --- a/app/Template/app/overview.php +++ b/app/Template/app/overview.php @@ -1,3 +1,13 @@ +<div class="search"> + <form method="get" action="<?= $this->url->dir() ?>" class="search"> + <?= $this->form->hidden('controller', array('controller' => 'search')) ?> + <?= $this->form->hidden('action', array('action' => 'index')) ?> + <?= $this->form->text('search', array(), array(), array('placeholder="'.t('Search').'"'), 'form-input-large') ?> + </form> + + <?= $this->render('app/filters_helper') ?> +</div> + <?= $this->render('app/projects', array('paginator' => $project_paginator)) ?> <?= $this->render('app/tasks', array('paginator' => $task_paginator)) ?> <?= $this->render('app/subtasks', array('paginator' => $subtask_paginator)) ?>
\ No newline at end of file diff --git a/app/Template/app/projects.php b/app/Template/app/projects.php index 61839cee..627ad21b 100644 --- a/app/Template/app/projects.php +++ b/app/Template/app/projects.php @@ -24,7 +24,7 @@ <?= $this->url->link($this->e($project['name']), 'board', 'show', array('project_id' => $project['id'])) ?> <?php if (! empty($project['description'])): ?> - <span class="column-tooltip" title='<?= $this->e($this->text->markdown($project['description'])) ?>'> + <span class="tooltip" title='<?= $this->e($this->text->markdown($project['description'])) ?>'> <i class="fa fa-info-circle"></i> </span> <?php endif ?> diff --git a/app/Template/app/sidebar.php b/app/Template/app/sidebar.php index 40bf6401..4cace15a 100644 --- a/app/Template/app/sidebar.php +++ b/app/Template/app/sidebar.php @@ -20,4 +20,6 @@ <?= $this->url->link(t('My activity stream'), 'app', 'activity', array('user_id' => $user['id'])) ?> </li> </ul> + <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> + <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/Template/auth/index.php b/app/Template/auth/index.php index 8801a512..ca303df9 100644 --- a/app/Template/auth/index.php +++ b/app/Template/auth/index.php @@ -4,7 +4,8 @@ <p class="alert alert-error"><?= $this->e($errors['login']) ?></p> <?php endif ?> - <form method="post" action="<?= $this->url->href('auth', 'check', array('redirect_query' => $redirect_query)) ?>"> + <?php if (! HIDE_LOGIN_FORM): ?> + <form method="post" action="<?= $this->url->href('auth', 'check') ?>"> <?= $this->form->csrf() ?> @@ -14,19 +15,24 @@ <?= $this->form->label(t('Password'), 'password') ?> <?= $this->form->password('password', $values, $errors, array('required')) ?> - <?= $this->form->checkbox('remember_me', t('Remember Me'), 1) ?><br/> + <?= $this->form->checkbox('remember_me', t('Remember Me'), 1, true) ?><br/> + <div class="form-actions"> + <input type="submit" value="<?= t('Sign in') ?>" class="btn btn-blue"/> + </div> + </form> + <?php endif ?> + + <?php if (GOOGLE_AUTH || GITHUB_AUTH): ?> + <ul class="no-bullet"> <?php if (GOOGLE_AUTH): ?> - <?= $this->url->link(t('Login with my Google Account'), 'user', 'google') ?> + <li><?= $this->url->link(t('Login with my Google Account'), 'oauth', 'google') ?></li> <?php endif ?> <?php if (GITHUB_AUTH): ?> - <?= $this->url->link(t('Login with my GitHub Account'), 'user', 'gitHub') ?> + <li><?= $this->url->link(t('Login with my Github Account'), 'oauth', 'gitHub') ?></li> <?php endif ?> - - <div class="form-actions"> - <input type="submit" value="<?= t('Sign in') ?>" class="btn btn-blue"/> - </div> - </form> + </ul> + <?php endif ?> </div>
\ No newline at end of file diff --git a/app/Template/board/filters.php b/app/Template/board/filters.php deleted file mode 100644 index b80234a0..00000000 --- a/app/Template/board/filters.php +++ /dev/null @@ -1,43 +0,0 @@ -<div class="page-header"> - <ul class="board-filters"> - <li> - <span class="dropdown"> - <span> - <i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a> - <ul> - <li> - <span class="filter-collapse"> - <i class="fa fa-compress fa-fw"></i> <a href="#" class="filter-collapse-link"><?= t('Collapse tasks') ?></a> - </span> - <span class="filter-expand" style="display: none"> - <i class="fa fa-expand fa-fw"></i> <a href="#" class="filter-expand-link"><?= t('Expand tasks') ?></a> - </span> - </li> - <li> - <span class="filter-compact"> - <i class="fa fa-th fa-fw"></i> <a href="#" class="filter-toggle-scrolling"><?= t('Compact view') ?></a> - </span> - <span class="filter-wide" style="display: none"> - <i class="fa fa-arrows-h fa-fw"></i> <a href="#" class="filter-toggle-scrolling"><?= t('Horizontal scrolling') ?></a> - </span> - </li> - <?= $this->render('project/dropdown', array('project' => $project)) ?> - </ul> - </span> - </span> - </li> - <li> - <?= $this->form->select('user_id', $users, array(), array(), array('data-placeholder="'.t('Filter by user').'"', 'data-notfound="'.t('No results match:').'"', 'tabindex=="-1"'), 'apply-filters chosen-select') ?> - </li> - <li> - <?= $this->form->select('category_id', $categories, array(), array(), array('data-placeholder="'.t('Filter by category').'"', 'data-notfound="'.t('No results match:').'"', 'tabindex=="-1"'), 'apply-filters chosen-select') ?> - </li> - <li> - <select id="more-filters" multiple data-placeholder="<?= t('More filters') ?>" data-notfound="<?= t('No results match:') ?>" class="apply-filters hide-mobile" tabindex="-1"> - <option value=""></option> - <option value="filter-due-date"><?= t('Filter by due date') ?></option> - <option value="filter-recent"><?= t('Filter recently updated') ?></option> - </select> - </li> - </ul> -</div>
\ No newline at end of file diff --git a/app/Template/board/assignee.php b/app/Template/board/popover_assignee.php index 4af19cf7..4af19cf7 100644 --- a/app/Template/board/assignee.php +++ b/app/Template/board/popover_assignee.php diff --git a/app/Template/board/category.php b/app/Template/board/popover_category.php index b38758d3..f391f492 100644 --- a/app/Template/board/category.php +++ b/app/Template/board/popover_category.php @@ -9,7 +9,7 @@ <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Category'), 'category_id') ?> - <?= $this->form->select('category_id', $categories_list, $values) ?><br/> + <?= $this->form->select('category_id', $categories_list, $values, array(), array('autofocus')) ?><br/> <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> @@ -18,5 +18,4 @@ </div> </form> </section> - </section>
\ No newline at end of file diff --git a/app/Template/board/index.php b/app/Template/board/private_view.php index 6f6fddbe..5fdaa7fc 100644 --- a/app/Template/board/index.php +++ b/app/Template/board/private_view.php @@ -1,12 +1,12 @@ <section id="main"> - <?= $this->render('board/filters', array( - 'categories' => $categories_listing, - 'users' => $users, + <?= $this->render('project/filters', array( 'project' => $project, + 'filters' => $filters, + 'is_board' => true, )) ?> - <?= $this->render('board/show', array( + <?= $this->render('board/table_container', array( 'project' => $project, 'swimlanes' => $swimlanes, 'board_private_refresh_interval' => $board_private_refresh_interval, diff --git a/app/Template/board/public.php b/app/Template/board/public_view.php index ad7515db..aea72031 100644 --- a/app/Template/board/public.php +++ b/app/Template/board/public_view.php @@ -1,6 +1,6 @@ <section id="main" class="public-board"> - <?= $this->render('board/show', array( + <?= $this->render('board/table_container', array( 'project' => $project, 'swimlanes' => $swimlanes, 'board_private_refresh_interval' => $board_private_refresh_interval, diff --git a/app/Template/board/show.php b/app/Template/board/table_container.php index c0aa5d36..65ccdc4f 100644 --- a/app/Template/board/show.php +++ b/app/Template/board/table_container.php @@ -8,7 +8,7 @@ data-check-interval="<?= $board_private_refresh_interval ?>" data-save-url="<?= $this->url->href('board', 'save', array('project_id' => $project['id'])) ?>" data-check-url="<?= $this->url->href('board', 'check', array('project_id' => $project['id'], 'timestamp' => time())) ?>" - data-task-creation-url="<?= $this->url->href('task', 'create', array('project_id' => $project['id'])) ?>" + data-task-creation-url="<?= $this->url->href('taskcreation', 'create', array('project_id' => $project['id'])) ?>" > <?php endif ?> @@ -17,7 +17,7 @@ <p class="alert alert-error"><?= t('There is no column in your project!') ?></p> <?php break ?> <?php else: ?> - <?= $this->render('board/swimlane', array( + <?= $this->render('board/table_swimlane', array( 'project' => $project, 'swimlane' => $swimlane, 'board_highlight_period' => $board_highlight_period, diff --git a/app/Template/board/swimlane.php b/app/Template/board/table_swimlane.php index b86fc446..4cd137cb 100644 --- a/app/Template/board/swimlane.php +++ b/app/Template/board/table_swimlane.php @@ -15,14 +15,14 @@ <th class="board-column"> <?php if (! $not_editable): ?> <div class="board-add-icon"> - <?= $this->url->link('+', 'task', 'create', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'task-board-popover', t('Add a new task')) ?> + <?= $this->url->link('+', 'taskcreation', 'create', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'task-board-popover', t('Add a new task')) ?> </div> <?php endif ?> <?= $this->e($column['title']) ?> <?php if (! $not_editable && ! empty($column['description'])): ?> - <span class="column-tooltip pull-right" title='<?= $this->e($this->text->markdown($column['description'])) ?>'> + <span class="tooltip pull-right" title='<?= $this->e($this->text->markdown($column['description'])) ?>'> <i class="fa fa-info-circle"></i> </span> <?php endif ?> diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php index 7593c102..69bf97c1 100644 --- a/app/Template/board/task_footer.php +++ b/app/Template/board/task_footer.php @@ -10,7 +10,7 @@ 'changeCategory', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, - 'task-board-popover' . (! empty($task['category_description']) ? ' column-tooltip' : ''), + 'task-board-popover' . (! empty($task['category_description']) ? ' tooltip' : ''), ! empty($task['category_description']) ? $this->text->markdown($task['category_description']) : t('Change category') ) ?> <?php endif ?> @@ -21,7 +21,8 @@ <div class="task-board-icons"> <?php if (! empty($task['date_due'])): ?> <span class="task-board-date <?= time() > $task['date_due'] ? 'task-board-date-overdue' : '' ?>"> - <i class="fa fa-calendar"></i> <?= dt('%b %e', $task['date_due']) ?> + <i class="fa fa-calendar"></i> + <?= (date('Y') === date('Y', $task['date_due']) ? dt('%b %e', $task['date_due']) : dt('%b %e %Y', $task['date_due'])) ?> </span> <?php endif ?> diff --git a/app/Template/board/task_menu.php b/app/Template/board/task_menu.php index 97c0f8dc..71963b5e 100644 --- a/app/Template/board/task_menu.php +++ b/app/Template/board/task_menu.php @@ -4,13 +4,12 @@ <ul> <li><i class="fa fa-user"></i> <?= $this->url->link(t('Change assignee'), 'board', 'changeAssignee', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-tag"></i> <?= $this->url->link(t('Change category'), 'board', 'changeCategory', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> - <li><i class="fa fa-align-left"></i> <?= $this->url->link(t('Change description'), 'task', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> - <li><i class="fa fa-pencil-square-o"></i> <?= $this->url->link(t('Edit this task'), 'task', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> + <li><i class="fa fa-align-left"></i> <?= $this->url->link(t('Change description'), 'taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> + <li><i class="fa fa-pencil-square-o"></i> <?= $this->url->link(t('Edit this task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-comment-o"></i> <?= $this->url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-code-fork"></i> <?= $this->url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-camera"></i> <?= $this->url->link(t('Add a screenshot'), 'board', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> - <li><i class="fa fa-refresh fa-rotate-90"></i> <?= $this->url->link(t('Edit recurrence'), 'task', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> - <li><i class="fa fa-close"></i> <?= $this->url->link(t('Close this task'), 'task', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'task-board-popover') ?></li> + <li><i class="fa fa-close"></i> <?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'task-board-popover') ?></li> </ul> </span> </span> diff --git a/app/Template/board/task_private.php b/app/Template/board/task_private.php index 44ce9d97..7eaff580 100644 --- a/app/Template/board/task_private.php +++ b/app/Template/board/task_private.php @@ -1,4 +1,7 @@ -<div class="task-board draggable-item color-<?= $task['color_id'] ?> <?= $task['date_modification'] > time() - $board_highlight_period ? 'task-board-recent' : '' ?>" +<div class=" + task-board + <?= $task['is_active'] == 1 ? 'draggable-item task-board-status-open '.($task['date_modification'] > (time() - $board_highlight_period) ? 'task-board-recent' : '') : 'task-board-status-closed' ?> + color-<?= $task['color_id'] ?>" data-task-id="<?= $task['id'] ?>" data-owner-id="<?= $task['owner_id'] ?>" data-category-id="<?= $task['category_id'] ?>" @@ -7,42 +10,55 @@ <?= $this->render('board/task_menu', array('task' => $task)) ?> - <div class="task-board-collapsed" style="display: none"> - <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-collapsed-title') ?> - </div> + <?php if ($this->board->isCollapsed($project['id'])): ?> + <div class="task-board-collapsed"> + <?php if (! empty($task['assignee_username'])): ?> + <span title="<?= $this->e($task['assignee_name'] ?: $task['assignee_username']) ?>"> + <?= $this->e($this->user->getInitials($task['assignee_name'] ?: $task['assignee_username'])) ?> + </span> - + <?php endif ?> + <span class="tooltip" title="<?= $this->e($task['title']) ?>" + <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-collapsed-title') ?> + </span> + </div> + <?php else: ?> + <div class="task-board-expanded"> - <div class="task-board-expanded"> + <?php if ($task['reference']): ?> + <span class="task-board-reference" title="<?= t('Reference') ?>"> + (<?= $task['reference'] ?>) + </span> + <?php endif ?> - <?php if ($task['reference']): ?> - <span class="task-board-reference" title="<?= t('Reference') ?>"> - (<?= $task['reference'] ?>) - </span> - <?php endif ?> + <span class="task-board-user <?= $this->user->isCurrentUser($task['owner_id']) ? 'task-board-current-user' : '' ?>"> + <?= $this->url->link( + (! empty($task['owner_id']) ? ($task['assignee_name'] ?: $task['assignee_username']) : t('Nobody assigned')), + 'board', + 'changeAssignee', + array('task_id' => $task['id'], 'project_id' => $task['project_id']), + false, + 'task-board-popover', + t('Change assignee') + ) ?> + </span> - <span class="task-board-user <?= $this->user->isCurrentUser($task['owner_id']) ? 'task-board-current-user' : '' ?>"> - <?= $this->url->link( - (! empty($task['owner_id']) ? ($task['assignee_name'] ?: $task['assignee_username']) : t('Nobody assigned')), - 'board', - 'changeAssignee', - array('task_id' => $task['id'], 'project_id' => $task['project_id']), - false, - 'task-board-popover', - t('Change assignee') - ) ?> - </span> + <?php if ($task['is_active'] == 1): ?> + <div class="task-board-days"> + <span title="<?= t('Task age in days')?>" class="task-days-age"><?= $this->dt->age($task['date_creation']) ?></span> + <span title="<?= t('Days in this column')?>" class="task-days-incolumn"><?= $this->dt->age($task['date_moved']) ?></span> + </div> + <?php else: ?> + <div class="task-board-closed"><i class="fa fa-ban fa-fw"></i><?= t('Closed') ?></div> + <?php endif ?> - <div class="task-board-days"> - <span title="<?= t('Task age in days')?>" class="task-days-age"><?= $this->datetime->age($task['date_creation']) ?></span> - <span title="<?= t('Days in this column')?>" class="task-days-incolumn"><?= $this->datetime->age($task['date_moved']) ?></span> - </div> + <div class="task-board-title"> + <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> + </div> - <div class="task-board-title"> - <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> + <?= $this->render('board/task_footer', array( + 'task' => $task, + 'not_editable' => $not_editable, + )) ?> </div> - - <?= $this->render('board/task_footer', array( - 'task' => $task, - 'not_editable' => $not_editable, - )) ?> - </div> + <?php endif ?> </div> diff --git a/app/Template/board/comments.php b/app/Template/board/tooltip_comments.php index 2e2c0c1e..2e2c0c1e 100644 --- a/app/Template/board/comments.php +++ b/app/Template/board/tooltip_comments.php diff --git a/app/Template/board/description.php b/app/Template/board/tooltip_description.php index 7e0e3430..7e0e3430 100644 --- a/app/Template/board/description.php +++ b/app/Template/board/tooltip_description.php diff --git a/app/Template/board/files.php b/app/Template/board/tooltip_files.php index 81136659..81136659 100644 --- a/app/Template/board/files.php +++ b/app/Template/board/tooltip_files.php diff --git a/app/Template/board/subtasks.php b/app/Template/board/tooltip_subtasks.php index 950da925..950da925 100644 --- a/app/Template/board/subtasks.php +++ b/app/Template/board/tooltip_subtasks.php diff --git a/app/Template/board/tasklinks.php b/app/Template/board/tooltip_tasklinks.php index 25aa91aa..25aa91aa 100644 --- a/app/Template/board/tasklinks.php +++ b/app/Template/board/tooltip_tasklinks.php diff --git a/app/Template/budget/sidebar.php b/app/Template/budget/sidebar.php index 7740cf00..0fdb8612 100644 --- a/app/Template/budget/sidebar.php +++ b/app/Template/budget/sidebar.php @@ -11,4 +11,6 @@ <?= $this->url->link(t('Cost breakdown'), 'budget', 'breakdown', array('project_id' => $project['id'])) ?> </li> </ul> + <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> + <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/Template/calendar/show.php b/app/Template/calendar/show.php index cf2a20ec..0406414c 100644 --- a/app/Template/calendar/show.php +++ b/app/Template/calendar/show.php @@ -1,46 +1,13 @@ <section id="main"> - <div class="page-header"> - <ul> - <li> - <span class="dropdown"> - <span> - <i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a> - <ul> - <?= $this->render('project/dropdown', array('project' => $project)) ?> - </ul> - </span> - </span> - </li> - <li> - <i class="fa fa-table fa-fw"></i> - <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?> - </li> - <li> - <i class="fa fa-folder fa-fw"></i> - <?= $this->url->link(t('All projects'), 'project', 'index') ?> - </li> - </ul> - </div> - <section class="sidebar-container"> - - <?= $this->render('calendar/sidebar', array( - 'project' => $project, - 'users_list' => $users_list, - 'categories_list' => $categories_list, - 'columns_list' => $columns_list, - 'swimlanes_list' => $swimlanes_list, - 'colors_list' => $colors_list, - 'status_list' => $status_list - )) ?> + <?= $this->render('project/filters', array( + 'project' => $project, + 'filters' => $filters, + )) ?> - <div class="sidebar-content"> - <div id="calendar" - data-project-id="<?= $project['id'] ?>" - data-save-url="<?= $this->url->href('calendar', 'save') ?>" - data-check-url="<?= $this->url->href('calendar', 'project', array('project_id' => $project['id'])) ?>" - data-check-interval="<?= $check_interval ?>" - > - </div> - </div> - </section> + <div id="calendar" + data-save-url="<?= $this->url->href('calendar', 'save') ?>" + data-check-url="<?= $this->url->href('calendar', 'project', array('project_id' => $project['id'])) ?>" + data-check-interval="<?= $check_interval ?>" + > + </div> </section>
\ No newline at end of file diff --git a/app/Template/calendar/sidebar.php b/app/Template/calendar/sidebar.php deleted file mode 100644 index 6c4fb5b0..00000000 --- a/app/Template/calendar/sidebar.php +++ /dev/null @@ -1,40 +0,0 @@ -<div class="sidebar"> - <ul class="no-bullet"> - <li> - <?= t('Filter by user') ?> - </li> - <li> - <?= $this->form->select('owner_id', $users_list, array(), array(), array(), 'calendar-filter') ?> - </li> - <li> - <?= t('Filter by category') ?> - </li> - <li> - <?= $this->form->select('category_id', $categories_list, array(), array(), array(), 'calendar-filter') ?> - </li> - <li> - <?= t('Filter by column') ?> - </li> - <li> - <?= $this->form->select('column_id', $columns_list, array(), array(), array(), 'calendar-filter') ?> - </li> - <li> - <?= t('Filter by swimlane') ?> - </li> - <li> - <?= $this->form->select('swimlane_id', $swimlanes_list, array(), array(), array(), 'calendar-filter') ?> - </li> - <li> - <?= t('Filter by color') ?> - </li> - <li> - <?= $this->form->select('color_id', $colors_list, array(), array(), array(), 'calendar-filter') ?> - </li> - <li> - <?= t('Filter by status') ?> - </li> - <li> - <?= $this->form->select('is_active', $status_list, array(), array(), array(), 'calendar-filter') ?> - </li> - </ul> -</div> diff --git a/app/Template/column/index.php b/app/Template/column/index.php index 18e7f284..a394ee67 100644 --- a/app/Template/column/index.php +++ b/app/Template/column/index.php @@ -18,7 +18,7 @@ <tr> <td class="column-60"><?= $this->e($column['title']) ?> <?php if (! empty($column['description'])): ?> - <span class="column-tooltip" title='<?= $this->e($this->text->markdown($column['description'])) ?>'> + <span class="tooltip" title='<?= $this->e($this->text->markdown($column['description'])) ?>'> <i class="fa fa-info-circle"></i> </span> <?php endif ?> diff --git a/app/Template/config/about.php b/app/Template/config/about.php index a7098c1b..03a66c3f 100644 --- a/app/Template/config/about.php +++ b/app/Template/config/about.php @@ -42,6 +42,12 @@ <h2><?= t('Keyboard shortcuts') ?></h2> </div> <div class="listing"> + <h3><?= t('Board/Calendar/List view') ?></h3> + <ul> + <li><?= t('Switch to the board view') ?> = <strong>v b</strong></li> + <li><?= t('Switch to the calendar view') ?> = <strong>v c</strong></li> + <li><?= t('Switch to the list view') ?> = <strong>v l</strong></li> + </ul> <h3><?= t('Board view') ?></h3> <ul> <li><?= t('New task') ?> = <strong>n</strong></li> @@ -51,6 +57,7 @@ <h3><?= t('Application') ?></h3> <ul> <li><?= t('Open board switcher') ?> = <strong>b</strong></li> + <li><?= t('Go to the search/filter box') ?> = <strong>f</strong></li> <li><?= t('Close dialog box') ?> = <strong>ESC</strong></li> <li><?= t('Submit a form') ?> = <strong>CTRL+ENTER</strong> <?= t('or') ?> <strong>⌘+ENTER</strong></li> </ul> diff --git a/app/Template/config/integrations.php b/app/Template/config/integrations.php index a1299806..47b45149 100644 --- a/app/Template/config/integrations.php +++ b/app/Template/config/integrations.php @@ -6,30 +6,42 @@ <?= $this->form->csrf() ?> - <h3><img src="assets/img/mailgun-icon.png"/> <?= t('Mailgun (incoming emails)') ?></h3> + <h3><i class="fa fa-google"></i> <?= t('Google Authentication') ?></h3> <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->base().$this->url->href('webhook', 'mailgun', array('token' => $values['webhook_token'])) ?>"/><br/> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('oauth', 'google', array(), false, '', true) ?>"/><br/> + <p class="form-help"><a href="http://kanboard.net/documentation/google-authentication" target="_blank"><?= t('Help on Google authentication') ?></a></p> + </div> + + <h3><i class="fa fa-github"></i> <?= t('Github Authentication') ?></h3> + <div class="listing"> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('oauth', 'github', array(), false, '', true) ?>"/><br/> + <p class="form-help"><a href="http://kanboard.net/documentation/github-authentication" target="_blank"><?= t('Help on Github authentication') ?></a></p> + </div> + + <h3><img src="<?= $this->url->dir() ?>assets/img/mailgun-icon.png"/> <?= t('Mailgun (incoming emails)') ?></h3> + <div class="listing"> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'mailgun', array('token' => $values['webhook_token']), false, '', true) ?>"/><br/> <p class="form-help"><a href="http://kanboard.net/documentation/mailgun" target="_blank"><?= t('Help on Mailgun integration') ?></a></p> </div> - <h3><img src="assets/img/sendgrid-icon.png"/> <?= t('Sendgrid (incoming emails)') ?></h3> + <h3><img src="<?= $this->url->dir() ?>assets/img/sendgrid-icon.png"/> <?= t('Sendgrid (incoming emails)') ?></h3> <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->base().$this->url->href('webhook', 'sendgrid', array('token' => $values['webhook_token'])) ?>"/><br/> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'sendgrid', array('token' => $values['webhook_token']), false, '', true) ?>"/><br/> <p class="form-help"><a href="http://kanboard.net/documentation/sendgrid" target="_blank"><?= t('Help on Sendgrid integration') ?></a></p> </div> - <h3><img src="assets/img/postmark-icon.png"/> <?= t('Postmark (incoming emails)') ?></h3> + <h3><img src="<?= $this->url->dir() ?>assets/img/postmark-icon.png"/> <?= t('Postmark (incoming emails)') ?></h3> <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->base().$this->url->href('webhook', 'postmark', array('token' => $values['webhook_token'])) ?>"/><br/> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'postmark', array('token' => $values['webhook_token']), false, '', true) ?>"/><br/> <p class="form-help"><a href="http://kanboard.net/documentation/postmark" target="_blank"><?= t('Help on Postmark integration') ?></a></p> </div> - <h3><img src="assets/img/gravatar-icon.png"/> <?= t('Gravatar') ?></h3> + <h3><img src="<?= $this->url->dir() ?>assets/img/gravatar-icon.png"/> <?= t('Gravatar') ?></h3> <div class="listing"> <?= $this->form->checkbox('integration_gravatar', t('Enable Gravatar images'), 1, $values['integration_gravatar'] == 1) ?> </div> - <h3><img src="assets/img/jabber-icon.png"/> <?= t('Jabber (XMPP)') ?></h3> + <h3><img src="<?= $this->url->dir() ?>assets/img/jabber-icon.png"/> <?= t('Jabber (XMPP)') ?></h3> <div class="listing"> <?= $this->form->checkbox('integration_jabber', t('Send notifications to Jabber'), 1, $values['integration_jabber'] == 1) ?> @@ -55,7 +67,7 @@ <p class="form-help"><a href="http://kanboard.net/documentation/jabber" target="_blank"><?= t('Help on Jabber integration') ?></a></p> </div> - <h3><img src="assets/img/hipchat-icon.png"/> <?= t('Hipchat') ?></h3> + <h3><img src="<?= $this->url->dir() ?>assets/img/hipchat-icon.png"/> <?= t('Hipchat') ?></h3> <div class="listing"> <?= $this->form->checkbox('integration_hipchat', t('Send notifications to Hipchat'), 1, $values['integration_hipchat'] == 1) ?> @@ -77,6 +89,8 @@ <?= $this->form->label(t('Webhook URL'), 'integration_slack_webhook_url') ?> <?= $this->form->text('integration_slack_webhook_url', $values, $errors) ?> + <?= $this->form->label(t('Channel/Group/User (Optional)'), 'integration_slack_webhook_channel') ?> + <?= $this->form->text('integration_slack_webhook_channel', $values, $errors) ?> <p class="form-help"><a href="http://kanboard.net/documentation/slack" target="_blank"><?= t('Help on Slack integration') ?></a></p> </div> diff --git a/app/Template/config/project.php b/app/Template/config/project.php index 1ab69e26..b762de24 100644 --- a/app/Template/config/project.php +++ b/app/Template/config/project.php @@ -6,6 +6,9 @@ <?= $this->form->csrf() ?> + <?= $this->form->label(t('Default task color'), 'default_color') ?> + <?= $this->form->select('default_color', $colors, $values, $errors) ?> + <?= $this->form->label(t('Default columns for new projects (Comma-separated)'), 'board_columns') ?> <?= $this->form->text('board_columns', $values, $errors) ?><br/> <p class="form-help"><?= t('Default values are "%s"', $default_columns) ?></p> diff --git a/app/Template/config/sidebar.php b/app/Template/config/sidebar.php index 7f946dee..05ec4094 100644 --- a/app/Template/config/sidebar.php +++ b/app/Template/config/sidebar.php @@ -32,4 +32,6 @@ <?= $this->url->link(t('API'), 'config', 'api') ?> </li> </ul> + <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> + <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/Template/config/webhook.php b/app/Template/config/webhook.php index 73ca3598..f1a98f8b 100644 --- a/app/Template/config/webhook.php +++ b/app/Template/config/webhook.php @@ -26,7 +26,7 @@ </li> <li> <?= t('URL for task creation:') ?> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->base().$this->url->href('webhook', 'task', array('token' => $values['webhook_token'])) ?>"> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'task', array('token' => $values['webhook_token']), false, '', true) ?>"> </li> <li> <?= $this->url->link(t('Reset token'), 'config', 'token', array('type' => 'webhook'), true) ?> diff --git a/app/Template/export/sidebar.php b/app/Template/export/sidebar.php index f93dcafb..f3bcf44d 100644 --- a/app/Template/export/sidebar.php +++ b/app/Template/export/sidebar.php @@ -14,4 +14,6 @@ <?= $this->url->link(t('Daily project summary'), 'export', 'summary', array('project_id' => $project['id'])) ?> </li> </ul> + <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> + <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/Template/feed/project.php b/app/Template/feed/project.php index 60b7ee96..76cf6cf0 100644 --- a/app/Template/feed/project.php +++ b/app/Template/feed/project.php @@ -2,15 +2,15 @@ <feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom"> <title><?= t('%s\'s activity', $project['name']) ?></title> <link rel="alternate" type="text/html" href="<?= $this->url->base() ?>"/> - <link rel="self" type="application/atom+xml" href="<?= $this->url->base().$this->url->href('feed', 'project', array('token' => $project['token'])) ?>"/> + <link rel="self" type="application/atom+xml" href="<?= $this->url->href('feed', 'project', array('token' => $project['token']), false, '', true) ?>"/> <updated><?= date(DATE_ATOM) ?></updated> - <id><?= $this->url->base().$this->url->href('feed', 'project', array('token' => $project['token'])) ?></id> + <id><?= $this->url->href('feed', 'project', array('token' => $project['token']), false, '', true) ?></id> <icon><?= $this->url->base() ?>assets/img/favicon.png</icon> <?php foreach ($events as $e): ?> <entry> <title type="text"><?= $e['event_title'] ?></title> - <link rel="alternate" href="<?= $this->url->base().$this->url->href('task', 'show', array('task_id' => $e['task_id'])) ?>"/> + <link rel="alternate" href="<?= $this->url->href('task', 'show', array('task_id' => $e['task_id']), false, '', true) ?>"/> <id><?= $e['id'].'-'.$e['event_name'].'-'.$e['task_id'].'-'.$e['date_creation'] ?></id> <published><?= date(DATE_ATOM, $e['date_creation']) ?></published> <updated><?= date(DATE_ATOM, $e['date_creation']) ?></updated> diff --git a/app/Template/feed/user.php b/app/Template/feed/user.php index b3279a0c..3e9606c6 100644 --- a/app/Template/feed/user.php +++ b/app/Template/feed/user.php @@ -2,15 +2,15 @@ <feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom"> <title><?= t('Project activities for %s', $user['name'] ?: $user['username']) ?></title> <link rel="alternate" type="text/html" href="<?= $this->url->base() ?>"/> - <link rel="self" type="application/atom+xml" href="<?= $this->url->base().$this->url->href('feed', 'user', array('token' => $user['token'])) ?>"/> + <link rel="self" type="application/atom+xml" href="<?= $this->url->href('feed', 'user', array('token' => $user['token']), false, '', true) ?>"/> <updated><?= date(DATE_ATOM) ?></updated> - <id><?= $this->url->base().$this->url->href('feed', 'user', array('token' => $user['token'])) ?></id> + <id><?= $this->url->href('feed', 'user', array('token' => $user['token']), false, '', true) ?></id> <icon><?= $this->url->base() ?>assets/img/favicon.png</icon> <?php foreach ($events as $e): ?> <entry> <title type="text"><?= $e['event_title'] ?></title> - <link rel="alternate" href="<?= $this->url->base().$this->url->href('task', 'show', array('task_id' => $e['task_id'])) ?>"/> + <link rel="alternate" href="<?= $this->url->href('task', 'show', array('task_id' => $e['task_id']), false, '', true) ?>"/> <id><?= $e['id'].'-'.$e['event_name'].'-'.$e['task_id'].'-'.$e['date_creation'] ?></id> <published><?= date(DATE_ATOM, $e['date_creation']) ?></published> <updated><?= date(DATE_ATOM, $e['date_creation']) ?></updated> diff --git a/app/Template/file/screenshot.php b/app/Template/file/screenshot.php index 89d9324c..73b72eae 100644 --- a/app/Template/file/screenshot.php +++ b/app/Template/file/screenshot.php @@ -15,3 +15,5 @@ <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form> + +<p class="alert alert-info"><?= t('This feature does not work with all browsers.') ?></p>
\ No newline at end of file diff --git a/app/Template/file/show.php b/app/Template/file/show.php index 7d5dc96f..9281c352 100644 --- a/app/Template/file/show.php +++ b/app/Template/file/show.php @@ -16,7 +16,7 @@ <?php endif ?> <p> <?= $this->e($file['name']) ?> - <span class="column-tooltip" title='<?= t('uploaded by: %s', $file['user_name'] ?: $file['username']).'<br>'.t('uploaded on: %s', dt('%B %e, %Y at %k:%M %p', $file['date'])).'<br>'.t('size: %s', $this->text->bytes($file['size'])) ?>'> + <span class="tooltip" title='<?= t('uploaded by: %s', $file['user_name'] ?: $file['username']).'<br>'.t('uploaded on: %s', dt('%B %e, %Y at %k:%M %p', $file['date'])).'<br>'.t('size: %s', $this->text->bytes($file['size'])) ?>'> <i class="fa fa-info-circle"></i> </span> </p> @@ -38,7 +38,7 @@ <td><i class="fa <?= $this->file->icon($file['name']) ?> fa-fw"></i></td> <td> <?= $this->e($file['name']) ?> - <span class="column-tooltip" title='<?= t('uploaded by: %s', $file['user_name'] ?: $file['username']).'<br>'.t('uploaded on: %s', dt('%B %e, %Y at %k:%M %p', $file['date'])).'<br>'.t('size: %s', $this->text->bytes($file['size'])) ?>'> + <span class="tooltip" title='<?= t('uploaded by: %s', $file['user_name'] ?: $file['username']).'<br>'.t('uploaded on: %s', dt('%B %e, %Y at %k:%M %p', $file['date'])).'<br>'.t('size: %s', $this->text->bytes($file['size'])) ?>'> <i class="fa fa-info-circle"></i> </span> </td> diff --git a/app/Template/layout.php b/app/Template/layout.php index c630132d..d02ba08d 100644 --- a/app/Template/layout.php +++ b/app/Template/layout.php @@ -15,16 +15,16 @@ <?= $this->asset->js('assets/js/app.js', true) ?> <?php endif ?> + <?= $this->asset->colorCss() ?> <?= $this->asset->css('assets/css/app.css') ?> <?= $this->asset->css('assets/css/print.css', true, 'print') ?> - <?= $this->asset->colorCss() ?> <?= $this->asset->customCss() ?> - <link rel="icon" type="image/png" href="assets/img/favicon.png"> - <link rel="apple-touch-icon" href="assets/img/touch-icon-iphone.png"> - <link rel="apple-touch-icon" sizes="72x72" href="assets/img/touch-icon-ipad.png"> - <link rel="apple-touch-icon" sizes="114x114" href="assets/img/touch-icon-iphone-retina.png"> - <link rel="apple-touch-icon" sizes="144x144" href="assets/img/touch-icon-ipad-retina.png"> + <link rel="icon" type="image/png" href="<?= $this->url->dir() ?>assets/img/favicon.png"> + <link rel="apple-touch-icon" href="<?= $this->url->dir() ?>assets/img/touch-icon-iphone.png"> + <link rel="apple-touch-icon" sizes="72x72" href="<?= $this->url->dir() ?>assets/img/touch-icon-ipad.png"> + <link rel="apple-touch-icon" sizes="114x114" href="<?= $this->url->dir() ?>assets/img/touch-icon-iphone-retina.png"> + <link rel="apple-touch-icon" sizes="144x144" href="<?= $this->url->dir() ?>assets/img/touch-icon-ipad-retina.png"> <title><?= isset($title) ? $this->e($title) : 'Kanboard' ?></title> </head> @@ -40,7 +40,7 @@ <nav> <h1><?= $this->url->link('K<span>B</span>', 'app', 'index', array(), false, 'logo', t('Dashboard')).' '.$this->e($title) ?> <?php if (! empty($description)): ?> - <span class="column-tooltip" title='<?= $this->e($this->text->markdown($description)) ?>'> + <span class="tooltip" title='<?= $this->e($this->text->markdown($description)) ?>'> <i class="fa fa-info-circle"></i> </span> <?php endif ?> @@ -48,7 +48,7 @@ <ul> <?php if (isset($board_selector) && ! empty($board_selector)): ?> <li> - <select id="board-selector" tabindex=="-1" data-notfound="<?= t('No results match:') ?>" data-placeholder="<?= t('Display another project') ?>" data-board-url="<?= $this->url->href('board', 'show', array('project_id' => 'PROJECT_ID')) ?>"> + <select id="board-selector" tabindex="-1" data-notfound="<?= t('No results match:') ?>" data-placeholder="<?= t('Display another project') ?>" data-board-url="<?= $this->url->href('board', 'show', array('project_id' => 'PROJECT_ID')) ?>"> <option value=""></option> <?php foreach($board_selector as $board_id => $board_name): ?> <option value="<?= $board_id ?>"><?= $this->e($board_name) ?></option> diff --git a/app/Template/listing/show.php b/app/Template/listing/show.php new file mode 100644 index 00000000..fc8a607b --- /dev/null +++ b/app/Template/listing/show.php @@ -0,0 +1,61 @@ +<section id="main"> + <?= $this->render('project/filters', array( + 'project' => $project, + 'filters' => $filters, + )) ?> + + <?php if (! empty($values['search']) && $paginator->isEmpty()): ?> + <p class="alert"><?= t('No tasks found.') ?></p> + <?php elseif (! $paginator->isEmpty()): ?> + <table class="table-fixed table-small"> + <tr> + <th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th> + <th class="column-10"><?= $paginator->order(t('Swimlane'), 'tasks.swimlane_id') ?></th> + <th class="column-10"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th> + <th class="column-10"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th> + <th><?= $paginator->order(t('Title'), 'tasks.title') ?></th> + <th class="column-10"><?= $paginator->order(t('Assignee'), 'users.username') ?></th> + <th class="column-10"><?= $paginator->order(t('Due date'), 'tasks.date_due') ?></th> + <th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th> + </tr> + <?php foreach ($paginator->getCollection() as $task): ?> + <tr> + <td class="task-table color-<?= $task['color_id'] ?>"> + <?= $this->url->link('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> + </td> + <td> + <?= $this->e($task['swimlane_name'] ?: $task['default_swimlane']) ?> + </td> + <td> + <?= $this->e($task['column_name']) ?> + </td> + <td> + <?= $this->e($task['category_name']) ?> + </td> + <td> + <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> + </td> + <td> + <?php if ($task['assignee_username']): ?> + <?= $this->e($task['assignee_name'] ?: $task['assignee_username']) ?> + <?php else: ?> + <?= t('Unassigned') ?> + <?php endif ?> + </td> + <td> + <?= dt('%B %e, %Y', $task['date_due']) ?> + </td> + <td> + <?php if ($task['is_active'] == \Model\Task::STATUS_OPEN): ?> + <?= t('Open') ?> + <?php else: ?> + <?= t('Closed') ?> + <?php endif ?> + </td> + </tr> + <?php endforeach ?> + </table> + + <?= $paginator ?> + <?php endif ?> +</section>
\ No newline at end of file diff --git a/app/Template/notification/footer.php b/app/Template/notification/footer.php index 69d2cf82..c3b37884 100644 --- a/app/Template/notification/footer.php +++ b/app/Template/notification/footer.php @@ -2,6 +2,6 @@ Kanboard <?php if (isset($application_url) && ! empty($application_url)): ?> - - <a href="<?= $application_url.$this->url->href('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= t('view the task on Kanboard') ?></a> - - <a href="<?= $application_url.$this->url->href('board', 'show', array('project_id' => $task['project_id'])) ?>"><?= t('view the board on Kanboard') ?></a> + - <a href="<?= $this->url->href('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', true) ?>"><?= t('view the task on Kanboard') ?></a> + - <a href="<?= $this->url->href('board', 'show', array('project_id' => $task['project_id']), false, '', true) ?>"><?= t('view the board on Kanboard') ?></a> <?php endif ?> diff --git a/app/Template/notification/task_overdue.php b/app/Template/notification/task_overdue.php index dc2659dc..a231937b 100644 --- a/app/Template/notification/task_overdue.php +++ b/app/Template/notification/task_overdue.php @@ -5,7 +5,7 @@ <li> (<strong>#<?= $task['id'] ?></strong>) <?php if ($application_url): ?> - <a href="<?= $application_url.$this->url->href('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= $this->e($task['title']) ?></a> + <a href="<?= $this->url->href('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', true) ?>"><?= $this->e($task['title']) ?></a> <?php else: ?> <?= $this->e($task['title']) ?> <?php endif ?> diff --git a/app/Template/project/dropdown.php b/app/Template/project/dropdown.php index 2e2650a7..aa4322e6 100644 --- a/app/Template/project/dropdown.php +++ b/app/Template/project/dropdown.php @@ -1,18 +1,6 @@ <li> - <i class="fa fa-search fa-fw"></i> - <?= $this->url->link(t('Search'), 'projectinfo', 'search', array('project_id' => $project['id'])) ?> -</li> -<li> - <i class="fa fa-check-square-o fa-fw"></i> - <?= $this->url->link(t('Completed tasks'), 'projectinfo', 'tasks', array('project_id' => $project['id'])) ?> -</li> -<li> <i class="fa fa-dashboard fa-fw"></i> - <?= $this->url->link(t('Activity'), 'projectinfo', 'activity', array('project_id' => $project['id'])) ?> -</li> -<li> - <i class="fa fa-calendar fa-fw"></i> - <?= $this->url->link(t('Calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?> + <?= $this->url->link(t('Activity'), 'activity', 'project', array('project_id' => $project['id'])) ?> </li> <?php if ($project['is_public']): ?> diff --git a/app/Template/project/filters.php b/app/Template/project/filters.php new file mode 100644 index 00000000..e2fdc751 --- /dev/null +++ b/app/Template/project/filters.php @@ -0,0 +1,51 @@ +<div class="page-header"> + <div class="dropdown"> + <span> + <i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a> + <ul> + <?php if (isset($is_board)): ?> + <li> + <span class="filter-display-mode" <?= $this->board->isCollapsed($project['id']) ? '' : 'style="display: none;"' ?>> + <i class="fa fa-expand fa-fw"></i> + <?= $this->url->link(t('Expand tasks'), 'board', 'expand', array('project_id' => $project['id']), false, 'board-display-mode', t('Keyboard shortcut: "%s"', 's')) ?> + </span> + <span class="filter-display-mode" <?= $this->board->isCollapsed($project['id']) ? 'style="display: none;"' : '' ?>> + <i class="fa fa-compress fa-fw"></i> + <?= $this->url->link(t('Collapse tasks'), 'board', 'collapse', array('project_id' => $project['id']), false, 'board-display-mode', t('Keyboard shortcut: "%s"', 's')) ?> + </span> + </li> + <li> + <span class="filter-compact"> + <i class="fa fa-th fa-fw"></i> <a href="#" class="filter-toggle-scrolling" title="<?= t('Keyboard shortcut: "%s"', 'c') ?>"><?= t('Compact view') ?></a> + </span> + <span class="filter-wide" style="display: none"> + <i class="fa fa-arrows-h fa-fw"></i> <a href="#" class="filter-toggle-scrolling" title="<?= t('Keyboard shortcut: "%s"', 'c') ?>"><?= t('Horizontal scrolling') ?></a> + </span> + </li> + <?php endif ?> + <?= $this->render('project/dropdown', array('project' => $project)) ?> + </ul> + </span> + </div> + <ul class="views"> + <li <?= $filters['controller'] === 'board' ? 'class="active"' : '' ?>> + <i class="fa fa-th fa-fw"></i> + <?= $this->url->link(t('Board'), 'board', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-board', t('Keyboard shortcut: "%s"', 'v b')) ?> + </li> + <li <?= $filters['controller'] === 'calendar' ? 'class="active"' : '' ?>> + <i class="fa fa-calendar fa-fw"></i> + <?= $this->url->link(t('Calendar'), 'calendar', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-calendar', t('Keyboard shortcut: "%s"', 'v c')) ?> + </li> + <li <?= $filters['controller'] === 'listing' ? 'class="active"' : '' ?>> + <i class="fa fa-list fa-fw"></i> + <?= $this->url->link(t('List'), 'listing', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-listing', t('Keyboard shortcut: "%s"', 'v l')) ?> + </li> + </ul> + <form method="get" action="<?= $this->url->dir() ?>" class="search"> + <?= $this->form->hidden('controller', $filters) ?> + <?= $this->form->hidden('action', $filters) ?> + <?= $this->form->hidden('project_id', $filters) ?> + <?= $this->form->text('search', $filters, array(), array('placeholder="'.t('Filter').'"'), 'form-input-large') ?> + </form> + <?= $this->render('app/filters_helper', array('reset' => 'status:open')) ?> +</div>
\ No newline at end of file diff --git a/app/Template/project/index.php b/app/Template/project/index.php index 1080968e..971ba2ae 100644 --- a/app/Template/project/index.php +++ b/app/Template/project/index.php @@ -35,7 +35,7 @@ <?= $this->e($project['identifier']) ?> </td> <td> - <?= $this->url->link('<i class="fa fa-table"></i>', 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Board')) ?> + <?= $this->url->link('<i class="fa fa-th"></i>', 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Board')) ?> <?php if ($project['is_public']): ?> <i class="fa fa-share-alt fa-fw"></i> @@ -46,7 +46,7 @@ <?= $this->url->link($this->e($project['name']), 'project', 'show', array('project_id' => $project['id'])) ?> <?php if (! empty($project['description'])): ?> - <span class="column-tooltip" title='<?= $this->e($this->text->markdown($project['description'])) ?>'> + <span class="tooltip" title='<?= $this->e($this->text->markdown($project['description'])) ?>'> <i class="fa fa-info-circle"></i> </span> <?php endif ?> diff --git a/app/Template/project/integrations.php b/app/Template/project/integrations.php index 698e438c..12a7ee4e 100644 --- a/app/Template/project/integrations.php +++ b/app/Template/project/integrations.php @@ -8,26 +8,26 @@ <h3><i class="fa fa-github fa-fw"></i> <?= t('Github webhooks') ?></h3> <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->base().$this->url->href('webhook', 'github', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'github', array('token' => $webhook_token, 'project_id' => $project['id']), false, '', true) ?>"/><br/> <p class="form-help"><a href="http://kanboard.net/documentation/github-webhooks" target="_blank"><?= t('Help on Github webhooks') ?></a></p> </div> - <h3><img src="assets/img/gitlab-icon.png"/> <?= t('Gitlab webhooks') ?></h3> + <h3><img src="<?= $this->url->dir() ?>assets/img/gitlab-icon.png"/> <?= t('Gitlab webhooks') ?></h3> <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->base().$this->url->href('webhook', 'gitlab', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'gitlab', array('token' => $webhook_token, 'project_id' => $project['id']), false, '', true) ?>"/><br/> <p class="form-help"><a href="http://kanboard.net/documentation/gitlab-webhooks" target="_blank"><?= t('Help on Gitlab webhooks') ?></a></p> </div> <h3><i class="fa fa-bitbucket fa-fw"></i> <?= t('Bitbucket webhooks') ?></h3> <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->base().$this->url->href('webhook', 'bitbucket', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/> + <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'bitbucket', array('token' => $webhook_token, 'project_id' => $project['id']), false, '', true) ?>"/><br/> <p class="form-help"><a href="http://kanboard.net/documentation/bitbucket-webhooks" target="_blank"><?= t('Help on Bitbucket webhooks') ?></a></p> </div> - <h3><img src="assets/img/jabber-icon.png"/> <?= t('Jabber (XMPP)') ?></h3> + <h3><img src="<?= $this->url->dir() ?>assets/img/jabber-icon.png"/> <?= t('Jabber (XMPP)') ?></h3> <div class="listing"> <?= $this->form->checkbox('jabber', t('Send notifications to Jabber'), 1, isset($values['jabber']) && $values['jabber'] == 1) ?> @@ -58,7 +58,7 @@ </div> - <h3><img src="assets/img/hipchat-icon.png"/> <?= t('Hipchat') ?></h3> + <h3><img src="<?= $this->url->dir() ?>assets/img/hipchat-icon.png"/> <?= t('Hipchat') ?></h3> <div class="listing"> <?= $this->form->checkbox('hipchat', t('Send notifications to Hipchat'), 1, isset($values['hipchat']) && $values['hipchat'] == 1) ?> @@ -85,6 +85,8 @@ <?= $this->form->label(t('Webhook URL'), 'slack_webhook_url') ?> <?= $this->form->text('slack_webhook_url', $values, $errors) ?> + <?= $this->form->label(t('Channel/Group/User (Optional)'), 'slack_webhook_channel') ?> + <?= $this->form->text('slack_webhook_channel', $values, $errors) ?> <p class="form-help"><a href="http://kanboard.net/documentation/slack" target="_blank"><?= t('Help on Slack integration') ?></a></p> diff --git a/app/Template/project/layout.php b/app/Template/project/layout.php index 7bb3d478..8ba92ef9 100644 --- a/app/Template/project/layout.php +++ b/app/Template/project/layout.php @@ -12,10 +12,14 @@ </span> </li> <li> - <i class="fa fa-table fa-fw"></i> + <i class="fa fa-th fa-fw"></i> <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?> </li> <li> + <i class="fa fa-calendar fa-fw"></i> + <?= $this->url->link(t('Back to the calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?> + </li> + <li> <i class="fa fa-folder fa-fw"></i> <?= $this->url->link(t('All projects'), 'project', 'index') ?> </li> diff --git a/app/Template/project/show.php b/app/Template/project/show.php index beb5a1fa..969dda17 100644 --- a/app/Template/project/show.php +++ b/app/Template/project/show.php @@ -51,7 +51,7 @@ <td> <?= $this->e($column['title']) ?> <?php if (! empty($column['description'])): ?> - <span class="column-tooltip" title='<?= $this->e($this->text->markdown($column['description'])) ?>'> + <span class="tooltip" title='<?= $this->e($this->text->markdown($column['description'])) ?>'> <i class="fa fa-info-circle"></i> </span> <?php endif ?> diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php index 3762f1ff..7ee39f53 100644 --- a/app/Template/project/sidebar.php +++ b/app/Template/project/sidebar.php @@ -6,47 +6,49 @@ </li> <?php if ($this->user->isManager($project['id'])): ?> - <li> - <?= $this->url->link(t('Public access'), 'project', 'share', array('project_id' => $project['id'])) ?> - </li> - <li> - <?= $this->url->link(t('Integrations'), 'project', 'integration', array('project_id' => $project['id'])) ?> - </li> - <li> - <?= $this->url->link(t('Edit project'), 'project', 'edit', array('project_id' => $project['id'])) ?> - </li> - <li> - <?= $this->url->link(t('Columns'), 'column', 'index', array('project_id' => $project['id'])) ?> - </li> - <li> - <?= $this->url->link(t('Swimlanes'), 'swimlane', 'index', array('project_id' => $project['id'])) ?> - </li> - <li> - <?= $this->url->link(t('Categories'), 'category', 'index', array('project_id' => $project['id'])) ?> - </li> - <?php if ($this->user->isAdmin() || $project['is_private'] == 0): ?> - <li> - <?= $this->url->link(t('Users'), 'project', 'users', array('project_id' => $project['id'])) ?> - </li> - <?php endif ?> - <li> - <?= $this->url->link(t('Automatic actions'), 'action', 'index', array('project_id' => $project['id'])) ?> - </li> - <li> - <?= $this->url->link(t('Duplicate'), 'project', 'duplicate', array('project_id' => $project['id'])) ?> - </li> - <li> - <?php if ($project['is_active']): ?> - <?= $this->url->link(t('Disable'), 'project', 'disable', array('project_id' => $project['id']), true) ?> - <?php else: ?> - <?= $this->url->link(t('Enable'), 'project', 'enable', array('project_id' => $project['id']), true) ?> + <li> + <?= $this->url->link(t('Public access'), 'project', 'share', array('project_id' => $project['id'])) ?> + </li> + <li> + <?= $this->url->link(t('Integrations'), 'project', 'integration', array('project_id' => $project['id'])) ?> + </li> + <li> + <?= $this->url->link(t('Edit project'), 'project', 'edit', array('project_id' => $project['id'])) ?> + </li> + <li> + <?= $this->url->link(t('Columns'), 'column', 'index', array('project_id' => $project['id'])) ?> + </li> + <li> + <?= $this->url->link(t('Swimlanes'), 'swimlane', 'index', array('project_id' => $project['id'])) ?> + </li> + <li> + <?= $this->url->link(t('Categories'), 'category', 'index', array('project_id' => $project['id'])) ?> + </li> + <?php if ($this->user->isAdmin() || $project['is_private'] == 0): ?> + <li> + <?= $this->url->link(t('Users'), 'project', 'users', array('project_id' => $project['id'])) ?> + </li> <?php endif ?> - </li> - <?php if ($this->user->isAdmin()): ?> <li> - <?= $this->url->link(t('Remove'), 'project', 'remove', array('project_id' => $project['id'])) ?> + <?= $this->url->link(t('Automatic actions'), 'action', 'index', array('project_id' => $project['id'])) ?> </li> - <?php endif ?> + <li> + <?= $this->url->link(t('Duplicate'), 'project', 'duplicate', array('project_id' => $project['id'])) ?> + </li> + <li> + <?php if ($project['is_active']): ?> + <?= $this->url->link(t('Disable'), 'project', 'disable', array('project_id' => $project['id']), true) ?> + <?php else: ?> + <?= $this->url->link(t('Enable'), 'project', 'enable', array('project_id' => $project['id']), true) ?> + <?php endif ?> + </li> + <?php if ($this->user->isAdmin()): ?> + <li> + <?= $this->url->link(t('Remove'), 'project', 'remove', array('project_id' => $project['id'])) ?> + </li> + <?php endif ?> <?php endif ?> </ul> + <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> + <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div> diff --git a/app/Template/projectinfo/search.php b/app/Template/projectinfo/search.php deleted file mode 100644 index 4b7c8f70..00000000 --- a/app/Template/projectinfo/search.php +++ /dev/null @@ -1,43 +0,0 @@ -<section id="main"> - <div class="page-header"> - <ul> - <li> - <span class="dropdown"> - <span> - <i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a> - <ul> - <?= $this->render('project/dropdown', array('project' => $project)) ?> - </ul> - </span> - </span> - </li> - <li> - <i class="fa fa-table fa-fw"></i> - <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?> - </li> - <li> - <i class="fa fa-folder fa-fw"></i> - <?= $this->url->link(t('All projects'), 'project', 'index') ?> - </li> - </ul> - </div> - - <form method="get" action="?" autocomplete="off"> - <?= $this->form->hidden('controller', $values) ?> - <?= $this->form->hidden('action', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->text('search', $values, array(), array('autofocus', 'required', 'placeholder="'.t('Search').'"'), 'form-input-large') ?> - <input type="submit" value="<?= t('Search') ?>" class="btn btn-blue"/> - </form> - - <?php if (! empty($values['search']) && $paginator->isEmpty()): ?> - <p class="alert"><?= t('Nothing found.') ?></p> - <?php elseif (! $paginator->isEmpty()): ?> - <?= $this->render('task/table', array( - 'paginator' => $paginator, - 'categories' => $categories, - 'columns' => $columns, - )) ?> - <?php endif ?> - -</section>
\ No newline at end of file diff --git a/app/Template/projectinfo/tasks.php b/app/Template/projectinfo/tasks.php deleted file mode 100644 index 41884783..00000000 --- a/app/Template/projectinfo/tasks.php +++ /dev/null @@ -1,33 +0,0 @@ -<section id="main"> - <div class="page-header"> - <ul> - <li> - <span class="dropdown"> - <span> - <i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a> - <ul> - <?= $this->render('project/dropdown', array('project' => $project)) ?> - </ul> - </span> - </span> - </li> - <li> - <i class="fa fa-table fa-fw"></i> - <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?> - </li> - <li> - <i class="fa fa-folder fa-fw"></i> - <?= $this->url->link(t('All projects'), 'project', 'index') ?> - </li> - </ul> - </div> - <?php if ($paginator->isEmpty()): ?> - <p class="alert"><?= t('There is no completed tasks at the moment.') ?></p> - <?php else: ?> - <?= $this->render('task/table', array( - 'paginator' => $paginator, - 'categories' => $categories, - 'columns' => $columns, - )) ?> - <?php endif ?> -</section>
\ No newline at end of file diff --git a/app/Template/search/index.php b/app/Template/search/index.php index 47a926f4..8940a24e 100644 --- a/app/Template/search/index.php +++ b/app/Template/search/index.php @@ -8,12 +8,15 @@ </ul> </div> - <form method="get" action="?" autocomplete="off"> - <?= $this->form->hidden('controller', $values) ?> - <?= $this->form->hidden('action', $values) ?> - <?= $this->form->text('search', $values, array(), array('autofocus', 'required', 'placeholder="'.t('Search').'"'), 'form-input-large') ?> - <input type="submit" value="<?= t('Search') ?>" class="btn btn-blue"/> - </form> + <div class="search"> + <form method="get" action="<?= $this->url->dir() ?>" class="search"> + <?= $this->form->hidden('controller', $values) ?> + <?= $this->form->hidden('action', $values) ?> + <?= $this->form->text('search', $values, array(), array(empty($values['search']) ? 'autofocus' : '', 'placeholder="'.t('Search').'"'), 'form-input-large') ?> + </form> + + <?= $this->render('app/filters_helper') ?> + </div> <?php if (empty($values['search'])): ?> <div class="listing"> @@ -28,7 +31,7 @@ <li><?= t('Search by description: ') ?><strong>description:"Something to find"</strong></li> <li><?= t('Search by due date: ') ?><strong>due:2015-07-01</strong></li> </ul> - <p><a href="http://kanboard.net/documentation/search" target="_blank"><?= t('More examples in the documentation') ?></a></p> + <p><i class="fa fa-external-link fa-fw"></i><a href="http://kanboard.net/documentation/search" target="_blank"><?= t('View advanced search syntax') ?></a></p> </div> <?php elseif (! empty($values['search']) && $paginator->isEmpty()): ?> <p class="alert"><?= t('Nothing found.') ?></p> diff --git a/app/Template/search/results.php b/app/Template/search/results.php index 1d8cc6e2..04cb6a19 100644 --- a/app/Template/search/results.php +++ b/app/Template/search/results.php @@ -1,14 +1,13 @@ <table class="table-fixed table-small"> <tr> <th class="column-8"><?= $paginator->order(t('Project'), 'tasks.project_id') ?></th> - <th class="column-8"><?= $paginator->order(t('Id'), 'tasks.id') ?></th> - <th class="column-8"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th> - <th class="column-8"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th> + <th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th> + <th class="column-10"><?= $paginator->order(t('Swimlane'), 'tasks.swimlane_id') ?></th> + <th class="column-10"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th> + <th class="column-10"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th> <th><?= $paginator->order(t('Title'), 'tasks.title') ?></th> <th class="column-10"><?= $paginator->order(t('Assignee'), 'users.username') ?></th> <th class="column-10"><?= $paginator->order(t('Due date'), 'tasks.date_due') ?></th> - <th class="column-10"><?= $paginator->order(t('Date created'), 'tasks.date_creation') ?></th> - <th class="column-10"><?= $paginator->order(t('Date completed'), 'tasks.date_completed') ?></th> <th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th> </tr> <?php foreach ($paginator->getCollection() as $task): ?> @@ -20,6 +19,9 @@ <?= $this->url->link('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> </td> <td> + <?= $this->e($task['swimlane_name'] ?: $task['default_swimlane']) ?> + </td> + <td> <?= $this->e($task['column_name']) ?> </td> <td> @@ -39,14 +41,6 @@ <?= dt('%B %e, %Y', $task['date_due']) ?> </td> <td> - <?= dt('%B %e, %Y', $task['date_creation']) ?> - </td> - <td> - <?php if ($task['date_completed']): ?> - <?= dt('%B %e, %Y', $task['date_completed']) ?> - <?php endif ?> - </td> - <td> <?php if ($task['is_active'] == \Model\Task::STATUS_OPEN): ?> <?= t('Open') ?> <?php else: ?> diff --git a/app/Template/subtask/show.php b/app/Template/subtask/show.php index b91e830f..1f0f9bba 100644 --- a/app/Template/subtask/show.php +++ b/app/Template/subtask/show.php @@ -29,7 +29,11 @@ </td> <td> <?php if (! empty($subtask['username'])): ?> - <?= $this->url->link($this->e($subtask['name'] ?: $subtask['username']), 'user', 'show', array('user_id' => $subtask['user_id'])) ?> + <?php if (! isset($not_editable)): ?> + <?= $this->url->link($this->e($subtask['name'] ?: $subtask['username']), 'user', 'show', array('user_id' => $subtask['user_id'])) ?> + <?php else: ?> + <?= $this->e($subtask['name'] ?: $subtask['username']) ?> + <?php endif ?> <?php endif ?> </td> <td> @@ -48,7 +52,7 @@ <?php if ($subtask['is_timer_started']): ?> <i class="fa fa-pause"></i> <?= $this->url->link(t('Stop timer'), 'timer', 'subtask', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?> - (<?= $this->datetime->age($subtask['timer_start_date']) ?>) + (<?= $this->dt->age($subtask['timer_start_date']) ?>) <?php else: ?> <i class="fa fa-play-circle-o"></i> <?= $this->url->link(t('Start timer'), 'timer', 'subtask', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?> @@ -88,6 +92,8 @@ <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> <?= $this->form->text('title', array(), array(), array('required', 'placeholder="'.t('Type here to create a new sub-task').'"')) ?> + <?= $this->form->numeric('time_estimated', array(), array(), array('placeholder="'.t('Original estimate').'"')) ?> + <?= $this->form->select('user_id', $users_list, array(), array(), array('placeholder="'.t('Assignee').'"')) ?> <input type="submit" value="<?= t('Add') ?>" class="btn btn-blue"/> </form> <?php endif ?> diff --git a/app/Template/task/analytics.php b/app/Template/task/analytics.php new file mode 100644 index 00000000..3b1d2855 --- /dev/null +++ b/app/Template/task/analytics.php @@ -0,0 +1,36 @@ +<div class="page-header"> + <h2><?= t('Analytics') ?></h2> +</div> + +<div class="listing"> + <ul> + <li><?= t('Lead time: ').'<strong>'.$this->dt->duration($lead_time) ?></strong></li> + <li><?= t('Cycle time: ').'<strong>'.$this->dt->duration($cycle_time) ?></strong></li> + </ul> +</div> + +<h3 id="analytic-task-time-column"><?= t('Time spent into each column') ?></h3> +<div id="chart" data-metrics='<?= json_encode($time_spent_columns) ?>' data-label="<?= t('Time spent') ?>"></div> +<table class="table-stripped"> + <tr> + <th><?= t('Column') ?></th> + <th><?= t('Time spent') ?></th> + </tr> + <?php foreach ($time_spent_columns as $column): ?> + <tr> + <td><?= $this->e($column['title']) ?></td> + <td><?= $this->dt->duration($column['time_spent']) ?></td> + </tr> + <?php endforeach ?> +</table> + +<div class="alert alert-info"> + <ul> + <li><?= t('The lead time is the duration between the task creation and the completion.') ?></li> + <li><?= t('The cycle time is the duration between the start date and the completion.') ?></li> + <li><?= t('If the task is not closed the current time is used instead of the completion date.') ?></li> + </ul> +</div> + +<?= $this->asset->js('assets/js/vendor/d3.v3.min.js') ?> +<?= $this->asset->js('assets/js/vendor/c3.min.js') ?>
\ No newline at end of file diff --git a/app/Template/task/show_description.php b/app/Template/task/description.php index f823e7d6..f823e7d6 100644 --- a/app/Template/task/show_description.php +++ b/app/Template/task/description.php diff --git a/app/Template/task/duplicate_project.php b/app/Template/task/duplicate_project.php deleted file mode 100644 index 9a8e3c4a..00000000 --- a/app/Template/task/duplicate_project.php +++ /dev/null @@ -1,24 +0,0 @@ -<div class="page-header"> - <h2><?= t('Duplicate the task to another project') ?></h2> -</div> - -<?php if (empty($projects_list)): ?> - <p class="alert"><?= t('No project') ?></p> -<?php else: ?> - - <form method="post" action="<?= $this->url->href('task', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> - - <?= $this->form->csrf() ?> - - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->label(t('Project'), 'project_id') ?> - <?= $this->form->select('project_id', $projects_list, $values, $errors) ?><br/> - - <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </div> - </form> - -<?php endif ?>
\ No newline at end of file diff --git a/app/Template/task/layout.php b/app/Template/task/layout.php index 5a14fb39..bbccf177 100644 --- a/app/Template/task/layout.php +++ b/app/Template/task/layout.php @@ -2,8 +2,12 @@ <div class="page-header"> <ul> <li> - <i class="fa fa-table fa-fw"></i> - <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $task['project_id']), false, '', '', false, 'swimlane-'.$task['swimlane_id']) ?> + <i class="fa fa-th fa-fw"></i> + <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $task['project_id']), false, '', '', false, $task['swimlane_id'] != 0 ? 'swimlane-'.$task['swimlane_id'] : '') ?> + </li> + <li> + <i class="fa fa-calendar fa-fw"></i> + <?= $this->url->link(t('Back to the calendar'), 'calendar', 'show', array('project_id' => $task['project_id'])) ?> </li> <?php if ($this->user->isManager($task['project_id'])): ?> <li> @@ -11,10 +15,6 @@ <?= $this->url->link(t('Project settings'), 'project', 'show', array('project_id' => $task['project_id'])) ?> </li> <?php endif ?> - <li> - <i class="fa fa-calendar fa-fw"></i> - <?= $this->url->link(t('Project calendar'), 'calendar', 'show', array('project_id' => $task['project_id'])) ?> - </li> </ul> </div> <section class="sidebar-container" id="task-section"> diff --git a/app/Template/task/move_project.php b/app/Template/task/move_project.php deleted file mode 100644 index b0b33f81..00000000 --- a/app/Template/task/move_project.php +++ /dev/null @@ -1,24 +0,0 @@ -<div class="page-header"> - <h2><?= t('Move the task to another project') ?></h2> -</div> - -<?php if (empty($projects_list)): ?> - <p class="alert"><?= t('No project') ?></p> -<?php else: ?> - - <form method="post" action="<?= $this->url->href('task', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> - - <?= $this->form->csrf() ?> - - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->label(t('Project'), 'project_id') ?> - <?= $this->form->select('project_id', $projects_list, $values, $errors) ?><br/> - - <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </div> - </form> - -<?php endif ?>
\ No newline at end of file diff --git a/app/Template/task/public.php b/app/Template/task/public.php index 73116de9..e3105488 100644 --- a/app/Template/task/public.php +++ b/app/Template/task/public.php @@ -4,7 +4,7 @@ <p class="pull-right"><?= $this->url->link(t('Back to the board'), 'board', 'readonly', array('token' => $project['token'])) ?></p> - <?= $this->render('task/show_description', array( + <?= $this->render('task/description', array( 'task' => $task, 'project' => $project, 'is_public' => true diff --git a/app/Template/task/show.php b/app/Template/task/show.php index 54c124f6..68d63c58 100644 --- a/app/Template/task/show.php +++ b/app/Template/task/show.php @@ -6,10 +6,10 @@ 'recurrence_basedate_list' => $this->task->recurrenceBasedates(), )) ?> -<?= $this->render('task/time', array('task' => $task, 'values' => $values, 'date_format' => $date_format, 'date_formats' => $date_formats)) ?> -<?= $this->render('task/show_description', array('task' => $task)) ?> +<?= $this->render('task_modification/edit_time', array('task' => $task, 'values' => $values, 'date_format' => $date_format, 'date_formats' => $date_formats)) ?> +<?= $this->render('task/description', array('task' => $task)) ?> <?= $this->render('tasklink/show', array('task' => $task, 'links' => $links, 'link_label_list' => $link_label_list)) ?> -<?= $this->render('subtask/show', array('task' => $task, 'subtasks' => $subtasks, 'project' => $project)) ?> -<?= $this->render('task/timesheet', array('task' => $task)) ?> +<?= $this->render('subtask/show', array('task' => $task, 'subtasks' => $subtasks, 'project' => $project, 'users_list' => isset($users_list) ? $users_list : array())) ?> +<?= $this->render('task/time_tracking_summary', array('task' => $task)) ?> <?= $this->render('file/show', array('task' => $task, 'files' => $files, 'images' => $images)) ?> <?= $this->render('task/comments', array('task' => $task, 'comments' => $comments, 'project' => $project)) ?> diff --git a/app/Template/task/sidebar.php b/app/Template/task/sidebar.php index bb137ac9..1116040a 100644 --- a/app/Template/task/sidebar.php +++ b/app/Template/task/sidebar.php @@ -5,27 +5,30 @@ <?= $this->url->link(t('Summary'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <?= $this->url->link(t('Activity stream'), 'task', 'activites', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Activity stream'), 'activity', 'task', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> <?= $this->url->link(t('Transitions'), 'task', 'transitions', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> + <li> + <?= $this->url->link(t('Analytics'), 'task', 'analytics', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + </li> <?php if ($task['time_estimated'] > 0 || $task['time_spent'] > 0): ?> <li> - <?= $this->url->link(t('Time tracking'), 'task', 'timesheet', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Time tracking'), 'task', 'timetracking', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php endif ?> </ul> <h2><?= t('Actions') ?></h2> <ul> <li> - <?= $this->url->link(t('Edit the task'), 'task', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Edit the task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <?= $this->url->link(t('Edit the description'), 'task', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Edit the description'), 'taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <?= $this->url->link(t('Edit recurrence'), 'task', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Edit recurrence'), 'taskmodification', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> <?= $this->url->link(t('Add a sub-task'), 'subtask', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> @@ -43,19 +46,19 @@ <?= $this->url->link(t('Add a screenshot'), 'file', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <?= $this->url->link(t('Duplicate'), 'task', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Duplicate'), 'taskduplication', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <?= $this->url->link(t('Duplicate to another project'), 'task', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Duplicate to another project'), 'taskduplication', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <?= $this->url->link(t('Move to another project'), 'task', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Move to another project'), 'taskduplication', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> <?php if ($task['is_active'] == 1): ?> - <?= $this->url->link(t('Close this task'), 'task', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> <?php else: ?> - <?= $this->url->link(t('Open this task'), 'task', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('Open this task'), 'taskstatus', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> <?php endif ?> </li> <?php if ($this->task->canRemove($task)): ?> @@ -64,4 +67,6 @@ </li> <?php endif ?> </ul> + <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> + <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div> diff --git a/app/Template/task/table.php b/app/Template/task/table.php deleted file mode 100644 index d06bc7b7..00000000 --- a/app/Template/task/table.php +++ /dev/null @@ -1,56 +0,0 @@ -<table class="table-fixed table-small"> - <tr> - <th class="column-8"><?= $paginator->order(t('Id'), 'tasks.id') ?></th> - <th class="column-8"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th> - <th class="column-8"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th> - <th><?= $paginator->order(t('Title'), 'tasks.title') ?></th> - <th class="column-10"><?= $paginator->order(t('Assignee'), 'users.username') ?></th> - <th class="column-10"><?= $paginator->order(t('Due date'), 'tasks.date_due') ?></th> - <th class="column-10"><?= $paginator->order(t('Date created'), 'tasks.date_creation') ?></th> - <th class="column-10"><?= $paginator->order(t('Date completed'), 'tasks.date_completed') ?></th> - <th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th> - </tr> - <?php foreach ($paginator->getCollection() as $task): ?> - <tr> - <td class="task-table color-<?= $task['color_id'] ?>"> - <?= $this->url->link('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> - </td> - <td> - <?= $this->text->in($task['column_id'], $columns) ?> - </td> - <td> - <?= $this->text->in($task['category_id'], $categories, '') ?> - </td> - <td> - <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> - </td> - <td> - <?php if ($task['assignee_username']): ?> - <?= $this->e($task['assignee_name'] ?: $task['assignee_username']) ?> - <?php else: ?> - <?= t('Unassigned') ?> - <?php endif ?> - </td> - <td> - <?= dt('%B %e, %Y', $task['date_due']) ?> - </td> - <td> - <?= dt('%B %e, %Y', $task['date_creation']) ?> - </td> - <td> - <?php if ($task['date_completed']): ?> - <?= dt('%B %e, %Y', $task['date_completed']) ?> - <?php endif ?> - </td> - <td> - <?php if ($task['is_active'] == \Model\Task::STATUS_OPEN): ?> - <?= t('Open') ?> - <?php else: ?> - <?= t('Closed') ?> - <?php endif ?> - </td> - </tr> - <?php endforeach ?> -</table> - -<?= $paginator ?> diff --git a/app/Template/task/time_tracking.php b/app/Template/task/time_tracking_details.php index 441cb585..faa07cb8 100644 --- a/app/Template/task/time_tracking.php +++ b/app/Template/task/time_tracking_details.php @@ -1,4 +1,4 @@ -<?= $this->render('task/timesheet', array('task' => $task)) ?> +<?= $this->render('task/time_tracking_summary', array('task' => $task)) ?> <h3><?= t('Subtask timesheet') ?></h3> <?php if ($subtask_paginator->isEmpty()): ?> diff --git a/app/Template/task/timesheet.php b/app/Template/task/time_tracking_summary.php index 0210be7e..0210be7e 100644 --- a/app/Template/task/timesheet.php +++ b/app/Template/task/time_tracking_summary.php diff --git a/app/Template/task/transitions.php b/app/Template/task/transitions.php index 6455fd66..2ca2387f 100644 --- a/app/Template/task/transitions.php +++ b/app/Template/task/transitions.php @@ -19,7 +19,7 @@ <td><?= $this->e($transition['src_column']) ?></td> <td><?= $this->e($transition['dst_column']) ?></td> <td><?= $this->url->link($this->e($transition['name'] ?: $transition['username']), 'user', 'show', array('user_id' => $transition['user_id'])) ?></td> - <td><?= n(round($transition['time_spent'] / 3600, 2)).' '.t('hours') ?></td> + <td><?= $this->dt->duration($transition['time_spent']) ?></td> </tr> <?php endforeach ?> </table> diff --git a/app/Template/task/new.php b/app/Template/task_creation/form.php index 181b82bf..84f28a1e 100644 --- a/app/Template/task/new.php +++ b/app/Template/task_creation/form.php @@ -1,7 +1,7 @@ <?php if (! $ajax): ?> <div class="page-header"> <ul> - <li><i class="fa fa-table fa-fw"></i><?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $values['project_id'])) ?></li> + <li><i class="fa fa-th fa-fw"></i><?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $values['project_id'])) ?></li> </ul> </div> <?php else: ?> @@ -11,7 +11,7 @@ <?php endif ?> <section id="task-section"> -<form method="post" action="<?= $this->url->href('task', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('taskcreation', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> diff --git a/app/Template/task_duplication/copy.php b/app/Template/task_duplication/copy.php new file mode 100644 index 00000000..f9106c1d --- /dev/null +++ b/app/Template/task_duplication/copy.php @@ -0,0 +1,43 @@ +<div class="page-header"> + <h2><?= t('Duplicate the task to another project') ?></h2> +</div> + +<?php if (empty($projects_list)): ?> + <p class="alert"><?= t('There is no destination project available.') ?></p> +<?php else: ?> + + <form method="post" action="<?= $this->url->href('taskduplication', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> + + <?= $this->form->csrf() ?> + <?= $this->form->hidden('id', $values) ?> + + <?= $this->form->label(t('Project'), 'project_id') ?> + <?= $this->form->select( + 'project_id', + $projects_list, + $values, + array(), + array('data-redirect="'.$this->url->href('taskduplication', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')).'"'), + 'task-reload-project-destination' + ) ?> + + <?= $this->form->label(t('Swimlane'), 'swimlane_id') ?> + <?= $this->form->select('swimlane_id', $swimlanes_list, $values) ?> + + <?= $this->form->label(t('Column'), 'column_id') ?> + <?= $this->form->select('column_id', $columns_list, $values) ?> + + <?= $this->form->label(t('Category'), 'category_id') ?> + <?= $this->form->select('category_id', $categories_list, $values) ?> + + <?= $this->form->label(t('Assignee'), 'owner_id') ?> + <?= $this->form->select('owner_id', $users_list, $values) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + </div> + </form> + +<?php endif ?>
\ No newline at end of file diff --git a/app/Template/task/duplicate.php b/app/Template/task_duplication/duplicate.php index e74d2906..4b50d9ca 100644 --- a/app/Template/task/duplicate.php +++ b/app/Template/task_duplication/duplicate.php @@ -8,7 +8,7 @@ </p> <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'task', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?> + <?= $this->url->link(t('Yes'), 'taskduplication', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </div> diff --git a/app/Template/task_duplication/move.php b/app/Template/task_duplication/move.php new file mode 100644 index 00000000..e90424a2 --- /dev/null +++ b/app/Template/task_duplication/move.php @@ -0,0 +1,43 @@ +<div class="page-header"> + <h2><?= t('Move the task to another project') ?></h2> +</div> + +<?php if (empty($projects_list)): ?> + <p class="alert"><?= t('There is no destination project available.') ?></p> +<?php else: ?> + + <form method="post" action="<?= $this->url->href('taskduplication', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> + + <?= $this->form->csrf() ?> + <?= $this->form->hidden('id', $values) ?> + + <?= $this->form->label(t('Project'), 'project_id') ?> + <?= $this->form->select( + 'project_id', + $projects_list, + $values, + array(), + array('data-redirect="'.$this->url->href('taskduplication', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')).'"'), + 'task-reload-project-destination' + ) ?> + + <?= $this->form->label(t('Swimlane'), 'swimlane_id') ?> + <?= $this->form->select('swimlane_id', $swimlanes_list, $values) ?> + + <?= $this->form->label(t('Column'), 'column_id') ?> + <?= $this->form->select('column_id', $columns_list, $values) ?> + + <?= $this->form->label(t('Category'), 'category_id') ?> + <?= $this->form->select('category_id', $categories_list, $values) ?> + + <?= $this->form->label(t('Assignee'), 'owner_id') ?> + <?= $this->form->select('owner_id', $users_list, $values) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + </div> + </form> + +<?php endif ?>
\ No newline at end of file diff --git a/app/Template/task/edit_description.php b/app/Template/task_modification/edit_description.php index 84f0cebd..3168f7a3 100644 --- a/app/Template/task/edit_description.php +++ b/app/Template/task_modification/edit_description.php @@ -2,7 +2,7 @@ <h2><?= t('Edit the description') ?></h2> </div> -<form method="post" action="<?= $this->url->href('task', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -17,7 +17,7 @@ </li> </ul> <div class="write-area"> - <?= $this->form->textarea('description', $values, $errors, array('autofocus', 'placeholder="'.t('Leave a description').'"'), 'description-textarea') ?> + <?= $this->form->textarea('description', $values, $errors, array('autofocus', 'placeholder="'.t('Leave a description').'"'), 'task-show-description-textarea') ?> </div> <div class="preview-area"> <div class="markdown"></div> diff --git a/app/Template/task/edit_recurrence.php b/app/Template/task_modification/edit_recurrence.php index c261e368..f63f1516 100644 --- a/app/Template/task/edit_recurrence.php +++ b/app/Template/task_modification/edit_recurrence.php @@ -15,7 +15,7 @@ <?php if ($task['recurrence_status'] != \Model\Task::RECURRING_STATUS_PROCESSED): ?> - <form method="post" action="<?= $this->url->href('task', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" autocomplete="off"> + <form method="post" action="<?= $this->url->href('taskmodification', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -40,12 +40,7 @@ <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> <?= t('or') ?> - - <?php if ($ajax): ?> - <?= $this->url->link(t('cancel'), 'board', 'show', array('project_id' => $task['project_id'])) ?> - <?php else: ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - <?php endif ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </div> </form> diff --git a/app/Template/task/edit.php b/app/Template/task_modification/edit_task.php index 359df779..fe4696d6 100644 --- a/app/Template/task/edit.php +++ b/app/Template/task_modification/edit_task.php @@ -2,7 +2,7 @@ <h2><?= t('Edit a task') ?></h2> </div> <section id="task-section"> -<form method="post" action="<?= $this->url->href('task', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('taskmodification', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -30,8 +30,6 @@ </ul> </div> - <div class="form-help"><a href="http://kanboard.net/documentation/syntax-guide" target="_blank" rel="noreferrer"><?= t('Write your text in Markdown') ?></a></div> - </div> <div class="form-column"> diff --git a/app/Template/task/time.php b/app/Template/task_modification/edit_time.php index 6682a08d..8e7f9b42 100644 --- a/app/Template/task/time.php +++ b/app/Template/task_modification/edit_time.php @@ -1,9 +1,14 @@ -<form method="post" action="<?= $this->url->href('task', 'time', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" class="form-inline task-time-form" autocomplete="off"> +<form method="post" action="<?= $this->url->href('taskmodification', 'time', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" class="form-inline task-time-form" autocomplete="off"> + + <?php if (empty($values['date_started'])): ?> + <?= $this->url->link('<i class="fa fa-play"></i>', 'taskmodification', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-show-start-link', t('Set automatically the start date')) ?> + <?php endif ?> + <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->label(t('Start date'), 'date_started') ?> - <?= $this->form->text('date_started', $values, array(), array('placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> + <?= $this->form->text('date_started', $values, array(), array('placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-datetime') ?> <?= $this->form->label(t('Time estimated'), 'time_estimated') ?> <?= $this->form->numeric('time_estimated', $values, array(), array('placeholder="'.t('hours').'"')) ?> diff --git a/app/Template/task/close.php b/app/Template/task_status/close.php index 160d5400..4de3dcb2 100644 --- a/app/Template/task/close.php +++ b/app/Template/task_status/close.php @@ -8,7 +8,7 @@ </p> <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'task', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes', 'redirect' => $redirect), true, 'btn btn-red') ?> + <?= $this->url->link(t('Yes'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes', 'redirect' => $redirect), true, 'btn btn-red') ?> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> diff --git a/app/Template/task/open.php b/app/Template/task_status/open.php index fbcc1111..0043fdae 100644 --- a/app/Template/task/open.php +++ b/app/Template/task_status/open.php @@ -8,7 +8,7 @@ </p> <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'task', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?> + <?= $this->url->link(t('Yes'), 'taskstatus', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </div> diff --git a/app/Template/timetable_day/index.php b/app/Template/timetable_day/index.php index d2877816..386ceec2 100644 --- a/app/Template/timetable_day/index.php +++ b/app/Template/timetable_day/index.php @@ -30,10 +30,10 @@ <?= $this->form->csrf() ?> <?= $this->form->label(t('Start time'), 'start') ?> - <?= $this->form->select('start', $this->datetime->getDayHours(), $values, $errors) ?> + <?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?> <?= $this->form->label(t('End time'), 'end') ?> - <?= $this->form->select('end', $this->datetime->getDayHours(), $values, $errors) ?> + <?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?> <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> diff --git a/app/Template/timetable_extra/index.php b/app/Template/timetable_extra/index.php index d3224ae6..e9982335 100644 --- a/app/Template/timetable_extra/index.php +++ b/app/Template/timetable_extra/index.php @@ -42,10 +42,10 @@ <?= $this->form->checkbox('all_day', t('All day'), 1) ?> <?= $this->form->label(t('Start time'), 'start') ?> - <?= $this->form->select('start', $this->datetime->getDayHours(), $values, $errors) ?> + <?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?> <?= $this->form->label(t('End time'), 'end') ?> - <?= $this->form->select('end', $this->datetime->getDayHours(), $values, $errors) ?> + <?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?> <?= $this->form->label(t('Comment'), 'comment') ?> <?= $this->form->text('comment', $values, $errors) ?> diff --git a/app/Template/timetable_off/index.php b/app/Template/timetable_off/index.php index 75e02dbd..615c2b8d 100644 --- a/app/Template/timetable_off/index.php +++ b/app/Template/timetable_off/index.php @@ -42,10 +42,10 @@ <?= $this->form->checkbox('all_day', t('All day'), 1) ?> <?= $this->form->label(t('Start time'), 'start') ?> - <?= $this->form->select('start', $this->datetime->getDayHours(), $values, $errors) ?> + <?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?> <?= $this->form->label(t('End time'), 'end') ?> - <?= $this->form->select('end', $this->datetime->getDayHours(), $values, $errors) ?> + <?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?> <?= $this->form->label(t('Comment'), 'comment') ?> <?= $this->form->text('comment', $values, $errors) ?> diff --git a/app/Template/timetable_week/index.php b/app/Template/timetable_week/index.php index 552e9302..d58c6cfb 100644 --- a/app/Template/timetable_week/index.php +++ b/app/Template/timetable_week/index.php @@ -13,7 +13,7 @@ </tr> <?php foreach ($timetable as $slot): ?> <tr> - <td><?= $this->datetime->getWeekDay($slot['day']) ?></td> + <td><?= $this->dt->getWeekDay($slot['day']) ?></td> <td><?= $slot['start'] ?></td> <td><?= $slot['end'] ?></td> <td> @@ -32,13 +32,13 @@ <?= $this->form->csrf() ?> <?= $this->form->label(t('Day'), 'day') ?> - <?= $this->form->select('day', $this->datetime->getWeekDays(), $values, $errors) ?> + <?= $this->form->select('day', $this->dt->getWeekDays(), $values, $errors) ?> <?= $this->form->label(t('Start time'), 'start') ?> - <?= $this->form->select('start', $this->datetime->getDayHours(), $values, $errors) ?> + <?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?> <?= $this->form->label(t('End time'), 'end') ?> - <?= $this->form->select('end', $this->datetime->getDayHours(), $values, $errors) ?> + <?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?> <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> diff --git a/app/Template/user/authentication.php b/app/Template/user/authentication.php new file mode 100644 index 00000000..a62c8f93 --- /dev/null +++ b/app/Template/user/authentication.php @@ -0,0 +1,32 @@ +<div class="page-header"> + <h2><?= t('Edit Authentication') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('user', 'authentication', array('user_id' => $user['id'])) ?>" autocomplete="off"> + + <?= $this->form->csrf() ?> + + <?= $this->form->hidden('id', $values) ?> + <?= $this->form->hidden('username', $values) ?> + + <?= $this->form->label(t('Google Id'), 'google_id') ?> + <?= $this->form->text('google_id', $values, $errors) ?> + + <?= $this->form->label(t('Github Id'), 'github_id') ?> + <?= $this->form->text('github_id', $values, $errors) ?> + + <?= $this->form->checkbox('is_ldap_user', t('Remote user'), 1, isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) ?> + <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'user', 'show', array('user_id' => $user['id'])) ?> + </div> + + <div class="alert alert-info"> + <ul> + <li><?= t('Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.') ?></li> + <li><?= t('If you check the box "Disallow login form", credentials entered in the login form will be ignored.') ?></li> + </ul> + </div> +</form>
\ No newline at end of file diff --git a/app/Template/user/new.php b/app/Template/user/create_local.php index 0db1e824..aeec300f 100644 --- a/app/Template/user/new.php +++ b/app/Template/user/create_local.php @@ -2,6 +2,7 @@ <div class="page-header"> <ul> <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'user', 'index') ?></li> + <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New remote user'), 'user', 'create', array('remote' => 1)) ?></li> </ul> </div> <section> diff --git a/app/Template/user/create_remote.php b/app/Template/user/create_remote.php new file mode 100644 index 00000000..52661585 --- /dev/null +++ b/app/Template/user/create_remote.php @@ -0,0 +1,57 @@ +<section id="main"> + <div class="page-header"> + <ul> + <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'user', 'index') ?></li> + <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New local user'), 'user', 'create') ?></li> + </ul> + </div> + <form method="post" action="<?= $this->url->href('user', 'save') ?>" autocomplete="off"> + + <?= $this->form->csrf() ?> + <?= $this->form->hidden('is_ldap_user', array('is_ldap_user' => 1)) ?> + + <div class="form-column"> + <?= $this->form->label(t('Username'), 'username') ?> + <?= $this->form->text('username', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?><br/> + + <?= $this->form->label(t('Name'), 'name') ?> + <?= $this->form->text('name', $values, $errors) ?><br/> + + <?= $this->form->label(t('Email'), 'email') ?> + <?= $this->form->email('email', $values, $errors) ?><br/> + + <?= $this->form->label(t('Google Id'), 'google_id') ?> + <?= $this->form->password('google_id', $values, $errors) ?><br/> + + <?= $this->form->label(t('Github Id'), 'github_id') ?> + <?= $this->form->password('github_id', $values, $errors) ?><br/> + </div> + + <div class="form-column"> + <?= $this->form->label(t('Add project member'), 'project_id') ?> + <?= $this->form->select('project_id', $projects, $values, $errors) ?><br/> + + <?= $this->form->label(t('Timezone'), 'timezone') ?> + <?= $this->form->select('timezone', $timezones, $values, $errors) ?><br/> + + <?= $this->form->label(t('Language'), 'language') ?> + <?= $this->form->select('language', $languages, $values, $errors) ?><br/> + + <?= $this->form->checkbox('notifications_enabled', t('Enable notifications'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1 ? true : false) ?> + <?= $this->form->checkbox('is_admin', t('Administrator'), 1, isset($values['is_admin']) && $values['is_admin'] == 1 ? true : false) ?> + <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?> + </div> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'user', 'index') ?> + </div> + </form> + <div class="alert alert-info"> + <ul> + <li><?= t('Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.') ?></li> + <li><?= t('If you check the box "Disallow login form", credentials entered in the login form will be ignored.') ?></li> + </ul> + </div> +</section>
\ No newline at end of file diff --git a/app/Template/user/edit.php b/app/Template/user/edit.php index 2462308f..ea7e3875 100644 --- a/app/Template/user/edit.php +++ b/app/Template/user/edit.php @@ -6,7 +6,6 @@ <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('is_ldap_user', $values) ?> <?= $this->form->label(t('Username'), 'username') ?> <?= $this->form->text('username', $values, $errors, array('required', $values['is_ldap_user'] == 1 ? 'readonly' : '', 'maxlength="50"')) ?><br/> @@ -23,13 +22,9 @@ <?= $this->form->label(t('Language'), 'language') ?> <?= $this->form->select('language', $languages, $values, $errors) ?><br/> - <div class="alert alert-error"> - <?= $this->form->checkbox('disable_login_form', t('Disable login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?><br/> - - <?php if ($this->user->isAdmin()): ?> - <?= $this->form->checkbox('is_admin', t('Administrator'), 1, isset($values['is_admin']) && $values['is_admin'] == 1) ?><br/> - <?php endif ?> - </div> + <?php if ($this->user->isAdmin()): ?> + <?= $this->form->checkbox('is_admin', t('Administrator'), 1, isset($values['is_admin']) && $values['is_admin'] == 1) ?><br/> + <?php endif ?> <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> diff --git a/app/Template/user/external.php b/app/Template/user/external.php index df85ace7..3b872e85 100644 --- a/app/Template/user/external.php +++ b/app/Template/user/external.php @@ -8,9 +8,9 @@ <p class="listing"> <?php if ($this->user->isCurrentUser($user['id'])): ?> <?php if (empty($user['google_id'])): ?> - <?= $this->url->link(t('Link my Google Account'), 'user', 'google', array(), true) ?> + <?= $this->url->link(t('Link my Google Account'), 'oauth', 'google', array(), true) ?> <?php else: ?> - <?= $this->url->link(t('Unlink my Google Account'), 'user', 'unlinkGoogle', array(), true) ?> + <?= $this->url->link(t('Unlink my Google Account'), 'oauth', 'unlink', array('backend' => 'google'), true) ?> <?php endif ?> <?php else: ?> <?= empty($user['google_id']) ? t('No account linked.') : t('Account linked.') ?> @@ -24,9 +24,9 @@ <p class="listing"> <?php if ($this->user->isCurrentUser($user['id'])): ?> <?php if (empty($user['github_id'])): ?> - <?= $this->url->link(t('Link my GitHub Account'), 'user', 'github', array(), true) ?> + <?= $this->url->link(t('Link my Github Account'), 'oauth', 'github', array(), true) ?> <?php else: ?> - <?= $this->url->link(t('Unlink my GitHub Account'), 'user', 'unlinkGitHub', array(), true) ?> + <?= $this->url->link(t('Unlink my Github Account'), 'oauth', 'unlink', array('backend' => 'github'), true) ?> <?php endif ?> <?php else: ?> <?= empty($user['github_id']) ? t('No account linked.') : t('Account linked.') ?> diff --git a/app/Template/user/index.php b/app/Template/user/index.php index fc575466..edf043a6 100644 --- a/app/Template/user/index.php +++ b/app/Template/user/index.php @@ -2,7 +2,8 @@ <div class="page-header"> <?php if ($this->user->isAdmin()): ?> <ul> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New user'), 'user', 'create') ?></li> + <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New local user'), 'user', 'create') ?></li> + <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New remote user'), 'user', 'create', array('remote' => 1)) ?></li> </ul> <?php endif ?> </div> diff --git a/app/Template/user/layout.php b/app/Template/user/layout.php index e60ab77d..a27f359b 100644 --- a/app/Template/user/layout.php +++ b/app/Template/user/layout.php @@ -3,7 +3,8 @@ <?php if ($this->user->isAdmin()): ?> <ul> <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'user', 'index') ?></li> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New user'), 'user', 'create') ?></li> + <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New local user'), 'user', 'create') ?></li> + <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New remote user'), 'user', 'create', array('remote' => 1)) ?></li> </ul> <?php endif ?> </div> diff --git a/app/Template/user/sidebar.php b/app/Template/user/sidebar.php index e61a43bf..213b3209 100644 --- a/app/Template/user/sidebar.php +++ b/app/Template/user/sidebar.php @@ -58,6 +58,9 @@ <?php if ($this->user->isAdmin()): ?> <li> + <?= $this->url->link(t('Edit Authentication'), 'user', 'authentication', array('user_id' => $user['id'])) ?> + </li> + <li> <?= $this->url->link(t('Hourly rates'), 'hourlyrate', 'index', array('user_id' => $user['id'])) ?> </li> <li> @@ -71,4 +74,6 @@ </li> <?php endif ?> </ul> + <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> + <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/common.php b/app/common.php index d1659018..7f66c05e 100644 --- a/app/common.php +++ b/app/common.php @@ -1,6 +1,6 @@ <?php -require 'vendor/autoload.php'; +require dirname(__DIR__) . '/vendor/autoload.php'; // Automatically parse environment configuration (Heroku) if (getenv('DATABASE_URL')) { @@ -12,7 +12,7 @@ if (getenv('DATABASE_URL')) { define('DB_PASSWORD', $dbopts["pass"]); define('DB_HOSTNAME', $dbopts["host"]); define('DB_PORT', isset($dbopts["port"]) ? $dbopts["port"] : null); - define('DB_NAME', ltrim($dbopts["path"],'/')); + define('DB_NAME', ltrim($dbopts["path"], '/')); } // Include custom config file @@ -28,3 +28,120 @@ $container->register(new ServiceProvider\LoggingProvider); $container->register(new ServiceProvider\DatabaseProvider); $container->register(new ServiceProvider\ClassProvider); $container->register(new ServiceProvider\EventDispatcherProvider); + +if (ENABLE_URL_REWRITE) { + + // Dashboard + $container['router']->addRoute('dashboard', 'app', 'index'); + $container['router']->addRoute('dashboard/:user_id', 'app', 'index', array('user_id')); + $container['router']->addRoute('dashboard/:user_id/projects', 'app', 'projects', array('user_id')); + $container['router']->addRoute('dashboard/:user_id/tasks', 'app', 'tasks', array('user_id')); + $container['router']->addRoute('dashboard/:user_id/subtasks', 'app', 'subtasks', array('user_id')); + $container['router']->addRoute('dashboard/:user_id/calendar', 'app', 'calendar', array('user_id')); + $container['router']->addRoute('dashboard/:user_id/activity', 'app', 'activity', array('user_id')); + + // Search routes + $container['router']->addRoute('search', 'search', 'index'); + $container['router']->addRoute('search/:search', 'search', 'index', array('search')); + + // Project routes + $container['router']->addRoute('projects', 'project', 'index'); + $container['router']->addRoute('project/create', 'project', 'create'); + $container['router']->addRoute('project/create/:private', 'project', 'create', array('private')); + $container['router']->addRoute('project/:project_id', 'project', 'show', array('project_id')); + $container['router']->addRoute('p/:project_id', 'project', 'show', array('project_id')); + $container['router']->addRoute('project/:project_id/share', 'project', 'share', array('project_id')); + $container['router']->addRoute('project/:project_id/edit', 'project', 'edit', array('project_id')); + $container['router']->addRoute('project/:project_id/integration', 'project', 'integration', array('project_id')); + $container['router']->addRoute('project/:project_id/users', 'project', 'users', array('project_id')); + $container['router']->addRoute('project/:project_id/duplicate', 'project', 'duplicate', array('project_id')); + $container['router']->addRoute('project/:project_id/remove', 'project', 'remove', array('project_id')); + $container['router']->addRoute('project/:project_id/disable', 'project', 'disable', array('project_id')); + $container['router']->addRoute('project/:project_id/enable', 'project', 'enable', array('project_id')); + + // Action routes + $container['router']->addRoute('project/:project_id/actions', 'action', 'index', array('project_id')); + $container['router']->addRoute('project/:project_id/action/:action_id/confirm', 'action', 'confirm', array('project_id', 'action_id')); + + // Column routes + $container['router']->addRoute('project/:project_id/columns', 'column', 'index', array('project_id')); + $container['router']->addRoute('project/:project_id/column/:column_id/edit', 'column', 'edit', array('project_id', 'column_id')); + $container['router']->addRoute('project/:project_id/column/:column_id/confirm', 'column', 'confirm', array('project_id', 'column_id')); + $container['router']->addRoute('project/:project_id/column/:column_id/move/:direction', 'column', 'move', array('project_id', 'column_id', 'direction')); + + // Swimlane routes + $container['router']->addRoute('project/:project_id/swimlanes', 'swimlane', 'index', array('project_id')); + $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/edit', 'swimlane', 'edit', array('project_id', 'swimlane_id')); + $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/confirm', 'swimlane', 'confirm', array('project_id', 'swimlane_id')); + $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/disable', 'swimlane', 'disable', array('project_id', 'swimlane_id')); + $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/enable', 'swimlane', 'enable', array('project_id', 'swimlane_id')); + $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/up', 'swimlane', 'moveup', array('project_id', 'swimlane_id')); + $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/down', 'swimlane', 'movedown', array('project_id', 'swimlane_id')); + + // Category routes + $container['router']->addRoute('project/:project_id/categories', 'category', 'index', array('project_id')); + $container['router']->addRoute('project/:project_id/category/:category_id/edit', 'category', 'edit', array('project_id', 'category_id')); + $container['router']->addRoute('project/:project_id/category/:category_id/confirm', 'category', 'confirm', array('project_id', 'category_id')); + + // Task routes + $container['router']->addRoute('project/:project_id/task/:task_id', 'task', 'show', array('project_id', 'task_id')); + $container['router']->addRoute('t/:task_id', 'task', 'show', array('task_id')); + $container['router']->addRoute('public/task/:task_id/:token', 'task', 'readonly', array('task_id', 'token')); + + $container['router']->addRoute('project/:project_id/task/:task_id/activity', 'activity', 'task', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/screenshot', 'file', 'screenshot', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/upload', 'file', 'create', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/comment', 'comment', 'create', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/link', 'tasklink', 'create', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/transitions', 'task', 'transitions', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/analytics', 'task', 'analytics', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/remove', 'task', 'remove', array('project_id', 'task_id')); + + $container['router']->addRoute('project/:project_id/task/:task_id/edit', 'taskmodification', 'edit', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/description', 'taskmodification', 'description', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/recurrence', 'taskmodification', 'recurrence', array('project_id', 'task_id')); + + $container['router']->addRoute('project/:project_id/task/:task_id/close', 'taskstatus', 'close', array('task_id', 'project_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/open', 'taskstatus', 'open', array('task_id', 'project_id')); + + $container['router']->addRoute('project/:project_id/task/:task_id/duplicate', 'taskduplication', 'duplicate', array('task_id', 'project_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/copy', 'taskduplication', 'copy', array('task_id', 'project_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/copy/:dst_project_id', 'taskduplication', 'copy', array('task_id', 'project_id', 'dst_project_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/move', 'taskduplication', 'move', array('task_id', 'project_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/move/:dst_project_id', 'taskduplication', 'move', array('task_id', 'project_id', 'dst_project_id')); + + // Board routes + $container['router']->addRoute('board/:project_id', 'board', 'show', array('project_id')); + $container['router']->addRoute('b/:project_id', 'board', 'show', array('project_id')); + $container['router']->addRoute('board/:project_id/filter/:search', 'board', 'show', array('project_id', 'search')); + $container['router']->addRoute('public/board/:token', 'board', 'readonly', array('token')); + + // Calendar routes + $container['router']->addRoute('calendar/:project_id', 'calendar', 'show', array('project_id')); + $container['router']->addRoute('c/:project_id', 'calendar', 'show', array('project_id')); + $container['router']->addRoute('calendar/:project_id/:search', 'calendar', 'show', array('project_id', 'search')); + + // Listing routes + $container['router']->addRoute('list/:project_id', 'listing', 'show', array('project_id')); + $container['router']->addRoute('l/:project_id', 'listing', 'show', array('project_id')); + $container['router']->addRoute('list/:project_id/:search', 'listing', 'show', array('project_id', 'search')); + + // Subtask routes + $container['router']->addRoute('project/:project_id/task/:task_id/subtask/create', 'subtask', 'create', array('project_id', 'task_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/subtask/:subtask_id/remove', 'subtask', 'confirm', array('project_id', 'task_id', 'subtask_id')); + $container['router']->addRoute('project/:project_id/task/:task_id/subtask/:subtask_id/edit', 'subtask', 'edit', array('project_id', 'task_id', 'subtask_id')); + + // Feed routes + $container['router']->addRoute('feed/project/:token', 'feed', 'project', array('token')); + $container['router']->addRoute('feed/user/:token', 'feed', 'user', array('token')); + + // Ical routes + $container['router']->addRoute('ical/project/:token', 'ical', 'project', array('token')); + $container['router']->addRoute('ical/user/:token', 'ical', 'user', array('token')); + + // Auth routes + $container['router']->addRoute('oauth/google', 'oauth', 'google'); + $container['router']->addRoute('oauth/github', 'oauth', 'github'); + $container['router']->addRoute('login', 'auth', 'login'); + $container['router']->addRoute('logout', 'auth', 'logout'); +} diff --git a/app/constants.php b/app/constants.php index 9b66b746..83fba468 100644 --- a/app/constants.php +++ b/app/constants.php @@ -7,9 +7,6 @@ defined('DEBUG_FILE') or define('DEBUG_FILE', __DIR__.'/../data/debug.log'); // Application version defined('APP_VERSION') or define('APP_VERSION', 'master'); -// Base directory -define('BASE_URL_DIRECTORY', dirname($_SERVER['PHP_SELF'])); - // Database driver: sqlite, mysql or postgres defined('DB_DRIVER') or define('DB_DRIVER', 'sqlite'); @@ -38,13 +35,14 @@ defined('LDAP_ACCOUNT_FULLNAME') or define('LDAP_ACCOUNT_FULLNAME', 'displayname defined('LDAP_ACCOUNT_EMAIL') or define('LDAP_ACCOUNT_EMAIL', 'mail'); defined('LDAP_ACCOUNT_ID') or define('LDAP_ACCOUNT_ID', ''); defined('LDAP_USERNAME_CASE_SENSITIVE') or define('LDAP_USERNAME_CASE_SENSITIVE', false); +defined('LDAP_ACCOUNT_CREATION') or define('LDAP_ACCOUNT_CREATION', true); // Google authentication defined('GOOGLE_AUTH') or define('GOOGLE_AUTH', false); defined('GOOGLE_CLIENT_ID') or define('GOOGLE_CLIENT_ID', ''); defined('GOOGLE_CLIENT_SECRET') or define('GOOGLE_CLIENT_SECRET', ''); -// GitHub authentication +// Github authentication defined('GITHUB_AUTH') or define('GITHUB_AUTH', false); defined('GITHUB_CLIENT_ID') or define('GITHUB_CLIENT_ID', ''); defined('GITHUB_CLIENT_SECRET') or define('GITHUB_CLIENT_SECRET', ''); @@ -84,3 +82,9 @@ defined('MARKDOWN_ESCAPE_HTML') or define('MARKDOWN_ESCAPE_HTML', true); // API alternative authentication header, the default is HTTP Basic Authentication defined in RFC2617 defined('API_AUTHENTICATION_HEADER') or define('API_AUTHENTICATION_HEADER', ''); + +// Enable/disable url rewrite +defined('ENABLE_URL_REWRITE') or define('ENABLE_URL_REWRITE', isset($_SERVER['HTTP_MOD_REWRITE'])); + +// Hide login form +defined('HIDE_LOGIN_FORM') or define('HIDE_LOGIN_FORM', false); |