summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Action/CommentCreation.php1
-rw-r--r--app/Api/Project.php30
-rw-r--r--app/Api/Task.php26
-rw-r--r--app/Auth/GitHub.php163
-rw-r--r--app/Auth/Github.php122
-rw-r--r--app/Auth/Google.php85
-rw-r--r--app/Auth/Ldap.php2
-rw-r--r--app/Auth/RememberMe.php38
-rw-r--r--app/Console/Base.php21
-rw-r--r--app/Console/ProjectDailyColumnStatsExport.php (renamed from app/Console/ProjectDailySummaryExport.php)8
-rw-r--r--app/Console/ProjectDailyStatsCalculation.php (renamed from app/Console/ProjectDailySummaryCalculation.php)9
-rw-r--r--app/Controller/Action.php10
-rw-r--r--app/Controller/Activity.php45
-rw-r--r--app/Controller/Analytic.php58
-rw-r--r--app/Controller/Auth.php8
-rw-r--r--app/Controller/Base.php48
-rw-r--r--app/Controller/Board.php111
-rw-r--r--app/Controller/Calendar.php23
-rw-r--r--app/Controller/Category.php8
-rw-r--r--app/Controller/Comment.php8
-rw-r--r--app/Controller/Config.php7
-rw-r--r--app/Controller/Export.php2
-rw-r--r--app/Controller/Ical.php4
-rw-r--r--app/Controller/Listing.php37
-rw-r--r--app/Controller/Oauth.php123
-rw-r--r--app/Controller/Project.php25
-rw-r--r--app/Controller/Projectinfo.php95
-rw-r--r--app/Controller/Search.php2
-rw-r--r--app/Controller/Subtask.php12
-rw-r--r--app/Controller/Swimlane.php18
-rw-r--r--app/Controller/Task.php494
-rw-r--r--app/Controller/Taskcreation.php86
-rw-r--r--app/Controller/Taskduplication.php143
-rw-r--r--app/Controller/Taskmodification.php212
-rw-r--r--app/Controller/Taskstatus.php79
-rw-r--r--app/Controller/User.php168
-rw-r--r--app/Core/Base.php3
-rw-r--r--app/Core/Helper.php34
-rw-r--r--app/Core/HttpClient.php21
-rw-r--r--app/Core/Lexer.php11
-rw-r--r--app/Core/OAuth2.php120
-rw-r--r--app/Core/Request.php11
-rw-r--r--app/Core/Router.php195
-rw-r--r--app/Core/Session.php2
-rw-r--r--app/Helper/Asset.php4
-rw-r--r--app/Helper/Board.php24
-rw-r--r--app/Helper/Dt.php (renamed from app/Helper/Datetime.php)22
-rw-r--r--app/Helper/Url.php98
-rw-r--r--app/Helper/User.php18
-rw-r--r--app/Integration/GitlabWebhook.php67
-rw-r--r--app/Integration/HipchatWebhook.php3
-rw-r--r--app/Integration/Jabber.php3
-rw-r--r--app/Integration/SlackWebhook.php27
-rw-r--r--app/Locale/da_DK/translations.php125
-rw-r--r--app/Locale/de_DE/translations.php325
-rw-r--r--app/Locale/es_ES/translations.php241
-rw-r--r--app/Locale/fi_FI/translations.php121
-rw-r--r--app/Locale/fr_FR/translations.php123
-rw-r--r--app/Locale/hu_HU/translations.php135
-rw-r--r--app/Locale/it_IT/translations.php121
-rw-r--r--app/Locale/ja_JP/translations.php125
-rw-r--r--app/Locale/nl_NL/translations.php121
-rw-r--r--app/Locale/pl_PL/translations.php121
-rw-r--r--app/Locale/pt_BR/translations.php295
-rw-r--r--app/Locale/ru_RU/translations.php141
-rw-r--r--app/Locale/sr_Latn_RS/translations.php121
-rw-r--r--app/Locale/sv_SE/translations.php433
-rw-r--r--app/Locale/th_TH/translations.php121
-rw-r--r--app/Locale/tr_TR/translations.php121
-rw-r--r--app/Locale/zh_CN/translations.php121
-rw-r--r--app/Model/Acl.php11
-rw-r--r--app/Model/Action.php1
-rw-r--r--app/Model/Authentication.php5
-rw-r--r--app/Model/Board.php11
-rw-r--r--app/Model/Color.php2
-rw-r--r--app/Model/DateParser.php51
-rw-r--r--app/Model/Notification.php4
-rw-r--r--app/Model/ProjectAnalytic.php94
-rw-r--r--app/Model/ProjectDailyColumnStats.php (renamed from app/Model/ProjectDailySummary.php)42
-rw-r--r--app/Model/ProjectDailyStats.php72
-rw-r--r--app/Model/TaskAnalytic.php71
-rw-r--r--app/Model/TaskCreation.php9
-rwxr-xr-xapp/Model/TaskDuplication.php41
-rw-r--r--app/Model/TaskFilter.php219
-rw-r--r--app/Model/TaskFinder.php22
-rw-r--r--app/Model/TaskLink.php1
-rw-r--r--app/Model/TaskModification.php3
-rw-r--r--app/Model/TaskPosition.php228
-rw-r--r--app/Model/TaskValidator.php2
-rw-r--r--app/Model/Transition.php16
-rw-r--r--app/Model/User.php14
-rw-r--r--app/Model/UserSession.php48
-rw-r--r--app/Schema/Mysql.php32
-rw-r--r--app/Schema/Postgres.php31
-rw-r--r--app/Schema/Sqlite.php31
-rw-r--r--app/ServiceProvider/ClassProvider.php10
-rw-r--r--app/Subscriber/ProjectDailySummarySubscriber.php3
-rw-r--r--app/Template/activity/project.php (renamed from app/Template/projectinfo/activity.php)12
-rw-r--r--app/Template/activity/task.php (renamed from app/Template/task/activity.php)0
-rw-r--r--app/Template/analytic/avg_time_columns.php29
-rw-r--r--app/Template/analytic/layout.php12
-rw-r--r--app/Template/analytic/lead_cycle_time.php42
-rw-r--r--app/Template/analytic/sidebar.php8
-rw-r--r--app/Template/app/calendar.php5
-rw-r--r--app/Template/app/filters_helper.php21
-rw-r--r--app/Template/app/overview.php10
-rw-r--r--app/Template/app/projects.php2
-rw-r--r--app/Template/app/sidebar.php2
-rw-r--r--app/Template/auth/index.php24
-rw-r--r--app/Template/board/filters.php43
-rw-r--r--app/Template/board/popover_assignee.php (renamed from app/Template/board/assignee.php)0
-rw-r--r--app/Template/board/popover_category.php (renamed from app/Template/board/category.php)3
-rw-r--r--app/Template/board/private_view.php (renamed from app/Template/board/index.php)8
-rw-r--r--app/Template/board/public_view.php (renamed from app/Template/board/public.php)2
-rw-r--r--app/Template/board/table_container.php (renamed from app/Template/board/show.php)4
-rw-r--r--app/Template/board/table_swimlane.php (renamed from app/Template/board/swimlane.php)4
-rw-r--r--app/Template/board/task_footer.php5
-rw-r--r--app/Template/board/task_menu.php7
-rw-r--r--app/Template/board/task_private.php82
-rw-r--r--app/Template/board/tooltip_comments.php (renamed from app/Template/board/comments.php)0
-rw-r--r--app/Template/board/tooltip_description.php (renamed from app/Template/board/description.php)0
-rw-r--r--app/Template/board/tooltip_files.php (renamed from app/Template/board/files.php)0
-rw-r--r--app/Template/board/tooltip_subtasks.php (renamed from app/Template/board/subtasks.php)0
-rw-r--r--app/Template/board/tooltip_tasklinks.php (renamed from app/Template/board/tasklinks.php)0
-rw-r--r--app/Template/budget/sidebar.php2
-rw-r--r--app/Template/calendar/show.php53
-rw-r--r--app/Template/calendar/sidebar.php40
-rw-r--r--app/Template/column/index.php2
-rw-r--r--app/Template/config/about.php7
-rw-r--r--app/Template/config/integrations.php32
-rw-r--r--app/Template/config/project.php3
-rw-r--r--app/Template/config/sidebar.php2
-rw-r--r--app/Template/config/webhook.php2
-rw-r--r--app/Template/export/sidebar.php2
-rw-r--r--app/Template/feed/project.php6
-rw-r--r--app/Template/feed/user.php6
-rw-r--r--app/Template/file/screenshot.php2
-rw-r--r--app/Template/file/show.php4
-rw-r--r--app/Template/layout.php16
-rw-r--r--app/Template/listing/show.php61
-rw-r--r--app/Template/notification/footer.php4
-rw-r--r--app/Template/notification/task_overdue.php2
-rw-r--r--app/Template/project/dropdown.php14
-rw-r--r--app/Template/project/filters.php51
-rw-r--r--app/Template/project/index.php4
-rw-r--r--app/Template/project/integrations.php14
-rw-r--r--app/Template/project/layout.php6
-rw-r--r--app/Template/project/show.php2
-rw-r--r--app/Template/project/sidebar.php78
-rw-r--r--app/Template/projectinfo/search.php43
-rw-r--r--app/Template/projectinfo/tasks.php33
-rw-r--r--app/Template/search/index.php17
-rw-r--r--app/Template/search/results.php20
-rw-r--r--app/Template/subtask/show.php10
-rw-r--r--app/Template/task/analytics.php36
-rw-r--r--app/Template/task/description.php (renamed from app/Template/task/show_description.php)0
-rw-r--r--app/Template/task/duplicate_project.php24
-rw-r--r--app/Template/task/layout.php12
-rw-r--r--app/Template/task/move_project.php24
-rw-r--r--app/Template/task/public.php2
-rw-r--r--app/Template/task/show.php8
-rw-r--r--app/Template/task/sidebar.php25
-rw-r--r--app/Template/task/table.php56
-rw-r--r--app/Template/task/time_tracking_details.php (renamed from app/Template/task/time_tracking.php)2
-rw-r--r--app/Template/task/time_tracking_summary.php (renamed from app/Template/task/timesheet.php)0
-rw-r--r--app/Template/task/transitions.php2
-rw-r--r--app/Template/task_creation/form.php (renamed from app/Template/task/new.php)4
-rw-r--r--app/Template/task_duplication/copy.php43
-rw-r--r--app/Template/task_duplication/duplicate.php (renamed from app/Template/task/duplicate.php)2
-rw-r--r--app/Template/task_duplication/move.php43
-rw-r--r--app/Template/task_modification/edit_description.php (renamed from app/Template/task/edit_description.php)4
-rw-r--r--app/Template/task_modification/edit_recurrence.php (renamed from app/Template/task/edit_recurrence.php)9
-rw-r--r--app/Template/task_modification/edit_task.php (renamed from app/Template/task/edit.php)4
-rw-r--r--app/Template/task_modification/edit_time.php (renamed from app/Template/task/time.php)9
-rw-r--r--app/Template/task_status/close.php (renamed from app/Template/task/close.php)2
-rw-r--r--app/Template/task_status/open.php (renamed from app/Template/task/open.php)2
-rw-r--r--app/Template/timetable_day/index.php4
-rw-r--r--app/Template/timetable_extra/index.php4
-rw-r--r--app/Template/timetable_off/index.php4
-rw-r--r--app/Template/timetable_week/index.php8
-rw-r--r--app/Template/user/authentication.php32
-rw-r--r--app/Template/user/create_local.php (renamed from app/Template/user/new.php)1
-rw-r--r--app/Template/user/create_remote.php57
-rw-r--r--app/Template/user/edit.php11
-rw-r--r--app/Template/user/external.php8
-rw-r--r--app/Template/user/index.php3
-rw-r--r--app/Template/user/layout.php3
-rw-r--r--app/Template/user/sidebar.php5
-rw-r--r--app/common.php121
-rw-r--r--app/constants.php12
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').' &gt; '.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'].' &gt; '.$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'].' &gt; '.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'].' &gt; '.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, '', '&amp;').(empty($anchor) ? '' : '#'.$anchor);
+ return $this->build('&amp;', $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('&quot;', '"', $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>&nbsp;<?= 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"/>&nbsp;<?= 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"/>&nbsp;<?= 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"/>&nbsp;<?= t('Sendgrid (incoming emails)') ?></h3>
+ <h3><img src="<?= $this->url->dir() ?>assets/img/sendgrid-icon.png"/>&nbsp;<?= 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"/>&nbsp;<?= t('Postmark (incoming emails)') ?></h3>
+ <h3><img src="<?= $this->url->dir() ?>assets/img/postmark-icon.png"/>&nbsp;<?= 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"/>&nbsp;<?= t('Gravatar') ?></h3>
+ <h3><img src="<?= $this->url->dir() ?>assets/img/gravatar-icon.png"/>&nbsp;<?= 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')) ?>&nbsp;
+ <?= $this->url->link('<i class="fa fa-th"></i>', 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Board')) ?>&nbsp;
<?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>&nbsp;<?= 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"/>&nbsp;<?= t('Gitlab webhooks') ?></h3>
+ <h3><img src="<?= $this->url->dir() ?>assets/img/gitlab-icon.png"/>&nbsp;<?= 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>&nbsp;<?= 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);