mirror of
https://github.com/YunoHost-Apps/kanboard_ynh.git
synced 2024-09-03 19:36:17 +02:00
Update kanboard v1.0.12
This commit is contained in:
parent
5188fc0571
commit
808c098171
392 changed files with 10259 additions and 5585 deletions
|
@ -4,6 +4,7 @@ namespace Action;
|
|||
|
||||
use Integration\GitlabWebhook;
|
||||
use Integration\GithubWebhook;
|
||||
use Integration\BitbucketWebhook;
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
|
@ -28,6 +29,7 @@ class TaskClose extends Base
|
|||
GithubWebhook::EVENT_ISSUE_CLOSED,
|
||||
GitlabWebhook::EVENT_COMMIT,
|
||||
GitlabWebhook::EVENT_ISSUE_CLOSED,
|
||||
BitbucketWebhook::EVENT_COMMIT,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -44,6 +46,7 @@ class TaskClose extends Base
|
|||
case GithubWebhook::EVENT_ISSUE_CLOSED:
|
||||
case GitlabWebhook::EVENT_COMMIT:
|
||||
case GitlabWebhook::EVENT_ISSUE_CLOSED:
|
||||
case BitbucketWebhook::EVENT_COMMIT:
|
||||
return array();
|
||||
default:
|
||||
return array('column_id' => t('Column'));
|
||||
|
@ -63,6 +66,7 @@ class TaskClose extends Base
|
|||
case GithubWebhook::EVENT_ISSUE_CLOSED:
|
||||
case GitlabWebhook::EVENT_COMMIT:
|
||||
case GitlabWebhook::EVENT_ISSUE_CLOSED:
|
||||
case BitbucketWebhook::EVENT_COMMIT:
|
||||
return array('task_id');
|
||||
default:
|
||||
return array('task_id', 'column_id');
|
||||
|
@ -95,6 +99,7 @@ class TaskClose extends Base
|
|||
case GithubWebhook::EVENT_ISSUE_CLOSED:
|
||||
case GitlabWebhook::EVENT_COMMIT:
|
||||
case GitlabWebhook::EVENT_ISSUE_CLOSED:
|
||||
case BitbucketWebhook::EVENT_COMMIT:
|
||||
return true;
|
||||
default:
|
||||
return $data['column_id'] == $this->getParam('column_id');
|
||||
|
|
84
sources/app/Action/TaskLogMoveAnotherColumn.php
Normal file
84
sources/app/Action/TaskLogMoveAnotherColumn.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Model\GithubWebhook;
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Add a log of the triggering event to the task description.
|
||||
*
|
||||
* @package action
|
||||
* @author Oren Ben-Kiki
|
||||
*/
|
||||
class TaskLogMoveAnotherColumn extends Base
|
||||
{
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
Task::EVENT_MOVE_COLUMN,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array('column_id' => t('Column'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array('task_id', 'column_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (append to the task description).
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
if (! $this->userSession->isLogged()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$column = $this->board->getColumn($data['column_id']);
|
||||
|
||||
return (bool) $this->comment->create(array(
|
||||
'comment' => t('Moved to column %s', $column['title']),
|
||||
'task_id' => $data['task_id'],
|
||||
'user_id' => $this->userSession->getId(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the event data meet the action condition
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRequiredCondition(array $data)
|
||||
{
|
||||
return $data['column_id'] == $this->getParam('column_id');
|
||||
}
|
||||
}
|
|
@ -30,9 +30,14 @@ class Database extends Base
|
|||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
$user = $this->db->table(User::TABLE)->eq('username', $username)->eq('is_ldap_user', 0)->findOne();
|
||||
$user = $this->db
|
||||
->table(User::TABLE)
|
||||
->eq('username', $username)
|
||||
->eq('disable_login_form', 0)
|
||||
->eq('is_ldap_user', 0)
|
||||
->findOne();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
if (is_array($user) && password_verify($password, $user['password'])) {
|
||||
$this->userSession->refresh($user);
|
||||
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
|
||||
return true;
|
||||
|
|
|
@ -29,6 +29,7 @@ class Ldap extends Base
|
|||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
$username = LDAP_USERNAME_CASE_SENSITIVE ? $username : strtolower($username);
|
||||
$result = $this->findUser($username, $password);
|
||||
|
||||
if (is_array($result)) {
|
||||
|
@ -199,11 +200,90 @@ class Ldap extends Base
|
|||
|
||||
return array(
|
||||
'username' => $username,
|
||||
'name' => isset($info[0][LDAP_ACCOUNT_FULLNAME][0]) ? $info[0][LDAP_ACCOUNT_FULLNAME][0] : '',
|
||||
'email' => isset($info[0][LDAP_ACCOUNT_EMAIL][0]) ? $info[0][LDAP_ACCOUNT_EMAIL][0] : '',
|
||||
'name' => $this->getFromInfo($info, LDAP_ACCOUNT_FULLNAME),
|
||||
'email' => $this->getFromInfo($info, LDAP_ACCOUNT_EMAIL),
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve info on LDAP user
|
||||
*
|
||||
* @param string $username Username
|
||||
* @param string $email Email address
|
||||
*/
|
||||
public function lookup($username = null, $email = null)
|
||||
{
|
||||
$query = $this->getQuery($username, $email);
|
||||
if ($query === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect and attempt anonymous bind
|
||||
$ldap = $this->connect();
|
||||
if (! is_resource($ldap) || ! $this->bind($ldap, null, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to find user
|
||||
$sr = @ldap_search($ldap, LDAP_ACCOUNT_BASE, $query, array(LDAP_ACCOUNT_FULLNAME, LDAP_ACCOUNT_EMAIL, LDAP_ACCOUNT_ID));
|
||||
if ($sr === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$info = ldap_get_entries($ldap, $sr);
|
||||
|
||||
// User not found
|
||||
if (count($info) == 0 || $info['count'] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// User id not retrieved: LDAP_ACCOUNT_ID not properly configured
|
||||
if (! $username && ! isset($info[0][LDAP_ACCOUNT_ID][0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array(
|
||||
'username' => $this->getFromInfo($info, LDAP_ACCOUNT_ID, $username),
|
||||
'name' => $this->getFromInfo($info, LDAP_ACCOUNT_FULLNAME),
|
||||
'email' => $this->getFromInfo($info, LDAP_ACCOUNT_EMAIL, $email),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LDAP query to find a user
|
||||
*
|
||||
* @param string $username Username
|
||||
* @param string $email Email address
|
||||
*/
|
||||
private function getQuery($username, $email)
|
||||
{
|
||||
if ($username && $email) {
|
||||
return '(&('.sprintf(LDAP_USER_PATTERN, $username).')('.sprintf(LDAP_ACCOUNT_EMAIL, $email).')';
|
||||
}
|
||||
else if ($username) {
|
||||
return sprintf(LDAP_USER_PATTERN, $username);
|
||||
}
|
||||
else if ($email) {
|
||||
return '('.LDAP_ACCOUNT_EMAIL.'='.$email.')';
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value from the LDAP info
|
||||
*
|
||||
* @param array $info LDAP info
|
||||
* @param string $key Key
|
||||
* @param string $default Default value if key not set in entry
|
||||
* @return string
|
||||
*/
|
||||
private function getFromInfo($info, $key, $default = '')
|
||||
{
|
||||
return isset($info[0][$key][0]) ? $info[0][$key][0] : $default;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ class ReverseProxy extends Base
|
|||
'username' => $login,
|
||||
'is_admin' => REVERSE_PROXY_DEFAULT_ADMIN === $login,
|
||||
'is_ldap_user' => 1,
|
||||
'disable_login_form' => 1,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
namespace Controller;
|
||||
|
||||
use Model\SubTask as SubTaskModel;
|
||||
use Model\Subtask as SubtaskModel;
|
||||
use Model\Task as TaskModel;
|
||||
|
||||
/**
|
||||
* Application controller
|
||||
|
@ -22,164 +23,62 @@ class App extends Base
|
|||
$this->response->text('OK');
|
||||
}
|
||||
|
||||
/**
|
||||
* User dashboard view for admins
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function dashboard()
|
||||
{
|
||||
$this->index($this->request->getIntegerParam('user_id'), 'dashboard');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard for the current user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
public function index($user_id = 0, $action = 'index')
|
||||
{
|
||||
$paginate = $this->request->getStringParam('paginate', 'userTasks');
|
||||
$offset = $this->request->getIntegerParam('offset', 0);
|
||||
$direction = $this->request->getStringParam('direction');
|
||||
$order = $this->request->getStringParam('order');
|
||||
|
||||
$user_id = $this->userSession->getId();
|
||||
$projects = $this->projectPermission->getMemberProjects($user_id);
|
||||
$status = array(SubTaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS);
|
||||
$user_id = $user_id ?: $this->userSession->getId();
|
||||
$projects = $this->projectPermission->getActiveMemberProjects($user_id);
|
||||
$project_ids = array_keys($projects);
|
||||
|
||||
$params = array(
|
||||
$task_paginator = $this->paginator
|
||||
->setUrl('app', $action, array('pagination' => 'tasks'))
|
||||
->setMax(10)
|
||||
->setOrder('tasks.id')
|
||||
->setQuery($this->taskFinder->getUserQuery($user_id))
|
||||
->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks');
|
||||
|
||||
$subtask_paginator = $this->paginator
|
||||
->setUrl('app', $action, array('pagination' => 'subtasks'))
|
||||
->setMax(10)
|
||||
->setOrder('tasks.id')
|
||||
->setQuery($this->subtask->getUserQuery($user_id, $status))
|
||||
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
|
||||
|
||||
$project_paginator = $this->paginator
|
||||
->setUrl('app', $action, array('pagination' => 'projects'))
|
||||
->setMax(10)
|
||||
->setOrder('name')
|
||||
->setQuery($this->project->getQueryColumnStats($project_ids))
|
||||
->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
|
||||
|
||||
$this->response->html($this->template->layout('app/dashboard', array(
|
||||
'title' => t('Dashboard'),
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($user_id),
|
||||
'events' => $this->projectActivity->getProjects($project_ids, 10),
|
||||
);
|
||||
|
||||
$params += $this->getTaskPagination($user_id, $paginate, $offset, $order, $direction);
|
||||
$params += $this->getSubtaskPagination($user_id, $paginate, $offset, $order, $direction);
|
||||
$params += $this->getProjectPagination($project_ids, $paginate, $offset, $order, $direction);
|
||||
|
||||
$this->response->html($this->template->layout('app/dashboard', $params));
|
||||
'events' => $this->projectActivity->getProjects($project_ids, 5),
|
||||
'task_paginator' => $task_paginator,
|
||||
'subtask_paginator' => $subtask_paginator,
|
||||
'project_paginator' => $project_paginator,
|
||||
'user_id' => $user_id,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tasks pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param string $paginate
|
||||
* @param integer $offset
|
||||
* @param string $order
|
||||
* @param string $direction
|
||||
*/
|
||||
private function getTaskPagination($user_id, $paginate, $offset, $order, $direction)
|
||||
{
|
||||
$limit = 10;
|
||||
|
||||
if (! in_array($order, array('tasks.id', 'project_name', 'title', 'date_due'))) {
|
||||
$order = 'tasks.id';
|
||||
$direction = 'ASC';
|
||||
}
|
||||
|
||||
if ($paginate === 'userTasks') {
|
||||
$tasks = $this->taskPaginator->userTasks($user_id, $offset, $limit, $order, $direction);
|
||||
}
|
||||
else {
|
||||
$offset = 0;
|
||||
$tasks = $this->taskPaginator->userTasks($user_id, $offset, $limit);
|
||||
}
|
||||
|
||||
return array(
|
||||
'tasks' => $tasks,
|
||||
'task_pagination' => array(
|
||||
'controller' => 'app',
|
||||
'action' => 'index',
|
||||
'params' => array('paginate' => 'userTasks'),
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => $this->taskPaginator->countUserTasks($user_id),
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subtasks pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param string $paginate
|
||||
* @param integer $offset
|
||||
* @param string $order
|
||||
* @param string $direction
|
||||
*/
|
||||
private function getSubtaskPagination($user_id, $paginate, $offset, $order, $direction)
|
||||
{
|
||||
$status = array(SubTaskModel::STATUS_TODO, SubTaskModel::STATUS_INPROGRESS);
|
||||
$limit = 10;
|
||||
|
||||
if (! in_array($order, array('tasks.id', 'project_name', 'status', 'title'))) {
|
||||
$order = 'tasks.id';
|
||||
$direction = 'ASC';
|
||||
}
|
||||
|
||||
if ($paginate === 'userSubtasks') {
|
||||
$subtasks = $this->subtaskPaginator->userSubtasks($user_id, $status, $offset, $limit, $order, $direction);
|
||||
}
|
||||
else {
|
||||
$offset = 0;
|
||||
$subtasks = $this->subtaskPaginator->userSubtasks($user_id, $status, $offset, $limit);
|
||||
}
|
||||
|
||||
return array(
|
||||
'subtasks' => $subtasks,
|
||||
'subtask_pagination' => array(
|
||||
'controller' => 'app',
|
||||
'action' => 'index',
|
||||
'params' => array('paginate' => 'userSubtasks'),
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => $this->subtaskPaginator->countUserSubtasks($user_id, $status),
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get projects pagination
|
||||
*
|
||||
* @access public
|
||||
* @param array $project_ids
|
||||
* @param string $paginate
|
||||
* @param integer $offset
|
||||
* @param string $order
|
||||
* @param string $direction
|
||||
*/
|
||||
private function getProjectPagination(array $project_ids, $paginate, $offset, $order, $direction)
|
||||
{
|
||||
$limit = 10;
|
||||
|
||||
if (! in_array($order, array('id', 'name'))) {
|
||||
$order = 'name';
|
||||
$direction = 'ASC';
|
||||
}
|
||||
|
||||
if ($paginate === 'projectSummaries') {
|
||||
$projects = $this->projectPaginator->projectSummaries($project_ids, $offset, $limit, $order, $direction);
|
||||
}
|
||||
else {
|
||||
$offset = 0;
|
||||
$projects = $this->projectPaginator->projectSummaries($project_ids, $offset, $limit);
|
||||
}
|
||||
|
||||
return array(
|
||||
'projects' => $projects,
|
||||
'project_pagination' => array(
|
||||
'controller' => 'app',
|
||||
'action' => 'index',
|
||||
'params' => array('paginate' => 'projectSummaries'),
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => count($project_ids),
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render Markdown Text and reply with the HTML Code
|
||||
* Render Markdown text and reply with the HTML Code
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
|
@ -190,10 +89,34 @@ class App extends Base
|
|||
if (empty($payload['text'])) {
|
||||
$this->response->html('<p>'.t('Nothing to preview...').'</p>');
|
||||
}
|
||||
else {
|
||||
$this->response->html(
|
||||
$this->template->markdown($payload['text'])
|
||||
);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->markdown($payload['text']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Colors stylesheet
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function colors()
|
||||
{
|
||||
$this->response->css($this->color->getCss());
|
||||
}
|
||||
|
||||
/**
|
||||
* Task autocompletion (Ajax)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function autocomplete()
|
||||
{
|
||||
$this->response->json(
|
||||
$this->taskFilter
|
||||
->create()
|
||||
->filterByProjects($this->projectPermission->getActiveMemberProjectIds($this->userSession->getId()))
|
||||
->excludeTasks(array($this->request->getIntegerParam('exclude_task_id')))
|
||||
->filterByTitle($this->request->getStringParam('term'))
|
||||
->toAutoCompletion()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,13 @@ use Symfony\Component\EventDispatcher\Event;
|
|||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Core\Helper $helper
|
||||
* @property \Core\Session $session
|
||||
* @property \Core\Template $template
|
||||
* @property \Core\Paginator $paginator
|
||||
* @property \Integration\GithubWebhook $githubWebhook
|
||||
* @property \Integration\GitlabWebhook $gitlabWebhook
|
||||
* @property \Integration\BitbucketWebhook $bitbucketWebhook
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Authentication $authentication
|
||||
* @property \Model\Action $action
|
||||
|
@ -33,22 +38,29 @@ use Symfony\Component\EventDispatcher\Event;
|
|||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\ProjectPermission $projectPermission
|
||||
* @property \Model\ProjectDuplication $projectDuplication
|
||||
* @property \Model\ProjectAnalytic $projectAnalytic
|
||||
* @property \Model\ProjectActivity $projectActivity
|
||||
* @property \Model\ProjectDailySummary $projectDailySummary
|
||||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\Subtask $subtask
|
||||
* @property \Model\Swimlane $swimlane
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\Link $link
|
||||
* @property \Model\TaskCreation $taskCreation
|
||||
* @property \Model\TaskModification $taskModification
|
||||
* @property \Model\TaskDuplication $taskDuplication
|
||||
* @property \Model\TaskHistory $taskHistory
|
||||
* @property \Model\TaskExport $taskExport
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
* @property \Model\TaskFilter $taskFilter
|
||||
* @property \Model\TaskPosition $taskPosition
|
||||
* @property \Model\TaskPermission $taskPermission
|
||||
* @property \Model\TaskStatus $taskStatus
|
||||
* @property \Model\TaskValidator $taskValidator
|
||||
* @property \Model\TaskLink $taskLink
|
||||
* @property \Model\CommentHistory $commentHistory
|
||||
* @property \Model\SubtaskHistory $subtaskHistory
|
||||
* @property \Model\SubtaskTimeTracking $subtaskTimeTracking
|
||||
* @property \Model\TimeTracking $timeTracking
|
||||
* @property \Model\User $user
|
||||
* @property \Model\UserSession $userSession
|
||||
|
@ -107,7 +119,7 @@ abstract class Base
|
|||
}
|
||||
|
||||
$this->container['logger']->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nb_queries));
|
||||
$this->container['logger']->debug('RENDERING={time}', array('time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']));
|
||||
$this->container['logger']->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT']));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +143,7 @@ abstract class Base
|
|||
private function sendHeaders($action)
|
||||
{
|
||||
// HTTP secure headers
|
||||
$this->response->csp(array('style-src' => "'self' 'unsafe-inline'"));
|
||||
$this->response->csp(array('style-src' => "'self' 'unsafe-inline'", 'img-src' => '*'));
|
||||
$this->response->nosniff();
|
||||
$this->response->xss();
|
||||
|
||||
|
@ -158,16 +170,19 @@ abstract class Base
|
|||
$this->container['dispatcher']->dispatch('session.bootstrap', new Event);
|
||||
|
||||
if (! $this->acl->isPublicAction($controller, $action)) {
|
||||
$this->handleAuthenticatedUser($controller, $action);
|
||||
$this->handleAuthentication();
|
||||
$this->handleAuthorization($controller, $action);
|
||||
|
||||
$this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check page access and authentication
|
||||
* Check authentication
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function handleAuthenticatedUser($controller, $action)
|
||||
public function handleAuthentication()
|
||||
{
|
||||
if (! $this->authentication->isAuthenticated()) {
|
||||
|
||||
|
@ -177,8 +192,24 @@ abstract class Base
|
|||
|
||||
$this->response->redirect('?controller=user&action=login&redirect_query='.urlencode($this->request->getQueryString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this->acl->isAllowed($controller, $action, $this->request->getIntegerParam('project_id', 0))) {
|
||||
/**
|
||||
* Check page access and authorization
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function handleAuthorization($controller, $action)
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$task_id = $this->request->getIntegerParam('task_id');
|
||||
|
||||
// Allow urls without "project_id"
|
||||
if ($task_id > 0 && $project_id === 0) {
|
||||
$project_id = $this->taskFinder->getProjectId($task_id);
|
||||
}
|
||||
|
||||
if (! $this->acl->isAllowed($controller, $action, $project_id)) {
|
||||
$this->forbidden();
|
||||
}
|
||||
}
|
||||
|
@ -280,7 +311,7 @@ abstract class Base
|
|||
{
|
||||
$task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task || $task['project_id'] != $this->request->getIntegerParam('project_id')) {
|
||||
if (! $task) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
|
|
|
@ -205,6 +205,7 @@ class Board extends Base
|
|||
|
||||
foreach ($columns as $column) {
|
||||
$values['title['.$column['id'].']'] = $column['title'];
|
||||
$values['description['.$column['id'].']'] = $column['description'];
|
||||
$values['task_limit['.$column['id'].']'] = $column['task_limit'] ?: null;
|
||||
}
|
||||
|
||||
|
@ -218,28 +219,39 @@ class Board extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Validate and update a board
|
||||
* Display a form to edit a board
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
public function editColumn(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$columns = $this->board->getColumns($project['id']);
|
||||
$data = $this->request->getValues();
|
||||
$values = $columns_list = array();
|
||||
$column = $this->board->getColumn($this->request->getIntegerParam('column_id'));
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$columns_list[$column['id']] = $column['title'];
|
||||
$values['title['.$column['id'].']'] = isset($data['title'][$column['id']]) ? $data['title'][$column['id']] : '';
|
||||
$values['task_limit['.$column['id'].']'] = isset($data['task_limit'][$column['id']]) ? $data['task_limit'][$column['id']] : 0;
|
||||
}
|
||||
$this->response->html($this->projectLayout('board/edit_column', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values ?: $column,
|
||||
'project' => $project,
|
||||
'column' => $column,
|
||||
'title' => t('Edit column "%s"', $column['title'])
|
||||
)));
|
||||
}
|
||||
|
||||
list($valid, $errors) = $this->board->validateModification($columns_list, $values);
|
||||
/**
|
||||
* Validate and update a column
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function updateColumn()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->board->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->board->update($data)) {
|
||||
if ($this->board->updateColumn($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
|
||||
$this->session->flash(t('Board updated successfully.'));
|
||||
$this->response->redirect('?controller=board&action=edit&project_id='.$project['id']);
|
||||
}
|
||||
|
@ -248,7 +260,7 @@ class Board extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->edit($values, $errors);
|
||||
$this->editcolumn($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -271,7 +283,7 @@ class Board extends Base
|
|||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->board->addColumn($project['id'], $data['title'])) {
|
||||
if ($this->board->addColumn($project['id'], $data['title'], $data['task_limit'], $data['description'])) {
|
||||
$this->session->flash(t('Board updated successfully.'));
|
||||
$this->response->redirect('?controller=board&action=edit&project_id='.$project['id']);
|
||||
}
|
||||
|
@ -389,6 +401,20 @@ class Board extends Base
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get links on mouseover
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function tasklinks()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$this->response->html($this->template->render('board/tasklinks', array(
|
||||
'links' => $this->taskLink->getLinks($task['id']),
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subtasks on mouseover
|
||||
*
|
||||
|
@ -398,23 +424,7 @@ class Board extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$this->response->html($this->template->render('board/subtasks', array(
|
||||
'subtasks' => $this->subTask->getAll($task['id']),
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the status of a subtask from the mouseover
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function toggleSubtask()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$this->subTask->toggleStatus($this->request->getIntegerParam('subtask_id'));
|
||||
|
||||
$this->response->html($this->template->render('board/subtasks', array(
|
||||
'subtasks' => $this->subTask->getAll($task['id']),
|
||||
'subtasks' => $this->subtask->getAll($task['id']),
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
@ -449,7 +459,7 @@ class Board extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Display the description
|
||||
* Display task description
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
|
|
107
sources/app/Controller/Calendar.php
Normal file
107
sources/app/Controller/Calendar.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Model\Task as TaskModel;
|
||||
|
||||
/**
|
||||
* Project Calendar controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
* @author Timo Litzbarski
|
||||
*/
|
||||
class Calendar extends Base
|
||||
{
|
||||
/**
|
||||
* Show calendar view
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
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()),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tasks to display on the calendar (project view)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function project()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$start = $this->request->getStringParam('start');
|
||||
$end = $this->request->getStringParam('end');
|
||||
|
||||
$due_tasks = $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))
|
||||
->filterByDueDateRange($start, $end)
|
||||
->toCalendarEvents();
|
||||
|
||||
$subtask_timeslots = $this->subtaskTimeTracking->getProjectCalendarEvents($project_id, $start, $end);
|
||||
|
||||
$this->response->json(array_merge($due_tasks, $subtask_timeslots));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tasks to display on the calendar (user view)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
$user_id = $this->request->getIntegerParam('user_id');
|
||||
$start = $this->request->getStringParam('start');
|
||||
$end = $this->request->getStringParam('end');
|
||||
|
||||
$due_tasks = $this->taskFilter
|
||||
->create()
|
||||
->filterByOwner($user_id)
|
||||
->filterByStatus(TaskModel::STATUS_OPEN)
|
||||
->filterByDueDateRange($start, $end)
|
||||
->toCalendarEvents();
|
||||
|
||||
$subtask_timeslots = $this->subtaskTimeTracking->getUserCalendarEvents($user_id, $start, $end);
|
||||
|
||||
$this->response->json(array_merge($due_tasks, $subtask_timeslots));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update task due date
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->request->isAjax() && $this->request->isPost()) {
|
||||
|
||||
$values = $this->request->getJson();
|
||||
|
||||
$this->taskModification->update(array(
|
||||
'id' => $values['task_id'],
|
||||
'date_due' => $values['date_due'],
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ class Comment extends Base
|
|||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
|
||||
|
||||
if (empty($values)) {
|
||||
$values = array(
|
||||
|
@ -49,11 +50,20 @@ class Comment extends Base
|
|||
);
|
||||
}
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->html($this->template->render('comment/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'ajax' => $ajax,
|
||||
)));
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('comment/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'title' => t('Add a comment')
|
||||
'title' => t('Add a comment'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -66,6 +76,7 @@ class Comment extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
|
||||
|
||||
list($valid, $errors) = $this->comment->validateCreation($values);
|
||||
|
||||
|
@ -78,6 +89,10 @@ class Comment extends Base
|
|||
$this->session->flashError(t('Unable to create your comment.'));
|
||||
}
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']);
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#comments');
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class Config extends Base
|
|||
{
|
||||
if ($this->request->isPost()) {
|
||||
|
||||
$values = $this->request->getValues();
|
||||
$values = $this->request->getValues() + array('subtask_restriction' => 0, 'subtask_time_tracking' => 0);
|
||||
|
||||
if ($this->config->save($values)) {
|
||||
$this->config->reload();
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Controller;
|
||||
|
||||
use Model\File as FileModel;
|
||||
|
||||
/**
|
||||
* File controller
|
||||
*
|
||||
|
@ -54,7 +52,7 @@ class File extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
$filename = FileModel::BASE_PATH.$file['path'];
|
||||
$filename = FILES_DIR.$file['path'];
|
||||
|
||||
if ($file['task_id'] == $task['id'] && file_exists($filename)) {
|
||||
$this->response->forceDownload($file['name']);
|
||||
|
@ -91,7 +89,7 @@ class File extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
$filename = FileModel::BASE_PATH.$file['path'];
|
||||
$filename = FILES_DIR.$file['path'];
|
||||
|
||||
if ($file['task_id'] == $task['id'] && file_exists($filename)) {
|
||||
$metadata = getimagesize($filename);
|
||||
|
|
162
sources/app/Controller/Link.php
Normal file
162
sources/app/Controller/Link.php
Normal file
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Link controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Olivier Maridat
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Link extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for config views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
|
||||
$params['config_content_for_layout'] = $this->template->render($template, $params);
|
||||
|
||||
return $this->template->layout('config/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current link
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getLink()
|
||||
{
|
||||
$link = $this->link->getById($this->request->getIntegerParam('link_id'));
|
||||
|
||||
if (! $link) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of links
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->response->html($this->layout('link/index', array(
|
||||
'links' => $this->link->getMergedList(),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'title' => t('Settings').' > '.t('Task\'s links'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->link->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->link->create($values['label'], $values['opposite_label'])) {
|
||||
$this->session->flash(t('Link added successfully.'));
|
||||
$this->response->redirect($this->helper->url('link', 'index'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your link.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->index($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$link = $this->getLink();
|
||||
$link['label'] = t($link['label']);
|
||||
|
||||
$this->response->html($this->layout('link/edit', array(
|
||||
'values' => $values ?: $link,
|
||||
'errors' => $errors,
|
||||
'labels' => $this->link->getList($link['id']),
|
||||
'link' => $link,
|
||||
'title' => t('Link modification')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a link (validate the form and update the database)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->link->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->link->update($values)) {
|
||||
$this->session->flash(t('Link updated successfully.'));
|
||||
$this->response->redirect($this->helper->url('link', 'index'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your link.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$link = $this->getLink();
|
||||
|
||||
$this->response->html($this->layout('link/remove', array(
|
||||
'link' => $link,
|
||||
'title' => t('Remove a link')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$link = $this->getLink();
|
||||
|
||||
if ($this->link->remove($link['id'])) {
|
||||
$this->session->flash(t('Link removed successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to remove this link.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url('link', 'index'));
|
||||
}
|
||||
}
|
|
@ -17,24 +17,25 @@ class Project extends Base
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$projects = $this->project->getAll(! $this->userSession->isAdmin());
|
||||
$nb_projects = count($projects);
|
||||
$active_projects = array();
|
||||
$inactive_projects = array();
|
||||
|
||||
foreach ($projects as $project) {
|
||||
if ($project['is_active'] == 1) {
|
||||
$active_projects[] = $project;
|
||||
}
|
||||
else {
|
||||
$inactive_projects[] = $project;
|
||||
}
|
||||
if ($this->userSession->isAdmin()) {
|
||||
$project_ids = $this->project->getAllIds();
|
||||
}
|
||||
else {
|
||||
$project_ids = $this->projectPermission->getMemberProjectIds($this->userSession->getId());
|
||||
}
|
||||
|
||||
$nb_projects = count($project_ids);
|
||||
|
||||
$paginator = $this->paginator
|
||||
->setUrl('project', 'index')
|
||||
->setMax(20)
|
||||
->setOrder('name')
|
||||
->setQuery($this->project->getQueryColumnStats($project_ids))
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->template->layout('project/index', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
|
||||
'active_projects' => $active_projects,
|
||||
'inactive_projects' => $inactive_projects,
|
||||
'paginator' => $paginator,
|
||||
'nb_projects' => $nb_projects,
|
||||
'title' => t('Projects').' ('.$nb_projects.')'
|
||||
)));
|
||||
|
@ -51,7 +52,7 @@ class Project extends Base
|
|||
|
||||
$this->response->html($this->projectLayout('project/show', array(
|
||||
'project' => $project,
|
||||
'stats' => $this->project->getStats($project['id']),
|
||||
'stats' => $this->project->getTaskStats($project['id']),
|
||||
'title' => $project['name'],
|
||||
)));
|
||||
}
|
||||
|
@ -297,6 +298,7 @@ class Project extends Base
|
|||
* Duplicate a project
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @author Michael Lüpkes
|
||||
* @access public
|
||||
*/
|
||||
public function duplicate()
|
||||
|
@ -304,10 +306,8 @@ class Project extends Base
|
|||
$project = $this->getProject();
|
||||
|
||||
if ($this->request->getStringParam('duplicate') === 'yes') {
|
||||
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->project->duplicate($project['id'])) {
|
||||
$values = array_keys($this->request->getValues());
|
||||
if ($this->projectDuplication->duplicate($project['id'], $values)) {
|
||||
$this->session->flash(t('Project cloned successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to clone this project.'));
|
||||
|
@ -425,38 +425,32 @@ class Project extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
$search = $this->request->getStringParam('search');
|
||||
$direction = $this->request->getStringParam('direction', 'DESC');
|
||||
$order = $this->request->getStringParam('order', 'tasks.id');
|
||||
$offset = $this->request->getIntegerParam('offset', 0);
|
||||
$tasks = array();
|
||||
$nb_tasks = 0;
|
||||
$limit = 25;
|
||||
|
||||
$paginator = $this->paginator
|
||||
->setUrl('project', 'search', array('search' => $search, 'project_id' => $project['id']))
|
||||
->setMax(30)
|
||||
->setOrder('tasks.id')
|
||||
->setDirection('DESC');
|
||||
|
||||
if ($search !== '') {
|
||||
$tasks = $this->taskPaginator->searchTasks($project['id'], $search, $offset, $limit, $order, $direction);
|
||||
$nb_tasks = $this->taskPaginator->countSearchTasks($project['id'], $search);
|
||||
|
||||
$paginator
|
||||
->setQuery($this->taskFinder->getSearchQuery($project['id'], $search))
|
||||
->calculate();
|
||||
|
||||
$nb_tasks = $paginator->getTotal();
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project/search', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'pagination' => array(
|
||||
'controller' => 'project',
|
||||
'action' => 'search',
|
||||
'params' => array('search' => $search, 'project_id' => $project['id']),
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => $nb_tasks,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
),
|
||||
'values' => array(
|
||||
'search' => $search,
|
||||
'controller' => 'project',
|
||||
'action' => 'search',
|
||||
'project_id' => $project['id'],
|
||||
),
|
||||
'paginator' => $paginator,
|
||||
'project' => $project,
|
||||
'columns' => $this->board->getColumnsList($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
|
@ -472,32 +466,21 @@ class Project extends Base
|
|||
public function tasks()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$direction = $this->request->getStringParam('direction', 'DESC');
|
||||
$order = $this->request->getStringParam('order', 'tasks.date_completed');
|
||||
$offset = $this->request->getIntegerParam('offset', 0);
|
||||
$limit = 25;
|
||||
|
||||
$tasks = $this->taskPaginator->closedTasks($project['id'], $offset, $limit, $order, $direction);
|
||||
$nb_tasks = $this->taskPaginator->countClosedTasks($project['id']);
|
||||
$paginator = $this->paginator
|
||||
->setUrl('project', '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('project/tasks', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
|
||||
'pagination' => array(
|
||||
'controller' => 'project',
|
||||
'action' => 'tasks',
|
||||
'params' => array('project_id' => $project['id']),
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => $nb_tasks,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
),
|
||||
'project' => $project,
|
||||
'columns' => $this->board->getColumnsList($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'title' => t('Completed tasks for "%s"', $project['name']).' ('.$nb_tasks.')'
|
||||
'paginator' => $paginator,
|
||||
'title' => t('Completed tasks for "%s"', $project['name']).' ('.$paginator->getTotal().')'
|
||||
)));
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace Controller;
|
||||
|
||||
use Model\Subtask as SubtaskModel;
|
||||
|
||||
/**
|
||||
* SubTask controller
|
||||
* Subtask controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
|
@ -18,7 +20,7 @@ class Subtask extends Base
|
|||
*/
|
||||
private function getSubtask()
|
||||
{
|
||||
$subtask = $this->subTask->getById($this->request->getIntegerParam('subtask_id'));
|
||||
$subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id'));
|
||||
|
||||
if (! $subtask) {
|
||||
$this->notfound();
|
||||
|
@ -61,11 +63,11 @@ class Subtask extends Base
|
|||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->subTask->validateCreation($values);
|
||||
list($valid, $errors) = $this->subtask->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->subTask->create($values)) {
|
||||
if ($this->subtask->create($values)) {
|
||||
$this->session->flash(t('Sub-task added successfully.'));
|
||||
}
|
||||
else {
|
||||
|
@ -96,7 +98,7 @@ class Subtask extends Base
|
|||
'values' => empty($values) ? $subtask : $values,
|
||||
'errors' => $errors,
|
||||
'users_list' => $this->projectPermission->getMemberList($task['project_id']),
|
||||
'status_list' => $this->subTask->getStatusList(),
|
||||
'status_list' => $this->subtask->getStatusList(),
|
||||
'subtask' => $subtask,
|
||||
'task' => $task,
|
||||
)));
|
||||
|
@ -113,11 +115,11 @@ class Subtask extends Base
|
|||
$this->getSubtask();
|
||||
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->subTask->validateModification($values);
|
||||
list($valid, $errors) = $this->subtask->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->subTask->update($values)) {
|
||||
if ($this->subtask->update($values)) {
|
||||
$this->session->flash(t('Sub-task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
|
@ -157,7 +159,7 @@ class Subtask extends Base
|
|||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
|
||||
if ($this->subTask->remove($subtask['id'])) {
|
||||
if ($this->subtask->remove($subtask['id'])) {
|
||||
$this->session->flash(t('Sub-task removed successfully.'));
|
||||
}
|
||||
else {
|
||||
|
@ -175,12 +177,86 @@ class Subtask extends Base
|
|||
public function toggleStatus()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask_id = $this->request->getIntegerParam('subtask_id');
|
||||
$subtask = $this->getSubtask();
|
||||
$redirect = $this->request->getStringParam('redirect', 'task');
|
||||
|
||||
if (! $this->subTask->toggleStatus($subtask_id)) {
|
||||
$this->session->flashError(t('Unable to update your sub-task.'));
|
||||
$this->subtask->toggleStatus($subtask['id']);
|
||||
|
||||
if ($redirect === 'board') {
|
||||
|
||||
$this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
|
||||
|
||||
$this->response->html($this->template->render('board/subtasks', array(
|
||||
'subtasks' => $this->subtask->getAll($task['id']),
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#subtasks');
|
||||
$this->toggleRedirect($task, $redirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle subtask restriction (popover)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function subtaskRestriction()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
|
||||
$this->response->html($this->template->render('subtask/restriction_change_status', array(
|
||||
'status_list' => array(
|
||||
SubtaskModel::STATUS_TODO => t('Todo'),
|
||||
SubtaskModel::STATUS_DONE => t('Done'),
|
||||
),
|
||||
'subtask_inprogress' => $this->subtask->getSubtaskInProgress($this->userSession->getId()),
|
||||
'subtask' => $subtask,
|
||||
'task' => $task,
|
||||
'redirect' => $this->request->getStringParam('redirect'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change status of the in progress subtask and the other subtask
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function changeRestrictionStatus()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
// Change status of the previous in progress subtask
|
||||
$this->subtask->update(array(
|
||||
'id' => $values['id'],
|
||||
'status' => $values['status'],
|
||||
));
|
||||
|
||||
// Set the current subtask to in pogress
|
||||
$this->subtask->update(array(
|
||||
'id' => $subtask['id'],
|
||||
'status' => SubtaskModel::STATUS_INPROGRESS,
|
||||
));
|
||||
|
||||
$this->toggleRedirect($task, $values['redirect']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to the right page
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function toggleRedirect(array $task, $redirect)
|
||||
{
|
||||
switch ($redirect) {
|
||||
case 'board':
|
||||
$this->response->redirect($this->helper->url('board', 'show', array('project_id' => $task['project_id'])));
|
||||
case 'dashboard':
|
||||
$this->response->redirect($this->helper->url('app', 'index'));
|
||||
default:
|
||||
$this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ class Task extends Base
|
|||
$this->response->html($this->template->layout('task/public', array(
|
||||
'project' => $project,
|
||||
'comments' => $this->comment->getAll($task['id']),
|
||||
'subtasks' => $this->subTask->getAll($task['id']),
|
||||
'subtasks' => $this->subtask->getAll($task['id']),
|
||||
'links' => $this->taskLink->getLinks($task['id']),
|
||||
'task' => $task,
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'colors_list' => $this->color->getList(),
|
||||
|
@ -54,7 +55,7 @@ class Task extends Base
|
|||
public function show()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtasks = $this->subTask->getAll($task['id']);
|
||||
$subtasks = $this->subtask->getAll($task['id']);
|
||||
|
||||
$values = array(
|
||||
'id' => $task['id'],
|
||||
|
@ -70,9 +71,9 @@ class Task extends Base
|
|||
'files' => $this->file->getAll($task['id']),
|
||||
'comments' => $this->comment->getAll($task['id']),
|
||||
'subtasks' => $subtasks,
|
||||
'links' => $this->taskLink->getLinks($task['id']),
|
||||
'task' => $task,
|
||||
'values' => $values,
|
||||
'timesheet' => $this->timeTracking->getTaskTimesheet($task, $subtasks),
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
|
@ -250,6 +251,7 @@ class Task extends Base
|
|||
public function close()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$redirect = $this->request->getStringParam('redirect');
|
||||
|
||||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
|
||||
|
@ -261,11 +263,23 @@ class Task extends Base
|
|||
$this->session->flashError(t('Unable to close this task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']);
|
||||
if ($redirect === 'board') {
|
||||
$this->response->redirect($this->helper->url('board', 'show', array('project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->html($this->template->render('task/close', array(
|
||||
'task' => $task,
|
||||
'redirect' => $redirect,
|
||||
)));
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task/close', array(
|
||||
'task' => $task,
|
||||
'redirect' => $redirect,
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -418,7 +432,7 @@ class Task extends Base
|
|||
$task = $this->getTask();
|
||||
$values = $task;
|
||||
$errors = array();
|
||||
$projects_list = $this->projectPermission->getMemberProjects($this->userSession->getId());
|
||||
$projects_list = $this->projectPermission->getActiveMemberProjects($this->userSession->getId());
|
||||
|
||||
unset($projects_list[$task['project_id']]);
|
||||
|
||||
|
@ -457,7 +471,7 @@ class Task extends Base
|
|||
$task = $this->getTask();
|
||||
$values = $task;
|
||||
$errors = array();
|
||||
$projects_list = $this->projectPermission->getMemberProjects($this->userSession->getId());
|
||||
$projects_list = $this->projectPermission->getActiveMemberProjects($this->userSession->getId());
|
||||
|
||||
unset($projects_list[$task['project_id']]);
|
||||
|
||||
|
@ -485,4 +499,27 @@ class Task extends Base
|
|||
'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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
116
sources/app/Controller/Tasklink.php
Normal file
116
sources/app/Controller/Tasklink.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* TaskLink controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Olivier Maridat
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Tasklink extends Base
|
||||
{
|
||||
/**
|
||||
* Get the current link
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getTaskLink()
|
||||
{
|
||||
$link = $this->taskLink->getById($this->request->getIntegerParam('link_id'));
|
||||
|
||||
if (! $link) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creation form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if (empty($values)) {
|
||||
$values = array(
|
||||
'task_id' => $task['id'],
|
||||
);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('tasklink/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'labels' => $this->link->getList(0, false),
|
||||
'title' => t('Add a new link')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation and creation
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->taskLink->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
|
||||
$this->session->flash(t('Link added successfully.'));
|
||||
$this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your link.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$link = $this->getTaskLink();
|
||||
|
||||
$this->response->html($this->taskLayout('tasklink/remove', array(
|
||||
'link' => $link,
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->taskLink->remove($this->request->getIntegerParam('link_id'))) {
|
||||
$this->session->flash(t('Link removed successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to remove this link.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@ class User extends Base
|
|||
|
||||
if ($valid) {
|
||||
if ($redirect_query !== '') {
|
||||
$this->response->redirect('?'.$redirect_query);
|
||||
$this->response->redirect('?'.urldecode($redirect_query));
|
||||
}
|
||||
else {
|
||||
$this->response->redirect('?controller=app');
|
||||
|
@ -115,31 +115,19 @@ class User extends Base
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$direction = $this->request->getStringParam('direction', 'ASC');
|
||||
$order = $this->request->getStringParam('order', 'username');
|
||||
$offset = $this->request->getIntegerParam('offset', 0);
|
||||
$limit = 25;
|
||||
|
||||
$users = $this->user->paginate($offset, $limit, $order, $direction);
|
||||
$nb_users = $this->user->count();
|
||||
$paginator = $this->paginator
|
||||
->setUrl('user', 'index')
|
||||
->setMax(30)
|
||||
->setOrder('username')
|
||||
->setQuery($this->user->getQuery())
|
||||
->calculate();
|
||||
|
||||
$this->response->html(
|
||||
$this->template->layout('user/index', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
|
||||
'projects' => $this->project->getList(),
|
||||
'nb_users' => $nb_users,
|
||||
'users' => $users,
|
||||
'title' => t('Users').' ('.$nb_users.')',
|
||||
'pagination' => array(
|
||||
'controller' => 'user',
|
||||
'action' => 'index',
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => $nb_users,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'params' => array(),
|
||||
),
|
||||
'title' => t('Users').' ('.$paginator->getTotal().')',
|
||||
'paginator' => $paginator,
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -201,6 +189,43 @@ class User extends Base
|
|||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display user calendar
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function calendar()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('user/calendar', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display timesheet
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function timesheet()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$subtask_paginator = $this->paginator
|
||||
->setUrl('user', 'timesheet', array('user_id' => $user['id'], 'pagination' => 'subtasks'))
|
||||
->setMax(20)
|
||||
->setOrder('start')
|
||||
->setDirection('DESC')
|
||||
->setQuery($this->subtaskTimeTracking->getUserQuery($user['id']))
|
||||
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
|
||||
|
||||
$this->response->html($this->layout('user/timesheet', array(
|
||||
'subtask_paginator' => $subtask_paginator,
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display last connections
|
||||
*
|
||||
|
@ -330,7 +355,7 @@ class User extends Base
|
|||
|
||||
if ($this->request->isPost()) {
|
||||
|
||||
$values = $this->request->getValues();
|
||||
$values = $this->request->getValues() + array('disable_login_form' => 0);
|
||||
|
||||
if ($this->userSession->isAdmin()) {
|
||||
$values += array('is_admin' => 0);
|
||||
|
@ -462,7 +487,7 @@ class User extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function gitHub()
|
||||
public function github()
|
||||
{
|
||||
$code = $this->request->getStringParam('code');
|
||||
|
||||
|
@ -506,7 +531,7 @@ class User extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function unlinkGitHub()
|
||||
public function unlinkGithub()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
|
||||
|
|
|
@ -82,4 +82,22 @@ class Webhook extends Base
|
|||
|
||||
echo $result ? 'PARSED' : 'IGNORED';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Bitbucket webhooks
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function bitbucket()
|
||||
{
|
||||
if ($this->config->get('webhook_token') !== $this->request->getStringParam('token')) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
}
|
||||
|
||||
$this->bitbucketWebhook->setProjectId($this->request->getIntegerParam('project_id'));
|
||||
|
||||
$result = $this->bitbucketWebhook->parsePayload(json_decode(@$_POST['payload'], true));
|
||||
|
||||
echo $result ? 'PARSED' : 'IGNORED';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace Core;
|
||||
|
||||
use Pimple\Container;
|
||||
use Parsedown;
|
||||
|
||||
/**
|
||||
* Template helpers
|
||||
|
@ -13,6 +12,7 @@ use Parsedown;
|
|||
*
|
||||
* @property \Core\Session $session
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\User $user
|
||||
* @property \Model\UserSession $userSession
|
||||
*/
|
||||
|
@ -49,6 +49,33 @@ class Helper
|
|||
return $this->container[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the age of an item in quasi human readable format.
|
||||
* It's in this format: <1h , NNh, NNd
|
||||
*
|
||||
* @access public
|
||||
* @param integer $timestamp Unix timestamp of the artifact for which age will be calculated
|
||||
* @param integer $now Compare with this timestamp (Default value is the current unix timestamp)
|
||||
* @return string
|
||||
*/
|
||||
public function getTaskAge($timestamp, $now = null)
|
||||
{
|
||||
if ($now === null) {
|
||||
$now = time();
|
||||
}
|
||||
|
||||
$diff = $now - $timestamp;
|
||||
|
||||
if ($diff < 3600) {
|
||||
return t('<1h');
|
||||
}
|
||||
else if ($diff < 86400) {
|
||||
return t('%dh', $diff / 3600);
|
||||
}
|
||||
|
||||
return t('%dd', ($now - $timestamp) / 86400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy cache helper for acl::isManagerActionAllowed()
|
||||
*
|
||||
|
@ -104,9 +131,9 @@ class Helper
|
|||
* @param string $filename Filename
|
||||
* @return string
|
||||
*/
|
||||
public function css($filename)
|
||||
public function css($filename, $is_file = true)
|
||||
{
|
||||
return '<link rel="stylesheet" href="'.$filename.'?'.filemtime($filename).'" media="screen">';
|
||||
return '<link rel="stylesheet" href="'.$filename.($is_file ? '?'.filemtime($filename) : '').'" media="screen">';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,9 +221,9 @@ class Helper
|
|||
* @param string $class CSS class
|
||||
* @return string
|
||||
*/
|
||||
public function formSelect($name, array $options, array $values = array(), array $errors = array(), $class = '')
|
||||
public function formSelect($name, array $options, array $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||
{
|
||||
$html = '<select name="'.$name.'" id="form-'.$name.'" class="'.$class.'">';
|
||||
$html = '<select name="'.$name.'" id="form-'.$name.'" class="'.$class.'" '.implode(' ', $attributes).'>';
|
||||
|
||||
foreach ($options as $id => $value) {
|
||||
|
||||
|
@ -245,7 +272,7 @@ class Helper
|
|||
*/
|
||||
public function formRadio($name, $label, $value, $selected = false, $class = '')
|
||||
{
|
||||
return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.$this->e($value).'" '.($selected ? 'selected="selected"' : '').'>'.$this->e($label).'</label>';
|
||||
return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.$this->e($value).'" '.($selected ? 'selected="selected"' : '').'> '.$this->e($label).'</label>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -417,7 +444,7 @@ class Helper
|
|||
}
|
||||
|
||||
/**
|
||||
* URL query string
|
||||
* Generate controller/action url for templates
|
||||
*
|
||||
* u('task', 'show', array('task_id' => $task_id))
|
||||
*
|
||||
|
@ -429,83 +456,40 @@ class Helper
|
|||
*/
|
||||
public function u($controller, $action, array $params = array(), $csrf = false)
|
||||
{
|
||||
$html = '?controller='.$controller.'&action='.$action;
|
||||
$values = array(
|
||||
'controller' => $controller,
|
||||
'action' => $action,
|
||||
);
|
||||
|
||||
if ($csrf) {
|
||||
$params['csrf_token'] = Security::getCSRFToken();
|
||||
}
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
$html .= '&'.$key.'='.$value;
|
||||
}
|
||||
$values += $params;
|
||||
|
||||
return $html;
|
||||
return '?'.http_build_query($values, '', '&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagination links
|
||||
* Generate controller/action url
|
||||
*
|
||||
* @param array $pagination Pagination information
|
||||
* l('task', 'show', array('task_id' => $task_id))
|
||||
*
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @param array $params Url parameters
|
||||
* @return string
|
||||
*/
|
||||
public function paginate(array $pagination)
|
||||
public function url($controller, $action, array $params = array())
|
||||
{
|
||||
extract($pagination);
|
||||
$values = array(
|
||||
'controller' => $controller,
|
||||
'action' => $action,
|
||||
);
|
||||
|
||||
if ($pagination['offset'] === 0 && ($total - $pagination['offset']) <= $limit) {
|
||||
return '';
|
||||
}
|
||||
$values += $params;
|
||||
|
||||
$html = '<div class="pagination">';
|
||||
$html .= '<span class="pagination-previous">';
|
||||
|
||||
if ($pagination['offset'] > 0) {
|
||||
$offset = $pagination['offset'] - $limit;
|
||||
$html .= $this->a('← '.t('Previous'), $controller, $action, $params + compact('offset', 'order', 'direction'));
|
||||
}
|
||||
else {
|
||||
$html .= '← '.t('Previous');
|
||||
}
|
||||
|
||||
$html .= '</span>';
|
||||
$html .= '<span class="pagination-next">';
|
||||
|
||||
if (($total - $pagination['offset']) > $limit) {
|
||||
$offset = $pagination['offset'] + $limit;
|
||||
$html .= $this->a(t('Next').' →', $controller, $action, $params + compact('offset', 'order', 'direction'));
|
||||
}
|
||||
else {
|
||||
$html .= t('Next').' →';
|
||||
}
|
||||
|
||||
$html .= '</span>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Column sorting (work with pagination)
|
||||
*
|
||||
* @param string $label Column title
|
||||
* @param string $column SQL column name
|
||||
* @param array $pagination Pagination information
|
||||
* @return string
|
||||
*/
|
||||
public function order($label, $column, array $pagination)
|
||||
{
|
||||
extract($pagination);
|
||||
|
||||
$prefix = '';
|
||||
|
||||
if ($order === $column) {
|
||||
$prefix = $direction === 'DESC' ? '▼ ' : '▲ ';
|
||||
$direction = $direction === 'DESC' ? 'ASC' : 'DESC';
|
||||
}
|
||||
|
||||
$order = $column;
|
||||
|
||||
return $prefix.$this->a($label, $controller, $action, $params + compact('offset', 'order', 'direction'));
|
||||
return '?'.http_build_query($values);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -517,24 +501,9 @@ class Helper
|
|||
*/
|
||||
public function markdown($text, array $link = array())
|
||||
{
|
||||
$html = Parsedown::instance()
|
||||
->setMarkupEscaped(true) # escapes markup (HTML)
|
||||
->text($text);
|
||||
|
||||
// Replace task #123 by a link to the task
|
||||
if (! empty($link) && preg_match_all('!#(\d+)!i', $html, $matches, PREG_SET_ORDER)) {
|
||||
|
||||
foreach ($matches as $match) {
|
||||
|
||||
$html = str_replace(
|
||||
$match[0],
|
||||
$this->a($match[0], $link['controller'], $link['action'], $link['params'] + array('task_id' => $match[1])),
|
||||
$html
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
$parser = new Markdown($link, $this);
|
||||
$parser->setMarkupEscaped(true);
|
||||
return $parser->text($text);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -656,4 +625,56 @@ class Helper
|
|||
|
||||
return $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get javascript language code
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function jsLang()
|
||||
{
|
||||
return $this->config->getJsLanguageCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current timezone
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getTimezone()
|
||||
{
|
||||
return $this->config->getCurrentTimezone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the link to toggle subtask status
|
||||
*
|
||||
* @access public
|
||||
* @param array $subtask
|
||||
* @param string $redirect
|
||||
* @return string
|
||||
*/
|
||||
public function toggleSubtaskStatus(array $subtask, $redirect)
|
||||
{
|
||||
if ($subtask['status'] == 0 && isset($this->session['has_subtask_inprogress']) && $this->session['has_subtask_inprogress'] === true) {
|
||||
|
||||
return $this->a(
|
||||
trim($this->render('subtask/icons', array('subtask' => $subtask))) . $this->e($subtask['title']),
|
||||
'subtask',
|
||||
'subtaskRestriction',
|
||||
array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'redirect' => $redirect),
|
||||
false,
|
||||
'popover task-board-popover'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->a(
|
||||
trim($this->render('subtask/icons', array('subtask' => $subtask))) . $this->e($subtask['title']),
|
||||
'subtask',
|
||||
'toggleStatus',
|
||||
array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'redirect' => $redirect)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
43
sources/app/Core/Markdown.php
Normal file
43
sources/app/Core/Markdown.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
use Parsedown;
|
||||
|
||||
/**
|
||||
* Specific Markdown rules for Kanboard
|
||||
*
|
||||
* @package core
|
||||
* @author norcnorc
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Markdown extends Parsedown
|
||||
{
|
||||
private $link;
|
||||
private $helper;
|
||||
|
||||
public function __construct($link, Helper $helper)
|
||||
{
|
||||
$this->link = $link;
|
||||
$this->helper = $helper;
|
||||
$this->InlineTypes['#'][] = 'TaskLink';
|
||||
$this->inlineMarkerList .= '#';
|
||||
}
|
||||
|
||||
protected function inlineTaskLink($Excerpt)
|
||||
{
|
||||
// Replace task #123 by a link to the task
|
||||
if (! empty($this->link) && preg_match('!#(\d+)!i', $Excerpt['text'], $matches)) {
|
||||
|
||||
$url = $this->helper->u($this->link['controller'],
|
||||
$this->link['action'],
|
||||
$this->link['params'] + array('task_id' => $matches[1]));
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => array(
|
||||
'name' => 'a',
|
||||
'text' => $matches[0],
|
||||
'attributes' => array('href' => $url)));
|
||||
}
|
||||
}
|
||||
}
|
461
sources/app/Core/Paginator.php
Normal file
461
sources/app/Core/Paginator.php
Normal file
|
@ -0,0 +1,461 @@
|
|||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
use Pimple\Container;
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* Paginator helper
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Paginator
|
||||
{
|
||||
/**
|
||||
* Container instance
|
||||
*
|
||||
* @access private
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* Total number of items
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $total = 0;
|
||||
|
||||
/**
|
||||
* Page number
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $page = 1;
|
||||
|
||||
/**
|
||||
* Offset
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $offset = 0;
|
||||
|
||||
/**
|
||||
* Limit
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $limit = 0;
|
||||
|
||||
/**
|
||||
* Sort by this column
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $order = '';
|
||||
|
||||
/**
|
||||
* Sorting direction
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $direction = 'ASC';
|
||||
|
||||
/**
|
||||
* Slice of items
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $items = array();
|
||||
|
||||
/**
|
||||
* PicoDb Table instance
|
||||
*
|
||||
* @access private
|
||||
* @var \Picodb\Table
|
||||
*/
|
||||
private $query = null;
|
||||
|
||||
/**
|
||||
* Controller name
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $controller = '';
|
||||
|
||||
/**
|
||||
* Action name
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $action = '';
|
||||
|
||||
/**
|
||||
* Url params
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $params = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Pimple\Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a PicoDb query
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoDb\Table
|
||||
* @return Paginator
|
||||
*/
|
||||
public function setQuery(Table $query)
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->total = $this->query->count();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a PicoDb query
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function executeQuery()
|
||||
{
|
||||
if ($this->query !== null) {
|
||||
return $this->query
|
||||
->offset($this->offset)
|
||||
->limit($this->limit)
|
||||
->orderBy($this->order, $this->direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set url parameters
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
* @return Paginator
|
||||
*/
|
||||
public function setUrl($controller, $action, array $params = array())
|
||||
{
|
||||
$this->controller = $controller;
|
||||
$this->action = $action;
|
||||
$this->params = $params;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add manually items
|
||||
*
|
||||
* @access public
|
||||
* @param array $items
|
||||
* @return Paginator
|
||||
*/
|
||||
public function setCollection(array $items)
|
||||
{
|
||||
$this->items = $items;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the items
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCollection()
|
||||
{
|
||||
return $this->items ?: $this->executeQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the total number of items
|
||||
*
|
||||
* @access public
|
||||
* @param integer $total
|
||||
* @return Paginator
|
||||
*/
|
||||
public function setTotal($total)
|
||||
{
|
||||
$this->total = $total;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of items
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getTotal()
|
||||
{
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default page number
|
||||
*
|
||||
* @access public
|
||||
* @param integer $page
|
||||
* @return Paginator
|
||||
*/
|
||||
public function setPage($page)
|
||||
{
|
||||
$this->page = $page;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default column order
|
||||
*
|
||||
* @access public
|
||||
* @param string $order
|
||||
* @return Paginator
|
||||
*/
|
||||
public function setOrder($order)
|
||||
{
|
||||
$this->order = $order;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default sorting direction
|
||||
*
|
||||
* @access public
|
||||
* @param string $direction
|
||||
* @return Paginator
|
||||
*/
|
||||
public function setDirection($direction)
|
||||
{
|
||||
$this->direction = $direction;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of items per page
|
||||
*
|
||||
* @access public
|
||||
* @param integer $limit
|
||||
* @return Paginator
|
||||
*/
|
||||
public function setMax($limit)
|
||||
{
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the collection is empty
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->total === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the offset calculation only if the $condition is true
|
||||
*
|
||||
* @access public
|
||||
* @param boolean $condition
|
||||
* @return Paginator
|
||||
*/
|
||||
public function calculateOnlyIf($condition)
|
||||
{
|
||||
if ($condition) {
|
||||
$this->calculate();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the offset value accoring to url params and the page number
|
||||
*
|
||||
* @access public
|
||||
* @return Paginator
|
||||
*/
|
||||
public function calculate()
|
||||
{
|
||||
$this->page = $this->container['request']->getIntegerParam('page', 1);
|
||||
$this->direction = $this->container['request']->getStringParam('direction', $this->direction);
|
||||
$this->order = $this->container['request']->getStringParam('order', $this->order);
|
||||
|
||||
if ($this->page < 1) {
|
||||
$this->page = 1;
|
||||
}
|
||||
|
||||
$this->offset = ($this->page - 1) * $this->limit;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url params for link generation
|
||||
*
|
||||
* @access public
|
||||
* @param integer $page
|
||||
* @param string $order
|
||||
* @param string $direction
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlParams($page, $order, $direction)
|
||||
{
|
||||
$params = array(
|
||||
'page' => $page,
|
||||
'order' => $order,
|
||||
'direction' => $direction,
|
||||
);
|
||||
|
||||
return array_merge($this->params, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the previous link
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function generatePreviousLink()
|
||||
{
|
||||
$html = '<span class="pagination-previous">';
|
||||
|
||||
if ($this->offset > 0) {
|
||||
$html .= $this->container['helper']->a(
|
||||
'← '.t('Previous'),
|
||||
$this->controller,
|
||||
$this->action,
|
||||
$this->getUrlParams($this->page - 1, $this->order, $this->direction)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$html .= '← '.t('Previous');
|
||||
}
|
||||
|
||||
$html .= '</span>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the next link
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function generateNextLink()
|
||||
{
|
||||
$html = '<span class="pagination-next">';
|
||||
|
||||
if (($this->total - $this->offset) > $this->limit) {
|
||||
$html .= $this->container['helper']->a(
|
||||
t('Next').' →',
|
||||
$this->controller,
|
||||
$this->action,
|
||||
$this->getUrlParams($this->page + 1, $this->order, $this->direction)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$html .= t('Next').' →';
|
||||
}
|
||||
|
||||
$html .= '</span>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is no pagination to show
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasNothingtoShow()
|
||||
{
|
||||
return $this->offset === 0 && ($this->total - $this->offset) <= $this->limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generation pagination links
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function toHtml()
|
||||
{
|
||||
$html = '';
|
||||
|
||||
if (! $this->hasNothingtoShow()) {
|
||||
$html .= '<div class="pagination">';
|
||||
$html .= $this->generatePreviousLink();
|
||||
$html .= $this->generateNextLink();
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to output pagination links
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toHtml();
|
||||
}
|
||||
|
||||
/**
|
||||
* Column sorting
|
||||
*
|
||||
* @param string $label Column title
|
||||
* @param string $column SQL column name
|
||||
* @return string
|
||||
*/
|
||||
public function order($label, $column)
|
||||
{
|
||||
$prefix = '';
|
||||
$direction = 'ASC';
|
||||
|
||||
if ($this->order === $column) {
|
||||
$prefix = $this->direction === 'DESC' ? '▼ ' : '▲ ';
|
||||
$direction = $this->direction === 'DESC' ? 'ASC' : 'DESC';
|
||||
}
|
||||
|
||||
return $prefix.$this->container['helper']->a(
|
||||
$label,
|
||||
$this->controller,
|
||||
$this->action,
|
||||
$this->getUrlParams($this->page, $column, $direction)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -167,6 +167,23 @@ class Response
|
|||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a css response
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Raw data
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function css($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Type: text/css; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a binary response
|
||||
*
|
||||
|
@ -195,24 +212,7 @@ class Response
|
|||
$policies['default-src'] = "'self'";
|
||||
$values = '';
|
||||
|
||||
foreach ($policies as $policy => $hosts) {
|
||||
|
||||
if (is_array($hosts)) {
|
||||
|
||||
$acl = '';
|
||||
|
||||
foreach ($hosts as &$host) {
|
||||
|
||||
if ($host === '*' || $host === 'self' || strpos($host, 'http') === 0) {
|
||||
$acl .= $host.' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
$acl = $hosts;
|
||||
}
|
||||
|
||||
foreach ($policies as $policy => $acl) {
|
||||
$values .= $policy.' '.trim($acl).'; ';
|
||||
}
|
||||
|
||||
|
|
97
sources/app/Integration/BitbucketWebhook.php
Normal file
97
sources/app/Integration/BitbucketWebhook.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace Integration;
|
||||
|
||||
use Event\TaskEvent;
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Bitbucket Webhook
|
||||
*
|
||||
* @package integration
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class BitbucketWebhook extends Base
|
||||
{
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_COMMIT = 'bitbucket.webhook.commit';
|
||||
|
||||
/**
|
||||
* Project id
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $project_id = 0;
|
||||
|
||||
/**
|
||||
* Set the project id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
*/
|
||||
public function setProjectId($project_id)
|
||||
{
|
||||
$this->project_id = $project_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse events
|
||||
*
|
||||
* @access public
|
||||
* @param array $payload Gitlab event
|
||||
* @return boolean
|
||||
*/
|
||||
public function parsePayload(array $payload)
|
||||
{
|
||||
if (! empty($payload['commits'])) {
|
||||
|
||||
foreach ($payload['commits'] as $commit) {
|
||||
|
||||
if ($this->handleCommit($commit)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse commit
|
||||
*
|
||||
* @access public
|
||||
* @param array $commit Gitlab commit
|
||||
* @return boolean
|
||||
*/
|
||||
public function handleCommit(array $commit)
|
||||
{
|
||||
$task_id = $this->task->getTaskIdFromText($commit['message']);
|
||||
|
||||
if (! $task_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$task = $this->taskFinder->getById($task_id);
|
||||
|
||||
if (! $task) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($task['is_active'] == Task::STATUS_OPEN && $task['project_id'] == $this->project_id) {
|
||||
|
||||
$this->container['dispatcher']->dispatch(
|
||||
self::EVENT_COMMIT,
|
||||
new TaskEvent(array('task_id' => $task_id) + $task)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -148,10 +148,10 @@ class GitlabWebhook extends Base
|
|||
*/
|
||||
public function handleIssueEvent(array $payload)
|
||||
{
|
||||
switch ($payload['object_attributes']['state']) {
|
||||
case 'opened':
|
||||
switch ($payload['object_attributes']['action']) {
|
||||
case 'open':
|
||||
return $this->handleIssueOpened($payload['object_attributes']);
|
||||
case 'closed':
|
||||
case 'close':
|
||||
return $this->handleIssueClosed($payload['object_attributes']);
|
||||
}
|
||||
|
||||
|
|
227
sources/app/Libs/password.php
Normal file
227
sources/app/Libs/password.php
Normal file
|
@ -0,0 +1,227 @@
|
|||
<?php
|
||||
/**
|
||||
* A Compatibility library with PHP 5.5's simplified password hashing API.
|
||||
*
|
||||
* @author Anthony Ferrara <ircmaxell@php.net>
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @copyright 2012 The Authors
|
||||
*/
|
||||
|
||||
if (!defined('PASSWORD_BCRYPT')) {
|
||||
|
||||
define('PASSWORD_BCRYPT', 1);
|
||||
define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.3.7', '<')) {
|
||||
|
||||
define('PASSWORD_PREFIX', '$2a$');
|
||||
}
|
||||
else {
|
||||
|
||||
define('PASSWORD_PREFIX', '$2y$');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash the password using the specified algorithm
|
||||
*
|
||||
* @param string $password The password to hash
|
||||
* @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
|
||||
* @param array $options The options for the algorithm to use
|
||||
*
|
||||
* @return string|false The hashed password, or false on error.
|
||||
*/
|
||||
function password_hash($password, $algo, array $options = array()) {
|
||||
if (!function_exists('crypt')) {
|
||||
trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
if (!is_string($password)) {
|
||||
trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
if (!is_int($algo)) {
|
||||
trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
switch ($algo) {
|
||||
case PASSWORD_BCRYPT:
|
||||
// Note that this is a C constant, but not exposed to PHP, so we don't define it here.
|
||||
$cost = 10;
|
||||
if (isset($options['cost'])) {
|
||||
$cost = $options['cost'];
|
||||
if ($cost < 4 || $cost > 31) {
|
||||
trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
$required_salt_len = 22;
|
||||
$hash_format = sprintf("%s%02d$", PASSWORD_PREFIX, $cost);
|
||||
break;
|
||||
default:
|
||||
trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
if (isset($options['salt'])) {
|
||||
switch (gettype($options['salt'])) {
|
||||
case 'NULL':
|
||||
case 'boolean':
|
||||
case 'integer':
|
||||
case 'double':
|
||||
case 'string':
|
||||
$salt = (string) $options['salt'];
|
||||
break;
|
||||
case 'object':
|
||||
if (method_exists($options['salt'], '__tostring')) {
|
||||
$salt = (string) $options['salt'];
|
||||
break;
|
||||
}
|
||||
case 'array':
|
||||
case 'resource':
|
||||
default:
|
||||
trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
if (strlen($salt) < $required_salt_len) {
|
||||
trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
|
||||
return null;
|
||||
} elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
|
||||
$salt = str_replace('+', '.', base64_encode($salt));
|
||||
}
|
||||
} else {
|
||||
$buffer = '';
|
||||
$raw_length = (int) ($required_salt_len * 3 / 4 + 1);
|
||||
$buffer_valid = false;
|
||||
if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
|
||||
$buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM);
|
||||
if ($buffer) {
|
||||
$buffer_valid = true;
|
||||
}
|
||||
}
|
||||
if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
|
||||
$buffer = openssl_random_pseudo_bytes($raw_length);
|
||||
if ($buffer) {
|
||||
$buffer_valid = true;
|
||||
}
|
||||
}
|
||||
if (!$buffer_valid && is_readable('/dev/urandom')) {
|
||||
$f = fopen('/dev/urandom', 'r');
|
||||
$read = strlen($buffer);
|
||||
while ($read < $raw_length) {
|
||||
$buffer .= fread($f, $raw_length - $read);
|
||||
$read = strlen($buffer);
|
||||
}
|
||||
fclose($f);
|
||||
if ($read >= $raw_length) {
|
||||
$buffer_valid = true;
|
||||
}
|
||||
}
|
||||
if (!$buffer_valid || strlen($buffer) < $raw_length) {
|
||||
$bl = strlen($buffer);
|
||||
for ($i = 0; $i < $raw_length; $i++) {
|
||||
if ($i < $bl) {
|
||||
$buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
|
||||
} else {
|
||||
$buffer .= chr(mt_rand(0, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
$salt = str_replace('+', '.', base64_encode($buffer));
|
||||
|
||||
}
|
||||
$salt = substr($salt, 0, $required_salt_len);
|
||||
|
||||
$hash = $hash_format . $salt;
|
||||
|
||||
$ret = crypt($password, $hash);
|
||||
|
||||
if (!is_string($ret) || strlen($ret) <= 13) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the password hash. Returns an array of the information
|
||||
* that was used to generate the password hash.
|
||||
*
|
||||
* array(
|
||||
* 'algo' => 1,
|
||||
* 'algoName' => 'bcrypt',
|
||||
* 'options' => array(
|
||||
* 'cost' => 10,
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
* @param string $hash The password hash to extract info from
|
||||
*
|
||||
* @return array The array of information about the hash.
|
||||
*/
|
||||
function password_get_info($hash) {
|
||||
$return = array(
|
||||
'algo' => 0,
|
||||
'algoName' => 'unknown',
|
||||
'options' => array(),
|
||||
);
|
||||
if (substr($hash, 0, 4) == PASSWORD_PREFIX && strlen($hash) == 60) {
|
||||
$return['algo'] = PASSWORD_BCRYPT;
|
||||
$return['algoName'] = 'bcrypt';
|
||||
list($cost) = sscanf($hash, PASSWORD_PREFIX."%d$");
|
||||
$return['options']['cost'] = $cost;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the password hash needs to be rehashed according to the options provided
|
||||
*
|
||||
* If the answer is true, after validating the password using password_verify, rehash it.
|
||||
*
|
||||
* @param string $hash The hash to test
|
||||
* @param int $algo The algorithm used for new password hashes
|
||||
* @param array $options The options array passed to password_hash
|
||||
*
|
||||
* @return boolean True if the password needs to be rehashed.
|
||||
*/
|
||||
function password_needs_rehash($hash, $algo, array $options = array()) {
|
||||
$info = password_get_info($hash);
|
||||
if ($info['algo'] != $algo) {
|
||||
return true;
|
||||
}
|
||||
switch ($algo) {
|
||||
case PASSWORD_BCRYPT:
|
||||
$cost = isset($options['cost']) ? $options['cost'] : 10;
|
||||
if ($cost != $info['options']['cost']) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a password against a hash using a timing attack resistant approach
|
||||
*
|
||||
* @param string $password The password to verify
|
||||
* @param string $hash The hash to verify against
|
||||
*
|
||||
* @return boolean If the password matches the hash
|
||||
*/
|
||||
function password_verify($password, $hash) {
|
||||
if (!function_exists('crypt')) {
|
||||
trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
$ret = crypt($password, $hash);
|
||||
if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$status = 0;
|
||||
for ($i = 0; $i < strlen($ret); $i++) {
|
||||
$status |= (ord($ret[$i]) ^ ord($hash[$i]));
|
||||
}
|
||||
|
||||
return $status === 0;
|
||||
}
|
||||
}
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'Kommentar opdateret',
|
||||
'New comment posted by %s' => 'Ny kommentar af %s',
|
||||
'List of due tasks for the project "%s"' => 'Udestående opgaver for projektet "%s"',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Ny vedhæftning] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Ny kommentar] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Kommentar opdateret] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Ny under-opgave] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Under-opgave opdateret] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Ny opgave] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Opgave opdateret] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Opgave lukket] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Opgave åbnet] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => 'Udestående opgaver',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notifikation',
|
||||
'I want to receive notifications only for those projects:' => 'Jeg vil kun have notifikationer for disse projekter:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => 'Opgaven ansvarlig ændring',
|
||||
'%s change the assignee of the task #%d to %s' => '%s skrift ansvarlig for opgaven #%d til %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s skift ansvarlig for opgaven %s til %s',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][Kolonne Skift] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][Position Skift] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][Ansvarlig Skift] %s (#%d)',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'Ny adgangskode for brugeren',
|
||||
'Choose an event' => 'Vælg et event',
|
||||
'Github commit received' => 'Github commit modtaget',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
// 'Application default' => '',
|
||||
// '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' => '',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
// 'Moved to column %s' => '',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'Kommentar wurde aktualisiert',
|
||||
'New comment posted by %s' => 'Neuer Kommentar verfasst durch %s',
|
||||
'List of due tasks for the project "%s"' => 'Liste der fälligen Aufgaben für das Projekt "%s"',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Neuer Anhang] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Neuer Kommentar] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Kommentar aktualisisiert] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Neue Teilaufgabe] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Teilaufgabe aktualisisert] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Neue Aufgabe] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Aufgabe aktualisiert] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Aufgabe geschlossen] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Aufgabe geöffnet] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][Fällige Aufgaben]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Benachrichtigung',
|
||||
'I want to receive notifications only for those projects:' => 'Ich möchte nur für diese Projekte Benachrichtigungen erhalten:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => 'Zuständigkeit geändert',
|
||||
'%s change the assignee of the task #%d to %s' => '%s hat die Zusständigkeit der Aufgabe #%d geändert um %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s hat die Zuständigkeit der Aufgabe %s geändert um %s',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][Spaltenänderung] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][Positionsänderung] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][Zuständigkeitsänderung] %s (#%d)',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'Neues Passwort des Benutzers "%s"',
|
||||
'Choose an event' => 'Aktion wählen',
|
||||
'Github commit received' => 'Github commit empfangen',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
'Application default' => 'Anwendungsstandard',
|
||||
'Language:' => 'Sprache:',
|
||||
'Timezone:' => 'Zeitzone:',
|
||||
// 'Next' => '',
|
||||
'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',
|
||||
'Add a comment logging moving the task between columns' => 'Kommentar hinzufügen wenn die Aufgabe verschoben wird',
|
||||
'Moved to column %s' => 'In Spalte %s verschoben',
|
||||
'Change description' => 'Beschreibung ändern',
|
||||
'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',
|
||||
'Enable time tracking for subtasks' => 'Aktiviere Zeiterfassung für Teilaufgaben',
|
||||
'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',
|
||||
'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',
|
||||
'Start' => 'Start',
|
||||
'End' => 'Ende',
|
||||
'Task age in days' => 'Aufgabenalter in Tagen',
|
||||
'Days in this column' => 'Tage in dieser Spalte',
|
||||
'%dd' => '%dT',
|
||||
'Add a link' => 'Verbindung hinzufügen',
|
||||
'Add a new link' => 'Neue Verbindung hinzufügen',
|
||||
'Do you really want to remove this link: "%s"?' => 'Die Verbindung "%s" wirklich löschen?',
|
||||
'Do you really want to remove this link with task #%d?' => 'Die Verbindung mit der Aufgabe #%d wirklich löschen?',
|
||||
'Field required' => 'Feld erforderlich',
|
||||
'Link added successfully.' => 'Verbindung erfolgreich hinzugefügt.',
|
||||
'Link updated successfully.' => 'Verbindung erfolgreich aktualisiert.',
|
||||
'Link removed successfully.' => 'Verbindung erfolgreich gelöscht.',
|
||||
'Link labels' => 'Verbindungsbeschriftung',
|
||||
'Link modification' => 'Verbindung ändern',
|
||||
'Links' => 'Verbindungen',
|
||||
'Link settings' => 'Verbindungseinstellungen',
|
||||
'Opposite label' => 'Gegenteil',
|
||||
'Remove a link' => 'Verbindung entfernen',
|
||||
'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',
|
||||
'Unable to create your link.' => 'Verbindung kann nicht erstellt werden.',
|
||||
'Unable to update your link.' => 'Verbindung kann nicht aktualisiert werden.',
|
||||
'Unable to remove this link.' => 'Verbindung kann nicht entfernt werden',
|
||||
'relates to' => 'gehört zu',
|
||||
'blocks' => 'blockiert',
|
||||
'is blocked by' => 'ist blockiert von',
|
||||
'duplicates' => 'doppelt',
|
||||
'is duplicated by' => 'ist gedoppelt von',
|
||||
'is a child of' => 'ist untergeordnet',
|
||||
'is a parent of' => 'ist übergeordnet',
|
||||
'targets milestone' => 'betrifft Meilenstein',
|
||||
'is a milestone of' => 'ist ein Meilenstein von',
|
||||
'fixes' => 'behebt',
|
||||
'is fixed by' => 'wird behoben von',
|
||||
'This task' => 'Diese Aufgabe',
|
||||
'<1h' => '<1Std',
|
||||
'%dh' => '%dStd',
|
||||
// '%b %e' => '',
|
||||
'Expand tasks' => 'Aufgaben aufklappen',
|
||||
'Collapse tasks' => 'Aufgaben zusammenklappen',
|
||||
'Expand/collapse tasks' => 'Aufgaben auf/zuklappen',
|
||||
'Close dialog box' => 'Dialog schließen',
|
||||
'Submit a form' => 'Formular abschicken',
|
||||
'Board view' => 'Pinnwand Ansicht',
|
||||
'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',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'Comentario actualizado',
|
||||
'New comment posted by %s' => 'Nuevo comentario agregado por %s',
|
||||
'List of due tasks for the project "%s"' => 'Lista de tareas para el proyecto "%s"',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][uevo adjunto] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Nuevo comentario] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Comentario actualizado] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Nueva subtarea] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Subtarea actualizada] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Nueva tarea] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Tarea actualizada] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Tarea cerrada] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Tarea abierta] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][Tareas vencidas]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notificación',
|
||||
'I want to receive notifications only for those projects:' => 'Quiero recibir notificaciones sólo de estos proyectos:',
|
||||
|
@ -500,131 +498,131 @@ return array(
|
|||
'Task assignee change' => 'Cambiar persona asignada a la tarea',
|
||||
// '%s change the assignee of the task #%d to %s' => '',
|
||||
// '%s changed the assignee of the task %s to %s' => '',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][Cambia Columna] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][Cambia Posición] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][Cambia Persona Asignada] %s (#%d)',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'Nueva contraseña para el usuario "%s"',
|
||||
// 'Choose an event' => '',
|
||||
// 'Github commit received' => '',
|
||||
// 'Github issue opened' => '',
|
||||
// 'Github issue closed' => '',
|
||||
// 'Github issue reopened' => '',
|
||||
// 'Github issue assignee change' => '',
|
||||
// 'Github issue label change' => '',
|
||||
// 'Create a task from an external provider' => '',
|
||||
// 'Change the assignee based on an external username' => '',
|
||||
// 'Change the category based on an external label' => '',
|
||||
// 'Reference' => '',
|
||||
// 'Reference: %s' => '',
|
||||
// 'Label' => '',
|
||||
// 'Database' => '',
|
||||
// 'About' => '',
|
||||
// 'Database driver:' => '',
|
||||
// 'Board settings' => '',
|
||||
// 'URL and token' => '',
|
||||
// 'Webhook settings' => '',
|
||||
// 'URL for task creation:' => '',
|
||||
// 'Reset token' => '',
|
||||
// 'API endpoint:' => '',
|
||||
// 'Refresh interval for private board' => '',
|
||||
// 'Refresh interval for public board' => '',
|
||||
// 'Task highlight period' => '',
|
||||
// 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '',
|
||||
// 'Frequency in second (60 seconds by default)' => '',
|
||||
// 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '',
|
||||
// 'Application URL' => '',
|
||||
// 'Example: http://example.kanboard.net/ (used by email notifications)' => '',
|
||||
// 'Token regenerated.' => '',
|
||||
// 'Date format' => '',
|
||||
// 'ISO format is always accepted, example: "%s" and "%s"' => '',
|
||||
// 'New private project' => '',
|
||||
// 'This project is private' => '',
|
||||
// 'Type here to create a new sub-task' => '',
|
||||
// 'Add' => '',
|
||||
// 'Estimated time: %s hours' => '',
|
||||
// 'Time spent: %s hours' => '',
|
||||
// 'Started on %B %e, %Y' => '',
|
||||
// 'Start date' => '',
|
||||
// 'Time estimated' => '',
|
||||
// 'There is nothing assigned to you.' => '',
|
||||
// 'My tasks' => '',
|
||||
// 'Activity stream' => '',
|
||||
// 'Dashboard' => '',
|
||||
// 'Confirmation' => '',
|
||||
// 'Allow everybody to access to this project' => '',
|
||||
// 'Everybody have access to this project.' => '',
|
||||
// 'Webhooks' => '',
|
||||
// 'API' => '',
|
||||
// 'Integration' => '',
|
||||
// 'Github webhooks' => '',
|
||||
// 'Help on Github webhooks' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
// 'Active swimlanes' => '',
|
||||
// 'Add a new swimlane' => '',
|
||||
// 'Change default swimlane' => '',
|
||||
// 'Default swimlane' => '',
|
||||
// 'Do you really want to remove this swimlane: "%s"?' => '',
|
||||
// 'Inactive swimlanes' => '',
|
||||
// 'Set project manager' => '',
|
||||
// 'Set project member' => '',
|
||||
// 'Remove a swimlane' => '',
|
||||
// 'Rename' => '',
|
||||
// 'Show default swimlane' => '',
|
||||
// 'Swimlane modification for the project "%s"' => '',
|
||||
// 'Swimlane not found.' => '',
|
||||
// 'Swimlane removed successfully.' => '',
|
||||
// 'Swimlanes' => '',
|
||||
// 'Swimlane updated successfully.' => '',
|
||||
// 'The default swimlane have been updated successfully.' => '',
|
||||
// 'Unable to create your swimlane.' => '',
|
||||
// 'Unable to remove this swimlane.' => '',
|
||||
// 'Unable to update this swimlane.' => '',
|
||||
// 'Your swimlane have been created successfully.' => '',
|
||||
// 'Example: "Bug, Feature Request, Improvement"' => '',
|
||||
'Choose an event' => 'Escoga un evento',
|
||||
'Github commit received' => 'Envío a Github recibido',
|
||||
'Github issue opened' => 'Problema en Github abierto',
|
||||
'Github issue closed' => 'Problema en Github cerrado',
|
||||
'Github issue reopened' => 'Problema en Github reabierto',
|
||||
'Github issue assignee change' => 'Cambio en signación de problema en Github',
|
||||
'Github issue label change' => 'Cambio en etiqueta del problema',
|
||||
'Create a task from an external provider' => 'Crear una tarea a partir de un proveedor externo',
|
||||
'Change the assignee based on an external username' => 'Cambiar la asignación basado en un nombre de usuario externo',
|
||||
'Change the category based on an external label' => 'Cambiar la categoría basado en una etiqueta externa',
|
||||
'Reference' => 'Referencia',
|
||||
'Reference: %s' => 'Referencia: %s',
|
||||
'Label' => 'Etiqueta',
|
||||
'Database' => 'Base de Datos',
|
||||
'About' => 'Acerca de',
|
||||
'Database driver:' => 'Driver de la base de datos',
|
||||
'Board settings' => 'Configuraciones del Tablero',
|
||||
'URL and token' => 'URL y token',
|
||||
'Webhook settings' => 'Configuraciones del Webhook',
|
||||
'URL for task creation:' => 'URL para la creación de tareas',
|
||||
'Reset token' => 'Resetear token',
|
||||
'API endpoint:' => 'Punto final del API',
|
||||
'Refresh interval for private board' => 'Intervalo de refrescamiento del tablero privado',
|
||||
'Refresh interval for public board' => 'Intervalo de refrescamiento del tablero público',
|
||||
'Task highlight period' => 'Periodo del realce de la tarea',
|
||||
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periodo (en segundos) para considerar que una tarea fué modificada recientemente (0 para deshabilitar, 2 días por defecto)',
|
||||
'Frequency in second (60 seconds by default)' => 'Frecuencia en segundos (60 segundos por defecto)',
|
||||
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frecuencia en segundos (0 para deshabilitar esta característica, 10 segundos por defecto)',
|
||||
'Application URL' => 'URL de la aplicación',
|
||||
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Ejemplo: http://ejemplo.kanboard.net/ (usado por las notificaciones de correo)',
|
||||
'Token regenerated.' => 'Token regenerado',
|
||||
'Date format' => 'Formato de la fecha',
|
||||
'ISO format is always accepted, example: "%s" and "%s"' => 'El formato ISO siempre es aceptado, ejemplo: "%s" y "%s"',
|
||||
'New private project' => 'Nuevo proyecto privado',
|
||||
'This project is private' => 'Este proyecto es privado',
|
||||
'Type here to create a new sub-task' => 'Escriba aquí para crear una nueva sub-tarea',
|
||||
'Add' => 'Añadir',
|
||||
'Estimated time: %s hours' => 'Tiempo estimado: % horas',
|
||||
'Time spent: %s hours' => 'Tiempo invertido: %s horas',
|
||||
'Started on %B %e, %Y' => 'Iniciado en %B %e, %Y',
|
||||
'Start date' => 'Fecha de inicio',
|
||||
'Time estimated' => 'Tiempo estimado',
|
||||
'There is nothing assigned to you.' => 'Esto no le está asignado',
|
||||
'My tasks' => 'Mis tareas',
|
||||
'Activity stream' => 'Flujo de actividad',
|
||||
'Dashboard' => 'Tablero',
|
||||
'Confirmation' => 'Confirmación',
|
||||
'Allow everybody to access to this project' => 'Permitir a cualquier acceder a este proyecto',
|
||||
'Everybody have access to this project.' => 'Cualquier tiene acceso a este proyecto',
|
||||
'Webhooks' => 'Webhooks',
|
||||
'API' => 'API',
|
||||
'Integration' => 'Integración',
|
||||
'Github webhooks' => 'Webhooks de Github',
|
||||
'Help on Github webhooks' => 'Ayuda con los Webhook de Github',
|
||||
'Create a comment from an external provider' => 'Crear un comentario a partir de un proveedor externo',
|
||||
'Github issue comment created' => 'Creado el comentario del problema en Github',
|
||||
'Configure' => 'Configurar',
|
||||
'Project management' => 'Administración del proyecto',
|
||||
'My projects' => 'Mis proyectos',
|
||||
'Columns' => 'Columnas',
|
||||
'Task' => 'Tarea',
|
||||
'Your are not member of any project.' => 'No es miembro de ningún proyecto',
|
||||
'Percentage' => 'Porcentaje',
|
||||
'Number of tasks' => 'Número de tareas',
|
||||
'Task distribution' => 'Distribución de tareas',
|
||||
'Reportings' => 'Reportes',
|
||||
'Task repartition for "%s"' => 'Repartición de tareas para "%s"',
|
||||
'Analytics' => 'Analítica',
|
||||
'Subtask' => 'Subtarea',
|
||||
'My subtasks' => 'Mis subtareas',
|
||||
'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',
|
||||
'Edit Project' => 'Editar Proyecto',
|
||||
'Github Issue' => 'Problema 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',
|
||||
'The status must be an integer' => 'El estado debe ser un entero',
|
||||
'The subtask id is required' => 'El id de la subtarea es requerido',
|
||||
'The subtask id must be an integer' => 'El id de la subtarea debe ser un entero',
|
||||
'The task id is required' => 'El id de la tarea es requerido',
|
||||
'The task id must be an integer' => 'El id de la tarea debe ser un entero',
|
||||
'The user id must be an integer' => 'El id del usuario debe ser un entero',
|
||||
'This value is required' => 'El valor es requerido',
|
||||
'This value must be numeric' => 'Este valor debe ser numérico',
|
||||
'Unable to create this task.' => 'Imposible crear esta tarea',
|
||||
'Cumulative flow diagram' => 'Diagrama de flujo acumulativo',
|
||||
'Cumulative flow diagram for "%s"' => 'Diagrama de flujo acumulativo para "%s"',
|
||||
'Daily project summary' => 'Sumario diario del proyecto',
|
||||
'Daily project summary export' => 'Exportar sumario diario del proyecto',
|
||||
'Daily project summary export for "%s"' => 'Exportar sumario diario del proyecto para "%s"',
|
||||
'Exports' => 'Exportar',
|
||||
'This export contains the number of tasks per column grouped per day.' => 'Esta exportación contiene el número de tereas por columna agrupada por día',
|
||||
'Nothing to preview...' => 'Nada que previsualizar...',
|
||||
'Preview' => 'Previsualizar',
|
||||
'Write' => 'Escribir',
|
||||
'Active swimlanes' => 'Carriles activos',
|
||||
'Add a new swimlane' => 'Añadir nuevo carril',
|
||||
'Change default swimlane' => 'Cambiar el carril por defecto',
|
||||
'Default swimlane' => 'Carril por defecto',
|
||||
'Do you really want to remove this swimlane: "%s"?' => '¿Realmente quiere remover este carril: "%s"?',
|
||||
'Inactive swimlanes' => 'Carriles inactivos',
|
||||
'Set project manager' => 'Asignar administrador del proyecto',
|
||||
'Set project member' => 'Asignar miembro del proyecto',
|
||||
'Remove a swimlane' => 'Remover un carril',
|
||||
'Rename' => 'Renombrar',
|
||||
'Show default swimlane' => 'Mostrar carril por defecto',
|
||||
'Swimlane modification for the project "%s"' => '',
|
||||
'Swimlane not found.' => 'Carril no encontrado',
|
||||
'Swimlane removed successfully.' => 'Carril removido correctamente',
|
||||
'Swimlanes' => 'Carriles',
|
||||
'Swimlane updated successfully.' => 'Carril actualizado correctamente',
|
||||
'The default swimlane have been updated successfully.' => 'El carril por defecto ha sido actualizado correctamente',
|
||||
'Unable to create your swimlane.' => 'Imposible crear su carril',
|
||||
'Unable to remove this swimlane.' => 'Imposible remover este carril',
|
||||
'Unable to update this swimlane.' => 'Imposible actualizar este carril',
|
||||
'Your swimlane have been created successfully.' => 'Su carril ha sido creado correctamente',
|
||||
'Example: "Bug, Feature Request, Improvement"' => 'Ejemplo: "Error, Solicitud de característica, Mejora',
|
||||
// 'Default categories for new projects (Comma-separated)' => '',
|
||||
// 'Gitlab commit received' => '',
|
||||
// 'Gitlab issue opened' => '',
|
||||
|
@ -642,10 +640,98 @@ return array(
|
|||
// 'Subtasks' => '',
|
||||
// 'Subtasks Export' => '',
|
||||
// 'Subtasks exportation for "%s"' => '',
|
||||
// 'Task Title' => '',
|
||||
// 'Untitled' => '',
|
||||
// 'Application default' => '',
|
||||
// 'Language:' => '',
|
||||
// 'Timezone:' => '',
|
||||
// 'Next' => '',
|
||||
'Task Title' => 'Título de la tarea',
|
||||
'Untitled' => 'Sin título',
|
||||
'Application default' => 'Predefinido de la aplicación',
|
||||
'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',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
'Moved to column %s' => 'Movido a columna %s',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'Kommentti päivitetty',
|
||||
'New comment posted by %s' => '%s lisäsi uuden kommentin',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
'I want to receive notifications only for those projects:' => 'Haluan vastaanottaa ilmoituksia ainoastaan näistä projekteista:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => 'Tehtävän saajan vaihto',
|
||||
'%s change the assignee of the task #%d to %s' => '%s vaihtoi tehtävän #%d saajaksi %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s vaihtoi tehtävän %s saajaksi %s',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'Uusi salasana käyttäjälle "%s"',
|
||||
'Choose an event' => 'Valitse toiminta',
|
||||
'Github commit received' => 'Github-kommitti vastaanotettu',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
// 'Application default' => '',
|
||||
// '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' => '',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
// 'Moved to column %s' => '',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -278,7 +278,7 @@ return array(
|
|||
'Remember Me' => 'Connexion automatique',
|
||||
'Creation date' => 'Date de création',
|
||||
'Filter by user' => 'Filtrer par utilisateur',
|
||||
'Filter by due date' => 'Filtrer par date d\'échéance',
|
||||
'Filter by due date' => 'Avec une date d\'échéance',
|
||||
'Everybody' => 'Tout le monde',
|
||||
'Open' => 'Ouvert',
|
||||
'Closed' => 'Fermé',
|
||||
|
@ -341,7 +341,7 @@ return array(
|
|||
'Add a comment' => 'Ajouter un commentaire',
|
||||
'Edit a comment' => 'Modifier un commentaire',
|
||||
'Summary' => 'Résumé',
|
||||
'Time tracking' => 'Gestion du temps',
|
||||
'Time tracking' => 'Suivi du temps',
|
||||
'Estimate:' => 'Estimation :',
|
||||
'Spent:' => 'Passé :',
|
||||
'Do you really want to remove this sub-task?' => 'Voulez-vous vraiment supprimer cette sous-tâche ?',
|
||||
|
@ -408,15 +408,15 @@ return array(
|
|||
'Comment updated' => 'Commentaire ajouté',
|
||||
'New comment posted by %s' => 'Nouveau commentaire ajouté par « %s »',
|
||||
'List of due tasks for the project "%s"' => 'Liste des tâches expirées pour le projet « %s »',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Pièce-jointe] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Nouveau commentaire] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Commentaire mis à jour] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Nouvelle sous-tâche] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Sous-tâche mise à jour] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Nouvelle tâche] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Tâche mise à jour] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Tâche fermée] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Tâche ouverte] %s (#%d)',
|
||||
'New attachment' => 'Nouveau document',
|
||||
'New comment' => 'Nouveau commentaire',
|
||||
'Comment updated' => 'Commentaire mis à jour',
|
||||
'New subtask' => 'Nouvelle sous-tâche',
|
||||
'Subtask updated' => 'Sous-tâche mise à jour',
|
||||
'New task' => 'Nouvelle tâche',
|
||||
'Task updated' => 'Tâche mise à jour',
|
||||
'Task closed' => 'Tâche fermée',
|
||||
'Task opened' => 'Tâche ouverte',
|
||||
'[%s][Due tasks]' => '[%s][Tâches expirées]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notification',
|
||||
'I want to receive notifications only for those projects:' => 'Je souhaite reçevoir les notifications uniquement pour les projets sélectionnés :',
|
||||
|
@ -500,9 +500,9 @@ return array(
|
|||
'Task assignee change' => 'Modification de la personne assignée sur une tâche',
|
||||
'%s change the assignee of the task #%d to %s' => '%s a changé la personne assignée sur la tâche n˚%d pour %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s a changé la personne assignée sur la tâche %s pour %s',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][Changement de colonne] %s (n˚%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][Changement de position] %s (n˚%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][Changement d\'assigné] %s (n˚%d)',
|
||||
'Column Change' => 'Changement de colonne',
|
||||
'Position Change' => 'Changement de position',
|
||||
'Assignee Change' => 'Changement d\'assigné',
|
||||
'New password for the user "%s"' => 'Nouveau mot de passe pour l\'utilisateur « %s »',
|
||||
'Choose an event' => 'Choisir un événement',
|
||||
'Github commit received' => '« Commit » reçu via Github',
|
||||
|
@ -647,6 +647,93 @@ return array(
|
|||
'Application default' => 'Valeur par défaut de l\'application',
|
||||
'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',
|
||||
'Add a comment logging moving the task between columns' => 'Ajouter un commentaire de log lorsqu\'une tâche est déplacée dans une autre colonne',
|
||||
'Moved to column %s' => 'Tâche déplacée à la colonne %s',
|
||||
'Change description' => 'Changer la description',
|
||||
'User dashboard' => 'Tableau de bord de l\'utilisateur',
|
||||
'Allow only one subtask in progress at the same time for a user' => 'Autoriser une seule sous-tâche en progrès en même temps pour un utilisateur',
|
||||
'Edit column "%s"' => 'Modifier la colonne « %s »',
|
||||
'Enable time tracking for subtasks' => 'Activer la feuille de temps pour les sous-tâches',
|
||||
'Select the new status of the subtask: "%s"' => 'Selectionnez le nouveau statut de la sous-tâche : « %s »',
|
||||
'Subtask timesheet' => 'Feuille de temps des sous-tâches',
|
||||
'There is nothing to show.' => 'Il n\'y a rien à montrer.',
|
||||
'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',
|
||||
'Bitbucket commit received' => '« Commit » reçu via Bitbucket',
|
||||
'Bitbucket webhooks' => 'Webhook Bitbucket',
|
||||
'Help on Bitbucket webhooks' => 'Aide sur les webhooks Bitbucket',
|
||||
'Start' => 'Début',
|
||||
'End' => 'Fin',
|
||||
'Task age in days' => 'Age de la tâche en jours',
|
||||
'Days in this column' => 'Jours dans cette colonne',
|
||||
'%dd' => '%dj',
|
||||
'Add a link' => 'Ajouter un lien',
|
||||
'Add a new link' => 'Ajouter un nouveau lien',
|
||||
'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien : « %s » ?',
|
||||
'Do you really want to remove this link with task #%d?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche n°%d ?',
|
||||
'Field required' => 'Champ obligatoire',
|
||||
'Link added successfully.' => 'Lien créé avec succès.',
|
||||
'Link updated successfully.' => 'Lien mis à jour avec succès.',
|
||||
'Link removed successfully.' => 'Lien supprimé avec succès.',
|
||||
'Link labels' => 'Libellé des liens',
|
||||
'Link modification' => 'Modification d\'un lien',
|
||||
'Links' => 'Liens',
|
||||
'Link settings' => 'Paramètres des liens',
|
||||
'Opposite label' => 'Nom du libellé opposé',
|
||||
'Remove a link' => 'Supprimer un lien',
|
||||
'Task\'s links' => 'Liens des tâches',
|
||||
'The labels must be different' => 'Les libellés doivent être différents',
|
||||
'There is no link.' => 'Il n\'y a aucun lien.',
|
||||
'This label must be unique' => 'Ce libellé doit être unique',
|
||||
'Unable to create your link.' => 'Impossible d\'ajouter ce lien.',
|
||||
'Unable to update your link.' => 'Impossible de mettre à jour ce lien.',
|
||||
'Unable to remove this link.' => 'Impossible de supprimer ce lien.',
|
||||
'relates to' => 'est liée à',
|
||||
'blocks' => 'bloque',
|
||||
'is blocked by' => 'est bloquée par',
|
||||
'duplicates' => 'duplique',
|
||||
'is duplicated by' => 'est dupliquée par',
|
||||
'is a child of' => 'est un enfant de',
|
||||
'is a parent of' => 'est un parent de',
|
||||
'targets milestone' => 'vise l\'étape importante',
|
||||
'is a milestone of' => 'est une étape importante de',
|
||||
'fixes' => 'corrige',
|
||||
'is fixed by' => 'est corrigée par',
|
||||
'This task' => 'Cette tâche',
|
||||
'<1h' => '<1h',
|
||||
'%dh' => '%dh',
|
||||
'%b %e' => '%e %b',
|
||||
'Expand tasks' => 'Déplier les tâches',
|
||||
'Collapse tasks' => 'Replier les tâches',
|
||||
'Expand/collapse tasks' => 'Plier/déplier les tâches',
|
||||
'Close dialog box' => 'Fermer une boite de dialogue',
|
||||
'Submit a form' => 'Enregistrer un formulaire',
|
||||
'Board view' => 'Page du tableau',
|
||||
'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',
|
||||
);
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'None' => 'Semelyik',
|
||||
'None' => 'Nincs',
|
||||
'edit' => 'szerkesztés',
|
||||
'Edit' => 'Szerkesztés',
|
||||
'remove' => 'eltávolít',
|
||||
'Remove' => 'Eltávolít',
|
||||
'remove' => 'eltávolítás',
|
||||
'Remove' => 'Eltávolítás',
|
||||
'Update' => 'Frissítés',
|
||||
'Yes' => 'Igen',
|
||||
'No' => 'Nincs',
|
||||
'cancel' => 'mégsem',
|
||||
'No' => 'Nem',
|
||||
'cancel' => 'Mégsem',
|
||||
'or' => 'vagy',
|
||||
'Yellow' => 'sárga',
|
||||
'Blue' => 'kék',
|
||||
'Green' => 'zöld',
|
||||
'Purple' => 'ibolya',
|
||||
'Red' => 'piros',
|
||||
'Orange' => 'narancs',
|
||||
'Grey' => 'szürke',
|
||||
'Yellow' => 'Sárga',
|
||||
'Blue' => 'Kék',
|
||||
'Green' => 'Zöld',
|
||||
'Purple' => 'Lila',
|
||||
'Red' => 'Piros',
|
||||
'Orange' => 'Narancs',
|
||||
'Grey' => 'Szürke',
|
||||
'Save' => 'Mentés',
|
||||
'Login' => 'Bejelentkezés',
|
||||
'Official website:' => 'Hivatalos honlap:',
|
||||
|
@ -25,7 +25,7 @@ return array(
|
|||
'View this task' => 'Feladat megtekintése',
|
||||
'Remove user' => 'Felhasználó törlése',
|
||||
'Do you really want to remove this user: "%s"?' => 'Tényleg törli ezt a felhasználót: "%s"?',
|
||||
'New user' => 'új felhasználó',
|
||||
'New user' => 'Új felhasználó',
|
||||
'All users' => 'Minden felhasználó',
|
||||
'Username' => 'Felhasználónév',
|
||||
'Password' => 'Jelszó',
|
||||
|
@ -56,17 +56,17 @@ return array(
|
|||
'Active' => 'Aktív',
|
||||
'Column %d' => 'Oszlop %d',
|
||||
'Add this column' => 'Oszlop hozzáadása',
|
||||
'%d tasks on the board' => 'A táblán %d feladat',
|
||||
'%d tasks on the board' => '%d feladat a táblán',
|
||||
'%d tasks in total' => 'Összesen %d feladat',
|
||||
'Unable to update this board.' => 'Nem lehet frissíteni a táblát.',
|
||||
'Edit board' => 'Tábla szerkesztése',
|
||||
'Disable' => 'Letilt',
|
||||
'Enable' => 'Engedélyez',
|
||||
'Disable' => 'Letiltás',
|
||||
'Enable' => 'Engedélyezés',
|
||||
'New project' => 'Új projekt',
|
||||
'Do you really want to remove this project: "%s"?' => 'Valóban törölni akarja ezt a projektet: "%s"?',
|
||||
'Remove project' => 'Projekt törlése',
|
||||
'Boards' => 'Táblák',
|
||||
'Edit the board for "%s"' => 'Tábla szerkesztése "%s"',
|
||||
'Edit the board for "%s"' => 'Tábla szerkesztése: "%s"',
|
||||
'All projects' => 'Minden projekt',
|
||||
'Change columns' => 'Oszlop módosítása',
|
||||
'Add a new column' => 'Új oszlop',
|
||||
|
@ -92,7 +92,7 @@ return array(
|
|||
'(VACUUM command)' => '(VACUUM parancs)',
|
||||
'(Gzip compressed Sqlite file)' => '(Gzip tömörített SQLite fájl)',
|
||||
'User settings' => 'Felhasználói beállítások',
|
||||
'My default project:' => 'Alapértelmezett project:',
|
||||
'My default project:' => 'Alapértelmezett projekt: ',
|
||||
'Close a task' => 'Feladat lezárása',
|
||||
'Do you really want to close this task: "%s"?' => 'Tényleg le akarja zárni ezt a feladatot: "%s"?',
|
||||
'Edit a task' => 'Feladat módosítása',
|
||||
|
@ -100,17 +100,17 @@ return array(
|
|||
'Color' => 'Szín',
|
||||
'Assignee' => 'Felelős',
|
||||
'Create another task' => 'Új feladat létrehozása',
|
||||
'New task' => 'új feladat',
|
||||
'Open a task' => 'Feladat megnyitása',
|
||||
'New task' => 'Új feladat',
|
||||
'Open a task' => 'Feladat felnyitás',
|
||||
'Do you really want to open this task: "%s"?' => 'Tényleg meg akarja nyitni ezt a feladatot: "%s"?',
|
||||
'Back to the board' => 'Vissza a táblához',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Létrehozva: %Y.%m.%d %k:%M %p',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Létrehozva: %Y.%m.%d %H:%M',
|
||||
'There is nobody assigned' => 'Nincs felelős',
|
||||
'Column on the board:' => 'Tábla oszlopa:',
|
||||
'Status is open' => 'Állapot nyitva',
|
||||
'Status is closed' => 'Állapot zárva',
|
||||
'Close this task' => 'Feladat bezárása',
|
||||
'Open this task' => 'Feladat megnyitása',
|
||||
'Column on the board:' => 'Tábla oszlopa: ',
|
||||
'Status is open' => 'Nyitott állapot',
|
||||
'Status is closed' => 'Zárt állapot',
|
||||
'Close this task' => 'Feladat lezárása',
|
||||
'Open this task' => 'Feladat felnyitása',
|
||||
'There is no description.' => 'Nincs elérhető leírás.',
|
||||
'Add a new task' => 'Új feladat hozzáadása',
|
||||
'The username is required' => 'Felhasználói név szükséges',
|
||||
|
@ -133,37 +133,37 @@ return array(
|
|||
'The title is required' => 'A címet meg kell adni',
|
||||
'The language is required' => 'A nyelvet meg kell adni',
|
||||
'There is no active project, the first step is to create a new project.' => 'Nincs aktív projekt. Először létre kell hozni egy projektet.',
|
||||
'Settings saved successfully.' => 'A beállítások sikeresen mentve.',
|
||||
'Unable to save your settings.' => 'Beállítások mentése nem sikerült.',
|
||||
'Settings saved successfully.' => 'A beállítások mentése sikeres.',
|
||||
'Unable to save your settings.' => 'A beállítások mentése sikertelen.',
|
||||
'Database optimization done.' => 'Adatbázis optimalizálás kész.',
|
||||
'Your project have been created successfully.' => 'A projekt sikeresen elkészült.',
|
||||
'Unable to create your project.' => 'Projekt létrehozása nem sikerült.',
|
||||
'Project updated successfully.' => 'Projekt sikeres frissítve.',
|
||||
'Unable to update this project.' => 'Projekt frissítése nem sikerült.',
|
||||
'Unable to remove this project.' => 'Projekt törlése nem sikerült.',
|
||||
'Your project have been created successfully.' => 'Projekt sikeresen létrehozva',
|
||||
'Unable to create your project.' => 'Projekt létrehozása sikertelen.',
|
||||
'Project updated successfully.' => 'Projekt frissítése sikeres.',
|
||||
'Unable to update this project.' => 'Projekt frissítése sikertelen.',
|
||||
'Unable to remove this project.' => 'Projekt törlése sikertelen.',
|
||||
'Project removed successfully.' => 'Projekt sikeresen törölve.',
|
||||
'Project activated successfully.' => 'Projekt sikeresen aktiválta.',
|
||||
'Unable to activate this project.' => 'Projekt aktiválása nem sikerült.',
|
||||
'Project activated successfully.' => 'Projekt sikeresen aktiválva.',
|
||||
'Unable to activate this project.' => 'Projekt aktiválása sikertelen.',
|
||||
'Project disabled successfully.' => 'Projekt sikeresen letiltva.',
|
||||
'Unable to disable this project.' => 'Projekt letiltása nem sikerült.',
|
||||
'Unable to open this task.' => 'A feladat megnyitása nem sikerült.',
|
||||
'Unable to disable this project.' => 'Projekt letiltása sikertelen.',
|
||||
'Unable to open this task.' => 'A feladat felnyitása nem sikerült.',
|
||||
'Task opened successfully.' => 'Feladat sikeresen megnyitva .',
|
||||
'Unable to close this task.' => 'A feladat lezárása nem sikerült.',
|
||||
'Unable to close this task.' => 'A feladat lezárása sikertelen.',
|
||||
'Task closed successfully.' => 'Feladat sikeresen lezárva.',
|
||||
'Unable to update your task.' => 'A feladat frissítése nem sikerült.',
|
||||
'Unable to update your task.' => 'Feladat frissítése sikertelen.',
|
||||
'Task updated successfully.' => 'Feladat sikeresen frissítve.',
|
||||
'Unable to create your task.' => 'A feladat létrehozása nem sikerült.',
|
||||
'Unable to create your task.' => 'Feladat létrehozása sikertelen.',
|
||||
'Task created successfully.' => 'Feladat sikeresen létrehozva.',
|
||||
'User created successfully.' => 'Felhasználó létrehozva .',
|
||||
'Unable to create your user.' => 'Felhasználó létrehozása nem sikerült.',
|
||||
'User created successfully.' => 'Felhasználó létrehozva.',
|
||||
'Unable to create your user.' => 'Felhasználó létrehozása sikertelen.',
|
||||
'User updated successfully.' => 'Felhasználó sikeresen frissítve.',
|
||||
'Unable to update your user.' => 'Felhasználó frissítése nem sikerült.',
|
||||
'Unable to update your user.' => 'Felhasználó frissítése sikertelen.',
|
||||
'User removed successfully.' => 'Felhasználó sikeresen törölve.',
|
||||
'Unable to remove this user.' => 'Felhasználó törlése nem sikerült.',
|
||||
'Unable to remove this user.' => 'Felhasználó törlése sikertelen.',
|
||||
'Board updated successfully.' => 'Tábla sikeresen frissítve.',
|
||||
'Ready' => 'Kész',
|
||||
'Ready' => 'Előkészítés',
|
||||
'Backlog' => 'Napló',
|
||||
'Work in progress' => 'Dolgozom',
|
||||
'Work in progress' => 'Folyamatban',
|
||||
'Done' => 'Kész',
|
||||
'Application version:' => 'Alkalmazás verzió:',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Elkészült %Y.%m.%d %H:%M ..',
|
||||
|
@ -174,7 +174,7 @@ return array(
|
|||
'No task' => 'Nincs feladat',
|
||||
'Completed tasks' => 'Elvégzett feladatok',
|
||||
'List of projects' => 'Projektek listája',
|
||||
'Completed tasks for "%s"' => 'Elvégzett feladatok "%s"',
|
||||
'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',
|
||||
|
@ -213,14 +213,14 @@ return array(
|
|||
'Invalid date' => 'Érvénytelen dátum',
|
||||
'Must be done before %B %e, %Y' => 'Kész kell lennie %Y.%m.%d előtt',
|
||||
'%B %e, %Y' => '%Y.%m.%d',
|
||||
// '%b %e, %Y' => '',
|
||||
'%b %e, %Y' => '%Y.%m.%d',
|
||||
'Automatic actions' => 'Automatikus intézkedések',
|
||||
'Your automatic action have been created successfully.' => 'Az automatikus intézkedés sikeresen elkészült.',
|
||||
'Unable to create your automatic action.' => 'Automatikus intézkedés létrehozása nem lehetséges.',
|
||||
'Remove an action' => 'Intézkedés törlése',
|
||||
'Unable to remove this action.' => 'Intézkedés törlése nem lehetséges.',
|
||||
'Action removed successfully.' => 'Intézkedés sikeresen törölve.',
|
||||
'Automatic actions for the project "%s"' => 'Automatikus intézkedések a projektben "%s"',
|
||||
'Automatic actions for the project "%s"' => 'Automatikus intézkedések a projektben: "%s"',
|
||||
'Defined actions' => 'Intézkedések',
|
||||
'Add an action' => 'Intézkedés létrehozása',
|
||||
'Event name' => 'Esemény neve',
|
||||
|
@ -242,15 +242,15 @@ return array(
|
|||
'Move a task to another position in the same column' => 'Feladat mozgatása oszlopon belül',
|
||||
'Task modification' => 'Feladat módosítása',
|
||||
'Task creation' => 'Feladat létrehozása',
|
||||
'Open a closed task' => 'Lezárt feladat megnyitása',
|
||||
'Open a closed task' => 'Lezárt feladat felnyitása',
|
||||
'Closing a task' => 'Feladat lezárása',
|
||||
'Assign a color to a specific user' => 'Szín hozzárendelése a felhasználóhoz',
|
||||
'Column title' => 'Oszlopfejléc',
|
||||
'Position' => 'Pozíció',
|
||||
'Move Up' => 'Fel',
|
||||
'Move Down' => 'Le',
|
||||
'Duplicate to another project' => 'Másold egy másik projektbe',
|
||||
'Duplicate' => 'Másolat',
|
||||
'Duplicate to another project' => 'Másolás másik projektbe',
|
||||
'Duplicate' => 'Másolás',
|
||||
'link' => 'link',
|
||||
'Update this comment' => 'Hozzászólás frissítése',
|
||||
'Comment updated successfully.' => 'Megjegyzés sikeresen frissítve.',
|
||||
|
@ -261,7 +261,7 @@ return array(
|
|||
'Do you really want to remove this comment?' => 'Valóban törölni szeretné ezt a megjegyzést?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Csak a rendszergazdák és a megjegyzés létrehozója férhet hozzá az oldalhoz.',
|
||||
'Details' => 'Részletek',
|
||||
'Current password for the user "%s"' => 'Felhasználó jelenlegi jelszava "%s"',
|
||||
'Current password for the user "%s"' => 'Felhasználó jelenlegi jelszava: "%s"',
|
||||
'The current password is required' => 'A jelenlegi jelszót meg kell adni',
|
||||
'Wrong password' => 'Hibás jelszó',
|
||||
'Reset all tokens' => 'Reseteld az összes tokent',
|
||||
|
@ -279,14 +279,14 @@ return array(
|
|||
'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' => 'Mindenki',
|
||||
'Everybody' => 'Minden felhasználó',
|
||||
'Open' => 'Nyitott',
|
||||
'Closed' => 'Lezárt',
|
||||
'Search' => 'Keres',
|
||||
'Nothing found.' => 'Semmit sem találtam.',
|
||||
'Search in the project "%s"' => 'Keresés a projektben "%s"',
|
||||
'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',
|
||||
'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',
|
||||
|
@ -302,7 +302,7 @@ return array(
|
|||
'Login with my Google Account' => 'Jelentkezzen be Google fiókkal',
|
||||
'Project not found.' => 'A projekt nem található.',
|
||||
'Task #%d' => 'Feladat #%d.',
|
||||
'Task removed successfully.' => 'Feladat törlése sikerült.',
|
||||
'Task removed successfully.' => 'Feladat sikeresen törölve.',
|
||||
'Unable to remove this task.' => 'A feladatot nem lehet törölni.',
|
||||
'Remove a task' => 'Feladat törlése',
|
||||
'Do you really want to remove this task: "%s"?' => 'Valóban törölni akarja ezt a feladatot: "%s"?',
|
||||
|
@ -313,7 +313,7 @@ return array(
|
|||
'Category:' => 'Kategória:',
|
||||
'Categories' => 'Kategóriák',
|
||||
'Category not found.' => 'Kategória nem található.',
|
||||
'Your category have been created successfully.' => 'Kategória sikeresen létrejött.',
|
||||
'Your category have been created successfully.' => 'Kategória sikeresen létrehozva.',
|
||||
'Unable to create your category.' => 'A kategória létrehozása nem lehetséges.',
|
||||
'Your category have been updated successfully.' => 'Kategória sikeresen frissítve.',
|
||||
'Unable to update your category.' => 'Kategória frissítése nem lehetséges.',
|
||||
|
@ -324,14 +324,14 @@ return array(
|
|||
'Category Name' => 'Kategória neve',
|
||||
'Categories for the project "%s"' => 'Projekt kategóriák "%s"',
|
||||
'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óriára',
|
||||
'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ő',
|
||||
'Remove a file' => 'Fájl törlése',
|
||||
'Unable to remove this file.' => 'Fájl törlése nem lehetséges.',
|
||||
'File removed successfully.' => 'A fájl törlése sikerült.',
|
||||
'File removed successfully.' => 'Fájl sikeresen törölve.',
|
||||
'Attach a document' => 'Fájl csatolása',
|
||||
'Do you really want to remove this file: "%s"?' => 'Valóban törölni akarja a fájlt: "%s"?',
|
||||
'open' => 'nyitott',
|
||||
|
@ -344,12 +344,12 @@ return array(
|
|||
'Time tracking' => 'Idő követés',
|
||||
'Estimate:' => 'Becsült:',
|
||||
'Spent:' => 'Eltöltött:',
|
||||
'Do you really want to remove this sub-task?' => 'Valóban törölni akarja ezt a részfeladatot "%s"?',
|
||||
'Do you really want to remove this sub-task?' => 'Valóban törölni akarja ezt a részfeladatot?',
|
||||
'Remaining:' => 'Hátralévő:',
|
||||
'hours' => 'óra',
|
||||
'spent' => 'eltöltött',
|
||||
'estimated' => 'becsült',
|
||||
'Sub-Tasks' => 'részfeladatok',
|
||||
'Sub-Tasks' => 'Részfeladatok',
|
||||
'Add a sub-task' => 'Részfeladat létrehozása',
|
||||
'Original estimate' => 'Eredeti időbecslés',
|
||||
'Create another sub-task' => 'További részfeladat létrehozása',
|
||||
|
@ -364,8 +364,8 @@ return array(
|
|||
'Sub-task updated successfully.' => 'Részfeladat sikeresen frissítve.',
|
||||
'Unable to update your sub-task.' => 'Részfeladat frissítése nem lehetséges.',
|
||||
'Unable to create your sub-task.' => 'Részfeladat létrehozása nem lehetséges.',
|
||||
'Sub-task added successfully.' => 'Részfeladat sikeresen létrejött.',
|
||||
'Maximum size: ' => 'Maximális méret:',
|
||||
'Sub-task added successfully.' => 'Részfeladat sikeresen létrehozva.',
|
||||
'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.',
|
||||
|
@ -377,9 +377,9 @@ return array(
|
|||
'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',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Utolsó módosítás: %Y.%m.%d %H:%M',
|
||||
'Tasks Export' => 'Feladatok exportálása',
|
||||
'Tasks exportation for "%s"' => 'Feladatok exportálása "%s" részére',
|
||||
'Tasks exportation for "%s"' => 'Feladatok exportálása: "%s"',
|
||||
'Start Date' => 'Kezdés dátuma',
|
||||
'End Date' => 'Befejezés dátuma',
|
||||
'Execute' => 'Végrehajt',
|
||||
|
@ -390,33 +390,31 @@ return array(
|
|||
'Webhook URL for task creation' => 'Webhook URL a feladat létrehozásakor',
|
||||
'Webhook URL for task modification' => 'Webhook URL a feladatot módosításakor',
|
||||
'Clone' => 'Másolat',
|
||||
'Clone Project' => 'Projekt megkettőzése',
|
||||
'Project cloned successfully.' => 'A projekt sikeresen megkettőzve.',
|
||||
'Unable to clone this project.' => 'Projekt megkettőzése nem sikerült.',
|
||||
'Clone Project' => 'Projekt másolása',
|
||||
'Project cloned successfully.' => 'A projekt másolása sikeres',
|
||||
'Unable to clone this project.' => 'A projekt másolása sikertelen.',
|
||||
'Email notifications' => 'E-mail értesítések',
|
||||
'Enable email notifications' => 'Engedélyezze az e-mail értesítéseket',
|
||||
'Enable email notifications' => 'E-mail értesítések engedélyezése',
|
||||
'Task position:' => 'Feladat helye:',
|
||||
'The task #%d have been opened.' => 'Feladat #%d megnyitva.',
|
||||
'The task #%d have been closed.' => 'Feladat #%d lezárva.',
|
||||
'Sub-task updated' => 'Részfeladat frissítve',
|
||||
'Title:' => 'Cím',
|
||||
'Status:' => 'Állapot',
|
||||
'Status:' => 'Állapot:',
|
||||
'Assignee:' => 'Felelős:',
|
||||
'Time tracking:' => 'Idő követés:',
|
||||
'New sub-task' => 'Új részfeladat',
|
||||
'New attachment added "%s"' => 'Új melléklet "%s" hozzáadva.',
|
||||
'Comment updated' => 'Megjegyzés frissítve',
|
||||
'New comment posted by %s' => 'Új megjegyzés %s',
|
||||
'List of due tasks for the project "%s"' => 'Projekt esedékes feladatai "%s"',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s] [Új csatolmány] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s] [Új hozzászólás] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s] [Megjegyzés frissítve] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s] [Új részfeladat] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s] [Részfeladat frissítve] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s] [Új feladat] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s] [Feladat frissítve] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s] [Feladat lezárva]%s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s] [Feladat megnyitva] %s (#%d)',
|
||||
'List of due tasks for the project "%s"' => 'Projekt esedékes feladatai: "%s"',
|
||||
'New attachment' => 'Új melléklet',
|
||||
'New comment' => 'Új megjegyzés',
|
||||
'New subtask' => 'Új részfeladat',
|
||||
'Subtask updated' => 'Részfeladat frissítve',
|
||||
'Task updated' => 'Feladat frissítve',
|
||||
'Task closed' => 'Feladat lezárva',
|
||||
'Task opened' => 'Feladat megnyitva',
|
||||
'[%s][Due tasks]' => '[%s] [Esedékes feladatok]',
|
||||
'[Kanboard] Notification' => '[Kanboard] értesítés',
|
||||
'I want to receive notifications only for those projects:' => 'Csak ezekről a projektekről kérek értesítést:',
|
||||
|
@ -434,27 +432,27 @@ return array(
|
|||
'Do you really want to duplicate this project: "%s"?' => 'Tényleg szeretné megkettőzni ezt a projektet: "%s"',
|
||||
'Do you really want to enable this project: "%s"?' => 'Tényleg szeretné engedélyezni ezt a projektet: "%s"',
|
||||
'Project activation' => 'Projekt aktiválás',
|
||||
'Move the task to another project' => 'Feladatot mozgatása másik projektbe',
|
||||
'Move to another project' => 'Másik projektbe',
|
||||
'Move the task to another project' => 'Feladat áthelyezése másik projektbe',
|
||||
'Move to another project' => 'Áthelyezés másik projektbe',
|
||||
'Do you really want to duplicate this task?' => 'Tényleg szeretné megkettőzni ezt a feladatot?',
|
||||
'Duplicate a task' => 'Feladat megkettőzése',
|
||||
'External accounts' => 'Külső fiókok',
|
||||
'Account type' => 'Fiók típus',
|
||||
'Account type' => 'Fiók típusa',
|
||||
'Local' => 'Helyi',
|
||||
'Remote' => 'Távoli',
|
||||
'Enabled' => 'Engedélyezve',
|
||||
'Disabled' => 'Letiltva',
|
||||
'Google account linked' => 'Google fiók összekapcsolva',
|
||||
'Github account linked' => 'GitHub fiók összekapcsolva',
|
||||
'Username:' => 'Felhasználónév',
|
||||
'Name:' => 'Név',
|
||||
'Email:' => 'E-mail',
|
||||
'Username:' => 'Felhasználónév:',
|
||||
'Name:' => 'Név:',
|
||||
'Email:' => 'E-mail:',
|
||||
'Default project:' => 'Alapértelmezett projekt:',
|
||||
'Notifications:' => 'Értesítések:',
|
||||
'Notifications' => 'Értesítések',
|
||||
'Group:' => 'Csoport:',
|
||||
'Regular user' => 'Default User',
|
||||
'Account type:' => 'Fiók típus:',
|
||||
'Account type:' => 'Fiók típusa:',
|
||||
'Edit profile' => 'Profil szerkesztése',
|
||||
'Change password' => 'Jelszó módosítása',
|
||||
'Password modification' => 'Jelszó módosítása',
|
||||
|
@ -464,9 +462,9 @@ return array(
|
|||
'Never connected.' => 'Sosem csatlakozva.',
|
||||
'No account linked.' => 'Nincs csatlakoztatott fiók.',
|
||||
'Account linked.' => 'Fiók csatlakoztatva.',
|
||||
'No external authentication enabled.' => 'Külső azonosítás nincs engedélyezve.',
|
||||
'Password modified successfully.' => 'Jelszó sikeresen módosítva.',
|
||||
'Unable to change the password.' => 'Jelszó módosítás sikertelen.',
|
||||
'No external authentication enabled.' => 'A külső azonosítás nincs engedélyezve.',
|
||||
'Password modified successfully.' => 'A jelszó sikeresen módosítva.',
|
||||
'Unable to change the password.' => 'A jelszó módosítása sikertelen.',
|
||||
'Change category for the task "%s"' => 'Feladat kategória módosítása "%s"',
|
||||
'Change category' => 'Kategória módosítása',
|
||||
'%s updated the task %s' => '%s frissítette a feladatot %s',
|
||||
|
@ -481,7 +479,7 @@ return array(
|
|||
'Not assigned, estimate of %sh' => 'Nincs kiosztva, becsült idő: %s óra',
|
||||
'%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' => '%s tevékenysége',
|
||||
'%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',
|
||||
|
@ -494,20 +492,20 @@ return array(
|
|||
'%s open the task #%d' => '%s megnyitotta a feladatot #%d',
|
||||
'%s moved the task #%d to the column "%s"' => '%s átmozgatta a feladatot #%d a "%s" oszlopba',
|
||||
'%s moved the task #%d to the position %d in the column "%s"' => '%s átmozgatta a feladatot #%d a %d pozícióba a "%s" oszlopban',
|
||||
'Activity' => 'Tevékenység',
|
||||
'Default values are "%s"' => 'Az alapértelmezett értékek "%s"',
|
||||
'Activity' => 'Tevékenységek',
|
||||
'Default values are "%s"' => 'Az alapértelmezett értékek: %s',
|
||||
'Default columns for new projects (Comma-separated)' => 'Alapértelmezett oszlopok az új projektekben (vesszővel elválasztva)',
|
||||
'Task assignee change' => 'Felelős módosítása',
|
||||
'%s change the assignee of the task #%d to %s' => '%s a felelőst módosította #%d %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s a felelőst %s módosította: %s',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s] [Oszlop módosítás] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s] [Pozíció módosítás] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s] [Felelős módosítás] %s (#%d)',
|
||||
'New password for the user "%s"' => 'Felhasználó új jelszava "%s"',
|
||||
'Column Change' => 'Oszlop változtatás',
|
||||
'Position Change' => 'Pozíció változtatás',
|
||||
'Assignee Change' => 'Felelős változtatá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 nyílt',
|
||||
'Github issue closed' => 'GitHub issue zárt',
|
||||
'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',
|
||||
|
@ -524,23 +522,23 @@ return array(
|
|||
'URL and token' => 'URL és tokenek',
|
||||
'Webhook settings' => 'Webhook beállítások',
|
||||
'URL for task creation:' => 'Feladat létrehozás URL:',
|
||||
'Reset token' => 'Reset token',
|
||||
'API endpoint:' => 'API endpoint:',
|
||||
'Reset token' => 'Token újragenerálása',
|
||||
'API endpoint:' => 'API végpont:',
|
||||
'Refresh interval for private board' => 'Privát táblák frissítési intervalluma',
|
||||
'Refresh interval for public board' => 'Nyilvános táblák frissítési intervalluma',
|
||||
'Task highlight period' => 'Feladat kiemelés időtartama',
|
||||
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Mennyi ideig tekintendő egy feladat "mostanában" módosítottnak (másodpercben) (0: funkció letiltva, alapértelmezés szerint 2 nap)',
|
||||
'Frequency in second (60 seconds by default)' => 'Infó másodpercben (alapértelmezett 60 másodperc)',
|
||||
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Infó másodpercben (0 funkció letiltva, alapértelmezés szerint 10 másodperc)',
|
||||
'Frequency in second (60 seconds by default)' => 'Gyakoriság másodpercben (alapértelmezetten 60 másodperc)',
|
||||
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Gyakoriság másodpercben (0 funkció letiltva, alapértelmezetten 10 másodperc)',
|
||||
'Application URL' => 'Alkalmazás URL',
|
||||
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Példa: http://example.kanboard.net/ (e-mail értesítőben)',
|
||||
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Példa: http://example.kanboard.net/ (e-mail értesítőben használt)',
|
||||
'Token regenerated.' => 'Token újragenerálva.',
|
||||
'Date format' => 'Dátum formátum',
|
||||
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formátum mindig elfogadott, pl: "%s" és "%s"',
|
||||
'New private project' => 'Új privát projekt',
|
||||
'This project is private' => 'Ez egy privát projekt',
|
||||
'Type here to create a new sub-task' => 'Ide írva létrehozhat egy új részfeladatot',
|
||||
'Add' => 'Hozzáad',
|
||||
'Add' => 'Hozzáadás',
|
||||
'Estimated time: %s hours' => 'Becsült idő: %s óra',
|
||||
'Time spent: %s hours' => 'Eltöltött idő: %s óra',
|
||||
'Started on %B %e, %Y' => 'Elkezdve: %Y.%m.%d',
|
||||
|
@ -549,9 +547,9 @@ return array(
|
|||
'There is nothing assigned to you.' => 'Nincs kiosztott feladat.',
|
||||
'My tasks' => 'Feladataim',
|
||||
'Activity stream' => 'Legutóbbi tevékenységek',
|
||||
'Dashboard' => 'Műszerfal',
|
||||
'Dashboard' => 'Vezérlőpult',
|
||||
'Confirmation' => 'Megerősítés',
|
||||
'Allow everybody to access to this project' => 'Engedélyezze a projekt elérését mindenkinek',
|
||||
'Allow everybody to access to this project' => 'A projekt elérése mindenkinek engedélyezett',
|
||||
'Everybody have access to this project.' => 'Mindenki elérheti a projektet',
|
||||
'Webhooks' => 'Webhook',
|
||||
'API' => 'API',
|
||||
|
@ -560,7 +558,7 @@ return array(
|
|||
'Help on Github webhooks' => 'Github Webhook súgó',
|
||||
'Create a comment from an external provider' => 'Megjegyzés létrehozása külső felhasználótól',
|
||||
'Github issue comment created' => 'Github issue megjegyzés létrehozva',
|
||||
'Configure' => 'Konfigurál',
|
||||
'Configure' => 'Beállítások',
|
||||
'Project management' => 'Projekt menedzsment',
|
||||
'My projects' => 'Projektjeim',
|
||||
'Columns' => 'Oszlopok',
|
||||
|
@ -570,12 +568,12 @@ return array(
|
|||
'Number of tasks' => 'A feladatok száma',
|
||||
'Task distribution' => 'Feladatelosztás',
|
||||
'Reportings' => 'Jelentések',
|
||||
'Task repartition for "%s"' => 'Feladat újraosztása "%s" számára',
|
||||
'Task repartition for "%s"' => 'Feladat újraosztása: %s',
|
||||
'Analytics' => 'Analitika',
|
||||
'Subtask' => 'Részfeladat',
|
||||
'My subtasks' => 'Részfeladataim',
|
||||
'User repartition' => 'Felhasználó újrafelosztás',
|
||||
'User repartition for "%s"' => 'Felhasználó újrafelosztás "%s" számára',
|
||||
'User repartition for "%s"' => 'Felhasználó újrafelosztás: %s',
|
||||
'Clone this project' => 'Projekt megkettőzése',
|
||||
'Column removed successfully.' => 'Oszlop sikeresen eltávolítva.',
|
||||
'Edit Project' => 'Projekt szerkesztése',
|
||||
|
@ -594,58 +592,146 @@ return array(
|
|||
'This value must be numeric' => 'Ez a mező csak szám lehet',
|
||||
'Unable to create this task.' => 'A feladat nem hozható létre,',
|
||||
'Cumulative flow diagram' => 'Kumulatív Flow Diagram',
|
||||
'Cumulative flow diagram for "%s"' => 'Kumulatív Flow Diagram "%s" számára',
|
||||
'Cumulative flow diagram for "%s"' => 'Kumulatív Flow Diagram: %s',
|
||||
'Daily project summary' => 'Napi projektösszefoglaló',
|
||||
'Daily project summary export' => 'Napi projektösszefoglaló exportálása',
|
||||
'Daily project summary export for "%s"' => 'Napi projektösszefoglaló exportálása "%s" számára',
|
||||
'Daily project summary export for "%s"' => 'Napi projektösszefoglaló exportálása: %s',
|
||||
'Exports' => 'Exportálások',
|
||||
'This export contains the number of tasks per column grouped per day.' => 'Ez az export tartalmazza a feladatok számát oszloponként összesítve, napokra lebontva.',
|
||||
'Nothing to preview...' => 'Nincs semmi az előnézetben ...',
|
||||
'Preview' => 'Előnézet',
|
||||
'Write' => 'Írd',
|
||||
// 'Active swimlanes' => '',
|
||||
// 'Add a new swimlane' => '',
|
||||
// 'Change default swimlane' => '',
|
||||
// 'Default swimlane' => '',
|
||||
// 'Do you really want to remove this swimlane: "%s"?' => '',
|
||||
// 'Inactive swimlanes' => '',
|
||||
// 'Set project manager' => '',
|
||||
// 'Set project member' => '',
|
||||
// 'Remove a swimlane' => '',
|
||||
// 'Rename' => '',
|
||||
// 'Show default swimlane' => '',
|
||||
// 'Swimlane modification for the project "%s"' => '',
|
||||
// 'Swimlane not found.' => '',
|
||||
// 'Swimlane removed successfully.' => '',
|
||||
// 'Swimlanes' => '',
|
||||
// 'Swimlane updated successfully.' => '',
|
||||
// 'The default swimlane have been updated successfully.' => '',
|
||||
// 'Unable to create your swimlane.' => '',
|
||||
// 'Unable to remove this swimlane.' => '',
|
||||
// 'Unable to update this swimlane.' => '',
|
||||
// 'Your swimlane have been created successfully.' => '',
|
||||
// 'Example: "Bug, Feature Request, Improvement"' => '',
|
||||
// 'Default categories for new projects (Comma-separated)' => '',
|
||||
// 'Gitlab commit received' => '',
|
||||
// 'Gitlab issue opened' => '',
|
||||
// 'Gitlab issue closed' => '',
|
||||
// 'Gitlab webhooks' => '',
|
||||
// 'Help on Gitlab webhooks' => '',
|
||||
// 'Integrations' => '',
|
||||
// 'Integration with third-party services' => '',
|
||||
// 'Role for this project' => '',
|
||||
// 'Project manager' => '',
|
||||
// 'Project member' => '',
|
||||
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
|
||||
// 'Gitlab Issue' => '',
|
||||
// 'Subtask Id' => '',
|
||||
// 'Subtasks' => '',
|
||||
// 'Subtasks Export' => '',
|
||||
// 'Subtasks exportation for "%s"' => '',
|
||||
// 'Task Title' => '',
|
||||
// 'Untitled' => '',
|
||||
// 'Application default' => '',
|
||||
// 'Language:' => '',
|
||||
// 'Timezone:' => '',
|
||||
// 'Next' => '',
|
||||
'Write' => 'Szerkesztés',
|
||||
'Active swimlanes' => 'Aktív folyamatok',
|
||||
'Add a new swimlane' => 'Új folyamat',
|
||||
'Change default swimlane' => 'Alapértelmezett folyamat változtatás',
|
||||
'Default swimlane' => 'Alapértelmezett folyamat',
|
||||
'Do you really want to remove this swimlane: "%s"?' => 'Valóban törli a folyamatot:%s ?',
|
||||
'Inactive swimlanes' => 'Inaktív folyamatok',
|
||||
'Set project manager' => 'Beállítás projekt kezelőnek',
|
||||
'Set project member' => 'Beállítás projekt felhasználónak',
|
||||
'Remove a swimlane' => 'Folyamat törlés',
|
||||
'Rename' => 'Átnevezés',
|
||||
'Show default swimlane' => 'Alapértelmezett folyamat megjelenítése',
|
||||
'Swimlane modification for the project "%s"' => '%s projekt folyamatainak módosítása',
|
||||
'Swimlane not found.' => 'Folyamat nem található',
|
||||
'Swimlane removed successfully.' => 'Folyamat sikeresen törölve.',
|
||||
'Swimlanes' => 'Folyamatok',
|
||||
'Swimlane updated successfully.' => 'Folyamat sikeresn frissítve',
|
||||
'The default swimlane have been updated successfully.' => 'Az alapértelmezett folyamat sikeresen frissítve.',
|
||||
'Unable to create your swimlane.' => 'A folyamat létrehozása sikertelen.',
|
||||
'Unable to remove this swimlane.' => 'A folyamat törlése sikertelen.',
|
||||
'Unable to update this swimlane.' => 'A folyamat frissítése sikertelen.',
|
||||
'Your swimlane have been created successfully.' => 'A folyamat sikeresen létrehozva.',
|
||||
'Example: "Bug, Feature Request, Improvement"' => 'Például: Hiba, Új funkció, Fejlesztés',
|
||||
'Default categories for new projects (Comma-separated)' => 'Alapértelmezett kategóriák az új projektekben (Vesszővel elválasztva)',
|
||||
'Gitlab commit received' => 'Gitlab commit érkezett',
|
||||
'Gitlab issue opened' => 'Gitlab issue nyitás',
|
||||
'Gitlab issue closed' => 'Gitlab issue zárás',
|
||||
'Gitlab webhooks' => 'Gitlab webhooks',
|
||||
'Help on Gitlab webhooks' => 'Gitlab webhooks súgó',
|
||||
'Integrations' => 'Integráció',
|
||||
'Integration with third-party services' => 'Integráció harmadik féllel',
|
||||
'Role for this project' => 'Projekt szerepkör',
|
||||
'Project manager' => 'Projekt kezelő',
|
||||
'Project member' => 'Projekt felhasználó',
|
||||
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'A projekt kezelő képes megváltoztatni a projekt beállításait és több joggal rendelkezik mint az alap felhasználók.',
|
||||
'Gitlab Issue' => 'Gitlab issue',
|
||||
'Subtask Id' => 'Részfeladat id',
|
||||
'Subtasks' => 'Részfeladatok',
|
||||
'Subtasks Export' => 'Részfeladat exportálás',
|
||||
'Subtasks exportation for "%s"' => 'Részfeladatok exportálása: %s',
|
||||
'Task Title' => 'Feladat címe',
|
||||
'Untitled' => 'Névtelen',
|
||||
'Application default' => 'Alkalmazás alapértelmezett',
|
||||
'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',
|
||||
'Add a comment logging moving the task between columns' => 'Feladat oszlopok közötti mozgatását megjegyzésben feltüntetni',
|
||||
'Moved to column %s' => '%s oszlopba áthelyezve',
|
||||
'Change description' => 'Leírás szerkesztés',
|
||||
'User dashboard' => 'Felhasználói vezérlőpult',
|
||||
'Allow only one subtask in progress at the same time for a user' => 'Egyszerre csak egy folyamatban levő részfeladat engedélyezése a felhasználóknak',
|
||||
'Edit column "%s"' => 'Oszlop szerkesztés: %s',
|
||||
'Enable time tracking for subtasks' => 'Idő követés engedélyezése a részfeladatokhoz',
|
||||
'Select the new status of the subtask: "%s"' => 'Részfeladat állapot változtatás: %s',
|
||||
'Subtask timesheet' => 'Részfeladat idővonal',
|
||||
'There is nothing to show.' => 'Nincs megjelenítendő adat.',
|
||||
'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é duplikálni?',
|
||||
'Change dashboard view' => 'Vezérlőpult megjeleníté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',
|
||||
'Bitbucket commit received' => 'Bitbucket commit érkezett',
|
||||
'Bitbucket webhooks' => 'Bitbucket webhooks',
|
||||
'Help on Bitbucket webhooks' => 'Bitbucket webhooks súgó',
|
||||
'Start' => 'Kezdet',
|
||||
'End' => 'Vég',
|
||||
'Task age in days' => 'Feladat életkora napokban',
|
||||
'Days in this column' => 'Napok ebben az oszlopban',
|
||||
'%dd' => '%dd',
|
||||
'Add a link' => 'Hivatkozás hozzáadása',
|
||||
'Add a new link' => 'Új hivatkozás hozzáadása',
|
||||
'Do you really want to remove this link: "%s"?' => 'Biztos törölni akarja a hivatkozást: "%s"?',
|
||||
'Do you really want to remove this link with task #%d?' => 'Biztos törölni akarja a(z) #%s. feladatra mutató hivatkozást?',
|
||||
'Field required' => 'Kötelező mező',
|
||||
'Link added successfully.' => 'Hivatkozás sikeresen létrehozva.',
|
||||
'Link updated successfully.' => 'Hivatkozás sikeresen frissítve.',
|
||||
'Link removed successfully.' => 'Hivatkozás sikeresen törölve.',
|
||||
'Link labels' => 'Hivatkozás címkék',
|
||||
'Link modification' => 'Hivatkozás módosítás',
|
||||
'Links' => 'Hivatkozások',
|
||||
'Link settings' => 'Hivatkozás beállítasok',
|
||||
'Opposite label' => 'Ellenekező címke',
|
||||
'Remove a link' => 'Hivatkozás törlése',
|
||||
'Task\'s links' => 'Feladat hivatkozások',
|
||||
'The labels must be different' => 'A címkék nem lehetnek azonosak',
|
||||
'There is no link.' => 'Nincs hivatkozás.',
|
||||
'This label must be unique' => 'A címkének egyedinek kell lennie.',
|
||||
'Unable to create your link.' => 'Hivatkozás létrehozása sikertelen.',
|
||||
'Unable to update your link.' => 'Hivatkozás frissítése sikertelen.',
|
||||
'Unable to remove this link.' => 'Hivatkozás törlése sikertelen.',
|
||||
'relates to' => 'hozzá tartozik:',
|
||||
'blocks' => 'letiltva:',
|
||||
'is blocked by' => 'letitoltta:',
|
||||
'duplicates' => 'eredeti:',
|
||||
'is duplicated by' => 'másolat:',
|
||||
'is a child of' => 'szülője:',
|
||||
'is a parent of' => 'gyermeke:',
|
||||
'targets milestone' => 'megcélzott mérföldkő:',
|
||||
'is a milestone of' => 'ehhez a mérföldkőhöz tartozik:',
|
||||
'fixes' => 'javítás:',
|
||||
'is fixed by' => 'javította:',
|
||||
'This task' => 'Ez a feladat',
|
||||
'<1h' => '<1ó',
|
||||
'%dh' => '%dó',
|
||||
'%b %e' => '%b %e',
|
||||
'Expand tasks' => 'Feladatok lenyitása',
|
||||
'Collapse tasks' => 'Feladatok összecsukása',
|
||||
'Expand/collapse tasks' => 'Feladatok lenyitása/összecsukása',
|
||||
'Close dialog box' => 'Ablak bezárása',
|
||||
'Submit a form' => 'Űrlap beküldése',
|
||||
'Board view' => 'Tábla nézet',
|
||||
'Keyboard shortcuts' => 'Billentyű kombináció',
|
||||
'Open board switcher' => 'Tábla választó lenyitása',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'Commento aggiornato',
|
||||
'New comment posted by %s' => 'Nuovo commento aggiunto da « %s »',
|
||||
'List of due tasks for the project "%s"' => 'Lista dei compiti scaduti per il progetto « %s »',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Nuovo allegato] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Nuovo commento] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Commento aggiornato] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Nuovo sotto-compito] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Sotto-compito aggiornato] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Nuovo compito] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Compito aggiornato] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Compito chiuso] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Compito aperto] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][Compiti scaduti]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notifica',
|
||||
'I want to receive notifications only for those projects:' => 'Vorrei ricevere le notifiche solo da questi progetti:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d to %s' => '',
|
||||
// '%s changed the assignee of the task %s to %s' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
// 'Choose an event' => '',
|
||||
// 'Github commit received' => '',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
// 'Application default' => '',
|
||||
// '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' => '',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
// 'Moved to column %s' => '',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'コメントが更新されました',
|
||||
'New comment posted by %s' => '「%s」の新しいコメントが追加されました',
|
||||
'List of due tasks for the project "%s"' => 'プロジェクト「%s」の期限切れのタスク',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][新規添付ファイル] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][新規コメント] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][コメント更新] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][新規サブタスク] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][サブタスク更新] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][新規タスク] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][タスク更新] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][タスククローズ] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][タスクオープン] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][タスク期限切れ]',
|
||||
'[Kanboard] Notification' => '[Kanboard] 通知',
|
||||
'I want to receive notifications only for those projects:' => '以下のプロジェクトにのみ通知を受け取る:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => '担当者の変更',
|
||||
'%s change the assignee of the task #%d to %s' => '%s がタスク #%d の担当を %s に変更しました',
|
||||
'%s changed the assignee of the task %s to %s' => '%s がタスク %s の担当を %s に変更しました',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][カラムの変更] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][位置の変更] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][担当者変更] %s (#%d)',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'ユーザ「%s」の新しいパスワード',
|
||||
'Choose an event' => 'イベントの選択',
|
||||
'Github commit received' => 'Github のコミットを受け取った',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
// 'Application default' => '',
|
||||
// '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' => '',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
// 'Moved to column %s' => '',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -84,7 +84,7 @@ return array(
|
|||
'Application settings' => 'Ustawienia aplikacji',
|
||||
'Language' => 'Język',
|
||||
'Webhook token:' => 'Token :',
|
||||
// 'API token:' => '',
|
||||
'API token:' => 'Token dla API',
|
||||
'More information' => 'Więcej informacji',
|
||||
'Database size:' => 'Rozmiar bazy danych :',
|
||||
'Download the database' => 'Pobierz bazę danych',
|
||||
|
@ -170,7 +170,7 @@ return array(
|
|||
'%B %e, %Y at %k:%M %p' => '%e %B %Y o %k:%M',
|
||||
'Date created' => 'Data utworzenia',
|
||||
'Date completed' => 'Data zakończenia',
|
||||
'Id' => 'Ident',
|
||||
'Id' => 'Id',
|
||||
'No task' => 'Brak zadań',
|
||||
'Completed tasks' => 'Ukończone zadania',
|
||||
'List of projects' => 'Lista projektów',
|
||||
|
@ -187,7 +187,7 @@ return array(
|
|||
'Complexity' => 'Poziom trudności',
|
||||
'limit' => 'limit',
|
||||
'Task limit' => 'Limit zadań',
|
||||
// 'Task count' => '',
|
||||
'Task count' => 'Liczba zadań',
|
||||
'This value must be greater than %d' => 'Wartość musi być większa niż %d',
|
||||
'Edit project access list' => 'Edycja list dostępu dla projektu',
|
||||
'Edit users access' => 'Edytuj dostęp',
|
||||
|
@ -197,14 +197,14 @@ return array(
|
|||
'Revoke' => 'Odbierz dostęp',
|
||||
'List of authorized users' => 'Lista użytkowników mających dostęp',
|
||||
'User' => 'Użytkownik',
|
||||
// 'Nobody have access to this project.' => '',
|
||||
'Nobody have access to this project.' => 'Żaden użytkownik nie ma dostępu do tego projektu',
|
||||
'You are not allowed to access to this project.' => 'Nie masz dostępu do tego projektu.',
|
||||
'Comments' => 'Komentarze',
|
||||
'Post comment' => 'Dodaj komentarz',
|
||||
'Write your text in Markdown' => 'Możesz użyć Markdown',
|
||||
'Leave a comment' => 'Zostaw komentarz',
|
||||
'Comment is required' => 'Komentarz jest wymagany',
|
||||
// 'Leave a description' => '',
|
||||
'Leave a description' => 'Dodaj opis',
|
||||
'Comment added successfully.' => 'Komentarz dodany',
|
||||
'Unable to create your comment.' => 'Nie udało się dodać komentarza',
|
||||
'The description is required' => 'Opis jest wymagany',
|
||||
|
@ -277,375 +277,461 @@ return array(
|
|||
'Expiration date' => 'Data zakończenia',
|
||||
'Remember Me' => 'Pamiętaj mnie',
|
||||
'Creation date' => 'Data utworzenia',
|
||||
// 'Filter by user' => '',
|
||||
// 'Filter by due date' => '',
|
||||
// 'Everybody' => '',
|
||||
// 'Open' => '',
|
||||
// 'Closed' => '',
|
||||
// 'Search' => '',
|
||||
// 'Nothing found.' => '',
|
||||
// 'Search in the project "%s"' => '',
|
||||
// 'Due date' => '',
|
||||
// 'Others formats accepted: %s and %s' => '',
|
||||
'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 comment' => '',
|
||||
// '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.' => '',
|
||||
// 'Email' => '',
|
||||
// 'Link my Google Account' => '',
|
||||
// 'Unlink my Google Account' => '',
|
||||
// 'Login with my Google Account' => '',
|
||||
// 'Project not found.' => '',
|
||||
// 'Task #%d' => '',
|
||||
// 'Task removed successfully.' => '',
|
||||
// 'Unable to remove this task.' => '',
|
||||
// 'Remove a task' => '',
|
||||
// 'Do you really want to remove this task: "%s"?' => '',
|
||||
// 'Assign automatically a color based on a category' => '',
|
||||
// 'Assign automatically a category based on a color' => '',
|
||||
// 'Task creation or modification' => '',
|
||||
// 'Category' => '',
|
||||
// 'Category:' => '',
|
||||
// 'Categories' => '',
|
||||
// 'Category not found.' => '',
|
||||
// 'Your category have been created successfully.' => '',
|
||||
// 'Unable to create your category.' => '',
|
||||
// 'Your category have been updated successfully.' => '',
|
||||
// 'Unable to update your category.' => '',
|
||||
// 'Remove a category' => '',
|
||||
// 'Category removed successfully.' => '',
|
||||
// 'Unable to remove this category.' => '',
|
||||
// 'Category modification for the project "%s"' => '',
|
||||
// 'Category Name' => '',
|
||||
// 'Categories for the project "%s"' => '',
|
||||
// 'Add a new category' => '',
|
||||
// 'Do you really want to remove this category: "%s"?' => '',
|
||||
// 'Filter by category' => '',
|
||||
// 'All categories' => '',
|
||||
// 'No category' => '',
|
||||
// 'The name is required' => '',
|
||||
// 'Remove a file' => '',
|
||||
// 'Unable to remove this file.' => '',
|
||||
// 'File removed successfully.' => '',
|
||||
// 'Attach a document' => '',
|
||||
// 'Do you really want to remove this file: "%s"?' => '',
|
||||
// 'open' => '',
|
||||
// 'Attachments' => '',
|
||||
// 'Edit the task' => '',
|
||||
// 'Edit the description' => '',
|
||||
// 'Add a comment' => '',
|
||||
// 'Edit a comment' => '',
|
||||
// 'Summary' => '',
|
||||
// 'Time tracking' => '',
|
||||
// 'Estimate:' => '',
|
||||
// 'Spent:' => '',
|
||||
// 'Do you really want to remove this sub-task?' => '',
|
||||
// 'Remaining:' => '',
|
||||
// 'hours' => '',
|
||||
// 'spent' => '',
|
||||
// 'estimated' => '',
|
||||
// 'Sub-Tasks' => '',
|
||||
// 'Add a sub-task' => '',
|
||||
// 'Original estimate' => '',
|
||||
// 'Create another sub-task' => '',
|
||||
// 'Time spent' => '',
|
||||
// 'Edit a sub-task' => '',
|
||||
// 'Remove a sub-task' => '',
|
||||
// 'The time must be a numeric value' => '',
|
||||
// 'Todo' => '',
|
||||
// 'In progress' => '',
|
||||
// 'Sub-task removed successfully.' => '',
|
||||
// 'Unable to remove this sub-task.' => '',
|
||||
// 'Sub-task updated successfully.' => '',
|
||||
// 'Unable to update your sub-task.' => '',
|
||||
// 'Unable to create your sub-task.' => '',
|
||||
// 'Sub-task added successfully.' => '',
|
||||
// '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' => '',
|
||||
// 'Created by %s' => '',
|
||||
// 'Last modified on %B %e, %Y at %k:%M %p' => '',
|
||||
// 'Tasks Export' => '',
|
||||
// 'Tasks exportation for "%s"' => '',
|
||||
// 'Start Date' => '',
|
||||
// 'End Date' => '',
|
||||
// 'Execute' => '',
|
||||
// 'Task Id' => '',
|
||||
// 'Creator' => '',
|
||||
// 'Modification date' => '',
|
||||
// 'Completion date' => '',
|
||||
// 'Webhook URL for task creation' => '',
|
||||
// 'Webhook URL for task modification' => '',
|
||||
// 'Clone' => '',
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
'%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',
|
||||
'Email' => 'Email',
|
||||
'Link my Google Account' => 'Połącz z kontem Google',
|
||||
'Unlink my Google Account' => 'Rozłącz z kontem Google',
|
||||
'Login with my Google Account' => 'Zaloguj przy pomocy konta Google',
|
||||
'Project not found.' => 'Projek nieznaleziony.',
|
||||
'Task #%d' => 'Zadanie #%d',
|
||||
'Task removed successfully.' => 'Zadanie usunięto pomyślnie.',
|
||||
'Unable to remove this task.' => 'Nie można usunąć tego zadania.',
|
||||
'Remove a task' => 'Usuń zadanie',
|
||||
'Do you really want to remove this task: "%s"?' => 'Czy na pewno chcesz usunąć zadanie "%s"?',
|
||||
'Assign automatically a color based on a category' => 'Przypisz kolor automatycznie na podstawie kategori',
|
||||
'Assign automatically a category based on a color' => 'Przypisz kategorię automatycznie na podstawie koloru',
|
||||
'Task creation or modification' => 'Tworzenie lub usuwanie zadania',
|
||||
'Category' => 'Kategoria',
|
||||
'Category:' => 'Kategoria:',
|
||||
'Categories' => 'Kategorie',
|
||||
'Category not found.' => 'Kategoria nie istnieje',
|
||||
'Your category have been created successfully.' => 'Pomyślnie utworzono kategorię.',
|
||||
'Unable to create your category.' => 'Nie można tworzyć kategorii.',
|
||||
'Your category have been updated successfully.' => 'Pomyślnie zaktualizowano kategorię',
|
||||
'Unable to update your category.' => 'Nie można zaktualizować kategorii',
|
||||
'Remove a category' => 'Usuń kategorię',
|
||||
'Category removed successfully.' => 'Pomyślnie usunięto kategorię.',
|
||||
'Unable to remove this category.' => 'Nie można usunąć tej kategorii.',
|
||||
'Category modification for the project "%s"' => 'Zmiania kategorii projektu "%s"',
|
||||
'Category Name' => 'Nazwa kategorii',
|
||||
'Categories for the project "%s"' => 'Kategorie projektu',
|
||||
'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',
|
||||
'Remove a file' => 'Usuń plik',
|
||||
'Unable to remove this file.' => 'Nie można usunąć tego pliku.',
|
||||
'File removed successfully.' => 'Plik Usunięty pomyślnie.',
|
||||
'Attach a document' => 'Dołącz plik',
|
||||
'Do you really want to remove this file: "%s"?' => 'Czy na pewno chcesz usunąć plik: "%s"?',
|
||||
'open' => 'otwórz',
|
||||
'Attachments' => 'Załączniki',
|
||||
'Edit the task' => 'Edytuj Zadanie',
|
||||
'Edit the description' => 'Edytuj opis',
|
||||
'Add a comment' => 'Dodaj komentarz',
|
||||
'Edit a comment' => 'Edytuj komentarz',
|
||||
'Summary' => 'Podsumowanie',
|
||||
'Time tracking' => 'Śledzenie czasu',
|
||||
'Estimate:' => 'Szacowany:',
|
||||
'Spent:' => 'Przeznaczony:',
|
||||
'Do you really want to remove this sub-task?' => 'Czy na pewno chcesz usunąć to pod-zadanie?',
|
||||
'Remaining:' => 'Pozostało:',
|
||||
'hours' => 'godzin',
|
||||
'spent' => 'przeznaczono',
|
||||
'estimated' => 'szacowany',
|
||||
'Sub-Tasks' => 'Pod-zadanie',
|
||||
'Add a sub-task' => 'Dodaj pod-zadanie',
|
||||
'Original estimate' => 'Szacowanie początkowe',
|
||||
'Create another sub-task' => 'Dodaj kolejne pod-zadanie',
|
||||
'Time spent' => 'Przeznaczony czas',
|
||||
'Edit a sub-task' => 'Edytuj pod-zadanie',
|
||||
'Remove a sub-task' => 'Usuń pod-zadanie',
|
||||
'The time must be a numeric value' => 'Czas musi być wartością liczbową',
|
||||
'Todo' => 'Do zrobienia',
|
||||
'In progress' => 'W trakcie',
|
||||
'Sub-task removed successfully.' => 'Pod-zadanie usunięte pomyślnie.',
|
||||
'Unable to remove this sub-task.' => 'Nie można usunąć tego pod-zadania.',
|
||||
'Sub-task updated successfully.' => 'Pod-zadanie zaktualizowane pomyślnie.',
|
||||
'Unable to update your sub-task.' => 'Nie można zaktalizować tego pod-zadania.',
|
||||
'Unable to create your sub-task.' => 'Nie można utworzyć tego pod-zadania.',
|
||||
'Sub-task added successfully.' => 'Pod-zadanie utworzone pomyślnie',
|
||||
'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',
|
||||
'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ń',
|
||||
'Tasks exportation for "%s"' => 'Eksport zadań dla "%s"',
|
||||
'Start Date' => 'Data początkowa',
|
||||
'End Date' => 'Data Końcowa',
|
||||
'Execute' => 'Wykonaj',
|
||||
'Task Id' => 'Identyfikator Zadania',
|
||||
'Creator' => 'Autor',
|
||||
'Modification date' => 'Data modyfyfikacji',
|
||||
'Completion date' => 'Data ukończenia',
|
||||
'Webhook URL for task creation' => 'Webhook URL do tworzenia zadań',
|
||||
'Webhook URL for task modification' => 'Webhook URL do modyfikacji zadań',
|
||||
'Clone' => 'Sklonuj',
|
||||
'Clone Project' => 'Sklonuj projekt',
|
||||
'Project cloned successfully.' => 'Projekt sklonowany pomyślnie.',
|
||||
'Unable to clone this project.' => 'Nie można sklonować projektu.',
|
||||
'Email notifications' => 'Powiadomienia email',
|
||||
'Enable email notifications' => 'Włącz powiadomienia email',
|
||||
'Task position:' => 'Pozycja zadania:',
|
||||
'The task #%d have been opened.' => 'Zadania #%d zostały otwarte.',
|
||||
'The task #%d have been closed.' => 'Zadania #$d zostały zamknięte.',
|
||||
'Sub-task updated' => 'Pod-zadanie zaktualizowane',
|
||||
'Title:' => 'Tytuł:',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
// 'view the task on Kanboard' => '',
|
||||
// 'Public access' => '',
|
||||
// 'Category management' => '',
|
||||
// 'User management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
// 'Move the task to another project' => '',
|
||||
// 'Move to another project' => '',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Notifications' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
// '%s updated the task %s' => '',
|
||||
// '%s opened the task %s' => '',
|
||||
// '%s moved the task %s to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task %s to the column "%s"' => '',
|
||||
// '%s created the task %s' => '',
|
||||
// '%s closed the task %s' => '',
|
||||
// '%s created a subtask for the task %s' => '',
|
||||
// '%s updated a subtask for the task %s' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task %s' => '',
|
||||
// '%s commented the task %s' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d to %s' => '',
|
||||
// '%s changed the assignee of the task %s to %s' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
// 'Choose an event' => '',
|
||||
'Assignee:' => 'Przypisano do:',
|
||||
'Time tracking:' => 'Śledzenie czasu: ',
|
||||
'New sub-task' => 'Nowe Pod-zadanie',
|
||||
'New attachment added "%s"' => 'Nowy załącznik dodany "%s"',
|
||||
'Comment updated' => 'Komentarz zaktualizowany',
|
||||
'New comment posted by %s' => 'Nowy komentarz dodany przez %s',
|
||||
'List of due tasks for the project "%s"' => 'Lista zadań oczekujących projektu "%s"',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][Zadania oczekujące]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Powiadomienie',
|
||||
'I want to receive notifications only for those projects:' => 'Chcę otrzymywaćpowiadiomienia tylko dla tych projektów:',
|
||||
'view the task on Kanboard' => 'Zobacz zadanie',
|
||||
'Public access' => 'Dostęp publiczny',
|
||||
'Category management' => 'Zarządzanie kategoriami',
|
||||
'User management' => 'Zarządzanie użytkownikami',
|
||||
'Active tasks' => 'Aktywne zadania',
|
||||
'Disable public access' => 'Zablokuj dostęp publiczny',
|
||||
'Enable public access' => 'Odblokuj dostęp publiczny',
|
||||
'Active projects' => 'Aktywne projety',
|
||||
'Inactive projects' => 'Nieaktywne projekty',
|
||||
'Public access disabled' => 'Dostęp publiczny zablokowany',
|
||||
'Do you really want to disable this project: "%s"?' => 'Czy napewno chcesz zablokować projekt: "%s"?',
|
||||
'Do you really want to duplicate this project: "%s"?' => 'Czy napewno chcesz zduplikować projekt: "%s"?',
|
||||
'Do you really want to enable this project: "%s"?' => 'Czy napewno chcesz odblokować projekt: "%s"?',
|
||||
'Project activation' => 'Aktywacja projekt',
|
||||
'Move the task to another project' => 'Przenieś zadanie do innego projektu',
|
||||
'Move to another project' => 'Przenieś do innego projektu',
|
||||
'Do you really want to duplicate this task?' => 'Czy napewno chcesz zduplikować to zadanie: "%s"?',
|
||||
'Duplicate a task' => 'Zduplikuj zadanie',
|
||||
'External accounts' => 'Konta zewnętrzne',
|
||||
'Account type' => 'Typ konta',
|
||||
'Local' => 'Lokalne',
|
||||
'Remote' => 'Zdalne',
|
||||
'Enabled' => 'Odblokowane',
|
||||
'Disabled' => 'Zablokowane',
|
||||
'Google account linked' => 'Połączone konto Google',
|
||||
'Github account linked' => 'Połączone konto Github',
|
||||
'Username:' => 'Nazwa Użytkownika:',
|
||||
'Name:' => 'Imię i Nazwisko',
|
||||
'Email:' => 'Email: ',
|
||||
'Default project:' => 'Projekt domyślny:',
|
||||
'Notifications:' => 'Powiadomienia: ',
|
||||
'Notifications' => 'Powiadomienia',
|
||||
'Group:' => 'Grupa:',
|
||||
'Regular user' => 'Zwykły użytkownik',
|
||||
'Account type:' => 'Typ konta:',
|
||||
'Edit profile' => 'Edytuj profil',
|
||||
'Change password' => 'Zmień hasło',
|
||||
'Password modification' => 'Zmiania hasła',
|
||||
'External authentications' => 'Autentykacja zewnętrzna',
|
||||
'Google Account' => 'Konto Google',
|
||||
'Github Account' => 'Konto Github',
|
||||
'Never connected.' => 'Nigdy nie połączone.',
|
||||
'No account linked.' => 'Brak połączonych kont.',
|
||||
'Account linked.' => 'Konto połączone.',
|
||||
'No external authentication enabled.' => 'Brak autentykacji zewnętrznych.',
|
||||
'Password modified successfully.' => 'Hasło zmienione pomyślne.',
|
||||
'Unable to change the password.' => 'Nie można zmienić hasła.',
|
||||
'Change category for the task "%s"' => 'Zmień kategorię dla zadania "%s"',
|
||||
'Change category' => 'Zmień kategorię',
|
||||
'%s updated the task %s' => '%s zaktualizował zadanie %s',
|
||||
'%s opened the task %s' => '%s otworzył zadanie %s',
|
||||
'%s moved the task %s to the position #%d in the column "%s"' => '%s przeniósł zadanie %s na pozycję #%d w kolumnie "%s"',
|
||||
'%s moved the task %s to the column "%s"' => '%s przeniósł zadanie %s do kolumny "%s"',
|
||||
'%s created the task %s' => '%s utworzył zadanie %s',
|
||||
'%s closed the task %s' => '%s zamknął zadanie %s',
|
||||
'%s created a subtask for the task %s' => '%s utworzył pod-zadanie dla zadania %s',
|
||||
'%s updated a subtask for the task %s' => '%s zaktualizował pod-zadanie dla zadania %s',
|
||||
'Assigned to %s with an estimate of %s/%sh' => 'Przypisano do %s z szacowanym czasem wykonania %s/%sh',
|
||||
'Not assigned, estimate of %sh' => 'Nie przypisane, szacowany czas wykonania %sh',
|
||||
'%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',
|
||||
'%s updated a subtask for the task #%d' => '%s zaktualizował pod-zadanie dla zadania #%d',
|
||||
'%s created a subtask for the task #%d' => '%s utworzył pod-zadanie dla zadania #%d',
|
||||
'%s updated the task #%d' => '%s zaktualizował zadanie #%d',
|
||||
'%s created the task #%d' => '%s utworzył zadanie #%d',
|
||||
'%s closed the task #%d' => '%s zamknął zadanie #%d',
|
||||
'%s open the task #%d' => '%s otworzył zadanie #%d',
|
||||
'%s moved the task #%d to the column "%s"' => '%s przeniósł zadanie #%d do kolumny "%s"',
|
||||
'%s moved the task #%d to the position %d in the column "%s"' => '%s przeniósł zadanie #%d na pozycję %d w kolmnie "%s"',
|
||||
'Activity' => 'Aktywność',
|
||||
'Default values are "%s"' => 'Domyślne wartości: "%s"',
|
||||
'Default columns for new projects (Comma-separated)' => 'Domyślne kolmny dla nowych projektów (oddzielone przecinkiem)',
|
||||
'Task assignee change' => 'Zmień osobę odpowiedzialną',
|
||||
'%s change the assignee of the task #%d to %s' => '%s zmienił osobę odpowiedzialną za zadanie #%d na %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s zmienił osobę odpowiedzialną za zadanie %s na %s',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'Nowe hasło użytkownika "%s"',
|
||||
'Choose an event' => 'Wybierz zdarzenie',
|
||||
// 'Github commit received' => '',
|
||||
// 'Github issue opened' => '',
|
||||
// 'Github issue closed' => '',
|
||||
// 'Github issue reopened' => '',
|
||||
// 'Github issue assignee change' => '',
|
||||
// 'Github issue label change' => '',
|
||||
// 'Create a task from an external provider' => '',
|
||||
// 'Change the assignee based on an external username' => '',
|
||||
// 'Change the category based on an external label' => '',
|
||||
'Create a task from an external provider' => 'Utwórz zadanie z dostawcy zewnętrznego',
|
||||
'Change the assignee based on an external username' => 'Zmień osobę odpowiedzialną na podstawie zewnętrznej nazwy użytkownika',
|
||||
'Change the category based on an external label' => 'Zmień kategorię na podstawie zewnętrzenj etykiety',
|
||||
// 'Reference' => '',
|
||||
// 'Reference: %s' => '',
|
||||
// 'Label' => '',
|
||||
// 'Database' => '',
|
||||
// 'About' => '',
|
||||
// 'Database driver:' => '',
|
||||
// 'Board settings' => '',
|
||||
// 'URL and token' => '',
|
||||
'Label' => 'Etykieta',
|
||||
'Database' => 'Baza danych',
|
||||
'About' => 'Informacje',
|
||||
'Database driver:' => 'Silnik bazy danych:',
|
||||
'Board settings' => 'Ustawienia tablicy',
|
||||
'URL and token' => 'URL i token',
|
||||
// 'Webhook settings' => '',
|
||||
// 'URL for task creation:' => '',
|
||||
// 'Reset token' => '',
|
||||
'URL for task creation:' => 'URL do tworzenia zadań',
|
||||
'Reset token' => 'Resetuj token',
|
||||
// 'API endpoint:' => '',
|
||||
// 'Refresh interval for private board' => '',
|
||||
// 'Refresh interval for public board' => '',
|
||||
// 'Task highlight period' => '',
|
||||
// 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '',
|
||||
// 'Frequency in second (60 seconds by default)' => '',
|
||||
// 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '',
|
||||
// 'Application URL' => '',
|
||||
// 'Example: http://example.kanboard.net/ (used by email notifications)' => '',
|
||||
// 'Token regenerated.' => '',
|
||||
// 'Date format' => '',
|
||||
// 'ISO format is always accepted, example: "%s" and "%s"' => '',
|
||||
// 'New private project' => '',
|
||||
// 'This project is private' => '',
|
||||
// 'Type here to create a new sub-task' => '',
|
||||
// 'Add' => '',
|
||||
// 'Estimated time: %s hours' => '',
|
||||
// 'Time spent: %s hours' => '',
|
||||
// 'Started on %B %e, %Y' => '',
|
||||
// 'Start date' => '',
|
||||
// 'Time estimated' => '',
|
||||
// 'There is nothing assigned to you.' => '',
|
||||
// 'My tasks' => '',
|
||||
// 'Activity stream' => '',
|
||||
// 'Dashboard' => '',
|
||||
// 'Confirmation' => '',
|
||||
// 'Allow everybody to access to this project' => '',
|
||||
// 'Everybody have access to this project.' => '',
|
||||
'Refresh interval for private board' => 'Częstotliwość odświerzania dla tablicy prywatnej',
|
||||
'Refresh interval for public board' => 'Częstotliwość odświerzania dla tablicy publicznej',
|
||||
'Task highlight period' => 'Okres wyróżniania zadań',
|
||||
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Okres (w sekundach) wymagany do uznania projektu za niedawno zmieniony (0 ab zablokować, domyślnie 2 dni)',
|
||||
'Frequency in second (60 seconds by default)' => 'Częstotliwosć w sekundach (domyślnie 60 sekund)',
|
||||
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Częstotliwość w sekundach (0 aby zablokować, domyślnie 10 sekund)',
|
||||
'Application URL' => 'Adres URL aplikacji',
|
||||
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Przykład: http://example.kanboard.net/ (Używane przez powiadomienia email)',
|
||||
'Token regenerated.' => 'Token wygenerowany ponownie.',
|
||||
'Date format' => 'Format daty',
|
||||
'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO jest zawsze akceptowany, przykłady: "%s", "%s"',
|
||||
'New private project' => 'Nowy projekt prywatny',
|
||||
'This project is private' => 'Ten projekt jest prywatny',
|
||||
'Type here to create a new sub-task' => 'Wpisz tutaj aby utworzyć pod-zadanie',
|
||||
'Add' => 'Dodaj',
|
||||
'Estimated time: %s hours' => 'Szacowany czas: %s godzin',
|
||||
'Time spent: %s hours' => 'Przeznaczony czas: %s godzin',
|
||||
'Started on %B %e, %Y' => 'Rozpoczęto %e %B %Y',
|
||||
'Start date' => 'Data rozpoczęcia',
|
||||
'Time estimated' => 'Szacowany czas',
|
||||
'There is nothing assigned to you.' => 'Nie ma przypisanych zadań',
|
||||
'My tasks' => 'Moje zadania',
|
||||
'Activity stream' => 'Dziennik aktywności',
|
||||
'Dashboard' => 'Panel',
|
||||
'Confirmation' => 'Potwierdzenie',
|
||||
'Allow everybody to access to this project' => 'Udostepnij ten projekt wszystkim',
|
||||
'Everybody have access to this project.' => 'Wszyscy mają dostęp do tego projektu.',
|
||||
// 'Webhooks' => '',
|
||||
// 'API' => '',
|
||||
// 'Integration' => '',
|
||||
'Integration' => 'Integracja',
|
||||
// 'Github webhooks' => '',
|
||||
// 'Help on Github webhooks' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
'Create a comment from an external provider' => 'Utwórz komentarz od zewnętrznego dostawcy',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
'Configure' => 'Konfiguruj',
|
||||
'Project management' => 'Menadżer projektu',
|
||||
'My projects' => 'Moje projekty',
|
||||
'Columns' => 'Kolumny',
|
||||
'Task' => 'zadania',
|
||||
'Your are not member of any project.' => 'Nie bierzesz udziału w żadnym projekcie',
|
||||
'Percentage' => 'Procent',
|
||||
'Number of tasks' => 'Liczba zadań',
|
||||
'Task distribution' => 'Rozmieszczenie zadań',
|
||||
'Reportings' => 'Raporty',
|
||||
'Task repartition for "%s"' => 'Przydział zadań dla "%s"',
|
||||
'Analytics' => 'Analizy',
|
||||
'Subtask' => 'Pod-zadanie',
|
||||
'My subtasks' => 'Moje pod-zadania',
|
||||
'User repartition' => 'Przydział użytkownika',
|
||||
'User repartition for "%s"' => 'Przydział użytkownika dla "%s"',
|
||||
'Clone this project' => 'Sklonuj ten projekt',
|
||||
'Column removed successfully.' => 'Kolumna usunięta pomyslnie.',
|
||||
'Edit Project' => 'Edytuj projekt',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
// 'Active swimlanes' => '',
|
||||
// 'Add a new swimlane' => '',
|
||||
// 'Change default swimlane' => '',
|
||||
// 'Default swimlane' => '',
|
||||
// 'Do you really want to remove this swimlane: "%s"?' => '',
|
||||
// 'Inactive swimlanes' => '',
|
||||
// 'Set project manager' => '',
|
||||
// 'Set project member' => '',
|
||||
// 'Remove a swimlane' => '',
|
||||
// 'Rename' => '',
|
||||
// 'Show default swimlane' => '',
|
||||
// 'Swimlane modification for the project "%s"' => '',
|
||||
// 'Swimlane not found.' => '',
|
||||
// 'Swimlane removed successfully.' => '',
|
||||
// 'Swimlanes' => '',
|
||||
// 'Swimlane updated successfully.' => '',
|
||||
// 'The default swimlane have been updated successfully.' => '',
|
||||
// 'Unable to create your swimlane.' => '',
|
||||
// 'Unable to remove this swimlane.' => '',
|
||||
// 'Unable to update this swimlane.' => '',
|
||||
// 'Your swimlane have been created successfully.' => '',
|
||||
// 'Example: "Bug, Feature Request, Improvement"' => '',
|
||||
// 'Default categories for new projects (Comma-separated)' => '',
|
||||
'Not enough data to show the graph.' => 'Za mało danych do utworzenia wykresu.',
|
||||
'Previous' => 'Poprzedni',
|
||||
'The id must be an integer' => 'ID musi być liczbą całkowitą',
|
||||
'The project id must be an integer' => 'ID projektu musi być liczbą całkowitą',
|
||||
'The status must be an integer' => 'Status musi być liczbą całkowitą',
|
||||
'The subtask id is required' => 'ID pod-zadanie jest wymagane',
|
||||
'The subtask id must be an integer' => 'ID pod-zadania musi być liczbą całkowitą',
|
||||
'The task id is required' => 'ID zadania jest wymagane',
|
||||
'The task id must be an integer' => 'ID zadania musi być liczbą całkowitą',
|
||||
'The user id must be an integer' => 'ID użytkownika musi być liczbą całkowitą',
|
||||
'This value is required' => 'Wymagana wartość',
|
||||
'This value must be numeric' => 'Wartość musi być liczbą',
|
||||
'Unable to create this task.' => 'Nie można tworzyć zadania.',
|
||||
'Cumulative flow diagram' => 'Zbiorowy diagram przepływu',
|
||||
'Cumulative flow diagram for "%s"' => 'Zbiorowy diagram przepływu dla "%s"',
|
||||
'Daily project summary' => 'Dzienne podsumowanie projektu',
|
||||
'Daily project summary export' => 'Eksport dziennego podsumowania projektu',
|
||||
'Daily project summary export for "%s"' => 'Eksport dziennego podsumowania projektu dla "%s"',
|
||||
'Exports' => 'Eksporty',
|
||||
'This export contains the number of tasks per column grouped per day.' => 'Ten eksport zawiera ilość zadań zgrupowanych w kolumnach na dzień',
|
||||
'Nothing to preview...' => 'Nic do podejrzenia...',
|
||||
'Preview' => 'Podgląd',
|
||||
'Write' => 'Edycja',
|
||||
'Active swimlanes' => 'Aktywne procesy',
|
||||
'Add a new swimlane' => 'Dodaj proces',
|
||||
'Change default swimlane' => 'Zmień domyślny proces',
|
||||
'Default swimlane' => 'Domyślny proces',
|
||||
'Do you really want to remove this swimlane: "%s"?' => 'Czy na pewno chcesz usunąć proces: "%s"?',
|
||||
'Inactive swimlanes' => 'Nieaktywne procesy',
|
||||
'Set project manager' => 'Ustaw menadżera projektu',
|
||||
'Set project member' => 'Ustaw członka projektu',
|
||||
'Remove a swimlane' => 'Usuń proces',
|
||||
'Rename' => 'Zmień nazwe',
|
||||
'Show default swimlane' => 'Pokaż domyślny proces',
|
||||
'Swimlane modification for the project "%s"' => 'Edycja procesów dla projektu "%s"',
|
||||
'Swimlane not found.' => 'Nie znaleziono procesu.',
|
||||
'Swimlane removed successfully.' => 'Proces usunięty pomyslnie.',
|
||||
'Swimlanes' => 'Procesy',
|
||||
'Swimlane updated successfully.' => 'Proces zaktualizowany pomyślnie.',
|
||||
'The default swimlane have been updated successfully.' => 'Domyślny proces zaktualizowany pomyślnie.',
|
||||
'Unable to create your swimlane.' => 'Nie można utworzyć procesu.',
|
||||
'Unable to remove this swimlane.' => 'Nie można usunąć procesu.',
|
||||
'Unable to update this swimlane.' => 'Nie można zaktualizować procesu.',
|
||||
'Your swimlane have been created successfully.' => 'Proces tworzony pomyślnie.',
|
||||
'Example: "Bug, Feature Request, Improvement"' => 'Przykład: "Błąd, Żądanie Funkcjonalnośći, Udoskonalenia"',
|
||||
'Default categories for new projects (Comma-separated)' => 'Domyślne kategorie dla nowych projektów (oddzielone przecinkiem)',
|
||||
// 'Gitlab commit received' => '',
|
||||
// 'Gitlab issue opened' => '',
|
||||
// 'Gitlab issue closed' => '',
|
||||
// 'Gitlab webhooks' => '',
|
||||
// 'Help on Gitlab webhooks' => '',
|
||||
// 'Integrations' => '',
|
||||
// 'Integration with third-party services' => '',
|
||||
// 'Role for this project' => '',
|
||||
// 'Project manager' => '',
|
||||
// 'Project member' => '',
|
||||
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
|
||||
'Integrations' => 'Integracje',
|
||||
'Integration with third-party services' => 'Integracja z usługami firm trzecich',
|
||||
'Role for this project' => 'Rola w tym projekcie',
|
||||
'Project manager' => 'Manadżer projektu',
|
||||
'Project member' => 'Członek projektu',
|
||||
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Menadżer projektu może zmieniać ustawienia projektu i posiada większe uprawnienia od zwykłego użytkownika',
|
||||
// 'Gitlab Issue' => '',
|
||||
// 'Subtask Id' => '',
|
||||
// 'Subtasks' => '',
|
||||
// 'Subtasks Export' => '',
|
||||
// 'Subtasks exportation for "%s"' => '',
|
||||
// 'Task Title' => '',
|
||||
// 'Untitled' => '',
|
||||
// 'Application default' => '',
|
||||
// 'Language:' => '',
|
||||
// 'Timezone:' => '',
|
||||
// 'Next' => '',
|
||||
'Subtask Id' => 'ID pod-zadania',
|
||||
'Subtasks' => 'Pod-zadania',
|
||||
'Subtasks Export' => 'Eksport pod-zadań',
|
||||
'Subtasks exportation for "%s"' => 'Eksporty pod-zadań dla "%s"',
|
||||
'Task Title' => 'Tytuł zadania',
|
||||
'Untitled' => 'Bez tytułu',
|
||||
'Application default' => 'Domyślne dla aplikacji',
|
||||
'Language:' => 'Język:',
|
||||
'Timezone:' => 'Strefa czasowa:',
|
||||
'All columns' => 'Wszystkie kolumny',
|
||||
'Calendar for "%s"' => 'Kalendarz dla "%s"',
|
||||
'Filter by column' => 'Filtrj według kolumn',
|
||||
'Filter by status' => 'Filtruj według statusu',
|
||||
'Calendar' => 'Kalendarz',
|
||||
'Next' => 'Następny',
|
||||
// '#%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',
|
||||
'Add a comment logging moving the task between columns' => 'Dodaj komentarz dokumentujący przeniesienie zadania pomiędzy kolumnami',
|
||||
'Moved to column %s' => 'Przeniosiono do kolumny %s',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'Comentário atualizado',
|
||||
'New comment posted by %s' => 'Novo comentário postado por %s',
|
||||
'List of due tasks for the project "%s"' => 'Lista de tarefas pendentes para o projeto "%s"',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Novo anexo] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Novo comentário] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Comentário atualizado] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Nova subtarefa] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Subtarefa atualizada] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Nova tarefa] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Tarefa atualizada] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Tarefa finalizada] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Tarefa aberta] %s (#%d)',
|
||||
'New attachment' => 'Novo anexo',
|
||||
'New comment' => 'Novo comentário',
|
||||
'New subtask' => 'Nova subtarefa',
|
||||
'Subtask updated' => 'Subtarefa alterada',
|
||||
'Task updated' => 'Tarefa alterada',
|
||||
'Task closed' => 'Tarefa finalizada',
|
||||
'Task opened' => 'Tarefa aberta',
|
||||
'[%s][Due tasks]' => '[%s][Tarefas pendentes]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notificação',
|
||||
'I want to receive notifications only for those projects:' => 'Quero receber notificações apenas destes projetos:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => 'Mudar designação da tarefa',
|
||||
'%s change the assignee of the task #%d to %s' => '%s mudou a designação da tarefa #%d para %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s mudou a designação da tarefa %s para %s',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][Modificou Coluna] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][Modificou Posição] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][Modificou Designação] %s (#%d)',
|
||||
'Column Change' => 'Mudança de coluna',
|
||||
'Position Change' => 'Mudança de posição',
|
||||
'Assignee Change' => 'Mudança de designado',
|
||||
'New password for the user "%s"' => 'Nova senha para o usuário "%s"',
|
||||
'Choose an event' => 'Escolher um evento',
|
||||
'Github commit received' => 'Github commit received',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
'Application default' => 'Aplicação padrão',
|
||||
'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',
|
||||
'Add a comment logging moving the task between columns' => 'Adicionar un comentário de log ao mover uma tarefa em outra coluna',
|
||||
'Moved to column %s' => 'Mover para a coluna %s',
|
||||
'Change description' => 'Modificar a descrição',
|
||||
'User dashboard' => 'Painel de Controle do usuário',
|
||||
'Allow only one subtask in progress at the same time for a user' => 'Permitir apenas uma subtarefa em andamento ao mesmo tempo para um usuário',
|
||||
'Edit column "%s"' => 'Editar a coluna "%s"',
|
||||
'Enable time tracking for subtasks' => 'Ativar a gestão de tempo par a subtarefa',
|
||||
'Select the new status of the subtask: "%s"' => 'Selecionar um novo status para a subtarefa',
|
||||
'Subtask timesheet' => 'Gestão de tempo das subtarefas',
|
||||
'There is nothing to show.' => 'Não há nada para mostrar',
|
||||
'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',
|
||||
'Bitbucket commit received' => '"Commit" recebido via Bitbucket',
|
||||
'Bitbucket webhooks' => 'Webhook Bitbucket',
|
||||
'Help on Bitbucket webhooks' => 'Ajuda sobre os webhooks Bitbucket',
|
||||
'Start' => 'Inicio',
|
||||
'End' => 'Fim',
|
||||
'Task age in days' => 'Idade da tarefa em dias',
|
||||
'Days in this column' => 'Dias nesta coluna',
|
||||
// '%dd' => '',
|
||||
'Add a link' => 'Adicionar uma associação',
|
||||
'Add a new link' => 'Adicionar uma nova associação',
|
||||
'Do you really want to remove this link: "%s"?' => 'Você realmente deseja remover esta associação: "%s"?',
|
||||
'Do you really want to remove this link with task #%d?' => 'Você realmente deseja remover esta associação com a tarefa n°%d?',
|
||||
// 'Field required' => '',
|
||||
'Link added successfully.' => 'Associação criada com sucesso.',
|
||||
'Link updated successfully.' => 'Associação atualizada com sucesso.',
|
||||
'Link removed successfully.' => 'Associação removida com sucesso.',
|
||||
'Link labels' => 'Etiquetas das associações',
|
||||
'Link modification' => 'Modificação de uma associação',
|
||||
'Links' => 'Associações',
|
||||
'Link settings' => 'Configuração das associações',
|
||||
'Opposite label' => 'Nome da etiqueta oposta',
|
||||
'Remove a link' => 'Remover uma associação',
|
||||
'Task\'s links' => 'Associações das tarefas',
|
||||
'The labels must be different' => 'As etiquetas devem ser diferentes',
|
||||
'There is no link.' => 'Não há nenhuma associação.',
|
||||
'This label must be unique' => 'Esta etiqueta deve ser unica',
|
||||
'Unable to create your link.' => 'Impossível de adicionar sua associação.',
|
||||
'Unable to update your link.' => 'Impossível de atualizar sua associação.',
|
||||
'Unable to remove this link.' => 'Impossível de remover sua associação.',
|
||||
'relates to' => 'é associado com',
|
||||
'blocks' => 'blocos',
|
||||
'is blocked by' => 'esta bloqueado por',
|
||||
'duplicates' => 'duplica',
|
||||
'is duplicated by' => 'é duplicado por',
|
||||
'is a child of' => 'é um filho de',
|
||||
'is a parent of' => 'é um parente do',
|
||||
'targets milestone' => 'visa um milestone',
|
||||
'is a milestone of' => 'é um milestone de',
|
||||
'fixes' => 'corrige',
|
||||
'is fixed by' => 'foi corrigido por',
|
||||
'This task' => 'Esta tarefa',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
'Expand tasks' => 'Expandir tarefas',
|
||||
'Collapse tasks' => 'Contrair tarefas',
|
||||
'Expand/collapse tasks' => 'Expandir/Contrair tarefas',
|
||||
// 'Close dialog box' => '',
|
||||
'Submit a form' => 'Envia o formulário',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
'Application' => 'Aplicação',
|
||||
'Filter recently updated' => 'Filtro recentemente atualizado',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
'More filters' => 'Mais filtros',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'Комментарий обновлен',
|
||||
'New comment posted by %s' => 'Новый комментарий написан « %s »',
|
||||
'List of due tasks for the project "%s"' => 'Список сроков к проекту « %s »',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Новых вложений] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Новых комментариев] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Обновленых коментариев] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Новых подзадач] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Обновленных подзадач] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Новых задач] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Обновленных задач] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Закрытых задач] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Открытых задач] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][Текущие задачи]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Оповещение',
|
||||
'I want to receive notifications only for those projects:' => 'Я хочу получать уведомления только по этим проектам :',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => 'Изменен назначенный',
|
||||
'%s change the assignee of the task #%d to %s' => '%s сменил назначенного для задачи #%d на %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s сменил назначенного для задачи %s на %s',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][Изменение колонки] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][Изменение позиции] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][Изменение назначеного] %s (#%d)',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'Новый пароль для пользователя %s"',
|
||||
'Choose an event' => 'Выберите событие',
|
||||
'Github commit received' => 'Github: коммит получен',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
// 'Application default' => '',
|
||||
// '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' => '',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
// 'Moved to column %s' => '',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'Kommentaren har uppdaterats',
|
||||
'New comment posted by %s' => 'Ny kommentar postad av %s',
|
||||
'List of due tasks for the project "%s"' => 'Lista med uppgifter för projektet "%s"',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Ny bifogning] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Ny kommentar] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Uppdaterad kommentar] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Ny deluppgift] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Deluppgiften uppdaterad] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Ny uppgift] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Uppgiften uppdaterad] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Uppgiften stängd] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Uppgiften öppnad] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][Förfallen uppgift]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notis',
|
||||
'I want to receive notifications only for those projects:' => 'Jag vill endast få notiser för dessa projekt:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => 'Ändra tilldelning av uppgiften',
|
||||
'%s change the assignee of the task #%d to %s' => '%s byt tilldelning av uppgiften #%d till %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s byt tilldelning av uppgiften %s till %s',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][Byt kolumn] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][Byt position] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][Byt tilldelning] %s (#%d)',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'Nytt lösenord för användaren "%s"',
|
||||
'Choose an event' => 'Välj en händelse',
|
||||
'Github commit received' => 'Github-bidrag mottaget',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
'Application default' => 'Applikationsstandard',
|
||||
'Language:' => 'Språk',
|
||||
'Timezone:' => 'Tidszon',
|
||||
// 'All columns' => '',
|
||||
// 'Calendar for "%s"' => '',
|
||||
// 'Filter by column' => '',
|
||||
// 'Filter by status' => '',
|
||||
// 'Calendar' => '',
|
||||
'Next' => 'Nästa',
|
||||
// '#%d' => '',
|
||||
// 'Filter by color' => '',
|
||||
// 'Filter by swimlane' => '',
|
||||
// 'All swimlanes' => '',
|
||||
// 'All colors' => '',
|
||||
// 'All status' => '',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
// 'Moved to column %s' => '',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => 'ปรับปรุงความคิดเห็น',
|
||||
'New comment posted by %s' => 'ความคิดเห็นใหม่จาก %s',
|
||||
'List of due tasks for the project "%s"' => 'รายการงานสำหรับโปรเจค "%s"',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][แนบใหม่] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][ความคิดเห็นใหม่] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][ปรับปรุงความคิดเห็น] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][งานย่อยใหม่] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][ปรับปรุงงานย่อย] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][งานใหม่] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][ปรับปรุุงงาน] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][งานที่ปิด] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][งานที่เปิด] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][งานปัจจุบัน]',
|
||||
'[Kanboard] Notification' => '[Kanboard] แจ้งเตือน',
|
||||
'I want to receive notifications only for those projects:' => 'ฉันต้องการรับการแจ้งเตือนสำหรับโปรเจค:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => 'เปลี่ยนการกำหนดบุคคลของงาน',
|
||||
// '%s change the assignee of the task #%d to %s' => '',
|
||||
// '%s changed the assignee of the task %s to %s' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => 'รหัสผ่านใหม่สำหรับผู้ใช้ "%s"',
|
||||
// 'Choose an event' => '',
|
||||
// 'Github commit received' => '',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
// 'Application default' => '',
|
||||
// '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' => '',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
// 'Moved to column %s' => '',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -408,15 +408,13 @@ return array(
|
|||
'Comment updated' => '更新了评论',
|
||||
'New comment posted by %s' => '%s 的新评论',
|
||||
'List of due tasks for the project "%s"' => '项目"%s"的到期任务列表',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][新附件] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][新评论] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][评论更新] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][新子任务] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][子任务更新] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][新任务] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][任务更新] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][任务关闭] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][任务开启] %s (#%d)',
|
||||
// 'New attachment' => '',
|
||||
// 'New comment' => '',
|
||||
// 'New subtask' => '',
|
||||
// 'Subtask updated' => '',
|
||||
// 'Task updated' => '',
|
||||
// 'Task closed' => '',
|
||||
// 'Task opened' => '',
|
||||
'[%s][Due tasks]' => '[%s][到期任务]',
|
||||
'[Kanboard] Notification' => '[Kanboard] 通知',
|
||||
'I want to receive notifications only for those projects:' => '我仅需要收到下面项目的通知:',
|
||||
|
@ -500,9 +498,9 @@ return array(
|
|||
'Task assignee change' => '任务分配变更',
|
||||
'%s change the assignee of the task #%d to %s' => '%s 将任务 #%d 分配给了 %s',
|
||||
'%s changed the assignee of the task %s to %s' => '%s 将任务 %s 分配给 %s',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][栏目变更] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][位置变更] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][任务分配变更] %s (#%d)',
|
||||
// 'Column Change' => '',
|
||||
// 'Position Change' => '',
|
||||
// 'Assignee Change' => '',
|
||||
'New password for the user "%s"' => '用户"%s"的新密码',
|
||||
'Choose an event' => '选择一个事件',
|
||||
'Github commit received' => '收到了Github提交',
|
||||
|
@ -647,5 +645,93 @@ return array(
|
|||
// 'Application default' => '',
|
||||
// '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' => '',
|
||||
// 'Add a comment logging moving the task between columns' => '',
|
||||
// 'Moved to column %s' => '',
|
||||
// 'Change description' => '',
|
||||
// 'User dashboard' => '',
|
||||
// 'Allow only one subtask in progress at the same time for a user' => '',
|
||||
// 'Edit column "%s"' => '',
|
||||
// 'Enable time tracking for subtasks' => '',
|
||||
// 'Select the new status of the subtask: "%s"' => '',
|
||||
// 'Subtask timesheet' => '',
|
||||
// 'There is nothing to show.' => '',
|
||||
// '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' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
// 'Start' => '',
|
||||
// 'End' => '',
|
||||
// 'Task age in days' => '',
|
||||
// 'Days in this column' => '',
|
||||
// '%dd' => '',
|
||||
// 'Add a link' => '',
|
||||
// 'Add a new link' => '',
|
||||
// 'Do you really want to remove this link: "%s"?' => '',
|
||||
// 'Do you really want to remove this link with task #%d?' => '',
|
||||
// 'Field required' => '',
|
||||
// 'Link added successfully.' => '',
|
||||
// 'Link updated successfully.' => '',
|
||||
// 'Link removed successfully.' => '',
|
||||
// 'Link labels' => '',
|
||||
// 'Link modification' => '',
|
||||
// 'Links' => '',
|
||||
// 'Link settings' => '',
|
||||
// 'Opposite label' => '',
|
||||
// 'Remove a link' => '',
|
||||
// 'Task\'s links' => '',
|
||||
// 'The labels must be different' => '',
|
||||
// 'There is no link.' => '',
|
||||
// 'This label must be unique' => '',
|
||||
// 'Unable to create your link.' => '',
|
||||
// 'Unable to update your link.' => '',
|
||||
// 'Unable to remove this link.' => '',
|
||||
// 'relates to' => '',
|
||||
// 'blocks' => '',
|
||||
// 'is blocked by' => '',
|
||||
// 'duplicates' => '',
|
||||
// 'is duplicated by' => '',
|
||||
// 'is a child of' => '',
|
||||
// 'is a parent of' => '',
|
||||
// 'targets milestone' => '',
|
||||
// 'is a milestone of' => '',
|
||||
// 'fixes' => '',
|
||||
// 'is fixed by' => '',
|
||||
// 'This task' => '',
|
||||
// '<1h' => '',
|
||||
// '%dh' => '',
|
||||
// '%b %e' => '',
|
||||
// 'Expand tasks' => '',
|
||||
// 'Collapse tasks' => '',
|
||||
// 'Expand/collapse tasks' => '',
|
||||
// 'Close dialog box' => '',
|
||||
// 'Submit a form' => '',
|
||||
// 'Board view' => '',
|
||||
// 'Keyboard shortcuts' => '',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
// 'Filter recently updated' => '',
|
||||
// 'since %B %e, %Y at %k:%M %p' => '',
|
||||
// 'More filters' => '',
|
||||
);
|
||||
|
|
|
@ -22,6 +22,7 @@ class Acl extends Base
|
|||
'board' => array('readonly'),
|
||||
'project' => array('feed'),
|
||||
'webhook' => '*',
|
||||
'app' => array('colors'),
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -37,6 +38,8 @@ class Acl extends Base
|
|||
'project' => array('show', 'tasks', 'search', 'activity'),
|
||||
'subtask' => '*',
|
||||
'task' => '*',
|
||||
'tasklink' => '*',
|
||||
'calendar' => array('show', 'project'),
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -48,7 +51,7 @@ class Acl extends Base
|
|||
private $manager_acl = array(
|
||||
'action' => '*',
|
||||
'analytic' => '*',
|
||||
'board' => array('movecolumn', 'edit', 'update', 'add', 'remove'),
|
||||
'board' => array('movecolumn', 'edit', 'editcolumn', 'updatecolumn', 'add', 'remove'),
|
||||
'category' => '*',
|
||||
'export' => array('tasks', 'subtasks', 'summary'),
|
||||
'project' => array('edit', 'update', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'),
|
||||
|
@ -62,8 +65,10 @@ class Acl extends Base
|
|||
* @var array
|
||||
*/
|
||||
private $admin_acl = array(
|
||||
'app' => array('dashboard'),
|
||||
'user' => array('index', 'create', 'save', 'remove'),
|
||||
'config' => '*',
|
||||
'link' => '*',
|
||||
'project' => array('remove'),
|
||||
);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Model;
|
|||
|
||||
use Integration\GitlabWebhook;
|
||||
use Integration\GithubWebhook;
|
||||
use Integration\BitbucketWebhook;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
|
@ -49,6 +50,7 @@ class Action extends Base
|
|||
'TaskAssignCategoryColor' => t('Assign automatically a category based on a color'),
|
||||
'CommentCreation' => t('Create a comment from an external provider'),
|
||||
'TaskCreation' => t('Create a task from an external provider'),
|
||||
'TaskLogMoveAnotherColumn' => t('Add a comment logging moving the task between columns'),
|
||||
'TaskAssignUser' => t('Change the assignee based on an external username'),
|
||||
'TaskAssignCategoryLabel' => t('Change the category based on an external label'),
|
||||
);
|
||||
|
@ -84,6 +86,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'),
|
||||
BitbucketWebhook::EVENT_COMMIT => t('Bitbucket commit received'),
|
||||
);
|
||||
|
||||
asort($values);
|
||||
|
|
|
@ -42,6 +42,13 @@ class Authentication extends Base
|
|||
// If the user is already logged it's ok
|
||||
if ($this->userSession->isLogged()) {
|
||||
|
||||
// Check if the user session match an existing user
|
||||
if (! $this->user->exists($this->userSession->getId())) {
|
||||
$this->backend('rememberMe')->destroy($this->userSession->getId());
|
||||
$this->session->close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// We update each time the RememberMe cookie tokens
|
||||
if ($this->backend('rememberMe')->hasCookie()) {
|
||||
$this->backend('rememberMe')->refresh();
|
||||
|
|
|
@ -10,37 +10,43 @@ use Pimple\Container;
|
|||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Core\Session $session
|
||||
* @property \Core\Template $template
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Action $action
|
||||
* @property \Model\Authentication $authentication
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Category $category
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\CommentHistory $commentHistory
|
||||
* @property \Model\Color $color
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\DateParser $dateParser
|
||||
* @property \Model\File $file
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\ProjectPermission $projectPermission
|
||||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\SubtaskHistory $subtaskHistory
|
||||
* @property \Model\Swimlane $swimlane
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskCreation $taskCreation
|
||||
* @property \Model\TaskExport $taskExport
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
* @property \Model\TaskHistory $taskHistory
|
||||
* @property \Model\TaskPosition $taskPosition
|
||||
* @property \Model\TaskValidator $taskValidator
|
||||
* @property \Model\TimeTracking $timeTracking
|
||||
* @property \Model\User $user
|
||||
* @property \Model\UserSession $userSession
|
||||
* @property \Model\Webhook $webhook
|
||||
* @property \Core\Session $session
|
||||
* @property \Core\Template $template
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Action $action
|
||||
* @property \Model\Authentication $authentication
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Category $category
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\CommentHistory $commentHistory
|
||||
* @property \Model\Color $color
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\DateParser $dateParser
|
||||
* @property \Model\File $file
|
||||
* @property \Model\Helper $helper
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Link $link
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\ProjectDuplication $projectDuplication
|
||||
* @property \Model\ProjectPermission $projectPermission
|
||||
* @property \Model\Subtask $subtask
|
||||
* @property \Model\SubtaskHistory $subtaskHistory
|
||||
* @property \Model\Swimlane $swimlane
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskCreation $taskCreation
|
||||
* @property \Model\TaskDuplication $taskDuplication
|
||||
* @property \Model\TaskExport $taskExport
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
* @property \Model\TaskHistory $taskHistory
|
||||
* @property \Model\TaskLink $taskLink
|
||||
* @property \Model\TaskPosition $taskPosition
|
||||
* @property \Model\TaskValidator $taskValidator
|
||||
* @property \Model\TimeTracking $timeTracking
|
||||
* @property \Model\SubtaskTimeTracking $subtaskTimeTracking
|
||||
* @property \Model\User $user
|
||||
* @property \Model\UserSession $userSession
|
||||
* @property \Model\Webhook $webhook
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
|
|
|
@ -47,7 +47,7 @@ class Board extends Base
|
|||
$column_name = trim($column_name);
|
||||
|
||||
if (! empty($column_name)) {
|
||||
$columns[] = array('title' => $column_name, 'task_limit' => 0);
|
||||
$columns[] = array('title' => $column_name, 'task_limit' => 0, 'description' => '');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,7 @@ class Board extends Base
|
|||
'position' => ++$position,
|
||||
'project_id' => $project_id,
|
||||
'task_limit' => $column['task_limit'],
|
||||
'description' => $column['description'],
|
||||
);
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
|
@ -94,7 +95,7 @@ class Board extends Base
|
|||
public function duplicate($project_from, $project_to)
|
||||
{
|
||||
$columns = $this->db->table(Board::TABLE)
|
||||
->columns('title', 'task_limit')
|
||||
->columns('title', 'task_limit', 'description')
|
||||
->eq('project_id', $project_from)
|
||||
->asc('position')
|
||||
->findAll();
|
||||
|
@ -109,48 +110,22 @@ class Board extends Base
|
|||
* @param integer $project_id Project id
|
||||
* @param string $title Column title
|
||||
* @param integer $task_limit Task limit
|
||||
* @param string $description Column description
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function addColumn($project_id, $title, $task_limit = 0)
|
||||
public function addColumn($project_id, $title, $task_limit = 0, $description = '')
|
||||
{
|
||||
$values = array(
|
||||
'project_id' => $project_id,
|
||||
'title' => $title,
|
||||
'task_limit' => $task_limit,
|
||||
'task_limit' => intval($task_limit),
|
||||
'position' => $this->getLastColumnPosition($project_id) + 1,
|
||||
'description' => $description,
|
||||
);
|
||||
|
||||
return $this->persist(self::TABLE, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update columns
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
$columns = array();
|
||||
|
||||
foreach (array('title', 'task_limit') as $field) {
|
||||
foreach ($values[$field] as $column_id => $value) {
|
||||
$columns[$column_id][$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
foreach ($columns as $column_id => $values) {
|
||||
$this->updateColumn($column_id, $values['title'], (int) $values['task_limit']);
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a column
|
||||
*
|
||||
|
@ -158,13 +133,15 @@ class Board extends Base
|
|||
* @param integer $column_id Column id
|
||||
* @param string $title Column title
|
||||
* @param integer $task_limit Task limit
|
||||
* @param string $description Optional description
|
||||
* @return boolean
|
||||
*/
|
||||
public function updateColumn($column_id, $title, $task_limit = 0)
|
||||
public function updateColumn($column_id, $title, $task_limit = 0, $description = '')
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $column_id)->update(array(
|
||||
'title' => $title,
|
||||
'task_limit' => $task_limit,
|
||||
'task_limit' => intval($task_limit),
|
||||
'description' => $description,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -178,7 +155,7 @@ class Board extends Base
|
|||
*/
|
||||
public function moveDown($project_id, $column_id)
|
||||
{
|
||||
$columns = $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->listing('id', 'position');
|
||||
$columns = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->asc('position')->getAll('id', 'position');
|
||||
$positions = array_flip($columns);
|
||||
|
||||
if (isset($columns[$column_id]) && $columns[$column_id] < count($columns)) {
|
||||
|
@ -207,7 +184,7 @@ class Board extends Base
|
|||
*/
|
||||
public function moveUp($project_id, $column_id)
|
||||
{
|
||||
$columns = $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->listing('id', 'position');
|
||||
$columns = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->asc('position')->getAll('id', 'position');
|
||||
$positions = array_flip($columns);
|
||||
|
||||
if (isset($columns[$column_id]) && $columns[$column_id] > 1) {
|
||||
|
@ -243,10 +220,12 @@ class Board extends Base
|
|||
|
||||
$swimlanes[$i]['columns'] = $columns;
|
||||
$swimlanes[$i]['nb_columns'] = $nb_columns;
|
||||
$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']);
|
||||
$swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']);
|
||||
$swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks'];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,16 +237,19 @@ class Board extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param boolean $prepend Prepend default value
|
||||
* @return array
|
||||
*/
|
||||
public function getColumnStats($project_id)
|
||||
public function getColumnStats($project_id, $prepend = false)
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', 1)
|
||||
->groupBy('column_id')
|
||||
->listing('column_id', 'COUNT(*) AS total');
|
||||
$listing = $this->db
|
||||
->hashtable(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', 1)
|
||||
->groupBy('column_id')
|
||||
->getAll('column_id', 'COUNT(*) AS total');
|
||||
|
||||
return $prepend ? array(-1 => t('All columns')) + $listing : $listing;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -287,11 +269,13 @@ class Board extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param boolean $prepend Prepend a default value
|
||||
* @return array
|
||||
*/
|
||||
public function getColumnsList($project_id)
|
||||
public function getColumnsList($project_id, $prepend = false)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->listing('id', 'title');
|
||||
$listing = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->asc('position')->getAll('id', 'title');
|
||||
return $prepend ? array(-1 => t('All columns')) + $listing : $listing;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -362,22 +346,16 @@ class Board extends Base
|
|||
* Validate column modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $columns Original columns List
|
||||
* @param array $values Required parameters to update a column
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $columns, array $values)
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$rules = array();
|
||||
|
||||
foreach ($columns as $column_id => $column_title) {
|
||||
$rules[] = new Validators\Integer('task_limit['.$column_id.']', t('This value must be an integer'));
|
||||
$rules[] = new Validators\GreaterThan('task_limit['.$column_id.']', t('This value must be greater than %d', 0), 0);
|
||||
$rules[] = new Validators\Required('title['.$column_id.']', t('The title is required'));
|
||||
$rules[] = new Validators\MaxLength('title['.$column_id.']', t('The maximum length is %d characters', 50), 50);
|
||||
}
|
||||
|
||||
$v = new Validator($values, $rules);
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Integer('task_limit', t('This value must be an integer')),
|
||||
new Validators\Required('title', t('The title is required')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 50), 50),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
|
|
@ -84,10 +84,10 @@ class Category extends Base
|
|||
*/
|
||||
public function getList($project_id, $prepend_none = true, $prepend_all = false)
|
||||
{
|
||||
$listing = $this->db->table(self::TABLE)
|
||||
$listing = $this->db->hashtable(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->asc('name')
|
||||
->listing('id', 'name');
|
||||
->getAll('id', 'name');
|
||||
|
||||
$prepend = array();
|
||||
|
||||
|
|
|
@ -3,22 +3,68 @@
|
|||
namespace Model;
|
||||
|
||||
/**
|
||||
* Color model (TODO: model for the future color picker)
|
||||
* Color model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Color extends Base
|
||||
{
|
||||
/**
|
||||
* Default colors
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $default_colors = array(
|
||||
'yellow' => array(
|
||||
'name' => 'Yellow',
|
||||
'background' => 'rgb(245, 247, 196)',
|
||||
'border' => 'rgb(223, 227, 45)',
|
||||
),
|
||||
'blue' => array(
|
||||
'name' => 'Blue',
|
||||
'background' => 'rgb(219, 235, 255)',
|
||||
'border' => 'rgb(168, 207, 255)',
|
||||
),
|
||||
'green' => array(
|
||||
'name' => 'Green',
|
||||
'background' => 'rgb(189, 244, 203)',
|
||||
'border' => 'rgb(74, 227, 113)',
|
||||
),
|
||||
'purple' => array(
|
||||
'name' => 'Purple',
|
||||
'background' => 'rgb(223, 176, 255)',
|
||||
'border' => 'rgb(205, 133, 254)',
|
||||
),
|
||||
'red' => array(
|
||||
'name' => 'Red',
|
||||
'background' => 'rgb(255, 187, 187)',
|
||||
'border' => 'rgb(255, 151, 151)',
|
||||
),
|
||||
'orange' => array(
|
||||
'name' => 'Orange',
|
||||
'background' => 'rgb(255, 215, 179)',
|
||||
'border' => 'rgb(255, 172, 98)',
|
||||
),
|
||||
'grey' => array(
|
||||
'name' => 'Grey',
|
||||
'background' => 'rgb(238, 238, 238)',
|
||||
'border' => 'rgb(204, 204, 204)',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Get available colors
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getList()
|
||||
public function getList($prepend = false)
|
||||
{
|
||||
return array(
|
||||
$listing = $prepend ? array('' => t('All colors')) : array();
|
||||
|
||||
return $listing + array(
|
||||
'yellow' => t('Yellow'),
|
||||
'blue' => t('Blue'),
|
||||
'green' => t('Green'),
|
||||
|
@ -39,4 +85,57 @@ class Color extends Base
|
|||
{
|
||||
return 'yellow'; // TODO: make this parameter configurable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Bordercolor from string
|
||||
*
|
||||
* @access public
|
||||
* @param string $color_id Color id
|
||||
* @return string
|
||||
*/
|
||||
public function getBorderColor($color_id)
|
||||
{
|
||||
if (isset($this->default_colors[$color_id])) {
|
||||
return $this->default_colors[$color_id]['border'];
|
||||
}
|
||||
|
||||
return $this->default_colors[$this->getDefaultColor()]['border'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get background color from the color_id
|
||||
*
|
||||
* @access public
|
||||
* @param string $color_id Color id
|
||||
* @return string
|
||||
*/
|
||||
public function getBackgroundColor($color_id)
|
||||
{
|
||||
if (isset($this->default_colors[$color_id])) {
|
||||
return $this->default_colors[$color_id]['background'];
|
||||
}
|
||||
|
||||
return $this->default_colors[$this->getDefaultColor()]['background'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSS stylesheet of all colors
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getCss()
|
||||
{
|
||||
$buffer = '';
|
||||
|
||||
foreach ($this->default_colors as $color => $values) {
|
||||
$buffer .= 'td.color-'.$color.',';
|
||||
$buffer .= 'div.color-'.$color.' {';
|
||||
$buffer .= 'background-color: '.$values['background'].';';
|
||||
$buffer .= 'border-color: '.$values['border'];
|
||||
$buffer .= '}';
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,52 @@ class Config extends Base
|
|||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get javascript language code
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getJsLanguageCode()
|
||||
{
|
||||
$languages = array(
|
||||
'da_DK' => 'da',
|
||||
'de_DE' => 'de',
|
||||
'en_US' => 'en',
|
||||
'es_ES' => 'es',
|
||||
'fr_FR' => 'fr',
|
||||
'it_IT' => 'it',
|
||||
'hu_HU' => 'hu',
|
||||
'pl_PL' => 'pl',
|
||||
'pt_BR' => 'pt-br',
|
||||
'ru_RU' => 'ru',
|
||||
'fi_FI' => 'fi',
|
||||
'sv_SE' => 'sv',
|
||||
'zh_CN' => 'zh-cn',
|
||||
'ja_JP' => 'ja',
|
||||
'th_TH' => 'th',
|
||||
);
|
||||
|
||||
$lang = $this->getCurrentLanguage();
|
||||
|
||||
return isset($languages[$lang]) ? $languages[$lang] : 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current language
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrentLanguage()
|
||||
{
|
||||
if ($this->userSession->isLogged() && ! empty($this->session['user']['language'])) {
|
||||
return $this->session['user']['language'];
|
||||
}
|
||||
|
||||
return $this->get('application_language', 'en_US');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a config variable from the session or the database
|
||||
*
|
||||
|
@ -110,7 +156,7 @@ class Config extends Base
|
|||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->db->table(self::TABLE)->listing('option', 'value');
|
||||
return $this->db->hashtable(self::TABLE)->getAll('option', 'value');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,12 +198,22 @@ class Config extends Base
|
|||
*/
|
||||
public function setupTranslations()
|
||||
{
|
||||
if ($this->userSession->isLogged() && ! empty($this->session['user']['language'])) {
|
||||
Translator::load($this->session['user']['language']);
|
||||
}
|
||||
else {
|
||||
Translator::load($this->get('application_language', 'en_US'));
|
||||
Translator::load($this->getCurrentLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current timezone
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrentTimezone()
|
||||
{
|
||||
if ($this->userSession->isLogged() && ! empty($this->session['user']['timezone'])) {
|
||||
return $this->session['user']['timezone'];
|
||||
}
|
||||
|
||||
return $this->get('application_timezone', 'UTC');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,12 +223,7 @@ class Config extends Base
|
|||
*/
|
||||
public function setupTimezone()
|
||||
{
|
||||
if ($this->userSession->isLogged() && ! empty($this->session['user']['timezone'])) {
|
||||
date_default_timezone_set($this->session['user']['timezone']);
|
||||
}
|
||||
else {
|
||||
date_default_timezone_set($this->get('application_timezone', 'UTC'));
|
||||
}
|
||||
date_default_timezone_set($this->getCurrentTimezone());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -87,17 +87,29 @@ class DateParser extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* For a given timestamp, reset the date to midnight
|
||||
* Remove the time from a timestamp
|
||||
*
|
||||
* @access public
|
||||
* @param integer $timestamp Timestamp
|
||||
* @return integer
|
||||
*/
|
||||
public function resetDateToMidnight($timestamp)
|
||||
public function removeTimeFromTimestamp($timestamp)
|
||||
{
|
||||
return mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp), date('Y', $timestamp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a timetstamp from an ISO date format
|
||||
*
|
||||
* @access public
|
||||
* @param string $date Date format
|
||||
* @return integer
|
||||
*/
|
||||
public function getTimestampFromIsoFormat($date)
|
||||
{
|
||||
return $this->removeTimeFromTimestamp(strtotime($date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date (form display)
|
||||
*
|
||||
|
@ -135,7 +147,7 @@ class DateParser extends Base
|
|||
foreach ($fields as $field) {
|
||||
|
||||
if (! empty($values[$field]) && ! is_numeric($values[$field])) {
|
||||
$values[$field] = $this->getTimestamp($values[$field]);
|
||||
$values[$field] = $this->removeTimeFromTimestamp($this->getTimestamp($values[$field]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,6 @@ class File extends Base
|
|||
*/
|
||||
const TABLE = 'task_has_files';
|
||||
|
||||
/**
|
||||
* Directory where are stored files
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const BASE_PATH = 'data/files/';
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
|
@ -56,7 +49,7 @@ class File extends Base
|
|||
{
|
||||
$file = $this->getbyId($file_id);
|
||||
|
||||
if (! empty($file) && @unlink(self::BASE_PATH.$file['path'])) {
|
||||
if (! empty($file) && @unlink(FILES_DIR.$file['path'])) {
|
||||
return $this->db->table(self::TABLE)->eq('id', $file_id)->remove();
|
||||
}
|
||||
|
||||
|
@ -152,14 +145,14 @@ class File extends Base
|
|||
*/
|
||||
public function setup()
|
||||
{
|
||||
if (! is_dir(self::BASE_PATH)) {
|
||||
if (! mkdir(self::BASE_PATH, 0755, true)) {
|
||||
die('Unable to create the upload directory: "'.self::BASE_PATH.'"');
|
||||
if (! is_dir(FILES_DIR)) {
|
||||
if (! mkdir(FILES_DIR, 0755, true)) {
|
||||
die('Unable to create the upload directory: "'.FILES_DIR.'"');
|
||||
}
|
||||
}
|
||||
|
||||
if (! is_writable(self::BASE_PATH)) {
|
||||
die('The directory "'.self::BASE_PATH.'" must be writeable by your webserver user');
|
||||
if (! is_writable(FILES_DIR)) {
|
||||
die('The directory "'.FILES_DIR.'" must be writeable by your webserver user');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,15 +180,15 @@ class File extends Base
|
|||
$uploaded_filename = $_FILES[$form_name]['tmp_name'][$key];
|
||||
$destination_filename = $this->generatePath($project_id, $task_id, $original_filename);
|
||||
|
||||
@mkdir(self::BASE_PATH.dirname($destination_filename), 0755, true);
|
||||
@mkdir(FILES_DIR.dirname($destination_filename), 0755, true);
|
||||
|
||||
if (@move_uploaded_file($uploaded_filename, self::BASE_PATH.$destination_filename)) {
|
||||
if (@move_uploaded_file($uploaded_filename, FILES_DIR.$destination_filename)) {
|
||||
|
||||
$result[] = $this->create(
|
||||
$task_id,
|
||||
$original_filename,
|
||||
$destination_filename,
|
||||
$this->isImage(self::BASE_PATH.$destination_filename)
|
||||
$this->isImage(FILES_DIR.$destination_filename)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
234
sources/app/Model/Link.php
Normal file
234
sources/app/Model/Link.php
Normal file
|
@ -0,0 +1,234 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use PDO;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Link model
|
||||
*
|
||||
* @package model
|
||||
* @author Olivier Maridat
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Link extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'links';
|
||||
|
||||
/**
|
||||
* Get a link by id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id Link id
|
||||
* @return array
|
||||
*/
|
||||
public function getById($link_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $link_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a link by name
|
||||
*
|
||||
* @access public
|
||||
* @param string $label
|
||||
* @return array
|
||||
*/
|
||||
public function getByLabel($label)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('label', $label)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opposite link id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id Link id
|
||||
* @return integer
|
||||
*/
|
||||
public function getOppositeLinkId($link_id)
|
||||
{
|
||||
$link = $this->getById($link_id);
|
||||
return $link['opposite_id'] ?: $link_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all links
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->db->table(self::TABLE)->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get merged links
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getMergedList()
|
||||
{
|
||||
return $this->db
|
||||
->execute('
|
||||
SELECT
|
||||
links.id, links.label, opposite.label as opposite_label
|
||||
FROM links
|
||||
LEFT JOIN links AS opposite ON opposite.id=links.opposite_id
|
||||
')
|
||||
->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get label list
|
||||
*
|
||||
* @access public
|
||||
* @param integer $exclude_id Exclude this link
|
||||
* @param booelan $prepend Prepend default value
|
||||
* @return array
|
||||
*/
|
||||
public function getList($exclude_id = 0, $prepend = true)
|
||||
{
|
||||
$labels = $this->db->hashtable(self::TABLE)->neq('id', $exclude_id)->asc('id')->getAll('id', 'label');
|
||||
|
||||
foreach ($labels as &$value) {
|
||||
$value = t($value);
|
||||
}
|
||||
|
||||
return $prepend ? array('') + $labels : $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new link label
|
||||
*
|
||||
* @access public
|
||||
* @param string $label
|
||||
* @param string $opposite_label
|
||||
* @return boolean
|
||||
*/
|
||||
public function create($label, $opposite_label = '')
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
if (! $this->db->table(self::TABLE)->insert(array('label' => $label))) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($opposite_label !== '') {
|
||||
$this->createOpposite($opposite_label);
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the opposite label (executed inside create() method)
|
||||
*
|
||||
* @access private
|
||||
* @param string $label
|
||||
*/
|
||||
private function createOpposite($label)
|
||||
{
|
||||
$label_id = $this->db->getConnection()->getLastId();
|
||||
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->insert(array(
|
||||
'label' => $label,
|
||||
'opposite_id' => $label_id,
|
||||
));
|
||||
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $label_id)
|
||||
->update(array(
|
||||
'opposite_id' => $this->db->getConnection()->getLastId()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a link
|
||||
*
|
||||
* @access public
|
||||
* @param array $values
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $values['id'])
|
||||
->update(array(
|
||||
'label' => $values['label'],
|
||||
'opposite_id' => $values['opposite_id'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a link a the relation to its opposite
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($link_id)
|
||||
{
|
||||
$this->db->table(self::TABLE)->eq('opposite_id', $link_id)->update(array('opposite_id' => 0));
|
||||
return $this->db->table(self::TABLE)->eq('id', $link_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('label', t('Field required')),
|
||||
new Validators\Unique('label', t('This label must be unique'), $this->db->getConnection(), self::TABLE),
|
||||
new Validators\NotEquals('label', 'opposite_label', t('The labels must be different')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('Field required')),
|
||||
new Validators\Required('opposite_id', t('Field required')),
|
||||
new Validators\Required('label', t('Field required')),
|
||||
new Validators\Unique('label', t('This label must be unique'), $this->db->getConnection(), self::TABLE),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -118,6 +118,18 @@ class Notification extends Base
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail subject for a given label
|
||||
*
|
||||
* @access private
|
||||
* @param string $label Label
|
||||
* @param array $data Template data
|
||||
*/
|
||||
private function getStandardMailSubject($label, array $data)
|
||||
{
|
||||
return sprintf('[%s][%s] %s (#%d)', $data['task']['project_name'], $label, $data['task']['title'], $data['task']['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail subject for a given template name
|
||||
*
|
||||
|
@ -129,40 +141,40 @@ class Notification extends Base
|
|||
{
|
||||
switch ($template) {
|
||||
case 'file_creation':
|
||||
$subject = e('[%s][New attachment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('New attachment'), $data);
|
||||
break;
|
||||
case 'comment_creation':
|
||||
$subject = e('[%s][New comment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('New comment'), $data);
|
||||
break;
|
||||
case 'comment_update':
|
||||
$subject = e('[%s][Comment updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('Comment updated'), $data);
|
||||
break;
|
||||
case 'subtask_creation':
|
||||
$subject = e('[%s][New subtask] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('New subtask'), $data);
|
||||
break;
|
||||
case 'subtask_update':
|
||||
$subject = e('[%s][Subtask updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('Subtask updated'), $data);
|
||||
break;
|
||||
case 'task_creation':
|
||||
$subject = e('[%s][New task] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('New task'), $data);
|
||||
break;
|
||||
case 'task_update':
|
||||
$subject = e('[%s][Task updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('Task updated'), $data);
|
||||
break;
|
||||
case 'task_close':
|
||||
$subject = e('[%s][Task closed] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('Task closed'), $data);
|
||||
break;
|
||||
case 'task_open':
|
||||
$subject = e('[%s][Task opened] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('Task opened'), $data);
|
||||
break;
|
||||
case 'task_move_column':
|
||||
$subject = e('[%s][Column Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('Column Change'), $data);
|
||||
break;
|
||||
case 'task_move_position':
|
||||
$subject = e('[%s][Position Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('Position Change'), $data);
|
||||
break;
|
||||
case 'task_assignee_change':
|
||||
$subject = e('[%s][Assignee Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
$subject = $this->getStandardMailSubject(t('Assignee Change'), $data);
|
||||
break;
|
||||
case 'task_due':
|
||||
$subject = e('[%s][Due tasks]', $data['project']);
|
||||
|
|
|
@ -95,27 +95,25 @@ class Project extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Get all projects, optionaly fetch stats for each project and can check users permissions
|
||||
* Get all projects
|
||||
*
|
||||
* @access public
|
||||
* @param bool $filter_permissions If true, remove projects not allowed for the current user
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($filter_permissions = false)
|
||||
public function getAll()
|
||||
{
|
||||
$projects = $this->db->table(self::TABLE)->asc('name')->findAll();
|
||||
return $this->db->table(self::TABLE)->asc('name')->findAll();
|
||||
}
|
||||
|
||||
if ($filter_permissions) {
|
||||
|
||||
foreach ($projects as $key => $project) {
|
||||
|
||||
if (! $this->projectPermission->isUserAllowed($project['id'], $this->userSession->getId())) {
|
||||
unset($projects[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $projects;
|
||||
/**
|
||||
* Get all project ids
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAllIds()
|
||||
{
|
||||
return $this->db->table(self::TABLE)->asc('name')->findAllByColumn('id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,10 +126,10 @@ class Project extends Base
|
|||
public function getList($prepend = true)
|
||||
{
|
||||
if ($prepend) {
|
||||
return array(t('None')) + $this->db->table(self::TABLE)->asc('name')->listing('id', 'name');
|
||||
return array(t('None')) + $this->db->hashtable(self::TABLE)->asc('name')->getAll('id', 'name');
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->asc('name')->listing('id', 'name');
|
||||
return $this->db->hashtable(self::TABLE)->asc('name')->getAll('id', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,10 +158,10 @@ class Project extends Base
|
|||
public function getListByStatus($status)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->hashtable(self::TABLE)
|
||||
->asc('name')
|
||||
->eq('is_active', $status)
|
||||
->listing('id', 'name');
|
||||
->getAll('id', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,7 +186,7 @@ class Project extends Base
|
|||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getStats($project_id)
|
||||
public function getTaskStats($project_id)
|
||||
{
|
||||
$stats = array();
|
||||
$stats['nb_active_tasks'] = 0;
|
||||
|
@ -208,62 +206,57 @@ class Project extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a project from another one.
|
||||
* Get stats for each column of a project
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_id Project Id
|
||||
* @return integer Cloned Project Id
|
||||
* @access public
|
||||
* @param array $project
|
||||
* @return array
|
||||
*/
|
||||
public function createProjectFromAnotherProject($project_id)
|
||||
public function getColumnStats(array &$project)
|
||||
{
|
||||
$project = $this->getById($project_id);
|
||||
$project['columns'] = $this->board->getColumns($project['id']);
|
||||
$stats = $this->board->getColumnStats($project['id']);
|
||||
|
||||
$values = array(
|
||||
'name' => $project['name'].' ('.t('Clone').')',
|
||||
'is_active' => true,
|
||||
'last_modified' => 0,
|
||||
'token' => '',
|
||||
'is_public' => 0,
|
||||
'is_private' => empty($project['is_private']) ? 0 : 1,
|
||||
);
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
return 0;
|
||||
foreach ($project['columns'] as &$column) {
|
||||
$column['nb_tasks'] = isset($stats[$column['id']]) ? $stats[$column['id']] : 0;
|
||||
}
|
||||
|
||||
return $this->db->getConnection()->getLastId();
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a project
|
||||
* Apply column stats to a collection of projects (filter callback)
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_id Project Id
|
||||
* @return integer Cloned Project Id
|
||||
* @access public
|
||||
* @param array $projects
|
||||
* @return array
|
||||
*/
|
||||
public function duplicate($project_id)
|
||||
public function applyColumnStats(array $projects)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Get the cloned project Id
|
||||
$clone_project_id = $this->createProjectFromAnotherProject($project_id);
|
||||
|
||||
if (! $clone_project_id) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
foreach ($projects as &$project) {
|
||||
$this->getColumnStats($project);
|
||||
}
|
||||
|
||||
foreach (array('board', 'category', 'projectPermission', 'action') as $model) {
|
||||
return $projects;
|
||||
}
|
||||
|
||||
if (! $this->$model->duplicate($project_id, $clone_project_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Get project summary for a list of project
|
||||
*
|
||||
* @access public
|
||||
* @param array $project_ids List of project id
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getQueryColumnStats(array $project_ids)
|
||||
{
|
||||
if (empty($project_ids)) {
|
||||
return $this->db->table(Project::TABLE)->limit(0);
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return (int) $clone_project_id;
|
||||
return $this->db
|
||||
->table(Project::TABLE)
|
||||
->in('id', $project_ids)
|
||||
->filter(array($this, 'applyColumnStats'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,11 +56,13 @@ class ProjectActivity extends Base
|
|||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $limit Maximum events number
|
||||
* @param integer $start Timestamp of earliest activity
|
||||
* @param integer $end Timestamp of latest activity
|
||||
* @return array
|
||||
*/
|
||||
public function getProject($project_id, $limit = 50)
|
||||
public function getProject($project_id, $limit = 50, $start = null, $end = null)
|
||||
{
|
||||
return $this->getProjects(array($project_id), $limit);
|
||||
return $this->getProjects(array($project_id), $limit, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,15 +71,17 @@ class ProjectActivity extends Base
|
|||
* @access public
|
||||
* @param integer[] $project_ids Projects id
|
||||
* @param integer $limit Maximum events number
|
||||
* @param integer $start Timestamp of earliest activity
|
||||
* @param integer $end Timestamp of latest activity
|
||||
* @return array
|
||||
*/
|
||||
public function getProjects(array $project_ids, $limit = 50)
|
||||
public function getProjects(array $project_ids, $limit = 50, $start = null, $end = null)
|
||||
{
|
||||
if (empty($project_ids)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$events = $this->db->table(self::TABLE)
|
||||
$query = $this->db->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.*',
|
||||
User::TABLE.'.username AS author_username',
|
||||
|
@ -85,9 +89,18 @@ class ProjectActivity extends Base
|
|||
)
|
||||
->in('project_id', $project_ids)
|
||||
->join(User::TABLE, 'id', 'creator_id')
|
||||
->desc('id')
|
||||
->limit($limit)
|
||||
->findAll();
|
||||
->desc(self::TABLE.'.id')
|
||||
->limit($limit);
|
||||
|
||||
if(!is_null($start)){
|
||||
$query->gte('date_creation', $start);
|
||||
}
|
||||
|
||||
if(!is_null($end)){
|
||||
$query->lte('date_creation', $end);
|
||||
}
|
||||
|
||||
$events = $query->findAll();
|
||||
|
||||
foreach ($events as &$event) {
|
||||
|
||||
|
@ -162,9 +175,9 @@ class ProjectActivity extends Base
|
|||
return t('%s moved the task #%d to the column "%s"', $event['author'], $event['task']['id'], $event['task']['column_title']);
|
||||
case Task::EVENT_MOVE_POSITION:
|
||||
return t('%s moved the task #%d to the position %d in the column "%s"', $event['author'], $event['task']['id'], $event['task']['position'], $event['task']['column_title']);
|
||||
case SubTask::EVENT_UPDATE:
|
||||
case Subtask::EVENT_UPDATE:
|
||||
return t('%s updated a subtask for the task #%d', $event['author'], $event['task']['id']);
|
||||
case SubTask::EVENT_CREATE:
|
||||
case Subtask::EVENT_CREATE:
|
||||
return t('%s created a subtask for the task #%d', $event['author'], $event['task']['id']);
|
||||
case Comment::EVENT_UPDATE:
|
||||
return t('%s updated a comment on the task #%d', $event['author'], $event['task']['id']);
|
||||
|
|
108
sources/app/Model/ProjectDuplication.php
Normal file
108
sources/app/Model/ProjectDuplication.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Project Duplication
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
* @author Antonio Rabelo
|
||||
*/
|
||||
class ProjectDuplication extends Base
|
||||
{
|
||||
/**
|
||||
* Get a valid project name for the duplication
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Project name
|
||||
* @param integer $max_length Max length allowed
|
||||
* @return string
|
||||
*/
|
||||
public function getClonedProjectName($name, $max_length = 50)
|
||||
{
|
||||
$suffix = ' ('.t('Clone').')';
|
||||
|
||||
if (strlen($name.$suffix) > $max_length) {
|
||||
$name = substr($name, 0, $max_length - strlen($suffix));
|
||||
}
|
||||
|
||||
return $name.$suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a project from another one
|
||||
*
|
||||
* @param integer $project_id Project Id
|
||||
* @return integer Cloned Project Id
|
||||
*/
|
||||
public function copy($project_id)
|
||||
{
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
$values = array(
|
||||
'name' => $this->getClonedProjectName($project['name']),
|
||||
'is_active' => true,
|
||||
'last_modified' => 0,
|
||||
'token' => '',
|
||||
'is_public' => 0,
|
||||
'is_private' => empty($project['is_private']) ? 0 : 1,
|
||||
);
|
||||
|
||||
if (! $this->db->table(Project::TABLE)->save($values)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->db->getConnection()->getLastId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a project with all settings
|
||||
*
|
||||
* @param integer $project_id Project Id
|
||||
* @param array $part_selection Selection of optional project parts to duplicate. Possible options: 'swimlane', 'action', 'category', 'task'
|
||||
* @return integer Cloned Project Id
|
||||
*/
|
||||
public function duplicate($project_id, $part_selection = array('category', 'action'))
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Get the cloned project Id
|
||||
$clone_project_id = $this->copy($project_id);
|
||||
|
||||
if (! $clone_project_id) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone Columns, Categories, Permissions and Actions
|
||||
$optional_parts = array('swimlane', 'action', 'category');
|
||||
foreach (array('board', 'category', 'projectPermission', 'action', 'swimlane') as $model) {
|
||||
|
||||
// Skip if optional part has not been selected
|
||||
if (in_array($model, $optional_parts) && ! in_array($model, $part_selection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->$model->duplicate($project_id, $clone_project_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
// Clone Tasks if in $part_selection
|
||||
if (in_array('task', $part_selection)) {
|
||||
$tasks = $this->taskFinder->getAll($project_id);
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
if (! $this->taskDuplication->duplicateToProject($task['id'], $clone_project_id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $clone_project_id;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Project Paginator
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectPaginator extends Base
|
||||
{
|
||||
/**
|
||||
* Get project summary for a list of project (number of tasks for each column)
|
||||
*
|
||||
* @access public
|
||||
* @param array $project_ids List of project id
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function projectSummaries(array $project_ids, $offset = 0, $limit = 25, $column = 'name', $direction = 'asc')
|
||||
{
|
||||
if (empty($project_ids)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$projects = $this->db
|
||||
->table(Project::TABLE)
|
||||
->in('id', $project_ids)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
|
||||
foreach ($projects as &$project) {
|
||||
|
||||
$project['columns'] = $this->board->getColumns($project['id']);
|
||||
$stats = $this->board->getColumnStats($project['id']);
|
||||
|
||||
foreach ($project['columns'] as &$column) {
|
||||
$column['nb_tasks'] = isset($stats[$column['id']]) ? $stats[$column['id']] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $projects;
|
||||
}
|
||||
}
|
|
@ -313,12 +313,62 @@ class ProjectPermission extends Base
|
|||
* @return array
|
||||
*/
|
||||
public function getMemberProjects($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->hashtable(Project::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->getAll('projects.id', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of project ids where the user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getMemberProjectIds($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Project::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->listing('projects.id', 'name');
|
||||
->findAllByColumn('projects.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of active project ids where the user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getActiveMemberProjectIds($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Project::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq(Project::TABLE.'.is_active', Project::ACTIVE)
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->findAllByColumn('projects.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of active projects where the user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getActiveMemberProjects($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->hashtable(Project::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq(Project::TABLE.'.is_active', Project::ACTIVE)
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->getAll('projects.id', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@ use SimpleValidator\Validators;
|
|||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubTask extends Base
|
||||
class Subtask extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
|
@ -65,6 +65,49 @@ class SubTask extends Base
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add subtask status status to the resultset
|
||||
*
|
||||
* @access public
|
||||
* @param array $subtasks Subtasks
|
||||
* @return array
|
||||
*/
|
||||
public function addStatusName(array $subtasks)
|
||||
{
|
||||
$status = $this->getStatusList();
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
$subtask['status_name'] = $status[$subtask['status']];
|
||||
}
|
||||
|
||||
return $subtasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query to fetch subtasks assigned to a user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param array $status List of status
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getUserQuery($user_id, array $status)
|
||||
{
|
||||
return $this->db->table(Subtask::TABLE)
|
||||
->columns(
|
||||
Subtask::TABLE.'.*',
|
||||
Task::TABLE.'.project_id',
|
||||
Task::TABLE.'.color_id',
|
||||
Project::TABLE.'.name AS project_name'
|
||||
)
|
||||
->eq('user_id', $user_id)
|
||||
->eq(Project::TABLE.'.is_active', Project::ACTIVE)
|
||||
->in(Subtask::TABLE.'.status', $status)
|
||||
->join(Task::TABLE, 'id', 'task_id')
|
||||
->join(Project::TABLE, 'id', 'project_id', Task::TABLE)
|
||||
->filter(array($this, 'addStatusName'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all subtasks for a given task
|
||||
*
|
||||
|
@ -74,19 +117,14 @@ class SubTask extends Base
|
|||
*/
|
||||
public function getAll($task_id)
|
||||
{
|
||||
$status = $this->getStatusList();
|
||||
$subtasks = $this->db->table(self::TABLE)
|
||||
->eq('task_id', $task_id)
|
||||
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->asc(self::TABLE.'.id')
|
||||
->findAll();
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
$subtask['status_name'] = $status[$subtask['status']];
|
||||
}
|
||||
|
||||
return $subtasks;
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('task_id', $task_id)
|
||||
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->asc(self::TABLE.'.id')
|
||||
->filter(array($this, 'addStatusName'))
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,18 +139,13 @@ class SubTask extends Base
|
|||
{
|
||||
if ($more) {
|
||||
|
||||
$subtask = $this->db->table(self::TABLE)
|
||||
->eq(self::TABLE.'.id', $subtask_id)
|
||||
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->findOne();
|
||||
|
||||
if ($subtask) {
|
||||
$status = $this->getStatusList();
|
||||
$subtask['status_name'] = $status[$subtask['status']];
|
||||
}
|
||||
|
||||
return $subtask;
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq(self::TABLE.'.id', $subtask_id)
|
||||
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->filter(array($this, 'addStatusName'))
|
||||
->findOne();
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('id', $subtask_id)->findOne();
|
||||
|
@ -165,6 +198,7 @@ class SubTask extends Base
|
|||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
|
||||
if ($result) {
|
||||
|
||||
$this->container['dispatcher']->dispatch(
|
||||
self::EVENT_UPDATE,
|
||||
new SubtaskEvent($values)
|
||||
|
@ -196,6 +230,37 @@ class SubTask extends Base
|
|||
return $this->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subtask in progress for this user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @return array
|
||||
*/
|
||||
public function getSubtaskInProgress($user_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->eq('status', self::STATUS_INPROGRESS)
|
||||
->eq('user_id', $user_id)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the user have a subtask in progress
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasSubtaskInProgress($user_id)
|
||||
{
|
||||
return $this->config->get('subtask_restriction') == 1 &&
|
||||
$this->db->table(self::TABLE)
|
||||
->eq('status', self::STATUS_INPROGRESS)
|
||||
->eq('user_id', $user_id)
|
||||
->count() === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove
|
||||
*
|
||||
|
@ -220,7 +285,7 @@ class SubTask extends Base
|
|||
{
|
||||
return $this->db->transaction(function ($db) use ($src_task_id, $dst_task_id) {
|
||||
|
||||
$subtasks = $db->table(SubTask::TABLE)
|
||||
$subtasks = $db->table(Subtask::TABLE)
|
||||
->columns('title', 'time_estimated')
|
||||
->eq('task_id', $src_task_id)
|
||||
->asc('id') // Explicit sorting for postgresql
|
||||
|
@ -230,7 +295,7 @@ class SubTask extends Base
|
|||
|
||||
$subtask['task_id'] = $dst_task_id;
|
||||
|
||||
if (! $db->table(SubTask::TABLE)->save($subtask)) {
|
||||
if (! $db->table(Subtask::TABLE)->save($subtask)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class SubtaskExport extends Base
|
|||
*/
|
||||
public function export($project_id, $from, $to)
|
||||
{
|
||||
$this->subtask_status = $this->subTask->getStatusList();
|
||||
$this->subtask_status = $this->subtask->getStatusList();
|
||||
$subtasks = $this->getSubtasks($project_id, $from, $to);
|
||||
$results = array($this->getColumns());
|
||||
|
||||
|
@ -86,25 +86,25 @@ class SubtaskExport extends Base
|
|||
* Get all subtasks for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param mixed $from Start date (timestamp or user formatted date)
|
||||
* @param mixed $to End date (timestamp or user formatted date)
|
||||
* @param integer $project_id Project id
|
||||
* @param mixed $from Start date (timestamp or user formatted date)
|
||||
* @param mixed $to End date (timestamp or user formatted date)
|
||||
* @return array
|
||||
*/
|
||||
public function getSubtasks($project_id, $from, $to)
|
||||
{
|
||||
if (! is_numeric($from)) {
|
||||
$from = $this->dateParser->resetDateToMidnight($this->dateParser->getTimestamp($from));
|
||||
$from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from));
|
||||
}
|
||||
|
||||
if (! is_numeric($to)) {
|
||||
$to = $this->dateParser->resetDateToMidnight(strtotime('+1 day', $this->dateParser->getTimestamp($to)));
|
||||
$to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to)));
|
||||
}
|
||||
|
||||
return $this->db->table(SubTask::TABLE)
|
||||
return $this->db->table(Subtask::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->columns(
|
||||
SubTask::TABLE.'.*',
|
||||
Subtask::TABLE.'.*',
|
||||
User::TABLE.'.username AS assignee_username',
|
||||
User::TABLE.'.name AS assignee_name',
|
||||
Task::TABLE.'.title AS task_title'
|
||||
|
@ -113,7 +113,7 @@ class SubtaskExport extends Base
|
|||
->lte('date_creation', $to)
|
||||
->join(Task::TABLE, 'id', 'task_id')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->asc(SubTask::TABLE.'.id')
|
||||
->asc(Subtask::TABLE.'.id')
|
||||
->findAll();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Subtask Paginator
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubtaskPaginator extends Base
|
||||
{
|
||||
/**
|
||||
* Get all subtasks assigned to a user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param array $status List of status
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function userSubtasks($user_id, array $status, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'asc')
|
||||
{
|
||||
$status_list = $this->subTask->getStatusList();
|
||||
|
||||
$subtasks = $this->db->table(SubTask::TABLE)
|
||||
->columns(
|
||||
SubTask::TABLE.'.*',
|
||||
Task::TABLE.'.project_id',
|
||||
Task::TABLE.'.color_id',
|
||||
Project::TABLE.'.name AS project_name'
|
||||
)
|
||||
->eq('user_id', $user_id)
|
||||
->in(SubTask::TABLE.'.status', $status)
|
||||
->join(Task::TABLE, 'id', 'task_id')
|
||||
->join(Project::TABLE, 'id', 'project_id', Task::TABLE)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
$subtask['status_name'] = $status_list[$subtask['status']];
|
||||
}
|
||||
|
||||
return $subtasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all subtasks assigned to the user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param array $status List of status
|
||||
* @return integer
|
||||
*/
|
||||
public function countUserSubtasks($user_id, array $status)
|
||||
{
|
||||
return $this->db
|
||||
->table(SubTask::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->in('status', $status)
|
||||
->count();
|
||||
}
|
||||
}
|
319
sources/app/Model/SubtaskTimeTracking.php
Normal file
319
sources/app/Model/SubtaskTimeTracking.php
Normal file
|
@ -0,0 +1,319 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Subtask timesheet
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubtaskTimeTracking extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'subtask_time_tracking';
|
||||
|
||||
/**
|
||||
* Get query for user timesheet (pagination)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getUserQuery($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.subtask_id',
|
||||
self::TABLE.'.end',
|
||||
self::TABLE.'.start',
|
||||
Subtask::TABLE.'.task_id',
|
||||
Subtask::TABLE.'.title AS subtask_title',
|
||||
Task::TABLE.'.title AS task_title',
|
||||
Task::TABLE.'.project_id',
|
||||
Task::TABLE.'.color_id'
|
||||
)
|
||||
->join(Subtask::TABLE, 'id', 'subtask_id')
|
||||
->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE)
|
||||
->eq(self::TABLE.'.user_id', $user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query for task timesheet (pagination)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getTaskQuery($task_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.subtask_id',
|
||||
self::TABLE.'.end',
|
||||
self::TABLE.'.start',
|
||||
self::TABLE.'.user_id',
|
||||
Subtask::TABLE.'.task_id',
|
||||
Subtask::TABLE.'.title AS subtask_title',
|
||||
Task::TABLE.'.project_id',
|
||||
User::TABLE.'.username',
|
||||
User::TABLE.'.name AS user_fullname'
|
||||
)
|
||||
->join(Subtask::TABLE, 'id', 'subtask_id')
|
||||
->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE)
|
||||
->join(User::TABLE, 'id', 'user_id', self::TABLE)
|
||||
->eq(Task::TABLE.'.id', $task_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query for project timesheet (pagination)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getProjectQuery($project_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.subtask_id',
|
||||
self::TABLE.'.end',
|
||||
self::TABLE.'.start',
|
||||
self::TABLE.'.user_id',
|
||||
Subtask::TABLE.'.task_id',
|
||||
Subtask::TABLE.'.title AS subtask_title',
|
||||
Task::TABLE.'.project_id',
|
||||
Task::TABLE.'.color_id',
|
||||
User::TABLE.'.username',
|
||||
User::TABLE.'.name AS user_fullname'
|
||||
)
|
||||
->join(Subtask::TABLE, 'id', 'subtask_id')
|
||||
->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE)
|
||||
->join(User::TABLE, 'id', 'user_id', self::TABLE)
|
||||
->eq(Task::TABLE.'.project_id', $project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all recorded time slots for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getUserTimesheet($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user calendar events
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param integer $start
|
||||
* @param integer $end
|
||||
* @return array
|
||||
*/
|
||||
public function getUserCalendarEvents($user_id, $start, $end)
|
||||
{
|
||||
$result = $this->getUserQuery($user_id)
|
||||
->addCondition($this->getCalendarCondition($start, $end))
|
||||
->findAll();
|
||||
|
||||
return $this->toCalendarEvents($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project calendar events
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $start
|
||||
* @param integer $end
|
||||
* @return array
|
||||
*/
|
||||
public function getProjectCalendarEvents($project_id, $start, $end)
|
||||
{
|
||||
$result = $this->getProjectQuery($project_id)
|
||||
->addCondition($this->getCalendarCondition($start, $end))
|
||||
->findAll();
|
||||
|
||||
return $this->toCalendarEvents($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time slots that should be displayed in the calendar time range
|
||||
*
|
||||
* @access private
|
||||
* @param string $start ISO8601 start date
|
||||
* @param string $end ISO8601 end date
|
||||
* @return string
|
||||
*/
|
||||
private function getCalendarCondition($start, $end)
|
||||
{
|
||||
$start_time = $this->dateParser->getTimestampFromIsoFormat($start);
|
||||
$end_time = $this->dateParser->getTimestampFromIsoFormat($end);
|
||||
$start_column = $this->db->escapeIdentifier('start');
|
||||
$end_column = $this->db->escapeIdentifier('end');
|
||||
|
||||
$conditions = array(
|
||||
"($start_column >= '$start_time' AND $start_column <= '$end_time')",
|
||||
"($start_column <= '$start_time' AND $end_column >= '$start_time')",
|
||||
"($start_column <= '$start_time' AND $end_column = '0')",
|
||||
);
|
||||
|
||||
return '('.implode(' OR ', $conditions).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a record set to calendar events
|
||||
*
|
||||
* @access private
|
||||
* @param array $rows
|
||||
* @return array
|
||||
*/
|
||||
private function toCalendarEvents(array $rows)
|
||||
{
|
||||
$events = array();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
|
||||
$user = isset($row['username']) ? ' ('.($row['user_fullname'] ?: $row['username']).')' : '';
|
||||
|
||||
$events[] = array(
|
||||
'id' => $row['id'],
|
||||
'subtask_id' => $row['subtask_id'],
|
||||
'title' => t('#%d', $row['task_id']).' '.$row['subtask_title'].$user,
|
||||
'start' => date('Y-m-d\TH:i:s', $row['start']),
|
||||
'end' => date('Y-m-d\TH:i:s', $row['end'] ?: time()),
|
||||
'backgroundColor' => $this->color->getBackgroundColor($row['color_id']),
|
||||
'borderColor' => $this->color->getBorderColor($row['color_id']),
|
||||
'textColor' => 'black',
|
||||
'url' => $this->helper->url('task', 'show', array('task_id' => $row['task_id'], 'project_id' => $row['project_id'])),
|
||||
'editable' => false,
|
||||
);
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log start time
|
||||
*
|
||||
* @access public
|
||||
* @param integer $subtask_id
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function logStartTime($subtask_id, $user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->insert(array('subtask_id' => $subtask_id, 'user_id' => $user_id, 'start' => time()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log end time
|
||||
*
|
||||
* @access public
|
||||
* @param integer $subtask_id
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function logEndTime($subtask_id, $user_id)
|
||||
{
|
||||
$this->updateSubtaskTimeSpent($subtask_id, $user_id);
|
||||
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('subtask_id', $subtask_id)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('end', 0)
|
||||
->update(array(
|
||||
'end' => time()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update task time tracking based on subtasks time tracking
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return bool
|
||||
*/
|
||||
public function updateTaskTimeTracking($task_id)
|
||||
{
|
||||
$result = $this->calculateSubtaskTime($task_id);
|
||||
|
||||
if (empty($result['total_spent']) && empty($result['total_estimated'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq('id', $task_id)
|
||||
->update(array(
|
||||
'time_spent' => $result['total_spent'],
|
||||
'time_estimated' => $result['total_estimated'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum time spent and time estimated for all subtasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return array
|
||||
*/
|
||||
public function calculateSubtaskTime($task_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Subtask::TABLE)
|
||||
->eq('task_id', $task_id)
|
||||
->columns(
|
||||
'SUM(time_spent) AS total_spent',
|
||||
'SUM(time_estimated) AS total_estimated'
|
||||
)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update subtask time spent based on the punch clock table
|
||||
*
|
||||
* @access public
|
||||
* @param integer $subtask_id
|
||||
* @param integer $user_id
|
||||
* @return bool
|
||||
*/
|
||||
public function updateSubtaskTimeSpent($subtask_id, $user_id)
|
||||
{
|
||||
$start_time = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('subtask_id', $subtask_id)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('end', 0)
|
||||
->findOneColumn('start');
|
||||
|
||||
$subtask = $this->subtask->getById($subtask_id);
|
||||
|
||||
return $start_time &&
|
||||
$this->subtask->update(array( // Fire the event subtask.update
|
||||
'id' => $subtask['id'],
|
||||
'time_spent' => $subtask['time_spent'] + round((time() - $start_time) / 3600, 1),
|
||||
'task_id' => $subtask['task_id'],
|
||||
));
|
||||
}
|
||||
}
|
|
@ -161,20 +161,20 @@ class Swimlane extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param boolean $prepend Prepend default value
|
||||
* @return array
|
||||
*/
|
||||
public function getSwimlanesList($project_id)
|
||||
public function getList($project_id, $prepend = false)
|
||||
{
|
||||
$swimlanes = $this->db->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->orderBy('position', 'asc')
|
||||
->listing('id', 'name');
|
||||
$swimlanes = array();
|
||||
$swimlanes[] = $this->db->table(Project::TABLE)->eq('id', $project_id)->findOneColumn('default_swimlane');
|
||||
|
||||
$swimlanes[0] = $this->db->table(Project::TABLE)
|
||||
->eq('id', $project_id)
|
||||
->findOneColumn('default_swimlane');
|
||||
$swimlanes = array_merge(
|
||||
$swimlanes,
|
||||
$this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->orderBy('name', 'asc')->getAll('id', 'name')
|
||||
);
|
||||
|
||||
return $swimlanes;
|
||||
return $prepend ? array(-1 => t('All swimlanes')) + $swimlanes : $swimlanes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,7 +183,7 @@ class Swimlane extends Base
|
|||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param string $name
|
||||
* @return bool
|
||||
* @return integer|boolean
|
||||
*/
|
||||
public function create($project_id, $name)
|
||||
{
|
||||
|
@ -354,11 +354,11 @@ class Swimlane extends Base
|
|||
*/
|
||||
public function moveDown($project_id, $swimlane_id)
|
||||
{
|
||||
$swimlanes = $this->db->table(self::TABLE)
|
||||
$swimlanes = $this->db->hashtable(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', self::ACTIVE)
|
||||
->asc('position')
|
||||
->listing('id', 'position');
|
||||
->getAll('id', 'position');
|
||||
|
||||
$positions = array_flip($swimlanes);
|
||||
|
||||
|
@ -388,11 +388,11 @@ class Swimlane extends Base
|
|||
*/
|
||||
public function moveUp($project_id, $swimlane_id)
|
||||
{
|
||||
$swimlanes = $this->db->table(self::TABLE)
|
||||
$swimlanes = $this->db->hashtable(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', self::ACTIVE)
|
||||
->asc('position')
|
||||
->listing('id', 'position');
|
||||
->getAll('id', 'position');
|
||||
|
||||
$positions = array_flip($swimlanes);
|
||||
|
||||
|
@ -412,6 +412,37 @@ class Swimlane extends Base
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate Swimlane to project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_from Project Template
|
||||
* @param integer $project_to Project that receives the copy
|
||||
* @return integer|boolean
|
||||
*/
|
||||
|
||||
public function duplicate($project_from, $project_to)
|
||||
{
|
||||
$swimlanes = $this->getAll($project_from);
|
||||
|
||||
foreach ($swimlanes as $swimlane) {
|
||||
|
||||
unset($swimlane['id']);
|
||||
$swimlane['project_id'] = $project_to;
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($swimlane)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$default_swimlane = $this->getDefault($project_from);
|
||||
$default_swimlane['id'] = $project_to;
|
||||
|
||||
$this->updateDefault($default_swimlane);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate creation
|
||||
*
|
||||
|
|
|
@ -62,6 +62,7 @@ class TaskCreation extends Base
|
|||
$values['swimlane_id'] = empty($values['swimlane_id']) ? 0 : $values['swimlane_id'];
|
||||
$values['date_creation'] = time();
|
||||
$values['date_modification'] = $values['date_creation'];
|
||||
$values['date_moved'] = $values['date_creation'];
|
||||
$values['position'] = $this->taskFinder->countByColumnAndSwimlaneId($values['project_id'], $values['column_id'], $values['swimlane_id']) + 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ class TaskDuplication extends Base
|
|||
$new_task_id = $this->taskCreation->create($values);
|
||||
|
||||
if ($new_task_id) {
|
||||
$this->subTask->duplicate($task_id, $new_task_id);
|
||||
$this->subtask->duplicate($task_id, $new_task_id);
|
||||
}
|
||||
|
||||
return $new_task_id;
|
||||
|
|
|
@ -24,7 +24,7 @@ class TaskExport extends Base
|
|||
public function export($project_id, $from, $to)
|
||||
{
|
||||
$tasks = $this->getTasks($project_id, $from, $to);
|
||||
$swimlanes = $this->swimlane->getSwimlanesList($project_id);
|
||||
$swimlanes = $this->swimlane->getList($project_id);
|
||||
$results = array($this->getColumns());
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
|
@ -77,11 +77,11 @@ class TaskExport extends Base
|
|||
';
|
||||
|
||||
if (! is_numeric($from)) {
|
||||
$from = $this->dateParser->resetDateToMidnight($this->dateParser->getTimestamp($from));
|
||||
$from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from));
|
||||
}
|
||||
|
||||
if (! is_numeric($to)) {
|
||||
$to = $this->dateParser->resetDateToMidnight(strtotime('+1 day', $this->dateParser->getTimestamp($to)));
|
||||
$to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to)));
|
||||
}
|
||||
|
||||
$rq = $this->db->execute($sql, array($from, $to, $project_id));
|
||||
|
|
150
sources/app/Model/TaskFilter.php
Normal file
150
sources/app/Model/TaskFilter.php
Normal file
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Filter
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskFilter extends Base
|
||||
{
|
||||
private $query;
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->query = $this->db->table(Task::TABLE);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function excludeTasks(array $task_ids)
|
||||
{
|
||||
$this->query->notin('id', $task_ids);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByTitle($title)
|
||||
{
|
||||
$this->query->ilike('title', '%'.$title.'%');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByProjects(array $project_ids)
|
||||
{
|
||||
$this->query->in('project_id', $project_ids);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByProject($project_id)
|
||||
{
|
||||
if ($project_id > 0) {
|
||||
$this->query->eq('project_id', $project_id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByCategory($category_id)
|
||||
{
|
||||
if ($category_id >= 0) {
|
||||
$this->query->eq('category_id', $category_id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByOwner($owner_id)
|
||||
{
|
||||
if ($owner_id >= 0) {
|
||||
$this->query->eq('owner_id', $owner_id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByColor($color_id)
|
||||
{
|
||||
if ($color_id !== '') {
|
||||
$this->query->eq('color_id', $color_id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByColumn($column_id)
|
||||
{
|
||||
if ($column_id >= 0) {
|
||||
$this->query->eq('column_id', $column_id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterBySwimlane($swimlane_id)
|
||||
{
|
||||
if ($swimlane_id >= 0) {
|
||||
$this->query->eq('swimlane_id', $swimlane_id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByStatus($is_active)
|
||||
{
|
||||
if ($is_active >= 0) {
|
||||
$this->query->eq('is_active', $is_active);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterByDueDateRange($start, $end)
|
||||
{
|
||||
$this->query->gte('date_due', $this->dateParser->getTimestampFromIsoFormat($start));
|
||||
$this->query->lte('date_due', $this->dateParser->getTimestampFromIsoFormat($end));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function findAll()
|
||||
{
|
||||
return $this->query->findAll();
|
||||
}
|
||||
|
||||
public function toAutoCompletion()
|
||||
{
|
||||
return $this->query->columns('id', 'title')->filter(function(array $results) {
|
||||
|
||||
foreach ($results as &$result) {
|
||||
$result['value'] = $result['title'];
|
||||
$result['label'] = '#'.$result['id'].' - '.$result['title'];
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
||||
})->findAll();
|
||||
}
|
||||
|
||||
public function toCalendarEvents()
|
||||
{
|
||||
$events = array();
|
||||
|
||||
foreach ($this->query->findAll() as $task) {
|
||||
$events[] = array(
|
||||
'timezoneParam' => $this->config->getCurrentTimezone(),
|
||||
'id' => $task['id'],
|
||||
'title' => t('#%d', $task['id']).' '.$task['title'],
|
||||
'start' => date('Y-m-d', $task['date_due']),
|
||||
'end' => date('Y-m-d', $task['date_due']),
|
||||
'allday' => true,
|
||||
'backgroundColor' => $this->color->getBackgroundColor($task['color_id']),
|
||||
'borderColor' => $this->color->getBorderColor($task['color_id']),
|
||||
'textColor' => 'black',
|
||||
'url' => $this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
|
||||
);
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
}
|
|
@ -13,12 +13,69 @@ use PDO;
|
|||
class TaskFinder extends Base
|
||||
{
|
||||
/**
|
||||
* Common request to fetch a list of tasks
|
||||
* 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 task search
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $search Search terms
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getSearchQuery($project_id, $search)
|
||||
{
|
||||
return $this->getExtendedQuery()
|
||||
->eq('project_id', $project_id)
|
||||
->ilike('title', '%'.$search.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query for assigned user tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getUserQuery($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->columns(
|
||||
'tasks.id',
|
||||
'tasks.title',
|
||||
'tasks.date_due',
|
||||
'tasks.date_creation',
|
||||
'tasks.project_id',
|
||||
'tasks.color_id',
|
||||
'tasks.time_spent',
|
||||
'tasks.time_estimated',
|
||||
'projects.name AS project_name'
|
||||
)
|
||||
->join(Project::TABLE, 'id', 'project_id')
|
||||
->eq(Task::TABLE.'.owner_id', $user_id)
|
||||
->eq(Task::TABLE.'.is_active', Task::STATUS_OPEN)
|
||||
->eq(Project::TABLE.'.is_active', Project::ACTIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended query
|
||||
*
|
||||
* @access public
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getQuery()
|
||||
public function getExtendedQuery()
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
|
@ -27,6 +84,7 @@ class TaskFinder extends Base
|
|||
'(SELECT count(*) FROM task_has_files WHERE task_id=tasks.id) AS nb_files',
|
||||
'(SELECT count(*) FROM task_has_subtasks WHERE task_id=tasks.id) AS nb_subtasks',
|
||||
'(SELECT count(*) FROM task_has_subtasks WHERE task_id=tasks.id AND status=2) AS nb_completed_subtasks',
|
||||
'(SELECT count(*) FROM ' . TaskLink::TABLE . ' WHERE ' . TaskLink::TABLE . '.task_id = tasks.id) AS nb_links',
|
||||
'tasks.id',
|
||||
'tasks.reference',
|
||||
'tasks.title',
|
||||
|
@ -45,6 +103,7 @@ class TaskFinder extends Base
|
|||
'tasks.is_active',
|
||||
'tasks.score',
|
||||
'tasks.category_id',
|
||||
'tasks.date_moved',
|
||||
'users.username AS assignee_username',
|
||||
'users.name AS assignee_name'
|
||||
)
|
||||
|
@ -62,7 +121,7 @@ class TaskFinder extends Base
|
|||
*/
|
||||
public function getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id = 0)
|
||||
{
|
||||
return $this->getQuery()
|
||||
return $this->getExtendedQuery()
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $column_id)
|
||||
->eq('swimlane_id', $swimlane_id)
|
||||
|
@ -117,6 +176,18 @@ class TaskFinder extends Base
|
|||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project id for a given task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return integer
|
||||
*/
|
||||
public function getProjectId($task_id)
|
||||
{
|
||||
return (int) $this->db->table(Task::TABLE)->eq('id', $task_id)->findOneColumn('project_id') ?: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a task by the id
|
||||
*
|
||||
|
@ -173,6 +244,7 @@ class TaskFinder extends Base
|
|||
tasks.score,
|
||||
tasks.category_id,
|
||||
tasks.swimlane_id,
|
||||
tasks.date_moved,
|
||||
project_has_categories.name AS category_name,
|
||||
projects.name AS project_name,
|
||||
columns.title AS column_title,
|
||||
|
|
144
sources/app/Model/TaskLink.php
Normal file
144
sources/app/Model/TaskLink.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* TaskLink model
|
||||
*
|
||||
* @package model
|
||||
* @author Olivier Maridat
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskLink extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'task_has_links';
|
||||
|
||||
/**
|
||||
* Get a task link
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_link_id Task link id
|
||||
* @return array
|
||||
*/
|
||||
public function getById($task_link_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $task_link_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all links attached to a task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return array
|
||||
*/
|
||||
public function getLinks($task_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.opposite_task_id AS task_id',
|
||||
Link::TABLE.'.label',
|
||||
Task::TABLE.'.title',
|
||||
Task::TABLE.'.is_active',
|
||||
Task::TABLE.'.project_id',
|
||||
Board::TABLE.'.title AS column_title'
|
||||
)
|
||||
->eq(self::TABLE.'.task_id', $task_id)
|
||||
->join(Link::TABLE, 'id', 'link_id')
|
||||
->join(Task::TABLE, 'id', 'opposite_task_id')
|
||||
->join(Board::TABLE, 'id', 'column_id', Task::TABLE)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new link
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $opposite_task_id Opposite task id
|
||||
* @param integer $link_id Link id
|
||||
* @return boolean
|
||||
*/
|
||||
public function create($task_id, $opposite_task_id, $link_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Create the original link
|
||||
$this->db->table(self::TABLE)->insert(array(
|
||||
'task_id' => $task_id,
|
||||
'opposite_task_id' => $opposite_task_id,
|
||||
'link_id' => $link_id,
|
||||
));
|
||||
|
||||
$link_id = $this->link->getOppositeLinkId($link_id);
|
||||
|
||||
// Create the opposite link
|
||||
$this->db->table(self::TABLE)->insert(array(
|
||||
'task_id' => $opposite_task_id,
|
||||
'opposite_task_id' => $task_id,
|
||||
'link_id' => $link_id,
|
||||
));
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a link between two tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_link_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($task_link_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$link = $this->getById($task_link_id);
|
||||
$link_id = $this->link->getOppositeLinkId($link['link_id']);
|
||||
|
||||
$this->db->table(self::TABLE)->eq('id', $task_link_id)->remove();
|
||||
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('opposite_task_id', $link['task_id'])
|
||||
->eq('task_id', $link['opposite_task_id'])
|
||||
->eq('link_id', $link_id)->remove();
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('task_id', t('Field required')),
|
||||
new Validators\Required('link_id', t('Field required')),
|
||||
new Validators\Required('title', t('Field required')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Paginator model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskPaginator extends Base
|
||||
{
|
||||
/**
|
||||
* Task search with pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $search Search terms
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function searchTasks($project_id, $search, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'DESC')
|
||||
{
|
||||
return $this->taskFinder->getQuery()
|
||||
->eq('project_id', $project_id)
|
||||
->ilike('title', '%'.$search.'%')
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of tasks for a custom search
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $search Search terms
|
||||
* @return integer
|
||||
*/
|
||||
public function countSearchTasks($project_id, $search)
|
||||
{
|
||||
return $this->db->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->ilike('title', '%'.$search.'%')
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all completed tasks with pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function closedTasks($project_id, $offset = 0, $limit = 25, $column = 'tasks.date_completed', $direction = 'DESC')
|
||||
{
|
||||
return $this->taskFinder->getQuery()
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', Task::STATUS_CLOSED)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all closed tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return integer
|
||||
*/
|
||||
public function countClosedTasks($project_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', Task::STATUS_CLOSED)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all open tasks for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function userTasks($user_id, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'ASC')
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->columns(
|
||||
'tasks.id',
|
||||
'tasks.title',
|
||||
'tasks.date_due',
|
||||
'tasks.date_creation',
|
||||
'tasks.project_id',
|
||||
'tasks.color_id',
|
||||
'projects.name AS project_name'
|
||||
)
|
||||
->join(Project::TABLE, 'id', 'project_id')
|
||||
->eq('tasks.owner_id', $user_id)
|
||||
->eq('tasks.is_active', Task::STATUS_OPEN)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all tasks assigned to the user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return integer
|
||||
*/
|
||||
public function countUserTasks($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq('owner_id', $user_id)
|
||||
->eq('is_active', Task::STATUS_OPEN)
|
||||
->count();
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ class TaskPosition extends Base
|
|||
$result = $this->calculateAndSave($project_id, $task_id, $column_id, $position, $swimlane_id);
|
||||
|
||||
if ($result) {
|
||||
|
||||
|
||||
if ($original_task['swimlane_id'] != $swimlane_id) {
|
||||
$this->calculateAndSave($project_id, 0, $column_id, 1, $original_task['swimlane_id']);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,23 @@ use Event\TaskEvent;
|
|||
*/
|
||||
class TaskStatus extends Base
|
||||
{
|
||||
/**
|
||||
* Return the list of statuses
|
||||
*
|
||||
* @access public
|
||||
* @param boolean $prepend Prepend default value
|
||||
* @return array
|
||||
*/
|
||||
public function getList($prepend = false)
|
||||
{
|
||||
$listing = $prepend ? array(-1 => t('All status')) : array();
|
||||
|
||||
return $listing + array(
|
||||
Task::STATUS_OPEN => t('Open'),
|
||||
Task::STATUS_CLOSED => t('Closed'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the task is closed
|
||||
*
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Time tracking model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TimeTracking extends Base
|
||||
{
|
||||
/**
|
||||
* Calculate time metrics for a task
|
||||
*
|
||||
* Use subtasks time metrics if not empty otherwise return task time metrics
|
||||
*
|
||||
* @access public
|
||||
* @param array $task Task properties
|
||||
* @param array $subtasks Subtasks list
|
||||
* @return array
|
||||
*/
|
||||
public function getTaskTimesheet(array $task, array $subtasks)
|
||||
{
|
||||
$timesheet = array(
|
||||
'time_spent' => 0,
|
||||
'time_estimated' => 0,
|
||||
'time_remaining' => 0,
|
||||
);
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
$timesheet['time_estimated'] += $subtask['time_estimated'];
|
||||
$timesheet['time_spent'] += $subtask['time_spent'];
|
||||
}
|
||||
|
||||
if ($timesheet['time_estimated'] == 0 && $timesheet['time_spent'] == 0) {
|
||||
$timesheet['time_estimated'] = $task['time_estimated'];
|
||||
$timesheet['time_spent'] = $task['time_spent'];
|
||||
}
|
||||
|
||||
$timesheet['time_remaining'] = $timesheet['time_estimated'] - $timesheet['time_spent'];
|
||||
|
||||
return $timesheet;
|
||||
}
|
||||
}
|
|
@ -28,6 +28,42 @@ class User extends Base
|
|||
*/
|
||||
const EVERYBODY_ID = -1;
|
||||
|
||||
/**
|
||||
* Return true if the user exists
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists($user_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $user_id)->count() === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query to fetch all users
|
||||
*
|
||||
* @access public
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
'id',
|
||||
'username',
|
||||
'name',
|
||||
'email',
|
||||
'is_admin',
|
||||
'default_project_id',
|
||||
'is_ldap_user',
|
||||
'notifications_enabled',
|
||||
'google_id',
|
||||
'github_id'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full name
|
||||
*
|
||||
|
@ -112,54 +148,7 @@ class User extends Base
|
|||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->asc('username')
|
||||
->columns(
|
||||
'id',
|
||||
'username',
|
||||
'name',
|
||||
'email',
|
||||
'is_admin',
|
||||
'default_project_id',
|
||||
'is_ldap_user',
|
||||
'notifications_enabled',
|
||||
'google_id',
|
||||
'github_id'
|
||||
)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users with pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function paginate($offset = 0, $limit = 25, $column = 'username', $direction = 'ASC')
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
'id',
|
||||
'username',
|
||||
'name',
|
||||
'email',
|
||||
'is_admin',
|
||||
'default_project_id',
|
||||
'is_ldap_user',
|
||||
'notifications_enabled',
|
||||
'google_id',
|
||||
'github_id'
|
||||
)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
return $this->getQuery()->asc('username')->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,8 +4,110 @@ namespace Schema;
|
|||
|
||||
use PDO;
|
||||
use Core\Security;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 41;
|
||||
const VERSION = 46;
|
||||
|
||||
function version_46($pdo)
|
||||
{
|
||||
$pdo->exec("CREATE TABLE links (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
label VARCHAR(255) NOT NULL,
|
||||
opposite_id INT DEFAULT 0,
|
||||
PRIMARY KEY(id),
|
||||
UNIQUE(label)
|
||||
) ENGINE=InnoDB CHARSET=utf8");
|
||||
|
||||
$pdo->exec("CREATE TABLE task_has_links (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
link_id INT NOT NULL,
|
||||
task_id INT NOT NULL,
|
||||
opposite_task_id INT NOT NULL,
|
||||
FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE=InnoDB CHARSET=utf8");
|
||||
|
||||
$pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)");
|
||||
$pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)");
|
||||
|
||||
$rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)');
|
||||
$rq->execute(array('relates to', 0));
|
||||
$rq->execute(array('blocks', 3));
|
||||
$rq->execute(array('is blocked by', 2));
|
||||
$rq->execute(array('duplicates', 5));
|
||||
$rq->execute(array('is duplicated by', 4));
|
||||
$rq->execute(array('is a child of', 7));
|
||||
$rq->execute(array('is a parent of', 6));
|
||||
$rq->execute(array('targets milestone', 9));
|
||||
$rq->execute(array('is a milestone of', 8));
|
||||
$rq->execute(array('fixes', 11));
|
||||
$rq->execute(array('is fixed by', 10));
|
||||
}
|
||||
|
||||
function version_45($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE tasks ADD COLUMN date_moved INT DEFAULT 0');
|
||||
|
||||
/* Update tasks.date_moved from project_activities table if tasks.date_moved = null or 0.
|
||||
* We take max project_activities.date_creation where event_name in task.create','task.move.column
|
||||
* since creation date is always less than task moves
|
||||
*/
|
||||
$pdo->exec("UPDATE tasks
|
||||
SET date_moved = (
|
||||
SELECT md
|
||||
FROM (
|
||||
SELECT task_id, max(date_creation) md
|
||||
FROM project_activities
|
||||
WHERE event_name IN ('task.create', 'task.move.column')
|
||||
GROUP BY task_id
|
||||
) src
|
||||
WHERE id = src.task_id
|
||||
)
|
||||
WHERE (date_moved IS NULL OR date_moved = 0) AND id IN (
|
||||
SELECT task_id
|
||||
FROM (
|
||||
SELECT task_id, max(date_creation) md
|
||||
FROM project_activities
|
||||
WHERE event_name IN ('task.create', 'task.move.column')
|
||||
GROUP BY task_id
|
||||
) src
|
||||
)");
|
||||
|
||||
// If there is no activities for some tasks use the date_creation
|
||||
$pdo->exec("UPDATE tasks SET date_moved = date_creation WHERE date_moved IS NULL OR date_moved = 0");
|
||||
}
|
||||
|
||||
function version_44($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN disable_login_form TINYINT(1) DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_43($pdo)
|
||||
{
|
||||
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
|
||||
$rq->execute(array('subtask_restriction', '0'));
|
||||
$rq->execute(array('subtask_time_tracking', '0'));
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE subtask_time_tracking (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
subtask_id INT NOT NULL,
|
||||
start INT DEFAULT 0,
|
||||
end INT DEFAULT 0,
|
||||
PRIMARY KEY(id),
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
}
|
||||
|
||||
function version_42($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE columns ADD COLUMN description TEXT');
|
||||
}
|
||||
|
||||
function version_41($pdo)
|
||||
{
|
||||
|
@ -59,7 +161,7 @@ function version_38($pdo)
|
|||
");
|
||||
|
||||
$pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INT DEFAULT 0');
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT '".t('Default swimlane')."'");
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT 'Default swimlane'");
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane INT DEFAULT 1");
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,107 @@ namespace Schema;
|
|||
|
||||
use PDO;
|
||||
use Core\Security;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 22;
|
||||
const VERSION = 27;
|
||||
|
||||
function version_27($pdo)
|
||||
{
|
||||
$pdo->exec('CREATE TABLE links (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"label" VARCHAR(255) NOT NULL,
|
||||
"opposite_id" INTEGER DEFAULT 0,
|
||||
UNIQUE("label")
|
||||
)');
|
||||
|
||||
$pdo->exec("CREATE TABLE task_has_links (
|
||||
id SERIAL PRIMARY KEY,
|
||||
link_id INTEGER NOT NULL,
|
||||
task_id INTEGER NOT NULL,
|
||||
opposite_task_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
)");
|
||||
|
||||
$pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)");
|
||||
$pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)");
|
||||
|
||||
$rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)');
|
||||
$rq->execute(array('relates to', 0));
|
||||
$rq->execute(array('blocks', 3));
|
||||
$rq->execute(array('is blocked by', 2));
|
||||
$rq->execute(array('duplicates', 5));
|
||||
$rq->execute(array('is duplicated by', 4));
|
||||
$rq->execute(array('is a child of', 7));
|
||||
$rq->execute(array('is a parent of', 6));
|
||||
$rq->execute(array('targets milestone', 9));
|
||||
$rq->execute(array('is a milestone of', 8));
|
||||
$rq->execute(array('fixes', 11));
|
||||
$rq->execute(array('is fixed by', 10));
|
||||
}
|
||||
|
||||
function version_26($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE tasks ADD COLUMN date_moved INT DEFAULT 0');
|
||||
|
||||
/* Update tasks.date_moved from project_activities table if tasks.date_moved = null or 0.
|
||||
* We take max project_activities.date_creation where event_name in task.create','task.move.column
|
||||
* since creation date is always less than task moves
|
||||
*/
|
||||
$pdo->exec("UPDATE tasks
|
||||
SET date_moved = (
|
||||
SELECT md
|
||||
FROM (
|
||||
SELECT task_id, max(date_creation) md
|
||||
FROM project_activities
|
||||
WHERE event_name IN ('task.create', 'task.move.column')
|
||||
GROUP BY task_id
|
||||
) src
|
||||
WHERE id = src.task_id
|
||||
)
|
||||
WHERE (date_moved IS NULL OR date_moved = 0) AND id IN (
|
||||
SELECT task_id
|
||||
FROM (
|
||||
SELECT task_id, max(date_creation) md
|
||||
FROM project_activities
|
||||
WHERE event_name IN ('task.create', 'task.move.column')
|
||||
GROUP BY task_id
|
||||
) src
|
||||
)");
|
||||
|
||||
// If there is no activities for some tasks use the date_creation
|
||||
$pdo->exec("UPDATE tasks SET date_moved = date_creation WHERE date_moved IS NULL OR date_moved = 0");
|
||||
}
|
||||
|
||||
function version_25($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN disable_login_form BOOLEAN DEFAULT '0'");
|
||||
}
|
||||
|
||||
function version_24($pdo)
|
||||
{
|
||||
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
|
||||
$rq->execute(array('subtask_restriction', '0'));
|
||||
$rq->execute(array('subtask_time_tracking', '0'));
|
||||
|
||||
$pdo->exec('
|
||||
CREATE TABLE subtask_time_tracking (
|
||||
id SERIAL PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"subtask_id" INTEGER NOT NULL,
|
||||
"start" INTEGER DEFAULT 0,
|
||||
"end" INTEGER DEFAULT 0,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE
|
||||
)
|
||||
');
|
||||
}
|
||||
|
||||
function version_23($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE columns ADD COLUMN description TEXT');
|
||||
}
|
||||
|
||||
function version_22($pdo)
|
||||
{
|
||||
|
@ -30,7 +129,7 @@ function version_21($pdo)
|
|||
$rq->execute();
|
||||
$project_ids = $rq->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
|
||||
$rq = $pdo->prepare('UPDATE project_has_users SET is_owner=1 WHERE project_id=?');
|
||||
$rq = $pdo->prepare("UPDATE project_has_users SET is_owner='1' WHERE project_id=?");
|
||||
|
||||
foreach ($project_ids as $project_id) {
|
||||
$rq->execute(array($project_id));
|
||||
|
@ -58,7 +157,7 @@ function version_19($pdo)
|
|||
");
|
||||
|
||||
$pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INTEGER DEFAULT 0');
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT '".t('Default swimlane')."'");
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT 'Default swimlane'");
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane BOOLEAN DEFAULT '1'");
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,107 @@ namespace Schema;
|
|||
|
||||
use Core\Security;
|
||||
use PDO;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 40;
|
||||
const VERSION = 45;
|
||||
|
||||
function version_45($pdo)
|
||||
{
|
||||
$pdo->exec("CREATE TABLE links (
|
||||
id INTEGER PRIMARY KEY,
|
||||
label TEXT NOT NULL,
|
||||
opposite_id INTEGER DEFAULT 0,
|
||||
UNIQUE(label)
|
||||
)");
|
||||
|
||||
$pdo->exec("CREATE TABLE task_has_links (
|
||||
id INTEGER PRIMARY KEY,
|
||||
link_id INTEGER NOT NULL,
|
||||
task_id INTEGER NOT NULL,
|
||||
opposite_task_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
)");
|
||||
|
||||
$pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)");
|
||||
$pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)");
|
||||
|
||||
$rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)');
|
||||
$rq->execute(array('relates to', 0));
|
||||
$rq->execute(array('blocks', 3));
|
||||
$rq->execute(array('is blocked by', 2));
|
||||
$rq->execute(array('duplicates', 5));
|
||||
$rq->execute(array('is duplicated by', 4));
|
||||
$rq->execute(array('is a child of', 7));
|
||||
$rq->execute(array('is a parent of', 6));
|
||||
$rq->execute(array('targets milestone', 9));
|
||||
$rq->execute(array('is a milestone of', 8));
|
||||
$rq->execute(array('fixes', 11));
|
||||
$rq->execute(array('is fixed by', 10));
|
||||
}
|
||||
|
||||
function version_44($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE tasks ADD COLUMN date_moved INTEGER DEFAULT 0');
|
||||
|
||||
/* Update tasks.date_moved from project_activities table if tasks.date_moved = null or 0.
|
||||
* We take max project_activities.date_creation where event_name in task.create','task.move.column
|
||||
* since creation date is always less than task moves
|
||||
*/
|
||||
$pdo->exec("UPDATE tasks
|
||||
SET date_moved = (
|
||||
SELECT md
|
||||
FROM (
|
||||
SELECT task_id, max(date_creation) md
|
||||
FROM project_activities
|
||||
WHERE event_name IN ('task.create', 'task.move.column')
|
||||
GROUP BY task_id
|
||||
) src
|
||||
WHERE id = src.task_id
|
||||
)
|
||||
WHERE (date_moved IS NULL OR date_moved = 0) AND id IN (
|
||||
SELECT task_id
|
||||
FROM (
|
||||
SELECT task_id, max(date_creation) md
|
||||
FROM project_activities
|
||||
WHERE event_name IN ('task.create', 'task.move.column')
|
||||
GROUP BY task_id
|
||||
) src
|
||||
)");
|
||||
|
||||
// If there is no activities for some tasks use the date_creation
|
||||
$pdo->exec("UPDATE tasks SET date_moved = date_creation WHERE date_moved IS NULL OR date_moved = 0");
|
||||
}
|
||||
|
||||
function version_43($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN disable_login_form INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_42($pdo)
|
||||
{
|
||||
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
|
||||
$rq->execute(array('subtask_restriction', '0'));
|
||||
$rq->execute(array('subtask_time_tracking', '0'));
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE subtask_time_tracking (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
subtask_id INTEGER NOT NULL,
|
||||
start INTEGER DEFAULT 0,
|
||||
end INTEGER DEFAULT 0,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
}
|
||||
|
||||
function version_41($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE columns ADD COLUMN description TEXT');
|
||||
}
|
||||
|
||||
function version_40($pdo)
|
||||
{
|
||||
|
@ -58,7 +157,7 @@ function version_37($pdo)
|
|||
");
|
||||
|
||||
$pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INTEGER DEFAULT 0');
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane TEXT DEFAULT '".t('Default swimlane')."'");
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane TEXT DEFAULT 'Default swimlane'");
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane INTEGER DEFAULT 1");
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace ServiceProvider;
|
||||
|
||||
use Core\Paginator;
|
||||
use Model\Config;
|
||||
use Model\Project;
|
||||
use Model\Webhook;
|
||||
|
@ -23,24 +24,26 @@ class ClassProvider implements ServiceProviderInterface
|
|||
'DateParser',
|
||||
'File',
|
||||
'LastLogin',
|
||||
'Link',
|
||||
'Notification',
|
||||
'Project',
|
||||
'ProjectActivity',
|
||||
'ProjectAnalytic',
|
||||
'ProjectDuplication',
|
||||
'ProjectDailySummary',
|
||||
'ProjectPaginator',
|
||||
'ProjectPermission',
|
||||
'SubTask',
|
||||
'SubtaskPaginator',
|
||||
'Subtask',
|
||||
'SubtaskExport',
|
||||
'SubtaskTimeTracking',
|
||||
'Swimlane',
|
||||
'Task',
|
||||
'TaskCreation',
|
||||
'TaskDuplication',
|
||||
'TaskExport',
|
||||
'TaskFinder',
|
||||
'TaskFilter',
|
||||
'TaskLink',
|
||||
'TaskModification',
|
||||
'TaskPaginator',
|
||||
'TaskPermission',
|
||||
'TaskPosition',
|
||||
'TaskStatus',
|
||||
|
@ -51,14 +54,17 @@ class ClassProvider implements ServiceProviderInterface
|
|||
'Webhook',
|
||||
),
|
||||
'Core' => array(
|
||||
'Helper',
|
||||
'Template',
|
||||
'Session',
|
||||
'MemoryCache',
|
||||
'FileCache',
|
||||
'Request',
|
||||
),
|
||||
'Integration' => array(
|
||||
'GitlabWebhook',
|
||||
'GithubWebhook',
|
||||
'BitbucketWebhook',
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -75,5 +81,9 @@ class ClassProvider implements ServiceProviderInterface
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
$container['paginator'] = $container->factory(function ($c) {
|
||||
return new Paginator($c);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ use Subscriber\ProjectActivitySubscriber;
|
|||
use Subscriber\ProjectDailySummarySubscriber;
|
||||
use Subscriber\ProjectModificationDateSubscriber;
|
||||
use Subscriber\WebhookSubscriber;
|
||||
use Subscriber\SubtaskTimesheetSubscriber;
|
||||
use Subscriber\TaskMovedDateSubscriber;
|
||||
|
||||
class EventDispatcherProvider implements ServiceProviderInterface
|
||||
{
|
||||
|
@ -25,6 +27,8 @@ class EventDispatcherProvider implements ServiceProviderInterface
|
|||
$container['dispatcher']->addSubscriber(new ProjectModificationDateSubscriber($container));
|
||||
$container['dispatcher']->addSubscriber(new WebhookSubscriber($container));
|
||||
$container['dispatcher']->addSubscriber(new NotificationSubscriber($container));
|
||||
$container['dispatcher']->addSubscriber(new SubtaskTimesheetSubscriber($container));
|
||||
$container['dispatcher']->addSubscriber(new TaskMovedDateSubscriber($container));
|
||||
|
||||
// Automatic actions
|
||||
$container['action']->attachEvents();
|
||||
|
|
|
@ -16,7 +16,7 @@ class LoggingProvider implements ServiceProviderInterface
|
|||
$logger->setLogger(new Syslog('kanboard'));
|
||||
|
||||
if (DEBUG) {
|
||||
$logger->setLogger(new File(__DIR__.'/../../data/debug.log'));
|
||||
$logger->setLogger(new File(DEBUG_FILE));
|
||||
}
|
||||
|
||||
$container['logger'] = $logger;
|
||||
|
|
|
@ -10,16 +10,23 @@ use Pimple\Container;
|
|||
* @package subscriber
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\ProjectPermission $projectPermission
|
||||
* @property \Model\ProjectActivity $projectActivity
|
||||
* @property \Model\ProjectAnalytic $projectAnalytic
|
||||
* @property \Model\ProjectDailySummary $projectDailySummary
|
||||
* @property \Model\Subtask $subtask
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskExport $taskExport
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
* @property \Model\SubtaskTimeTracking $subtaskTimeTracking
|
||||
* @property \Model\UserSession $userSession
|
||||
* @property \Model\Webhook $webhook
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Subscriber;
|
|||
use Event\GenericEvent;
|
||||
use Model\Task;
|
||||
use Model\Comment;
|
||||
use Model\SubTask;
|
||||
use Model\Subtask;
|
||||
use Model\File;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
|
@ -19,8 +19,8 @@ class NotificationSubscriber extends Base implements EventSubscriberInterface
|
|||
Task::EVENT_MOVE_COLUMN => 'task_move_column',
|
||||
Task::EVENT_MOVE_POSITION => 'task_move_position',
|
||||
Task::EVENT_ASSIGNEE_CHANGE => 'task_assignee_change',
|
||||
SubTask::EVENT_CREATE => 'subtask_creation',
|
||||
SubTask::EVENT_UPDATE => 'subtask_update',
|
||||
Subtask::EVENT_CREATE => 'subtask_creation',
|
||||
Subtask::EVENT_UPDATE => 'subtask_update',
|
||||
Comment::EVENT_CREATE => 'comment_creation',
|
||||
Comment::EVENT_UPDATE => 'comment_update',
|
||||
File::EVENT_CREATE => 'file_creation',
|
||||
|
@ -36,8 +36,8 @@ class NotificationSubscriber extends Base implements EventSubscriberInterface
|
|||
Task::EVENT_MOVE_COLUMN => array('execute', 0),
|
||||
Task::EVENT_MOVE_POSITION => array('execute', 0),
|
||||
Task::EVENT_ASSIGNEE_CHANGE => array('execute', 0),
|
||||
SubTask::EVENT_CREATE => array('execute', 0),
|
||||
SubTask::EVENT_UPDATE => array('execute', 0),
|
||||
Subtask::EVENT_CREATE => array('execute', 0),
|
||||
Subtask::EVENT_UPDATE => array('execute', 0),
|
||||
Comment::EVENT_CREATE => array('execute', 0),
|
||||
Comment::EVENT_UPDATE => array('execute', 0),
|
||||
File::EVENT_CREATE => array('execute', 0),
|
||||
|
@ -63,7 +63,7 @@ class NotificationSubscriber extends Base implements EventSubscriberInterface
|
|||
$values['task'] = $this->taskFinder->getDetails($event['task_id']);
|
||||
break;
|
||||
case 'Event\SubtaskEvent':
|
||||
$values['subtask'] = $this->subTask->getById($event['id'], true);
|
||||
$values['subtask'] = $this->subtask->getById($event['id'], true);
|
||||
$values['task'] = $this->taskFinder->getDetails($event['task_id']);
|
||||
break;
|
||||
case 'Event\FileEvent':
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Subscriber;
|
|||
use Event\GenericEvent;
|
||||
use Model\Task;
|
||||
use Model\Comment;
|
||||
use Model\SubTask;
|
||||
use Model\Subtask;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class ProjectActivitySubscriber extends Base implements EventSubscriberInterface
|
||||
|
@ -22,8 +22,8 @@ class ProjectActivitySubscriber extends Base implements EventSubscriberInterface
|
|||
Task::EVENT_MOVE_POSITION => array('execute', 0),
|
||||
Comment::EVENT_UPDATE => array('execute', 0),
|
||||
Comment::EVENT_CREATE => array('execute', 0),
|
||||
SubTask::EVENT_UPDATE => array('execute', 0),
|
||||
SubTask::EVENT_CREATE => array('execute', 0),
|
||||
Subtask::EVENT_UPDATE => array('execute', 0),
|
||||
Subtask::EVENT_CREATE => array('execute', 0),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ class ProjectActivitySubscriber extends Base implements EventSubscriberInterface
|
|||
|
||||
switch (get_class($event)) {
|
||||
case 'Event\SubtaskEvent':
|
||||
$values['subtask'] = $this->subTask->getById($event['id'], true);
|
||||
$values['subtask'] = $this->subtask->getById($event['id'], true);
|
||||
break;
|
||||
case 'Event\CommentEvent':
|
||||
$values['comment'] = $this->comment->getById($event['id']);
|
||||
|
|
47
sources/app/Subscriber/SubtaskTimesheetSubscriber.php
Normal file
47
sources/app/Subscriber/SubtaskTimesheetSubscriber.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Subscriber;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Model\Subtask;
|
||||
use Event\SubtaskEvent;
|
||||
|
||||
class SubtaskTimesheetSubscriber extends Base implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
Subtask::EVENT_CREATE => array('updateTaskTime', 0),
|
||||
Subtask::EVENT_UPDATE => array(
|
||||
array('logStartEnd', 10),
|
||||
array('updateTaskTime', 0),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function updateTaskTime(SubtaskEvent $event)
|
||||
{
|
||||
if (isset($event['task_id'])) {
|
||||
$this->subtaskTimeTracking->updateTaskTimeTracking($event['task_id']);
|
||||
}
|
||||
}
|
||||
|
||||
public function logStartEnd(SubtaskEvent $event)
|
||||
{
|
||||
if ($this->config->get('subtask_time_tracking') == 1 && isset($event['status'])) {
|
||||
|
||||
$subtask = $this->subtask->getById($event['id']);
|
||||
|
||||
if (empty($subtask['user_id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($subtask['status'] == Subtask::STATUS_INPROGRESS) {
|
||||
return $this->subtaskTimeTracking->logStartTime($subtask['id'], $subtask['user_id']);
|
||||
}
|
||||
else {
|
||||
return $this->subtaskTimeTracking->logEndTime($subtask['id'], $subtask['user_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
sources/app/Subscriber/TaskMovedDateSubscriber.php
Normal file
24
sources/app/Subscriber/TaskMovedDateSubscriber.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Subscriber;
|
||||
|
||||
use Event\TaskEvent;
|
||||
use Model\Task;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class TaskMovedDateSubscriber extends Base implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
Task::EVENT_MOVE_COLUMN => array('execute', 0),
|
||||
);
|
||||
}
|
||||
|
||||
public function execute(TaskEvent $event)
|
||||
{
|
||||
if (isset($event['task_id'])) {
|
||||
$this->container['db']->table(Task::TABLE)->eq('id', $event['task_id'])->update(array('date_moved' => time()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?= $this->js('assets/js/d3.v3.4.8.min.js') ?>
|
||||
<?= $this->js('assets/js/dimple.v2.1.0.min.js') ?>
|
||||
<?= $this->js('assets/js/vendor/d3.v3.4.8.min.js') ?>
|
||||
<?= $this->js('assets/js/vendor/dimple.v2.1.0.min.js') ?>
|
||||
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<div class="page-header page-header-mobile">
|
||||
<ul>
|
||||
<?php if ($this->userSession->isAdmin()): ?>
|
||||
<li><i class="fa fa-plus fa-fw"></i><?= $this->a(t('New project'), 'project', 'create') ?></li>
|
||||
|
@ -10,17 +10,51 @@
|
|||
<li><i class="fa fa-user fa-fw"></i><?= $this->a(t('User management'), 'user', 'index') ?></li>
|
||||
<li><i class="fa fa-cog fa-fw"></i><?= $this->a(t('Settings'), 'config', 'index') ?></li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<ul class="dropdown">
|
||||
<li>
|
||||
<i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Change dashboard view') ?></a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="dashboard-toggle" data-toggle="projects"><?= t('Show/hide projects') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dashboard-toggle" data-toggle="tasks"><?= t('Show/hide tasks') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dashboard-toggle" data-toggle="subtasks"><?= t('Show/hide subtasks') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dashboard-toggle" data-toggle="calendar"><?= t('Show/hide calendar') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dashboard-toggle" data-toggle="activities"><?= t('Show/hide activities') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<section id="dashboard">
|
||||
<div class="dashboard-left-column">
|
||||
<?= $this->render('app/projects', array('projects' => $projects, 'pagination' => $project_pagination)) ?>
|
||||
<?= $this->render('app/tasks', array('tasks' => $tasks, 'pagination' => $task_pagination)) ?>
|
||||
<?= $this->render('app/subtasks', array('subtasks' => $subtasks, 'pagination' => $subtask_pagination)) ?>
|
||||
<div id="dashboard-projects"><?= $this->render('app/projects', array('paginator' => $project_paginator)) ?></div>
|
||||
<div id="dashboard-tasks"><?= $this->render('app/tasks', array('paginator' => $task_paginator)) ?></div>
|
||||
<div id="dashboard-subtasks"><?= $this->render('app/subtasks', array('paginator' => $subtask_paginator)) ?></div>
|
||||
</div>
|
||||
<div class="dashboard-right-column">
|
||||
<h2><?= t('Activity stream') ?></h2>
|
||||
<?= $this->render('project/events', array('events' => $events)) ?>
|
||||
<div id="dashboard-calendar">
|
||||
<div id="user-calendar"
|
||||
data-check-url="<?= $this->u('calendar', 'user') ?>"
|
||||
data-user-id="<?= $user_id ?>"
|
||||
data-save-url="<?= $this->u('calendar', 'save') ?>"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dashboard-activities">
|
||||
<h2><?= t('Activity stream') ?></h2>
|
||||
<?= $this->render('project/events', array('events' => $events)) ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
|
@ -1,14 +1,14 @@
|
|||
<h2><?= t('My projects') ?></h2>
|
||||
<?php if (empty($projects)): ?>
|
||||
<?php if ($paginator->isEmpty()): ?>
|
||||
<p class="alert"><?= t('Your are not member of any project.') ?></p>
|
||||
<?php else: ?>
|
||||
<table class="table-fixed">
|
||||
<tr>
|
||||
<th class="column-8"><?= $this->order('Id', 'id', $pagination) ?></th>
|
||||
<th class="column-20"><?= $this->order(t('Project'), 'name', $pagination) ?></th>
|
||||
<th class="column-8"><?= $paginator->order('Id', 'id') ?></th>
|
||||
<th class="column-20"><?= $paginator->order(t('Project'), 'name') ?></th>
|
||||
<th><?= t('Columns') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($projects as $project): ?>
|
||||
<?php foreach ($paginator->getCollection() as $project): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?= $this->a('#'.$project['id'], 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link') ?>
|
||||
|
@ -17,6 +17,8 @@
|
|||
<?php if ($this->isManager($project['id'])): ?>
|
||||
<?= $this->a('<i class="fa fa-cog"></i>', 'project', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Settings')) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?= $this->a('<i class="fa fa-calendar"></i>', 'calendar', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Calendar')) ?>
|
||||
<?= $this->a($this->e($project['name']), 'board', 'show', array('project_id' => $project['id'])) ?>
|
||||
</td>
|
||||
<td class="dashboard-project-stats">
|
||||
|
@ -29,5 +31,5 @@
|
|||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<?= $this->paginate($pagination) ?>
|
||||
<?= $paginator ?>
|
||||
<?php endif ?>
|
|
@ -1,31 +1,37 @@
|
|||
<h2><?= t('My subtasks') ?></h2>
|
||||
<?php if (empty($subtasks)): ?>
|
||||
<?php if ($paginator->isEmpty()): ?>
|
||||
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
|
||||
<?php else: ?>
|
||||
<table class="table-fixed">
|
||||
<tr>
|
||||
<th class="column-10"><?= $this->order('Id', 'tasks.id', $pagination) ?></th>
|
||||
<th class="column-20"><?= $this->order(t('Project'), 'project_name', $pagination) ?></th>
|
||||
<th class="column-15"><?= $this->order(t('Status'), 'status', $pagination) ?></th>
|
||||
<th><?= $this->order(t('Subtask'), 'title', $pagination) ?></th>
|
||||
<th class="column-10"><?= $paginator->order('Id', 'tasks.id') ?></th>
|
||||
<th class="column-20"><?= $paginator->order(t('Project'), 'project_name') ?></th>
|
||||
<th><?= $paginator->order(t('Subtask'), 'title') ?></th>
|
||||
<th class="column-20"><?= t('Time tracking') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($subtasks as $subtask): ?>
|
||||
<?php foreach ($paginator->getCollection() as $subtask): ?>
|
||||
<tr>
|
||||
<td class="task-table task-<?= $subtask['color_id'] ?>">
|
||||
<td class="task-table color-<?= $subtask['color_id'] ?>">
|
||||
<?= $this->a('#'.$subtask['task_id'], 'task', 'show', array('task_id' => $subtask['task_id'], 'project_id' => $subtask['project_id'])) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= $this->a($this->e($subtask['project_name']), 'board', 'show', array('project_id' => $subtask['project_id'])) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= $this->e($subtask['status_name']) ?>
|
||||
<?= $this->toggleSubtaskStatus($subtask, 'dashboard') ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= $this->a($this->e($subtask['title']), 'task', 'show', array('task_id' => $subtask['task_id'], 'project_id' => $subtask['project_id'])) ?>
|
||||
<?php if (! empty($subtask['time_spent'])): ?>
|
||||
<strong><?= $this->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($subtask['time_estimated'])): ?>
|
||||
<strong><?= $this->e($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<?= $this->paginate($pagination) ?>
|
||||
<?= $paginator ?>
|
||||
<?php endif ?>
|
|
@ -1,17 +1,18 @@
|
|||
<h2><?= t('My tasks') ?></h2>
|
||||
<?php if (empty($tasks)): ?>
|
||||
<?php if ($paginator->isEmpty()): ?>
|
||||
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
|
||||
<?php else: ?>
|
||||
<table class="table-fixed">
|
||||
<tr>
|
||||
<th class="column-8"><?= $this->order('Id', 'tasks.id', $pagination) ?></th>
|
||||
<th class="column-20"><?= $this->order(t('Project'), 'project_name', $pagination) ?></th>
|
||||
<th><?= $this->order(t('Task'), 'title', $pagination) ?></th>
|
||||
<th class="column-20"><?= $this->order(t('Due date'), 'date_due', $pagination) ?></th>
|
||||
<th class="column-8"><?= $paginator->order('Id', 'tasks.id') ?></th>
|
||||
<th class="column-20"><?= $paginator->order(t('Project'), 'project_name') ?></th>
|
||||
<th><?= $paginator->order(t('Task'), 'title') ?></th>
|
||||
<th class="column-20"><?= t('Time tracking') ?></th>
|
||||
<th class="column-20"><?= $paginator->order(t('Due date'), 'date_due') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($tasks as $task): ?>
|
||||
<?php foreach ($paginator->getCollection() as $task): ?>
|
||||
<tr>
|
||||
<td class="task-table task-<?= $task['color_id'] ?>">
|
||||
<td class="task-table color-<?= $task['color_id'] ?>">
|
||||
<?= $this->a('#'.$task['id'], 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -20,6 +21,15 @@
|
|||
<td>
|
||||
<?= $this->a($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (! empty($task['time_spent'])): ?>
|
||||
<strong><?= $this->e($task['time_spent']).'h' ?></strong> <?= t('spent') ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['time_estimated'])): ?>
|
||||
<strong><?= $this->e($task['time_estimated']).'h' ?></strong> <?= t('estimated') ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= dt('%B %e, %Y', $task['date_due']) ?>
|
||||
</td>
|
||||
|
@ -27,5 +37,5 @@
|
|||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<?= $this->paginate($pagination) ?>
|
||||
<?= $paginator ?>
|
||||
<?php endif ?>
|
|
@ -14,7 +14,7 @@
|
|||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?>
|
||||
<?= $this->a(t('cancel'), 'board', 'show', array('project_id' => $project['id'])) ?>
|
||||
<?= $this->a(t('cancel'), 'board', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?>
|
||||
<?= $this->a(t('cancel'), 'board', 'show', array('project_id' => $project['id'])) ?>
|
||||
<?= $this->a(t('cancel'), 'board', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
|
|
@ -1,50 +1,48 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Edit the board for "%s"', $project['name']) ?></h2>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<h3><?= t('Change columns') ?></h3>
|
||||
<form method="post" action="<?= $this->u('board', 'update', array('project_id' => $project['id'])) ?>" autocomplete="off">
|
||||
<?= $this->formCsrf() ?>
|
||||
<?php $i = 0; ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Position') ?></th>
|
||||
<th><?= t('Column title') ?></th>
|
||||
<th><?= t('Task limit') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($columns as $column): ?>
|
||||
<tr>
|
||||
<td><?= $this->formLabel('#'.++$i, 'title['.$column['id'].']', array('title="column_id='.$column['id'].'"')) ?></td>
|
||||
<td><?= $this->formText('title['.$column['id'].']', $values, $errors, array('required')) ?></td>
|
||||
<td><?= $this->formNumber('task_limit['.$column['id'].']', $values, $errors, array('placeholder="'.t('limit').'"')) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<?php if ($column['position'] != 1): ?>
|
||||
<li>
|
||||
<?= $this->a(t('Move Up'), 'board', 'moveColumn', array('project_id' => $project['id'], 'column_id' => $column['id'], 'direction' => 'up'), true) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($column['position'] != count($columns)): ?>
|
||||
<li>
|
||||
<?= $this->a(t('Move Down'), 'board', 'moveColumn', array('project_id' => $project['id'], 'column_id' => $column['id'], 'direction' => 'down'), true) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<?= $this->a(t('Remove'), 'board', 'remove', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Column title') ?></th>
|
||||
<th><?= t('Task limit') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($columns as $column): ?>
|
||||
<tr>
|
||||
<td class="column-60"><?= $this->e($column['title']) ?>
|
||||
<?php if (! empty($column['description'])): ?>
|
||||
<span class="column-tooltip" title="<?= $this->markdown($column['description']) ?>">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td class="column-10"><?= $this->e($column['task_limit']) ?></td>
|
||||
<td class="column-30">
|
||||
<ul>
|
||||
<li>
|
||||
<?= $this->a(t('Edit'), 'board', 'editColumn', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
|
||||
</li>
|
||||
<?php if ($column['position'] != 1): ?>
|
||||
<li>
|
||||
<?= $this->a(t('Move Up'), 'board', 'moveColumn', array('project_id' => $project['id'], 'column_id' => $column['id'], 'direction' => 'up'), true) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($column['position'] != count($columns)): ?>
|
||||
<li>
|
||||
<?= $this->a(t('Move Down'), 'board', 'moveColumn', array('project_id' => $project['id'], 'column_id' => $column['id'], 'direction' => 'down'), true) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<?= $this->a(t('Remove'), 'board', 'remove', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Update') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
<hr/>
|
||||
<h3><?= t('Add a new column') ?></h3>
|
||||
<form method="post" action="<?= $this->u('board', 'add', array('project_id' => $project['id'])) ?>" autocomplete="off">
|
||||
|
||||
|
@ -53,7 +51,30 @@
|
|||
<?= $this->formHidden('project_id', $values) ?>
|
||||
|
||||
<?= $this->formLabel(t('Title'), 'title') ?>
|
||||
<?= $this->formText('title', $values, $errors, array('required')) ?>
|
||||
<?= $this->formText('title', $values, $errors, array('required', 'maxlength="50"')) ?>
|
||||
|
||||
<?= $this->formLabel(t('Task limit'), 'task_limit') ?>
|
||||
<?= $this->formNumber('task_limit', $values, $errors) ?>
|
||||
|
||||
<?= $this->formLabel(t('Description'), 'description') ?>
|
||||
|
||||
<div class="form-tabs">
|
||||
<div class="write-area">
|
||||
<?= $this->formTextarea('description', $values, $errors) ?>
|
||||
</div>
|
||||
<div class="preview-area">
|
||||
<div class="markdown"></div>
|
||||
</div>
|
||||
<ul class="form-tabs-nav">
|
||||
<li class="form-tab form-tab-selected">
|
||||
<i class="fa fa-pencil-square-o fa-fw"></i><a id="markdown-write" href="#"><?= t('Write') ?></a>
|
||||
</li>
|
||||
<li class="form-tab">
|
||||
<a id="markdown-preview" href="#"><i class="fa fa-eye fa-fw"></i><?= t('Preview') ?></a>
|
||||
</li>
|
||||
</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 class="form-actions">
|
||||
<input type="submit" value="<?= t('Add this column') ?>" class="btn btn-blue"/>
|
||||
|
|
42
sources/app/Template/board/edit_column.php
Normal file
42
sources/app/Template/board/edit_column.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Edit column "%s"', $column['title']) ?></h2>
|
||||
</div>
|
||||
|
||||
<form method="post" action="<?= $this->u('board', 'updateColumn', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->formCsrf() ?>
|
||||
|
||||
<?= $this->formHidden('id', $values) ?>
|
||||
<?= $this->formHidden('project_id', $values) ?>
|
||||
|
||||
<?= $this->formLabel(t('Title'), 'title') ?>
|
||||
<?= $this->formText('title', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
|
||||
|
||||
<?= $this->formLabel(t('Task limit'), 'task_limit') ?>
|
||||
<?= $this->formNumber('task_limit', $values, $errors) ?>
|
||||
|
||||
<?= $this->formLabel(t('Description'), 'description') ?>
|
||||
|
||||
<div class="form-tabs">
|
||||
|
||||
<div class="write-area">
|
||||
<?= $this->formTextarea('description', $values, $errors) ?>
|
||||
</div>
|
||||
<div class="preview-area">
|
||||
<div class="markdown"></div>
|
||||
</div>
|
||||
<ul class="form-tabs-nav">
|
||||
<li class="form-tab form-tab-selected">
|
||||
<i class="fa fa-pencil-square-o fa-fw"></i><a id="markdown-write" href="#"><?= t('Write') ?></a>
|
||||
</li>
|
||||
<li class="form-tab">
|
||||
<a id="markdown-preview" href="#"><i class="fa fa-eye fa-fw"></i><?= t('Preview') ?></a>
|
||||
</li>
|
||||
</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 class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
|
@ -6,7 +6,7 @@
|
|||
$this->e($file['name']),
|
||||
'file',
|
||||
'download',
|
||||
array('file_id' => $file['id'], 'task_id' => $file['task_id'])
|
||||
array('file_id' => $file['id'], 'task_id' => $file['task_id'], 'project_id' => $task['project_id'])
|
||||
) ?>
|
||||
|
||||
<br/>
|
||||
|
|
|
@ -1,35 +1,65 @@
|
|||
<div class="page-header">
|
||||
<ul class="board-filters">
|
||||
<li class="hide-tablet">
|
||||
<?= t('Filter by user') ?>
|
||||
<?= $this->formSelect('user_id', $users) ?>
|
||||
</li>
|
||||
<li class="hide-tablet">
|
||||
<?= t('Filter by category') ?>
|
||||
<?= $this->formSelect('category_id', $categories) ?>
|
||||
</li>
|
||||
<li class="hide-tablet">
|
||||
<a href="#" id="filter-due-date"><?= t('Filter by due date') ?></a>
|
||||
<li>
|
||||
<ul class="dropdown">
|
||||
<li>
|
||||
<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>
|
||||
<i class="fa fa-search fa-fw"></i>
|
||||
<?= $this->a(t('Search'), 'project', 'search', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-check-square-o fa-fw"></i>
|
||||
<?= $this->a(t('Completed tasks'), 'project', 'tasks', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-dashboard fa-fw"></i>
|
||||
<?= $this->a(t('Activity'), 'project', 'activity', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-calendar fa-fw"></i>
|
||||
<?= $this->a(t('Calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
<?php if ($project['is_public']): ?>
|
||||
<li>
|
||||
<i class="fa fa-share-alt fa-fw"></i> <?= $this->a(t('Public link'), 'board', 'readonly', array('token' => $project['token']), false, '', '', true) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($this->acl->isManagerActionAllowed($project['id'])): ?>
|
||||
<li>
|
||||
<i class="fa fa-line-chart fa-fw"></i>
|
||||
<?= $this->a(t('Analytics'), 'analytic', 'tasks', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-cog fa-fw"></i>
|
||||
<?= $this->a(t('Configure'), 'project', 'show', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-search"></i>
|
||||
<?= $this->a(t('Search'), 'project', 'search', array('project_id' => $project['id'])) ?>
|
||||
<?= $this->formSelect('user_id', $users, array(), array(), array('data-placeholder="'.t('Filter by user').'"'), 'apply-filters chosen-select') ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-check-square-o fa-fw"></i>
|
||||
<?= $this->a(t('Completed tasks'), 'project', 'tasks', array('project_id' => $project['id'])) ?>
|
||||
<?= $this->formSelect('category_id', $categories, array(), array(), array('data-placeholder="'.t('Filter by category').'"'), 'apply-filters chosen-select') ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-dashboard fa-fw"></i>
|
||||
<?= $this->a(t('Activity'), 'project', 'activity', array('project_id' => $project['id'])) ?>
|
||||
<select id="more-filters" multiple data-placeholder="<?= t('More filters') ?>" class="apply-filters chosen-select hide-mobile">
|
||||
<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>
|
||||
<?php if ($this->acl->isManagerActionAllowed($project['id'])): ?>
|
||||
<li>
|
||||
<i class="fa fa-line-chart fa-fw"></i>
|
||||
<?= $this->a(t('Analytics'), 'analytic', 'tasks', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
<li><i class="fa fa-cog fa-fw"></i>
|
||||
<?= $this->a(t('Configure'), 'project', 'show', array('project_id' => $project['id'])) ?>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,28 +1,30 @@
|
|||
|
||||
<?php if (isset($not_editable)): ?>
|
||||
<table id="board">
|
||||
<?php else: ?>
|
||||
<table id="board"
|
||||
data-project-id="<?= $project['id'] ?>"
|
||||
data-check-interval="<?= $board_private_refresh_interval ?>"
|
||||
data-save-url="<?= $this->u('board', 'save', array('project_id' => $project['id'])) ?>"
|
||||
data-check-url="<?= $this->u('board', 'check', array('project_id' => $project['id'], 'timestamp' => time())) ?>"
|
||||
>
|
||||
<?php endif ?>
|
||||
|
||||
<?php foreach ($swimlanes as $swimlane): ?>
|
||||
<?php if (empty($swimlane['columns'])): ?>
|
||||
<p class="alert alert-error"><?= t('There is no column in your project!') ?></p>
|
||||
<?php break ?>
|
||||
<div id="board-container">
|
||||
<?php if (isset($not_editable)): ?>
|
||||
<table id="board">
|
||||
<?php else: ?>
|
||||
<?= $this->render('board/swimlane', array(
|
||||
'project' => $project,
|
||||
'swimlane' => $swimlane,
|
||||
'board_highlight_period' => $board_highlight_period,
|
||||
'categories' => $categories,
|
||||
'hide_swimlane' => count($swimlanes) === 1,
|
||||
'not_editable' => isset($not_editable),
|
||||
)) ?>
|
||||
<table id="board"
|
||||
data-project-id="<?= $project['id'] ?>"
|
||||
data-check-interval="<?= $board_private_refresh_interval ?>"
|
||||
data-save-url="<?= $this->u('board', 'save', array('project_id' => $project['id'])) ?>"
|
||||
data-check-url="<?= $this->u('board', 'check', array('project_id' => $project['id'], 'timestamp' => time())) ?>"
|
||||
data-task-creation-url="<?= $this->u('task', 'create', array('project_id' => $project['id'])) ?>"
|
||||
>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<?php foreach ($swimlanes as $swimlane): ?>
|
||||
<?php if (empty($swimlane['columns'])): ?>
|
||||
<p class="alert alert-error"><?= t('There is no column in your project!') ?></p>
|
||||
<?php break ?>
|
||||
<?php else: ?>
|
||||
<?= $this->render('board/swimlane', array(
|
||||
'project' => $project,
|
||||
'swimlane' => $swimlane,
|
||||
'board_highlight_period' => $board_highlight_period,
|
||||
'categories' => $categories,
|
||||
'hide_swimlane' => count($swimlanes) === 1,
|
||||
'not_editable' => isset($not_editable),
|
||||
)) ?>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
|
@ -1,14 +1,7 @@
|
|||
<section id="tooltip-subtasks">
|
||||
<?php foreach ($subtasks as $subtask): ?>
|
||||
<?= $this->a(
|
||||
trim($this->render('subtask/icons', array('subtask' => $subtask))) . $this->e($subtask['title']),
|
||||
'board',
|
||||
'toggleSubtask',
|
||||
array('task_id' => $subtask['task_id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])
|
||||
) ?>
|
||||
|
||||
<?= $this->toggleSubtaskStatus($subtask, 'board') ?>
|
||||
<?= $this->e(empty($subtask['username']) ? '' : ' ['.$this->getFullname($subtask).']') ?>
|
||||
|
||||
<br/>
|
||||
<?php endforeach ?>
|
||||
</section>
|
||||
|
|
|
@ -1,18 +1,38 @@
|
|||
<tr>
|
||||
<?php if (! $hide_swimlane): ?>
|
||||
<td width="10%"></td>
|
||||
<th>
|
||||
|
||||
<?php if (! $not_editable && $swimlane['nb_tasks'] > 0): ?>
|
||||
<a href="#" class="board-swimlane-toggle" data-swimlane-id="<?= $swimlane['id'] ?>">
|
||||
<i class="fa fa-minus-circle hide-icon-swimlane-<?= $swimlane['id'] ?>"></i>
|
||||
<i class="fa fa-plus-circle show-icon-swimlane-<?= $swimlane['id'] ?>" style="display: none"></i>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
|
||||
<?= $this->e($swimlane['name']) ?>
|
||||
|
||||
<span title="<?= t('Task count') ?>" class="task-count">
|
||||
(<span><?= $swimlane['nb_tasks'] ?></span>)
|
||||
</span>
|
||||
</th>
|
||||
<?php endif ?>
|
||||
|
||||
<?php foreach ($swimlane['columns'] as $column): ?>
|
||||
<th>
|
||||
<th class="board-column">
|
||||
<?php if (! $not_editable): ?>
|
||||
<div class="board-add-icon">
|
||||
<?= $this->a('+', 'task', 'create', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'task-creation-popover', t('Add a new task')) ?>
|
||||
<?= $this->a('+', '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')) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?= $this->e($column['title']) ?>
|
||||
|
||||
<?php if (! empty($column['description'])): ?>
|
||||
<span class="column-tooltip pull-right" title="<?= $this->markdown($column['description']) ?>">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($column['task_limit']): ?>
|
||||
<span title="<?= t('Task limit') ?>" class="task-limit">
|
||||
(<span id="task-number-column-<?= $column['id'] ?>"><?= $column['nb_tasks'] ?></span>/<?= $this->e($column['task_limit']) ?>)
|
||||
|
@ -25,11 +45,10 @@
|
|||
</th>
|
||||
<?php endforeach ?>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr class="swimlane-row-<?= $swimlane['id'] ?>">
|
||||
|
||||
<?php if (! $hide_swimlane): ?>
|
||||
<th class="board-swimlane-title">
|
||||
<?= $this->e($swimlane['name']) ?>
|
||||
</th>
|
||||
<th></th>
|
||||
<?php endif ?>
|
||||
|
||||
<?php foreach ($swimlane['columns'] as $column): ?>
|
||||
|
@ -46,7 +65,7 @@
|
|||
<?php endif ?>
|
||||
|
||||
<?php foreach ($column['tasks'] as $task): ?>
|
||||
<?= $this->render('board/task', array(
|
||||
<?= $this->render($not_editable ? 'board/task_public' : 'board/task_private', array(
|
||||
'project' => $project,
|
||||
'task' => $task,
|
||||
'categories' => $categories,
|
||||
|
|
|
@ -1,125 +0,0 @@
|
|||
<?php if ($not_editable): ?>
|
||||
|
||||
<div class="task-board task-<?= $task['color_id'] ?> <?= $task['date_modification'] > time() - $board_highlight_period ? 'task-board-recent' : '' ?>">
|
||||
|
||||
<?= $this->a('#'.$task['id'], 'task', 'readonly', array('task_id' => $task['id'], 'token' => $project['token'])) ?>
|
||||
|
||||
<?php if ($task['reference']): ?>
|
||||
<span class="task-board-reference" title="<?= t('Reference') ?>">
|
||||
(<?= $task['reference'] ?>)
|
||||
</span>
|
||||
<?php endif ?>
|
||||
|
||||
-
|
||||
|
||||
<span class="task-board-user">
|
||||
<?php if (! empty($task['owner_id'])): ?>
|
||||
<?= t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?>
|
||||
<?php else: ?>
|
||||
<span class="task-board-nobody"><?= t('Nobody assigned') ?></span>
|
||||
<?php endif ?>
|
||||
</span>
|
||||
|
||||
<?php if ($task['score']): ?>
|
||||
<span class="task-score"><?= $this->e($task['score']) ?></span>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-board-title">
|
||||
<?= $this->a($this->e($task['title']), 'task', 'readonly', array('task_id' => $task['id'], 'token' => $project['token'])) ?>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="task-board draggable-item task-<?= $task['color_id'] ?> <?= $task['date_modification'] > time() - $board_highlight_period ? 'task-board-recent' : '' ?>"
|
||||
data-task-id="<?= $task['id'] ?>"
|
||||
data-owner-id="<?= $task['owner_id'] ?>"
|
||||
data-category-id="<?= $task['category_id'] ?>"
|
||||
data-due-date="<?= $task['date_due'] ?>"
|
||||
data-task-url="<?= $this->u('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"
|
||||
title="<?= t('View this task') ?>">
|
||||
|
||||
<?= $this->a('#'.$task['id'], 'task', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-edit-popover', t('Edit this task')) ?>
|
||||
|
||||
<?php if ($task['reference']): ?>
|
||||
<span class="task-board-reference" title="<?= t('Reference') ?>">
|
||||
(<?= $task['reference'] ?>)
|
||||
</span>
|
||||
<?php endif ?>
|
||||
|
||||
<span class="task-board-user <?= $this->userSession->isCurrentUser($task['owner_id']) ? 'task-board-current-user' : '' ?>">
|
||||
<?= $this->a(
|
||||
(! empty($task['owner_id']) ? t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) : t('Nobody assigned')),
|
||||
'board',
|
||||
'changeAssignee',
|
||||
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
|
||||
false,
|
||||
'assignee-popover',
|
||||
t('Change assignee')
|
||||
) ?>
|
||||
</span>
|
||||
|
||||
<?php if ($task['score']): ?>
|
||||
<span class="task-score"><?= $this->e($task['score']) ?></span>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-board-title">
|
||||
<?= $this->a($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<?php if ($task['category_id']): ?>
|
||||
<div class="task-board-category-container">
|
||||
<span class="task-board-category">
|
||||
<?= $this->a(
|
||||
$this->inList($task['category_id'], $categories),
|
||||
'board',
|
||||
'changeCategory',
|
||||
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
|
||||
false,
|
||||
'category-popover',
|
||||
t('Change category')
|
||||
) ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<?php if (! empty($task['date_due']) || ! empty($task['nb_files']) || ! empty($task['nb_comments']) || ! empty($task['description']) || ! empty($task['nb_subtasks'])): ?>
|
||||
<div class="task-board-footer">
|
||||
|
||||
<?php if (! empty($task['date_due'])): ?>
|
||||
<div class="task-board-date <?= time() > $task['date_due'] ? 'task-board-date-overdue' : '' ?>">
|
||||
<i class="fa fa-calendar"></i> <?= dt('%b %e, %Y', $task['date_due']) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-board-icons">
|
||||
|
||||
<?php if (! empty($task['nb_subtasks'])): ?>
|
||||
<span title="<?= t('Sub-Tasks') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'subtasks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= $task['nb_completed_subtasks'].'/'.$task['nb_subtasks'] ?> <i class="fa fa-bars"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_files'])): ?>
|
||||
<span title="<?= t('Attachments') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'attachments', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= $task['nb_files'] ?> <i class="fa fa-paperclip"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_comments'])): ?>
|
||||
<span title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'comments', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= $task['nb_comments'] ?> <i class="fa fa-comment-o"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<span title="<?= t('Description') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>">
|
||||
<?php if (! isset($not_editable)): ?>
|
||||
<a class="task-description-popover" href="?controller=task&action=description&task_id=<?= $task['id'] ?>"><i class="fa fa-file-text-o" data-href="?controller=task&action=description&task_id=<?= $task['id'] ?>"></i></a>
|
||||
<?php else: ?>
|
||||
<i class="fa fa-file-text-o"></i>
|
||||
<?php endif ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
45
sources/app/Template/board/task_footer.php
Normal file
45
sources/app/Template/board/task_footer.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php if ($task['category_id']): ?>
|
||||
<div class="task-board-category-container">
|
||||
<span class="task-board-category">
|
||||
<?= $this->a(
|
||||
$this->inList($task['category_id'], $categories),
|
||||
'board',
|
||||
'changeCategory',
|
||||
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
|
||||
false,
|
||||
'task-board-popover',
|
||||
t('Change category')
|
||||
) ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-board-icons">
|
||||
<?php if (! empty($task['date_due'])): ?>
|
||||
<span class="task-board-date <?= time() > $task['date_due'] ? 'task-board-date-overdue' : '' ?>">
|
||||
<i class="fa fa-calendar"></i> <?= dt('%b %e', $task['date_due']) ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_links'])): ?>
|
||||
<span title="<?= t('Links') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'tasklinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= $task['nb_links'] ?> <i class="fa fa-code-fork"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_subtasks'])): ?>
|
||||
<span title="<?= t('Sub-Tasks') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'subtasks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= round($task['nb_completed_subtasks']/$task['nb_subtasks']*100, 0).'%' ?> <i class="fa fa-bars"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_files'])): ?>
|
||||
<span title="<?= t('Attachments') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'attachments', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= $task['nb_files'] ?> <i class="fa fa-paperclip"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_comments'])): ?>
|
||||
<span title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'comments', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><?= $task['nb_comments'] ?> <i class="fa fa-comment-o"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<span title="<?= t('Description') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>">
|
||||
<i class="fa fa-file-text-o"></i>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</div>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue