Merge branch 'master' into develop

isekai-main
Barış Soner Uşaklı 1 year ago
commit cbc092be1e

@ -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

@ -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",

@ -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);
}
});
};

@ -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,
};

@ -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;
}
}
};

@ -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 || {});

@ -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);
}
}
};
};

@ -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,
});

@ -26,7 +26,7 @@ module.exports = {
});
await db.sortedSetAddBulk(bulkAdd);
}, {
batch: 500,
batch: 100,
});
},
};

@ -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,
});

@ -1,9 +1,10 @@
<li data-type="item" class="list-group-item">
<li data-type="item" class="list-group-item" data-theme-name="{custom-skin-name}" data-theme="{custom-skin-name}">
<div class="d-flex justify-content-between align-items-center">
<div class="">
<strong>{custom-skin-name}</strong>
</div>
<div class="">
<button type="button" data-action="use" class="btn btn-sm btn-primary">[[admin/appearance/skins:select-skin]]</button>
<button type="button" data-type="edit" class="btn btn-sm btn-light"><i class="fa fa-edit text-primary"></i></button>
<button type="button" data-type="remove" class="btn btn-sm btn-light"><i class="fa fa-trash-o text-danger"></i></button>
</div>

@ -1,5 +1,5 @@
{{{ each themes }}}
<div class="col-lg-4 col-md-6 col-12 mb-4" data-type="{./type}" data-theme="{./id}"{{{ if ./css }}} data-css="{./css}" {{{ end }}}>
<div class="col-lg-4 col-md-6 col-12 mb-4" data-type="{./type}" data-theme-name="{./name}" data-theme="{./id}"{{{ if ./css }}} data-css="{./css}" {{{ end }}}>
<div class="card h-100">
<img src="{./screenshot_url}" class="card-img-top">
<div class="card-body">

@ -1,13 +1,11 @@
<div id="post-tooltip" class="card card-body shadow text-bg-light" style="position:absolute; z-index: 1;">
<div class="clearfix">
<div class="icon float-start">
<div id="post-tooltip" class="card card-body shadow bg-body text-body z-1 position-absolute">
<div class="d-flex flex-column gap-2">
<div class="d-flex gap-1 align-items-center">
<a href="{{{ if post.user.userslug }}}{config.relative_path}/user/{post.user.userslug}{{{ else }}}#{{{ end }}}">
{buildAvatar(post.user, "24px", true, "", "user/picture")} {post.user.username}
</a>
<span class="timeago text-xs" title="{post.timestampISO}"></span>
</div>
<small class="float-end">
<span class="timeago" title="{post.timestampISO}"></span>
</small>
<div class="content">{post.content}</div>
</div>
<div class="content">{post.content}</div>
</div>

Loading…
Cancel
Save