diff --git a/CHANGELOG.md b/CHANGELOG.md index b66481805a..731e39ccc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,81 @@ +#### v3.8.4 (2024-08-01) + +##### Chores + +* up widgets (d1663ac2) +* up widgets (cb4bf9dd) +* up persona (324610de) +* incrementing version number - v3.8.3 (97ce2c44) +* update changelog for v3.8.3 (2a239a79) +* incrementing version number - v3.8.2 (72d91251) +* incrementing version number - v3.8.1 (527326f7) +* incrementing version number - v3.8.0 (e228a6eb) +* incrementing version number - v3.7.5 (6882894d) +* incrementing version number - v3.7.4 (6678744c) +* incrementing version number - v3.7.3 (2d62b6f6) +* incrementing version number - v3.7.2 (cc257e7e) +* incrementing version number - v3.7.1 (712365a5) +* incrementing version number - v3.7.0 (9a6153d7) +* incrementing version number - v3.6.7 (86a17e38) +* incrementing version number - v3.6.6 (6604bf37) +* incrementing version number - v3.6.5 (6c653625) +* incrementing version number - v3.6.4 (83d131b4) +* incrementing version number - v3.6.3 (fc7d2bfd) +* incrementing version number - v3.6.2 (0f577a57) +* incrementing version number - v3.6.1 (f1a69468) +* incrementing version number - v3.6.0 (4cdf85f8) +* incrementing version number - v3.5.3 (ed0e8783) +* incrementing version number - v3.5.2 (52fbb2da) +* incrementing version number - v3.5.1 (4c543488) +* incrementing version number - v3.5.0 (d06fb4f0) +* incrementing version number - v3.4.3 (5c984250) +* incrementing version number - v3.4.2 (3f0dac38) +* incrementing version number - v3.4.1 (01e69574) +* incrementing version number - v3.4.0 (fd9247c5) +* incrementing version number - v3.3.9 (5805e770) +* incrementing version number - v3.3.8 (a5603565) +* incrementing version number - v3.3.7 (b26f1744) +* incrementing version number - v3.3.6 (7fb38792) +* incrementing version number - v3.3.4 (a67f84ea) +* incrementing version number - v3.3.3 (f94d239b) +* incrementing version number - v3.3.2 (ec9dac97) +* incrementing version number - v3.3.1 (151cc68f) +* incrementing version number - v3.3.0 (fc1ad70f) +* incrementing version number - v3.2.3 (b06d3e63) +* incrementing version number - v3.2.2 (758ecfcd) +* incrementing version number - v3.2.1 (20145074) +* incrementing version number - v3.2.0 (9ecac38e) +* incrementing version number - v3.1.7 (0b4e81ab) +* incrementing version number - v3.1.6 (b3a3b130) +* incrementing version number - v3.1.5 (ec19343a) +* incrementing version number - v3.1.4 (2452783c) +* incrementing version number - v3.1.3 (3b4e9d3f) +* incrementing version number - v3.1.2 (40fa3489) +* incrementing version number - v3.1.1 (40250733) +* incrementing version number - v3.1.0 (0cb386bd) +* incrementing version number - v3.0.1 (26f6ea49) +* incrementing version number - v3.0.0 (224e08cd) + +##### New Features + +* update chat teasers when a new chat starts, closes #12713 (0204f2aa) +* add downvoteVisibility setting, closes #12698 (269fc068) + +##### Bug Fixes + +* prevent category filter to go to selected cid (10c5cd4c) +* add sizes to maskable so it doesnt crash chrome application tab (ccc86825) +* post preview not working on parent post (a28dd70f) + +##### Refactors + +* hooks page design (cea18d0c) +* closes #12699, allow boolean false for log-colorize (42ac1f9d) + +##### Tests + +* test fix maybe (a73f269f) + #### v3.8.3 (2024-06-27) ##### Chores diff --git a/install/package.json b/install/package.json index c1b813cd95..fc6a03f8ab 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "3.8.3", + "version": "3.8.4", "homepage": "https://www.nodebb.org", "repository": { "type": "git", @@ -104,7 +104,7 @@ "nodebb-plugin-ntfy": "1.7.5", "nodebb-plugin-spam-be-gone": "2.2.2", "nodebb-rewards-essentials": "1.0.0", - "nodebb-theme-harmony": "1.2.63", + "nodebb-theme-harmony": "1.2.64", "nodebb-theme-lavender": "7.1.8", "nodebb-theme-peace": "2.2.6", "nodebb-theme-persona": "13.3.25", diff --git a/public/src/client/chats.js b/public/src/client/chats.js index abaae9f818..1570872d62 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -697,6 +697,12 @@ define('forum/chats', [ data.message.timestamp = Math.min(Date.now(), data.message.timestamp); data.message.timestampISO = utils.toISOString(data.message.timestamp); messages.appendChatMessage($('[component="chat/message/content"]'), data.message); + + Chats.updateTeaser(data.roomId, { + content: utils.stripHTMLTags(utils.decodeHTMLEntities(data.message.content)), + user: data.message.fromUser, + timestampISO: data.message.timestampISO, + }); } }); @@ -754,6 +760,31 @@ define('forum/chats', [ }); }; + Chats.updateTeaser = async function (roomId, teaser) { + if (!ajaxify.data.template.chats || !app.user.userslug) { + return; + } + const roomEl = chatNavWrapper.find(`[data-roomid="${roomId}"]`); + if (roomEl.length) { + const html = await app.parseAndTranslate('partials/chats/room-teaser', { + teaser: teaser, + }); + roomEl.find('[component="chat/room/teaser"]').html(html[0].outerHTML); + roomEl.find('.timeago').timeago(); + } else { + const { rooms } = await api.get(`/chats`, { start: 0, perPage: 2 }); + const room = rooms.find(r => parseInt(r.roomId, 10) === parseInt(roomId, 10)); + if (room) { + const recentEl = components.get('chat/recent'); + const html = await app.parseAndTranslate('chats', 'rooms', { + rooms: [room], + showBottomHr: true, + }); + recentEl.prepend(html); + } + } + }; + Chats.markChatPageElUnread = function (data) { if (!ajaxify.data.template.chats) { return; diff --git a/public/src/client/header/chat.js b/public/src/client/header/chat.js index a087a45939..d52f4c6c72 100644 --- a/public/src/client/header/chat.js +++ b/public/src/client/header/chat.js @@ -42,6 +42,7 @@ define('forum/header/chat', [ return; } chatPage.markChatPageElUnread(data); + chatPage.updateTeaser(data.roomId, data.teaser); } let { count } = await api.get('/chats/unread'); diff --git a/src/api/chats.js b/src/api/chats.js index db07ac32f0..abd5c908f2 100644 --- a/src/api/chats.js +++ b/src/api/chats.js @@ -37,7 +37,7 @@ async function rateLimitExceeded(caller, field) { } chatsAPI.list = async (caller, { uid = caller.uid, start, stop, page, perPage } = {}) => { - if (!start && !stop && !page) { + if ((!utils.isNumber(start) || !utils.isNumber(stop)) && !utils.isNumber(page)) { throw new Error('[[error:invalid-data]]'); } diff --git a/src/events.js b/src/events.js index 41e1f0d29b..d7938e3b5a 100644 --- a/src/events.js +++ b/src/events.js @@ -75,6 +75,7 @@ events.types = [ 'export:uploads', 'account-locked', 'getUsersCSV', + 'getGroupCSV', 'chat-room-deleted', // To add new types from plugins, just Array.push() to this array ]; diff --git a/src/messaging/notifications.js b/src/messaging/notifications.js index 503382cf01..d939bc939b 100644 --- a/src/messaging/notifications.js +++ b/src/messaging/notifications.js @@ -1,6 +1,7 @@ 'use strict'; const winston = require('winston'); +const validator = require('validator'); const batch = require('../batch'); const db = require('../database'); @@ -8,6 +9,7 @@ const notifications = require('../notifications'); const user = require('../user'); const io = require('../socket.io'); const plugins = require('../plugins'); +const utils = require('../utils'); module.exports = function (Messaging) { Messaging.setUserNotificationSetting = async (uid, roomId, value) => { @@ -66,6 +68,13 @@ module.exports = function (Messaging) { // push unread count only for private rooms if (!isPublic) { const uids = await Messaging.getAllUidsInRoomFromSet(`chat:room:${roomId}:uids:online`); + unreadData.teaser = { + content: validator.escape( + String(utils.stripHTMLTags(utils.decodeHTMLEntities(messageObj.content))) + ), + user: messageObj.fromUser, + timestampISO: messageObj.timestampISO, + }; Messaging.pushUnreadCount(uids, unreadData); } diff --git a/src/socket.io/meta.js b/src/socket.io/meta.js index f150102f13..0e59e93849 100644 --- a/src/socket.io/meta.js +++ b/src/socket.io/meta.js @@ -5,6 +5,7 @@ const os = require('os'); const user = require('../user'); const meta = require('../meta'); const topics = require('../topics'); +const privileges = require('../privileges'); const SocketMeta = module.exports; SocketMeta.rooms = {}; @@ -44,6 +45,20 @@ SocketMeta.rooms.enter = async function (socket, data) { throw new Error('[[error:not-allowed]]'); } + if (data.enter && data.enter.startsWith('topic_')) { + const tid = data.enter.split('_').pop(); + if (!await privileges.topics.can('topics:read', tid, socket.uid)) { + throw new Error('[[error:no-privileges]]'); + } + } + + if (data.enter && data.enter.startsWith('category_')) { + const cid = data.enter.split('_').pop(); + if (!await privileges.categories.can('read', cid, socket.uid)) { + throw new Error('[[error:no-privileges]]'); + } + } + leaveCurrentRoom(socket); if (data.enter) { diff --git a/src/user/admin.js b/src/user/admin.js index 369aafee50..35598bbbd9 100644 --- a/src/user/admin.js +++ b/src/user/admin.js @@ -64,7 +64,7 @@ module.exports = function (User) { 'w' ); fs.promises.appendFile(fd, `${fields.map(f => `"${f}"`).join(',')}\n`); - await batch.processSortedSet('group:administrators:members', async (uids) => { + await batch.processSortedSet('users:joindate', async (uids) => { const userFieldsToLoad = fields.filter(field => field !== 'ip' && field !== 'password'); const usersData = await User.getUsersFields(uids, userFieldsToLoad); let userIps = []; diff --git a/src/views/admin/advanced/hooks.tpl b/src/views/admin/advanced/hooks.tpl index 44a417917b..61aaa26ebc 100644 --- a/src/views/admin/advanced/hooks.tpl +++ b/src/views/admin/advanced/hooks.tpl @@ -11,7 +11,7 @@