nodebb-plugin-mentions 4.8.10 → 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/library.js +81 -43
- package/package.json +3 -3
- package/static/admin.js +1 -19
- package/static/autofill.js +29 -26
- package/test/index.js +18 -5
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,12 +26,13 @@ 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
|
|
|
33
33
|
const parts = {
|
|
34
34
|
before: '(?<=(^|\\P{L}))', // a single unicode non-letter character or start of line
|
|
35
|
-
main: '(@[\\p{L}\\d\\-_.@]+)', // unicode letters, numbers, dashes, underscores, or periods
|
|
35
|
+
main: '(@[\\p{L}\\d\\-_.@]+(?<![.-]))', // unicode letters, numbers, dashes, underscores, or periods, negative lookbehind to guard against periods/dashes at end
|
|
36
36
|
after: '((?=\\b)(?=[^-])|(?=[^\\p{L}\\d\\-_.@])|$)', // used to figure out where latin mentions end
|
|
37
37
|
};
|
|
38
38
|
const regex = RegExp(`${parts.before}${parts.main}`, 'gu');
|
|
@@ -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,
|
|
@@ -434,6 +438,7 @@ Mentions.parseRaw = async (content, type = 'default') => {
|
|
|
434
438
|
});
|
|
435
439
|
|
|
436
440
|
// Convert matches to anchor html
|
|
441
|
+
let replacements = new Set();
|
|
437
442
|
await Promise.all(matches.map(async (match) => {
|
|
438
443
|
const slug = slugify(match.slice(1));
|
|
439
444
|
match = removePunctuationSuffix(match);
|
|
@@ -475,6 +480,16 @@ Mentions.parseRaw = async (content, type = 'default') => {
|
|
|
475
480
|
}
|
|
476
481
|
}
|
|
477
482
|
|
|
483
|
+
replacements.add({ match, url, user, mentionType });
|
|
484
|
+
}
|
|
485
|
+
}));
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
replacements = Array.from(replacements)
|
|
489
|
+
.sort((a, b) => {
|
|
490
|
+
return b.user.userslug.length - a.user.userslug.length;
|
|
491
|
+
})
|
|
492
|
+
.forEach(({ match, url, user, mentionType }) => {
|
|
478
493
|
const regex = isLatinMention.test(match) ?
|
|
479
494
|
RegExp(`${parts.before}${match}${parts.after}`, 'gu') :
|
|
480
495
|
RegExp(`${parts.before}${match}`, 'gu');
|
|
@@ -492,7 +507,7 @@ Mentions.parseRaw = async (content, type = 'default') => {
|
|
|
492
507
|
const plain = match.slice(0, atIndex);
|
|
493
508
|
match = match.slice(atIndex + 1);
|
|
494
509
|
if (user && user.uid) {
|
|
495
|
-
switch (
|
|
510
|
+
switch (settings.display) {
|
|
496
511
|
case 'fullname':
|
|
497
512
|
match = user.displayname || match;
|
|
498
513
|
break;
|
|
@@ -504,16 +519,15 @@ Mentions.parseRaw = async (content, type = 'default') => {
|
|
|
504
519
|
|
|
505
520
|
let str;
|
|
506
521
|
if (type === 'markdown') {
|
|
507
|
-
str = `[${!
|
|
522
|
+
str = `[${!settings.display ? '@' : ''}${match}](${nconf.get('url')}${url})`;
|
|
508
523
|
} else {
|
|
509
|
-
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>`;
|
|
510
525
|
}
|
|
511
526
|
|
|
512
527
|
return plain + str;
|
|
513
528
|
});
|
|
514
529
|
});
|
|
515
|
-
}
|
|
516
|
-
}));
|
|
530
|
+
});
|
|
517
531
|
|
|
518
532
|
return splitContent.join('');
|
|
519
533
|
};
|
|
@@ -553,17 +567,18 @@ async function filterPrivilegedUids(uids, cid, toPid) {
|
|
|
553
567
|
}
|
|
554
568
|
|
|
555
569
|
async function filterDisallowedFullnames(users) {
|
|
570
|
+
if (!users.length) return [];
|
|
556
571
|
const userSettings = await User.getMultipleUserSettings(users.map(user => user.uid));
|
|
557
|
-
return users.filter((user, index) => userSettings[index].showfullname);
|
|
572
|
+
return users.filter((user, index) => userSettings[index] && userSettings[index].showfullname);
|
|
558
573
|
}
|
|
559
574
|
|
|
560
575
|
async function stripDisallowedFullnames(users) {
|
|
576
|
+
if (!users.length) return [];
|
|
561
577
|
const userSettings = await User.getMultipleUserSettings(users.map(user => user.uid));
|
|
562
|
-
|
|
563
|
-
if (!userSettings[index].showfullname) {
|
|
578
|
+
users.forEach((user, index) => {
|
|
579
|
+
if (user && userSettings[index] && !userSettings[index].showfullname) {
|
|
564
580
|
user.fullname = null;
|
|
565
581
|
}
|
|
566
|
-
return user;
|
|
567
582
|
});
|
|
568
583
|
}
|
|
569
584
|
|
|
@@ -578,39 +593,62 @@ SocketPlugins.mentions.getTopicUsers = async (socket, data) => {
|
|
|
578
593
|
if (Meta.config.hideFullname) {
|
|
579
594
|
return users;
|
|
580
595
|
}
|
|
581
|
-
|
|
596
|
+
await stripDisallowedFullnames(users);
|
|
597
|
+
return users;
|
|
582
598
|
};
|
|
583
599
|
|
|
584
600
|
SocketPlugins.mentions.listGroups = async function () {
|
|
585
|
-
|
|
601
|
+
const settings = await getSettings();
|
|
602
|
+
if (settings.autofillGroups === 'off') {
|
|
586
603
|
return [];
|
|
587
604
|
}
|
|
588
605
|
|
|
589
606
|
const groups = await Groups.getGroups('groups:visible:createtime', 0, -1);
|
|
590
|
-
const noMentionGroups = getNoMentionGroups();
|
|
591
|
-
|
|
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;
|
|
592
612
|
};
|
|
593
613
|
|
|
594
614
|
SocketPlugins.mentions.userSearch = async (socket, data) => {
|
|
595
|
-
|
|
596
|
-
|
|
615
|
+
const allowed = await privileges.global.can('search:users', socket.uid);
|
|
616
|
+
if (!allowed) {
|
|
617
|
+
throw new Error('[[error:no-privileges]]');
|
|
618
|
+
}
|
|
597
619
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
+
]);
|
|
636
|
+
|
|
637
|
+
let { users } = byUsername;
|
|
601
638
|
|
|
602
|
-
|
|
603
|
-
let { users: fullnameUsers } = await api.users.search(socket, { query: data.query, searchBy: 'fullname' });
|
|
639
|
+
const [fullnameUsers] = await Promise.all([
|
|
604
640
|
// Hide results of users that do not allow their full name to be visible (prevents "enumeration attack")
|
|
605
|
-
|
|
641
|
+
filterDisallowedFullnames(byFullname.users),
|
|
642
|
+
// Strip fullnames of users that do not allow their full name to be visible
|
|
643
|
+
stripDisallowedFullnames(users),
|
|
644
|
+
]);
|
|
606
645
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
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);
|
|
612
650
|
|
|
613
|
-
if (
|
|
651
|
+
if (settings.privilegedDirectReplies === 'on') {
|
|
614
652
|
if (data.composerObj) {
|
|
615
653
|
const cid = Topics.getTopicField(data.composerObj.tid, 'cid');
|
|
616
654
|
const filteredUids = await filterPrivilegedUids(users.map(userObj => userObj.uid), cid, data.composerObj.toPid);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-mentions",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.12",
|
|
4
4
|
"description": "NodeBB Plugin that allows users to mention other users by prepending an '@' sign to their username",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"repository": {
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"validator": "^13.0.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"eslint": "^
|
|
34
|
-
"eslint-config-nodebb": "^
|
|
33
|
+
"eslint": "^10.0.0",
|
|
34
|
+
"eslint-config-nodebb": "^2.0.0",
|
|
35
35
|
"eslint-plugin-import": "^2.31.0",
|
|
36
36
|
"mocha": "11.7.5"
|
|
37
37
|
}
|
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
|
|
package/test/index.js
CHANGED
|
@@ -38,7 +38,7 @@ describe('regex', () => {
|
|
|
38
38
|
it('should match a mention in all test strings', () => {
|
|
39
39
|
const matches = string.match(matcher);
|
|
40
40
|
assert(matches, `@testUser was not found in this string: ${string}`);
|
|
41
|
-
assert.
|
|
41
|
+
assert(['@testuser', '@testuser.'].includes(slugify(matches[0])));
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
44
|
|
|
@@ -163,7 +163,7 @@ describe('parser', () => {
|
|
|
163
163
|
beforeEach(async () => {
|
|
164
164
|
slug = utils.generateUUID().slice(0, 10);
|
|
165
165
|
uid = await user.create({ username: slug });
|
|
166
|
-
emailUid = await user.create({ username: `${slug}@test.nodebb.org` });
|
|
166
|
+
// emailUid = await user.create({ username: `${slug}@test.nodebb.org` });
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
it('should properly parse both users even if one user\'s username is a subset of the other', async () => {
|
|
@@ -172,7 +172,7 @@ describe('parser', () => {
|
|
|
172
172
|
|
|
173
173
|
const html = await main.parseRaw(md);
|
|
174
174
|
|
|
175
|
-
assert.strictEqual(html, `This sentence contains two mentions: <a class="plugin-mentions-user plugin-mentions-a" href="
|
|
175
|
+
assert.strictEqual(html, `This sentence contains two mentions: <a class="plugin-mentions-user plugin-mentions-a" href="/user/${slug}" aria-label="Profile: ${slug}">@<bdi>${slug}</bdi></a> and <a class="plugin-mentions-user plugin-mentions-a" href="/user/${slug}-two" aria-label="Profile: ${slug}-two">@<bdi>${slug}-two</bdi></a>`);
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
strings.forEach((string) => {
|
|
@@ -180,8 +180,9 @@ describe('parser', () => {
|
|
|
180
180
|
const index = string.indexOf('@testUser');
|
|
181
181
|
let check = string;
|
|
182
182
|
if (!index || string[index - 1] !== '>') {
|
|
183
|
-
check = string.replace(/@testUser
|
|
184
|
-
|
|
183
|
+
check = string.replace(/@testUser\.?/g, `<a class="plugin-mentions-user plugin-mentions-a" href="/user/${slug}" aria-label="Profile: ${slug}">@<bdi>${slug}</bdi></a>`);
|
|
184
|
+
// check = string.replace(/@testUser/g, `<a class="plugin-mentions-user plugin-mentions-a" href="http://127.0.0.1:4567/uid/${uid}">@${slug}</a>`);
|
|
185
|
+
string = string.replace(/testUser\.?/g, slug);
|
|
185
186
|
}
|
|
186
187
|
const html = await main.parseRaw(string);
|
|
187
188
|
|
|
@@ -205,3 +206,15 @@ describe('parser', () => {
|
|
|
205
206
|
});
|
|
206
207
|
});
|
|
207
208
|
});
|
|
209
|
+
|
|
210
|
+
describe('.getMatches() edge cases', () => {
|
|
211
|
+
before(async function () {
|
|
212
|
+
this.slug = slugify(utils.generateUUID().slice(0, 8));
|
|
213
|
+
this.uid = await user.create({ username: this.slug });
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should match a mention at the end of a sentence', async function () {
|
|
217
|
+
const matches = await main.getMatches(`test sentence @${this.slug}.`);
|
|
218
|
+
console.log(matches);
|
|
219
|
+
});
|
|
220
|
+
});
|