nodebb-plugin-admin-chats 2.1.0 → 2.1.1
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/README.md +5 -0
- package/client.js +74 -0
- package/library.js +68 -0
- package/package.json +3 -3
- package/plugin.json +11 -0
package/README.md
CHANGED
|
@@ -18,6 +18,10 @@ A professional administration plugin for NodeBB that allows administrators to mo
|
|
|
18
18
|
* **ID**: `nodebb-plugin-admin-chats`
|
|
19
19
|
* **Compatibility**: NodeBB versions `^3.0.0` or `^4.0.0`
|
|
20
20
|
|
|
21
|
+
### Chat Route Behavior (`/chats`)
|
|
22
|
+
NodeBB exposes a core route at `/chats` (and `/chats/:roomId/:index?`) that redirects logged-in users to their personal chat page under `/user/:userslug/chats/...`.
|
|
23
|
+
This plugin also rewrites chat notification links to use `/user/:userslug/chats/...`, ensuring admins land on the correct user-scoped chat view when opening notifications.
|
|
24
|
+
|
|
21
25
|
### Implemented Hooks
|
|
22
26
|
The plugin utilizes several filters to elevate administrator permissions:
|
|
23
27
|
|
|
@@ -54,3 +58,4 @@ The plugin includes a `client.js` script that:
|
|
|
54
58
|
|
|
55
59
|
---
|
|
56
60
|
*Developed by [palmoni5](https://github.com/palmoni5/nodebb-plugin-admin-chats).*
|
|
61
|
+
|
package/client.js
CHANGED
|
@@ -160,6 +160,14 @@ $(document).ready(function() {
|
|
|
160
160
|
Chats._adminAllChatsPatched = true;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// Auto-open chat if roomId is specified in URL
|
|
164
|
+
const currentRoomId = ajaxify && ajaxify.data && ajaxify.data.roomId;
|
|
165
|
+
if (currentRoomId && !$('body').hasClass('chat-loaded')) {
|
|
166
|
+
setTimeout(function() {
|
|
167
|
+
Chats.switchChat(currentRoomId);
|
|
168
|
+
}, 100);
|
|
169
|
+
}
|
|
170
|
+
|
|
163
171
|
bindAdminRecentChatsInfiniteScroll();
|
|
164
172
|
});
|
|
165
173
|
}
|
|
@@ -471,6 +479,18 @@ $(document).ready(function() {
|
|
|
471
479
|
if (isAdminAllChatsPage()) {
|
|
472
480
|
patchForumChatsForAdminAll();
|
|
473
481
|
bindAdminRecentChatsInfiniteScroll();
|
|
482
|
+
|
|
483
|
+
// Auto-open chat if roomId is specified in URL and chat is not already loaded
|
|
484
|
+
const currentRoomId = ajaxify && ajaxify.data && ajaxify.data.roomId;
|
|
485
|
+
if (currentRoomId && !$('body').hasClass('chat-loaded')) {
|
|
486
|
+
setTimeout(function() {
|
|
487
|
+
require(['forum/chats'], function(Chats) {
|
|
488
|
+
if (Chats && Chats.switchChat) {
|
|
489
|
+
Chats.switchChat(currentRoomId);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}, 200);
|
|
493
|
+
}
|
|
474
494
|
}
|
|
475
495
|
|
|
476
496
|
const url = data && data.url ? data.url : '';
|
|
@@ -478,6 +498,11 @@ $(document).ready(function() {
|
|
|
478
498
|
refreshChatUi();
|
|
479
499
|
setTimeout(refreshChatUi, 500);
|
|
480
500
|
setTimeout(refreshChatUi, 1200);
|
|
501
|
+
|
|
502
|
+
// Handle chat navigation for internal URL changes
|
|
503
|
+
if (url.match(/^chats\/\d+/)) {
|
|
504
|
+
setTimeout(handleChatNavigation, 300);
|
|
505
|
+
}
|
|
481
506
|
}
|
|
482
507
|
});
|
|
483
508
|
|
|
@@ -485,6 +510,20 @@ $(document).ready(function() {
|
|
|
485
510
|
if (isAdminAllChatsPage()) {
|
|
486
511
|
patchForumChatsForAdminAll();
|
|
487
512
|
bindAdminRecentChatsInfiniteScroll();
|
|
513
|
+
|
|
514
|
+
// Auto-open chat if roomId is specified in URL and no chat is currently active
|
|
515
|
+
const currentRoomId = ajaxify && ajaxify.data && ajaxify.data.roomId;
|
|
516
|
+
const activeChatId = $('[component="chat/main-wrapper"]').attr('data-roomid');
|
|
517
|
+
|
|
518
|
+
if (currentRoomId && (!activeChatId || activeChatId !== String(currentRoomId))) {
|
|
519
|
+
setTimeout(function() {
|
|
520
|
+
require(['forum/chats'], function(Chats) {
|
|
521
|
+
if (Chats && Chats.switchChat) {
|
|
522
|
+
Chats.switchChat(currentRoomId);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
}, 100);
|
|
526
|
+
}
|
|
488
527
|
}
|
|
489
528
|
refreshChatUi();
|
|
490
529
|
setTimeout(refreshChatUi, 200);
|
|
@@ -499,6 +538,41 @@ $(document).ready(function() {
|
|
|
499
538
|
setTimeout(refreshChatUi, 0);
|
|
500
539
|
});
|
|
501
540
|
|
|
541
|
+
// Handle browser back/forward navigation
|
|
542
|
+
$(window).on('popstate', function() {
|
|
543
|
+
if (isAdminAllChatsPage()) {
|
|
544
|
+
setTimeout(handleChatNavigation, 100);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Handle URL changes for internal navigation
|
|
549
|
+
function handleChatNavigation() {
|
|
550
|
+
if (!isAdminAllChatsPage()) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Try to get roomId from multiple sources
|
|
555
|
+
let currentRoomId = ajaxify && ajaxify.data && ajaxify.data.roomId;
|
|
556
|
+
|
|
557
|
+
// If not found in ajaxify.data, try to extract from URL
|
|
558
|
+
if (!currentRoomId) {
|
|
559
|
+
const urlMatch = window.location.pathname.match(/\/chats\/(\d+)/);
|
|
560
|
+
if (urlMatch) {
|
|
561
|
+
currentRoomId = parseInt(urlMatch[1], 10);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const activeChatId = $('[component="chat/main-wrapper"]').attr('data-roomid');
|
|
566
|
+
|
|
567
|
+
if (currentRoomId && (!activeChatId || activeChatId !== String(currentRoomId))) {
|
|
568
|
+
require(['forum/chats'], function(Chats) {
|
|
569
|
+
if (Chats && Chats.switchChat) {
|
|
570
|
+
Chats.switchChat(currentRoomId);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
502
576
|
$(document).on('click', '.admin-chat-lock-toggle-item', async function(ev) {
|
|
503
577
|
ev.preventDefault();
|
|
504
578
|
|
package/library.js
CHANGED
|
@@ -43,6 +43,63 @@ plugin.init = async function (params) {
|
|
|
43
43
|
overrideCoreChatRedirect(params.controllers);
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
plugin.filterNotificationCreate = async function (data) {
|
|
47
|
+
if (!data || !data.data) {
|
|
48
|
+
return data;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const notification = data.data;
|
|
52
|
+
|
|
53
|
+
// Check if this is a chat notification with path like /chats/123
|
|
54
|
+
if (notification.path && notification.path.match(/^\/chats\/\d+/)) {
|
|
55
|
+
const chatMatch = notification.path.match(/^\/chats\/(\d+)(?:\/(\d+))?$/);
|
|
56
|
+
if (chatMatch && notification && notification.uid) {
|
|
57
|
+
const roomId = chatMatch[1];
|
|
58
|
+
const index = chatMatch[2];
|
|
59
|
+
|
|
60
|
+
// Get the user's slug to redirect to their personal chat page
|
|
61
|
+
const userSlug = await User.getUserField(notification.uid, 'userslug');
|
|
62
|
+
if (userSlug) {
|
|
63
|
+
notification.path = `/user/${userSlug}/chats/${roomId}${index ? `/${index}` : ''}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return data;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
plugin.filterUserNotificationsGetNotifications = async function (data) {
|
|
72
|
+
if (!data || !data.uid || !Array.isArray(data.notifications) || !data.notifications.length) {
|
|
73
|
+
return data;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const userSlug = await User.getUserField(data.uid, 'userslug');
|
|
77
|
+
if (!userSlug) {
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const userPathRegex = /\/user\/[^/]+\/chats\//;
|
|
82
|
+
const chatPathRegex = /\/chats\/(\d+)(?:\/(\d+))?(?=$|[/?#])/;
|
|
83
|
+
|
|
84
|
+
data.notifications.forEach((notification) => {
|
|
85
|
+
if (!notification || !notification.path) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (userPathRegex.test(notification.path)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const match = notification.path.match(chatPathRegex);
|
|
92
|
+
if (!match) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const roomId = match[1];
|
|
96
|
+
const index = match[2];
|
|
97
|
+
const replacement = `/user/${userSlug}/chats/${roomId}${index ? `/${index}` : ''}`;
|
|
98
|
+
notification.path = notification.path.replace(chatPathRegex, replacement);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return data;
|
|
102
|
+
};
|
|
46
103
|
plugin.addScripts = async function (data) {
|
|
47
104
|
let isHebrew = false;
|
|
48
105
|
|
|
@@ -393,6 +450,15 @@ function registerRoutes(app, router, middleware) {
|
|
|
393
450
|
|
|
394
451
|
const pageController = async (req, res, next) => {
|
|
395
452
|
try {
|
|
453
|
+
// If user is not admin, redirect to user-specific chat URL
|
|
454
|
+
if (req.uid && !await User.isAdministrator(req.uid)) {
|
|
455
|
+
const userSlug = await User.getUserField(req.uid, 'userslug');
|
|
456
|
+
if (userSlug && req.params.roomId) {
|
|
457
|
+
const redirectUrl = `${req.baseUrl || ''}/user/${userSlug}/chats/${req.params.roomId}${req.params.index ? `/${req.params.index}` : ''}`;
|
|
458
|
+
return res.redirect(redirectUrl);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
396
462
|
await renderAdminChatsPage(req, res, next);
|
|
397
463
|
} catch (err) {
|
|
398
464
|
next(err);
|
|
@@ -834,3 +900,5 @@ function getRoomId(payload) {
|
|
|
834
900
|
|
|
835
901
|
module.exports = plugin;
|
|
836
902
|
|
|
903
|
+
|
|
904
|
+
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-admin-chats",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "A plugin that allows the administrator to view and manage user chats.",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"lodash": "^4.17.
|
|
7
|
+
"lodash": "^4.17.21"
|
|
8
8
|
},
|
|
9
9
|
"nbbpm": {
|
|
10
|
-
"compatibility": "^
|
|
10
|
+
"compatibility": "^4.0.0"
|
|
11
11
|
}
|
|
12
12
|
}
|
package/plugin.json
CHANGED
|
@@ -63,6 +63,17 @@
|
|
|
63
63
|
{
|
|
64
64
|
"hook": "filter:scripts.get",
|
|
65
65
|
"method": "addScripts"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"hook": "filter:notifications.create",
|
|
69
|
+
"method": "filterNotificationCreate"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"hook": "filter:user.notifications.getNotifications",
|
|
73
|
+
"method": "filterUserNotificationsGetNotifications"
|
|
66
74
|
}
|
|
67
75
|
]
|
|
68
76
|
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|