diff --git a/phpBB/assets/javascript/editor.js b/phpBB/assets/javascript/editor.js
index d6287f5e49..aa39c15568 100644
--- a/phpBB/assets/javascript/editor.js
+++ b/phpBB/assets/javascript/editor.js
@@ -386,21 +386,24 @@ function getCaretPosition(txtarea) {
(function($) {
function Mentions() {
+ let $mentionDataContainer = $('[data-mention-url]:first'), cachedNames = null, filteredNamesCount = 0;
+
function defaultAvatar(type) {
return (type === 'group') ? '' : '';
}
this.isEnabled = function() {
- return $('[data-mention-url]').length;
+ return $mentionDataContainer.length;
};
this.handle = function(txtarea) {
- let $mentionDataContainer = $('[data-mention-url]'),
- mentionURL = $mentionDataContainer.data('mentionUrl'),
+ let mentionURL = $mentionDataContainer.data('mentionUrl'),
mentionNamesLimit = $mentionDataContainer.data('mentionNamesLimit'),
- mentionTopicId = $mentionDataContainer.data('topicId');
+ mentionTopicId = $mentionDataContainer.data('topicId'),
+ defaultFilter = $.fn.atwho['default'].callbacks.filter;
$(txtarea).atwho({
at: "@",
+ acceptSpaceBar: true,
displayTpl: function(data) {
// TODO: handle image scaling
let avatar = (data.avatar.img) ? "" + data.avatar.img + "" : defaultAvatar(data.avatar.type),
@@ -410,11 +413,66 @@ function getCaretPosition(txtarea) {
insertTpl: "[mention=${type}:${id}]${name}[/mention]",
limit: mentionNamesLimit,
callbacks: {
+ filter: function(query, data, searchKey) {
+ data = defaultFilter(query, data, searchKey);
+
+ // Update our cached statistics used by remoteFilter
+ filteredNamesCount = data.length;
+
+ return data;
+ },
remoteFilter: function(query, callback) {
+ if (cachedNames && filteredNamesCount >= mentionNamesLimit) {
+ callback(cachedNames);
+ return;
+ }
let params = {keyword: query, topic_id: mentionTopicId, _referer: location.href};
$.getJSON(mentionURL, params, function (data) {
- callback(data)
+ cachedNames = data;
+ callback(data);
});
+ },
+ sorter: function(query, items, searchKey) {
+ let _unsorted, _results, _exactMatch, i, item, len, highestPriority = 0;
+ _unsorted = {u: {}, g: {}};
+ _exactMatch = [];
+ for (i = 0, len = items.length; i < len; i++) {
+ item = items[i];
+ if (item.name === query) {
+ _exactMatch.push(items[i]);
+ continue;
+ }
+ if (!_unsorted[item.type]) {
+ continue;
+ }
+ if (!_unsorted[item.type][item.id] || item.type === 'g') {
+ _unsorted[item.type][item.id] = item;
+ continue;
+ }
+ _unsorted[item.type][item.id].priority += parseFloat(item.priority);
+ highestPriority = Math.max(highestPriority, _unsorted[item.type][item.id].priority);
+ }
+ _results = [];
+ if (_unsorted['u']) {
+ $.each(_unsorted['u'], function(name, value) {
+ _results.push(value);
+ });
+ }
+ if (_unsorted['g']) {
+ $.each(_unsorted['g'], function(name, value) {
+ // Groups should come at the same level of importance
+ // as users, otherwise they will be unlikely to be shown
+ value.priority = highestPriority;
+ _results.push(value);
+ });
+ }
+ _results = _results.sort(function(a, b) {
+ return a.priority - b.priority;
+ });
+ $.each(_exactMatch, function(name, value) {
+ _results.unshift(value);
+ });
+ return _results;
}
}
});
diff --git a/phpBB/phpbb/mention/controller/mention.php b/phpBB/phpbb/mention/controller/mention.php
index b4a42799b9..0aa08e090b 100644
--- a/phpBB/phpbb/mention/controller/mention.php
+++ b/phpBB/phpbb/mention/controller/mention.php
@@ -54,7 +54,7 @@ class mention
foreach ($this->mention_sources as $source)
{
- $names = array_merge($names, $source->get($keyword, $topic_id));
+ $names += $source->get($keyword, $topic_id);
}
return new JsonResponse(array_values($names));
diff --git a/phpBB/phpbb/mention/source/base_group.php b/phpBB/phpbb/mention/source/base_group.php
index 644119ecd9..9a8c70695c 100644
--- a/phpBB/phpbb/mention/source/base_group.php
+++ b/phpBB/phpbb/mention/source/base_group.php
@@ -131,7 +131,7 @@ abstract class base_group implements source_interface
foreach ($group_ids as $group_id)
{
$group_rank = phpbb_get_user_rank($groups[$group_id], false);
- $names['g' . $group_id] = [
+ $names[] = [
'name' => $groups[$group_id]['group_name'],
'type' => 'g',
'id' => $group_id,
diff --git a/phpBB/phpbb/mention/source/base_user.php b/phpBB/phpbb/mention/source/base_user.php
index 804c20c14d..45f5e3b917 100644
--- a/phpBB/phpbb/mention/source/base_user.php
+++ b/phpBB/phpbb/mention/source/base_user.php
@@ -15,6 +15,9 @@ namespace phpbb\mention\source;
abstract class base_user implements source_interface
{
+ /** @var int */
+ const NAMES_BATCH_SIZE = 100;
+
/** @var \phpbb\db\driver\driver_interface */
protected $db;
@@ -56,19 +59,30 @@ abstract class base_user implements source_interface
*/
abstract protected function query($keyword, $topic_id);
+ /**
+ * Returns the priority of the currently selected name
+ *
+ * @param array $row Array of fetched user data
+ * @return int Priority (defaults to 1)
+ */
+ public function get_priority($row)
+ {
+ return 1;
+ }
+
/**
* {@inheritdoc}
*/
public function get($keyword, $topic_id)
{
$keyword = utf8_clean_string($keyword);
- $result = $this->db->sql_query_limit($this->query($keyword, $topic_id), $this->config['mention_names_limit']);
+ $result = $this->db->sql_query_limit($this->query($keyword, $topic_id), self::NAMES_BATCH_SIZE);
$names = [];
while ($row = $this->db->sql_fetchrow($result))
{
$user_rank = $this->user_loader->get_rank($row['user_id'], true);
- $names['u' . $row['user_id']] = [
+ $names[] = [
'name' => $row['username'],
'type' => 'u',
'id' => $row['user_id'],
@@ -77,6 +91,7 @@ abstract class base_user implements source_interface
'img' => $this->user_loader->get_avatar($row['user_id'], true),
],
'rank' => (isset($user_rank['rank_title'])) ? $user_rank['rank_title'] : '',
+ 'priority' => $this->get_priority($row),
];
}