nodebb-plugin-admin-chats 2.1.1 → 3.0.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 +18 -5
- package/client.js +224 -28
- package/library.js +204 -36
- package/package.json +1 -4
- package/plugin.json +6 -4
- package/public/languages/en-GB/admin-chats.json +3 -1
- package/public/languages/he/admin-chats.json +3 -1
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@ A professional administration plugin for NodeBB that allows administrators to mo
|
|
|
4
4
|
|
|
5
5
|
## Main Features
|
|
6
6
|
|
|
7
|
-
* **Global Chat Access**: Administrators can view any private or group conversation.
|
|
8
|
-
* **Management Privileges**:
|
|
7
|
+
* **Global Chat Access**: Administrators and users with chat view permission can view any private or group conversation.
|
|
8
|
+
* **Management Privileges**: Administrators and users with chat management permission can edit or delete any message in any room.
|
|
9
9
|
* **Admin Room Locking**: Administrators can lock any chat room so only admins can continue replying.
|
|
10
10
|
* **Profile Integration**: Adds a "View Chats" button to the user profile menu for administrators to quickly audit a user's interactions.
|
|
11
11
|
* **Non-Intrusive Monitoring**: Admins can load and view room history without being added as permanent members, maintaining a clean member list.
|
|
@@ -16,7 +16,13 @@ A professional administration plugin for NodeBB that allows administrators to mo
|
|
|
16
16
|
### Plugin Information
|
|
17
17
|
* **Name**: Super Admin Chat Control
|
|
18
18
|
* **ID**: `nodebb-plugin-admin-chats`
|
|
19
|
-
* **Compatibility**: NodeBB
|
|
19
|
+
* **Compatibility**: NodeBB version `^4.0.0`
|
|
20
|
+
|
|
21
|
+
### Permissions
|
|
22
|
+
The plugin exposes two global permissions in the ACP:
|
|
23
|
+
|
|
24
|
+
* `admin-chats:view`: View any chat room and load history (read-only access).
|
|
25
|
+
* `admin-chats:manage`: Manage any chat room (edit/delete messages, lock/unlock rooms, and other moderator-level actions).
|
|
20
26
|
|
|
21
27
|
### Chat Route Behavior (`/chats`)
|
|
22
28
|
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/...`.
|
|
@@ -41,6 +47,14 @@ The plugin overrides core messaging functions to ensure full administrative cont
|
|
|
41
47
|
|
|
42
48
|
## Installation
|
|
43
49
|
|
|
50
|
+
### Option 1: Via Admin Control Panel (Recommended)
|
|
51
|
+
1. Navigate to the NodeBB Admin Control Panel (ACP).
|
|
52
|
+
2. Go to **Extend** → **Plugins**.
|
|
53
|
+
3. Search for "nodebb-plugin-admin-chats".
|
|
54
|
+
4. Click **Install**.
|
|
55
|
+
5. Activate the plugin and restart NodeBB.
|
|
56
|
+
|
|
57
|
+
### Option 2: Via Terminal
|
|
44
58
|
1. Install the plugin via terminal:
|
|
45
59
|
```bash
|
|
46
60
|
npm install nodebb-plugin-admin-chats
|
|
@@ -57,5 +71,4 @@ The plugin includes a `client.js` script that:
|
|
|
57
71
|
* Cleans up the chat UI empty states for a better admin experience.
|
|
58
72
|
|
|
59
73
|
---
|
|
60
|
-
*Developed by [palmoni5](https://github.com/palmoni5/nodebb-plugin-admin-chats).*
|
|
61
|
-
|
|
74
|
+
*Developed by [palmoni5](https://github.com/palmoni5/nodebb-plugin-admin-chats).*
|
package/client.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
$(document).ready(function() {
|
|
2
|
+
syncAdminAccessFromAjaxify();
|
|
3
|
+
fetchAdminPrivileges();
|
|
2
4
|
const LOCK_PREFIX = '[admin-chat-lock]';
|
|
3
5
|
const roomStateCache = new Map();
|
|
4
6
|
const TEXT = {
|
|
@@ -25,12 +27,106 @@ $(document).ready(function() {
|
|
|
25
27
|
function isEnglishSystem() {
|
|
26
28
|
return $('html').attr('lang') && $('html').attr('lang').startsWith('en');
|
|
27
29
|
}
|
|
28
|
-
|
|
29
30
|
function t(key) {
|
|
30
31
|
const dict = isEnglishSystem() ? TEXT.en : TEXT.he;
|
|
31
32
|
return dict[key];
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
function hasAdminChatsAccess() {
|
|
36
|
+
return !!((ajaxify && ajaxify.data && (ajaxify.data.adminChatsAccess || ajaxify.data.adminChatsCanManage)) || window.adminChatsAccess || window.adminChatsCanManage || (app.user && app.user.isAdmin));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isAdminUser() {
|
|
40
|
+
return !!((ajaxify && ajaxify.data && ajaxify.data.adminChatsIsAdmin) || window.adminChatsIsAdmin || (app.user && app.user.isAdmin));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function fetchAdminPrivileges() {
|
|
44
|
+
if (!hasAdminChatsAccess() || window.adminChatsPrivilegesPending) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
window.adminChatsPrivilegesPending = true;
|
|
49
|
+
try {
|
|
50
|
+
const response = await fetch(`${config.relative_path || ''}/api/admin-chats/privileges`, {
|
|
51
|
+
headers: {
|
|
52
|
+
'x-csrf-token': config.csrf_token,
|
|
53
|
+
},
|
|
54
|
+
credentials: 'same-origin',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const payload = await response.json();
|
|
62
|
+
if (payload && typeof payload.adminChatsAccess === 'boolean') {
|
|
63
|
+
window.adminChatsAccess = payload.adminChatsAccess;
|
|
64
|
+
}
|
|
65
|
+
if (payload && typeof payload.adminChatsCanManage === 'boolean') {
|
|
66
|
+
window.adminChatsCanManage = payload.adminChatsCanManage;
|
|
67
|
+
}
|
|
68
|
+
if (payload && typeof payload.adminChatsIsAdmin === 'boolean') {
|
|
69
|
+
window.adminChatsIsAdmin = payload.adminChatsIsAdmin;
|
|
70
|
+
}
|
|
71
|
+
} catch (err) {
|
|
72
|
+
// ignore
|
|
73
|
+
} finally {
|
|
74
|
+
window.adminChatsPrivilegesPending = false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function syncAdminAccessFromAjaxify() {
|
|
79
|
+
if (!ajaxify || !ajaxify.data) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (typeof ajaxify.data.adminChatsAccess === 'boolean') {
|
|
83
|
+
window.adminChatsAccess = ajaxify.data.adminChatsAccess;
|
|
84
|
+
}
|
|
85
|
+
if (typeof ajaxify.data.adminChatsCanManage === 'boolean') {
|
|
86
|
+
window.adminChatsCanManage = ajaxify.data.adminChatsCanManage;
|
|
87
|
+
}
|
|
88
|
+
if (typeof ajaxify.data.adminChatsIsAdmin === 'boolean') {
|
|
89
|
+
window.adminChatsIsAdmin = ajaxify.data.adminChatsIsAdmin;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function canManageChats() {
|
|
94
|
+
return !!((ajaxify && ajaxify.data && ajaxify.data.adminChatsCanManage) || window.adminChatsCanManage || isAdminUser());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let adminChatsAccessProbe = null;
|
|
98
|
+
|
|
99
|
+
function resolveAdminChatsAccess() {
|
|
100
|
+
if (hasAdminChatsAccess()) {
|
|
101
|
+
return Promise.resolve(true);
|
|
102
|
+
}
|
|
103
|
+
if (window.adminChatsAccessChecked) {
|
|
104
|
+
return Promise.resolve(false);
|
|
105
|
+
}
|
|
106
|
+
if (adminChatsAccessProbe) {
|
|
107
|
+
return adminChatsAccessProbe;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
adminChatsAccessProbe = fetch(`${config.relative_path || ''}/api/admin-chats?start=0`, {
|
|
111
|
+
headers: {
|
|
112
|
+
'x-csrf-token': config.csrf_token,
|
|
113
|
+
},
|
|
114
|
+
credentials: 'same-origin',
|
|
115
|
+
}).then((response) => {
|
|
116
|
+
const ok = response && response.ok;
|
|
117
|
+
window.adminChatsAccess = ok;
|
|
118
|
+
return ok;
|
|
119
|
+
}).catch(() => {
|
|
120
|
+
window.adminChatsAccess = false;
|
|
121
|
+
return false;
|
|
122
|
+
}).finally(() => {
|
|
123
|
+
window.adminChatsAccessChecked = true;
|
|
124
|
+
adminChatsAccessProbe = null;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return adminChatsAccessProbe;
|
|
128
|
+
}
|
|
129
|
+
|
|
34
130
|
// Get lockedAction message from server translations only (based on forum settings)
|
|
35
131
|
function getLockedActionMessage() {
|
|
36
132
|
if (window.adminChatsTranslations && window.adminChatsTranslations.lockedAction) {
|
|
@@ -173,7 +269,7 @@ $(document).ready(function() {
|
|
|
173
269
|
}
|
|
174
270
|
|
|
175
271
|
function replaceAdminEmptyStateText() {
|
|
176
|
-
if (!
|
|
272
|
+
if (!hasAdminChatsAccess()) {
|
|
177
273
|
return;
|
|
178
274
|
}
|
|
179
275
|
|
|
@@ -247,19 +343,50 @@ $(document).ready(function() {
|
|
|
247
343
|
credentials: 'same-origin',
|
|
248
344
|
});
|
|
249
345
|
if (!response.ok) {
|
|
346
|
+
if (hasAdminChatsAccess() && !isAdminAllChatsPage()) {
|
|
347
|
+
const adminResponse = await fetch(`${config.relative_path || ''}/api/admin-chats/page/${roomId}`, {
|
|
348
|
+
headers: {
|
|
349
|
+
'x-csrf-token': config.csrf_token,
|
|
350
|
+
},
|
|
351
|
+
credentials: 'same-origin',
|
|
352
|
+
});
|
|
353
|
+
if (!adminResponse.ok) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
const adminPayload = await adminResponse.json();
|
|
357
|
+
const adminRoom = adminPayload && adminPayload.roomId ? adminPayload : (adminPayload && adminPayload.room ? adminPayload.room : null);
|
|
358
|
+
if (adminRoom) {
|
|
359
|
+
setCachedRoomData(roomId, adminRoom);
|
|
360
|
+
}
|
|
361
|
+
return adminRoom;
|
|
362
|
+
}
|
|
250
363
|
return null;
|
|
251
364
|
}
|
|
252
365
|
|
|
253
366
|
const payload = await response.json();
|
|
254
367
|
const roomData = payload && payload.roomId ? payload : (payload && payload.room ? payload.room : null);
|
|
368
|
+
if (roomData && !roomData.adminChatLock && hasAdminChatsAccess() && !isAdminAllChatsPage()) {
|
|
369
|
+
const adminResponse = await fetch(`${config.relative_path || ''}/api/admin-chats/page/${roomId}`, {
|
|
370
|
+
headers: {
|
|
371
|
+
'x-csrf-token': config.csrf_token,
|
|
372
|
+
},
|
|
373
|
+
credentials: 'same-origin',
|
|
374
|
+
});
|
|
375
|
+
if (adminResponse.ok) {
|
|
376
|
+
const adminPayload = await adminResponse.json();
|
|
377
|
+
const adminRoom = adminPayload && adminPayload.roomId ? adminPayload : (adminPayload && adminPayload.room ? adminPayload.room : null);
|
|
378
|
+
if (adminRoom && adminRoom.adminChatLock) {
|
|
379
|
+
roomData.adminChatLock = adminRoom.adminChatLock;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
255
383
|
if (roomData) {
|
|
256
384
|
setCachedRoomData(roomId, roomData);
|
|
257
385
|
}
|
|
258
386
|
return roomData;
|
|
259
387
|
}
|
|
260
|
-
|
|
261
388
|
function isLockedForUser(roomData) {
|
|
262
|
-
return !!(roomData && roomData.adminChatLock && roomData.adminChatLock.isLocked && !(
|
|
389
|
+
return !!(roomData && roomData.adminChatLock && roomData.adminChatLock.isLocked && !canManageChats());
|
|
263
390
|
}
|
|
264
391
|
|
|
265
392
|
function setComposerHidden($window, hidden) {
|
|
@@ -314,6 +441,15 @@ $(document).ready(function() {
|
|
|
314
441
|
.toggleClass('hidden', hidden);
|
|
315
442
|
}
|
|
316
443
|
|
|
444
|
+
function updateDeleteRestoreVisibility($window) {
|
|
445
|
+
$window.find('[component="chat/message"]').each(function() {
|
|
446
|
+
const $message = $(this);
|
|
447
|
+
const isDeleted = $message.hasClass('deleted');
|
|
448
|
+
$message.find('[data-action="delete"]').toggleClass('hidden', isDeleted).toggle(!isDeleted);
|
|
449
|
+
$message.find('[data-action="restore"]').toggleClass('hidden', !isDeleted).toggle(isDeleted);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
317
453
|
function normalizeLockMessages($window) {
|
|
318
454
|
$window.find('[component="chat/system-message"] > div').each(function() {
|
|
319
455
|
const $el = $(this);
|
|
@@ -336,7 +472,7 @@ $(document).ready(function() {
|
|
|
336
472
|
function renderAdminLockControl($window, roomData) {
|
|
337
473
|
$window.find('.admin-chat-lock-toggle-item, .admin-chat-lock-divider, .admin-chat-lock-item-wrap').remove();
|
|
338
474
|
|
|
339
|
-
if (!
|
|
475
|
+
if (!canManageChats() || !roomData || !roomData.roomId) {
|
|
340
476
|
return;
|
|
341
477
|
}
|
|
342
478
|
|
|
@@ -382,6 +518,7 @@ $(document).ready(function() {
|
|
|
382
518
|
const hidden = isLockedForUser(roomData);
|
|
383
519
|
setComposerHidden($window, hidden);
|
|
384
520
|
updateLockedActionVisibility($window, hidden);
|
|
521
|
+
updateDeleteRestoreVisibility($window);
|
|
385
522
|
renderLockBanner($window, hidden);
|
|
386
523
|
renderAdminLockControl($window, roomData);
|
|
387
524
|
normalizeLockMessages($window);
|
|
@@ -451,30 +588,73 @@ $(document).ready(function() {
|
|
|
451
588
|
return await response.json();
|
|
452
589
|
}
|
|
453
590
|
|
|
454
|
-
|
|
455
|
-
const
|
|
591
|
+
async function injectProfileChatsMenu() {
|
|
592
|
+
const allowed = await resolveAdminChatsAccess();
|
|
593
|
+
if (!allowed) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
const isUserProfile = $(".account").length && window.location.pathname.includes("/user/");
|
|
598
|
+
if (!isUserProfile) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
456
601
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
602
|
+
const userSlug = (ajaxify && ajaxify.data && (ajaxify.data.userslug || (ajaxify.data.user && ajaxify.data.user.userslug))) || null;
|
|
603
|
+
if (!userSlug) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const relativePath = config.relative_path || "";
|
|
608
|
+
const menuItemHtml = `
|
|
609
|
+
<li role="presentation" class="admin-chats-profile-link">
|
|
610
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="${relativePath}/user/${userSlug}/chats" role="menuitem">
|
|
611
|
+
<i class="far fa-fw fa-comments"></i>
|
|
612
|
+
<span>${t("viewChats")}</span>
|
|
613
|
+
</a>
|
|
614
|
+
</li>
|
|
615
|
+
`;
|
|
616
|
+
|
|
617
|
+
let menu = $(".account-sub-links");
|
|
618
|
+
if (!menu.length) {
|
|
619
|
+
const container = $(".account .flex-shrink-0.d-flex.gap-1").first();
|
|
620
|
+
const fallbackContainer = container.length ? container : $(".account .flex-shrink-0").first();
|
|
621
|
+
|
|
622
|
+
if (fallbackContainer.length) {
|
|
623
|
+
const menuHtml = `
|
|
624
|
+
<div class="btn-group bottom-sheet admin-chats-privileges-menu">
|
|
625
|
+
<button type="button" class="btn btn-light dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
626
|
+
<i class="fa fa-gear fa-fw"></i>
|
|
627
|
+
</button>
|
|
628
|
+
<ul class="dropdown-menu dropdown-menu-end p-1 text-sm account-sub-links" role="menu"></ul>
|
|
629
|
+
</div>
|
|
470
630
|
`;
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
631
|
+
fallbackContainer.append(menuHtml);
|
|
632
|
+
menu = fallbackContainer.find(".account-sub-links").last();
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (menu.length) {
|
|
637
|
+
menu.find(".admin-chats-profile-link").remove();
|
|
638
|
+
menu.find(".admin-chats-profile-divider").remove();
|
|
639
|
+
menu.find(`a[href*="/user/${userSlug}/chats"]`).parent().remove();
|
|
640
|
+
menu.prepend(menuItemHtml);
|
|
641
|
+
|
|
642
|
+
const hasOtherItems = menu.find("li").not(".admin-chats-profile-link").length > 0;
|
|
643
|
+
if (hasOtherItems) {
|
|
644
|
+
menu.find(".admin-chats-profile-link").after('<li role="presentation" class="dropdown-divider admin-chats-profile-divider"></li>');
|
|
476
645
|
}
|
|
477
646
|
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
$(window).on('action:ajaxify.end', function(ev, data) {
|
|
650
|
+
syncAdminAccessFromAjaxify();
|
|
651
|
+
fetchAdminPrivileges();
|
|
652
|
+
const templateName = ajaxify && ajaxify.data && ajaxify.data.template ? ajaxify.data.template.name : '';
|
|
653
|
+
|
|
654
|
+
if (templateName.startsWith('account/')) {
|
|
655
|
+
injectProfileChatsMenu();
|
|
656
|
+
setTimeout(injectProfileChatsMenu, 200);
|
|
657
|
+
}
|
|
478
658
|
|
|
479
659
|
if (isAdminAllChatsPage()) {
|
|
480
660
|
patchForumChatsForAdminAll();
|
|
@@ -538,6 +718,13 @@ $(document).ready(function() {
|
|
|
538
718
|
setTimeout(refreshChatUi, 0);
|
|
539
719
|
});
|
|
540
720
|
|
|
721
|
+
if (window.socket) {
|
|
722
|
+
socket.removeListener('event:chats.delete', refreshChatUi);
|
|
723
|
+
socket.removeListener('event:chats.restore', refreshChatUi);
|
|
724
|
+
socket.on('event:chats.delete', function() { setTimeout(refreshChatUi, 0); });
|
|
725
|
+
socket.on('event:chats.restore', function() { setTimeout(refreshChatUi, 0); });
|
|
726
|
+
}
|
|
727
|
+
|
|
541
728
|
// Handle browser back/forward navigation
|
|
542
729
|
$(window).on('popstate', function() {
|
|
543
730
|
if (isAdminAllChatsPage()) {
|
|
@@ -573,6 +760,16 @@ $(document).ready(function() {
|
|
|
573
760
|
}
|
|
574
761
|
}
|
|
575
762
|
|
|
763
|
+
$(document).on('click', '[data-action="delete"], [data-action="restore"]', function() {
|
|
764
|
+
const $window = $(this).closest('[component="chat/message/window"]');
|
|
765
|
+
if (!$window.length) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
setTimeout(function() {
|
|
769
|
+
updateDeleteRestoreVisibility($window);
|
|
770
|
+
}, 50);
|
|
771
|
+
});
|
|
772
|
+
|
|
576
773
|
$(document).on('click', '.admin-chat-lock-toggle-item', async function(ev) {
|
|
577
774
|
ev.preventDefault();
|
|
578
775
|
|
|
@@ -591,7 +788,7 @@ $(document).ready(function() {
|
|
|
591
788
|
const currentRoom = ajaxify && ajaxify.data && (ajaxify.data.room || ajaxify.data);
|
|
592
789
|
if (currentRoom && parseInt(currentRoom.roomId, 10) === roomId) {
|
|
593
790
|
currentRoom.adminChatLock = result.lockData;
|
|
594
|
-
currentRoom.canReply = !result.lockData.isLocked || (
|
|
791
|
+
currentRoom.canReply = !result.lockData.isLocked || canManageChats();
|
|
595
792
|
currentRoom.showUserInput = currentRoom.canReply;
|
|
596
793
|
if (ajaxify.data.room) {
|
|
597
794
|
ajaxify.data.room.adminChatLock = result.lockData;
|
|
@@ -602,7 +799,7 @@ $(document).ready(function() {
|
|
|
602
799
|
|
|
603
800
|
const cached = getCachedRoomData(roomId) || { roomId };
|
|
604
801
|
cached.adminChatLock = result.lockData;
|
|
605
|
-
cached.canReply = !result.lockData.isLocked || (
|
|
802
|
+
cached.canReply = !result.lockData.isLocked || canManageChats();
|
|
606
803
|
cached.showUserInput = cached.canReply;
|
|
607
804
|
setCachedRoomData(roomId, cached);
|
|
608
805
|
await refreshChatUi();
|
|
@@ -626,4 +823,3 @@ $(document).ready(function() {
|
|
|
626
823
|
app.alertError._adminChatsWrapped = true;
|
|
627
824
|
}
|
|
628
825
|
});
|
|
629
|
-
|
package/library.js
CHANGED
|
@@ -11,6 +11,8 @@ const helpers = require.main.require('./src/controllers/helpers');
|
|
|
11
11
|
const plugin = {};
|
|
12
12
|
const LOCK_PREFIX = '[admin-chat-lock]';
|
|
13
13
|
const LOCKED_MESSAGE_TEXT = '🔒 חדר זה ננעל ע"י המנהלים.';
|
|
14
|
+
const ADMIN_CHATS_PRIVILEGE = 'admin-chats:view';
|
|
15
|
+
const ADMIN_CHATS_MANAGE_PRIVILEGE = 'admin-chats:manage';
|
|
14
16
|
|
|
15
17
|
async function getLockedActionMessage(uid) {
|
|
16
18
|
try {
|
|
@@ -36,12 +38,63 @@ async function getLockedActionMessage(uid) {
|
|
|
36
38
|
const ADMIN_CHAT_PAGE_SIZE = 30;
|
|
37
39
|
const ADMIN_CHAT_SCAN_SIZE = 100;
|
|
38
40
|
|
|
41
|
+
async function canAccessAdminChats(uid) {
|
|
42
|
+
if (!uid) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const [isAdmin, canView, canManage] = await Promise.all([
|
|
47
|
+
User.isAdministrator(uid),
|
|
48
|
+
privileges.global.can(ADMIN_CHATS_PRIVILEGE, uid),
|
|
49
|
+
privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, uid),
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
return isAdmin || canView || canManage;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function canManageAdminChats(uid) {
|
|
56
|
+
if (!uid) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const [isAdmin, canManage] = await Promise.all([
|
|
61
|
+
User.isAdministrator(uid),
|
|
62
|
+
privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, uid),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
return isAdmin || canManage;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function isUserMemberOfRoom(uid, roomId) {
|
|
69
|
+
if (!uid || !roomId) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return await db.isSortedSetMember(`chat:room:${roomId}:uids`, uid);
|
|
74
|
+
}
|
|
39
75
|
plugin.init = async function (params) {
|
|
40
76
|
registerRoutes(params.app, params.router, params.middleware);
|
|
41
77
|
overrideMessagingFunctions();
|
|
42
78
|
overrideChatsApi();
|
|
43
79
|
overrideCoreChatRedirect(params.controllers);
|
|
44
80
|
};
|
|
81
|
+
plugin.registerPrivileges = async function (data) {
|
|
82
|
+
if (!data || !data.privileges || typeof data.privileges.set !== 'function') {
|
|
83
|
+
return data;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
data.privileges.set(ADMIN_CHATS_PRIVILEGE, {
|
|
87
|
+
label: '[[admin-chats:admin-chats-view]]',
|
|
88
|
+
type: 'moderation',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
data.privileges.set(ADMIN_CHATS_MANAGE_PRIVILEGE, {
|
|
92
|
+
label: '[[admin-chats:admin-chats-manage]]',
|
|
93
|
+
type: 'moderation',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return data;
|
|
97
|
+
};
|
|
45
98
|
|
|
46
99
|
plugin.filterNotificationCreate = async function (data) {
|
|
47
100
|
if (!data || !data.data) {
|
|
@@ -127,10 +180,14 @@ plugin.addScripts = async function (data) {
|
|
|
127
180
|
lockedAction: isHebrew ? 'לא ניתן לבצע פעולה זו בחדר נעול' : 'This action cannot be performed in a locked room.',
|
|
128
181
|
};
|
|
129
182
|
|
|
183
|
+
const isAdmin = uid ? await User.isAdministrator(uid) : false;
|
|
184
|
+
const adminChatsManage = uid ? (isAdmin || await privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, uid)) : false;
|
|
185
|
+
const adminChatsAccess = uid ? (isAdmin || adminChatsManage || await privileges.global.can(ADMIN_CHATS_PRIVILEGE, uid)) : false;
|
|
186
|
+
|
|
130
187
|
data.scripts = data.scripts || [];
|
|
131
188
|
data.scripts.push({
|
|
132
189
|
src: false,
|
|
133
|
-
script: `window.adminChatsTranslations = ${JSON.stringify(translations)};`
|
|
190
|
+
script: `window.adminChatsTranslations = ${JSON.stringify(translations)}; window.adminChatsAccess = ${adminChatsAccess}; window.adminChatsIsAdmin = ${isAdmin}; window.adminChatsCanManage = ${adminChatsManage};`
|
|
134
191
|
});
|
|
135
192
|
|
|
136
193
|
return data;
|
|
@@ -138,6 +195,11 @@ plugin.addScripts = async function (data) {
|
|
|
138
195
|
|
|
139
196
|
plugin.addProfileLink = async function (data) {
|
|
140
197
|
try {
|
|
198
|
+
const uid = data && (data.uid || (data.req && data.req.uid));
|
|
199
|
+
if (!uid || !await canAccessAdminChats(uid)) {
|
|
200
|
+
return data;
|
|
201
|
+
}
|
|
202
|
+
|
|
141
203
|
const userSlug = data.user ? data.user.userslug : (data.userData ? data.userData.userslug : null);
|
|
142
204
|
|
|
143
205
|
if (userSlug) {
|
|
@@ -145,12 +207,12 @@ plugin.addProfileLink = async function (data) {
|
|
|
145
207
|
id: 'admin-view-chats',
|
|
146
208
|
route: 'user/' + userSlug + '/chats',
|
|
147
209
|
icon: 'fa-comments',
|
|
148
|
-
name: '
|
|
210
|
+
name: '[[admin-chats:profile.viewChats]]',
|
|
149
211
|
visibility: {
|
|
150
|
-
self:
|
|
212
|
+
self: true,
|
|
151
213
|
other: true,
|
|
152
|
-
moderator:
|
|
153
|
-
globalMod:
|
|
214
|
+
moderator: true,
|
|
215
|
+
globalMod: true,
|
|
154
216
|
admin: true,
|
|
155
217
|
}
|
|
156
218
|
});
|
|
@@ -162,16 +224,19 @@ plugin.addProfileLink = async function (data) {
|
|
|
162
224
|
};
|
|
163
225
|
|
|
164
226
|
plugin.isRoomOwner = async function (payload) {
|
|
165
|
-
const isAdmin = await
|
|
166
|
-
|
|
227
|
+
const [isAdmin, canManage] = await Promise.all([
|
|
228
|
+
User.isAdministrator(payload.uid),
|
|
229
|
+
canManageAdminChats(payload.uid),
|
|
230
|
+
]);
|
|
231
|
+
if (isAdmin || canManage) {
|
|
167
232
|
payload.isOwner = true;
|
|
168
233
|
}
|
|
169
234
|
return payload;
|
|
170
235
|
};
|
|
171
236
|
|
|
172
237
|
plugin.isUserInRoom = async function (payload) {
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
238
|
+
const uid = getCallerUid(payload);
|
|
239
|
+
if (await canAccessAdminChats(uid)) {
|
|
175
240
|
payload.inRoom = true;
|
|
176
241
|
}
|
|
177
242
|
return payload;
|
|
@@ -180,13 +245,28 @@ plugin.isUserInRoom = async function (payload) {
|
|
|
180
245
|
plugin.canReply = async function (payload) {
|
|
181
246
|
const uid = getCallerUid(payload);
|
|
182
247
|
const roomId = getRoomId(payload);
|
|
183
|
-
const isAdmin = await User.isAdministrator(uid);
|
|
184
248
|
|
|
185
|
-
|
|
249
|
+
const [isAdmin, canView, canManage] = await Promise.all([
|
|
250
|
+
User.isAdministrator(uid),
|
|
251
|
+
privileges.global.can(ADMIN_CHATS_PRIVILEGE, uid),
|
|
252
|
+
privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, uid),
|
|
253
|
+
]);
|
|
254
|
+
const isManager = isAdmin || canManage;
|
|
255
|
+
const hasAccess = isAdmin || canView || canManage;
|
|
256
|
+
|
|
257
|
+
if (isManager) {
|
|
186
258
|
payload.canReply = true;
|
|
187
259
|
return payload;
|
|
188
260
|
}
|
|
189
261
|
|
|
262
|
+
if (roomId && hasAccess) {
|
|
263
|
+
const isMember = await isUserMemberOfRoom(uid, roomId);
|
|
264
|
+
if (!isMember) {
|
|
265
|
+
payload.canReply = false;
|
|
266
|
+
return payload;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
190
270
|
if (roomId && await isRoomLocked(roomId)) {
|
|
191
271
|
payload.canReply = false;
|
|
192
272
|
}
|
|
@@ -206,6 +286,17 @@ plugin.filterMessagingSend = async function (payload) {
|
|
|
206
286
|
return payload;
|
|
207
287
|
}
|
|
208
288
|
|
|
289
|
+
if (await canManageAdminChats(uid)) {
|
|
290
|
+
return payload;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (await canAccessAdminChats(uid)) {
|
|
294
|
+
const isMember = await isUserMemberOfRoom(uid, roomId);
|
|
295
|
+
if (!isMember) {
|
|
296
|
+
throw new Error('[[error:no-privileges]]');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
209
300
|
if (await isRoomLocked(roomId)) {
|
|
210
301
|
const message = await getLockedActionMessage(uid);
|
|
211
302
|
throw new Error(message);
|
|
@@ -214,6 +305,9 @@ plugin.filterMessagingSend = async function (payload) {
|
|
|
214
305
|
return payload;
|
|
215
306
|
};
|
|
216
307
|
plugin.filterAddUsersToRoom = async function (payload) {
|
|
308
|
+
if (await canManageAdminChats(payload.uid)) {
|
|
309
|
+
return payload;
|
|
310
|
+
}
|
|
217
311
|
if (await canNonAdminModifyLockedRoom(payload.uid, payload.roomId)) {
|
|
218
312
|
const message = await getLockedActionMessage(payload.uid);
|
|
219
313
|
throw new Error(message);
|
|
@@ -222,6 +316,9 @@ plugin.filterAddUsersToRoom = async function (payload) {
|
|
|
222
316
|
};
|
|
223
317
|
|
|
224
318
|
plugin.filterRemoveUsersFromRoom = async function (payload) {
|
|
319
|
+
if (await canManageAdminChats(payload.uid)) {
|
|
320
|
+
return payload;
|
|
321
|
+
}
|
|
225
322
|
if (await canNonAdminModifyLockedRoom(payload.uid, payload.roomId)) {
|
|
226
323
|
const message = await getLockedActionMessage(payload.uid);
|
|
227
324
|
throw new Error(message);
|
|
@@ -230,6 +327,9 @@ plugin.filterRemoveUsersFromRoom = async function (payload) {
|
|
|
230
327
|
};
|
|
231
328
|
|
|
232
329
|
plugin.filterRenameRoom = async function (payload) {
|
|
330
|
+
if (await canManageAdminChats(payload.uid)) {
|
|
331
|
+
return payload;
|
|
332
|
+
}
|
|
233
333
|
if (await canNonAdminModifyLockedRoom(payload.uid, payload.roomId)) {
|
|
234
334
|
const message = await getLockedActionMessage(payload.uid);
|
|
235
335
|
throw new Error(message);
|
|
@@ -238,24 +338,21 @@ plugin.filterRenameRoom = async function (payload) {
|
|
|
238
338
|
};
|
|
239
339
|
|
|
240
340
|
plugin.canGetMessages = async function (payload) {
|
|
241
|
-
|
|
242
|
-
if (isAdmin) {
|
|
341
|
+
if (await canAccessAdminChats(payload.callerUid)) {
|
|
243
342
|
payload.canGet = true;
|
|
244
343
|
}
|
|
245
344
|
return payload;
|
|
246
345
|
};
|
|
247
346
|
|
|
248
347
|
plugin.canGetRecentChats = async function (payload) {
|
|
249
|
-
|
|
250
|
-
if (isAdmin) {
|
|
348
|
+
if (await canAccessAdminChats(payload.callerUid)) {
|
|
251
349
|
payload.canGet = true;
|
|
252
350
|
}
|
|
253
351
|
return payload;
|
|
254
352
|
};
|
|
255
353
|
|
|
256
354
|
plugin.canGetPublicChats = async function (payload) {
|
|
257
|
-
|
|
258
|
-
if (isAdmin) {
|
|
355
|
+
if (await canAccessAdminChats(payload.callerUid)) {
|
|
259
356
|
payload.canGet = true;
|
|
260
357
|
}
|
|
261
358
|
return payload;
|
|
@@ -267,20 +364,27 @@ plugin.onLoadRoom = async function (payload) {
|
|
|
267
364
|
return payload;
|
|
268
365
|
}
|
|
269
366
|
|
|
270
|
-
const isAdmin = await
|
|
367
|
+
const [isAdmin, canView, canManage] = await Promise.all([
|
|
368
|
+
User.isAdministrator(uid),
|
|
369
|
+
privileges.global.can(ADMIN_CHATS_PRIVILEGE, uid),
|
|
370
|
+
privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, uid),
|
|
371
|
+
]);
|
|
372
|
+
const isManager = isAdmin || canManage;
|
|
373
|
+
const hasAccess = isAdmin || canView || canManage;
|
|
374
|
+
|
|
271
375
|
const lockData = await getRoomLockData(room.roomId);
|
|
272
|
-
const isLockedForUser = lockData.isLocked && !
|
|
376
|
+
const isLockedForUser = lockData.isLocked && !isManager;
|
|
273
377
|
|
|
274
378
|
room.adminChatLock = lockData;
|
|
275
379
|
room.messages = ensureFirstVisibleMessageHeader(buildRoomMessagesWithLockNotice(room.messages, lockData));
|
|
276
380
|
room.canReply = room.canReply && !isLockedForUser;
|
|
277
381
|
room.showUserInput = room.showUserInput && !isLockedForUser;
|
|
278
382
|
|
|
279
|
-
if (!
|
|
383
|
+
if (!hasAccess) {
|
|
280
384
|
return payload;
|
|
281
385
|
}
|
|
282
386
|
|
|
283
|
-
const isOfficialMember = await
|
|
387
|
+
const isOfficialMember = await isUserMemberOfRoom(uid, room.roomId);
|
|
284
388
|
|
|
285
389
|
if (!isOfficialMember) {
|
|
286
390
|
await db.sortedSetRemove(`chat:room:${room.roomId}:uids:online`, uid);
|
|
@@ -325,12 +429,24 @@ plugin.onLoadRoom = async function (payload) {
|
|
|
325
429
|
|
|
326
430
|
room.messages = ensureFirstVisibleMessageHeader(room.messages);
|
|
327
431
|
}
|
|
432
|
+
}
|
|
328
433
|
|
|
329
|
-
|
|
434
|
+
if (!isManager && !isOfficialMember) {
|
|
435
|
+
room.canReply = false;
|
|
436
|
+
room.showUserInput = false;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
room.isAdmin = isManager;
|
|
440
|
+
room.isAdminOrGlobalMod = isManager;
|
|
441
|
+
payload.isAdminOrGlobalMod = isManager;
|
|
442
|
+
if (isManager) {
|
|
330
443
|
room.isOwner = true;
|
|
331
|
-
} else {
|
|
332
|
-
room.isAdmin = true;
|
|
333
444
|
}
|
|
445
|
+
room.adminChatsAccess = hasAccess;
|
|
446
|
+
payload.adminChatsAccess = hasAccess;
|
|
447
|
+
payload.adminChatsCanManage = isManager;
|
|
448
|
+
payload.adminChatsIsAdmin = isAdmin;
|
|
449
|
+
|
|
334
450
|
return payload;
|
|
335
451
|
};
|
|
336
452
|
|
|
@@ -342,7 +458,7 @@ function overrideCoreChatRedirect(controllers) {
|
|
|
342
458
|
|
|
343
459
|
const originalRedirectToChat = chatsController.redirectToChat;
|
|
344
460
|
const wrappedRedirectToChat = async function (req, res, next) {
|
|
345
|
-
if (req && req.uid && await
|
|
461
|
+
if (req && req.uid && await canAccessAdminChats(req.uid)) {
|
|
346
462
|
return await renderAdminChatsPage(req, res, next);
|
|
347
463
|
}
|
|
348
464
|
|
|
@@ -360,7 +476,7 @@ function overrideChatsApi() {
|
|
|
360
476
|
|
|
361
477
|
const originalKick = ChatsAPI.kick;
|
|
362
478
|
const wrappedKick = async function (caller, data) {
|
|
363
|
-
if (caller && !await User.isAdministrator(caller.uid) && data && data.roomId && Array.isArray(data.uids) && data.uids.length === 1 && parseInt(data.uids[0], 10) === parseInt(caller.uid, 10) && await isRoomLocked(data.roomId)) {
|
|
479
|
+
if (caller && !await User.isAdministrator(caller.uid) && !await canManageAdminChats(caller.uid) && data && data.roomId && Array.isArray(data.uids) && data.uids.length === 1 && parseInt(data.uids[0], 10) === parseInt(caller.uid, 10) && await isRoomLocked(data.roomId)) {
|
|
364
480
|
const message = await getLockedActionMessage(caller.uid);
|
|
365
481
|
throw new Error(message);
|
|
366
482
|
}
|
|
@@ -382,6 +498,10 @@ function overrideMessagingFunctions() {
|
|
|
382
498
|
return true;
|
|
383
499
|
}
|
|
384
500
|
|
|
501
|
+
if (await canManageAdminChats(uid)) {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
|
|
385
505
|
const roomId = await getMessageRoomId(messageId);
|
|
386
506
|
if (roomId && await isRoomLocked(roomId)) {
|
|
387
507
|
const message = await getLockedActionMessage(uid);
|
|
@@ -396,6 +516,10 @@ function overrideMessagingFunctions() {
|
|
|
396
516
|
return true;
|
|
397
517
|
}
|
|
398
518
|
|
|
519
|
+
if (await canManageAdminChats(uid)) {
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
|
|
399
523
|
const roomId = await getMessageRoomId(messageId);
|
|
400
524
|
if (roomId && await isRoomLocked(roomId)) {
|
|
401
525
|
const message = await getLockedActionMessage(uid);
|
|
@@ -406,7 +530,7 @@ function overrideMessagingFunctions() {
|
|
|
406
530
|
};
|
|
407
531
|
|
|
408
532
|
Messaging.canViewMessage = async function (mids, roomId, uid) {
|
|
409
|
-
if (await
|
|
533
|
+
if (await canAccessAdminChats(uid)) {
|
|
410
534
|
return Array.isArray(mids) ? mids.map(() => true) : true;
|
|
411
535
|
}
|
|
412
536
|
return await originalCanViewMessage(mids, roomId, uid);
|
|
@@ -415,9 +539,23 @@ function overrideMessagingFunctions() {
|
|
|
415
539
|
if (typeof originalCanReply === 'function') {
|
|
416
540
|
Messaging.canReply = async function (...args) {
|
|
417
541
|
const guessed = guessRoomIdUidFromArgs(args);
|
|
418
|
-
|
|
542
|
+
const [isAdmin, canView, canManage] = await Promise.all([
|
|
543
|
+
User.isAdministrator(guessed.uid),
|
|
544
|
+
privileges.global.can(ADMIN_CHATS_PRIVILEGE, guessed.uid),
|
|
545
|
+
privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, guessed.uid),
|
|
546
|
+
]);
|
|
547
|
+
const isManager = isAdmin || canManage;
|
|
548
|
+
const hasAccess = isAdmin || canView || canManage;
|
|
549
|
+
|
|
550
|
+
if (isManager) {
|
|
419
551
|
return true;
|
|
420
552
|
}
|
|
553
|
+
if (guessed.roomId && hasAccess) {
|
|
554
|
+
const isMember = await isUserMemberOfRoom(guessed.uid, guessed.roomId);
|
|
555
|
+
if (!isMember) {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
421
559
|
if (guessed.roomId && await isRoomLocked(guessed.roomId)) {
|
|
422
560
|
return false;
|
|
423
561
|
}
|
|
@@ -450,8 +588,8 @@ function registerRoutes(app, router, middleware) {
|
|
|
450
588
|
|
|
451
589
|
const pageController = async (req, res, next) => {
|
|
452
590
|
try {
|
|
453
|
-
// If user is not
|
|
454
|
-
if (req.uid && !await
|
|
591
|
+
// If user is not allowed, redirect to user-specific chat URL
|
|
592
|
+
if (req.uid && !await canAccessAdminChats(req.uid)) {
|
|
455
593
|
const userSlug = await User.getUserField(req.uid, 'userslug');
|
|
456
594
|
if (userSlug && req.params.roomId) {
|
|
457
595
|
const redirectUrl = `${req.baseUrl || ''}/user/${userSlug}/chats/${req.params.roomId}${req.params.index ? `/${req.params.index}` : ''}`;
|
|
@@ -482,6 +620,25 @@ function registerRoutes(app, router, middleware) {
|
|
|
482
620
|
}
|
|
483
621
|
});
|
|
484
622
|
|
|
623
|
+
|
|
624
|
+
router.get('/api/admin-chats/privileges', ...routeMiddleware, async (req, res) => {
|
|
625
|
+
try {
|
|
626
|
+
if (!await assertAdminChatsAccess(req, res)) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const isAdmin = await User.isAdministrator(req.uid);
|
|
631
|
+
const canManage = isAdmin || await privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, req.uid);
|
|
632
|
+
res.json({
|
|
633
|
+
adminChatsAccess: true,
|
|
634
|
+
adminChatsCanManage: canManage,
|
|
635
|
+
adminChatsIsAdmin: isAdmin,
|
|
636
|
+
});
|
|
637
|
+
} catch (err) {
|
|
638
|
+
res.status(500).json({ status: { code: 'error', message: err.message } });
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
485
642
|
router.get('/api/admin-chats/page/:roomId?/:index?', ...routeMiddleware, async (req, res) => {
|
|
486
643
|
try {
|
|
487
644
|
if (!await assertAdminChatsAccess(req, res)) {
|
|
@@ -504,8 +661,8 @@ function registerRoutes(app, router, middleware) {
|
|
|
504
661
|
return res.status(400).json({ status: { code: 'bad-request', message: 'Invalid room id' } });
|
|
505
662
|
}
|
|
506
663
|
|
|
507
|
-
if (!await User.isAdministrator(actingUid)) {
|
|
508
|
-
return res.status(403).json({ status: { code: 'forbidden', message: 'Admin
|
|
664
|
+
if (!await User.isAdministrator(actingUid) && !await canManageAdminChats(actingUid)) {
|
|
665
|
+
return res.status(403).json({ status: { code: 'forbidden', message: 'Admin or manage privilege required' } });
|
|
509
666
|
}
|
|
510
667
|
|
|
511
668
|
if (!await roomExists(roomId)) {
|
|
@@ -546,7 +703,7 @@ async function renderAdminChatsPage(req, res, next) {
|
|
|
546
703
|
}
|
|
547
704
|
|
|
548
705
|
async function assertAdminChatsAccess(req, res) {
|
|
549
|
-
if (!req.uid || !await
|
|
706
|
+
if (!req.uid || !await canAccessAdminChats(req.uid)) {
|
|
550
707
|
helpers.notAllowed(req, res);
|
|
551
708
|
return false;
|
|
552
709
|
}
|
|
@@ -556,6 +713,13 @@ async function assertAdminChatsAccess(req, res) {
|
|
|
556
713
|
|
|
557
714
|
async function buildAdminChatsPayload(req) {
|
|
558
715
|
const userslug = await User.getUserField(req.uid, 'userslug');
|
|
716
|
+
const [isAdmin, canView, canManage] = await Promise.all([
|
|
717
|
+
User.isAdministrator(req.uid),
|
|
718
|
+
privileges.global.can(ADMIN_CHATS_PRIVILEGE, req.uid),
|
|
719
|
+
privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, req.uid),
|
|
720
|
+
]);
|
|
721
|
+
const adminChatsManage = isAdmin || canManage;
|
|
722
|
+
const adminChatsAccess = isAdmin || canView || canManage;
|
|
559
723
|
const [recentChats, publicRooms, privateRoomCount] = await Promise.all([
|
|
560
724
|
getAdminRecentChats(req.uid, 0, ADMIN_CHAT_PAGE_SIZE),
|
|
561
725
|
Messaging.getPublicRooms(req.uid, req.uid),
|
|
@@ -567,6 +731,9 @@ async function buildAdminChatsPayload(req) {
|
|
|
567
731
|
uid: req.uid,
|
|
568
732
|
userslug,
|
|
569
733
|
adminAllChats: true,
|
|
734
|
+
adminChatsAccess,
|
|
735
|
+
adminChatsIsAdmin: isAdmin,
|
|
736
|
+
adminChatsCanManage: adminChatsManage,
|
|
570
737
|
rooms: recentChats.rooms,
|
|
571
738
|
nextStart: recentChats.nextStart,
|
|
572
739
|
publicRooms: publicRooms || [],
|
|
@@ -763,6 +930,10 @@ async function canNonAdminModifyLockedRoom(uid, roomId) {
|
|
|
763
930
|
return false;
|
|
764
931
|
}
|
|
765
932
|
|
|
933
|
+
if (await canManageAdminChats(uid)) {
|
|
934
|
+
return false;
|
|
935
|
+
}
|
|
936
|
+
|
|
766
937
|
return await isRoomLocked(roomId);
|
|
767
938
|
}
|
|
768
939
|
async function roomExists(roomId) {
|
|
@@ -899,6 +1070,3 @@ function getRoomId(payload) {
|
|
|
899
1070
|
}
|
|
900
1071
|
|
|
901
1072
|
module.exports = plugin;
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
package/package.json
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-admin-chats",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "A plugin that allows the administrator to view and manage user chats.",
|
|
5
5
|
"main": "library.js",
|
|
6
|
-
"dependencies": {
|
|
7
|
-
"lodash": "^4.17.21"
|
|
8
|
-
},
|
|
9
6
|
"nbbpm": {
|
|
10
7
|
"compatibility": "^4.0.0"
|
|
11
8
|
}
|
package/plugin.json
CHANGED
|
@@ -71,9 +71,11 @@
|
|
|
71
71
|
{
|
|
72
72
|
"hook": "filter:user.notifications.getNotifications",
|
|
73
73
|
"method": "filterUserNotificationsGetNotifications"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"hook": "static:privileges.global.init",
|
|
77
|
+
"method": "registerPrivileges"
|
|
74
78
|
}
|
|
75
|
-
]
|
|
79
|
+
],
|
|
80
|
+
"languages": "public/languages"
|
|
76
81
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
@@ -5,5 +5,7 @@
|
|
|
5
5
|
"errors.update": "Unable to update room lock.",
|
|
6
6
|
"profile.viewChats": "View Chats",
|
|
7
7
|
"empty.selectChat": "Please select a chat from the sidebar.",
|
|
8
|
-
"errors.lockedAction": "This action cannot be performed in a locked room."
|
|
8
|
+
"errors.lockedAction": "This action cannot be performed in a locked room.",
|
|
9
|
+
"admin-chats-view": "View all chats",
|
|
10
|
+
"admin-chats-manage": "Manage chat rooms"
|
|
9
11
|
}
|
|
@@ -5,5 +5,7 @@
|
|
|
5
5
|
"errors.update": "לא ניתן לעדכן את נעילת החדר.",
|
|
6
6
|
"profile.viewChats": "צפיה בצ'אטים",
|
|
7
7
|
"empty.selectChat": "אנא בחר צ'אט מסרגל הצד.",
|
|
8
|
-
"errors.lockedAction": "לא ניתן לבצע פעולה זו בחדר נעול"
|
|
8
|
+
"errors.lockedAction": "לא ניתן לבצע פעולה זו בחדר נעול",
|
|
9
|
+
"admin-chats-view": "צפיה בכל הצ'אטים",
|
|
10
|
+
"admin-chats-manage": "ניהול חדרי צ'אט"
|
|
9
11
|
}
|