From d1952440042b596a630bc75061aea32100ac3230 Mon Sep 17 00:00:00 2001 From: lavigor Date: Fri, 6 Jul 2018 07:30:07 +0300 Subject: [PATCH] [ticket/13713] Introduce priorities based sorting PHPBB3-13713 --- phpBB/assets/javascript/editor.js | 68 ++++++++++++++++++++-- phpBB/phpbb/mention/controller/mention.php | 2 +- phpBB/phpbb/mention/source/base_group.php | 2 +- phpBB/phpbb/mention/source/base_user.php | 19 +++++- 4 files changed, 82 insertions(+), 9 deletions(-) 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), ]; }