summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2015-12-29 09:30:36 +0100
committerFrederic Guillot <fred@kanboard.net>2015-12-29 09:30:36 +0100
commitded63d21a84811c9e082c0fea0110a1b498265d6 (patch)
tree2d95b985ef8402ae899ced0307af4e8e3d47e431 /app
parent9ff0abd8d683ab5a285fcf70e27d1bcd1cbc47df (diff)
Send notifications on user mentions
Diffstat (limited to 'app')
-rw-r--r--app/Api/Task.php4
-rw-r--r--app/Integration/BitbucketWebhook.php4
-rw-r--r--app/Integration/GithubWebhook.php4
-rw-r--r--app/Integration/GitlabWebhook.php2
-rw-r--r--app/Model/Action.php2
-rw-r--r--app/Model/Comment.php9
-rw-r--r--app/Model/Notification.php54
-rw-r--r--app/Model/ProjectPermission.php15
-rw-r--r--app/Model/Task.php1
-rw-r--r--app/Model/TaskCreation.php11
-rw-r--r--app/Model/UserMention.php61
-rw-r--r--app/Model/UserNotification.php19
-rw-r--r--app/Notification/Mail.php4
-rw-r--r--app/ServiceProvider/ClassProvider.php1
-rw-r--r--app/Subscriber/NotificationSubscriber.php41
-rw-r--r--app/Template/notification/comment_user_mention.php7
-rw-r--r--app/Template/notification/task_user_mention.php7
17 files changed, 172 insertions, 74 deletions
diff --git a/app/Api/Task.php b/app/Api/Task.php
index 4a7ee932..1491cd35 100644
--- a/app/Api/Task.php
+++ b/app/Api/Task.php
@@ -71,7 +71,7 @@ class Task extends Base
{
$this->checkProjectPermission($project_id);
- if ($owner_id !== 0 && ! $this->projectPermission->isMember($project_id, $owner_id)) {
+ if ($owner_id !== 0 && ! $this->projectPermission->isAssignable($project_id, $owner_id)) {
return false;
}
@@ -117,7 +117,7 @@ class Task extends Base
return false;
}
- if ($owner_id !== null && ! $this->projectPermission->isMember($project_id, $owner_id)) {
+ if ($owner_id !== null && ! $this->projectPermission->isAssignable($project_id, $owner_id)) {
return false;
}
diff --git a/app/Integration/BitbucketWebhook.php b/app/Integration/BitbucketWebhook.php
index 97a39437..3814e35c 100644
--- a/app/Integration/BitbucketWebhook.php
+++ b/app/Integration/BitbucketWebhook.php
@@ -81,7 +81,7 @@ class BitbucketWebhook extends \Kanboard\Core\Base
if (! empty($task)) {
$user = $this->user->getByUsername($payload['actor']['username']);
- if (! empty($user) && ! $this->projectPermission->isMember($this->project_id, $user['id'])) {
+ if (! empty($user) && ! $this->projectPermission->isAssignable($this->project_id, $user['id'])) {
$user = array();
}
@@ -213,7 +213,7 @@ class BitbucketWebhook extends \Kanboard\Core\Base
return false;
}
- if (! $this->projectPermission->isMember($this->project_id, $user['id'])) {
+ if (! $this->projectPermission->isAssignable($this->project_id, $user['id'])) {
return false;
}
diff --git a/app/Integration/GithubWebhook.php b/app/Integration/GithubWebhook.php
index c8b53e37..6dd7a8d9 100644
--- a/app/Integration/GithubWebhook.php
+++ b/app/Integration/GithubWebhook.php
@@ -149,7 +149,7 @@ class GithubWebhook extends \Kanboard\Core\Base
if (! empty($task)) {
$user = $this->user->getByUsername($payload['comment']['user']['login']);
- if (! empty($user) && ! $this->projectPermission->isMember($this->project_id, $user['id'])) {
+ if (! empty($user) && ! $this->projectPermission->isAssignable($this->project_id, $user['id'])) {
$user = array();
}
@@ -266,7 +266,7 @@ class GithubWebhook extends \Kanboard\Core\Base
$user = $this->user->getByUsername($issue['assignee']['login']);
$task = $this->taskFinder->getByReference($this->project_id, $issue['number']);
- if (! empty($user) && ! empty($task) && $this->projectPermission->isMember($this->project_id, $user['id'])) {
+ if (! empty($user) && ! empty($task) && $this->projectPermission->isAssignable($this->project_id, $user['id'])) {
$event = array(
'project_id' => $this->project_id,
'task_id' => $task['id'],
diff --git a/app/Integration/GitlabWebhook.php b/app/Integration/GitlabWebhook.php
index 17b6da70..7ab4cedf 100644
--- a/app/Integration/GitlabWebhook.php
+++ b/app/Integration/GitlabWebhook.php
@@ -273,7 +273,7 @@ class GitlabWebhook extends \Kanboard\Core\Base
if (! empty($task)) {
$user = $this->user->getByUsername($payload['user']['username']);
- if (! empty($user) && ! $this->projectPermission->isMember($this->project_id, $user['id'])) {
+ if (! empty($user) && ! $this->projectPermission->isAssignable($this->project_id, $user['id'])) {
$user = array();
}
diff --git a/app/Model/Action.php b/app/Model/Action.php
index 289471f4..d3d18edb 100644
--- a/app/Model/Action.php
+++ b/app/Model/Action.php
@@ -427,7 +427,7 @@ class Action extends Base
return $this->board->getColumnIdByTitle($project_id, $column['title']) ?: false;
case 'user_id':
case 'owner_id':
- return $this->projectPermission->isMember($project_id, $param['value']) ? $param['value'] : false;
+ return $this->projectPermission->isAssignable($project_id, $param['value']) ? $param['value'] : false;
default:
return $param['value'];
}
diff --git a/app/Model/Comment.php b/app/Model/Comment.php
index f60a96e3..71e964dc 100644
--- a/app/Model/Comment.php
+++ b/app/Model/Comment.php
@@ -26,8 +26,9 @@ class Comment extends Base
*
* @var string
*/
- const EVENT_UPDATE = 'comment.update';
- const EVENT_CREATE = 'comment.create';
+ const EVENT_UPDATE = 'comment.update';
+ const EVENT_CREATE = 'comment.create';
+ const EVENT_USER_MENTION = 'comment.user.mention';
/**
* Get all comments for a given task
@@ -110,7 +111,9 @@ class Comment extends Base
$comment_id = $this->persist(self::TABLE, $values);
if ($comment_id) {
- $this->container['dispatcher']->dispatch(self::EVENT_CREATE, new CommentEvent(array('id' => $comment_id) + $values));
+ $event = new CommentEvent(array('id' => $comment_id) + $values);
+ $this->dispatcher->dispatch(self::EVENT_CREATE, $event);
+ $this->userMention->fireEvents($values['comment'], self::EVENT_USER_MENTION, $event);
}
return $comment_id;
diff --git a/app/Model/Notification.php b/app/Model/Notification.php
index f1122993..87c1a796 100644
--- a/app/Model/Notification.php
+++ b/app/Model/Notification.php
@@ -74,6 +74,10 @@ class Notification extends Base
return e('%s commented on the task #%d', $event_author, $event_data['task']['id']);
case File::EVENT_CREATE:
return e('%s attached a file to the task #%d', $event_author, $event_data['task']['id']);
+ case Task::EVENT_USER_MENTION:
+ return e('%s mentioned you in the task #%d', $event_author, $event_data['task']['id']);
+ case Comment::EVENT_USER_MENTION:
+ return e('%s mentioned you in a comment on the task #%d', $event_author, $event_data['task']['id']);
default:
return e('Notification');
}
@@ -91,52 +95,40 @@ class Notification extends Base
{
switch ($event_name) {
case File::EVENT_CREATE:
- $title = e('New attachment on task #%d: %s', $event_data['file']['task_id'], $event_data['file']['name']);
- break;
+ return e('New attachment on task #%d: %s', $event_data['file']['task_id'], $event_data['file']['name']);
case Comment::EVENT_CREATE:
- $title = e('New comment on task #%d', $event_data['comment']['task_id']);
- break;
+ return e('New comment on task #%d', $event_data['comment']['task_id']);
case Comment::EVENT_UPDATE:
- $title = e('Comment updated on task #%d', $event_data['comment']['task_id']);
- break;
+ return e('Comment updated on task #%d', $event_data['comment']['task_id']);
case Subtask::EVENT_CREATE:
- $title = e('New subtask on task #%d', $event_data['subtask']['task_id']);
- break;
+ return e('New subtask on task #%d', $event_data['subtask']['task_id']);
case Subtask::EVENT_UPDATE:
- $title = e('Subtask updated on task #%d', $event_data['subtask']['task_id']);
- break;
+ return e('Subtask updated on task #%d', $event_data['subtask']['task_id']);
case Task::EVENT_CREATE:
- $title = e('New task #%d: %s', $event_data['task']['id'], $event_data['task']['title']);
- break;
+ return e('New task #%d: %s', $event_data['task']['id'], $event_data['task']['title']);
case Task::EVENT_UPDATE:
- $title = e('Task updated #%d', $event_data['task']['id']);
- break;
+ return e('Task updated #%d', $event_data['task']['id']);
case Task::EVENT_CLOSE:
- $title = e('Task #%d closed', $event_data['task']['id']);
- break;
+ return e('Task #%d closed', $event_data['task']['id']);
case Task::EVENT_OPEN:
- $title = e('Task #%d opened', $event_data['task']['id']);
- break;
+ return e('Task #%d opened', $event_data['task']['id']);
case Task::EVENT_MOVE_COLUMN:
- $title = e('Column changed for task #%d', $event_data['task']['id']);
- break;
+ return e('Column changed for task #%d', $event_data['task']['id']);
case Task::EVENT_MOVE_POSITION:
- $title = e('New position for task #%d', $event_data['task']['id']);
- break;
+ return e('New position for task #%d', $event_data['task']['id']);
case Task::EVENT_MOVE_SWIMLANE:
- $title = e('Swimlane changed for task #%d', $event_data['task']['id']);
- break;
+ return e('Swimlane changed for task #%d', $event_data['task']['id']);
case Task::EVENT_ASSIGNEE_CHANGE:
- $title = e('Assignee changed on task #%d', $event_data['task']['id']);
- break;
+ return e('Assignee changed on task #%d', $event_data['task']['id']);
case Task::EVENT_OVERDUE:
$nb = count($event_data['tasks']);
- $title = $nb > 1 ? e('%d overdue tasks', $nb) : e('Task #%d is overdue', $event_data['tasks'][0]['id']);
- break;
+ return $nb > 1 ? e('%d overdue tasks', $nb) : e('Task #%d is overdue', $event_data['tasks'][0]['id']);
+ case Task::EVENT_USER_MENTION:
+ return e('You were mentioned in the task #%d', $event_data['task']['id']);
+ case Comment::EVENT_USER_MENTION:
+ return e('You were mentioned in a comment on the task #%d', $event_data['task']['id']);
default:
- $title = e('Notification');
+ return e('Notification');
}
-
- return $title;
}
}
diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php
index 4ad9bbf1..66f4091d 100644
--- a/app/Model/ProjectPermission.php
+++ b/app/Model/ProjectPermission.php
@@ -86,12 +86,25 @@ class ProjectPermission extends Base
* @param integer $user_id
* @return boolean
*/
- public function isMember($project_id, $user_id)
+ public function isAssignable($project_id, $user_id)
{
return in_array($this->projectUserRole->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER));
}
/**
+ * Return true if the user is member
+ *
+ * @access public
+ * @param integer $project_id
+ * @param integer $user_id
+ * @return boolean
+ */
+ public function isMember($project_id, $user_id)
+ {
+ return in_array($this->projectUserRole->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER, Role::PROJECT_VIEWER));
+ }
+
+ /**
* Get active project ids by user
*
* @access public
diff --git a/app/Model/Task.php b/app/Model/Task.php
index f1cd094f..7aa9e312 100644
--- a/app/Model/Task.php
+++ b/app/Model/Task.php
@@ -41,6 +41,7 @@ class Task extends Base
const EVENT_CREATE_UPDATE = 'task.create_update';
const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change';
const EVENT_OVERDUE = 'task.overdue';
+ const EVENT_USER_MENTION = 'task.user.mention';
/**
* Recurrence: status
diff --git a/app/Model/TaskCreation.php b/app/Model/TaskCreation.php
index 5ef1a04b..88912d4d 100644
--- a/app/Model/TaskCreation.php
+++ b/app/Model/TaskCreation.php
@@ -86,8 +86,13 @@ class TaskCreation extends Base
*/
private function fireEvents($task_id, array $values)
{
- $values['task_id'] = $task_id;
- $this->container['dispatcher']->dispatch(Task::EVENT_CREATE_UPDATE, new TaskEvent($values));
- $this->container['dispatcher']->dispatch(Task::EVENT_CREATE, new TaskEvent($values));
+ $event = new TaskEvent(array('task_id' => $task_id) + $values);
+
+ $this->dispatcher->dispatch(Task::EVENT_CREATE_UPDATE, $event);
+ $this->dispatcher->dispatch(Task::EVENT_CREATE, $event);
+
+ if (! empty($values['description'])) {
+ $this->userMention->fireEvents($values['description'], Task::EVENT_USER_MENTION, $event);
+ }
}
}
diff --git a/app/Model/UserMention.php b/app/Model/UserMention.php
new file mode 100644
index 00000000..97a4e419
--- /dev/null
+++ b/app/Model/UserMention.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Event\GenericEvent;
+
+/**
+ * User Mention
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class UserMention 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(User::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->taskFinder->getProjectId($event['task_id']);
+ }
+
+ $users = $this->getMentionedUsers($content);
+
+ foreach ($users as $user) {
+ if ($this->projectPermission->isMember($event['project_id'], $user['id'])) {
+ $event['mention'] = $user;
+ $this->dispatcher->dispatch($eventName, $event);
+ }
+ }
+ }
+}
diff --git a/app/Model/UserNotification.php b/app/Model/UserNotification.php
index e00f23c5..8161288c 100644
--- a/app/Model/UserNotification.php
+++ b/app/Model/UserNotification.php
@@ -21,18 +21,12 @@ class UserNotification extends Base
*/
public function sendNotifications($event_name, array $event_data)
{
- $logged_user_id = $this->userSession->isLogged() ? $this->userSession->getId() : 0;
- $users = $this->getUsersWithNotificationEnabled($event_data['task']['project_id'], $logged_user_id);
-
- if (! empty($users)) {
- foreach ($users as $user) {
- if ($this->userNotificationFilter->shouldReceiveNotification($user, $event_data)) {
- $this->sendUserNotification($user, $event_name, $event_data);
- }
- }
+ $users = $this->getUsersWithNotificationEnabled($event_data['task']['project_id'], $this->userSession->getId());
- // Restore locales
- $this->config->setupTranslations();
+ foreach ($users as $user) {
+ if ($this->userNotificationFilter->shouldReceiveNotification($user, $event_data)) {
+ $this->sendUserNotification($user, $event_name, $event_data);
+ }
}
}
@@ -58,6 +52,9 @@ class UserNotification extends Base
foreach ($this->userNotificationType->getSelectedTypes($user['id']) as $type) {
$this->userNotificationType->getType($type)->notifyUser($user, $event_name, $event_data);
}
+
+ // Restore locales
+ $this->config->setupTranslations();
}
/**
diff --git a/app/Notification/Mail.php b/app/Notification/Mail.php
index 98bffef2..d05dbdf2 100644
--- a/app/Notification/Mail.php
+++ b/app/Notification/Mail.php
@@ -121,6 +121,10 @@ class Mail extends Base implements NotificationInterface
case Task::EVENT_ASSIGNEE_CHANGE:
$subject = $this->getStandardMailSubject(e('Assignee change'), $event_data);
break;
+ case Task::EVENT_USER_MENTION:
+ case Comment::EVENT_USER_MENTION:
+ $subject = $this->getStandardMailSubject(e('Mentioned'), $event_data);
+ break;
case Task::EVENT_OVERDUE:
$subject = e('[%s] Overdue tasks', $event_data['project_name']);
break;
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index fad73047..03257c07 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -67,6 +67,7 @@ class ClassProvider implements ServiceProviderInterface
'User',
'UserImport',
'UserLocking',
+ 'UserMention',
'UserNotification',
'UserNotificationFilter',
'UserUnreadNotification',
diff --git a/app/Subscriber/NotificationSubscriber.php b/app/Subscriber/NotificationSubscriber.php
index 394573e4..1bde24dd 100644
--- a/app/Subscriber/NotificationSubscriber.php
+++ b/app/Subscriber/NotificationSubscriber.php
@@ -2,6 +2,7 @@
namespace Kanboard\Subscriber;
+use Kanboard\Core\Base;
use Kanboard\Event\GenericEvent;
use Kanboard\Model\Task;
use Kanboard\Model\Comment;
@@ -9,34 +10,40 @@ use Kanboard\Model\Subtask;
use Kanboard\Model\File;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-class NotificationSubscriber extends \Kanboard\Core\Base implements EventSubscriberInterface
+class NotificationSubscriber extends Base implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
- Task::EVENT_CREATE => array('execute', 0),
- Task::EVENT_UPDATE => array('execute', 0),
- Task::EVENT_CLOSE => array('execute', 0),
- Task::EVENT_OPEN => array('execute', 0),
- Task::EVENT_MOVE_COLUMN => array('execute', 0),
- Task::EVENT_MOVE_POSITION => array('execute', 0),
- Task::EVENT_MOVE_SWIMLANE => array('execute', 0),
- Task::EVENT_ASSIGNEE_CHANGE => array('execute', 0),
- Subtask::EVENT_CREATE => array('execute', 0),
- Subtask::EVENT_UPDATE => array('execute', 0),
- Comment::EVENT_CREATE => array('execute', 0),
- Comment::EVENT_UPDATE => array('execute', 0),
- File::EVENT_CREATE => array('execute', 0),
+ Task::EVENT_USER_MENTION => 'handleEvent',
+ Task::EVENT_CREATE => 'handleEvent',
+ Task::EVENT_UPDATE => 'handleEvent',
+ Task::EVENT_CLOSE => 'handleEvent',
+ Task::EVENT_OPEN => 'handleEvent',
+ Task::EVENT_MOVE_COLUMN => 'handleEvent',
+ Task::EVENT_MOVE_POSITION => 'handleEvent',
+ Task::EVENT_MOVE_SWIMLANE => 'handleEvent',
+ Task::EVENT_ASSIGNEE_CHANGE => 'handleEvent',
+ Subtask::EVENT_CREATE => 'handleEvent',
+ Subtask::EVENT_UPDATE => 'handleEvent',
+ Comment::EVENT_CREATE => 'handleEvent',
+ Comment::EVENT_UPDATE => 'handleEvent',
+ Comment::EVENT_USER_MENTION => 'handleEvent',
+ File::EVENT_CREATE => 'handleEvent',
);
}
- public function execute(GenericEvent $event, $event_name)
+ public function handleEvent(GenericEvent $event, $event_name)
{
$event_data = $this->getEventData($event);
if (! empty($event_data)) {
- $this->userNotification->sendNotifications($event_name, $event_data);
- $this->projectNotification->sendNotifications($event_data['task']['project_id'], $event_name, $event_data);
+ if (! empty($event['mention'])) {
+ $this->userNotification->sendUserNotification($event['mention'], $event_name, $event_data);
+ } else {
+ $this->userNotification->sendNotifications($event_name, $event_data);
+ $this->projectNotification->sendNotifications($event_data['task']['project_id'], $event_name, $event_data);
+ }
}
}
diff --git a/app/Template/notification/comment_user_mention.php b/app/Template/notification/comment_user_mention.php
new file mode 100644
index 00000000..59f5127e
--- /dev/null
+++ b/app/Template/notification/comment_user_mention.php
@@ -0,0 +1,7 @@
+<h2><?= t('You were mentioned in a comment on the task #%d', $task['id']) ?></h2>
+
+<p><?= $this->e($task['title']) ?></p>
+
+<?= $this->text->markdown($comment['comment']) ?>
+
+<?= $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
new file mode 100644
index 00000000..40ddddca
--- /dev/null
+++ b/app/Template/notification/task_user_mention.php
@@ -0,0 +1,7 @@
+<h2><?= t('You were mentioned in the task #%d', $task['id']) ?></h2>
+<p><?= $this->e($task['title']) ?></p>
+
+<h2><?= t('Description') ?></h2>
+<?= $this->text->markdown($task['description']) ?>
+
+<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file