From ec0ecc5b0387924f061865f4ec12dbfc5b7018fe Mon Sep 17 00:00:00 2001
From: Frederic Guillot <fred@kanboard.net>
Date: Sun, 17 Jul 2016 17:15:14 -0400
Subject: Added event for removed comments with some refactoring

---
 app/Core/Base.php                            |  2 ++
 app/Core/Queue/JobHandler.php                | 24 ++++++++-----
 app/Core/Queue/QueueManager.php              |  6 +++-
 app/EventBuilder/BaseEventBuilder.php        | 23 +++++++++++++
 app/EventBuilder/CommentEventBuilder.php     | 48 ++++++++++++++++++++++++++
 app/Job/CommentEventJob.php                  | 50 ++++++++++++++++++++++++++++
 app/Job/NotificationJob.php                  |  3 +-
 app/Model/CommentModel.php                   |  9 +++--
 app/Model/NotificationModel.php              |  5 +++
 app/ServiceProvider/ClassProvider.php        |  4 +++
 app/Subscriber/NotificationSubscriber.php    |  1 +
 app/Template/event/comment_remove.php        | 11 ++++++
 app/Template/event/comment_update.php        |  3 ++
 app/Template/notification/comment_remove.php |  7 ++++
 14 files changed, 180 insertions(+), 16 deletions(-)
 create mode 100644 app/EventBuilder/BaseEventBuilder.php
 create mode 100644 app/EventBuilder/CommentEventBuilder.php
 create mode 100644 app/Job/CommentEventJob.php
 create mode 100644 app/Template/event/comment_remove.php
 create mode 100644 app/Template/notification/comment_remove.php

(limited to 'app')

diff --git a/app/Core/Base.php b/app/Core/Base.php
index 8103ec14..e413a4ac 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -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
diff --git a/app/Core/Queue/JobHandler.php b/app/Core/Queue/JobHandler.php
index 7ca36328..326f3cef 100644
--- a/app/Core/Queue/JobHandler.php
+++ b/app/Core/Queue/JobHandler.php
@@ -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']);
+            $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));
+        }
     }
 
     /**
diff --git a/app/Core/Queue/QueueManager.php b/app/Core/Queue/QueueManager.php
index f34cb220..dcf0ebf5 100644
--- a/app/Core/Queue/QueueManager.php
+++ b/app/Core/Queue/QueueManager.php
@@ -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()) {
diff --git a/app/EventBuilder/BaseEventBuilder.php b/app/EventBuilder/BaseEventBuilder.php
new file mode 100644
index 00000000..c677563e
--- /dev/null
+++ b/app/EventBuilder/BaseEventBuilder.php
@@ -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();
+}
diff --git a/app/EventBuilder/CommentEventBuilder.php b/app/EventBuilder/CommentEventBuilder.php
new file mode 100644
index 00000000..7b4060e4
--- /dev/null
+++ b/app/EventBuilder/CommentEventBuilder.php
@@ -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']),
+        ));
+    }
+}
diff --git a/app/Job/CommentEventJob.php b/app/Job/CommentEventJob.php
new file mode 100644
index 00000000..c89350ed
--- /dev/null
+++ b/app/Job/CommentEventJob.php
@@ -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);
+            }
+        }
+    }
+}
diff --git a/app/Job/NotificationJob.php b/app/Job/NotificationJob.php
index 904a9273..cfd0699d 100644
--- a/app/Job/NotificationJob.php
+++ b/app/Job/NotificationJob.php
@@ -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;
         }
 
diff --git a/app/Model/CommentModel.php b/app/Model/CommentModel.php
index 4231f29d..6b983858 100644
--- a/app/Model/CommentModel.php
+++ b/app/Model/CommentModel.php
@@ -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();
     }
 }
diff --git a/app/Model/NotificationModel.php b/app/Model/NotificationModel.php
index 4d697b5e..df481fc7 100644
--- a/app/Model/NotificationModel.php
+++ b/app/Model/NotificationModel.php
@@ -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:
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index e32c0d43..43ade56e 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -26,6 +26,10 @@ class ClassProvider implements ServiceProviderInterface
             'AverageLeadCycleTimeAnalytic',
             'AverageTimeSpentColumnAnalytic',
         ),
+        'Job' => array(
+            'CommentEventJob',
+            'NotificationJob',
+        ),
         'Model' => array(
             'ActionModel',
             'ActionParameterModel',
diff --git a/app/Subscriber/NotificationSubscriber.php b/app/Subscriber/NotificationSubscriber.php
index db11e585..6cd7675c 100644
--- a/app/Subscriber/NotificationSubscriber.php
+++ b/app/Subscriber/NotificationSubscriber.php
@@ -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',
         );
diff --git a/app/Template/event/comment_remove.php b/app/Template/event/comment_remove.php
new file mode 100644
index 00000000..ead7d56a
--- /dev/null
+++ b/app/Template/event/comment_remove.php
@@ -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>
diff --git a/app/Template/event/comment_update.php b/app/Template/event/comment_update.php
index 5a0821bd..5be598ac 100644
--- a/app/Template/event/comment_update.php
+++ b/app/Template/event/comment_update.php
@@ -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>
diff --git a/app/Template/notification/comment_remove.php b/app/Template/notification/comment_remove.php
new file mode 100644
index 00000000..928623ec
--- /dev/null
+++ b/app/Template/notification/comment_remove.php
@@ -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)) ?>
-- 
cgit v1.2.3