nodebb-plugin-admin-chats 3.5.1 → 3.6.0
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/lib/history.js +19 -53
- package/lib/routes.js +57 -20
- package/package.json +1 -1
- package/plugin.json +2 -1
- package/public/languages/ar/admin-chats.json +11 -1
- package/public/languages/de/admin-chats.json +11 -1
- package/public/languages/de-CH/admin-chats.json +11 -1
- package/public/languages/el/admin-chats.json +11 -1
- package/public/languages/en/admin-chats.json +11 -1
- package/public/languages/en-GB/admin-chats.json +11 -1
- package/public/languages/es/admin-chats.json +11 -1
- package/public/languages/fr/admin-chats.json +11 -1
- package/public/languages/fr-CH/admin-chats.json +11 -1
- package/public/languages/he/admin-chats.json +11 -1
- package/public/languages/hi/admin-chats.json +11 -1
- package/public/languages/it/admin-chats.json +11 -1
- package/public/languages/it-CH/admin-chats.json +11 -1
- package/public/languages/ja/admin-chats.json +11 -1
- package/public/languages/nl/admin-chats.json +11 -1
- package/public/languages/pl/admin-chats.json +11 -1
- package/public/languages/pt-BR/admin-chats.json +11 -1
- package/public/languages/rm-CH/admin-chats.json +11 -1
- package/public/languages/ru/admin-chats.json +11 -1
- package/public/languages/uk/admin-chats.json +11 -1
- package/public/languages/zh-CN/admin-chats.json +11 -1
- package/public/languages/zh-TW/admin-chats.json +11 -1
- package/static/lib/message-history.js +221 -0
- package/templates/partials/chats/message.tpl +5 -0
package/lib/history.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const validator = nodebb.require('validator');
|
|
4
3
|
const Messaging = nodebb.require('./src/messaging');
|
|
5
4
|
const User = nodebb.require('./src/user');
|
|
6
5
|
const db = nodebb.require('./src/database');
|
|
@@ -59,6 +58,10 @@ async function getMessageEditHistory(mid) {
|
|
|
59
58
|
};
|
|
60
59
|
};
|
|
61
60
|
|
|
61
|
+
// The author of the current version is whoever performed the most recent
|
|
62
|
+
// edit (the newest revision's editor); with no revisions it's the sender.
|
|
63
|
+
const lastRevision = revisions[revisions.length - 1];
|
|
64
|
+
|
|
62
65
|
return {
|
|
63
66
|
mid,
|
|
64
67
|
exists: !!exists,
|
|
@@ -69,6 +72,7 @@ async function getMessageEditHistory(mid) {
|
|
|
69
72
|
edited: parseInt(current && current.edited, 10) || 0,
|
|
70
73
|
timestamp: parseInt(current && current.timestamp, 10) || 0,
|
|
71
74
|
deleted: parseInt(current && current.deleted, 10) === 1,
|
|
75
|
+
editor: decorate(lastRevision ? lastRevision.editedByUid : authorUid),
|
|
72
76
|
},
|
|
73
77
|
revisionCount: revisions.length,
|
|
74
78
|
revisions: revisions.map(rev => ({
|
|
@@ -80,65 +84,27 @@ async function getMessageEditHistory(mid) {
|
|
|
80
84
|
};
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
function
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
return numeric ? new Date(numeric).toISOString().replace('T', ' ').replace(/\..+$/, ' UTC') : '—';
|
|
88
|
-
};
|
|
87
|
+
async function deleteRevision(mid, index) {
|
|
88
|
+
const key = editHistoryKey(mid);
|
|
89
|
+
const items = await db.getListRange(key, 0, -1);
|
|
90
|
+
const numericIndex = parseInt(index, 10);
|
|
89
91
|
|
|
90
|
-
if (!
|
|
91
|
-
return
|
|
92
|
-
<h1>הודעה ${esc(history.mid)} לא נמצאה</h1></body></html>`;
|
|
92
|
+
if (!Array.isArray(items) || Number.isNaN(numericIndex) || numericIndex < 0 || numericIndex >= items.length) {
|
|
93
|
+
return false;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const currentBlock = `
|
|
102
|
-
<li class="rev current">
|
|
103
|
-
<div class="meta">גרסה נוכחית${history.current.edited ? ` · נערכה לאחרונה ${esc(fmt(history.current.edited))}` : ''}${history.current.deleted ? ' · מחוקה' : ''}</div>
|
|
104
|
-
<pre class="content">${esc(history.current.content)}</pre>
|
|
105
|
-
</li>`;
|
|
106
|
-
|
|
107
|
-
return `<!doctype html>
|
|
108
|
-
<html lang="he" dir="rtl">
|
|
109
|
-
<head>
|
|
110
|
-
<meta charset="utf-8">
|
|
111
|
-
<meta name="robots" content="noindex,nofollow">
|
|
112
|
-
<title>היסטוריית עריכות · הודעה ${esc(history.mid)}</title>
|
|
113
|
-
<style>
|
|
114
|
-
body { font-family: system-ui, Segoe UI, Arial, sans-serif; margin: 0; padding: 2rem; background: #f5f6f8; color: #1c1e21; }
|
|
115
|
-
h1 { font-size: 1.25rem; margin: 0 0 .25rem; }
|
|
116
|
-
.sub { color: #65676b; font-size: .875rem; margin-bottom: 1.5rem; }
|
|
117
|
-
ul { list-style: none; margin: 0; padding: 0; max-width: 780px; }
|
|
118
|
-
.rev { background: #fff; border: 1px solid #dcdfe3; border-radius: 8px; padding: .75rem 1rem; margin-bottom: 1rem; }
|
|
119
|
-
.rev.current { border-color: #2d6cdf; box-shadow: 0 0 0 1px #2d6cdf33; }
|
|
120
|
-
.meta { font-size: .8rem; color: #65676b; margin-bottom: .5rem; }
|
|
121
|
-
.content { white-space: pre-wrap; word-break: break-word; margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9rem; }
|
|
122
|
-
@media (prefers-color-scheme: dark) {
|
|
123
|
-
body { background: #18191a; color: #e4e6eb; }
|
|
124
|
-
.rev { background: #242526; border-color: #3a3b3c; }
|
|
125
|
-
.meta, .sub { color: #b0b3b8; }
|
|
96
|
+
items.splice(numericIndex, 1);
|
|
97
|
+
await db.delete(key);
|
|
98
|
+
for (const item of items) {
|
|
99
|
+
// eslint-disable-next-line no-await-in-loop
|
|
100
|
+
await db.listAppend(key, item);
|
|
126
101
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<body>
|
|
130
|
-
<h1>היסטוריית עריכות · הודעה ${esc(history.mid)}</h1>
|
|
131
|
-
<div class="sub">מחבר: ${esc(history.author.username)} · חדר: ${esc(history.roomId)} · ${esc(history.revisionCount)} עריכות קודמות</div>
|
|
132
|
-
<ul>
|
|
133
|
-
${history.revisionCount ? timeline : '<li class="rev"><div class="meta">אין עריכות קודמות מתועדות</div></li>'}
|
|
134
|
-
${currentBlock}
|
|
135
|
-
</ul>
|
|
136
|
-
</body>
|
|
137
|
-
</html>`;
|
|
102
|
+
|
|
103
|
+
return true;
|
|
138
104
|
}
|
|
139
105
|
|
|
140
106
|
module.exports = {
|
|
141
107
|
editHistoryKey,
|
|
142
108
|
getMessageEditHistory,
|
|
143
|
-
|
|
109
|
+
deleteRevision,
|
|
144
110
|
};
|
package/lib/routes.js
CHANGED
|
@@ -6,11 +6,10 @@ const db = nodebb.require('./src/database');
|
|
|
6
6
|
const meta = nodebb.require('./src/meta');
|
|
7
7
|
const privileges = nodebb.require('./src/privileges');
|
|
8
8
|
const helpers = nodebb.require('./src/controllers/helpers');
|
|
9
|
-
const validator = nodebb.require('validator');
|
|
10
9
|
const sockets = nodebb.require('./src/socket.io');
|
|
11
10
|
|
|
12
11
|
const { canAccessAdminChats, canManageAdminChats } = require('./access');
|
|
13
|
-
const { getMessageEditHistory,
|
|
12
|
+
const { getMessageEditHistory, deleteRevision } = require('./history');
|
|
14
13
|
const { roomExists, lockRoom, unlockRoom, getRoomLockData } = require('./lock');
|
|
15
14
|
|
|
16
15
|
const ADMIN_CHAT_PAGE_SIZE = 30;
|
|
@@ -374,19 +373,17 @@ function registerRoutes(app, router, middleware) {
|
|
|
374
373
|
}
|
|
375
374
|
});
|
|
376
375
|
|
|
377
|
-
// ---
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
// GET /api/admin-chats/history/:mid -> JSON
|
|
385
|
-
// GET /admin-chats/history/:mid -> standalone HTML page
|
|
376
|
+
// --- Chat edit-history (message options menu) -------------------------
|
|
377
|
+
// Surfaced as an "Edit history" item in each message's options dropdown
|
|
378
|
+
// (templates/partials/chats/message.tpl + static/lib/message-history.js),
|
|
379
|
+
// mirroring core's post diffs modal. Both routes require the manage
|
|
380
|
+
// privilege — view-only admin-chats access is not enough.
|
|
381
|
+
// GET /api/admin-chats/history/:mid -> JSON
|
|
382
|
+
// DELETE /api/admin-chats/history/:mid/:index -> delete one revision
|
|
386
383
|
router.get('/api/admin-chats/history/:mid', ...routeMiddleware, async (req, res) => {
|
|
387
384
|
try {
|
|
388
|
-
if (!await
|
|
389
|
-
return;
|
|
385
|
+
if (!req.uid || !await canManageAdminChats(req.uid)) {
|
|
386
|
+
return res.status(403).json({ status: { code: 'forbidden', message: 'Manage privilege required' } });
|
|
390
387
|
}
|
|
391
388
|
|
|
392
389
|
const mid = parseInt(req.params.mid, 10);
|
|
@@ -401,21 +398,61 @@ function registerRoutes(app, router, middleware) {
|
|
|
401
398
|
}
|
|
402
399
|
});
|
|
403
400
|
|
|
404
|
-
|
|
401
|
+
// Restore a revision — like core post diffs (src/posts/diffs.js Diffs.restore),
|
|
402
|
+
// restoring is just an edit that sets the message content back to the
|
|
403
|
+
// revision's content. Going through Messaging.editMessage means the
|
|
404
|
+
// replaced current version is snapshotted into history (lib/overrides.js)
|
|
405
|
+
// and open chat windows update live via core's edit broadcast.
|
|
406
|
+
router.put('/api/admin-chats/history/:mid/:index', ...routeMiddleware, async (req, res) => {
|
|
405
407
|
try {
|
|
406
|
-
if (!await
|
|
407
|
-
return;
|
|
408
|
+
if (!req.uid || !await canManageAdminChats(req.uid)) {
|
|
409
|
+
return res.status(403).json({ status: { code: 'forbidden', message: 'Manage privilege required' } });
|
|
408
410
|
}
|
|
409
411
|
|
|
410
412
|
const mid = parseInt(req.params.mid, 10);
|
|
411
|
-
|
|
412
|
-
|
|
413
|
+
const index = parseInt(req.params.index, 10);
|
|
414
|
+
if (Number.isNaN(mid) || Number.isNaN(index) || index < 0) {
|
|
415
|
+
return res.status(400).json({ status: { code: 'bad-request', message: 'Invalid message id or revision index' } });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const history = await getMessageEditHistory(mid);
|
|
419
|
+
if (!history.exists) {
|
|
420
|
+
return res.status(404).json({ status: { code: 'not-found', message: 'Message not found' } });
|
|
421
|
+
}
|
|
422
|
+
if (index >= history.revisions.length) {
|
|
423
|
+
return res.status(404).json({ status: { code: 'not-found', message: 'Revision not found' } });
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
await Messaging.editMessage(req.uid, mid, history.roomId, history.revisions[index].content);
|
|
427
|
+
|
|
428
|
+
const fresh = await getMessageEditHistory(mid);
|
|
429
|
+
return res.json({ status: { code: 'ok', message: 'Revision restored' }, ...fresh });
|
|
430
|
+
} catch (err) {
|
|
431
|
+
return res.status(500).json({ status: { code: 'error', message: err.message } });
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
router.delete('/api/admin-chats/history/:mid/:index', ...routeMiddleware, async (req, res) => {
|
|
436
|
+
try {
|
|
437
|
+
if (!req.uid || !await canManageAdminChats(req.uid)) {
|
|
438
|
+
return res.status(403).json({ status: { code: 'forbidden', message: 'Manage privilege required' } });
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const mid = parseInt(req.params.mid, 10);
|
|
442
|
+
const index = parseInt(req.params.index, 10);
|
|
443
|
+
if (Number.isNaN(mid) || Number.isNaN(index) || index < 0) {
|
|
444
|
+
return res.status(400).json({ status: { code: 'bad-request', message: 'Invalid message id or revision index' } });
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const deleted = await deleteRevision(mid, index);
|
|
448
|
+
if (!deleted) {
|
|
449
|
+
return res.status(404).json({ status: { code: 'not-found', message: 'Revision not found' } });
|
|
413
450
|
}
|
|
414
451
|
|
|
415
452
|
const history = await getMessageEditHistory(mid);
|
|
416
|
-
return res.
|
|
453
|
+
return res.json({ status: { code: 'ok', message: 'Revision deleted' }, ...history });
|
|
417
454
|
} catch (err) {
|
|
418
|
-
return res.status(500).
|
|
455
|
+
return res.status(500).json({ status: { code: 'error', message: err.message } });
|
|
419
456
|
}
|
|
420
457
|
});
|
|
421
458
|
}
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "لا يمكن تنفيذ هذا الإجراء في غرفة مقفلة.",
|
|
9
9
|
"admin-chats-view": "عرض جميع الدردشات",
|
|
10
10
|
"admin-chats-manage": "إدارة غرف الدردشة",
|
|
11
|
-
"participants": "المشاركون"
|
|
11
|
+
"participants": "المشاركون",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Diese Aktion kann in einem gesperrten Raum nicht ausgeführt werden.",
|
|
9
9
|
"admin-chats-view": "Alle Chats ansehen",
|
|
10
10
|
"admin-chats-manage": "Chat-Räume verwalten",
|
|
11
|
-
"participants": "Teilnehmer"
|
|
11
|
+
"participants": "Teilnehmer",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Diese Aktion kann in einem gesperrten Raum nicht ausgeführt werden.",
|
|
9
9
|
"admin-chats-view": "Alle Chats ansehen",
|
|
10
10
|
"admin-chats-manage": "Chat-Räume verwalten",
|
|
11
|
-
"participants": "Teilnehmer"
|
|
11
|
+
"participants": "Teilnehmer",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Αυτή η ενέργεια δεν μπορεί να εκτελεστεί σε κλειδωμένο δωμάτιο.",
|
|
9
9
|
"admin-chats-view": "Προβολή όλων των συνομιλιών",
|
|
10
10
|
"admin-chats-manage": "Διαχείριση δωματίων συνομιλίας",
|
|
11
|
-
"participants": "Συμμετέχοντες"
|
|
11
|
+
"participants": "Συμμετέχοντες",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "This action cannot be performed in a locked room.",
|
|
9
9
|
"admin-chats-view": "View all chats",
|
|
10
10
|
"admin-chats-manage": "Manage chat rooms",
|
|
11
|
-
"participants": "Participants"
|
|
11
|
+
"participants": "Participants",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "This action cannot be performed in a locked room.",
|
|
9
9
|
"admin-chats-view": "View all chats",
|
|
10
10
|
"admin-chats-manage": "Manage chat rooms",
|
|
11
|
-
"participants": "Participants"
|
|
11
|
+
"participants": "Participants",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Esta acción no se puede realizar en una sala bloqueada.",
|
|
9
9
|
"admin-chats-view": "Ver todos los chats",
|
|
10
10
|
"admin-chats-manage": "Administrar salas de chat",
|
|
11
|
-
"participants": "Participantes"
|
|
11
|
+
"participants": "Participantes",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Cette action ne peut pas être effectuée dans un salon verrouillé.",
|
|
9
9
|
"admin-chats-view": "Voir tous les Chats",
|
|
10
10
|
"admin-chats-manage": "Gérer les salons de discussion",
|
|
11
|
-
"participants": "Participants"
|
|
11
|
+
"participants": "Participants",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Cette action ne peut pas être effectuée dans une salle verrouillée.",
|
|
9
9
|
"admin-chats-view": "Voir toutes les discussions",
|
|
10
10
|
"admin-chats-manage": "Gérer les salons de discussion",
|
|
11
|
-
"participants": "Participants"
|
|
11
|
+
"participants": "Participants",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "לא ניתן לבצע פעולה זו בחדר נעול",
|
|
9
9
|
"admin-chats-view": "צפיה בכל הצ'אטים",
|
|
10
10
|
"admin-chats-manage": "ניהול חדרי צ'אט",
|
|
11
|
-
"participants": "משתתפים"
|
|
11
|
+
"participants": "משתתפים",
|
|
12
|
+
"history.title": "היסטוריית עריכות",
|
|
13
|
+
"history.current": "גרסה נוכחית",
|
|
14
|
+
"history.revisionCount": "%1 עריכות קודמות",
|
|
15
|
+
"history.deleteRevision": "מחיקת גרסה",
|
|
16
|
+
"history.confirmDelete": "האם למחוק גרסה זו לצמיתות?",
|
|
17
|
+
"history.deleted": "הגרסה נמחקה",
|
|
18
|
+
"errors.history": "לא ניתן לטעון או לעדכן את היסטוריית העריכות.",
|
|
19
|
+
"history.restore": "שחזור גרסה",
|
|
20
|
+
"history.restored": "ההודעה שוחזרה לגרסה שנבחרה",
|
|
21
|
+
"history.restoreDescription": "שחזור יציב את תוכן הגרסה שנבחרה כגרסה הנוכחית; הגרסה שהוחלפה תישמר בהיסטוריה."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "लॉक किए गए कमरे में यह कार्रवाई नहीं की जा सकती।",
|
|
9
9
|
"admin-chats-view": "सभी चैट देखें",
|
|
10
10
|
"admin-chats-manage": "चैट रूम प्रबंधित करें",
|
|
11
|
-
"participants": "प्रतिभागी"
|
|
11
|
+
"participants": "प्रतिभागी",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Questa azione non può essere eseguita in una stanza bloccata.",
|
|
9
9
|
"admin-chats-view": "Visualizza tutte le chat",
|
|
10
10
|
"admin-chats-manage": "Gestisci le stanze di chat",
|
|
11
|
-
"participants": "Partecipanti"
|
|
11
|
+
"participants": "Partecipanti",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Questa azione non può essere eseguita in una stanza bloccata.",
|
|
9
9
|
"admin-chats-view": "Visualizza tutte le chat",
|
|
10
10
|
"admin-chats-manage": "Gestisci le stanze di chat",
|
|
11
|
-
"participants": "Partecipanti"
|
|
11
|
+
"participants": "Partecipanti",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "ロックされたルームではこの操作は実行できません。",
|
|
9
9
|
"admin-chats-view": "すべてのチャットを見る",
|
|
10
10
|
"admin-chats-manage": "チャットルームを管理",
|
|
11
|
-
"participants": "参加者"
|
|
11
|
+
"participants": "参加者",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Deze actie kan niet worden uitgevoerd in een vergrendelde chatruimte.",
|
|
9
9
|
"admin-chats-view": "Alle chats bekijken",
|
|
10
10
|
"admin-chats-manage": "Chatruimtes beheren",
|
|
11
|
-
"participants": "Deelnemers"
|
|
11
|
+
"participants": "Deelnemers",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Tej czynności nie można wykonać w zablokowanym pokoju.",
|
|
9
9
|
"admin-chats-view": "Wyświetl wszystkie czaty",
|
|
10
10
|
"admin-chats-manage": "Zarządzaj pokojami czatu",
|
|
11
|
-
"participants": "Uczestnicy"
|
|
11
|
+
"participants": "Uczestnicy",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Esta ação não pode ser realizada em uma sala bloqueada.",
|
|
9
9
|
"admin-chats-view": "Ver todos os chats",
|
|
10
10
|
"admin-chats-manage": "Gerenciar salas de chat",
|
|
11
|
-
"participants": "Participantes"
|
|
11
|
+
"participants": "Participantes",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Questa acziun na po betg vegnir fatga en in local bloccà.",
|
|
9
9
|
"admin-chats-view": "Veser tut ils chats",
|
|
10
10
|
"admin-chats-manage": "Administrar locals da chat",
|
|
11
|
-
"participants": "Participonts"
|
|
11
|
+
"participants": "Participonts",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Это действие нельзя выполнить в заблокированной комнате.",
|
|
9
9
|
"admin-chats-view": "Просмотреть все чаты",
|
|
10
10
|
"admin-chats-manage": "Управлять чат-комнатами",
|
|
11
|
-
"participants": "Участники"
|
|
11
|
+
"participants": "Участники",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "Цю дію не можна виконати в блокованій кімнаті.",
|
|
9
9
|
"admin-chats-view": "Переглянути всі чати",
|
|
10
10
|
"admin-chats-manage": "Керувати чат-кімнатами",
|
|
11
|
-
"participants": "Учасники"
|
|
11
|
+
"participants": "Учасники",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "锁定的聊天室无法执行此操作。",
|
|
9
9
|
"admin-chats-view": "查看所有聊天",
|
|
10
10
|
"admin-chats-manage": "管理聊天室",
|
|
11
|
-
"participants": "参与者"
|
|
11
|
+
"participants": "参与者",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"errors.lockedAction": "鎖定的聊天室無法執行此操作。",
|
|
9
9
|
"admin-chats-view": "查看所有聊天",
|
|
10
10
|
"admin-chats-manage": "管理聊天室",
|
|
11
|
-
"participants": "參與者"
|
|
11
|
+
"participants": "參與者",
|
|
12
|
+
"history.title": "Edit History",
|
|
13
|
+
"history.current": "Current version",
|
|
14
|
+
"history.revisionCount": "%1 previous edits",
|
|
15
|
+
"history.deleteRevision": "Delete version",
|
|
16
|
+
"history.confirmDelete": "Permanently delete this version?",
|
|
17
|
+
"history.deleted": "Version deleted",
|
|
18
|
+
"errors.history": "Unable to load or update the edit history.",
|
|
19
|
+
"history.restore": "Restore version",
|
|
20
|
+
"history.restored": "Message restored to the selected version",
|
|
21
|
+
"history.restoreDescription": "Restoring sets the selected version as the current one; the replaced version is kept in the history."
|
|
12
22
|
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// "Edit history" item in the chat message options dropdown (rendered
|
|
4
|
+
// server-side in templates/partials/chats/message.tpl, gated on manage
|
|
5
|
+
// privilege via isAdminOrGlobalMod). Opens a modal modelled on core's post
|
|
6
|
+
// diffs modal (public/src/client/topic/diffs.js): a revision selector, the
|
|
7
|
+
// selected revision's content, and a delete-revision button. The API routes
|
|
8
|
+
// it calls are manage-only as well, so the client gating is cosmetic.
|
|
9
|
+
$(document).ready(function () {
|
|
10
|
+
const localeStringOpts = { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
|
11
|
+
|
|
12
|
+
function prettyDate(timestamp) {
|
|
13
|
+
const numeric = parseInt(timestamp, 10) || 0;
|
|
14
|
+
if (!numeric) {
|
|
15
|
+
return '—';
|
|
16
|
+
}
|
|
17
|
+
return new Date(numeric).toLocaleString((config.userLang || 'en-GB').replace('_', '-'), localeStringOpts);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function requestHistory(mid, method, revisionIndex) {
|
|
21
|
+
const suffix = (method === 'DELETE' || method === 'PUT') ? `/${revisionIndex}` : '';
|
|
22
|
+
const response = await fetch(`${config.relative_path || ''}/api/admin-chats/history/${mid}${suffix}`, {
|
|
23
|
+
method: method || 'GET',
|
|
24
|
+
headers: { 'x-csrf-token': config.csrf_token },
|
|
25
|
+
credentials: 'same-origin',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const data = await response.json();
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error((data && data.status && data.status.message) || 'Request failed');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return data;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// The selector lists the current version first, then prior revisions
|
|
37
|
+
// newest-first. Option values keep the revision's index in the stored
|
|
38
|
+
// (oldest-first) list so deletes address the right entry.
|
|
39
|
+
function buildEntries(history, labels) {
|
|
40
|
+
const currentEditor = (history.current.editor && history.current.editor.username) ||
|
|
41
|
+
(history.author && history.author.username) || '';
|
|
42
|
+
const entries = [{
|
|
43
|
+
value: 'current',
|
|
44
|
+
label: `${prettyDate(history.current.edited || history.current.timestamp)} [${currentEditor}] · ${labels.current}`,
|
|
45
|
+
content: history.current.content,
|
|
46
|
+
}];
|
|
47
|
+
|
|
48
|
+
history.revisions.forEach((revision, index) => {
|
|
49
|
+
entries.push({
|
|
50
|
+
value: String(index),
|
|
51
|
+
label: `${prettyDate(revision.replacedAt)} [${revision.editor.username}]`,
|
|
52
|
+
content: revision.content,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return [entries[0]].concat(entries.slice(1).reverse());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function renderState($modal, history, labels) {
|
|
60
|
+
const $select = $modal.find('select');
|
|
61
|
+
const $content = $modal.find('.admin-chat-history-content');
|
|
62
|
+
const $deleteBtn = $modal.find('[data-action="delete-revision"]');
|
|
63
|
+
const $restoreBtn = $modal.find('[data-action="restore-revision"]');
|
|
64
|
+
const $count = $modal.find('.admin-chat-history-count');
|
|
65
|
+
|
|
66
|
+
const entries = buildEntries(history, labels);
|
|
67
|
+
const contentByValue = new Map(entries.map(entry => [entry.value, entry.content]));
|
|
68
|
+
|
|
69
|
+
$select.empty();
|
|
70
|
+
entries.forEach((entry) => {
|
|
71
|
+
const option = document.createElement('option');
|
|
72
|
+
option.value = entry.value;
|
|
73
|
+
option.textContent = entry.label;
|
|
74
|
+
$select.append(option);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
$count.text(labels.count.replace('%1', String(history.revisionCount)));
|
|
78
|
+
|
|
79
|
+
const showSelection = () => {
|
|
80
|
+
const value = $select.val();
|
|
81
|
+
$content.text(contentByValue.get(value) || '');
|
|
82
|
+
$deleteBtn.prop('disabled', value === 'current');
|
|
83
|
+
$restoreBtn.prop('disabled', value === 'current');
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
$select.off('change').on('change', showSelection);
|
|
87
|
+
showSelection();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function openHistoryModal(mid, modals, alerts, translator) {
|
|
91
|
+
const history = await requestHistory(mid);
|
|
92
|
+
|
|
93
|
+
const labels = {
|
|
94
|
+
current: await translator.translate('[[admin-chats:history.current]]'),
|
|
95
|
+
count: await translator.translate('[[admin-chats:history.revisionCount, %1]]'),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// modals.dialog runs the message through the translator itself
|
|
99
|
+
const $modal = await modals.dialog({
|
|
100
|
+
title: '[[admin-chats:history.title]]',
|
|
101
|
+
message: '<p class="admin-chat-history-count text-muted"></p>' +
|
|
102
|
+
'<div class="mb-3">' +
|
|
103
|
+
'<select class="form-control"></select>' +
|
|
104
|
+
'<hr />' +
|
|
105
|
+
'<pre class="admin-chat-history-content text-break mb-3" style="white-space: pre-wrap;"></pre>' +
|
|
106
|
+
'<button class="btn btn-primary" data-action="restore-revision">[[admin-chats:history.restore]]</button> ' +
|
|
107
|
+
'<button class="btn btn-danger" data-action="delete-revision">[[admin-chats:history.deleteRevision]]</button>' +
|
|
108
|
+
'<p class="form-text">[[admin-chats:history.restoreDescription]]</p>' +
|
|
109
|
+
'</div>',
|
|
110
|
+
size: 'large',
|
|
111
|
+
onEscape: true,
|
|
112
|
+
backdrop: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
renderState($modal, history, labels);
|
|
116
|
+
|
|
117
|
+
$modal.find('[data-action="restore-revision"]').on('click', async function () {
|
|
118
|
+
const value = $modal.find('select').val();
|
|
119
|
+
if (value === 'current') {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
await requestHistory(mid, 'PUT', value);
|
|
125
|
+
$modal.modal('hide');
|
|
126
|
+
alerts.success('[[admin-chats:history.restored]]');
|
|
127
|
+
} catch (err) {
|
|
128
|
+
alerts.error('[[admin-chats:errors.history]]');
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
$modal.find('[data-action="delete-revision"]').on('click', function () {
|
|
133
|
+
const value = $modal.find('select').val();
|
|
134
|
+
if (value === 'current') {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
modals.confirm('[[admin-chats:history.confirmDelete]]', async (confirmed) => {
|
|
139
|
+
if (!confirmed) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const fresh = await requestHistory(mid, 'DELETE', value);
|
|
145
|
+
renderState($modal, fresh, labels);
|
|
146
|
+
alerts.success('[[admin-chats:history.deleted]]');
|
|
147
|
+
} catch (err) {
|
|
148
|
+
alerts.error('[[admin-chats:errors.history]]');
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Messages rendered live on the client (incoming messages, and a message
|
|
155
|
+
// re-parsed right after an edit) go through core's parseMessage, which
|
|
156
|
+
// sets isAdminOrGlobalMod = app.user.isAdmin || app.user.isGlobalMod —
|
|
157
|
+
// ignoring the plugin's manage privilege that gates the server-rendered
|
|
158
|
+
// item. Re-add the item on dropdown open when the room payload grants
|
|
159
|
+
// management and the item is missing.
|
|
160
|
+
$(document).on('show.bs.dropdown', '[component="chat/message"] .btn-group', function () {
|
|
161
|
+
if (!ajaxify.data || !ajaxify.data.isAdminOrGlobalMod) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const $menu = $(this).find('.dropdown-menu');
|
|
166
|
+
if (!$menu.length || $menu.find('.admin-chat-history-item').length) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const $message = $(this).closest('[component="chat/message"]');
|
|
171
|
+
const mid = parseInt($message.attr('data-mid'), 10);
|
|
172
|
+
if (!mid) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Same gate as the server-rendered item: only edited messages have a
|
|
177
|
+
// history to show. The edited indicator element exists on every
|
|
178
|
+
// message and carries `hidden` while the message is unedited.
|
|
179
|
+
const $edited = $message.find('[component="chat/message/edited"]');
|
|
180
|
+
if (!$edited.length || $edited.hasClass('hidden')) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
require(['translator'], function (translator) {
|
|
185
|
+
translator.translate('[[admin-chats:history.title]]', function (title) {
|
|
186
|
+
if ($menu.find('.admin-chat-history-item').length) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const $item = $(
|
|
191
|
+
'<li><a href="#" class="dropdown-item rounded-1 admin-chat-history-item" role="menuitem">' +
|
|
192
|
+
'<span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-history text-muted"></i> </span></a></li>'
|
|
193
|
+
);
|
|
194
|
+
$item.find('a').attr('data-mid', String(mid));
|
|
195
|
+
$item.find('span').append(document.createTextNode(title));
|
|
196
|
+
|
|
197
|
+
const $copyText = $menu.find('[data-action="copy-text"]').closest('li');
|
|
198
|
+
if ($copyText.length) {
|
|
199
|
+
$item.insertBefore($copyText);
|
|
200
|
+
} else {
|
|
201
|
+
$menu.append($item);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
$(document).on('click', '.admin-chat-history-item', function (ev) {
|
|
208
|
+
ev.preventDefault();
|
|
209
|
+
|
|
210
|
+
const mid = parseInt($(this).attr('data-mid'), 10);
|
|
211
|
+
if (!mid) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
require(['modals', 'alerts', 'translator'], function (modals, alerts, translator) {
|
|
216
|
+
openHistoryModal(mid, modals, alerts, translator).catch(() => {
|
|
217
|
+
alerts.error('[[admin-chats:errors.history]]');
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -55,6 +55,11 @@
|
|
|
55
55
|
{{{ end }}}
|
|
56
56
|
|
|
57
57
|
{{{ if isAdminOrGlobalMod }}}
|
|
58
|
+
{{{ if messages.edited }}}
|
|
59
|
+
<li>
|
|
60
|
+
<a href="#" class="dropdown-item rounded-1 admin-chat-history-item" data-mid="{messages.messageId}" role="menuitem"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-history text-muted"></i> {{tx("admin-chats:history.title")}}</span></a>
|
|
61
|
+
</li>
|
|
62
|
+
{{{ end }}}
|
|
58
63
|
<li>
|
|
59
64
|
<a href="#" class="dropdown-item rounded-1 chat-ip-button" role="menuitem">
|
|
60
65
|
<span class="d-inline-flex align-items-center gap-2 show"><i class="fa fa-fw fa-info-circle text-muted"></i> {{tx("modules:chat.show-ip")}}</span>
|