summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Api/Authorization/ProjectAuthorization.php4
-rw-r--r--app/Api/Authorization/TagAuthorization.php23
-rw-r--r--app/Api/Authorization/TaskAuthorization.php4
-rw-r--r--app/Api/Middleware/AuthenticationMiddleware.php2
-rw-r--r--app/Api/Procedure/TagProcedure.php44
-rw-r--r--app/Api/Procedure/TaskProcedure.php14
-rw-r--r--app/Api/Procedure/TaskTagProcedure.php26
-rw-r--r--app/Auth/ReverseProxyAuth.php2
-rw-r--r--app/Controller/PasswordResetController.php2
-rw-r--r--app/Controller/ProjectFileController.php2
-rw-r--r--app/Controller/TaskFileController.php2
-rw-r--r--app/Controller/TaskImportController.php2
-rw-r--r--app/Controller/UserImportController.php2
-rw-r--r--app/Core/Base.php3
-rw-r--r--app/Core/Markdown.php21
-rw-r--r--app/Decorator/UserCacheDecorator.php59
-rw-r--r--app/Job/UserMentionJob.php3
-rw-r--r--app/ServiceProvider/ApiProvider.php4
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php4
-rw-r--r--app/ServiceProvider/CacheProvider.php8
-rw-r--r--app/functions.php10
21 files changed, 213 insertions, 28 deletions
diff --git a/app/Api/Authorization/ProjectAuthorization.php b/app/Api/Authorization/ProjectAuthorization.php
index 21ecf311..7dcdc445 100644
--- a/app/Api/Authorization/ProjectAuthorization.php
+++ b/app/Api/Authorization/ProjectAuthorization.php
@@ -23,13 +23,13 @@ class ProjectAuthorization extends Base
protected function checkProjectPermission($class, $method, $project_id)
{
if (empty($project_id)) {
- throw new AccessDeniedException('Project not found');
+ throw new AccessDeniedException('Project Not Found');
}
$role = $this->projectUserRoleModel->getUserRole($project_id, $this->userSession->getId());
if (! $this->apiProjectAuthorization->isAllowed($class, $method, $role)) {
- throw new AccessDeniedException('Project access denied');
+ throw new AccessDeniedException('Project Access Denied');
}
}
}
diff --git a/app/Api/Authorization/TagAuthorization.php b/app/Api/Authorization/TagAuthorization.php
new file mode 100644
index 00000000..247f57db
--- /dev/null
+++ b/app/Api/Authorization/TagAuthorization.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class TagAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class TagAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $tag_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $tag = $this->tagModel->getById($tag_id);
+
+ if (! empty($tag)) {
+ $this->checkProjectPermission($class, $method, $tag['project_id']);
+ }
+ }
+ }
+}
diff --git a/app/Api/Authorization/TaskAuthorization.php b/app/Api/Authorization/TaskAuthorization.php
index db93b76b..6e044211 100644
--- a/app/Api/Authorization/TaskAuthorization.php
+++ b/app/Api/Authorization/TaskAuthorization.php
@@ -10,10 +10,10 @@ namespace Kanboard\Api\Authorization;
*/
class TaskAuthorization extends ProjectAuthorization
{
- public function check($class, $method, $category_id)
+ public function check($class, $method, $task_id)
{
if ($this->userSession->isLogged()) {
- $this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($category_id));
+ $this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($task_id));
}
}
}
diff --git a/app/Api/Middleware/AuthenticationMiddleware.php b/app/Api/Middleware/AuthenticationMiddleware.php
index c4fa874a..174dc467 100644
--- a/app/Api/Middleware/AuthenticationMiddleware.php
+++ b/app/Api/Middleware/AuthenticationMiddleware.php
@@ -31,7 +31,7 @@ class AuthenticationMiddleware extends Base implements MiddlewareInterface
$this->sessionStorage->scope = 'API';
if ($this->isUserAuthenticated($username, $password)) {
- $this->userSession->initialize($this->userModel->getByUsername($username));
+ $this->userSession->initialize($this->userCacheDecorator->getByUsername($username));
} elseif (! $this->isAppAuthenticated($username, $password)) {
$this->logger->error('API authentication failure for '.$username);
throw new AuthenticationFailureException('Wrong credentials');
diff --git a/app/Api/Procedure/TagProcedure.php b/app/Api/Procedure/TagProcedure.php
new file mode 100644
index 00000000..f1c06d01
--- /dev/null
+++ b/app/Api/Procedure/TagProcedure.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\ProjectAuthorization;
+use Kanboard\Api\Authorization\TagAuthorization;
+
+/**
+ * Class TagProcedure
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class TagProcedure extends BaseProcedure
+{
+ public function getAllTags()
+ {
+ return $this->tagModel->getAll();
+ }
+
+ public function getTagsByProject($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTagsByProject', $project_id);
+ return $this->tagModel->getAllByProject($project_id);
+ }
+
+ public function createTag($project_id, $tag)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTag', $project_id);
+ return $this->tagModel->findOrCreateTag($project_id, $tag);
+ }
+
+ public function updateTag($tag_id, $tag)
+ {
+ TagAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTag', $tag_id);
+ return $this->tagModel->update($tag_id, $tag);
+ }
+
+ public function removeTag($tag_id)
+ {
+ TagAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTag', $tag_id);
+ return $this->tagModel->remove($tag_id);
+ }
+}
diff --git a/app/Api/Procedure/TaskProcedure.php b/app/Api/Procedure/TaskProcedure.php
index ee9242d1..af67f3de 100644
--- a/app/Api/Procedure/TaskProcedure.php
+++ b/app/Api/Procedure/TaskProcedure.php
@@ -87,9 +87,9 @@ class TaskProcedure extends BaseProcedure
}
public function createTask($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0,
- $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, $priority = 0,
- $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
- $recurrence_basedate = 0, $reference = '')
+ $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, $priority = 0,
+ $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
+ $recurrence_basedate = 0, $reference = '', array $tags = array())
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id);
@@ -120,6 +120,7 @@ class TaskProcedure extends BaseProcedure
'recurrence_basedate' => $recurrence_basedate,
'reference' => $reference,
'priority' => $priority,
+ 'tags' => $tags,
);
list($valid, ) = $this->taskValidator->validateCreation($values);
@@ -128,9 +129,9 @@ class TaskProcedure extends BaseProcedure
}
public function updateTask($id, $title = null, $color_id = null, $owner_id = null,
- $date_due = null, $description = null, $category_id = null, $score = null, $priority = null,
- $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
- $recurrence_timeframe = null, $recurrence_basedate = null, $reference = null)
+ $date_due = null, $description = null, $category_id = null, $score = null, $priority = null,
+ $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
+ $recurrence_timeframe = null, $recurrence_basedate = null, $reference = null, $tags = null)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id);
$project_id = $this->taskFinderModel->getProjectId($id);
@@ -159,6 +160,7 @@ class TaskProcedure extends BaseProcedure
'recurrence_basedate' => $recurrence_basedate,
'reference' => $reference,
'priority' => $priority,
+ 'tags' => $tags,
));
list($valid) = $this->taskValidator->validateApiModification($values);
diff --git a/app/Api/Procedure/TaskTagProcedure.php b/app/Api/Procedure/TaskTagProcedure.php
new file mode 100644
index 00000000..8596f507
--- /dev/null
+++ b/app/Api/Procedure/TaskTagProcedure.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\TaskAuthorization;
+
+/**
+ * Class TaskTagProcedure
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class TaskTagProcedure extends BaseProcedure
+{
+ public function setTaskTags($project_id, $task_id, array $tags)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setTaskTags', $task_id);
+ return $this->taskTagModel->save($project_id, $task_id, $tags);
+ }
+
+ public function getTaskTags($task_id)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskTags', $task_id);
+ return $this->taskTagModel->getList($task_id);
+ }
+}
diff --git a/app/Auth/ReverseProxyAuth.php b/app/Auth/ReverseProxyAuth.php
index 02afc302..bf71e71e 100644
--- a/app/Auth/ReverseProxyAuth.php
+++ b/app/Auth/ReverseProxyAuth.php
@@ -45,7 +45,7 @@ class ReverseProxyAuth extends Base implements PreAuthenticationProviderInterfac
$username = $this->request->getRemoteUser();
if (! empty($username)) {
- $userProfile = $this->userModel->getByUsername($username);
+ $userProfile = $this->userCacheDecorator->getByUsername($username);
$this->userInfo = new ReverseProxyUserProvider($username, $userProfile ?: array());
return true;
}
diff --git a/app/Controller/PasswordResetController.php b/app/Controller/PasswordResetController.php
index a1780ed9..6189f946 100644
--- a/app/Controller/PasswordResetController.php
+++ b/app/Controller/PasswordResetController.php
@@ -109,7 +109,7 @@ class PasswordResetController extends BaseController
$token = $this->passwordResetModel->create($username);
if ($token !== false) {
- $user = $this->userModel->getByUsername($username);
+ $user = $this->userCacheDecorator->getByUsername($username);
$this->emailClient->send(
$user['email'],
diff --git a/app/Controller/ProjectFileController.php b/app/Controller/ProjectFileController.php
index cbe48679..9c38f684 100644
--- a/app/Controller/ProjectFileController.php
+++ b/app/Controller/ProjectFileController.php
@@ -21,7 +21,7 @@ class ProjectFileController extends BaseController
$this->response->html($this->template->render('project_file/create', array(
'project' => $project,
- 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')),
+ 'max_size' => $this->helper->text->phpToBytes(get_upload_max_size()),
)));
}
diff --git a/app/Controller/TaskFileController.php b/app/Controller/TaskFileController.php
index 77c0c026..8a0971e4 100644
--- a/app/Controller/TaskFileController.php
+++ b/app/Controller/TaskFileController.php
@@ -40,7 +40,7 @@ class TaskFileController extends BaseController
$this->response->html($this->template->render('task_file/create', array(
'task' => $task,
- 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')),
+ 'max_size' => $this->helper->text->phpToBytes(get_upload_max_size()),
)));
}
diff --git a/app/Controller/TaskImportController.php b/app/Controller/TaskImportController.php
index aff2d390..3ce275a5 100644
--- a/app/Controller/TaskImportController.php
+++ b/app/Controller/TaskImportController.php
@@ -27,7 +27,7 @@ class TaskImportController extends BaseController
'project' => $project,
'values' => $values,
'errors' => $errors,
- 'max_size' => ini_get('upload_max_filesize'),
+ 'max_size' => get_upload_max_size(),
'delimiters' => Csv::getDelimiters(),
'enclosures' => Csv::getEnclosures(),
'title' => t('Import tasks from CSV file'),
diff --git a/app/Controller/UserImportController.php b/app/Controller/UserImportController.php
index fec9a31d..6a9d5992 100644
--- a/app/Controller/UserImportController.php
+++ b/app/Controller/UserImportController.php
@@ -23,7 +23,7 @@ class UserImportController extends BaseController
$this->response->html($this->template->render('user_import/show', array(
'values' => $values,
'errors' => $errors,
- 'max_size' => ini_get('upload_max_filesize'),
+ 'max_size' => get_upload_max_size(),
'delimiters' => Csv::getDelimiters(),
'enclosures' => Csv::getEnclosures(),
)));
diff --git a/app/Core/Base.php b/app/Core/Base.php
index e7ccafaa..881cccbd 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -58,7 +58,8 @@ use Pimple\Container;
* @property \Kanboard\Core\Paginator $paginator
* @property \Kanboard\Core\Template $template
* @property \Kanboard\Decorator\MetadataCacheDecorator $userMetadataCacheDecorator
- * @property \Kanboard\Decorator\columnRestrictionCacheDecorator $columnRestrictionCacheDecorator
+ * @property \Kanboard\Decorator\UserCacheDecorator $userCacheDecorator
+ * @property \Kanboard\Decorator\ColumnRestrictionCacheDecorator $columnRestrictionCacheDecorator
* @property \Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator $columnMoveRestrictionCacheDecorator
* @property \Kanboard\Decorator\ProjectRoleRestrictionCacheDecorator $projectRoleRestrictionCacheDecorator
* @property \Kanboard\Model\ActionModel $actionModel
diff --git a/app/Core/Markdown.php b/app/Core/Markdown.php
index 799aefb4..4487bf2a 100644
--- a/app/Core/Markdown.php
+++ b/app/Core/Markdown.php
@@ -86,18 +86,23 @@ class Markdown extends Parsedown
*/
protected function inlineUserLink(array $Excerpt)
{
- if (! $this->isPublicLink && preg_match('/^@([^\s,!.:?]+)/', $Excerpt['text'], $matches)) {
- $user_id = $this->container['userModel']->getIdByUsername($matches[1]);
+ if (! $this->isPublicLink && preg_match('/^@([^\s,!:?]+)/', $Excerpt['text'], $matches)) {
+ $username = rtrim($matches[1], '.');
+ $user = $this->container['userCacheDecorator']->getByUsername($username);
- if (! empty($user_id)) {
- $url = $this->container['helper']->url->href('UserViewController', 'profile', array('user_id' => $user_id));
+ if (! empty($user)) {
+ $url = $this->container['helper']->url->href('UserViewController', 'profile', array('user_id' => $user['id']));
return array(
- 'extent' => strlen($matches[0]),
+ 'extent' => strlen($username) + 1,
'element' => array(
- 'name' => 'a',
- 'text' => $matches[0],
- 'attributes' => array('href' => $url, 'class' => 'user-mention-link'),
+ 'name' => 'a',
+ 'text' => '@' . $username,
+ 'attributes' => array(
+ 'href' => $url,
+ 'class' => 'user-mention-link',
+ 'title' => $user['name'] ?: $user['username'],
+ ),
),
);
}
diff --git a/app/Decorator/UserCacheDecorator.php b/app/Decorator/UserCacheDecorator.php
new file mode 100644
index 00000000..1cfe31c9
--- /dev/null
+++ b/app/Decorator/UserCacheDecorator.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Kanboard\Decorator;
+
+use Kanboard\Core\Cache\CacheInterface;
+use Kanboard\Model\UserModel;
+
+/**
+ * Class UserCacheDecorator
+ *
+ * @package Kanboard\Decorator
+ * @author Frederic Guillot
+ */
+class UserCacheDecorator
+{
+ protected $cachePrefix = 'user_model:';
+
+ /**
+ * @var CacheInterface
+ */
+ protected $cache;
+
+ /**
+ * @var UserModel
+ */
+ private $userModel;
+
+ /**
+ * UserCacheDecorator constructor.
+ *
+ * @param CacheInterface $cache
+ * @param UserModel $userModel
+ */
+ public function __construct(CacheInterface $cache, UserModel $userModel)
+ {
+ $this->cache = $cache;
+ $this->userModel = $userModel;
+ }
+
+ /**
+ * Get a specific user by the username
+ *
+ * @access public
+ * @param string $username Username
+ * @return array
+ */
+ public function getByUsername($username)
+ {
+ $key = $this->cachePrefix.$username;
+ $user = $this->cache->get($key);
+
+ if ($user === null) {
+ $user = $this->userModel->getByUsername($username);
+ $this->cache->set($key, $user);
+ }
+
+ return $user;
+ }
+}
diff --git a/app/Job/UserMentionJob.php b/app/Job/UserMentionJob.php
index bbb27131..355095bb 100644
--- a/app/Job/UserMentionJob.php
+++ b/app/Job/UserMentionJob.php
@@ -58,7 +58,8 @@ class UserMentionJob extends BaseJob
{
$users = array();
- if (preg_match_all('/@([^\s,!.:?]+)/', $text, $matches)) {
+ if (preg_match_all('/@([^\s,!:?]+)/', $text, $matches)) {
+ array_walk($matches[1], function (&$username) { $username = rtrim($username, '.'); });
$users = $this->db->table(UserModel::TABLE)
->columns('id', 'username', 'name', 'email', 'language')
->eq('notifications_enabled', 1)
diff --git a/app/ServiceProvider/ApiProvider.php b/app/ServiceProvider/ApiProvider.php
index d5d1f260..2c9abec7 100644
--- a/app/ServiceProvider/ApiProvider.php
+++ b/app/ServiceProvider/ApiProvider.php
@@ -10,6 +10,7 @@ use Kanboard\Api\Procedure\CategoryProcedure;
use Kanboard\Api\Procedure\ColumnProcedure;
use Kanboard\Api\Procedure\CommentProcedure;
use Kanboard\Api\Procedure\ProjectFileProcedure;
+use Kanboard\Api\Procedure\TagProcedure;
use Kanboard\Api\Procedure\TaskExternalLinkProcedure;
use Kanboard\Api\Procedure\TaskFileProcedure;
use Kanboard\Api\Procedure\GroupProcedure;
@@ -25,6 +26,7 @@ use Kanboard\Api\Procedure\SwimlaneProcedure;
use Kanboard\Api\Procedure\TaskMetadataProcedure;
use Kanboard\Api\Procedure\TaskProcedure;
use Kanboard\Api\Procedure\TaskLinkProcedure;
+use Kanboard\Api\Procedure\TaskTagProcedure;
use Kanboard\Api\Procedure\UserProcedure;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
@@ -71,9 +73,11 @@ class ApiProvider implements ServiceProviderInterface
->withObject(new TaskLinkProcedure($container))
->withObject(new TaskExternalLinkProcedure($container))
->withObject(new TaskMetadataProcedure($container))
+ ->withObject(new TaskTagProcedure($container))
->withObject(new UserProcedure($container))
->withObject(new GroupProcedure($container))
->withObject(new GroupMemberProcedure($container))
+ ->withObject(new TagProcedure($container))
->withBeforeMethod('beforeProcedure')
;
diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php
index c2dad0e6..80882456 100644
--- a/app/ServiceProvider/AuthenticationProvider.php
+++ b/app/ServiceProvider/AuthenticationProvider.php
@@ -136,7 +136,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('BoardViewController', 'readonly', Role::APP_PUBLIC);
$acl->add('ICalendarController', '*', Role::APP_PUBLIC);
$acl->add('FeedController', '*', Role::APP_PUBLIC);
- $acl->add('AvatarFileController', 'show', Role::APP_PUBLIC);
+ $acl->add('AvatarFileController', array('show', 'image'), Role::APP_PUBLIC);
$acl->add('ConfigController', '*', Role::APP_ADMIN);
$acl->add('TagController', '*', Role::APP_ADMIN);
@@ -210,6 +210,8 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('TaskLinkProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('TaskExternalLinkProcedure', array('createExternalTaskLink', 'updateExternalTaskLink', 'removeExternalTaskLink'), Role::PROJECT_MEMBER);
$acl->add('TaskProcedure', '*', Role::PROJECT_MEMBER);
+ $acl->add('TaskTagProcedure', array('setTaskTags'), Role::PROJECT_MEMBER);
+ $acl->add('TagProcedure', array('createTag', 'updateTag', 'removeTag'), Role::PROJECT_MEMBER);
return $acl;
}
diff --git a/app/ServiceProvider/CacheProvider.php b/app/ServiceProvider/CacheProvider.php
index e93dd502..af8a8e7a 100644
--- a/app/ServiceProvider/CacheProvider.php
+++ b/app/ServiceProvider/CacheProvider.php
@@ -8,6 +8,7 @@ use Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator;
use Kanboard\Decorator\ColumnRestrictionCacheDecorator;
use Kanboard\Decorator\MetadataCacheDecorator;
use Kanboard\Decorator\ProjectRoleRestrictionCacheDecorator;
+use Kanboard\Decorator\UserCacheDecorator;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
@@ -40,6 +41,13 @@ class CacheProvider implements ServiceProviderInterface
$container['cacheDriver'] = $container['memoryCache'];
}
+ $container['userCacheDecorator'] = function($c) {
+ return new UserCacheDecorator(
+ $c['memoryCache'],
+ $c['userModel']
+ );
+ };
+
$container['userMetadataCacheDecorator'] = function($c) {
return new MetadataCacheDecorator(
$c['cacheDriver'],
diff --git a/app/functions.php b/app/functions.php
index e732f308..9dd054fb 100644
--- a/app/functions.php
+++ b/app/functions.php
@@ -136,6 +136,16 @@ function build_app_version($ref, $commit_hash)
}
/**
+ * Get upload max size.
+ *
+ * @return string
+ */
+function get_upload_max_size()
+{
+ return min(ini_get('upload_max_filesize'), ini_get('post_max_size'));
+}
+
+/**
* Translate a string
*
* @return string