Add autocompletion in textarea for user mentions
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
89
app/Model/ProjectGroupRoleFilter.php
Normal file
89
app/Model/ProjectGroupRoleFilter.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,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
|
||||
*
|
||||
|
||||
88
app/Model/ProjectUserRoleFilter.php
Normal file
88
app/Model/ProjectUserRoleFilter.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ class ClassProvider implements ServiceProviderInterface
|
||||
'ProjectNotification',
|
||||
'ProjectMetadata',
|
||||
'ProjectGroupRole',
|
||||
'ProjectGroupRoleFilter',
|
||||
'ProjectUserRole',
|
||||
'ProjectUserRoleFilter',
|
||||
'RememberMeSession',
|
||||
'Subtask',
|
||||
'SubtaskExport',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user