summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2015-12-29 18:44:21 +0100
committerFrederic Guillot <fred@kanboard.net>2015-12-29 18:44:21 +0100
commit178eda18872f99840937dc3d23629b5d7aa7cee0 (patch)
treee2e27f289b5620e92b1accddcea9489286a65243 /app
parentb4c5e36ee4cbf34020eb043e54e9b7294062acee (diff)
Add autocompletion in textarea for user mentions
Diffstat (limited to 'app')
-rw-r--r--app/Controller/UserHelper.php13
-rw-r--r--app/Model/ProjectGroupRoleFilter.php89
-rw-r--r--app/Model/ProjectPermission.php19
-rw-r--r--app/Model/ProjectUserRoleFilter.php88
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php1
-rw-r--r--app/ServiceProvider/ClassProvider.php2
-rw-r--r--app/Template/comment/create.php13
-rw-r--r--app/Template/task_creation/form.php11
-rw-r--r--app/Template/task_modification/edit_description.php12
-rw-r--r--app/Template/task_modification/edit_task.php11
10 files changed, 255 insertions, 4 deletions
diff --git a/app/Controller/UserHelper.php b/app/Controller/UserHelper.php
index f164d0a6..041ed2c8 100644
--- a/app/Controller/UserHelper.php
+++ b/app/Controller/UserHelper.php
@@ -21,4 +21,17 @@ class UserHelper extends Base
$users = $this->userFilterAutoCompleteFormatter->create($search)->filterByUsernameOrByName()->format();
$this->response->json($users);
}
+
+ /**
+ * User mention autocompletion (Ajax)
+ *
+ * @access public
+ */
+ public function mention()
+ {
+ $project_id = $this->request->getStringParam('project_id');
+ $query = $this->request->getStringParam('q');
+ $users = $this->projectPermission->findUsernames($project_id, $query);
+ $this->response->json($users);
+ }
}
diff --git a/app/Model/ProjectGroupRoleFilter.php b/app/Model/ProjectGroupRoleFilter.php
new file mode 100644
index 00000000..989d3073
--- /dev/null
+++ b/app/Model/ProjectGroupRoleFilter.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Kanboard\Model;
+
+/**
+ * Project Group Role Filter
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class ProjectGroupRoleFilter extends Base
+{
+ /**
+ * Query
+ *
+ * @access protected
+ * @var \PicoDb\Table
+ */
+ protected $query;
+
+ /**
+ * Initialize filter
+ *
+ * @access public
+ * @return UserFilter
+ */
+ public function create()
+ {
+ $this->query = $this->db->table(ProjectGroupRole::TABLE);
+ return $this;
+ }
+
+ /**
+ * Get all results of the filter
+ *
+ * @access public
+ * @param string $column
+ * @return array
+ */
+ public function findAll($column = '')
+ {
+ if ($column !== '') {
+ return $this->query->asc($column)->findAllByColumn($column);
+ }
+
+ return $this->query->findAll();
+ }
+
+ /**
+ * Get the PicoDb query
+ *
+ * @access public
+ * @return \PicoDb\Table
+ */
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
+ /**
+ * Filter by project id
+ *
+ * @access public
+ * @param integer $project_id
+ * @return ProjectUserRoleFilter
+ */
+ public function filterByProjectId($project_id)
+ {
+ $this->query->eq(ProjectGroupRole::TABLE.'.project_id', $project_id);
+ return $this;
+ }
+
+ /**
+ * Filter by username
+ *
+ * @access public
+ * @param string $input
+ * @return ProjectUserRoleFilter
+ */
+ public function startWithUsername($input)
+ {
+ $this->query
+ ->join(GroupMember::TABLE, 'group_id', 'group_id', ProjectGroupRole::TABLE)
+ ->join(User::TABLE, 'id', 'user_id', GroupMember::TABLE)
+ ->ilike(User::TABLE.'.username', $input.'%');
+
+ return $this;
+ }
+}
diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php
index 66f4091d..cea62e13 100644
--- a/app/Model/ProjectPermission.php
+++ b/app/Model/ProjectPermission.php
@@ -44,6 +44,25 @@ class ProjectPermission extends Base
}
/**
+ * Get all usernames (fetch users from groups)
+ *
+ * @access public
+ * @param integer $project_id
+ * @param string $input
+ * @return array
+ */
+ public function findUsernames($project_id, $input)
+ {
+ $userMembers = $this->projectUserRoleFilter->create()->filterByProjectId($project_id)->startWithUsername($input)->findAll('username');
+ $groupMembers = $this->projectGroupRoleFilter->create()->filterByProjectId($project_id)->startWithUsername($input)->findAll('username');
+ $members = array_unique(array_merge($userMembers, $groupMembers));
+
+ sort($members);
+
+ return $members;
+ }
+
+ /**
* Return true if everybody is allowed for the project
*
* @access public
diff --git a/app/Model/ProjectUserRoleFilter.php b/app/Model/ProjectUserRoleFilter.php
new file mode 100644
index 00000000..64403643
--- /dev/null
+++ b/app/Model/ProjectUserRoleFilter.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Kanboard\Model;
+
+/**
+ * Project User Role Filter
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class ProjectUserRoleFilter extends Base
+{
+ /**
+ * Query
+ *
+ * @access protected
+ * @var \PicoDb\Table
+ */
+ protected $query;
+
+ /**
+ * Initialize filter
+ *
+ * @access public
+ * @return UserFilter
+ */
+ public function create()
+ {
+ $this->query = $this->db->table(ProjectUserRole::TABLE);
+ return $this;
+ }
+
+ /**
+ * Get all results of the filter
+ *
+ * @access public
+ * @param string $column
+ * @return array
+ */
+ public function findAll($column = '')
+ {
+ if ($column !== '') {
+ return $this->query->asc($column)->findAllByColumn($column);
+ }
+
+ return $this->query->findAll();
+ }
+
+ /**
+ * Get the PicoDb query
+ *
+ * @access public
+ * @return \PicoDb\Table
+ */
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
+ /**
+ * Filter by project id
+ *
+ * @access public
+ * @param integer $project_id
+ * @return ProjectUserRoleFilter
+ */
+ public function filterByProjectId($project_id)
+ {
+ $this->query->eq(ProjectUserRole::TABLE.'.project_id', $project_id);
+ return $this;
+ }
+
+ /**
+ * Filter by username
+ *
+ * @access public
+ * @param string $input
+ * @return ProjectUserRoleFilter
+ */
+ public function startWithUsername($input)
+ {
+ $this->query
+ ->join(User::TABLE, 'id', 'user_id')
+ ->ilike(User::TABLE.'.username', $input.'%');
+
+ return $this;
+ }
+}
diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php
index 46ba5be8..6e7c49d9 100644
--- a/app/ServiceProvider/AuthenticationProvider.php
+++ b/app/ServiceProvider/AuthenticationProvider.php
@@ -106,6 +106,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('Taskmodification', '*', Role::PROJECT_MEMBER);
$acl->add('Taskstatus', '*', Role::PROJECT_MEMBER);
$acl->add('Timer', '*', Role::PROJECT_MEMBER);
+ $acl->add('UserHelper', array('mention'), Role::PROJECT_MEMBER);
return $acl;
}
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index 03257c07..559a78c1 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -42,7 +42,9 @@ class ClassProvider implements ServiceProviderInterface
'ProjectNotification',
'ProjectMetadata',
'ProjectGroupRole',
+ 'ProjectGroupRoleFilter',
'ProjectUserRole',
+ 'ProjectUserRoleFilter',
'RememberMeSession',
'Subtask',
'SubtaskExport',
diff --git a/app/Template/comment/create.php b/app/Template/comment/create.php
index 8bcbe0f7..e9a6404d 100644
--- a/app/Template/comment/create.php
+++ b/app/Template/comment/create.php
@@ -17,7 +17,18 @@
</li>
</ul>
<div class="write-area">
- <?= $this->form->textarea('comment', $values, $errors, array(! isset($skip_cancel) ? 'autofocus' : '', 'required', 'placeholder="'.t('Leave a comment').'"'), 'comment-textarea') ?>
+ <?= $this->form->textarea(
+ 'comment',
+ $values,
+ $errors,
+ array(
+ ! isset($skip_cancel) ? 'autofocus' : '',
+ 'required',
+ 'placeholder="'.t('Leave a comment').'"',
+ 'data-mention-search-url="'.$this->url->href('UserHelper', 'mention', array('project_id' => $task['project_id'])).'"',
+ ),
+ 'comment-textarea'
+ ) ?>
</div>
<div class="preview-area">
<div class="markdown"></div>
diff --git a/app/Template/task_creation/form.php b/app/Template/task_creation/form.php
index 325ca1c8..c9f367a0 100644
--- a/app/Template/task_creation/form.php
+++ b/app/Template/task_creation/form.php
@@ -22,7 +22,16 @@
<div class="form-tabs">
<div class="write-area">
- <?= $this->form->textarea('description', $values, $errors, array('placeholder="'.t('Leave a description').'"', 'tabindex="2"')) ?>
+ <?= $this->form->textarea(
+ 'description',
+ $values,
+ $errors,
+ array(
+ 'placeholder="'.t('Leave a description').'"',
+ 'tabindex="2"',
+ 'data-mention-search-url="'.$this->url->href('UserHelper', 'mention', array('project_id' => $values['project_id'])).'"'
+ )
+ ) ?>
</div>
<div class="preview-area">
<div class="markdown"></div>
diff --git a/app/Template/task_modification/edit_description.php b/app/Template/task_modification/edit_description.php
index 4cae939c..c38e885d 100644
--- a/app/Template/task_modification/edit_description.php
+++ b/app/Template/task_modification/edit_description.php
@@ -17,7 +17,17 @@
</li>
</ul>
<div class="write-area">
- <?= $this->form->textarea('description', $values, $errors, array('autofocus', 'placeholder="'.t('Leave a description').'"'), 'task-show-description-textarea') ?>
+ <?= $this->form->textarea(
+ 'description',
+ $values,
+ $errors,
+ array(
+ 'autofocus',
+ 'placeholder="'.t('Leave a description').'"',
+ 'data-mention-search-url="'.$this->url->href('UserHelper', 'mention', array('project_id' => $task['project_id'])).'"'
+ ),
+ 'task-show-description-textarea'
+ ) ?>
</div>
<div class="preview-area">
<div class="markdown"></div>
diff --git a/app/Template/task_modification/edit_task.php b/app/Template/task_modification/edit_task.php
index f4d7449a..6fdb77b5 100644
--- a/app/Template/task_modification/edit_task.php
+++ b/app/Template/task_modification/edit_task.php
@@ -13,7 +13,16 @@
<?= $this->form->label(t('Description'), 'description') ?>
<div class="form-tabs">
<div class="write-area">
- <?= $this->form->textarea('description', $values, $errors, array('placeholder="'.t('Leave a description').'"', 'tabindex="2"')) ?>
+ <?= $this->form->textarea(
+ 'description',
+ $values,
+ $errors,
+ array(
+ 'placeholder="'.t('Leave a description').'"',
+ 'tabindex="2"',
+ 'data-mention-search-url="'.$this->url->href('UserHelper', 'mention', array('project_id' => $task['project_id'])).'"'
+ )
+ ) ?>
</div>
<div class="preview-area">
<div class="markdown"></div>