Added event for removed comments with some refactoring

This commit is contained in:
Frederic Guillot
2016-07-17 17:15:14 -04:00
parent 3aa0f85748
commit ec0ecc5b03
18 changed files with 272 additions and 18 deletions

View File

@@ -4,6 +4,7 @@ Version 1.0.32 (unreleased)
New features:
* New automated action to close tasks without activity in a specific column
* Added new event for removed comments
* Added search filter for task priority
* Added the possibility to hide tasks in dashboard for a specific column

View File

@@ -150,6 +150,8 @@ use Pimple\Container;
* @property \Kanboard\Core\Filter\QueryBuilder $taskQuery
* @property \Kanboard\Core\Filter\LexerBuilder $taskLexer
* @property \Kanboard\Core\Filter\LexerBuilder $projectActivityLexer
* @property \Kanboard\Job\CommentEventJob $commentEventJob
* @property \Kanboard\Job\NotificationJob $notificationJob
* @property \Psr\Log\LoggerInterface $logger
* @property \PicoDb\Database $db
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher

View File

@@ -2,6 +2,7 @@
namespace Kanboard\Core\Queue;
use Exception;
use Kanboard\Core\Base;
use Kanboard\Job\BaseJob;
use SimpleQueue\Job;
@@ -39,16 +40,23 @@ class JobHandler extends Base
public function executeJob(Job $job)
{
$payload = $job->getBody();
$className = $payload['class'];
$this->memoryCache->flush();
$this->prepareJobSession($payload['user_id']);
if (DEBUG) {
$this->logger->debug(__METHOD__.' Received job => '.$className.' ('.getmypid().')');
try {
$className = $payload['class'];
$this->memoryCache->flush();
$this->prepareJobSession($payload['user_id']);
if (DEBUG) {
$this->logger->debug(__METHOD__.' Received job => '.$className.' ('.getmypid().')');
$this->logger->debug(__METHOD__.' => '.json_encode($payload));
}
$worker = new $className($this->container);
call_user_func_array(array($worker, 'execute'), $payload['params']);
} catch (Exception $e) {
$this->logger->error(__METHOD__.': Error during job execution: '.$e->getMessage());
$this->logger->error(__METHOD__ .' => '.json_encode($payload));
}
$worker = new $className($this->container);
call_user_func_array(array($worker, 'execute'), $payload['params']);
}
/**

View File

@@ -42,9 +42,13 @@ class QueueManager extends Base
*/
public function push(BaseJob $job)
{
$jobClassName = get_class($job);
if ($this->queue !== null) {
$this->logger->debug(__METHOD__.': Job pushed in queue: '.$jobClassName);
$this->queue->push(JobHandler::getInstance($this->container)->serializeJob($job));
} else {
$this->logger->debug(__METHOD__.': Job executed synchronously: '.$jobClassName);
call_user_func_array(array($job, 'execute'), $job->getJobParams());
}
@@ -60,7 +64,7 @@ class QueueManager extends Base
public function listen()
{
if ($this->queue === null) {
throw new LogicException('No Queue Driver defined!');
throw new LogicException('No queue driver defined!');
}
while ($job = $this->queue->pull()) {

View File

@@ -0,0 +1,23 @@
<?php
namespace Kanboard\EventBuilder;
use Kanboard\Core\Base;
use Kanboard\Event\GenericEvent;
/**
* Class BaseEventBuilder
*
* @package Kanboard\EventBuilder
* @author Frederic Guillot
*/
abstract class BaseEventBuilder extends Base
{
/**
* Build event data
*
* @access public
* @return GenericEvent|null
*/
abstract public function build();
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Kanboard\EventBuilder;
use Kanboard\Event\CommentEvent;
/**
* Class CommentEventBuilder
*
* @package Kanboard\EventBuilder
* @author Frederic Guillot
*/
class CommentEventBuilder extends BaseEventBuilder
{
protected $commentId = 0;
/**
* Set commentId
*
* @param int $commentId
* @return $this
*/
public function withCommentId($commentId)
{
$this->commentId = $commentId;
return $this;
}
/**
* Build event data
*
* @access public
* @return CommentEvent|null
*/
public function build()
{
$comment = $this->commentModel->getById($this->commentId);
if (empty($comment)) {
return null;
}
return new CommentEvent(array(
'comment' => $comment,
'task' => $this->taskFinderModel->getDetails($comment['task_id']),
));
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace Kanboard\Job;
use Kanboard\EventBuilder\CommentEventBuilder;
use Kanboard\Model\CommentModel;
/**
* Class CommentEventJob
*
* @package Kanboard\Job
* @author Frederic Guillot
*/
class CommentEventJob extends BaseJob
{
/**
* Set job params
*
* @param int $commentId
* @param string $eventName
* @return $this
*/
public function withParams($commentId, $eventName)
{
$this->jobParams = array($commentId, $eventName);
return $this;
}
/**
* Execute job
*
* @param int $commentId
* @param string $eventName
* @return $this
*/
public function execute($commentId, $eventName)
{
$event = CommentEventBuilder::getInstance($this->container)
->withCommentId($commentId)
->build();
if ($event !== null) {
$this->dispatcher->dispatch($eventName, $event);
if ($eventName === CommentModel::EVENT_CREATE) {
$this->userMentionModel->fireEvents($event['comment']['comment'], CommentModel::EVENT_USER_MENTION, $event);
}
}
}
}

View File

@@ -75,8 +75,7 @@ class NotificationJob extends BaseJob
$values['task'] = $this->taskFinderModel->getDetails($values['file']['task_id']);
break;
case 'Kanboard\Event\CommentEvent':
$values['comment'] = $this->commentModel->getById($event['id']);
$values['task'] = $this->taskFinderModel->getDetails($values['comment']['task_id']);
$values = $event;
break;
}

View File

@@ -2,7 +2,6 @@
namespace Kanboard\Model;
use Kanboard\Event\CommentEvent;
use Kanboard\Core\Base;
/**
@@ -27,6 +26,7 @@ class CommentModel extends Base
*/
const EVENT_UPDATE = 'comment.update';
const EVENT_CREATE = 'comment.create';
const EVENT_REMOVE = 'comment.remove';
const EVENT_USER_MENTION = 'comment.user.mention';
/**
@@ -130,9 +130,7 @@ class CommentModel extends Base
$comment_id = $this->db->table(self::TABLE)->persist($values);
if ($comment_id !== false) {
$event = new CommentEvent(array('id' => $comment_id) + $values);
$this->dispatcher->dispatch(self::EVENT_CREATE, $event);
$this->userMentionModel->fireEvents($values['comment'], self::EVENT_USER_MENTION, $event);
$this->queueManager->push($this->commentEventJob->withParams($comment_id, self::EVENT_CREATE));
}
return $comment_id;
@@ -153,7 +151,7 @@ class CommentModel extends Base
->update(array('comment' => $values['comment']));
if ($result) {
$this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new CommentEvent($values));
$this->queueManager->push($this->commentEventJob->withParams($values['id'], self::EVENT_UPDATE));
}
return $result;
@@ -168,6 +166,7 @@ class CommentModel extends Base
*/
public function remove($comment_id)
{
$this->commentEventJob->execute($comment_id, self::EVENT_REMOVE);
return $this->db->table(self::TABLE)->eq('id', $comment_id)->remove();
}
}

View File

@@ -74,6 +74,8 @@ class NotificationModel extends Base
return e('%s updated a comment on the task #%d', $event_author, $event_data['task']['id']);
case CommentModel::EVENT_CREATE:
return e('%s commented on the task #%d', $event_author, $event_data['task']['id']);
case CommentModel::EVENT_REMOVE:
return e('%s removed a comment on the task #%d', $event_author, $event_data['task']['id']);
case TaskFileModel::EVENT_CREATE:
return e('%s attached a file to the task #%d', $event_author, $event_data['task']['id']);
case TaskModel::EVENT_USER_MENTION:
@@ -102,6 +104,8 @@ class NotificationModel extends Base
return e('New comment on task #%d', $event_data['comment']['task_id']);
case CommentModel::EVENT_UPDATE:
return e('Comment updated on task #%d', $event_data['comment']['task_id']);
case CommentModel::EVENT_REMOVE:
return e('Comment removed on task #%d', $event_data['comment']['task_id']);
case SubtaskModel::EVENT_CREATE:
return e('New subtask on task #%d', $event_data['subtask']['task_id']);
case SubtaskModel::EVENT_UPDATE:
@@ -149,6 +153,7 @@ class NotificationModel extends Base
return $event_data['file']['task_id'];
case CommentModel::EVENT_CREATE:
case CommentModel::EVENT_UPDATE:
case CommentModel::EVENT_REMOVE:
return $event_data['comment']['task_id'];
case SubtaskModel::EVENT_CREATE:
case SubtaskModel::EVENT_UPDATE:

View File

@@ -26,6 +26,10 @@ class ClassProvider implements ServiceProviderInterface
'AverageLeadCycleTimeAnalytic',
'AverageTimeSpentColumnAnalytic',
),
'Job' => array(
'CommentEventJob',
'NotificationJob',
),
'Model' => array(
'ActionModel',
'ActionParameterModel',

View File

@@ -28,6 +28,7 @@ class NotificationSubscriber extends BaseSubscriber implements EventSubscriberIn
SubtaskModel::EVENT_UPDATE => 'handleEvent',
CommentModel::EVENT_CREATE => 'handleEvent',
CommentModel::EVENT_UPDATE => 'handleEvent',
CommentModel::EVENT_REMOVE => 'handleEvent',
CommentModel::EVENT_USER_MENTION => 'handleEvent',
TaskFileModel::EVENT_CREATE => 'handleEvent',
);

View File

@@ -0,0 +1,11 @@
<p class="activity-title">
<?= e('%s removed a comment on the task %s',
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
<span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
<div class="markdown"><?= $this->text->markdown($comment['comment']) ?></div>
</div>

View File

@@ -7,4 +7,7 @@
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
<?php if (! empty($comment['comment'])): ?>
<div class="markdown"><?= $this->text->markdown($comment['comment']) ?></div>
<?php endif ?>
</div>

View File

@@ -0,0 +1,7 @@
<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
<h3><?= t('Comment removed') ?></h3>
<?= $this->text->markdown($comment['comment']) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>

View File

@@ -0,0 +1,37 @@
<?php
use Kanboard\EventBuilder\CommentEventBuilder;
use Kanboard\Model\CommentModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\TaskCreationModel;
require_once __DIR__.'/../Base.php';
class CommentEventBuilderTest extends Base
{
public function testWithMissingComment()
{
$commentEventBuilder = new CommentEventBuilder($this->container);
$commentEventBuilder->withCommentId(42);
$this->assertNull($commentEventBuilder->build());
}
public function testBuild()
{
$commentModel = new CommentModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$projectModel = new ProjectModel($this->container);
$commentEventBuilder = new CommentEventBuilder($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
$this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'bla bla', 'user_id' => 1)));
$commentEventBuilder->withCommentId(1);
$event = $commentEventBuilder->build();
$this->assertInstanceOf('Kanboard\Event\CommentEvent', $event);
$this->assertNotEmpty($event['comment']);
$this->assertNotEmpty($event['task']);
}
}

View File

@@ -0,0 +1,52 @@
<?php
use Kanboard\Job\CommentEventJob;
use Kanboard\Model\CommentModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\TaskCreationModel;
require_once __DIR__.'/../Base.php';
class CommentEventJobTest extends Base
{
public function testJobParams()
{
$commentEventJob = new CommentEventJob($this->container);
$commentEventJob->withParams(123, 'foobar');
$this->assertSame(array(123, 'foobar'), $commentEventJob->getJobParams());
}
public function testWithMissingComment()
{
$this->container['dispatcher']->addListener(CommentModel::EVENT_CREATE, function() {});
$commentEventJob = new CommentEventJob($this->container);
$commentEventJob->execute(42, CommentModel::EVENT_CREATE);
$called = $this->container['dispatcher']->getCalledListeners();
$this->assertEmpty($called);
}
public function testTriggerEvents()
{
$this->container['dispatcher']->addListener(CommentModel::EVENT_CREATE, function() {});
$this->container['dispatcher']->addListener(CommentModel::EVENT_UPDATE, function() {});
$this->container['dispatcher']->addListener(CommentModel::EVENT_REMOVE, function() {});
$commentModel = new CommentModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
$this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'foobar', 'user_id' => 1)));
$this->assertTrue($commentModel->update(array('id' => 1, 'comment' => 'test')));
$this->assertTrue($commentModel->remove(1));
$called = $this->container['dispatcher']->getCalledListeners();
$this->assertArrayHasKey(CommentModel::EVENT_CREATE.'.closure', $called);
$this->assertArrayHasKey(CommentModel::EVENT_UPDATE.'.closure', $called);
$this->assertArrayHasKey(CommentModel::EVENT_REMOVE.'.closure', $called);
}
}

View File

@@ -6,7 +6,7 @@ use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\CommentModel;
class CommentTest extends Base
class CommentModelTest extends Base
{
public function testCreate()
{
@@ -75,7 +75,7 @@ class CommentTest extends Base
$this->assertEquals('bla', $comment['comment']);
}
public function validateRemove()
public function testRemove()
{
$commentModel = new CommentModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);