summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Api/Middleware/AuthenticationMiddleware.php1
-rw-r--r--app/Auth/ApiAccessTokenAuth.php119
-rw-r--r--app/Auth/DatabaseAuth.php2
-rw-r--r--app/Auth/LdapAuth.php2
-rw-r--r--app/Auth/RememberMeAuth.php4
-rw-r--r--app/Auth/ReverseProxyAuth.php2
-rw-r--r--app/Auth/TotpAuth.php2
-rw-r--r--app/Console/DatabaseMigrationCommand.php23
-rw-r--r--app/Console/DatabaseVersionCommand.php23
-rw-r--r--app/Controller/CommentController.php14
-rw-r--r--app/Controller/TaskAjaxController.php25
-rw-r--r--app/Controller/UserAjaxController.php10
-rw-r--r--app/Controller/UserApiAccessController.php50
-rw-r--r--app/Core/Base.php2
-rw-r--r--app/Core/Helper.php1
-rw-r--r--app/Core/Markdown.php7
-rw-r--r--app/Core/Session/SessionStorage.php1
-rw-r--r--app/Event/GenericEvent.php22
-rw-r--r--app/Event/ProjectFileEvent.php8
-rw-r--r--app/Filter/TaskStartsWithIdFilter.php38
-rw-r--r--app/Formatter/BaseFormatter.php14
-rw-r--r--app/Formatter/BaseTaskCalendarFormatter.php4
-rw-r--r--app/Formatter/TaskAutoCompleteFormatter.php28
-rw-r--r--app/Formatter/TaskSuggestMenuFormatter.php63
-rw-r--r--app/Formatter/UserMentionFormatter.php60
-rw-r--r--app/Helper/FormHelper.php44
-rw-r--r--app/Job/CommentEventJob.php4
-rw-r--r--app/Job/TaskEventJob.php3
-rw-r--r--app/Job/UserMentionJob.php72
-rw-r--r--app/Locale/bs_BA/translations.php9
-rw-r--r--app/Locale/cs_CZ/translations.php9
-rw-r--r--app/Locale/da_DK/translations.php9
-rw-r--r--app/Locale/de_DE/translations.php9
-rw-r--r--app/Locale/el_GR/translations.php9
-rw-r--r--app/Locale/es_ES/translations.php9
-rw-r--r--app/Locale/fi_FI/translations.php9
-rw-r--r--app/Locale/fr_FR/translations.php9
-rw-r--r--app/Locale/hu_HU/translations.php9
-rw-r--r--app/Locale/id_ID/translations.php9
-rw-r--r--app/Locale/it_IT/translations.php43
-rw-r--r--app/Locale/ja_JP/translations.php9
-rw-r--r--app/Locale/ko_KR/translations.php227
-rw-r--r--app/Locale/my_MY/translations.php9
-rw-r--r--app/Locale/nb_NO/translations.php9
-rw-r--r--app/Locale/nl_NL/translations.php9
-rw-r--r--app/Locale/pl_PL/translations.php9
-rw-r--r--app/Locale/pt_BR/translations.php9
-rw-r--r--app/Locale/pt_PT/translations.php9
-rw-r--r--app/Locale/ru_RU/translations.php9
-rw-r--r--app/Locale/sr_Latn_RS/translations.php9
-rw-r--r--app/Locale/sv_SE/translations.php9
-rw-r--r--app/Locale/th_TH/translations.php9
-rw-r--r--app/Locale/tr_TR/translations.php9
-rw-r--r--app/Locale/zh_CN/translations.php9
-rw-r--r--app/Model/ProjectPermissionModel.php24
-rw-r--r--app/Model/UserMentionModel.php62
-rw-r--r--app/Schema/Mysql.php12
-rw-r--r--app/Schema/Postgres.php12
-rw-r--r--app/Schema/Sql/mysql.sql78
-rw-r--r--app/Schema/Sql/postgres.sql572
-rw-r--r--app/Schema/Sqlite.php7
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php3
-rw-r--r--app/ServiceProvider/ClassProvider.php1
-rw-r--r--app/ServiceProvider/CommandProvider.php4
-rw-r--r--app/ServiceProvider/DatabaseProvider.php41
-rw-r--r--app/ServiceProvider/JobProvider.php5
-rw-r--r--app/ServiceProvider/RouteProvider.php1
-rw-r--r--app/Subscriber/NotificationSubscriber.php38
-rw-r--r--app/Template/category/edit.php4
-rw-r--r--app/Template/category/index.php2
-rw-r--r--app/Template/column/create.php10
-rw-r--r--app/Template/comments/show.php1
-rw-r--r--app/Template/notification/comment_create.php2
-rw-r--r--app/Template/notification/comment_delete.php2
-rw-r--r--app/Template/notification/comment_update.php2
-rw-r--r--app/Template/notification/comment_user_mention.php2
-rw-r--r--app/Template/notification/task_assignee_change.php2
-rw-r--r--app/Template/notification/task_create.php2
-rw-r--r--app/Template/notification/task_update.php2
-rw-r--r--app/Template/notification/task_user_mention.php2
-rw-r--r--app/Template/swimlane/create.php6
-rw-r--r--app/Template/swimlane/edit.php6
-rw-r--r--app/Template/swimlane/edit_default.php2
-rw-r--r--app/Template/swimlane/table.php7
-rw-r--r--app/Template/task/changes.php6
-rw-r--r--app/Template/task_move_position/show.php55
-rw-r--r--app/Template/user_api_access/show.php17
-rw-r--r--app/Template/user_view/sidebar.php5
-rw-r--r--app/constants.php3
-rw-r--r--app/functions.php31
90 files changed, 1686 insertions, 470 deletions
diff --git a/app/Api/Middleware/AuthenticationMiddleware.php b/app/Api/Middleware/AuthenticationMiddleware.php
index 8e309593..c4fa874a 100644
--- a/app/Api/Middleware/AuthenticationMiddleware.php
+++ b/app/Api/Middleware/AuthenticationMiddleware.php
@@ -28,6 +28,7 @@ class AuthenticationMiddleware extends Base implements MiddlewareInterface
public function execute($username, $password, $procedureName)
{
$this->dispatcher->dispatch('app.bootstrap');
+ $this->sessionStorage->scope = 'API';
if ($this->isUserAuthenticated($username, $password)) {
$this->userSession->initialize($this->userModel->getByUsername($username));
diff --git a/app/Auth/ApiAccessTokenAuth.php b/app/Auth/ApiAccessTokenAuth.php
new file mode 100644
index 00000000..12ab21a7
--- /dev/null
+++ b/app/Auth/ApiAccessTokenAuth.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace Kanboard\Auth;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
+use Kanboard\Model\UserModel;
+use Kanboard\User\DatabaseUserProvider;
+
+/**
+ * API Access Token Authentication Provider
+ *
+ * @package Kanboard\Auth
+ * @author Frederic Guillot
+ */
+class ApiAccessTokenAuth extends Base implements PasswordAuthenticationProviderInterface
+{
+ /**
+ * User properties
+ *
+ * @access protected
+ * @var array
+ */
+ protected $userInfo = array();
+
+ /**
+ * Username
+ *
+ * @access protected
+ * @var string
+ */
+ protected $username = '';
+
+ /**
+ * Password
+ *
+ * @access protected
+ * @var string
+ */
+ protected $password = '';
+
+ /**
+ * Get authentication provider name
+ *
+ * @access public
+ * @return string
+ */
+ public function getName()
+ {
+ return 'API Access Token';
+ }
+
+ /**
+ * Authenticate the user
+ *
+ * @access public
+ * @return boolean
+ */
+ public function authenticate()
+ {
+ if (! isset($this->sessionStorage->scope) || $this->sessionStorage->scope !== 'API') {
+ $this->logger->debug(__METHOD__.': Authentication provider skipped because invalid scope');
+ return false;
+ }
+
+ $user = $this->db
+ ->table(UserModel::TABLE)
+ ->columns('id', 'password')
+ ->eq('username', $this->username)
+ ->eq('api_access_token', $this->password)
+ ->notNull('api_access_token')
+ ->eq('is_active', 1)
+ ->findOne();
+
+ if (! empty($user)) {
+ $this->userInfo = $user;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get user object
+ *
+ * @access public
+ * @return \Kanboard\User\DatabaseUserProvider
+ */
+ public function getUser()
+ {
+ if (empty($this->userInfo)) {
+ return null;
+ }
+
+ return new DatabaseUserProvider($this->userInfo);
+ }
+
+ /**
+ * Set username
+ *
+ * @access public
+ * @param string $username
+ */
+ public function setUsername($username)
+ {
+ $this->username = $username;
+ }
+
+ /**
+ * Set password
+ *
+ * @access public
+ * @param string $password
+ */
+ public function setPassword($password)
+ {
+ $this->password = $password;
+ }
+}
diff --git a/app/Auth/DatabaseAuth.php b/app/Auth/DatabaseAuth.php
index ecb42c17..84a1e019 100644
--- a/app/Auth/DatabaseAuth.php
+++ b/app/Auth/DatabaseAuth.php
@@ -11,7 +11,7 @@ use Kanboard\User\DatabaseUserProvider;
/**
* Database Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class DatabaseAuth extends Base implements PasswordAuthenticationProviderInterface, SessionCheckProviderInterface
diff --git a/app/Auth/LdapAuth.php b/app/Auth/LdapAuth.php
index a8dcfcb6..05ffbebf 100644
--- a/app/Auth/LdapAuth.php
+++ b/app/Auth/LdapAuth.php
@@ -12,7 +12,7 @@ use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
/**
* LDAP Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class LdapAuth extends Base implements PasswordAuthenticationProviderInterface
diff --git a/app/Auth/RememberMeAuth.php b/app/Auth/RememberMeAuth.php
index 5d0a8b2e..e0f4ceb6 100644
--- a/app/Auth/RememberMeAuth.php
+++ b/app/Auth/RememberMeAuth.php
@@ -7,9 +7,9 @@ use Kanboard\Core\Security\PreAuthenticationProviderInterface;
use Kanboard\User\DatabaseUserProvider;
/**
- * Rember Me Cookie Authentication Provider
+ * RememberMe Cookie Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class RememberMeAuth extends Base implements PreAuthenticationProviderInterface
diff --git a/app/Auth/ReverseProxyAuth.php b/app/Auth/ReverseProxyAuth.php
index fdf936b1..02afc302 100644
--- a/app/Auth/ReverseProxyAuth.php
+++ b/app/Auth/ReverseProxyAuth.php
@@ -10,7 +10,7 @@ use Kanboard\User\ReverseProxyUserProvider;
/**
* Reverse-Proxy Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class ReverseProxyAuth extends Base implements PreAuthenticationProviderInterface, SessionCheckProviderInterface
diff --git a/app/Auth/TotpAuth.php b/app/Auth/TotpAuth.php
index 8e1ebe35..abfb2168 100644
--- a/app/Auth/TotpAuth.php
+++ b/app/Auth/TotpAuth.php
@@ -11,7 +11,7 @@ use Kanboard\Core\Security\PostAuthenticationProviderInterface;
/**
* TOTP Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class TotpAuth extends Base implements PostAuthenticationProviderInterface
diff --git a/app/Console/DatabaseMigrationCommand.php b/app/Console/DatabaseMigrationCommand.php
new file mode 100644
index 00000000..252d4369
--- /dev/null
+++ b/app/Console/DatabaseMigrationCommand.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Kanboard\Console;
+
+use Kanboard\ServiceProvider\DatabaseProvider;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DatabaseMigrationCommand extends DatabaseVersionCommand
+{
+ protected function configure()
+ {
+ $this
+ ->setName('db:migrate')
+ ->setDescription('Execute SQL migrations');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ parent::execute($input, $output);
+ DatabaseProvider::runMigrations($this->container['db']);
+ }
+}
diff --git a/app/Console/DatabaseVersionCommand.php b/app/Console/DatabaseVersionCommand.php
new file mode 100644
index 00000000..5b1f1ed1
--- /dev/null
+++ b/app/Console/DatabaseVersionCommand.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Kanboard\Console;
+
+use Kanboard\ServiceProvider\DatabaseProvider;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DatabaseVersionCommand extends BaseCommand
+{
+ protected function configure()
+ {
+ $this
+ ->setName('db:version')
+ ->setDescription('Show database schema version');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $output->writeln('<info>Current version: '.DatabaseProvider::getSchemaVersion($this->container['db']).'</info>');
+ $output->writeln('<info>Last version: '.\Schema\VERSION.'</info>');
+ }
+}
diff --git a/app/Controller/CommentController.php b/app/Controller/CommentController.php
index c61a0602..526bd2bf 100644
--- a/app/Controller/CommentController.php
+++ b/app/Controller/CommentController.php
@@ -48,6 +48,7 @@ class CommentController extends BaseController
*/
public function create(array $values = array(), array $errors = array())
{
+ $project = $this->getProject();
$task = $this->getTask();
if (empty($values)) {
@@ -57,10 +58,13 @@ class CommentController extends BaseController
);
}
- $this->response->html($this->template->render('comment/create', array(
+ $values['project_id'] = $task['project_id'];
+
+ $this->response->html($this->helper->layout->task('comment/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
+ 'project' => $project,
)));
}
@@ -103,8 +107,14 @@ class CommentController extends BaseController
$task = $this->getTask();
$comment = $this->getComment();
+ if (empty($values)) {
+ $values = $comment;
+ }
+
+ $values['project_id'] = $task['project_id'];
+
$this->response->html($this->template->render('comment/edit', array(
- 'values' => empty($values) ? $comment : $values,
+ 'values' => $values,
'errors' => $errors,
'comment' => $comment,
'task' => $task,
diff --git a/app/Controller/TaskAjaxController.php b/app/Controller/TaskAjaxController.php
index f9feff15..609dd23c 100644
--- a/app/Controller/TaskAjaxController.php
+++ b/app/Controller/TaskAjaxController.php
@@ -5,8 +5,12 @@ namespace Kanboard\Controller;
use Kanboard\Filter\TaskIdExclusionFilter;
use Kanboard\Filter\TaskIdFilter;
use Kanboard\Filter\TaskProjectsFilter;
+use Kanboard\Filter\TaskStartsWithIdFilter;
+use Kanboard\Filter\TaskStatusFilter;
use Kanboard\Filter\TaskTitleFilter;
use Kanboard\Formatter\TaskAutoCompleteFormatter;
+use Kanboard\Formatter\TaskSuggestMenuFormatter;
+use Kanboard\Model\TaskModel;
/**
* Task Ajax Controller
@@ -19,7 +23,6 @@ class TaskAjaxController extends BaseController
/**
* Task auto-completion (Ajax)
*
- * @access public
*/
public function autocomplete()
{
@@ -46,4 +49,24 @@ class TaskAjaxController extends BaseController
$this->response->json($filter->format(new TaskAutoCompleteFormatter($this->container)));
}
}
+
+ /**
+ * Task ID suggest menu
+ */
+ public function suggest()
+ {
+ $taskId = $this->request->getIntegerParam('search');
+ $projectIds = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
+
+ if (empty($projectIds)) {
+ $this->response->json(array());
+ } else {
+ $filter = $this->taskQuery
+ ->withFilter(new TaskProjectsFilter($projectIds))
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
+ ->withFilter(new TaskStartsWithIdFilter($taskId));
+
+ $this->response->json($filter->format(new TaskSuggestMenuFormatter($this->container)));
+ }
+ }
}
diff --git a/app/Controller/UserAjaxController.php b/app/Controller/UserAjaxController.php
index ed180471..d93bfe9a 100644
--- a/app/Controller/UserAjaxController.php
+++ b/app/Controller/UserAjaxController.php
@@ -4,6 +4,7 @@ namespace Kanboard\Controller;
use Kanboard\Filter\UserNameFilter;
use Kanboard\Formatter\UserAutoCompleteFormatter;
+use Kanboard\Formatter\UserMentionFormatter;
use Kanboard\Model\UserModel;
/**
@@ -35,9 +36,14 @@ class UserAjaxController extends BaseController
public function mention()
{
$project_id = $this->request->getStringParam('project_id');
- $query = $this->request->getStringParam('q');
+ $query = $this->request->getStringParam('search');
$users = $this->projectPermissionModel->findUsernames($project_id, $query);
- $this->response->json($users);
+
+ $this->response->json(
+ UserMentionFormatter::getInstance($this->container)
+ ->withUsers($users)
+ ->format()
+ );
}
/**
diff --git a/app/Controller/UserApiAccessController.php b/app/Controller/UserApiAccessController.php
new file mode 100644
index 00000000..e03514d5
--- /dev/null
+++ b/app/Controller/UserApiAccessController.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Security\Token;
+
+/**
+ * Class UserApiAccessController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class UserApiAccessController extends BaseController
+{
+ public function show()
+ {
+ $user = $this->getUser();
+
+ return $this->response->html($this->helper->layout->user('user_api_access/show', array(
+ 'user' => $user,
+ 'title' => t('API User Access'),
+ )));
+ }
+
+ public function generate()
+ {
+ $user = $this->getUser();
+ $this->checkCSRFParam();
+
+ $this->userModel->update(array(
+ 'id' => $user['id'],
+ 'api_access_token' => Token::getToken(),
+ ));
+
+ $this->response->redirect($this->helper->url->to('UserApiAccessController', 'show', array('user_id' => $user['id'])));
+ }
+
+ public function remove()
+ {
+ $user = $this->getUser();
+ $this->checkCSRFParam();
+
+ $this->userModel->update(array(
+ 'id' => $user['id'],
+ 'api_access_token' => null,
+ ));
+
+ $this->response->redirect($this->helper->url->to('UserApiAccessController', 'show', array('user_id' => $user['id'])));
+ }
+} \ No newline at end of file
diff --git a/app/Core/Base.php b/app/Core/Base.php
index 3dbf47f9..e7ccafaa 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -126,7 +126,6 @@ use Pimple\Container;
* @property \Kanboard\Model\TransitionModel $transitionModel
* @property \Kanboard\Model\UserModel $userModel
* @property \Kanboard\Model\UserLockingModel $userLockingModel
- * @property \Kanboard\Model\UserMentionModel $userMentionModel
* @property \Kanboard\Model\UserNotificationModel $userNotificationModel
* @property \Kanboard\Model\UserNotificationTypeModel $userNotificationTypeModel
* @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel
@@ -178,6 +177,7 @@ use Pimple\Container;
* @property \Kanboard\Job\ProjectFileEventJob $projectFileEventJob
* @property \Kanboard\Job\NotificationJob $notificationJob
* @property \Kanboard\Job\ProjectMetricJob $projectMetricJob
+ * @property \Kanboard\Job\UserMentionJob $userMentionJob
* @property \Psr\Log\LoggerInterface $logger
* @property \PicoDb\Database $db
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
diff --git a/app/Core/Helper.php b/app/Core/Helper.php
index b5c560af..9660c348 100644
--- a/app/Core/Helper.php
+++ b/app/Core/Helper.php
@@ -12,6 +12,7 @@ use Pimple\Container;
*
* @property \Kanboard\Helper\AppHelper $app
* @property \Kanboard\Helper\AssetHelper $asset
+ * @property \Kanboard\Helper\AvatarHelper $avatar
* @property \Kanboard\Helper\BoardHelper $board
* @property \Kanboard\Helper\CalendarHelper $calendar
* @property \Kanboard\Helper\DateHelper $dt
diff --git a/app/Core/Markdown.php b/app/Core/Markdown.php
index b5abe5ed..799aefb4 100644
--- a/app/Core/Markdown.php
+++ b/app/Core/Markdown.php
@@ -86,7 +86,7 @@ class Markdown extends Parsedown
*/
protected function inlineUserLink(array $Excerpt)
{
- if (! $this->isPublicLink && preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) {
+ if (! $this->isPublicLink && preg_match('/^@([^\s,!.:?]+)/', $Excerpt['text'], $matches)) {
$user_id = $this->container['userModel']->getIdByUsername($matches[1]);
if (! empty($user_id)) {
@@ -125,7 +125,10 @@ class Markdown extends Parsedown
array(
'token' => $token,
'task_id' => $task_id,
- )
+ ),
+ false,
+ '',
+ true
);
}
diff --git a/app/Core/Session/SessionStorage.php b/app/Core/Session/SessionStorage.php
index 9e93602c..e6478d8d 100644
--- a/app/Core/Session/SessionStorage.php
+++ b/app/Core/Session/SessionStorage.php
@@ -19,6 +19,7 @@ namespace Kanboard\Core\Session;
* @property bool $hasSubtaskInProgress
* @property bool $hasRememberMe
* @property bool $boardCollapsed
+ * @property string $scope
* @property bool $twoFactorBeforeCodeCalled
* @property string $twoFactorSecret
* @property string $oauthState
diff --git a/app/Event/GenericEvent.php b/app/Event/GenericEvent.php
index 94a51479..e87d9481 100644
--- a/app/Event/GenericEvent.php
+++ b/app/Event/GenericEvent.php
@@ -14,6 +14,28 @@ class GenericEvent extends BaseEvent implements ArrayAccess
$this->container = $values;
}
+ public function getTaskId()
+ {
+ if (isset($this->container['task']['id'])) {
+ return $this->container['task']['id'];
+ }
+
+ if (isset($this->container['task_id'])) {
+ return $this->container['task_id'];
+ }
+
+ return null;
+ }
+
+ public function getProjectId()
+ {
+ if (isset($this->container['task']['project_id'])) {
+ return $this->container['task']['project_id'];
+ }
+
+ return null;
+ }
+
public function getAll()
{
return $this->container;
diff --git a/app/Event/ProjectFileEvent.php b/app/Event/ProjectFileEvent.php
index 5d57e463..e1d29c48 100644
--- a/app/Event/ProjectFileEvent.php
+++ b/app/Event/ProjectFileEvent.php
@@ -4,4 +4,12 @@ namespace Kanboard\Event;
class ProjectFileEvent extends GenericEvent
{
+ public function getProjectId()
+ {
+ if (isset($this->container['file']['project_id'])) {
+ return $this->container['file']['project_id'];
+ }
+
+ return null;
+ }
}
diff --git a/app/Filter/TaskStartsWithIdFilter.php b/app/Filter/TaskStartsWithIdFilter.php
new file mode 100644
index 00000000..8b7cc678
--- /dev/null
+++ b/app/Filter/TaskStartsWithIdFilter.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Kanboard\Filter;
+
+use Kanboard\Core\Filter\FilterInterface;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskIdSearchFilter
+ *
+ * @package Kanboard\Filter
+ * @author Frederic Guillot
+ */
+class TaskStartsWithIdFilter extends BaseFilter implements FilterInterface
+{
+ /**
+ * Get search attribute
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getAttributes()
+ {
+ return array('starts_with_id');
+ }
+
+ /**
+ * Apply filter
+ *
+ * @access public
+ * @return FilterInterface
+ */
+ public function apply()
+ {
+ $this->query->ilike('CAST('.TaskModel::TABLE.'.id AS CHAR(8))', $this->value.'%');
+ return $this;
+ }
+}
diff --git a/app/Formatter/BaseFormatter.php b/app/Formatter/BaseFormatter.php
index 89c48437..0d62628e 100644
--- a/app/Formatter/BaseFormatter.php
+++ b/app/Formatter/BaseFormatter.php
@@ -4,7 +4,6 @@ namespace Kanboard\Formatter;
use Kanboard\Core\Base;
use PicoDb\Table;
-use Pimple\Container;
/**
* Class BaseFormatter
@@ -23,19 +22,6 @@ abstract class BaseFormatter extends Base
protected $query;
/**
- * Get object instance
- *
- * @static
- * @access public
- * @param Container $container
- * @return static
- */
- public static function getInstance(Container $container)
- {
- return new static($container);
- }
-
- /**
* Set query
*
* @access public
diff --git a/app/Formatter/BaseTaskCalendarFormatter.php b/app/Formatter/BaseTaskCalendarFormatter.php
index 8fab3e9a..3d9ead4d 100644
--- a/app/Formatter/BaseTaskCalendarFormatter.php
+++ b/app/Formatter/BaseTaskCalendarFormatter.php
@@ -2,8 +2,6 @@
namespace Kanboard\Formatter;
-use Kanboard\Core\Filter\FormatterInterface;
-
/**
* Common class to handle calendar events
*
@@ -34,7 +32,7 @@ abstract class BaseTaskCalendarFormatter extends BaseFormatter
* @access public
* @param string $start_column Column name for the start date
* @param string $end_column Column name for the end date
- * @return FormatterInterface
+ * @return $this
*/
public function setColumns($start_column, $end_column = '')
{
diff --git a/app/Formatter/TaskAutoCompleteFormatter.php b/app/Formatter/TaskAutoCompleteFormatter.php
index 2d9f7341..3a4f1e1a 100644
--- a/app/Formatter/TaskAutoCompleteFormatter.php
+++ b/app/Formatter/TaskAutoCompleteFormatter.php
@@ -14,6 +14,20 @@ use Kanboard\Model\TaskModel;
*/
class TaskAutoCompleteFormatter extends BaseFormatter implements FormatterInterface
{
+ protected $limit = 25;
+
+ /**
+ * Limit number of results
+ *
+ * @param $limit
+ * @return $this
+ */
+ public function withLimit($limit)
+ {
+ $this->limit = $limit;
+ return $this;
+ }
+
/**
* Apply formatter
*
@@ -22,11 +36,15 @@ class TaskAutoCompleteFormatter extends BaseFormatter implements FormatterInterf
*/
public function format()
{
- $tasks = $this->query->columns(
- TaskModel::TABLE.'.id',
- TaskModel::TABLE.'.title',
- ProjectModel::TABLE.'.name AS project_name'
- )->asc(TaskModel::TABLE.'.id')->findAll();
+ $tasks = $this->query
+ ->columns(
+ TaskModel::TABLE.'.id',
+ TaskModel::TABLE.'.title',
+ ProjectModel::TABLE.'.name AS project_name'
+ )
+ ->asc(TaskModel::TABLE.'.id')
+ ->limit($this->limit)
+ ->findAll();
foreach ($tasks as &$task) {
$task['value'] = $task['title'];
diff --git a/app/Formatter/TaskSuggestMenuFormatter.php b/app/Formatter/TaskSuggestMenuFormatter.php
new file mode 100644
index 00000000..518f99e6
--- /dev/null
+++ b/app/Formatter/TaskSuggestMenuFormatter.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+use Kanboard\Core\Filter\FormatterInterface;
+use Kanboard\Model\ProjectModel;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskSuggestMenuFormatter
+ *
+ * @package Kanboard\Formatter
+ * @author Frederic Guillot
+ */
+class TaskSuggestMenuFormatter extends BaseFormatter implements FormatterInterface
+{
+ protected $limit = 25;
+
+ /**
+ * Limit number of results
+ *
+ * @param $limit
+ * @return $this
+ */
+ public function withLimit($limit)
+ {
+ $this->limit = $limit;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return mixed
+ */
+ public function format()
+ {
+ $result = array();
+ $tasks = $this->query
+ ->columns(
+ TaskModel::TABLE.'.id',
+ TaskModel::TABLE.'.title',
+ ProjectModel::TABLE.'.name AS project_name'
+ )
+ ->asc(TaskModel::TABLE.'.id')
+ ->limit($this->limit)
+ ->findAll();
+
+ foreach ($tasks as $task) {
+ $html = '#'.$task['id'].' ';
+ $html .= $this->helper->text->e($task['title']).' ';
+ $html .= '<small>'.$this->helper->text->e($task['project_name']).'</small>';
+
+ $result[] = array(
+ 'value' => (string) $task['id'],
+ 'html' => $html,
+ );
+ }
+
+ return $result;
+ }
+}
diff --git a/app/Formatter/UserMentionFormatter.php b/app/Formatter/UserMentionFormatter.php
new file mode 100644
index 00000000..395fc463
--- /dev/null
+++ b/app/Formatter/UserMentionFormatter.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+/**
+ * Class UserMentionFormatter
+ *
+ * @package Kanboard\Formatter
+ * @author Frederic Guillot
+ */
+class UserMentionFormatter extends BaseFormatter
+{
+ protected $users = array();
+
+ /**
+ * Set users
+ *
+ * @param array $users
+ * @return $this
+ */
+ public function withUsers(array $users) {
+ $this->users = $users;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ $result = array();
+
+ foreach ($this->users as $user) {
+ $html = $this->helper->avatar->small(
+ $user['id'],
+ $user['username'],
+ $user['name'],
+ $user['email'],
+ $user['avatar_path'],
+ 'avatar-inline'
+ );
+
+ $html .= ' '.$this->helper->text->e($user['username']);
+
+ if (! empty($user['name'])) {
+ $html .= ' <small>'.$this->helper->text->e($user['name']).'</small>';
+ }
+
+ $result[] = array(
+ 'value' => $user['username'],
+ 'html' => $html,
+ );
+ }
+
+ return $result;
+ }
+} \ No newline at end of file
diff --git a/app/Helper/FormHelper.php b/app/Helper/FormHelper.php
index 629de9ff..9eabd724 100644
--- a/app/Helper/FormHelper.php
+++ b/app/Helper/FormHelper.php
@@ -131,16 +131,34 @@ class FormHelper extends Base
* Display a checkbox field
*
* @access public
- * @param string $name Field name
- * @param string $label Form label
- * @param string $value Form value
- * @param boolean $checked Field selected or not
- * @param string $class CSS class
+ * @param string $name Field name
+ * @param string $label Form label
+ * @param string $value Form value
+ * @param boolean $checked Field selected or not
+ * @param string $class CSS class
+ * @param array $attributes
* @return string
*/
- public function checkbox($name, $label, $value, $checked = false, $class = '')
+ public function checkbox($name, $label, $value, $checked = false, $class = '', array $attributes = array())
{
- return '<label><input type="checkbox" name="'.$name.'" class="'.$class.'" value="'.$this->helper->text->e($value).'" '.($checked ? 'checked="checked"' : '').'>&nbsp;'.$this->helper->text->e($label).'</label>';
+ $htmlAttributes = '';
+
+ if ($checked) {
+ $attributes['checked'] = 'checked';
+ }
+
+ foreach ($attributes as $attribute => $attributeValue) {
+ $htmlAttributes .= sprintf('%s="%s"', $attribute, $this->helper->text->e($attributeValue));
+ }
+
+ return sprintf(
+ '<label><input type="checkbox" name="%s" class="%s" value="%s" %s>&nbsp;%s</label>',
+ $name,
+ $class,
+ $this->helper->text->e($value),
+ $htmlAttributes,
+ $this->helper->text->e($label)
+ );
}
/**
@@ -195,15 +213,25 @@ class FormHelper extends Base
{
$params = array(
'name' => $name,
- 'text' => isset($values[$name]) ? $this->helper->text->e($values[$name]) : '',
+ 'text' => isset($values[$name]) ? $values[$name] : '',
'css' => $this->errorClass($errors, $name),
'required' => isset($attributes['required']) && $attributes['required'],
'tabindex' => isset($attributes['tabindex']) ? $attributes['tabindex'] : '-1',
'labelPreview' => t('Preview'),
'labelWrite' => t('Write'),
'placeholder' => t('Write your text in Markdown'),
+ 'autofocus' => isset($attributes['autofocus']) && $attributes['autofocus'],
+ 'suggestOptions' => array(
+ 'triggers' => array(
+ '#' => $this->helper->url->to('TaskAjaxController', 'suggest', array('search' => 'SEARCH_TERM')),
+ )
+ ),
);
+ if (isset($values['project_id'])) {
+ $params['suggestOptions']['triggers']['@'] = $this->helper->url->to('UserAjaxController', 'mention', array('project_id' => $values['project_id'], 'search' => 'SEARCH_TERM'));
+ }
+
$html = '<div class="js-text-editor" data-params=\''.json_encode($params, JSON_HEX_APOS).'\'></div>';
$html .= $this->errorList($errors, $name);
diff --git a/app/Job/CommentEventJob.php b/app/Job/CommentEventJob.php
index 47cf8020..62fae40a 100644
--- a/app/Job/CommentEventJob.php
+++ b/app/Job/CommentEventJob.php
@@ -31,7 +31,6 @@ class CommentEventJob extends BaseJob
*
* @param int $commentId
* @param string $eventName
- * @return $this
*/
public function execute($commentId, $eventName)
{
@@ -43,7 +42,8 @@ class CommentEventJob extends BaseJob
$this->dispatcher->dispatch($eventName, $event);
if ($eventName === CommentModel::EVENT_CREATE) {
- $this->userMentionModel->fireEvents($event['comment']['comment'], CommentModel::EVENT_USER_MENTION, $event);
+ $userMentionJob = $this->userMentionJob->withParams($event['comment']['comment'], CommentModel::EVENT_USER_MENTION, $event);
+ $this->queueManager->push($userMentionJob);
}
}
}
diff --git a/app/Job/TaskEventJob.php b/app/Job/TaskEventJob.php
index 7d026a68..acc7fca3 100644
--- a/app/Job/TaskEventJob.php
+++ b/app/Job/TaskEventJob.php
@@ -69,7 +69,8 @@ class TaskEventJob extends BaseJob
$this->dispatcher->dispatch($eventName, $event);
if ($eventName === TaskModel::EVENT_CREATE) {
- $this->userMentionModel->fireEvents($event['task']['description'], TaskModel::EVENT_USER_MENTION, $event);
+ $userMentionJob = $this->userMentionJob->withParams($event['task']['description'], TaskModel::EVENT_USER_MENTION, $event);
+ $this->queueManager->push($userMentionJob);
}
}
}
diff --git a/app/Job/UserMentionJob.php b/app/Job/UserMentionJob.php
new file mode 100644
index 00000000..bbb27131
--- /dev/null
+++ b/app/Job/UserMentionJob.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Kanboard\Job;
+
+use Kanboard\Event\GenericEvent;
+use Kanboard\Model\UserModel;
+
+/**
+ * Class UserMentionJob
+ *
+ * @package Kanboard\Job
+ * @author Frederic Guillot
+ */
+class UserMentionJob extends BaseJob
+{
+ /**
+ * Set job parameters
+ *
+ * @param string $text
+ * @param string $eventName
+ * @param GenericEvent $event
+ * @return $this
+ */
+ public function withParams($text, $eventName, GenericEvent $event)
+ {
+ $this->jobParams = array($text, $eventName, $event->getAll());
+ return $this;
+ }
+
+ /**
+ * Execute job
+ *
+ * @param string $text
+ * @param string $eventName
+ * @param array $eventData
+ */
+ public function execute($text, $eventName, array $eventData)
+ {
+ $event = new GenericEvent($eventData);
+ $users = $this->getMentionedUsers($text);
+
+ foreach ($users as $user) {
+ if ($this->projectPermissionModel->isMember($event->getProjectId(), $user['id'])) {
+ $event['mention'] = $user;
+ $this->dispatcher->dispatch($eventName, $event);
+ }
+ }
+ }
+
+ /**
+ * Get list of mentioned users
+ *
+ * @access public
+ * @param string $text
+ * @return array
+ */
+ public function getMentionedUsers($text)
+ {
+ $users = array();
+
+ if (preg_match_all('/@([^\s,!.:?]+)/', $text, $matches)) {
+ $users = $this->db->table(UserModel::TABLE)
+ ->columns('id', 'username', 'name', 'email', 'language')
+ ->eq('notifications_enabled', 1)
+ ->neq('id', $this->userSession->getId())
+ ->in('username', array_unique($matches[1]))
+ ->findAll();
+ }
+
+ return $users;
+ }
+}
diff --git a/app/Locale/bs_BA/translations.php b/app/Locale/bs_BA/translations.php
index cb9ad9d5..e27a4501 100644
--- a/app/Locale/bs_BA/translations.php
+++ b/app/Locale/bs_BA/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/cs_CZ/translations.php b/app/Locale/cs_CZ/translations.php
index 8541f303..5091de00 100644
--- a/app/Locale/cs_CZ/translations.php
+++ b/app/Locale/cs_CZ/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php
index 78a6d35e..2ca864e5 100644
--- a/app/Locale/da_DK/translations.php
+++ b/app/Locale/da_DK/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php
index 433b7a2f..950a3b77 100644
--- a/app/Locale/de_DE/translations.php
+++ b/app/Locale/de_DE/translations.php
@@ -1278,4 +1278,13 @@ return array(
'Moving a task is not permitted' => 'Verschieben einer Aufgabe ist nicht erlaubt',
'This value must be in the range %d to %d' => 'Dieser Wert muss im Bereich %d bis %d sein',
'You are not allowed to move this task.' => 'Sie haben nicht die Berechtigung, diese Aufgabe zu verschieben.',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/el_GR/translations.php b/app/Locale/el_GR/translations.php
index ad17b4ed..7b7d855e 100644
--- a/app/Locale/el_GR/translations.php
+++ b/app/Locale/el_GR/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php
index b3a68751..984d42db 100644
--- a/app/Locale/es_ES/translations.php
+++ b/app/Locale/es_ES/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php
index 733625b6..41e51fa1 100644
--- a/app/Locale/fi_FI/translations.php
+++ b/app/Locale/fi_FI/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php
index 59f4609e..daac98ed 100644
--- a/app/Locale/fr_FR/translations.php
+++ b/app/Locale/fr_FR/translations.php
@@ -1279,4 +1279,13 @@ return array(
'Moving a task is not permitted' => 'Déplaçer une tâche n\'est pas autorisé',
'This value must be in the range %d to %d' => 'Cette valeur doit être définie entre %d et %d',
'You are not allowed to move this task.' => 'Vous n\'êtes pas autorisé à déplacer cette tâche.',
+ 'API User Access' => 'Accès utilisateur de l\'API',
+ 'Preview' => 'Aperçu',
+ 'Write' => 'Écrire',
+ 'Write your text in Markdown' => 'Écrivez votre texte en Markdown',
+ 'New External Task: %s' => 'Nouvelle tâche externe : %s',
+ 'No personal API access token registered.' => 'Aucun jeton d\'accès personnel à l\'API enregistré.',
+ 'Your personal API access token is "%s"' => 'Votre jeton d\'accès personnel à l\'API est « %s »',
+ 'Remove your token' => 'Supprimer votre jeton',
+ 'Generate a new token' => 'Générer un nouveau jeton',
);
diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php
index b5a23428..784c27ba 100644
--- a/app/Locale/hu_HU/translations.php
+++ b/app/Locale/hu_HU/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/id_ID/translations.php b/app/Locale/id_ID/translations.php
index e711a512..9398d51d 100644
--- a/app/Locale/id_ID/translations.php
+++ b/app/Locale/id_ID/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php
index fa5f5105..65defe86 100644
--- a/app/Locale/it_IT/translations.php
+++ b/app/Locale/it_IT/translations.php
@@ -297,7 +297,7 @@ return array(
'Display another project' => 'Mostra un altro progetto',
'Created by %s' => 'Creato da %s',
'Tasks Export' => 'Export dei task',
- 'Start Date' => 'Data d\'inizio',
+ 'Start Date' => 'Data di inizio',
'End Date' => 'Data di fine',
'Execute' => 'Esegui',
'Task Id' => 'Id del task',
@@ -780,8 +780,8 @@ return array(
'Add task' => 'Aggiungi task',
'Start date:' => 'Data di inizio:',
'Due date:' => 'Data di completamento:',
- 'There is no start date or due date for this task.' => 'Nessuna data di inzio o di scadenza per questo task.',
- 'Moving or resizing a task will change the start and due date of the task.' => 'Spostando o ridimensionado un task ne modifca la data di inzio e di scadenza.',
+ 'There is no start date or due date for this task.' => 'Nessuna data di inizio o di scadenza per questo task.',
+ 'Moving or resizing a task will change the start and due date of the task.' => 'Spostando o ridimensionado un task ne modifca la data di inizio e di scadenza.',
'There is no task in your project.' => 'Non ci sono task nel tuo progetto.',
'Gantt chart' => 'Grafici Gantt',
'People who are project managers' => 'Persone che sono manager di progetto',
@@ -1184,7 +1184,7 @@ return array(
'Global tags' => 'Tag globali',
'There is no global tag at the moment.' => 'Non sono definiti tag globali al momento.',
'This field cannot be empty' => 'Questo campo non può essere vuoto',
- 'Close a task when there is no activity in an specific column' => 'Chiudi un task quando non vi è alcuna attività in un specifica colonna',
+ 'Close a task when there is no activity in an specific column' => 'Chiudi un task quando non vi è alcuna attività in una specifica colonna',
'%s removed a subtask for the task #%d' => '%s rimosso un subtask per il task #%d',
'%s removed a comment on the task #%d' => '%s rimosso un commento nel task #%d',
'Comment removed on task #%d' => 'Commento rimosso nel task #%d',
@@ -1234,17 +1234,17 @@ return array(
'Remove this role' => 'Cancella questo ruolo',
'There is no restriction for this role.' => 'Non ci sono restrizioni per questo ruolo.',
'Only moving task between those columns is permitted' => 'È permesso solo muovere i tast tra queste colonne',
- 'Close a task in a specific column when not moved during a given period' => 'Chiudi un task in una colonna specifica quando non è mosso per un determinato periodo',
+ 'Close a task in a specific column when not moved during a given period' => 'Chiudi un task in una colonna specifica quando non viene mosso per un determinato periodo',
'Edit columns' => 'Modifica colonne',
'The column restriction has been created successfully.' => 'La restrizione per le colonne è stata creata correttamente.',
'Unable to create this column restriction.' => 'Impossibile creare questa restrizione per colonne.',
'Column restriction removed successfully.' => 'Restrizione per colonne rimossa correttamente.',
'Unable to remove this restriction.' => 'Impossibile rimuovere questa restrizione.',
- 'Your custom project role has been created successfully.' => 'La regola per il progetto personalizzato è stata creata correttamente.',
- 'Unable to create custom project role.' => 'Impossibile creare la regola per il progetto personalizzato.',
- 'Your custom project role has been updated successfully.' => 'La regola per il progetto personalizzato è stata modificata correttamente.',
- 'Unable to update custom project role.' => 'Impossibile modificare correttamente la regola per il progetto personalizzato.',
- 'Custom project role removed successfully.' => 'Regola per il progetto personalizzato rimossa correttamente.',
+ 'Your custom project role has been created successfully.' => 'La regola personalizzata per il progetto è stata creata correttamente.',
+ 'Unable to create custom project role.' => 'Impossibile creare la regola personalizzata per il progetto.',
+ 'Your custom project role has been updated successfully.' => 'La regola personalizzata per il progetto è stata modificata correttamente.',
+ 'Unable to update custom project role.' => 'Impossibile modificare correttamente la regola personalizzata per il progetto.',
+ 'Custom project role removed successfully.' => 'Regola personalizzata per il progetto rimossa correttamente.',
'Unable to remove this project role.' => 'Impossibile rimuovere questa regola per il progetto.',
'The project restriction has been created successfully.' => 'La restrizione di progetto è stata creata con successo.',
'Unable to create this project restriction.' => 'Impossibile creare la restrizione di progetto.',
@@ -1257,10 +1257,10 @@ return array(
'Task creation is not permitted' => 'Creazione task non permessa',
'Closing or opening a task is not permitted' => 'Chiudere o aprire task non è permesso',
'New drag and drop restriction for the role "%s"' => 'Nuova restrizione "drag and drop" per la regola "%s"',
- 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Gli utenti che appartengono a questa regola saranno in grado di muovere i task solo tra la colonna di origine e quella di destinazione',
- 'Remove a column restriction' => 'Rimuovere restrizione colonna',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Gli utenti che appartengono a questa regola saranno in grado di spostare i task solo tra la colonna di origine e quella di destinazione',
+ 'Remove a column restriction' => 'Rimuovere una restrizione di colonna',
'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Vuoi davvero rimuovere questa restrizione di colonna: "%s" a "%s"?',
- 'New column restriction for the role "%s"' => 'Nuova restrizione colonna per la regola "%s"',
+ 'New column restriction for the role "%s"' => 'Nuova restrizione di colonna per la regola "%s"',
'Rule' => 'Regola',
'Do you really want to remove this column restriction?' => 'Vuoi davvero rimuovere questa restrizione di colonna?',
'Custom roles' => 'Regole personalizzate',
@@ -1273,9 +1273,18 @@ return array(
'Restriction' => 'Restrizione',
'Remove a project restriction' => 'Rimuovi restrizione di progetto',
'Do you really want to remove this project restriction: "%s"?' => 'Vuoi davvero rimuovere questa restrizione di progetto: "%s"?',
- 'Duplicate to multiple projects' => 'Dupplica i più progetti',
- 'This field is required' => 'Questo campo è richiesto',
- 'Moving a task is not permitted' => 'Muovere task non è permesso',
+ 'Duplicate to multiple projects' => 'Duplica su più progetti',
+ 'This field is required' => 'Questo campo è obbligatorio',
+ 'Moving a task is not permitted' => 'Spostare task non è permesso',
'This value must be in the range %d to %d' => 'Questo valore deve essere compreso tra %d e %d',
- 'You are not allowed to move this task.' => 'Non ti è permesso muovere questo task.',
+ 'You are not allowed to move this task.' => 'Non ti è permesso spostare questo task.',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php
index b9feed07..d0487fb7 100644
--- a/app/Locale/ja_JP/translations.php
+++ b/app/Locale/ja_JP/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/ko_KR/translations.php b/app/Locale/ko_KR/translations.php
index 5339111b..a613b467 100644
--- a/app/Locale/ko_KR/translations.php
+++ b/app/Locale/ko_KR/translations.php
@@ -402,9 +402,9 @@ return array(
'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)' => '',
+ 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '최근 수정된 작업 고려 기간(초), (비활성화:0, 기본값:2일)',
+ 'Frequency in second (60 seconds by default)' => '초당 횟수(기본값:60초)',
+ 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '초당 횟수(비활성화:0, 기본값:10초)',
'Application URL' => '애플리케이션의 URL',
'Token regenerated.' => '토큰이 다시 생성되었습니다.',
'Date format' => '데이터 포멧',
@@ -555,18 +555,18 @@ return array(
'No results match:' => '결과가 일치하지 않았습니다',
'Currency' => '통화',
'Private project' => '개인 프로젝트',
- // 'AUD - Australian Dollar' => '',
- // 'CAD - Canadian Dollar' => '',
- // 'CHF - Swiss Francs' => '',
+ 'AUD - Australian Dollar' => 'AUD - 호주 달러',
+ 'CAD - Canadian Dollar' => 'CAD -캐나다 달러',
+ 'CHF - Swiss Francs' => 'CHF - 스위스 프랑',
'Custom Stylesheet' => '커스텀 스타일 시트',
'download' => '다운로드',
- // 'EUR - Euro' => '',
- // 'GBP - British Pound' => '',
- // 'INR - Indian Rupee' => '',
- // 'JPY - Japanese Yen' => '',
- // 'NZD - New Zealand Dollar' => '',
- // 'RSD - Serbian dinar' => '',
- // 'USD - US Dollar' => '',
+ 'EUR - Euro' => 'EUR - 유로',
+ 'GBP - British Pound' => 'GBP - 영국 파운드',
+ 'INR - Indian Rupee' => 'INR - 인도 루피',
+ 'JPY - Japanese Yen' => 'JPY - 일본 엔',
+ 'NZD - New Zealand Dollar' => 'NZD - 뉴질랜드 달러',
+ 'RSD - Serbian dinar' => 'RSD - 세르비아 디나르',
+ 'USD - US Dollar' => 'USD - 미국 달러',
'Destination column' => '이동 후 컬럼',
'Move the task to another column when assigned to a user' => '사용자의 할당을 하면 할일을 다른 컬럼에 이동',
'Move the task to another column when assignee is cleared' => '사용자의 할당이 없어지면 할일을 다른 컬럼에 이동',
@@ -600,12 +600,12 @@ return array(
'Assign a color when the task is moved to a specific column' => '상세 컬럼으로 이동할 할일의 색깔을 지정하세요',
'%s via Kanboard' => '%s via E-board',
'Burndown chart' => '번다운 차트',
- // 'This chart show the task complexity over the time (Work Remaining).' => '',
+ 'This chart show the task complexity over the time (Work Remaining).' => '이 차트는 시간에 따른 할일의 복잡성을 보여줍니다. (남아있는 일)',
'Screenshot taken %s' => '스크린샷_%s',
'Add a screenshot' => '스크린샷 추가',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '스크린샷을 CTRL+V 혹은 ⌘+V를 눌러 붙여넣기',
'Screenshot uploaded successfully.' => '스크린샷을 업로드하였습니다',
- // 'SEK - Swedish Krona' => '',
+ 'SEK - Swedish Krona' => 'SEK - 스위스 크로나',
'Identifier' => '식별자',
'Disable two factor authentication' => '이중 인증 비활성화',
'Do you really want to disable the two factor authentication for this user: "%s"?' => '"%s" 담당자의 이중 인증을 비활성화 하시겠습니까?',
@@ -613,7 +613,7 @@ return array(
'Start to type task title...' => '할일 제목을 입력하세요',
'A task cannot be linked to itself' => '할일을 자기자신에게 연결할 수 없습니다',
'The exact same link already exists' => '동일한 링크가 이미 존재합니다',
- // 'Recurrent task is scheduled to be generated' => '',
+ 'Recurrent task is scheduled to be generated' => '반복 할일이 생성된 예정입니다.',
'Score' => '점수',
'The identifier must be unique' => '식별자는 유일해야 합니다',
'This linked task id doesn\'t exists' => '연결된 할일 ID가 존재하지 않습니다',
@@ -626,7 +626,7 @@ return array(
'Base date to calculate new due date' => '새로운 기본 종료날짜 계산',
'Action date' => '시작날짜',
'Base date to calculate new due date: ' => '새로운 기본 종료날짜 계산: ',
- // 'This task has created this child task: ' => '',
+ 'This task has created this child task: ' => '이 할일은 하위 할일을 만들었습니다.',
'Day(s)' => '일',
'Existing due date' => '기존 종료날짜',
'Factor to calculate new due date: ' => '새로운 종료날짜 계산: ',
@@ -635,7 +635,7 @@ return array(
'This task has been created by: ' => '할일을 만들었습니다: ',
'Recurrent task has been generated:' => '반복 할일이 생성되었습니다',
'Timeframe to calculate new due date: ' => '종료날짜 계산 단위',
- // 'Trigger to generate recurrent task: ' => '',
+ 'Trigger to generate recurrent task: ' => '반복 할일 생성 트리거',
'When task is closed' => '할일을 마쳤을때',
'When task is moved from first column' => '할일이 첫번째 컬럼으로 옮겨졌을때',
'When task is moved to last column' => '할일이 마지막 컬럼으로 옮겨졌을때',
@@ -649,7 +649,7 @@ return array(
'Subtasks time tracking' => '서브 할일 시간 트래킹',
'User calendar view' => '담당자 달력 보기',
'Automatically update the start date' => '시작일에 자동 갱신',
- // 'iCal feed' => '',
+ 'iCal feed' => 'iCal 피드',
'Preferences' => '우선권',
'Security' => '보안',
'Two factor authentication disabled' => '이중 인증이 비활성화 되었습니다',
@@ -666,7 +666,7 @@ return array(
'Notification' => '알림',
'%s moved the task #%d to the first swimlane' => '%s가 할일 #%d를 첫번째 스웜레인으로 이동시켰습니다',
'Swimlane' => '스윔레인',
- // 'Gravatar' => '',
+ 'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s가 할일 %s를 첫번째 스웜레인으로 이동시켰습니다',
'%s moved the task %s to the swimlane "%s"' => '%s가 할일 %s를 %s 스웜레인으로 이동시켰습니다',
'This report contains all subtasks information for the given date range.' => '해당 기간의 모든 서브 할일 정보가 보고서에 포함됩니다',
@@ -695,7 +695,7 @@ return array(
'Only for tasks assigned to me' => '내가 담당자인 일',
'Only for tasks created by me' => '내가 만든 일',
'Only for tasks created by me and assigned to me' => '내가 만들었거나 내가 담당자인 일',
- // '%%Y-%%m-%%d' => '',
+ '%%Y-%%m-%%d' => '%%Y-%%m-%%d',
'Total for all columns' => '모든 컬럼',
'You need at least 2 days of data to show the chart.' => '차트를 보기 위하여 최소 2일의 데이터가 필요합니다',
'<15m' => '<15분',
@@ -781,12 +781,12 @@ return array(
'Start date:' => '시작일:',
'Due date:' => '만기일:',
'There is no start date or due date for this task.' => '할일의 시작일 또는 만기일이 없습니다',
- // 'Moving or resizing a task will change the start and due date of the task.' => '',
- // 'There is no task in your project.' => '',
+ 'Moving or resizing a task will change the start and due date of the task.' => '할일의 이동 혹은 리사이징으로 시작시간과 마감시간이 변경됩니다.',
+ 'There is no task in your project.' => '프로젝트에 할일이 없습니다.',
'Gantt chart' => '간트 차트',
'People who are project managers' => '프로젝트 매니저',
'People who are project members' => '프로젝트 멤버',
- // 'NOK - Norwegian Krone' => '',
+ 'NOK - Norwegian Krone' => 'NOK - 노르웨이 크로네',
'Show this column' => '컬럼 보이기',
'Hide this column' => '컬럼 숨기기',
'open file' => '열기',
@@ -802,14 +802,14 @@ return array(
'End date:' => '날짜 수정',
'There is no start date or end date for this project.' => '이 프로젝트에는 시작날짜와 종료날짜가 없습니다',
'Projects Gantt chart' => '프로젝트 간트차트',
- // 'Change task color when using a specific task link' => '',
- // 'Task link creation or modification' => '',
+ 'Change task color when using a specific task link' => '특정 할일 링크를 사용할때 할일의 색깔 변경',
+ 'Task link creation or modification' => '할일 링크 생성 혹은 수정',
'Milestone' => '마일스톤',
'Documentation: %s' => '문서: %s',
'Switch to the Gantt chart view' => '간트 차트 보기로 변경',
- // 'Reset the search/filter box' => '',
+ 'Reset the search/filter box' => '찾기/필터 박스 초기화',
'Documentation' => '문서',
- // 'Table of contents' => '',
+ 'Table of contents' => '목차',
'Gantt' => '간트',
'Author' => '글쓴이',
'Version' => '버전',
@@ -825,7 +825,7 @@ return array(
'Your custom filter have been updated successfully.' => '사용자 정의 필터가 성공적으로 수정되었습니다',
'Unable to update custom filter.' => '정의 필터 수정 비활성화',
'Web' => '웹',
- // 'New attachment on task #%d: %s' => '',
+ 'New attachment on task #%d: %s' => '할일 #%d의 새로운 첨부파일: %s',
'New comment on task #%d' => '할일 #%d에 새로운 댓글이 달렸습니다',
'Comment updated on task #%d' => '할일 #%d에 댓글이 갱신 되었습니다',
'New subtask on task #%d' => '서브 할일 #%d이 갱신 되었습니다',
@@ -843,7 +843,7 @@ return array(
'No new notifications.' => '알림이 없습니다',
'Mark all as read' => '모두 읽음',
'Mark as read' => '읽음',
- // 'Total number of tasks in this column across all swimlanes' => '',
+ 'Total number of tasks in this column across all swimlanes' => '모든 스웜라인 칼럼의 할일 수',
'Collapse swimlane' => '스웜라인 붕괴',
'Expand swimlane' => '스웜라인 확장',
'Add a new filter' => '새로운 필터 추가',
@@ -866,7 +866,7 @@ return array(
'Single Quote' => '따옴표',
'%s attached a file to the task #%d' => '%s가 할일 #%d에 파일을 추가하였습니다',
'There is no column or swimlane activated in your project!' => '프로젝트에 활성화된 컬럼이나 스웜라인이 없습니다',
- // 'Append filter (instead of replacement)' => '',
+ 'Append filter (instead of replacement)' => '필터 (변경 대신)추가',
'Append/Replace' => '추가/변경',
'Append' => '추가',
'Replace' => '변경',
@@ -890,7 +890,7 @@ return array(
'%s attached a new file to the task %s' => '%s이 새로운 파일을 할일 %s에 추가했습니다',
'Link type' => '링크 형식',
'Assign automatically a category based on a link' => '링크 기반 자동 카테고리 할당',
- // 'BAM - Konvertible Mark' => '',
+ 'BAM - Konvertible Mark' => 'BAM - 보스니아 마르카',
'Assignee Username' => '담당자의 사용자이름',
'Assignee Name' => '당장자 이름',
'Groups' => '그룹',
@@ -916,7 +916,7 @@ return array(
'Project Member' => '프로젝트 멤버',
'Project Viewer' => '프로젝트 뷰어',
'Your account is locked for %d minutes' => '%d분 동안 계정이 잠깁니다',
- // 'Invalid captcha' => '',
+ 'Invalid captcha' => '잘못된 보안 문자',
'The name must be unique' => '이름은 유일해야 합니다',
'View all groups' => '모든그룹보기',
'There is no user available.' => '가능한 사용자가 없습니다',
@@ -950,14 +950,14 @@ return array(
'Estimated Time' => '예상 시간',
'Actual Time' => '실제 시간',
'Estimated vs actual time' => '예상 vs 실제 시간',
- // 'RUB - Russian Ruble' => '',
+ 'RUB - Russian Ruble' => 'RUB - 러시아 루블',
'Assign the task to the person who does the action when the column is changed' => '컬럼이 변경되면 액션하지 않는 사람에게 할일을 할당합니다',
'Close a task in a specific column' => '상세 컬럼의 할일을 종료합니다',
'Time-based One-time Password Algorithm' => '시간에 기반한 1회용 패스워드 알고리즘',
'Two-Factor Provider: ' => '이중 인증: ',
'Disable two-factor authentication' => '이중 인증 비활성화',
'Enable two-factor authentication' => '이중 인증 활성화',
- // 'There is no integration registered at the moment.' => '',
+ 'There is no integration registered at the moment.' => '현재 등록된 통합이 없습니다.',
'Password Reset for Kanboard' => 'Kanboard의 비밀번호 초기화',
'Forgot password?' => '비밀번호 찾기',
'Enable "Forget Password"' => '"비밀번호 분실" 활성화',
@@ -977,9 +977,9 @@ return array(
'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '프로젝트 알림 방법으로 플러그인이 등록되지 않았습니다. 각각의 알림을 프로파일에서 설정하실 수 있습니다',
'My dashboard' => '대시보드',
'My profile' => '프로필',
- 'Project owner: ' => '프로젝트 소유자',
+ 'Project owner: ' => '프로젝트 소유자:',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '프로젝트 구분자는 선택사항이며, 숫자와 문자만 가능합니다. 예: MYPROJECT.',
- // 'Project owner' => '',
+ 'Project owner' => '프로젝트 소유자',
'Those dates are useful for the project Gantt chart.' => '이 날짜는 프로젝트 간트 차트에 사용됩니다',
'Private projects do not have users and groups management.' => '비밀 프로젝트는 사용자나 관리 그룹이 소유하지 않습니다',
'There is no project member.' => '프로젝트 맴버가 없습니다',
@@ -995,7 +995,7 @@ return array(
'Duration in days' => '기간',
'Send email when there is no activity on a task' => '활동이 없는 할일을 이메일로 보냅니다',
'Unable to fetch link information.' => '링크 정보 가져오기 비활성화',
- // 'Daily background job for tasks' => '',
+ 'Daily background job for tasks' => '할일의 일일 배경 작업 ',
'Auto' => '자동',
'Related' => '연관된',
'Attachment' => '첨부',
@@ -1010,7 +1010,7 @@ return array(
'Edit external link' => '외부 링크 수정',
'External link' => '외부 링크',
'Copy and paste your link here...' => '여기에 링크를 복사/붙여넣기',
- // 'URL' => '',
+ 'URL' => '인터넷 주소',
'Internal links' => '내부 링크',
'Assign to me' => '나에게 할당',
'Me' => '나',
@@ -1047,9 +1047,9 @@ return array(
'Do you really want to remove this custom filter: "%s"?' => '정의 필터를 삭제하시겠습니까: "%s"?',
'Remove a custom filter' => '정의 필터 삭제',
'User activated successfully.' => '사용자가 성공적으로 활성화되었습니다',
- // 'Unable to enable this user.' => '',
+ 'Unable to enable this user.' => '이 사용자를 활성화할 수 없습니다.',
'User disabled successfully.' => '사용자가 성공적으로 비활성화되었습니다',
- // 'Unable to disable this user.' => '',
+ 'Unable to disable this user.' => '이 사용자를 비활성화할 수 없습니다.',
'All files have been uploaded successfully.' => '모든 파일이 성공적으로 업로드되었습니다',
'View uploaded files' => '업로드 파일 보기',
'The maximum allowed file size is %sB.' => '업로드 파일의 최대 크기는 %sB 입니다',
@@ -1093,7 +1093,7 @@ return array(
'Local File' => '로컬 파일',
'Configuration' => '구성',
'PHP version:' => 'PHP 버전:',
- // 'PHP SAPI:' => '',
+ 'PHP SAPI:' => 'PHP SAPI:',
'OS version:' => '운영체제 버전:',
'Database version:' => '데이터베이스 버전:',
'Browser:' => '브라우저:',
@@ -1140,7 +1140,7 @@ return array(
'Unable to open plugin archive.' => '플러그인 아카이브를 열수 없습니다.',
'There is no file in the plugin archive.' => '플러그인 아카이브에 파일이 존재하지 않습니다.',
'Create tasks in bulk' => '대량의 할일 만들기',
- // 'Your Kanboard instance is not configured to install plugins from the user interface.' => '',
+ 'Your Kanboard instance is not configured to install plugins from the user interface.' => '칸보드 인스턴스가 사용자 인터페이스에서 플러그인을 설치하도록 구성되지 않았습니다',
'There is no plugin available.' => '사용 가능한 플러그인이 없습니다.',
'Install' => '설치',
'Update' => '갱신',
@@ -1212,70 +1212,79 @@ return array(
'Notifications for %s' => '%s의 알림',
'Subtasks export' => '서브할일 내보내기',
'Tasks exportation' => '할일 내보내기',
- // 'Assign a color when the task is moved to a specific swimlane' => '',
- // 'Assign a priority when the task is moved to a specific swimlane' => '',
- // 'User unlocked successfully.' => '',
- // 'Unable to unlock the user.' => '',
- // 'Move a task to another swimlane' => '',
- // 'Creator Name' => '',
- // 'Time spent and estimated' => '',
- // 'Move position' => '',
- // 'Move task to another position on the board' => '',
- // 'Insert before this task' => '',
- // 'Insert after this task' => '',
- // 'Unlock this user' => '',
- // 'Custom Project Roles' => '',
- // 'Add a new custom role' => '',
- // 'Restrictions for the role "%s"' => '',
- // 'Add a new project restriction' => '',
- // 'Add a new drag and drop restriction' => '',
- // 'Add a new column restriction' => '',
- // 'Edit this role' => '',
- // 'Remove this role' => '',
- // 'There is no restriction for this role.' => '',
- // 'Only moving task between those columns is permitted' => '',
- // 'Close a task in a specific column when not moved during a given period' => '',
- // 'Edit columns' => '',
- // 'The column restriction has been created successfully.' => '',
- // 'Unable to create this column restriction.' => '',
- // 'Column restriction removed successfully.' => '',
- // 'Unable to remove this restriction.' => '',
- // 'Your custom project role has been created successfully.' => '',
- // 'Unable to create custom project role.' => '',
- // 'Your custom project role has been updated successfully.' => '',
- // 'Unable to update custom project role.' => '',
- // 'Custom project role removed successfully.' => '',
- // 'Unable to remove this project role.' => '',
- // 'The project restriction has been created successfully.' => '',
- // 'Unable to create this project restriction.' => '',
- // 'Project restriction removed successfully.' => '',
- // 'You cannot create tasks in this column.' => '',
- // 'Task creation is permitted for this column' => '',
- // 'Closing or opening a task is permitted for this column' => '',
- // 'Task creation is blocked for this column' => '',
- // 'Closing or opening a task is blocked for this column' => '',
- // 'Task creation is not permitted' => '',
- // 'Closing or opening a task is not permitted' => '',
- // 'New drag and drop restriction for the role "%s"' => '',
- // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
- // 'Remove a column restriction' => '',
- // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
- // 'New column restriction for the role "%s"' => '',
- // 'Rule' => '',
- // 'Do you really want to remove this column restriction?' => '',
- // 'Custom roles' => '',
- // 'New custom project role' => '',
- // 'Edit custom project role' => '',
- // 'Remove a custom role' => '',
- // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
- // 'There is no custom role for this project.' => '',
- // 'New project restriction for the role "%s"' => '',
- // 'Restriction' => '',
- // 'Remove a project restriction' => '',
- // 'Do you really want to remove this project restriction: "%s"?' => '',
- // 'Duplicate to multiple projects' => '',
- // 'This field is required' => '',
- // 'Moving a task is not permitted' => '',
- // 'This value must be in the range %d to %d' => '',
- // 'You are not allowed to move this task.' => '',
+ 'Assign a color when the task is moved to a specific swimlane' => '할일이 특정 스웜라인으로 옮겨질 때 색상 지정',
+ 'Assign a priority when the task is moved to a specific swimlane' => '할일이 특정 스웜라인으로 옮겨질 때 우선순위 지정',
+ 'User unlocked successfully.' => '사용자 잠금 성공',
+ 'Unable to unlock the user.' => '사용자 해제 성공',
+ 'Move a task to another swimlane' => '다른 스웜라인으로 할일 이동',
+ 'Creator Name' => '생성자 이름',
+ 'Time spent and estimated' => '소요시간과 예상시간',
+ 'Move position' => '이동 위치',
+ 'Move task to another position on the board' => '할일을 보드의 다른 위치로 이동',
+ 'Insert before this task' => '이 할일 이전에 삽입',
+ 'Insert after this task' => '이 할일 이후에 삽입',
+ 'Unlock this user' => '사용자 잠금',
+ 'Custom Project Roles' => '정의 프로젝트 규칙',
+ 'Add a new custom role' => '새로운 정의 규칙 추가',
+ 'Restrictions for the role "%s"' => '"%s" 역할의 제약',
+ 'Add a new project restriction' => '새로운 프로젝트 제약 추가',
+ 'Add a new drag and drop restriction' => '새로운 드래그 앤 드롭 제약 추가',
+ 'Add a new column restriction' => '새로운 칼럼 제약 추가',
+ 'Edit this role' => '역할 수정',
+ 'Remove this role' => '역할 삭제',
+ 'There is no restriction for this role.' => '역할에 대한 제약이 없습니다.',
+ 'Only moving task between those columns is permitted' => '칼럼간 이동만 허용됩니다.',
+ 'Close a task in a specific column when not moved during a given period' => '특정 기간동안 이동하지 않은 특정 칼럼의 할일 마치기',
+ 'Edit columns' => '칼럼 수정',
+ 'The column restriction has been created successfully.' => '칼럼 제약이 생성되었습니다.',
+ 'Unable to create this column restriction.' => '칼럼 제약을 생성할 수 없습니다.',
+ 'Column restriction removed successfully.' => '칼럼 제약이 삭제되었습니다.',
+ 'Unable to remove this restriction.' => '칼럼 제약을 삭제할 수 없습니다.',
+ 'Your custom project role has been created successfully.' => '정의 프로젝트 역할이 생성되었습니다.',
+ 'Unable to create custom project role.' => '정의 프로젝트 역할을 생성할 수 없습니다.',
+ 'Your custom project role has been updated successfully.' => '정의 프로젝트 역할이 수정되었습니다.',
+ 'Unable to update custom project role.' => '정의 프로젝트 역할을 수정할 수 없습니다.',
+ 'Custom project role removed successfully.' => '정의 프로젝트 역할이 삭제되었습니다.',
+ 'Unable to remove this project role.' => '정의 프로젝트 역할을 삭제할 수 없습니다.',
+ 'The project restriction has been created successfully.' => '프로젝트 제약이 생성되었습니다.',
+ 'Unable to create this project restriction.' => '프로젝트 제약을 생성할 수 없습니다.',
+ 'Project restriction removed successfully.' => '프로젝트 제약을 삭제하였습니다.',
+ 'You cannot create tasks in this column.' => '이 칼럼의 할일을 생성할 수 없습니다.',
+ 'Task creation is permitted for this column' => '이 칼럼의 할일 생성이 허가되었습니다.',
+ 'Closing or opening a task is permitted for this column' => '이 칼럼의 할일 열기 혹은 닫기가 허가되었습니다.',
+ 'Task creation is blocked for this column' => '이 칼럼의 할일 생성이 거부되었습니다.',
+ 'Closing or opening a task is blocked for this column' => '이 칼럼의 할일 열기 혹은 닫기가 거부되었습니다.',
+ 'Task creation is not permitted' => '할일 생성이 거부되었습니다.',
+ 'Closing or opening a task is not permitted' => '할일 열기 혹은 닫기가 거부되었습니다.',
+ 'New drag and drop restriction for the role "%s"' => '"%s" 역할의 새로운 드래그 앤 드롭 제약',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '이 역할에 속한 사용자는 원본 및 대상 칼럼 사이에서만 할일을 이동할 수 있습니다',
+ 'Remove a column restriction' => '칼럼 제약 삭제',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => '칼럼 제약을 "%s" 에서 "%s"로 이동하시겠습니까?',
+ 'New column restriction for the role "%s"' => '"%s" 역할의 새로운 칼럼 제약',
+ 'Rule' => '역할',
+ 'Do you really want to remove this column restriction?' => '칼럼 제약을 삭제하시겠습니까?',
+ 'Custom roles' => '정의 역할',
+ 'New custom project role' => '새로운 정의 프로젝트 역할',
+ 'Edit custom project role' => '정의 프로젝트 역할 수정',
+ 'Remove a custom role' => '정의 역할 삭제',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '"%s" 정의 역할을 삭제하시겠습니까? 이 역할에 할당 된 모든 사람들이 프로젝트 멤버가됩니다.',
+ 'There is no custom role for this project.' => '이 프로젝트에는 정의 역할이 없습니다.',
+ 'New project restriction for the role "%s"' => '"%s" 역할의 새로운 프로젝트 제약',
+ 'Restriction' => '제약',
+ 'Remove a project restriction' => '프로젝트 제약 삭제',
+ 'Do you really want to remove this project restriction: "%s"?' => '"%s" 프로젝트 제약을 삭제하시겠습니까?',
+ 'Duplicate to multiple projects' => '다수의 프로젝트 복제',
+ 'This field is required' => '이 필드는 필수 항목입니다.',
+ 'Moving a task is not permitted' => '할일 이동이 거부되었습니다.',
+ 'This value must be in the range %d to %d' => '값의 범위는 %d 부터 %d 까지 입니다.',
+ 'You are not allowed to move this task.' => '당신은 할일 이동이 거부되었습니다.',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/my_MY/translations.php b/app/Locale/my_MY/translations.php
index 048d8b4b..be335dec 100644
--- a/app/Locale/my_MY/translations.php
+++ b/app/Locale/my_MY/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/nb_NO/translations.php b/app/Locale/nb_NO/translations.php
index 0d2a340d..6b49545c 100644
--- a/app/Locale/nb_NO/translations.php
+++ b/app/Locale/nb_NO/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php
index e1745c14..44434a4e 100644
--- a/app/Locale/nl_NL/translations.php
+++ b/app/Locale/nl_NL/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php
index 164abe0c..51c0fef8 100644
--- a/app/Locale/pl_PL/translations.php
+++ b/app/Locale/pl_PL/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php
index 637de30e..c0fa2387 100644
--- a/app/Locale/pt_BR/translations.php
+++ b/app/Locale/pt_BR/translations.php
@@ -1278,4 +1278,13 @@ return array(
'Moving a task is not permitted' => 'Mover uma tarefa não é permitido',
'This value must be in the range %d to %d' => 'Este valor precisa estar no intervalo %d até %d',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php
index 3b8975d9..47763d30 100644
--- a/app/Locale/pt_PT/translations.php
+++ b/app/Locale/pt_PT/translations.php
@@ -1278,4 +1278,13 @@ return array(
'Moving a task is not permitted' => 'Não é permitido mover uma tarefa',
'This value must be in the range %d to %d' => 'Este valor deve estar entre %d e %d',
'You are not allowed to move this task.' => 'Não lhe é permitido mover esta tarefa.',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php
index 1e5c02c2..4d821f6a 100644
--- a/app/Locale/ru_RU/translations.php
+++ b/app/Locale/ru_RU/translations.php
@@ -1278,4 +1278,13 @@ return array(
'Moving a task is not permitted' => 'Перемещение задачи не разрешено',
'This value must be in the range %d to %d' => 'Значение должно находиться в диапазоне от %d до %d',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php
index 2f061260..470e3390 100644
--- a/app/Locale/sr_Latn_RS/translations.php
+++ b/app/Locale/sr_Latn_RS/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php
index f32728e7..2ec4fa82 100644
--- a/app/Locale/sv_SE/translations.php
+++ b/app/Locale/sv_SE/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php
index 4025714a..5e0912fc 100644
--- a/app/Locale/th_TH/translations.php
+++ b/app/Locale/th_TH/translations.php
@@ -1278,4 +1278,13 @@ return array(
// 'Moving a task is not permitted' => '',
// 'This value must be in the range %d to %d' => '',
// 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php
index 24399891..1a648bbf 100644
--- a/app/Locale/tr_TR/translations.php
+++ b/app/Locale/tr_TR/translations.php
@@ -1278,4 +1278,13 @@ return array(
'Moving a task is not permitted' => 'Görev taşımaya izin verilmemiş',
'This value must be in the range %d to %d' => 'Bu değer şu aralıkta olmalı: "%d" "%d"',
'You are not allowed to move this task.' => 'Bu görevi taşımaya izniniz yok.',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php
index e27a8a56..c87adbec 100644
--- a/app/Locale/zh_CN/translations.php
+++ b/app/Locale/zh_CN/translations.php
@@ -1278,4 +1278,13 @@ return array(
'Moving a task is not permitted' => '禁止移动任务',
'This value must be in the range %d to %d' => '输入值必须在%d到%d之间',
'You are not allowed to move this task.' => '你不能移动此任务',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
);
diff --git a/app/Model/ProjectPermissionModel.php b/app/Model/ProjectPermissionModel.php
index 25b6a382..dabd406c 100644
--- a/app/Model/ProjectPermissionModel.php
+++ b/app/Model/ProjectPermissionModel.php
@@ -62,17 +62,33 @@ class ProjectPermissionModel extends Base
->withFilter(new ProjectUserRoleProjectFilter($project_id))
->withFilter(new ProjectUserRoleUsernameFilter($input))
->getQuery()
- ->findAllByColumn('username');
+ ->columns(
+ UserModel::TABLE.'.id',
+ UserModel::TABLE.'.username',
+ UserModel::TABLE.'.name',
+ UserModel::TABLE.'.email',
+ UserModel::TABLE.'.avatar_path'
+ )
+ ->findAll();
$groupMembers = $this->projectGroupRoleQuery
->withFilter(new ProjectGroupRoleProjectFilter($project_id))
->withFilter(new ProjectGroupRoleUsernameFilter($input))
->getQuery()
- ->findAllByColumn('username');
+ ->columns(
+ UserModel::TABLE.'.id',
+ UserModel::TABLE.'.username',
+ UserModel::TABLE.'.name',
+ UserModel::TABLE.'.email',
+ UserModel::TABLE.'.avatar_path'
+ )
+ ->findAll();
- $members = array_unique(array_merge($userMembers, $groupMembers));
+ $userMembers = array_column_index_unique($userMembers, 'username');
+ $groupMembers = array_column_index_unique($groupMembers, 'username');
+ $members = array_merge($userMembers, $groupMembers);
- sort($members);
+ ksort($members);
return $members;
}
diff --git a/app/Model/UserMentionModel.php b/app/Model/UserMentionModel.php
deleted file mode 100644
index cdb9949e..00000000
--- a/app/Model/UserMentionModel.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace Kanboard\Model;
-
-use Kanboard\Core\Base;
-use Kanboard\Event\GenericEvent;
-
-/**
- * User Mention
- *
- * @package Kanboard\Model
- * @author Frederic Guillot
- */
-class UserMentionModel extends Base
-{
- /**
- * Get list of mentioned users
- *
- * @access public
- * @param string $content
- * @return array
- */
- public function getMentionedUsers($content)
- {
- $users = array();
-
- if (preg_match_all('/@([^\s]+)/', $content, $matches)) {
- $users = $this->db->table(UserModel::TABLE)
- ->columns('id', 'username', 'name', 'email', 'language')
- ->eq('notifications_enabled', 1)
- ->neq('id', $this->userSession->getId())
- ->in('username', array_unique($matches[1]))
- ->findAll();
- }
-
- return $users;
- }
-
- /**
- * Fire events for user mentions
- *
- * @access public
- * @param string $content
- * @param string $eventName
- * @param GenericEvent $event
- */
- public function fireEvents($content, $eventName, GenericEvent $event)
- {
- if (empty($event['project_id'])) {
- $event['project_id'] = $this->taskFinderModel->getProjectId($event['task_id']);
- }
-
- $users = $this->getMentionedUsers($content);
-
- foreach ($users as $user) {
- if ($this->projectPermissionModel->isMember($event['project_id'], $user['id'])) {
- $event['mention'] = $user;
- $this->dispatcher->dispatch($eventName, $event);
- }
- }
- }
-}
diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php
index b50164ca..fac8688a 100644
--- a/app/Schema/Mysql.php
+++ b/app/Schema/Mysql.php
@@ -6,7 +6,17 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
-const VERSION = 116;
+const VERSION = 118;
+
+function version_118(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE `users` ADD COLUMN `api_access_token` VARCHAR(255) DEFAULT NULL');
+}
+
+function version_117(PDO $pdo)
+{
+ $pdo->exec("ALTER TABLE `settings` MODIFY `value` TEXT");
+}
function version_116(PDO $pdo)
{
diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php
index 83926f19..32a7a744 100644
--- a/app/Schema/Postgres.php
+++ b/app/Schema/Postgres.php
@@ -6,7 +6,17 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
-const VERSION = 95;
+const VERSION = 97;
+
+function version_97(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE "users" ADD COLUMN api_access_token VARCHAR(255) DEFAULT NULL');
+}
+
+function version_96(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE "settings" ALTER COLUMN "value" TYPE TEXT');
+}
function version_95(PDO $pdo)
{
diff --git a/app/Schema/Sql/mysql.sql b/app/Schema/Sql/mysql.sql
index 8d494dcf..0ee88d88 100644
--- a/app/Schema/Sql/mysql.sql
+++ b/app/Schema/Sql/mysql.sql
@@ -35,6 +35,44 @@ CREATE TABLE `actions` (
CONSTRAINT `actions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `column_has_move_restrictions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `column_has_move_restrictions` (
+ `restriction_id` int(11) NOT NULL AUTO_INCREMENT,
+ `project_id` int(11) NOT NULL,
+ `role_id` int(11) NOT NULL,
+ `src_column_id` int(11) NOT NULL,
+ `dst_column_id` int(11) NOT NULL,
+ PRIMARY KEY (`restriction_id`),
+ UNIQUE KEY `role_id` (`role_id`,`src_column_id`,`dst_column_id`),
+ KEY `project_id` (`project_id`),
+ KEY `src_column_id` (`src_column_id`),
+ KEY `dst_column_id` (`dst_column_id`),
+ CONSTRAINT `column_has_move_restrictions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_move_restrictions_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `project_has_roles` (`role_id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_move_restrictions_ibfk_3` FOREIGN KEY (`src_column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_move_restrictions_ibfk_4` FOREIGN KEY (`dst_column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `column_has_restrictions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `column_has_restrictions` (
+ `restriction_id` int(11) NOT NULL AUTO_INCREMENT,
+ `project_id` int(11) NOT NULL,
+ `role_id` int(11) NOT NULL,
+ `column_id` int(11) NOT NULL,
+ `rule` varchar(255) NOT NULL,
+ PRIMARY KEY (`restriction_id`),
+ UNIQUE KEY `role_id` (`role_id`,`column_id`,`rule`),
+ KEY `project_id` (`project_id`),
+ KEY `column_id` (`column_id`),
+ CONSTRAINT `column_has_restrictions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_restrictions_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `project_has_roles` (`role_id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_restrictions_ibfk_3` FOREIGN KEY (`column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `columns`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
@@ -260,7 +298,7 @@ DROP TABLE IF EXISTS `project_has_groups`;
CREATE TABLE `project_has_groups` (
`group_id` int(11) NOT NULL,
`project_id` int(11) NOT NULL,
- `role` varchar(25) NOT NULL,
+ `role` varchar(255) NOT NULL,
UNIQUE KEY `group_id` (`group_id`,`project_id`),
KEY `project_id` (`project_id`),
CONSTRAINT `project_has_groups_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE,
@@ -292,19 +330,46 @@ CREATE TABLE `project_has_notification_types` (
CONSTRAINT `project_has_notification_types_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `project_has_roles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `project_has_roles` (
+ `role_id` int(11) NOT NULL AUTO_INCREMENT,
+ `role` varchar(255) NOT NULL,
+ `project_id` int(11) NOT NULL,
+ PRIMARY KEY (`role_id`),
+ UNIQUE KEY `project_id` (`project_id`,`role`),
+ CONSTRAINT `project_has_roles_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `project_has_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `project_has_users` (
`project_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
- `role` varchar(25) NOT NULL DEFAULT 'project-viewer',
+ `role` varchar(255) NOT NULL,
UNIQUE KEY `idx_project_user` (`project_id`,`user_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `project_has_users_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
CONSTRAINT `project_has_users_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `project_role_has_restrictions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `project_role_has_restrictions` (
+ `restriction_id` int(11) NOT NULL AUTO_INCREMENT,
+ `project_id` int(11) NOT NULL,
+ `role_id` int(11) NOT NULL,
+ `rule` varchar(255) NOT NULL,
+ PRIMARY KEY (`restriction_id`),
+ UNIQUE KEY `role_id` (`role_id`,`rule`),
+ KEY `project_id` (`project_id`),
+ CONSTRAINT `project_role_has_restrictions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `project_role_has_restrictions_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `project_has_roles` (`role_id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `projects`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
@@ -359,7 +424,7 @@ DROP TABLE IF EXISTS `settings`;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `settings` (
`option` varchar(100) NOT NULL,
- `value` varchar(255) DEFAULT '',
+ `value` text,
`changed_by` int(11) NOT NULL DEFAULT '0',
`changed_on` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`option`)
@@ -537,6 +602,8 @@ CREATE TABLE `tasks` (
`recurrence_parent` int(11) DEFAULT NULL,
`recurrence_child` int(11) DEFAULT NULL,
`priority` int(11) DEFAULT '0',
+ `external_provider` varchar(255) DEFAULT NULL,
+ `external_uri` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_task_active` (`is_active`),
KEY `column_id` (`column_id`),
@@ -648,6 +715,7 @@ CREATE TABLE `users` (
`role` varchar(25) NOT NULL DEFAULT 'app-user',
`is_active` tinyint(1) DEFAULT '1',
`avatar_path` varchar(255) DEFAULT NULL,
+ `api_access_token` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_username_idx` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@@ -671,7 +739,7 @@ CREATE TABLE `users` (
LOCK TABLES `settings` WRITE;
/*!40000 ALTER TABLE `settings` DISABLE KEYS */;
-INSERT INTO `settings` VALUES ('api_token','4064ef3d26efa9a0ff78fa7067d8bb9d99323455128edd89e9dc7c53ed76',0,0),('application_currency','USD',0,0),('application_date_format','m/d/Y',0,0),('application_language','en_US',0,0),('application_stylesheet','',0,0),('application_timezone','UTC',0,0),('application_url','',0,0),('board_columns','',0,0),('board_highlight_period','172800',0,0),('board_private_refresh_interval','10',0,0),('board_public_refresh_interval','60',0,0),('calendar_project_tasks','date_started',0,0),('calendar_user_subtasks_time_tracking','0',0,0),('calendar_user_tasks','date_started',0,0),('cfd_include_closed_tasks','1',0,0),('default_color','yellow',0,0),('integration_gravatar','0',0,0),('password_reset','1',0,0),('project_categories','',0,0),('subtask_restriction','0',0,0),('subtask_time_tracking','1',0,0),('webhook_token','c8f53c0bcd8aead902ad04f180ffafd7889b9c0062c2d510e2297ef543b8',0,0),('webhook_url','',0,0);
+INSERT INTO `settings` VALUES ('api_token','f149956cb60c88d01123a28964fc035b1ce4513be454f2a85fe6b4ca3758',0,0),('application_currency','USD',0,0),('application_date_format','m/d/Y',0,0),('application_language','en_US',0,0),('application_stylesheet','',0,0),('application_timezone','UTC',0,0),('application_url','',0,0),('board_columns','',0,0),('board_highlight_period','172800',0,0),('board_private_refresh_interval','10',0,0),('board_public_refresh_interval','60',0,0),('calendar_project_tasks','date_started',0,0),('calendar_user_subtasks_time_tracking','0',0,0),('calendar_user_tasks','date_started',0,0),('cfd_include_closed_tasks','1',0,0),('default_color','yellow',0,0),('integration_gravatar','0',0,0),('password_reset','1',0,0),('project_categories','',0,0),('subtask_restriction','0',0,0),('subtask_time_tracking','1',0,0),('webhook_token','47d1d896b6612234c7543eb3f3a09a0a669f77a079d13ad3d810ccb79896',0,0),('webhook_url','',0,0);
/*!40000 ALTER TABLE `settings` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -700,4 +768,4 @@ UNLOCK TABLES;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$yUJ9QnhG.f47yO.YvWKo3eMAHULukpluDNTOF9.Z7QQg0vOfFRB6u', 'app-admin');INSERT INTO schema_version VALUES ('112');
+INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$R1zYk04d96KcHRpd9.r5I.5I6mgKIgUdsaISZYmaDLPIJCUO0FFJG', 'app-admin');INSERT INTO schema_version VALUES ('118');
diff --git a/app/Schema/Sql/postgres.sql b/app/Schema/Sql/postgres.sql
index 0add9c91..578b0c75 100644
--- a/app/Schema/Sql/postgres.sql
+++ b/app/Schema/Sql/postgres.sql
@@ -2,8 +2,8 @@
-- PostgreSQL database dump
--
--- Dumped from database version 9.5.2
--- Dumped by pg_dump version 9.5.2
+-- Dumped from database version 9.6.1
+-- Dumped by pg_dump version 9.6.1
SET statement_timeout = 0;
SET lock_timeout = 0;
@@ -89,6 +89,70 @@ ALTER SEQUENCE "actions_id_seq" OWNED BY "actions"."id";
--
+-- Name: column_has_move_restrictions; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "column_has_move_restrictions" (
+ "restriction_id" integer NOT NULL,
+ "project_id" integer NOT NULL,
+ "role_id" integer NOT NULL,
+ "src_column_id" integer NOT NULL,
+ "dst_column_id" integer NOT NULL
+);
+
+
+--
+-- Name: column_has_move_restrictions_restriction_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "column_has_move_restrictions_restriction_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: column_has_move_restrictions_restriction_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "column_has_move_restrictions_restriction_id_seq" OWNED BY "column_has_move_restrictions"."restriction_id";
+
+
+--
+-- Name: column_has_restrictions; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "column_has_restrictions" (
+ "restriction_id" integer NOT NULL,
+ "project_id" integer NOT NULL,
+ "role_id" integer NOT NULL,
+ "column_id" integer NOT NULL,
+ "rule" character varying(255) NOT NULL
+);
+
+
+--
+-- Name: column_has_restrictions_restriction_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "column_has_restrictions_restriction_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: column_has_restrictions_restriction_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "column_has_restrictions_restriction_id_seq" OWNED BY "column_has_restrictions"."restriction_id";
+
+
+--
-- Name: columns; Type: TABLE; Schema: public; Owner: -
--
@@ -499,7 +563,7 @@ ALTER SEQUENCE "project_has_files_id_seq" OWNED BY "project_has_files"."id";
CREATE TABLE "project_has_groups" (
"group_id" integer NOT NULL,
"project_id" integer NOT NULL,
- "role" character varying(25) NOT NULL
+ "role" character varying(255) NOT NULL
);
@@ -547,17 +611,78 @@ ALTER SEQUENCE "project_has_notification_types_id_seq" OWNED BY "project_has_not
--
+-- Name: project_has_roles; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "project_has_roles" (
+ "role_id" integer NOT NULL,
+ "role" character varying(255) NOT NULL,
+ "project_id" integer NOT NULL
+);
+
+
+--
+-- Name: project_has_roles_role_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "project_has_roles_role_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: project_has_roles_role_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "project_has_roles_role_id_seq" OWNED BY "project_has_roles"."role_id";
+
+
+--
-- Name: project_has_users; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE "project_has_users" (
"project_id" integer NOT NULL,
"user_id" integer NOT NULL,
- "role" character varying(25) DEFAULT 'project-viewer'::character varying NOT NULL
+ "role" character varying(255) DEFAULT 'project-viewer'::character varying NOT NULL
+);
+
+
+--
+-- Name: project_role_has_restrictions; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "project_role_has_restrictions" (
+ "restriction_id" integer NOT NULL,
+ "project_id" integer NOT NULL,
+ "role_id" integer NOT NULL,
+ "rule" character varying(255) NOT NULL
);
--
+-- Name: project_role_has_restrictions_restriction_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "project_role_has_restrictions_restriction_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: project_role_has_restrictions_restriction_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "project_role_has_restrictions_restriction_id_seq" OWNED BY "project_role_has_restrictions"."restriction_id";
+
+
+--
-- Name: projects; Type: TABLE; Schema: public; Owner: -
--
@@ -652,7 +777,7 @@ CREATE TABLE "schema_version" (
CREATE TABLE "settings" (
"option" character varying(100) NOT NULL,
- "value" character varying(255) DEFAULT ''::character varying,
+ "value" "text" DEFAULT ''::character varying,
"changed_by" integer DEFAULT 0 NOT NULL,
"changed_on" integer DEFAULT 0 NOT NULL
);
@@ -948,7 +1073,9 @@ CREATE TABLE "tasks" (
"recurrence_basedate" integer DEFAULT 0 NOT NULL,
"recurrence_parent" integer,
"recurrence_child" integer,
- "priority" integer DEFAULT 0
+ "priority" integer DEFAULT 0,
+ "external_provider" character varying(255),
+ "external_uri" character varying(255)
);
@@ -1117,7 +1244,8 @@ CREATE TABLE "users" (
"gitlab_id" integer,
"role" character varying(25) DEFAULT 'app-user'::character varying NOT NULL,
"is_active" boolean DEFAULT true,
- "avatar_path" character varying(255)
+ "avatar_path" character varying(255),
+ "api_access_token" character varying(255) DEFAULT NULL::character varying
);
@@ -1141,203 +1269,231 @@ ALTER SEQUENCE "users_id_seq" OWNED BY "users"."id";
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: action_has_params id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "action_has_params" ALTER COLUMN "id" SET DEFAULT "nextval"('"action_has_params_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: actions id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "actions" ALTER COLUMN "id" SET DEFAULT "nextval"('"actions_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: column_has_move_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"column_has_move_restrictions_restriction_id_seq"'::"regclass");
+
+
+--
+-- Name: column_has_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"column_has_restrictions_restriction_id_seq"'::"regclass");
+
+
+--
+-- Name: columns id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "columns" ALTER COLUMN "id" SET DEFAULT "nextval"('"columns_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: comments id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "comments" ALTER COLUMN "id" SET DEFAULT "nextval"('"comments_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: custom_filters id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "custom_filters" ALTER COLUMN "id" SET DEFAULT "nextval"('"custom_filters_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: groups id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "groups" ALTER COLUMN "id" SET DEFAULT "nextval"('"groups_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: last_logins id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "last_logins" ALTER COLUMN "id" SET DEFAULT "nextval"('"last_logins_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: links id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "links" ALTER COLUMN "id" SET DEFAULT "nextval"('"links_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_activities id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_activities_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_daily_column_stats id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_column_stats" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_daily_summaries_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_daily_stats id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_stats" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_daily_stats_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_has_categories id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_categories" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_has_categories_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_has_files id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_files" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_has_files_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_has_notification_types id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_notification_types" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_has_notification_types_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_has_roles role_id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_has_roles" ALTER COLUMN "role_id" SET DEFAULT "nextval"('"project_has_roles_role_id_seq"'::"regclass");
+
+
+--
+-- Name: project_role_has_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"project_role_has_restrictions_restriction_id_seq"'::"regclass");
+
+
+--
+-- Name: projects id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "projects" ALTER COLUMN "id" SET DEFAULT "nextval"('"projects_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: remember_me id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "remember_me" ALTER COLUMN "id" SET DEFAULT "nextval"('"remember_me_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: subtask_time_tracking id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtask_time_tracking" ALTER COLUMN "id" SET DEFAULT "nextval"('"subtask_time_tracking_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: subtasks id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtasks" ALTER COLUMN "id" SET DEFAULT "nextval"('"task_has_subtasks_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: swimlanes id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "swimlanes" ALTER COLUMN "id" SET DEFAULT "nextval"('"swimlanes_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: tags id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tags" ALTER COLUMN "id" SET DEFAULT "nextval"('"tags_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: task_has_external_links id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_external_links" ALTER COLUMN "id" SET DEFAULT "nextval"('"task_has_external_links_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: task_has_files id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_files" ALTER COLUMN "id" SET DEFAULT "nextval"('"task_has_files_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: task_has_links id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links" ALTER COLUMN "id" SET DEFAULT "nextval"('"task_has_links_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: tasks id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tasks" ALTER COLUMN "id" SET DEFAULT "nextval"('"tasks_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: transitions id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions" ALTER COLUMN "id" SET DEFAULT "nextval"('"transitions_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: user_has_notification_types id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notification_types" ALTER COLUMN "id" SET DEFAULT "nextval"('"user_has_notification_types_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: user_has_unread_notifications id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_unread_notifications" ALTER COLUMN "id" SET DEFAULT "nextval"('"user_has_unread_notifications_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: users id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "users" ALTER COLUMN "id" SET DEFAULT "nextval"('"users_id_seq"'::"regclass");
--
--- Name: action_has_params_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: action_has_params action_has_params_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "action_has_params"
@@ -1345,7 +1501,7 @@ ALTER TABLE ONLY "action_has_params"
--
--- Name: actions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: actions actions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "actions"
@@ -1353,7 +1509,39 @@ ALTER TABLE ONLY "actions"
--
--- Name: columns_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: column_has_move_restrictions column_has_move_restrictions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_pkey" PRIMARY KEY ("restriction_id");
+
+
+--
+-- Name: column_has_move_restrictions column_has_move_restrictions_role_id_src_column_id_dst_colu_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_role_id_src_column_id_dst_colu_key" UNIQUE ("role_id", "src_column_id", "dst_column_id");
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_pkey" PRIMARY KEY ("restriction_id");
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_role_id_column_id_rule_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_role_id_column_id_rule_key" UNIQUE ("role_id", "column_id", "rule");
+
+
+--
+-- Name: columns columns_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "columns"
@@ -1361,7 +1549,7 @@ ALTER TABLE ONLY "columns"
--
--- Name: columns_title_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: columns columns_title_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "columns"
@@ -1369,7 +1557,7 @@ ALTER TABLE ONLY "columns"
--
--- Name: comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: comments comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "comments"
@@ -1377,7 +1565,7 @@ ALTER TABLE ONLY "comments"
--
--- Name: currencies_currency_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: currencies currencies_currency_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "currencies"
@@ -1385,7 +1573,7 @@ ALTER TABLE ONLY "currencies"
--
--- Name: custom_filters_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: custom_filters custom_filters_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "custom_filters"
@@ -1393,7 +1581,7 @@ ALTER TABLE ONLY "custom_filters"
--
--- Name: group_has_users_group_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: group_has_users group_has_users_group_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "group_has_users"
@@ -1401,7 +1589,7 @@ ALTER TABLE ONLY "group_has_users"
--
--- Name: groups_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: groups groups_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "groups"
@@ -1409,7 +1597,7 @@ ALTER TABLE ONLY "groups"
--
--- Name: groups_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: groups groups_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "groups"
@@ -1417,7 +1605,7 @@ ALTER TABLE ONLY "groups"
--
--- Name: last_logins_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: last_logins last_logins_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "last_logins"
@@ -1425,7 +1613,7 @@ ALTER TABLE ONLY "last_logins"
--
--- Name: links_label_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: links links_label_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "links"
@@ -1433,7 +1621,7 @@ ALTER TABLE ONLY "links"
--
--- Name: links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: links links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "links"
@@ -1441,7 +1629,7 @@ ALTER TABLE ONLY "links"
--
--- Name: password_reset_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: password_reset password_reset_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "password_reset"
@@ -1449,7 +1637,7 @@ ALTER TABLE ONLY "password_reset"
--
--- Name: plugin_schema_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: plugin_schema_versions plugin_schema_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "plugin_schema_versions"
@@ -1457,7 +1645,7 @@ ALTER TABLE ONLY "plugin_schema_versions"
--
--- Name: project_activities_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_activities project_activities_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities"
@@ -1465,7 +1653,7 @@ ALTER TABLE ONLY "project_activities"
--
--- Name: project_daily_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_stats project_daily_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_stats"
@@ -1473,7 +1661,7 @@ ALTER TABLE ONLY "project_daily_stats"
--
--- Name: project_daily_summaries_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_column_stats project_daily_summaries_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_column_stats"
@@ -1481,7 +1669,7 @@ ALTER TABLE ONLY "project_daily_column_stats"
--
--- Name: project_has_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_categories project_has_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_categories"
@@ -1489,7 +1677,7 @@ ALTER TABLE ONLY "project_has_categories"
--
--- Name: project_has_categories_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_categories project_has_categories_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_categories"
@@ -1497,7 +1685,7 @@ ALTER TABLE ONLY "project_has_categories"
--
--- Name: project_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_files project_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_files"
@@ -1505,7 +1693,7 @@ ALTER TABLE ONLY "project_has_files"
--
--- Name: project_has_groups_group_id_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_groups project_has_groups_group_id_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_groups"
@@ -1513,7 +1701,7 @@ ALTER TABLE ONLY "project_has_groups"
--
--- Name: project_has_metadata_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_metadata project_has_metadata_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_metadata"
@@ -1521,7 +1709,7 @@ ALTER TABLE ONLY "project_has_metadata"
--
--- Name: project_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_notification_types project_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_notification_types"
@@ -1529,7 +1717,7 @@ ALTER TABLE ONLY "project_has_notification_types"
--
--- Name: project_has_notification_types_project_id_notification_type_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_notification_types project_has_notification_types_project_id_notification_type_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_notification_types"
@@ -1537,7 +1725,23 @@ ALTER TABLE ONLY "project_has_notification_types"
--
--- Name: project_has_users_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_roles project_has_roles_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_has_roles"
+ ADD CONSTRAINT "project_has_roles_pkey" PRIMARY KEY ("role_id");
+
+
+--
+-- Name: project_has_roles project_has_roles_project_id_role_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_has_roles"
+ ADD CONSTRAINT "project_has_roles_project_id_role_key" UNIQUE ("project_id", "role");
+
+
+--
+-- Name: project_has_users project_has_users_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_users"
@@ -1545,7 +1749,23 @@ ALTER TABLE ONLY "project_has_users"
--
--- Name: projects_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_role_has_restrictions project_role_has_restrictions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions"
+ ADD CONSTRAINT "project_role_has_restrictions_pkey" PRIMARY KEY ("restriction_id");
+
+
+--
+-- Name: project_role_has_restrictions project_role_has_restrictions_role_id_rule_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions"
+ ADD CONSTRAINT "project_role_has_restrictions_role_id_rule_key" UNIQUE ("role_id", "rule");
+
+
+--
+-- Name: projects projects_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "projects"
@@ -1553,7 +1773,7 @@ ALTER TABLE ONLY "projects"
--
--- Name: remember_me_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: remember_me remember_me_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "remember_me"
@@ -1561,7 +1781,7 @@ ALTER TABLE ONLY "remember_me"
--
--- Name: settings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: settings settings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "settings"
@@ -1569,7 +1789,7 @@ ALTER TABLE ONLY "settings"
--
--- Name: subtask_time_tracking_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: subtask_time_tracking subtask_time_tracking_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtask_time_tracking"
@@ -1577,7 +1797,7 @@ ALTER TABLE ONLY "subtask_time_tracking"
--
--- Name: swimlanes_name_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: swimlanes swimlanes_name_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "swimlanes"
@@ -1585,7 +1805,7 @@ ALTER TABLE ONLY "swimlanes"
--
--- Name: swimlanes_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: swimlanes swimlanes_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "swimlanes"
@@ -1593,7 +1813,7 @@ ALTER TABLE ONLY "swimlanes"
--
--- Name: tags_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: tags tags_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tags"
@@ -1601,7 +1821,7 @@ ALTER TABLE ONLY "tags"
--
--- Name: tags_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: tags tags_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tags"
@@ -1609,7 +1829,7 @@ ALTER TABLE ONLY "tags"
--
--- Name: task_has_external_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_external_links task_has_external_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_external_links"
@@ -1617,7 +1837,7 @@ ALTER TABLE ONLY "task_has_external_links"
--
--- Name: task_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_files task_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_files"
@@ -1625,7 +1845,7 @@ ALTER TABLE ONLY "task_has_files"
--
--- Name: task_has_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_links task_has_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links"
@@ -1633,7 +1853,7 @@ ALTER TABLE ONLY "task_has_links"
--
--- Name: task_has_metadata_task_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_metadata task_has_metadata_task_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_metadata"
@@ -1641,7 +1861,7 @@ ALTER TABLE ONLY "task_has_metadata"
--
--- Name: task_has_subtasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: subtasks task_has_subtasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtasks"
@@ -1649,7 +1869,7 @@ ALTER TABLE ONLY "subtasks"
--
--- Name: task_has_tags_tag_id_task_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_tags task_has_tags_tag_id_task_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_tags"
@@ -1657,7 +1877,7 @@ ALTER TABLE ONLY "task_has_tags"
--
--- Name: tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: tasks tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tasks"
@@ -1665,7 +1885,7 @@ ALTER TABLE ONLY "tasks"
--
--- Name: transitions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -1673,7 +1893,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: user_has_metadata_user_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_metadata user_has_metadata_user_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_metadata"
@@ -1681,7 +1901,7 @@ ALTER TABLE ONLY "user_has_metadata"
--
--- Name: user_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notification_types user_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notification_types"
@@ -1689,7 +1909,7 @@ ALTER TABLE ONLY "user_has_notification_types"
--
--- Name: user_has_notifications_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notifications user_has_notifications_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notifications"
@@ -1697,7 +1917,7 @@ ALTER TABLE ONLY "user_has_notifications"
--
--- Name: user_has_unread_notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_unread_notifications user_has_unread_notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_unread_notifications"
@@ -1705,7 +1925,7 @@ ALTER TABLE ONLY "user_has_unread_notifications"
--
--- Name: users_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "users"
@@ -1839,7 +2059,7 @@ CREATE UNIQUE INDEX "users_username_idx" ON "users" USING "btree" ("username");
--
--- Name: action_has_params_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: action_has_params action_has_params_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "action_has_params"
@@ -1847,7 +2067,7 @@ ALTER TABLE ONLY "action_has_params"
--
--- Name: actions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: actions actions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "actions"
@@ -1855,7 +2075,63 @@ ALTER TABLE ONLY "actions"
--
--- Name: columns_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: column_has_move_restrictions column_has_move_restrictions_dst_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_dst_column_id_fkey" FOREIGN KEY ("dst_column_id") REFERENCES "columns"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_move_restrictions column_has_move_restrictions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_move_restrictions column_has_move_restrictions_role_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "project_has_roles"("role_id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_move_restrictions column_has_move_restrictions_src_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_src_column_id_fkey" FOREIGN KEY ("src_column_id") REFERENCES "columns"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_column_id_fkey" FOREIGN KEY ("column_id") REFERENCES "columns"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_role_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "project_has_roles"("role_id") ON DELETE CASCADE;
+
+
+--
+-- Name: columns columns_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "columns"
@@ -1863,7 +2139,7 @@ ALTER TABLE ONLY "columns"
--
--- Name: comments_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: comments comments_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "comments"
@@ -1871,7 +2147,7 @@ ALTER TABLE ONLY "comments"
--
--- Name: group_has_users_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: group_has_users group_has_users_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "group_has_users"
@@ -1879,7 +2155,7 @@ ALTER TABLE ONLY "group_has_users"
--
--- Name: group_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: group_has_users group_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "group_has_users"
@@ -1887,7 +2163,7 @@ ALTER TABLE ONLY "group_has_users"
--
--- Name: last_logins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: last_logins last_logins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "last_logins"
@@ -1895,7 +2171,7 @@ ALTER TABLE ONLY "last_logins"
--
--- Name: password_reset_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: password_reset password_reset_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "password_reset"
@@ -1903,7 +2179,7 @@ ALTER TABLE ONLY "password_reset"
--
--- Name: project_activities_creator_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_activities project_activities_creator_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities"
@@ -1911,7 +2187,7 @@ ALTER TABLE ONLY "project_activities"
--
--- Name: project_activities_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_activities project_activities_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities"
@@ -1919,7 +2195,7 @@ ALTER TABLE ONLY "project_activities"
--
--- Name: project_activities_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_activities project_activities_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities"
@@ -1927,7 +2203,7 @@ ALTER TABLE ONLY "project_activities"
--
--- Name: project_daily_stats_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_stats project_daily_stats_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_stats"
@@ -1935,7 +2211,7 @@ ALTER TABLE ONLY "project_daily_stats"
--
--- Name: project_daily_summaries_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_column_stats project_daily_summaries_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_column_stats"
@@ -1943,7 +2219,7 @@ ALTER TABLE ONLY "project_daily_column_stats"
--
--- Name: project_daily_summaries_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_column_stats project_daily_summaries_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_column_stats"
@@ -1951,7 +2227,7 @@ ALTER TABLE ONLY "project_daily_column_stats"
--
--- Name: project_has_categories_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_categories project_has_categories_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_categories"
@@ -1959,7 +2235,7 @@ ALTER TABLE ONLY "project_has_categories"
--
--- Name: project_has_files_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_files project_has_files_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_files"
@@ -1967,7 +2243,7 @@ ALTER TABLE ONLY "project_has_files"
--
--- Name: project_has_groups_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_groups project_has_groups_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_groups"
@@ -1975,7 +2251,7 @@ ALTER TABLE ONLY "project_has_groups"
--
--- Name: project_has_groups_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_groups project_has_groups_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_groups"
@@ -1983,7 +2259,7 @@ ALTER TABLE ONLY "project_has_groups"
--
--- Name: project_has_metadata_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_metadata project_has_metadata_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_metadata"
@@ -1991,7 +2267,7 @@ ALTER TABLE ONLY "project_has_metadata"
--
--- Name: project_has_notification_types_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_notification_types project_has_notification_types_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_notification_types"
@@ -1999,7 +2275,15 @@ ALTER TABLE ONLY "project_has_notification_types"
--
--- Name: project_has_users_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_roles project_has_roles_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_has_roles"
+ ADD CONSTRAINT "project_has_roles_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: project_has_users project_has_users_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_users"
@@ -2007,7 +2291,7 @@ ALTER TABLE ONLY "project_has_users"
--
--- Name: project_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_users project_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_users"
@@ -2015,7 +2299,23 @@ ALTER TABLE ONLY "project_has_users"
--
--- Name: remember_me_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_role_has_restrictions project_role_has_restrictions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions"
+ ADD CONSTRAINT "project_role_has_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: project_role_has_restrictions project_role_has_restrictions_role_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions"
+ ADD CONSTRAINT "project_role_has_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "project_has_roles"("role_id") ON DELETE CASCADE;
+
+
+--
+-- Name: remember_me remember_me_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "remember_me"
@@ -2023,7 +2323,7 @@ ALTER TABLE ONLY "remember_me"
--
--- Name: subtask_time_tracking_subtask_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: subtask_time_tracking subtask_time_tracking_subtask_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtask_time_tracking"
@@ -2031,7 +2331,7 @@ ALTER TABLE ONLY "subtask_time_tracking"
--
--- Name: subtask_time_tracking_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: subtask_time_tracking subtask_time_tracking_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtask_time_tracking"
@@ -2039,7 +2339,7 @@ ALTER TABLE ONLY "subtask_time_tracking"
--
--- Name: swimlanes_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: swimlanes swimlanes_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "swimlanes"
@@ -2047,7 +2347,7 @@ ALTER TABLE ONLY "swimlanes"
--
--- Name: task_has_external_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_external_links task_has_external_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_external_links"
@@ -2055,7 +2355,7 @@ ALTER TABLE ONLY "task_has_external_links"
--
--- Name: task_has_files_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_files task_has_files_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_files"
@@ -2063,7 +2363,7 @@ ALTER TABLE ONLY "task_has_files"
--
--- Name: task_has_links_link_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_links task_has_links_link_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links"
@@ -2071,7 +2371,7 @@ ALTER TABLE ONLY "task_has_links"
--
--- Name: task_has_links_opposite_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_links task_has_links_opposite_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links"
@@ -2079,7 +2379,7 @@ ALTER TABLE ONLY "task_has_links"
--
--- Name: task_has_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_links task_has_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links"
@@ -2087,7 +2387,7 @@ ALTER TABLE ONLY "task_has_links"
--
--- Name: task_has_metadata_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_metadata task_has_metadata_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_metadata"
@@ -2095,7 +2395,7 @@ ALTER TABLE ONLY "task_has_metadata"
--
--- Name: task_has_subtasks_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: subtasks task_has_subtasks_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtasks"
@@ -2103,7 +2403,7 @@ ALTER TABLE ONLY "subtasks"
--
--- Name: task_has_tags_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_tags task_has_tags_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_tags"
@@ -2111,7 +2411,7 @@ ALTER TABLE ONLY "task_has_tags"
--
--- Name: task_has_tags_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_tags task_has_tags_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_tags"
@@ -2119,7 +2419,7 @@ ALTER TABLE ONLY "task_has_tags"
--
--- Name: tasks_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: tasks tasks_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tasks"
@@ -2127,7 +2427,7 @@ ALTER TABLE ONLY "tasks"
--
--- Name: tasks_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: tasks tasks_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tasks"
@@ -2135,7 +2435,7 @@ ALTER TABLE ONLY "tasks"
--
--- Name: transitions_dst_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_dst_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2143,7 +2443,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: transitions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2151,7 +2451,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: transitions_src_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_src_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2159,7 +2459,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: transitions_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2167,7 +2467,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: transitions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2175,7 +2475,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: user_has_metadata_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_metadata user_has_metadata_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_metadata"
@@ -2183,7 +2483,7 @@ ALTER TABLE ONLY "user_has_metadata"
--
--- Name: user_has_notification_types_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notification_types user_has_notification_types_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notification_types"
@@ -2191,7 +2491,7 @@ ALTER TABLE ONLY "user_has_notification_types"
--
--- Name: user_has_notifications_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notifications user_has_notifications_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notifications"
@@ -2199,7 +2499,7 @@ ALTER TABLE ONLY "user_has_notifications"
--
--- Name: user_has_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notifications user_has_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notifications"
@@ -2207,7 +2507,7 @@ ALTER TABLE ONLY "user_has_notifications"
--
--- Name: user_has_unread_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_unread_notifications user_has_unread_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_unread_notifications"
@@ -2222,8 +2522,8 @@ ALTER TABLE ONLY "user_has_unread_notifications"
-- PostgreSQL database dump
--
--- Dumped from database version 9.5.2
--- Dumped by pg_dump version 9.5.2
+-- Dumped from database version 9.6.1
+-- Dumped by pg_dump version 9.6.1
SET statement_timeout = 0;
SET lock_timeout = 0;
@@ -2243,8 +2543,8 @@ INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_high
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_public_refresh_interval', '60', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_private_refresh_interval', '10', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_columns', '', 0, 0);
-INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('webhook_token', 'c9a7c2a4523f1724b2ca047c5685f8e2b26bba47eb69baf4f22d5d50d837', 0, 0);
-INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token', 'c57a6cb1789269547b616454e4e2f06d3de0514f83baf8fa5b5a8af44a08', 0, 0);
+INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('webhook_token', 'b64911d9b61ea71adada348105281e16470e268fce7cb9bf1895958d4bbc', 0, 0);
+INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token', 'f63bd25c2e952d78b70f178fd96b4207ef29315ca73d308af37c02d8d51f', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_language', 'en_US', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_timezone', 'UTC', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_url', '', 0, 0);
@@ -2272,8 +2572,8 @@ INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('password_r
-- PostgreSQL database dump
--
--- Dumped from database version 9.5.2
--- Dumped by pg_dump version 9.5.2
+-- Dumped from database version 9.6.1
+-- Dumped by pg_dump version 9.6.1
SET statement_timeout = 0;
SET lock_timeout = 0;
@@ -2313,4 +2613,4 @@ SELECT pg_catalog.setval('links_id_seq', 11, true);
-- PostgreSQL database dump complete
--
-INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$yUJ9QnhG.f47yO.YvWKo3eMAHULukpluDNTOF9.Z7QQg0vOfFRB6u', 'app-admin');INSERT INTO schema_version VALUES ('91');
+INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$0.8BJyhOEBHGqfwD3nIJHuxObqTlZGJ/KRNDVHfSu7RGd42rEbSa.', 'app-admin');INSERT INTO schema_version VALUES ('97');
diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php
index edf6ce63..11dcd537 100644
--- a/app/Schema/Sqlite.php
+++ b/app/Schema/Sqlite.php
@@ -6,7 +6,12 @@ use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
use PDO;
-const VERSION = 107;
+const VERSION = 108;
+
+function version_108(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE users ADD COLUMN api_access_token VARCHAR(255) DEFAULT NULL');
+}
function version_107(PDO $pdo)
{
diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php
index adff1e63..c2dad0e6 100644
--- a/app/ServiceProvider/AuthenticationProvider.php
+++ b/app/ServiceProvider/AuthenticationProvider.php
@@ -2,6 +2,7 @@
namespace Kanboard\ServiceProvider;
+use Kanboard\Auth\ApiAccessTokenAuth;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Kanboard\Core\Security\AuthenticationManager;
@@ -44,6 +45,8 @@ class AuthenticationProvider implements ServiceProviderInterface
$container['authenticationManager']->register(new LdapAuth($container));
}
+ $container['authenticationManager']->register(new ApiAccessTokenAuth($container));
+
$container['projectAccessMap'] = $this->getProjectAccessMap();
$container['applicationAccessMap'] = $this->getApplicationAccessMap();
$container['apiAccessMap'] = $this->getApiAccessMap();
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index c5bf0678..8d471b79 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -91,7 +91,6 @@ class ClassProvider implements ServiceProviderInterface
'TransitionModel',
'UserModel',
'UserLockingModel',
- 'UserMentionModel',
'UserNotificationModel',
'UserNotificationFilterModel',
'UserUnreadNotificationModel',
diff --git a/app/ServiceProvider/CommandProvider.php b/app/ServiceProvider/CommandProvider.php
index 55c2712b..c9abb294 100644
--- a/app/ServiceProvider/CommandProvider.php
+++ b/app/ServiceProvider/CommandProvider.php
@@ -3,6 +3,8 @@
namespace Kanboard\ServiceProvider;
use Kanboard\Console\CronjobCommand;
+use Kanboard\Console\DatabaseMigrationCommand;
+use Kanboard\Console\DatabaseVersionCommand;
use Kanboard\Console\LocaleComparatorCommand;
use Kanboard\Console\LocaleSyncCommand;
use Kanboard\Console\PluginInstallCommand;
@@ -55,6 +57,8 @@ class CommandProvider implements ServiceProviderInterface
$application->add(new PluginUpgradeCommand($container));
$application->add(new PluginInstallCommand($container));
$application->add(new PluginUninstallCommand($container));
+ $application->add(new DatabaseMigrationCommand($container));
+ $application->add(new DatabaseVersionCommand($container));
$container['cli'] = $application;
return $container;
diff --git a/app/ServiceProvider/DatabaseProvider.php b/app/ServiceProvider/DatabaseProvider.php
index a3f57457..9998ac43 100644
--- a/app/ServiceProvider/DatabaseProvider.php
+++ b/app/ServiceProvider/DatabaseProvider.php
@@ -27,6 +27,10 @@ class DatabaseProvider implements ServiceProviderInterface
{
$container['db'] = $this->getInstance();
+ if (DB_RUN_MIGRATIONS) {
+ self::runMigrations($container['db']);
+ }
+
if (DEBUG) {
$container['db']->getStatementHandler()
->withLogging()
@@ -38,7 +42,7 @@ class DatabaseProvider implements ServiceProviderInterface
}
/**
- * Setup the database driver and execute schema migration
+ * Setup the database driver
*
* @access public
* @return \PicoDb\Database
@@ -59,12 +63,39 @@ class DatabaseProvider implements ServiceProviderInterface
throw new LogicException('Database driver not supported');
}
- if ($db->schema()->check(\Schema\VERSION)) {
- return $db;
- } else {
+ return $db;
+ }
+
+ /**
+ * Get current database version
+ *
+ * @static
+ * @access public
+ * @param Database $db
+ * @return int
+ */
+ public static function getSchemaVersion(Database $db)
+ {
+ return $db->getDriver()->getSchemaVersion();
+ }
+
+ /**
+ * Execute database migrations
+ *
+ * @static
+ * @access public
+ * @throws RuntimeException
+ * @param Database $db
+ * @return bool
+ */
+ public static function runMigrations(Database $db)
+ {
+ if (! $db->schema()->check(\Schema\VERSION)) {
$messages = $db->getLogMessages();
throw new RuntimeException('Unable to run SQL migrations: '.implode(', ', $messages).' (You may have to fix it manually)');
}
+
+ return true;
}
/**
@@ -79,7 +110,7 @@ class DatabaseProvider implements ServiceProviderInterface
return new Database(array(
'driver' => 'sqlite',
- 'filename' => DB_FILENAME
+ 'filename' => DB_FILENAME,
));
}
diff --git a/app/ServiceProvider/JobProvider.php b/app/ServiceProvider/JobProvider.php
index 2194b11c..4e5e0f1a 100644
--- a/app/ServiceProvider/JobProvider.php
+++ b/app/ServiceProvider/JobProvider.php
@@ -10,6 +10,7 @@ use Kanboard\Job\SubtaskEventJob;
use Kanboard\Job\TaskEventJob;
use Kanboard\Job\TaskFileEventJob;
use Kanboard\Job\TaskLinkEventJob;
+use Kanboard\Job\UserMentionJob;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
@@ -62,6 +63,10 @@ class JobProvider implements ServiceProviderInterface
return new ProjectMetricJob($c);
});
+ $container['userMentionJob'] = $container->factory(function ($c) {
+ return new UserMentionJob($c);
+ });
+
return $container;
}
}
diff --git a/app/ServiceProvider/RouteProvider.php b/app/ServiceProvider/RouteProvider.php
index 0d1a7931..52687647 100644
--- a/app/ServiceProvider/RouteProvider.php
+++ b/app/ServiceProvider/RouteProvider.php
@@ -158,6 +158,7 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('user/:user_id/authentication', 'UserCredentialController', 'changeAuthentication');
$container['route']->addRoute('user/:user_id/2fa', 'TwoFactorController', 'index');
$container['route']->addRoute('user/:user_id/avatar', 'AvatarFileController', 'show');
+ $container['route']->addRoute('user/:user_id/api', 'UserApiAccessController', 'show');
// Groups
$container['route']->addRoute('groups', 'GroupListController', 'index');
diff --git a/app/Subscriber/NotificationSubscriber.php b/app/Subscriber/NotificationSubscriber.php
index 7cc68b26..ad16685b 100644
--- a/app/Subscriber/NotificationSubscriber.php
+++ b/app/Subscriber/NotificationSubscriber.php
@@ -15,25 +15,25 @@ class NotificationSubscriber extends BaseSubscriber implements EventSubscriberIn
public static function getSubscribedEvents()
{
return array(
- TaskModel::EVENT_USER_MENTION => 'handleEvent',
- TaskModel::EVENT_CREATE => 'handleEvent',
- TaskModel::EVENT_UPDATE => 'handleEvent',
- TaskModel::EVENT_CLOSE => 'handleEvent',
- TaskModel::EVENT_OPEN => 'handleEvent',
- TaskModel::EVENT_MOVE_COLUMN => 'handleEvent',
- TaskModel::EVENT_MOVE_POSITION => 'handleEvent',
- TaskModel::EVENT_MOVE_SWIMLANE => 'handleEvent',
- TaskModel::EVENT_ASSIGNEE_CHANGE => 'handleEvent',
- SubtaskModel::EVENT_CREATE => 'handleEvent',
- SubtaskModel::EVENT_UPDATE => 'handleEvent',
- SubtaskModel::EVENT_DELETE => 'handleEvent',
- CommentModel::EVENT_CREATE => 'handleEvent',
- CommentModel::EVENT_UPDATE => 'handleEvent',
- CommentModel::EVENT_DELETE => 'handleEvent',
- CommentModel::EVENT_USER_MENTION => 'handleEvent',
- TaskFileModel::EVENT_CREATE => 'handleEvent',
- TaskLinkModel::EVENT_CREATE_UPDATE => 'handleEvent',
- TaskLinkModel::EVENT_DELETE => 'handleEvent',
+ TaskModel::EVENT_USER_MENTION => 'handleEvent',
+ TaskModel::EVENT_CREATE => 'handleEvent',
+ TaskModel::EVENT_UPDATE => 'handleEvent',
+ TaskModel::EVENT_CLOSE => 'handleEvent',
+ TaskModel::EVENT_OPEN => 'handleEvent',
+ TaskModel::EVENT_MOVE_COLUMN => 'handleEvent',
+ TaskModel::EVENT_MOVE_POSITION => 'handleEvent',
+ TaskModel::EVENT_MOVE_SWIMLANE => 'handleEvent',
+ TaskModel::EVENT_ASSIGNEE_CHANGE => 'handleEvent',
+ SubtaskModel::EVENT_CREATE => 'handleEvent',
+ SubtaskModel::EVENT_UPDATE => 'handleEvent',
+ SubtaskModel::EVENT_DELETE => 'handleEvent',
+ CommentModel::EVENT_CREATE => 'handleEvent',
+ CommentModel::EVENT_UPDATE => 'handleEvent',
+ CommentModel::EVENT_DELETE => 'handleEvent',
+ CommentModel::EVENT_USER_MENTION => 'handleEvent',
+ TaskFileModel::EVENT_CREATE => 'handleEvent',
+ TaskLinkModel::EVENT_CREATE_UPDATE => 'handleEvent',
+ TaskLinkModel::EVENT_DELETE => 'handleEvent',
);
}
diff --git a/app/Template/category/edit.php b/app/Template/category/edit.php
index 72fd40de..d8ca313d 100644
--- a/app/Template/category/edit.php
+++ b/app/Template/category/edit.php
@@ -10,10 +10,10 @@
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->label(t('Category Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?>
<?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textEditor('description', $values, $errors) ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
diff --git a/app/Template/category/index.php b/app/Template/category/index.php
index ac60d9a8..336b79a2 100644
--- a/app/Template/category/index.php
+++ b/app/Template/category/index.php
@@ -15,9 +15,11 @@
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
+ <i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Edit'), 'CategoryController', 'edit', array('project_id' => $project['id'], 'category_id' => $category_id), false, 'popover') ?>
</li>
<li>
+ <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Remove'), 'CategoryController', 'confirm', array('project_id' => $project['id'], 'category_id' => $category_id), false, 'popover') ?>
</li>
</ul>
diff --git a/app/Template/column/create.php b/app/Template/column/create.php
index 71c94062..f4cded52 100644
--- a/app/Template/column/create.php
+++ b/app/Template/column/create.php
@@ -8,18 +8,18 @@
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->label(t('Title'), 'title') ?>
- <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+ <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?>
<?= $this->form->label(t('Task limit'), 'task_limit') ?>
- <?= $this->form->number('task_limit', $values, $errors) ?>
+ <?= $this->form->number('task_limit', $values, $errors, array('tabindex="2"')) ?>
- <?= $this->form->checkbox('hide_in_dashboard', t('Hide tasks in this column in the dashboard'), 1) ?>
+ <?= $this->form->checkbox('hide_in_dashboard', t('Hide tasks in this column in the dashboard'), 1, false, '', array('tabindex' => 3)) ?>
<?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textEditor('description', $values, $errors) ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 4)) ?>
<div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <button type="submit" class="btn btn-blue" tabindex="5"><?= t('Save') ?></button>
<?= t('or') ?>
<?= $this->url->link(t('cancel'), 'column', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
</div>
diff --git a/app/Template/comments/show.php b/app/Template/comments/show.php
index 5c6d8e20..dfc13821 100644
--- a/app/Template/comments/show.php
+++ b/app/Template/comments/show.php
@@ -26,6 +26,7 @@
'values' => array(
'user_id' => $this->user->getId(),
'task_id' => $task['id'],
+ 'project_id' => $task['project_id'],
),
'errors' => array(),
'task' => $task,
diff --git a/app/Template/notification/comment_create.php b/app/Template/notification/comment_create.php
index fefc8ba1..41262a7e 100644
--- a/app/Template/notification/comment_create.php
+++ b/app/Template/notification/comment_create.php
@@ -6,6 +6,6 @@
<h3><?= t('New comment') ?></h3>
<?php endif ?>
-<?= $this->text->markdown($comment['comment']) ?>
+<?= $this->text->markdown($comment['comment'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/comment_delete.php b/app/Template/notification/comment_delete.php
index 928623ec..14babbd9 100644
--- a/app/Template/notification/comment_delete.php
+++ b/app/Template/notification/comment_delete.php
@@ -2,6 +2,6 @@
<h3><?= t('Comment removed') ?></h3>
-<?= $this->text->markdown($comment['comment']) ?>
+<?= $this->text->markdown($comment['comment'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>
diff --git a/app/Template/notification/comment_update.php b/app/Template/notification/comment_update.php
index 2477d8b3..f1cffae6 100644
--- a/app/Template/notification/comment_update.php
+++ b/app/Template/notification/comment_update.php
@@ -2,6 +2,6 @@
<h3><?= t('Comment updated') ?></h3>
-<?= $this->text->markdown($comment['comment']) ?>
+<?= $this->text->markdown($comment['comment'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/comment_user_mention.php b/app/Template/notification/comment_user_mention.php
index 372183df..0990e7ab 100644
--- a/app/Template/notification/comment_user_mention.php
+++ b/app/Template/notification/comment_user_mention.php
@@ -2,6 +2,6 @@
<p><?= $this->text->e($task['title']) ?></p>
-<?= $this->text->markdown($comment['comment']) ?>
+<?= $this->text->markdown($comment['comment'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/task_assignee_change.php b/app/Template/notification/task_assignee_change.php
index 53f7c5c1..f075fdbf 100644
--- a/app/Template/notification/task_assignee_change.php
+++ b/app/Template/notification/task_assignee_change.php
@@ -14,7 +14,7 @@
<?php if (! empty($task['description'])): ?>
<h2><?= t('Description') ?></h2>
- <?= $this->text->markdown($task['description']) ?: t('There is no description.') ?>
+ <?= $this->text->markdown($task['description'], true) ?: t('There is no description.') ?>
<?php endif ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/task_create.php b/app/Template/notification/task_create.php
index 3cd68ac0..3439e357 100644
--- a/app/Template/notification/task_create.php
+++ b/app/Template/notification/task_create.php
@@ -37,7 +37,7 @@
<?php if (! empty($task['description'])): ?>
<h2><?= t('Description') ?></h2>
- <?= $this->text->markdown($task['description']) ?>
+ <?= $this->text->markdown($task['description'], true) ?>
<?php endif ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/task_update.php b/app/Template/notification/task_update.php
index 8adb2553..9abe8e0a 100644
--- a/app/Template/notification/task_update.php
+++ b/app/Template/notification/task_update.php
@@ -1,4 +1,4 @@
<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
-<?= $this->render('task/changes', array('changes' => $changes, 'task' => $task)) ?>
+<?= $this->render('task/changes', array('changes' => $changes, 'task' => $task, 'public' => true)) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/task_user_mention.php b/app/Template/notification/task_user_mention.php
index 3d8c8e95..71ad348b 100644
--- a/app/Template/notification/task_user_mention.php
+++ b/app/Template/notification/task_user_mention.php
@@ -2,6 +2,6 @@
<p><?= $this->text->e($task['title']) ?></p>
<h2><?= t('Description') ?></h2>
-<?= $this->text->markdown($task['description']) ?>
+<?= $this->text->markdown($task['description'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/swimlane/create.php b/app/Template/swimlane/create.php
index 0eb25411..207b526c 100644
--- a/app/Template/swimlane/create.php
+++ b/app/Template/swimlane/create.php
@@ -7,13 +7,13 @@
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->label(t('Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?>
<?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textEditor('description', $values, $errors) ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?>
<div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <button type="submit" class="btn btn-blue" tabindex="3"><?= t('Save') ?></button>
<?= t('or') ?>
<?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
</div>
diff --git a/app/Template/swimlane/edit.php b/app/Template/swimlane/edit.php
index 2cbabb60..d225b345 100644
--- a/app/Template/swimlane/edit.php
+++ b/app/Template/swimlane/edit.php
@@ -10,13 +10,13 @@
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->label(t('Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?>
<?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textEditor('description', $values, $errors) ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?>
<div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <button type="submit" class="btn btn-blue" tabindex="3"><?= t('Save') ?></button>
<?= t('or') ?>
<?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
</div>
diff --git a/app/Template/swimlane/edit_default.php b/app/Template/swimlane/edit_default.php
index f271c513..8a0c0a15 100644
--- a/app/Template/swimlane/edit_default.php
+++ b/app/Template/swimlane/edit_default.php
@@ -6,7 +6,7 @@
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->label(t('Name'), 'default_swimlane') ?>
- <?= $this->form->text('default_swimlane', $values, $errors, array('required', 'maxlength="50"')) ?>
+ <?= $this->form->text('default_swimlane', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
<?= $this->form->checkbox('show_default_swimlane', t('Show default swimlane'), 1, $values['show_default_swimlane'] == 1) ?>
diff --git a/app/Template/swimlane/table.php b/app/Template/swimlane/table.php
index cefef9de..81daed01 100644
--- a/app/Template/swimlane/table.php
+++ b/app/Template/swimlane/table.php
@@ -20,12 +20,15 @@
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
+ <i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Edit'), 'SwimlaneController', 'editDefault', array('project_id' => $project['id']), false, 'popover') ?>
</li>
<li>
<?php if ($default_swimlane['show_default_swimlane'] == 1): ?>
+ <i class="fa fa-toggle-off fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Disable'), 'SwimlaneController', 'disableDefault', array('project_id' => $project['id']), true) ?>
<?php else: ?>
+ <i class="fa fa-toggle-on fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Enable'), 'SwimlaneController', 'enableDefault', array('project_id' => $project['id']), true) ?>
<?php endif ?>
</li>
@@ -55,16 +58,20 @@
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
+ <i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Edit'), 'SwimlaneController', 'edit', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?>
</li>
<li>
<?php if ($swimlane['is_active']): ?>
+ <i class="fa fa-toggle-off fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Disable'), 'SwimlaneController', 'disable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
<?php else: ?>
+ <i class="fa fa-toggle-on fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Enable'), 'SwimlaneController', 'enable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
<?php endif ?>
</li>
<li>
+ <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Remove'), 'SwimlaneController', 'confirm', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?>
</li>
</ul>
diff --git a/app/Template/task/changes.php b/app/Template/task/changes.php
index 9d36f09f..2c2bf267 100644
--- a/app/Template/task/changes.php
+++ b/app/Template/task/changes.php
@@ -69,6 +69,10 @@
<?php if (! empty($changes['description'])): ?>
<p><strong><?= t('The description has been modified:') ?></strong></p>
- <div class="markdown"><?= $this->text->markdown($task['description']) ?></div>
+ <?php if (isset($public)): ?>
+ <div class="markdown"><?= $this->text->markdown($task['description'], true) ?></div>
+ <?php else: ?>
+ <div class="markdown"><?= $this->text->markdown($task['description']) ?></div>
+ <?php endif ?>
<?php endif ?>
<?php endif ?> \ No newline at end of file
diff --git a/app/Template/task_move_position/show.php b/app/Template/task_move_position/show.php
index c1a02484..91241016 100644
--- a/app/Template/task_move_position/show.php
+++ b/app/Template/task_move_position/show.php
@@ -2,45 +2,22 @@
<h2><?= t('Move task to another position on the board') ?></h2>
</div>
-<script type="x/template" id="template-task-move-position">
- <?= $this->form->label(t('Swimlane'), 'swimlane') ?>
- <select v-model="swimlaneId" @change="onChangeSwimlane()" id="form-swimlane">
- <option v-for="swimlane in board" v-bind:value="swimlane.id">
- {{ swimlane.name }}
- </option>
- </select>
+<form>
- <div v-if="columns.length > 0">
- <?= $this->form->label(t('Column'), 'column') ?>
- <select v-model="columnId" @change="onChangeColumn()" id="form-column">
- <option v-for="column in columns" v-bind:value="column.id">
- {{ column.title }}
- </option>
- </select>
- </div>
+<?= $this->app->component('task-move-position', array(
+ 'saveUrl' => $this->url->href('TaskMovePositionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
+ 'board' => $board,
+ 'swimlaneLabel' => t('Swimlane'),
+ 'columnLabel' => t('Column'),
+ 'positionLabel' => t('Position'),
+ 'beforeLabel' => t('Insert before this task'),
+ 'afterLabel' => t('Insert after this task'),
+)) ?>
- <div v-if="tasks.length > 0">
- <?= $this->form->label(t('Position'), 'position') ?>
- <select v-model="position" id="form-position">
- <option v-for="task in tasks" v-bind:value="task.position">#{{ task.id }} - {{ task.title }}</option>
- </select>
- <label><input type="radio" value="before" v-model="positionChoice"><?= t('Insert before this task') ?></label>
- <label><input type="radio" value="after" v-model="positionChoice"><?= t('Insert after this task') ?></label>
- </div>
+<?= $this->app->component('submit-cancel', array(
+ 'submitLabel' => t('Save'),
+ 'orLabel' => t('or'),
+ 'cancelLabel' => t('cancel'),
+)) ?>
- <div v-if="errorMessage">
- <div class="alert alert-error">{{ errorMessage }}</div>
- </div>
-
- <submit-cancel
- label-button="<?= t('Save') ?>"
- label-or="<?= t('or') ?>"
- label-cancel="<?= t('cancel') ?>"
- :callback="onSubmit">
- </submit-cancel>
-</script>
-
-<task-move-position
- save-url="<?= $this->url->href('TaskMovePositionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"
- :board='<?= json_encode($board, JSON_HEX_APOS) ?>'
-></task-move-position>
+</form>
diff --git a/app/Template/user_api_access/show.php b/app/Template/user_api_access/show.php
new file mode 100644
index 00000000..3d58e0d5
--- /dev/null
+++ b/app/Template/user_api_access/show.php
@@ -0,0 +1,17 @@
+<div class="page-header">
+ <h2><?= t('API User Access') ?></h2>
+</div>
+
+<p class="alert">
+ <?php if (empty($user['api_access_token'])): ?>
+ <?= t('No personal API access token registered.') ?>
+ <?php else: ?>
+ <?= t('Your personal API access token is "%s"', $user['api_access_token']) ?>
+ <?php endif ?>
+</p>
+
+<?php if (! empty($user['api_access_token'])): ?>
+ <?= $this->url->link(t('Remove your token'), 'UserApiAccessController', 'remove', array('user_id' => $user['id']), true, 'btn btn-red') ?>
+<?php endif ?>
+
+<?= $this->url->link(t('Generate a new token'), 'UserApiAccessController', 'generate', array('user_id' => $user['id']), true, 'btn btn-blue') ?>
diff --git a/app/Template/user_view/sidebar.php b/app/Template/user_view/sidebar.php
index a80daefa..ef494e42 100644
--- a/app/Template/user_view/sidebar.php
+++ b/app/Template/user_view/sidebar.php
@@ -90,6 +90,11 @@
<?= $this->url->link(t('Integrations'), 'UserViewController', 'integrations', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
+ <?php if ($this->user->hasAccess('UserApiAccessController', 'show')): ?>
+ <li <?= $this->app->checkMenuSelection('UserApiAccessController', 'show') ?>>
+ <?= $this->url->link(t('API'), 'UserApiAccessController', 'show', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
<?php endif ?>
<?php if ($this->user->hasAccess('UserCredentialController', 'changeAuthentication')): ?>
diff --git a/app/constants.php b/app/constants.php
index 3adb0835..ba14cde6 100644
--- a/app/constants.php
+++ b/app/constants.php
@@ -35,6 +35,9 @@ defined('LOG_FILE') or define('LOG_FILE', DATA_DIR.DIRECTORY_SEPARATOR.'debug.lo
// Application version
defined('APP_VERSION') or define('APP_VERSION', build_app_version('$Format:%d$', '$Format:%H$'));
+// Run automatically database migrations
+defined('DB_RUN_MIGRATIONS') or define('DB_RUN_MIGRATIONS', true);
+
// Database driver: sqlite, mysql or postgres
defined('DB_DRIVER') or define('DB_DRIVER', 'sqlite');
diff --git a/app/functions.php b/app/functions.php
index 8f0d482c..e732f308 100644
--- a/app/functions.php
+++ b/app/functions.php
@@ -53,6 +53,37 @@ function array_column_index(array &$input, $column)
}
/**
+ * Create indexed array from a list of dict with unique values
+ *
+ * $input = [
+ * ['k1' => 1, 'k2' => 2], ['k1' => 3, 'k2' => 4], ['k1' => 1, 'k2' => 5]
+ * ]
+ *
+ * array_column_index_unique($input, 'k1') will returns:
+ *
+ * [
+ * 1 => ['k1' => 1, 'k2' => 2],
+ * 3 => ['k1' => 3, 'k2' => 4],
+ * ]
+ *
+ * @param array $input
+ * @param string $column
+ * @return array
+ */
+function array_column_index_unique(array &$input, $column)
+{
+ $result = array();
+
+ foreach ($input as &$row) {
+ if (isset($row[$column]) && ! isset($result[$row[$column]])) {
+ $result[$row[$column]] = $row;
+ }
+ }
+
+ return $result;
+}
+
+/**
* Sum all values from a single column in the input array
*
* $input = [