nodebb-plugin-mentions 4.8.11 → 4.8.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -7
- package/README.md +13 -13
- package/library.js +69 -40
- package/package.json +1 -1
- package/static/admin.js +1 -19
- package/static/autofill.js +29 -26
- package/package-lock.json +0 -3748
package/LICENSE
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
Copyright (c) 2013-2014, Julian Lam <julian@designcreateplay.com>
|
|
2
|
-
All rights reserved.
|
|
3
|
-
|
|
4
|
-
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
5
|
-
|
|
6
|
-
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
7
|
-
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
1
|
+
Copyright (c) 2013-2014, Julian Lam <julian@designcreateplay.com>
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
5
|
+
|
|
6
|
+
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
7
|
+
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
8
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# Username/Group Mentions
|
|
2
|
-
|
|
3
|
-
This NodeBB plugin allows posters to reference (or *mention*) other users or groups on a NodeBB by simply
|
|
4
|
-
precluding the `@` symbol before a username.
|
|
5
|
-
|
|
6
|
-
A link is automatically added to the post.
|
|
7
|
-
|
|
8
|
-
## Installation
|
|
9
|
-
|
|
10
|
-
This plugin is bundled with every NodeBB install. If not, you can install it via the Plugins page of the ACP.
|
|
11
|
-
|
|
12
|
-
Alternatively,
|
|
13
|
-
|
|
1
|
+
# Username/Group Mentions
|
|
2
|
+
|
|
3
|
+
This NodeBB plugin allows posters to reference (or *mention*) other users or groups on a NodeBB by simply
|
|
4
|
+
precluding the `@` symbol before a username.
|
|
5
|
+
|
|
6
|
+
A link is automatically added to the post.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
This plugin is bundled with every NodeBB install. If not, you can install it via the Plugins page of the ACP.
|
|
11
|
+
|
|
12
|
+
Alternatively,
|
|
13
|
+
|
|
14
14
|
npm install nodebb-plugin-mentions
|
package/library.js
CHANGED
|
@@ -10,7 +10,6 @@ const nconf = require.main.require('nconf');
|
|
|
10
10
|
const winston = require.main.require('winston');
|
|
11
11
|
|
|
12
12
|
const db = require.main.require('./src/database');
|
|
13
|
-
const api = require.main.require('./src/api');
|
|
14
13
|
const meta = require.main.require('./src/meta');
|
|
15
14
|
const categories = require.main.require('./src/categories');
|
|
16
15
|
const Topics = require.main.require('./src/topics');
|
|
@@ -27,6 +26,7 @@ const batch = require.main.require('./src/batch');
|
|
|
27
26
|
const utils = require.main.require('./src/utils');
|
|
28
27
|
const SocketPlugins = require.main.require('./src/socket.io/plugins');
|
|
29
28
|
const translator = require.main.require('./src/translator');
|
|
29
|
+
const privileges = require.main.require('./src/privileges');
|
|
30
30
|
|
|
31
31
|
const utility = require('./lib/utility');
|
|
32
32
|
|
|
@@ -40,10 +40,9 @@ const isLatinMention = /@[\w\d\-_.@]+$/;
|
|
|
40
40
|
|
|
41
41
|
const Mentions = module.exports;
|
|
42
42
|
|
|
43
|
-
Mentions._settings = {};
|
|
44
43
|
Mentions._defaults = {
|
|
45
44
|
disableFollowedTopics: 'off',
|
|
46
|
-
autofillGroups: '
|
|
45
|
+
autofillGroups: 'on',
|
|
47
46
|
disableGroupMentions: '[]',
|
|
48
47
|
overrideIgnores: 'off',
|
|
49
48
|
display: '',
|
|
@@ -57,11 +56,13 @@ Mentions.init = async (data) => {
|
|
|
57
56
|
const controllers = require('./controllers');
|
|
58
57
|
|
|
59
58
|
routeHelpers.setupAdminPageRoute(data.router, '/admin/plugins/mentions', controllers.renderAdminPage);
|
|
60
|
-
|
|
61
|
-
// Retrieve settings
|
|
62
|
-
Object.assign(Mentions._settings, Mentions._defaults, await Meta.settings.get('mentions'));
|
|
63
59
|
};
|
|
64
60
|
|
|
61
|
+
async function getSettings() {
|
|
62
|
+
const settings = await Meta.settings.get('mentions');
|
|
63
|
+
return { ...Mentions._defaults, ...settings };
|
|
64
|
+
}
|
|
65
|
+
|
|
65
66
|
Mentions.addAdminNavigation = async (header) => {
|
|
66
67
|
header.plugins.push({
|
|
67
68
|
route: '/plugins/mentions',
|
|
@@ -71,10 +72,11 @@ Mentions.addAdminNavigation = async (header) => {
|
|
|
71
72
|
return header;
|
|
72
73
|
};
|
|
73
74
|
|
|
74
|
-
function getNoMentionGroups() {
|
|
75
|
-
let noMentionGroups = ['registered-users', 'verified-users', 'unverified-users', 'guests'];
|
|
75
|
+
async function getNoMentionGroups() {
|
|
76
|
+
let noMentionGroups = ['registered-users', 'verified-users', 'unverified-users', 'guests', 'banned-users'];
|
|
76
77
|
try {
|
|
77
|
-
|
|
78
|
+
const settings = await getSettings();
|
|
79
|
+
noMentionGroups = noMentionGroups.concat(JSON.parse(settings.disableGroupMentions));
|
|
78
80
|
} catch (err) {
|
|
79
81
|
winston.error(err);
|
|
80
82
|
}
|
|
@@ -93,7 +95,7 @@ Mentions.notify = async function ({ post }) {
|
|
|
93
95
|
return;
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
const noMentionGroups = getNoMentionGroups();
|
|
98
|
+
const noMentionGroups = await getNoMentionGroups();
|
|
97
99
|
matches = _.uniq(
|
|
98
100
|
matches
|
|
99
101
|
.map(match => slugify(match.slice(1)))
|
|
@@ -129,11 +131,11 @@ Mentions.notify = async function ({ post }) {
|
|
|
129
131
|
if ((!uidsToNotify && !groupsToNotify) || (!uidsToNotify.length && !groupsToNotify.length)) {
|
|
130
132
|
return;
|
|
131
133
|
}
|
|
132
|
-
|
|
134
|
+
const settings = await getSettings();
|
|
133
135
|
const [topic, userData, topicFollowers] = await Promise.all([
|
|
134
136
|
Topics.getTopicFields(post.tid, ['title', 'cid']),
|
|
135
137
|
User.getUserFields(post.uid, ['username']),
|
|
136
|
-
|
|
138
|
+
settings.disableFollowedTopics === 'on' ? Topics.getFollowers(post.tid) : [],
|
|
137
139
|
]);
|
|
138
140
|
const { displayname } = userData;
|
|
139
141
|
const title = entitiesDecode(topic.title);
|
|
@@ -142,7 +144,7 @@ Mentions.notify = async function ({ post }) {
|
|
|
142
144
|
uid => uid !== postOwner && !topicFollowers.includes(uid)
|
|
143
145
|
);
|
|
144
146
|
|
|
145
|
-
if (
|
|
147
|
+
if (settings.privilegedDirectReplies === 'on') {
|
|
146
148
|
const toPid = await posts.getPostField(post.pid, 'toPid');
|
|
147
149
|
uids = await filterPrivilegedUids(uids, post.cid, toPid);
|
|
148
150
|
}
|
|
@@ -298,10 +300,10 @@ async function sendNotificationToUids(postData, uids, nidType, notificationText)
|
|
|
298
300
|
if (!notification) {
|
|
299
301
|
return;
|
|
300
302
|
}
|
|
301
|
-
|
|
303
|
+
const settings = await getSettings();
|
|
302
304
|
await batch.processArray(uids, async (uids) => {
|
|
303
305
|
uids = await Privileges.topics.filterUids('read', postData.tid, uids);
|
|
304
|
-
if (
|
|
306
|
+
if (settings.overrideIgnores !== 'on') {
|
|
305
307
|
uids = await Topics.filterIgnoringUids(postData.tid, uids);
|
|
306
308
|
}
|
|
307
309
|
filteredUids.push(...uids);
|
|
@@ -423,6 +425,8 @@ Mentions.parseRaw = async (content, type = 'default') => {
|
|
|
423
425
|
return content;
|
|
424
426
|
}
|
|
425
427
|
|
|
428
|
+
const settings = await getSettings();
|
|
429
|
+
|
|
426
430
|
matches = _.uniq(matches).map((match) => {
|
|
427
431
|
/**
|
|
428
432
|
* Javascript-flavour of regex does not support lookaround,
|
|
@@ -480,6 +484,7 @@ Mentions.parseRaw = async (content, type = 'default') => {
|
|
|
480
484
|
}
|
|
481
485
|
}));
|
|
482
486
|
|
|
487
|
+
|
|
483
488
|
replacements = Array.from(replacements)
|
|
484
489
|
.sort((a, b) => {
|
|
485
490
|
return b.user.userslug.length - a.user.userslug.length;
|
|
@@ -502,7 +507,7 @@ Mentions.parseRaw = async (content, type = 'default') => {
|
|
|
502
507
|
const plain = match.slice(0, atIndex);
|
|
503
508
|
match = match.slice(atIndex + 1);
|
|
504
509
|
if (user && user.uid) {
|
|
505
|
-
switch (
|
|
510
|
+
switch (settings.display) {
|
|
506
511
|
case 'fullname':
|
|
507
512
|
match = user.displayname || match;
|
|
508
513
|
break;
|
|
@@ -514,9 +519,9 @@ Mentions.parseRaw = async (content, type = 'default') => {
|
|
|
514
519
|
|
|
515
520
|
let str;
|
|
516
521
|
if (type === 'markdown') {
|
|
517
|
-
str = `[${!
|
|
522
|
+
str = `[${!settings.display ? '@' : ''}${match}](${nconf.get('url')}${url})`;
|
|
518
523
|
} else {
|
|
519
|
-
str = `<a class="plugin-mentions-${mentionType} plugin-mentions-a" href="${nconf.get('relative_path')}${url}" aria-label="Profile: ${match}">${!
|
|
524
|
+
str = `<a class="plugin-mentions-${mentionType} plugin-mentions-a" href="${nconf.get('relative_path')}${url}" aria-label="Profile: ${match}">${!settings.display ? '@' : ''}<bdi>${match}</bdi></a>`;
|
|
520
525
|
}
|
|
521
526
|
|
|
522
527
|
return plain + str;
|
|
@@ -562,17 +567,18 @@ async function filterPrivilegedUids(uids, cid, toPid) {
|
|
|
562
567
|
}
|
|
563
568
|
|
|
564
569
|
async function filterDisallowedFullnames(users) {
|
|
570
|
+
if (!users.length) return [];
|
|
565
571
|
const userSettings = await User.getMultipleUserSettings(users.map(user => user.uid));
|
|
566
|
-
return users.filter((user, index) => userSettings[index].showfullname);
|
|
572
|
+
return users.filter((user, index) => userSettings[index] && userSettings[index].showfullname);
|
|
567
573
|
}
|
|
568
574
|
|
|
569
575
|
async function stripDisallowedFullnames(users) {
|
|
576
|
+
if (!users.length) return [];
|
|
570
577
|
const userSettings = await User.getMultipleUserSettings(users.map(user => user.uid));
|
|
571
|
-
|
|
572
|
-
if (!userSettings[index].showfullname) {
|
|
578
|
+
users.forEach((user, index) => {
|
|
579
|
+
if (user && userSettings[index] && !userSettings[index].showfullname) {
|
|
573
580
|
user.fullname = null;
|
|
574
581
|
}
|
|
575
|
-
return user;
|
|
576
582
|
});
|
|
577
583
|
}
|
|
578
584
|
|
|
@@ -587,39 +593,62 @@ SocketPlugins.mentions.getTopicUsers = async (socket, data) => {
|
|
|
587
593
|
if (Meta.config.hideFullname) {
|
|
588
594
|
return users;
|
|
589
595
|
}
|
|
590
|
-
|
|
596
|
+
await stripDisallowedFullnames(users);
|
|
597
|
+
return users;
|
|
591
598
|
};
|
|
592
599
|
|
|
593
600
|
SocketPlugins.mentions.listGroups = async function () {
|
|
594
|
-
|
|
601
|
+
const settings = await getSettings();
|
|
602
|
+
if (settings.autofillGroups === 'off') {
|
|
595
603
|
return [];
|
|
596
604
|
}
|
|
597
605
|
|
|
598
606
|
const groups = await Groups.getGroups('groups:visible:createtime', 0, -1);
|
|
599
|
-
const noMentionGroups = getNoMentionGroups();
|
|
600
|
-
|
|
607
|
+
const noMentionGroups = await getNoMentionGroups();
|
|
608
|
+
const filteredGroups = groups.filter(g => g && !noMentionGroups.includes(g))
|
|
609
|
+
.map(g => validator.escape(String(g)));
|
|
610
|
+
|
|
611
|
+
return filteredGroups;
|
|
601
612
|
};
|
|
602
613
|
|
|
603
614
|
SocketPlugins.mentions.userSearch = async (socket, data) => {
|
|
604
|
-
|
|
605
|
-
|
|
615
|
+
const allowed = await privileges.global.can('search:users', socket.uid);
|
|
616
|
+
if (!allowed) {
|
|
617
|
+
throw new Error('[[error:no-privileges]]');
|
|
618
|
+
}
|
|
606
619
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
620
|
+
const searchOpts = {
|
|
621
|
+
uid: socket.uid,
|
|
622
|
+
query: data.query,
|
|
623
|
+
sortBy: 'postcount',
|
|
624
|
+
hardCap: 1000,
|
|
625
|
+
paginate: true,
|
|
626
|
+
resultsPerPage: 100,
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
const [byUsername, byFullname, settings] = await Promise.all([
|
|
630
|
+
User.search({ ...searchOpts, searchBy: 'username' }),
|
|
631
|
+
!Meta.config.hideFullname ?
|
|
632
|
+
User.search({ ...searchOpts, searchBy: 'fullname' }) :
|
|
633
|
+
Promise.resolve({ users : [] }),
|
|
634
|
+
getSettings(),
|
|
635
|
+
]);
|
|
610
636
|
|
|
611
|
-
|
|
612
|
-
|
|
637
|
+
let { users } = byUsername;
|
|
638
|
+
|
|
639
|
+
const [fullnameUsers] = await Promise.all([
|
|
613
640
|
// Hide results of users that do not allow their full name to be visible (prevents "enumeration attack")
|
|
614
|
-
|
|
641
|
+
filterDisallowedFullnames(byFullname.users),
|
|
642
|
+
// Strip fullnames of users that do not allow their full name to be visible
|
|
643
|
+
stripDisallowedFullnames(users),
|
|
644
|
+
]);
|
|
615
645
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
}
|
|
646
|
+
// Merge results, filter duplicates (from username search, leave fullname results)
|
|
647
|
+
const fullnameUidSet = new Set(fullnameUsers.map(u => u.uid));
|
|
648
|
+
users = users.filter(u => !fullnameUidSet.has(u.uid)).concat(fullnameUsers);
|
|
649
|
+
users.sort((a, b) => b.postcount - a.postcount);
|
|
621
650
|
|
|
622
|
-
if (
|
|
651
|
+
if (settings.privilegedDirectReplies === 'on') {
|
|
623
652
|
if (data.composerObj) {
|
|
624
653
|
const cid = Topics.getTopicField(data.composerObj.tid, 'cid');
|
|
625
654
|
const filteredUids = await filterPrivilegedUids(users.map(userObj => userObj.uid), cid, data.composerObj.toPid);
|
package/package.json
CHANGED
package/static/admin.js
CHANGED
|
@@ -6,30 +6,12 @@ define('admin/plugins/mentions', ['settings', 'alerts'], function (Settings, ale
|
|
|
6
6
|
ACP.init = function () {
|
|
7
7
|
Settings.load('mentions', $('.mentions-settings'));
|
|
8
8
|
|
|
9
|
-
$(window).on('action:admin.settingsLoaded', applyDefaults);
|
|
10
|
-
|
|
11
9
|
$('#save').on('click', function () {
|
|
12
10
|
Settings.save('mentions', $('.mentions-settings'), function () {
|
|
13
|
-
alerts.
|
|
14
|
-
type: 'success',
|
|
15
|
-
alert_id: 'mentions-saved',
|
|
16
|
-
title: 'Settings Saved',
|
|
17
|
-
message: 'Please reload your NodeBB to apply these settings',
|
|
18
|
-
timeout: 5000,
|
|
19
|
-
clickfn: function () {
|
|
20
|
-
socket.emit('admin.reload');
|
|
21
|
-
},
|
|
22
|
-
});
|
|
11
|
+
alerts.success('Settings Saved');
|
|
23
12
|
});
|
|
24
13
|
});
|
|
25
14
|
};
|
|
26
15
|
|
|
27
|
-
function applyDefaults() {
|
|
28
|
-
if (!ajaxify.data.settings || !ajaxify.data.settings.hasOwnProperty('autofillGroups')) {
|
|
29
|
-
$('input#autofillGroups').parents('.mdl-switch').toggleClass('is-checked', false);
|
|
30
|
-
$('input#autofillGroups').prop('checked', false);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
16
|
return ACP;
|
|
35
17
|
});
|
package/static/autofill.js
CHANGED
|
@@ -8,6 +8,12 @@ $(document).ready(function () {
|
|
|
8
8
|
let localUserList = [];
|
|
9
9
|
let helpers;
|
|
10
10
|
|
|
11
|
+
function showAlert(type, message) {
|
|
12
|
+
require(['alerts'], function (alerts) {
|
|
13
|
+
alerts[type](message);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
$(window).on('composer:autocomplete:init chat:autocomplete:init', function (ev, data) {
|
|
12
18
|
loadTopicUsers(data.element);
|
|
13
19
|
|
|
@@ -36,37 +42,30 @@ $(document).ready(function () {
|
|
|
36
42
|
composerObj: composer.posts[uuid],
|
|
37
43
|
}, function (err, users) {
|
|
38
44
|
if (err) {
|
|
39
|
-
|
|
40
|
-
alerts.alert({
|
|
41
|
-
id: 'mention-error',
|
|
42
|
-
type: 'danger',
|
|
43
|
-
message: err.message,
|
|
44
|
-
timeout: 5000,
|
|
45
|
-
});
|
|
46
|
-
});
|
|
45
|
+
showAlert('error', err.message);
|
|
47
46
|
return callback([]);
|
|
48
47
|
}
|
|
49
48
|
const termLowerCase = term.toLocaleLowerCase();
|
|
50
49
|
const localMatches = localUserList.filter(
|
|
51
50
|
u => u.username.toLocaleLowerCase().startsWith(termLowerCase)
|
|
52
51
|
);
|
|
53
|
-
const categoryMatches = categoryList.filter(
|
|
52
|
+
const categoryMatches = categoryList.filter(
|
|
53
|
+
c => c && c.handle && c.handle.startsWith(termLowerCase)
|
|
54
|
+
);
|
|
54
55
|
|
|
55
56
|
// remove local matches from search results, add category matches
|
|
56
57
|
users = users.filter(u => !localMatches.find(lu => lu.uid === u.uid));
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
|
|
59
|
+
users = sortEntries(localMatches).concat(users).concat(sortEntries(categoryMatches));
|
|
59
60
|
|
|
60
61
|
// Add groups that start with the search term
|
|
61
|
-
const groupMentions = groupList.filter(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
const groupMentions = groupList.filter(
|
|
63
|
+
group => group.name.toLocaleLowerCase().startsWith(termLowerCase) ||
|
|
64
|
+
group.slug.startsWith(termLowerCase)
|
|
65
|
+
).sort((a, b) =>a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase() ? 1 : -1)
|
|
66
|
+
.map(group => group.name);
|
|
66
67
|
|
|
67
68
|
// Add group mentions at the bottom of dropdown
|
|
68
|
-
// mentions = mentions.concat(groupMentions);
|
|
69
|
-
|
|
70
69
|
callback([...users, ...groupMentions]);
|
|
71
70
|
});
|
|
72
71
|
});
|
|
@@ -86,6 +85,12 @@ $(document).ready(function () {
|
|
|
86
85
|
};
|
|
87
86
|
|
|
88
87
|
data.strategies.push(strategy);
|
|
88
|
+
data.options = {
|
|
89
|
+
...data.options,
|
|
90
|
+
...{
|
|
91
|
+
maxCount: 100,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
89
94
|
});
|
|
90
95
|
|
|
91
96
|
$(window).on('action:composer.loaded', function (ev, data) {
|
|
@@ -121,7 +126,7 @@ $(document).ready(function () {
|
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
function loadTopicUsers(element) {
|
|
124
|
-
require(['composer'
|
|
129
|
+
require(['composer'], function (composer) {
|
|
125
130
|
function findTid() {
|
|
126
131
|
const composerEl = element.parents('.composer').get(0);
|
|
127
132
|
if (composerEl) {
|
|
@@ -146,7 +151,7 @@ $(document).ready(function () {
|
|
|
146
151
|
tid: tid,
|
|
147
152
|
}, function (err, users) {
|
|
148
153
|
if (err) {
|
|
149
|
-
return
|
|
154
|
+
return showAlert('error', err);
|
|
150
155
|
}
|
|
151
156
|
localUserList = users;
|
|
152
157
|
});
|
|
@@ -154,14 +159,12 @@ $(document).ready(function () {
|
|
|
154
159
|
}
|
|
155
160
|
|
|
156
161
|
function loadGroupList() {
|
|
157
|
-
socket.emit('plugins.mentions.listGroups', function (err, groupNames) {
|
|
162
|
+
socket.emit('plugins.mentions.listGroups', async function (err, groupNames) {
|
|
158
163
|
if (err) {
|
|
159
|
-
|
|
160
|
-
alerts.error(err);
|
|
161
|
-
});
|
|
162
|
-
return;
|
|
164
|
+
return showAlert('error', err.message);
|
|
163
165
|
}
|
|
164
|
-
|
|
166
|
+
const s = await app.require('slugify');
|
|
167
|
+
groupList = groupNames.map(name => ({ name, slug: s(name) }));
|
|
165
168
|
});
|
|
166
169
|
}
|
|
167
170
|
|