diff --git a/ChangeLog b/ChangeLog index d67d8e02d..39a2ca5d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,13 +1,18 @@ Version 1.0.35 -------------- +New features: + +* Rewrite of Markdown editor (remove CodeMirror) +* Suggest menu for task ID and user mentions in Markdown editor + Improvements: * Add button to close inline popups * Simplify `.htaccess` to avoid potential issues with possible specific Apache configurations * Replace notifications Javascript code by CSS * Refactoring of user mentions job -* Remove nitrous installer +* Remove Nitrous installer Breaking changes: diff --git a/app/Controller/TaskAjaxController.php b/app/Controller/TaskAjaxController.php index f9feff15a..609dd23c6 100644 --- a/app/Controller/TaskAjaxController.php +++ b/app/Controller/TaskAjaxController.php @@ -5,8 +5,12 @@ namespace Kanboard\Controller; use Kanboard\Filter\TaskIdExclusionFilter; use Kanboard\Filter\TaskIdFilter; use Kanboard\Filter\TaskProjectsFilter; +use Kanboard\Filter\TaskStartsWithIdFilter; +use Kanboard\Filter\TaskStatusFilter; use Kanboard\Filter\TaskTitleFilter; use Kanboard\Formatter\TaskAutoCompleteFormatter; +use Kanboard\Formatter\TaskSuggestMenuFormatter; +use Kanboard\Model\TaskModel; /** * Task Ajax Controller @@ -19,7 +23,6 @@ class TaskAjaxController extends BaseController /** * Task auto-completion (Ajax) * - * @access public */ public function autocomplete() { @@ -46,4 +49,24 @@ class TaskAjaxController extends BaseController $this->response->json($filter->format(new TaskAutoCompleteFormatter($this->container))); } } + + /** + * Task ID suggest menu + */ + public function suggest() + { + $taskId = $this->request->getIntegerParam('search'); + $projectIds = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + + if (empty($projectIds)) { + $this->response->json(array()); + } else { + $filter = $this->taskQuery + ->withFilter(new TaskProjectsFilter($projectIds)) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskStartsWithIdFilter($taskId)); + + $this->response->json($filter->format(new TaskSuggestMenuFormatter($this->container))); + } + } } diff --git a/app/Controller/UserAjaxController.php b/app/Controller/UserAjaxController.php index 0e6543335..d93bfe9a2 100644 --- a/app/Controller/UserAjaxController.php +++ b/app/Controller/UserAjaxController.php @@ -36,7 +36,7 @@ class UserAjaxController extends BaseController public function mention() { $project_id = $this->request->getStringParam('project_id'); - $query = $this->request->getStringParam('q'); + $query = $this->request->getStringParam('search'); $users = $this->projectPermissionModel->findUsernames($project_id, $query); $this->response->json( diff --git a/app/Core/Markdown.php b/app/Core/Markdown.php index b4208e9a7..799aefb43 100644 --- a/app/Core/Markdown.php +++ b/app/Core/Markdown.php @@ -90,7 +90,7 @@ class Markdown extends Parsedown $user_id = $this->container['userModel']->getIdByUsername($matches[1]); if (! empty($user_id)) { - $url = $this->container['helper']->url->href('UserViewController', 'profile', array('user_id' => $user_id), false, '', true); + $url = $this->container['helper']->url->href('UserViewController', 'profile', array('user_id' => $user_id)); return array( 'extent' => strlen($matches[0]), @@ -125,7 +125,10 @@ class Markdown extends Parsedown array( 'token' => $token, 'task_id' => $task_id, - ) + ), + false, + '', + true ); } diff --git a/app/Filter/TaskStartsWithIdFilter.php b/app/Filter/TaskStartsWithIdFilter.php new file mode 100644 index 000000000..8b7cc6781 --- /dev/null +++ b/app/Filter/TaskStartsWithIdFilter.php @@ -0,0 +1,38 @@ +query->ilike('CAST('.TaskModel::TABLE.'.id AS CHAR(8))', $this->value.'%'); + return $this; + } +} diff --git a/app/Formatter/TaskAutoCompleteFormatter.php b/app/Formatter/TaskAutoCompleteFormatter.php index 2d9f73416..3a4f1e1a5 100644 --- a/app/Formatter/TaskAutoCompleteFormatter.php +++ b/app/Formatter/TaskAutoCompleteFormatter.php @@ -14,6 +14,20 @@ use Kanboard\Model\TaskModel; */ class TaskAutoCompleteFormatter extends BaseFormatter implements FormatterInterface { + protected $limit = 25; + + /** + * Limit number of results + * + * @param $limit + * @return $this + */ + public function withLimit($limit) + { + $this->limit = $limit; + return $this; + } + /** * Apply formatter * @@ -22,11 +36,15 @@ class TaskAutoCompleteFormatter extends BaseFormatter implements FormatterInterf */ public function format() { - $tasks = $this->query->columns( - TaskModel::TABLE.'.id', - TaskModel::TABLE.'.title', - ProjectModel::TABLE.'.name AS project_name' - )->asc(TaskModel::TABLE.'.id')->findAll(); + $tasks = $this->query + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + ProjectModel::TABLE.'.name AS project_name' + ) + ->asc(TaskModel::TABLE.'.id') + ->limit($this->limit) + ->findAll(); foreach ($tasks as &$task) { $task['value'] = $task['title']; diff --git a/app/Formatter/TaskSuggestMenuFormatter.php b/app/Formatter/TaskSuggestMenuFormatter.php new file mode 100644 index 000000000..518f99e6d --- /dev/null +++ b/app/Formatter/TaskSuggestMenuFormatter.php @@ -0,0 +1,63 @@ +limit = $limit; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + $result = array(); + $tasks = $this->query + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + ProjectModel::TABLE.'.name AS project_name' + ) + ->asc(TaskModel::TABLE.'.id') + ->limit($this->limit) + ->findAll(); + + foreach ($tasks as $task) { + $html = '#'.$task['id'].' '; + $html .= $this->helper->text->e($task['title']).' '; + $html .= ''.$this->helper->text->e($task['project_name']).''; + + $result[] = array( + 'value' => (string) $task['id'], + 'html' => $html, + ); + } + + return $result; + } +} diff --git a/app/Helper/FormHelper.php b/app/Helper/FormHelper.php index 8c9ca3325..570946888 100644 --- a/app/Helper/FormHelper.php +++ b/app/Helper/FormHelper.php @@ -220,11 +220,16 @@ class FormHelper extends Base 'labelPreview' => t('Preview'), 'labelWrite' => t('Write'), 'placeholder' => t('Write your text in Markdown'), - 'autofocus' => isset($attributes['autofocus']) && $attributes['autofocus'] + 'autofocus' => isset($attributes['autofocus']) && $attributes['autofocus'], + 'suggestOptions' => array( + 'triggers' => array( + '#' => $this->helper->url->to('TaskAjaxController', 'suggest', array('search' => 'SEARCH_TERM')), + ) + ), ); if (isset($values['project_id'])) { - $params['mentionUrl'] = $this->helper->url->to('UserAjaxController', 'mention', array('project_id' => $values['project_id'])); + $params['suggestOptions']['triggers']['@'] = $this->helper->url->to('UserAjaxController', 'mention', array('project_id' => $values['project_id'], 'search' => 'SEARCH_TERM')); } $html = '
'; diff --git a/app/Template/notification/comment_create.php b/app/Template/notification/comment_create.php index fefc8ba10..41262a7e6 100644 --- a/app/Template/notification/comment_create.php +++ b/app/Template/notification/comment_create.php @@ -6,6 +6,6 @@= $this->text->e($task['title']) ?>
-= $this->text->markdown($comment['comment']) ?> += $this->text->markdown($comment['comment'], true) ?> = $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file diff --git a/app/Template/notification/task_assignee_change.php b/app/Template/notification/task_assignee_change.php index 53f7c5c12..f075fdbfb 100644 --- a/app/Template/notification/task_assignee_change.php +++ b/app/Template/notification/task_assignee_change.php @@ -14,7 +14,7 @@= $this->text->e($task['title']) ?>
= t('The description has been modified:') ?>
-