summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--app/Controller/BoardTooltipController.php5
-rw-r--r--app/Controller/CommentController.php20
-rw-r--r--app/Controller/TaskViewController.php4
-rw-r--r--app/Core/Base.php1
-rw-r--r--app/Core/Cache/BaseCache.php35
-rw-r--r--app/Core/Cache/CacheInterface.php45
-rw-r--r--app/Core/User/UserSession.php22
-rw-r--r--app/Decorator/MetadataCacheDecorator.php96
-rw-r--r--app/Model/UserMetadataModel.php2
-rw-r--r--app/ServiceProvider/CacheProvider.php10
-rw-r--r--tests/units/Core/User/UserSessionTest.php9
-rw-r--r--tests/units/Decorator/MetadataCacheDecoratorTest.php127
13 files changed, 304 insertions, 73 deletions
diff --git a/ChangeLog b/ChangeLog
index 145f5fd8..4462c05a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,7 @@ New features:
Improvements:
+* Store comment sorting direction in the database
* Avoid tags overlapping on the board
* Show project name in notifications
* Allow priority changes for inverted priority scales
diff --git a/app/Controller/BoardTooltipController.php b/app/Controller/BoardTooltipController.php
index 134d728e..79b9b509 100644
--- a/app/Controller/BoardTooltipController.php
+++ b/app/Controller/BoardTooltipController.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Model\UserMetadataModel;
+
/**
* Board Tooltip
*
@@ -75,10 +77,11 @@ class BoardTooltipController extends BaseController
public function comments()
{
$task = $this->getTask();
+ $commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
$this->response->html($this->template->render('board/tooltip_comments', array(
'task' => $task,
- 'comments' => $this->commentModel->getAll($task['id'], $this->userSession->getCommentSorting())
+ 'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection)
)));
}
diff --git a/app/Controller/CommentController.php b/app/Controller/CommentController.php
index 2a8c258a..c61a0602 100644
--- a/app/Controller/CommentController.php
+++ b/app/Controller/CommentController.php
@@ -4,6 +4,7 @@ namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
+use Kanboard\Model\UserMetadataModel;
/**
* Comment Controller
@@ -82,10 +83,10 @@ class CommentController extends BaseController
$this->flash->failure(t('Unable to create your comment.'));
}
- return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true);
+ $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true);
+ } else {
+ $this->create($values, $errors);
}
-
- return $this->create($values, $errors);
}
/**
@@ -183,9 +184,16 @@ class CommentController extends BaseController
{
$task = $this->getTask();
- $order = $this->userSession->getCommentSorting() === 'ASC' ? 'DESC' : 'ASC';
- $this->userSession->setCommentSorting($order);
+ $oldDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
+ $newDirection = $oldDirection === 'ASC' ? 'DESC' : 'ASC';
- $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'));
+ $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, $newDirection);
+
+ $this->response->redirect($this->helper->url->to(
+ 'TaskViewController',
+ 'show',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id']),
+ 'comments'
+ ));
}
}
diff --git a/app/Controller/TaskViewController.php b/app/Controller/TaskViewController.php
index 36597457..31b9de11 100644
--- a/app/Controller/TaskViewController.php
+++ b/app/Controller/TaskViewController.php
@@ -4,6 +4,7 @@ namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
+use Kanboard\Model\UserMetadataModel;
/**
* Task Controller
@@ -61,13 +62,14 @@ class TaskViewController extends BaseController
{
$task = $this->getTask();
$subtasks = $this->subtaskModel->getAll($task['id']);
+ $commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
$this->response->html($this->helper->layout->task('task/show', array(
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'files' => $this->taskFileModel->getAllDocuments($task['id']),
'images' => $this->taskFileModel->getAllImages($task['id']),
- 'comments' => $this->commentModel->getAll($task['id'], $this->userSession->getCommentSorting()),
+ 'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection),
'subtasks' => $subtasks,
'internal_links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']),
'external_links' => $this->taskExternalLinkModel->getAll($task['id']),
diff --git a/app/Core/Base.php b/app/Core/Base.php
index df82febd..3b7c5e66 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -56,6 +56,7 @@ use Pimple\Container;
* @property \Kanboard\Core\Helper $helper
* @property \Kanboard\Core\Paginator $paginator
* @property \Kanboard\Core\Template $template
+ * @property \Kanboard\Decorator\MetadataCacheDecorator $userMetadataCacheDecorator
* @property \Kanboard\Model\ActionModel $actionModel
* @property \Kanboard\Model\ActionParameterModel $actionParameterModel
* @property \Kanboard\Model\AvatarFileModel $avatarFileModel
diff --git a/app/Core/Cache/BaseCache.php b/app/Core/Cache/BaseCache.php
index 04f8d220..b51c4c0c 100644
--- a/app/Core/Cache/BaseCache.php
+++ b/app/Core/Cache/BaseCache.php
@@ -8,42 +8,9 @@ namespace Kanboard\Core\Cache;
* @package Kanboard\Core\Cache
* @author Frederic Guillot
*/
-abstract class BaseCache
+abstract class BaseCache implements CacheInterface
{
/**
- * Store an item in the cache
- *
- * @access public
- * @param string $key
- * @param string $value
- */
- abstract public function set($key, $value);
-
- /**
- * Retrieve an item from the cache by key
- *
- * @access public
- * @param string $key
- * @return mixed Null when not found, cached value otherwise
- */
- abstract public function get($key);
-
- /**
- * Remove all items from the cache
- *
- * @access public
- */
- abstract public function flush();
-
- /**
- * Remove an item from the cache
- *
- * @access public
- * @param string $key
- */
- abstract public function remove($key);
-
- /**
* Proxy cache
*
* Note: Arguments must be scalar types
diff --git a/app/Core/Cache/CacheInterface.php b/app/Core/Cache/CacheInterface.php
new file mode 100644
index 00000000..19bd6ef7
--- /dev/null
+++ b/app/Core/Cache/CacheInterface.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Kanboard\Core\Cache;
+
+/**
+ * Interface CacheInterface
+ *
+ * @package Kanboard\Core\Cache
+ * @author Frederic Guillot
+ */
+interface CacheInterface
+{
+ /**
+ * Store an item in the cache
+ *
+ * @access public
+ * @param string $key
+ * @param string $value
+ */
+ public function set($key, $value);
+
+ /**
+ * Retrieve an item from the cache by key
+ *
+ * @access public
+ * @param string $key
+ * @return mixed Null when not found, cached value otherwise
+ */
+ public function get($key);
+
+ /**
+ * Remove all items from the cache
+ *
+ * @access public
+ */
+ public function flush();
+
+ /**
+ * Remove an item from the cache
+ *
+ * @access public
+ * @param string $key
+ */
+ public function remove($key);
+}
diff --git a/app/Core/User/UserSession.php b/app/Core/User/UserSession.php
index 9c63f07a..4397876c 100644
--- a/app/Core/User/UserSession.php
+++ b/app/Core/User/UserSession.php
@@ -203,26 +203,4 @@ class UserSession extends Base
{
$this->sessionStorage->boardCollapsed[$project_id] = $is_collapsed;
}
-
- /**
- * Set comments sorting
- *
- * @access public
- * @param string $order
- */
- public function setCommentSorting($order)
- {
- $this->sessionStorage->commentSorting = $order;
- }
-
- /**
- * Get comments sorting direction
- *
- * @access public
- * @return string
- */
- public function getCommentSorting()
- {
- return empty($this->sessionStorage->commentSorting) ? 'ASC' : $this->sessionStorage->commentSorting;
- }
}
diff --git a/app/Decorator/MetadataCacheDecorator.php b/app/Decorator/MetadataCacheDecorator.php
new file mode 100644
index 00000000..0897b51c
--- /dev/null
+++ b/app/Decorator/MetadataCacheDecorator.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Kanboard\Decorator;
+
+use Kanboard\Core\Cache\CacheInterface;
+use Kanboard\Model\MetadataModel;
+
+/**
+ * Class MetadataCacheDecorator
+ *
+ * @package Kanboard\Decorator
+ * @author Frederic Guillot
+ */
+class MetadataCacheDecorator
+{
+ /**
+ * @var CacheInterface
+ */
+ protected $cache;
+
+ /**
+ * @var MetadataModel
+ */
+ protected $metadataModel;
+
+ /**
+ * @var string
+ */
+ protected $cachePrefix;
+
+ /**
+ * @var int
+ */
+ protected $entityId;
+
+ /**
+ * Constructor
+ *
+ * @param CacheInterface $cache
+ * @param MetadataModel $metadataModel
+ * @param string $cachePrefix
+ * @param integer $entityId
+ */
+ public function __construct(CacheInterface $cache, MetadataModel $metadataModel, $cachePrefix, $entityId)
+ {
+ $this->cache = $cache;
+ $this->metadataModel = $metadataModel;
+ $this->cachePrefix = $cachePrefix;
+ $this->entityId = $entityId;
+ }
+
+ /**
+ * Get metadata value by key
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($key, $default)
+ {
+ $metadata = $this->cache->get($this->getCacheKey());
+
+ if ($metadata === null) {
+ $metadata = $this->metadataModel->getAll($this->entityId);
+ $this->cache->set($this->getCacheKey(), $metadata);
+ }
+
+ return isset($metadata[$key]) ? $metadata[$key] : $default;
+ }
+
+ /**
+ * Set new metadata value
+ *
+ * @param $key
+ * @param $value
+ */
+ public function set($key, $value)
+ {
+ $this->metadataModel->save($this->entityId, array(
+ $key => $value,
+ ));
+
+ $metadata = $this->metadataModel->getAll($this->entityId);
+ $this->cache->set($this->getCacheKey(), $metadata);
+ }
+
+ /**
+ * Get cache key
+ *
+ * @return string
+ */
+ protected function getCacheKey()
+ {
+ return $this->cachePrefix.$this->entityId;
+ }
+}
diff --git a/app/Model/UserMetadataModel.php b/app/Model/UserMetadataModel.php
index e931d3ba..e285b98e 100644
--- a/app/Model/UserMetadataModel.php
+++ b/app/Model/UserMetadataModel.php
@@ -10,6 +10,8 @@ namespace Kanboard\Model;
*/
class UserMetadataModel extends MetadataModel
{
+ const KEY_COMMENT_SORTING_DIRECTION = 'comment.sorting.direction';
+
/**
* Get the table
*
diff --git a/app/ServiceProvider/CacheProvider.php b/app/ServiceProvider/CacheProvider.php
index 0d56e601..fac44d53 100644
--- a/app/ServiceProvider/CacheProvider.php
+++ b/app/ServiceProvider/CacheProvider.php
@@ -4,6 +4,7 @@ namespace Kanboard\ServiceProvider;
use Kanboard\Core\Cache\FileCache;
use Kanboard\Core\Cache\MemoryCache;
+use Kanboard\Decorator\MetadataCacheDecorator;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
@@ -36,6 +37,15 @@ class CacheProvider implements ServiceProviderInterface
$container['cacheDriver'] = $container['memoryCache'];
}
+ $container['userMetadataCacheDecorator'] = function($c) {
+ return new MetadataCacheDecorator(
+ $c['cacheDriver'],
+ $c['userMetadataModel'],
+ 'user.metadata.',
+ $c['userSession']->getId()
+ );
+ };
+
return $container;
}
}
diff --git a/tests/units/Core/User/UserSessionTest.php b/tests/units/Core/User/UserSessionTest.php
index 64413f98..7e9674c0 100644
--- a/tests/units/Core/User/UserSessionTest.php
+++ b/tests/units/Core/User/UserSessionTest.php
@@ -83,15 +83,6 @@ class UserSessionTest extends Base
$this->assertFalse($us->isAdmin());
}
- public function testCommentSorting()
- {
- $us = new UserSession($this->container);
- $this->assertEquals('ASC', $us->getCommentSorting());
-
- $us->setCommentSorting('DESC');
- $this->assertEquals('DESC', $us->getCommentSorting());
- }
-
public function testBoardCollapseMode()
{
$us = new UserSession($this->container);
diff --git a/tests/units/Decorator/MetadataCacheDecoratorTest.php b/tests/units/Decorator/MetadataCacheDecoratorTest.php
new file mode 100644
index 00000000..3e4dd320
--- /dev/null
+++ b/tests/units/Decorator/MetadataCacheDecoratorTest.php
@@ -0,0 +1,127 @@
+<?php
+
+use Kanboard\Decorator\MetadataCacheDecorator;
+
+require_once __DIR__.'/../Base.php';
+
+class MetadataCacheDecoratorTest extends Base
+{
+ protected $cachePrefix = 'cache_prefix';
+ protected $entityId = 123;
+
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $cacheMock;
+
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $metadataModelMock;
+
+ /**
+ * @var MetadataCacheDecorator
+ */
+ protected $metadataCacheDecorator;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->cacheMock = $this
+ ->getMockBuilder('\Kanboard\Core\Cache\MemoryCache')
+ ->setMethods(array(
+ 'set',
+ 'get',
+ ))
+ ->getMock();
+
+ $this->metadataModelMock = $this
+ ->getMockBuilder('\Kanboard\Model\UserMetadataModel')
+ ->setConstructorArgs(array($this->container))
+ ->setMethods(array(
+ 'getAll',
+ 'save',
+ ))
+ ->getMock()
+ ;
+
+ $this->metadataCacheDecorator = new MetadataCacheDecorator(
+ $this->cacheMock,
+ $this->metadataModelMock,
+ $this->cachePrefix,
+ $this->entityId
+ );
+ }
+
+ public function testSet()
+ {
+ $this->cacheMock
+ ->expects($this->once())
+ ->method('set');
+
+ $this->metadataModelMock
+ ->expects($this->at(0))
+ ->method('save');
+
+ $this->metadataModelMock
+ ->expects($this->at(1))
+ ->method('getAll')
+ ->with($this->entityId)
+ ;
+
+ $this->metadataCacheDecorator->set('key', 'value');
+ }
+
+ public function testGetWithCache()
+ {
+ $this->cacheMock
+ ->expects($this->once())
+ ->method('get')
+ ->with($this->cachePrefix.$this->entityId)
+ ->will($this->returnValue(array('key' => 'foobar')))
+ ;
+
+ $this->assertEquals('foobar', $this->metadataCacheDecorator->get('key', 'default'));
+ }
+
+ public function testGetWithCacheAndDefaultValue()
+ {
+ $this->cacheMock
+ ->expects($this->once())
+ ->method('get')
+ ->with($this->cachePrefix.$this->entityId)
+ ->will($this->returnValue(array('key1' => 'foobar')))
+ ;
+
+ $this->assertEquals('default', $this->metadataCacheDecorator->get('key', 'default'));
+ }
+
+ public function testGetWithoutCache()
+ {
+ $this->cacheMock
+ ->expects($this->at(0))
+ ->method('get')
+ ->with($this->cachePrefix.$this->entityId)
+ ->will($this->returnValue(null))
+ ;
+
+ $this->cacheMock
+ ->expects($this->at(1))
+ ->method('set')
+ ->with(
+ $this->cachePrefix.$this->entityId,
+ array('key' => 'something')
+ )
+ ;
+
+ $this->metadataModelMock
+ ->expects($this->once())
+ ->method('getAll')
+ ->with($this->entityId)
+ ->will($this->returnValue(array('key' => 'something')))
+ ;
+
+ $this->assertEquals('something', $this->metadataCacheDecorator->get('key', 'default'));
+ }
+}