diff --git a/CHANGELOG.md b/CHANGELOG.md index c4cca4a71f..3c0ad6f4ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,65 @@ +#### v3.3.2 (2023-08-18) + +##### Chores + +* incrementing version number - v3.3.1 (151cc68f) +* update changelog for v3.3.1 (6f961f9c) +* 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) + +##### Bug Fixes + +* upgrade script (c02f1d70) + +#### v3.3.1 (2023-08-18) + +##### Chores + +* up themes (62231baa) +* incrementing version number - v3.3.0 (fc1ad70f) +* update changelog for v3.3.0 (46f7405d) +* 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 + +* #11930, ability to set custom skins as default (db07ab15) + +##### Bug Fixes + +* some more upgrade script fixes (f23b0b5b) +* #11906, userData.sso — don't serve deauthUrl or non-associated url if caller uid is not same as target uid (19e047e2) +* include latin-ext subset of fonts in admin styles (#11918) (556a1c48) + +##### Other Changes + +* fix lint (d1949cee) + #### v3.3.0 (2023-08-16) ##### Chores diff --git a/install/package.json b/install/package.json index 99243a7955..fe1241d406 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "3.3.0", + "version": "3.3.2", "homepage": "https://www.nodebb.org", "repository": { "type": "git", diff --git a/public/src/admin/appearance/skins.js b/public/src/admin/appearance/skins.js index 9d3b7b92d3..04b2676615 100644 --- a/public/src/admin/appearance/skins.js +++ b/public/src/admin/appearance/skins.js @@ -2,8 +2,8 @@ define('admin/appearance/skins', [ - 'translator', 'alerts', 'settings', -], function (translator, alerts, settings) { + 'translator', 'alerts', 'settings', 'hooks', +], function (translator, alerts, settings, hooks) { const Skins = {}; Skins.init = function () { @@ -11,9 +11,19 @@ define('admin/appearance/skins', [ $.ajax({ method: 'get', url: 'https://bootswatch.com/api/5.json', - }).done(Skins.render); + }).done((bsData) => { + hooks.on('action:settings.sorted-list.loaded', (data) => { + if (data.hash === 'custom-skins') { + // lower case all custom-skin ids after load + $('.custom-skin-settings [data-type="list"] [data-theme]').each((i, el) => { + $(el).attr('data-theme', $(el).attr('data-theme').toLowerCase()); + }); + Skins.render(bsData); + } + }); + settings.load('custom-skins', $('.custom-skin-settings')); + }); - settings.load('custom-skins', $('.custom-skin-settings')); $('#save-custom-skins').on('click', function () { settings.save('custom-skins', $('.custom-skin-settings'), function () { alerts.success('[[admin/appearance/skins:save-custom-skins-success]]'); @@ -33,13 +43,12 @@ define('admin/appearance/skins', [ if (action && action === 'use') { const parentEl = target.parents('[data-theme]'); - const themeType = parentEl.attr('data-type'); const cssSrc = parentEl.attr('data-css'); const themeId = parentEl.attr('data-theme'); - + const themeName = parentEl.attr('data-theme-name'); socket.emit('admin.themes.set', { - type: themeType, + type: 'bootswatch', id: themeId, src: cssSrc, }, function (err) { @@ -52,7 +61,7 @@ define('admin/appearance/skins', [ alert_id: 'admin:theme', type: 'info', title: '[[admin/appearance/skins:skin-updated]]', - message: themeId ? ('[[admin/appearance/skins:applied-success, ' + themeId + ']]') : '[[admin/appearance/skins:revert-success]]', + message: themeId ? ('[[admin/appearance/skins:applied-success, ' + themeName + ']]') : '[[admin/appearance/skins:revert-success]]', timeout: 5000, }); }); @@ -67,7 +76,7 @@ define('admin/appearance/skins', [ themes: bootswatch.themes.map(function (theme) { return { type: 'bootswatch', - id: theme.name, + id: theme.name.toLowerCase(), name: theme.name, description: theme.description, screenshot_url: theme.thumbnail, @@ -82,9 +91,7 @@ define('admin/appearance/skins', [ if (app.config.bootswatchSkin) { const skin = app.config.bootswatchSkin; - highlightSelectedTheme( - skin.charAt(0).toUpperCase() + skin.slice(1) - ); + highlightSelectedTheme(skin); } }); }; diff --git a/src/database/mongo/connection.js b/src/database/mongo/connection.js index 2f5d64e714..b5c375b4e6 100644 --- a/src/database/mongo/connection.js +++ b/src/database/mongo/connection.js @@ -44,7 +44,7 @@ connection.getConnectionString = function (mongo) { connection.getConnectionOptions = function (mongo) { mongo = mongo || nconf.get('mongo'); const connOptions = { - maxPoolSize: 10, + maxPoolSize: 20, minPoolSize: 3, connectTimeoutMS: 90000, }; diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index 9dbf947b1a..4a8bbc691d 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -574,7 +574,7 @@ module.exports = function (module) { if (processFn && processFn.constructor && processFn.constructor.name !== 'AsyncFunction') { processFn = util.promisify(processFn); } - + let iteration = 1; while (!done) { /* eslint-disable no-await-in-loop */ const item = await cursor.next(); @@ -585,12 +585,12 @@ module.exports = function (module) { } if (ids.length >= options.batch || (done && ids.length !== 0)) { - await processFn(ids); - - ids.length = 0; - if (options.interval) { + if (iteration > 1 && options.interval) { await sleep(options.interval); } + await processFn(ids); + iteration += 1; + ids.length = 0; } } }; diff --git a/src/database/postgres/connection.js b/src/database/postgres/connection.js index 54cdc5ebee..19d796d7ed 100644 --- a/src/database/postgres/connection.js +++ b/src/database/postgres/connection.js @@ -28,6 +28,8 @@ connection.getConnectionOptions = function (postgres) { password: postgres.password, database: postgres.database, ssl: String(postgres.ssl) === 'true', + max: 20, + connectionTimeoutMillis: 90000, }; return _.merge(connOptions, postgres.options || {}); diff --git a/src/database/postgres/sorted.js b/src/database/postgres/sorted.js index 70e66af314..2b707a3a7d 100644 --- a/src/database/postgres/sorted.js +++ b/src/database/postgres/sorted.js @@ -677,7 +677,7 @@ SELECT z."value", z."score" if (process && process.constructor && process.constructor.name !== 'AsyncFunction') { process = util.promisify(process); } - + let iteration = 1; while (true) { /* eslint-disable no-await-in-loop */ let rows = await cursor.readAsync(batchSize); @@ -692,14 +692,15 @@ SELECT z."value", z."score" rows = rows.map(r => r.value); } try { + if (iteration > 1 && options.interval) { + await sleep(options.interval); + } await process(rows); + iteration += 1; } catch (err) { await client.release(); throw err; } - if (options.interval) { - await sleep(options.interval); - } } }; }; diff --git a/src/upgrades/3.3.0/chat_message_mids.js b/src/upgrades/3.3.0/chat_message_mids.js index d7f349ae98..5f97a85121 100644 --- a/src/upgrades/3.3.0/chat_message_mids.js +++ b/src/upgrades/3.3.0/chat_message_mids.js @@ -1,5 +1,6 @@ -'use strict'; +/* eslint-disable no-await-in-loop */ +'use strict'; const db = require('../../database'); const batch = require('../../batch'); @@ -13,7 +14,7 @@ module.exports = { progress.total = await db.sortedSetCard(`chat:rooms`); await batch.processSortedSet(`chat:rooms`, async (roomIds) => { progress.incr(roomIds.length); - await Promise.all(roomIds.map(async (roomId) => { + for (const roomId of roomIds) { await batch.processSortedSet(`chat:room:${roomId}:mids`, async (mids) => { let messageData = await db.getObjects(mids.map(mid => `message:${mid}`)); messageData.forEach((m, idx) => { @@ -36,7 +37,7 @@ module.exports = { }, { batch: 500, }); - })); + } }, { batch: 500, }); diff --git a/src/upgrades/3.3.0/chat_room_online_zset.js b/src/upgrades/3.3.0/chat_room_online_zset.js index 0a57076fa0..409d8bc0ef 100644 --- a/src/upgrades/3.3.0/chat_room_online_zset.js +++ b/src/upgrades/3.3.0/chat_room_online_zset.js @@ -26,7 +26,7 @@ module.exports = { }); await db.sortedSetAddBulk(bulkAdd); }, { - batch: 500, + batch: 100, }); }, }; diff --git a/src/upgrades/3.3.0/chat_room_refactor.js b/src/upgrades/3.3.0/chat_room_refactor.js index 7c008a9764..fbf54418c3 100644 --- a/src/upgrades/3.3.0/chat_room_refactor.js +++ b/src/upgrades/3.3.0/chat_room_refactor.js @@ -1,12 +1,10 @@ -'use strict'; - +/* eslint-disable no-await-in-loop */ -const _ = require('lodash'); +'use strict'; const db = require('../../database'); const batch = require('../../batch'); - module.exports = { name: 'Update chat messages to add roomId field', timestamp: Date.UTC(2023, 6, 2), @@ -18,47 +16,71 @@ module.exports = { for (let i = 1; i <= nextChatRoomId; i++) { allRoomIds.push(i); } - progress.total = allRoomIds.length; - await batch.processArray(allRoomIds, async (roomIds) => { - progress.incr(roomIds.length); - const [arrayOfUids, arrayOfRoomData] = await Promise.all([ - db.getSortedSetsMembers(roomIds.map(roomId => `chat:room:${roomId}:uids`)), - db.getObjects(roomIds.map(roomId => `chat:room:${roomId}`)), - ]); + progress.total = 0; - await Promise.all(roomIds.map(async (roomId, index) => { - const uids = arrayOfUids[index]; - const roomData = arrayOfRoomData[index]; - if (!uids.length && !roomData) { - return; - } - if (roomData && roomData.owner && !uids.includes(String(roomData.owner))) { - uids.push(roomData.owner); + // calculate user count and set progress.total + await batch.processArray(allRoomIds, async (roomIds) => { + const arrayOfRoomData = await db.getObjects(roomIds.map(roomId => `chat:room:${roomId}`)); + await Promise.all(roomIds.map(async (roomId, idx) => { + const roomData = arrayOfRoomData[idx]; + if (roomData) { + const userCount = await db.sortedSetCard(`chat:room:${roomId}:uids`); + progress.total += userCount; } - const userKeys = uids.map(uid => `uid:${uid}:chat:room:${roomId}:mids`); - const mids = await db.getSortedSetsMembers(userKeys); - const uniqMids = _.uniq(_.flatten(mids)); - let messageData = await db.getObjects(uniqMids.map(mid => `message:${mid}`)); - messageData.forEach((m, idx) => { - if (m) { - m.mid = parseInt(uniqMids[idx], 10); - } - }); - messageData = messageData.filter(Boolean); + })); + }, { + batch: 500, + }); + + await batch.processArray(allRoomIds, async (roomIds) => { + const arrayOfRoomData = await db.getObjects(roomIds.map(roomId => `chat:room:${roomId}`)); + for (const roomData of arrayOfRoomData) { + if (roomData) { + const midsSeen = {}; + const { roomId } = roomData; + await batch.processSortedSet(`chat:room:${roomId}:uids`, async (uids) => { + for (const uid of uids) { + await batch.processSortedSet(`uid:${uid}:chat:room:${roomId}:mids`, async (mids) => { + const uniqMids = mids.filter(mid => !midsSeen.hasOwnProperty(mid)); + if (!uniqMids.length) { + return; + } - const bulkSet = messageData.map( - msg => [`message:${msg.mid}`, { roomId: roomId }] - ); + let messageData = await db.getObjects(uniqMids.map(mid => `message:${mid}`)); + messageData.forEach((m, idx) => { + if (m) { + m.mid = parseInt(uniqMids[idx], 10); + } + }); + messageData = messageData.filter(Boolean); - await db.setObjectBulk(bulkSet); - await db.setObjectField(`chat:room:${roomId}`, 'userCount', uids.length); - await db.sortedSetAdd( - `chat:room:${roomId}:mids`, - messageData.map(m => m.timestamp), - messageData.map(m => m.mid), - ); - await db.deleteAll(userKeys); - })); + const bulkSet = messageData.map( + msg => [`message:${msg.mid}`, { roomId: roomId }] + ); + + await db.setObjectBulk(bulkSet); + await db.sortedSetAdd( + `chat:room:${roomId}:mids`, + messageData.map(m => m.timestamp), + messageData.map(m => m.mid), + ); + uniqMids.forEach((mid) => { + midsSeen[mid] = 1; + }); + }, { + batch: 500, + }); + // eslint-disable-next-line no-await-in-loop + await db.deleteAll(`uid:${uid}:chat:room:${roomId}:mids`); + } + progress.incr(uids.length); + }, { + batch: 500, + }); + const userCount = await db.sortedSetCard(`chat:room:${roomId}:uids`); + await db.setObjectField(`chat:room:${roomId}`, 'userCount', userCount); + } + } }, { batch: 500, }); diff --git a/src/views/admin/partials/appearance/skins/item-custom-skin.tpl b/src/views/admin/partials/appearance/skins/item-custom-skin.tpl index 005f7b6dab..951ce8aef6 100644 --- a/src/views/admin/partials/appearance/skins/item-custom-skin.tpl +++ b/src/views/admin/partials/appearance/skins/item-custom-skin.tpl @@ -1,9 +1,10 @@ -
  • +
  • {custom-skin-name}
    +
    diff --git a/src/views/admin/partials/theme_list.tpl b/src/views/admin/partials/theme_list.tpl index 4ac0a67743..b583511234 100644 --- a/src/views/admin/partials/theme_list.tpl +++ b/src/views/admin/partials/theme_list.tpl @@ -1,5 +1,5 @@ {{{ each themes }}} -
    +
    diff --git a/src/views/partials/topic/post-preview.tpl b/src/views/partials/topic/post-preview.tpl index 696a673f19..107075eef3 100644 --- a/src/views/partials/topic/post-preview.tpl +++ b/src/views/partials/topic/post-preview.tpl @@ -1,13 +1,11 @@ -
    -