Merge branch 'master' into develop

This commit is contained in:
Barış Soner Uşaklı
2026-03-10 11:01:14 -04:00
8 changed files with 66 additions and 51 deletions

View File

@@ -78,6 +78,8 @@ get:
properties:
pid:
type: number
uid:
type: number
timestamp:
type: number
content:
@@ -136,6 +138,8 @@ get:
properties:
pid:
type: number
uid:
type: number
tid:
type: number
timestamp:

View File

@@ -138,6 +138,8 @@ get:
properties:
pid:
type: number
uid:
type: number
tid:
type: number
timestamp:

View File

@@ -99,7 +99,7 @@ module.exports = function (Categories) {
const [topicData, crossposts] = await Promise.all([
topics.getTopicsFields(
tids,
['tid', 'mainPid', 'slug', 'title', 'teaserPid', 'cid', 'postcount']
['tid', 'uid', 'mainPid', 'slug', 'title', 'teaserPid', 'cid', 'postcount']
),
topics.crossposts.get(tids),
]);
@@ -137,11 +137,12 @@ module.exports = function (Categories) {
function assignTopicsToCategories(categories, topics) {
categories.forEach((category) => {
if (category) {
const categoryCid = String(category.cid);
category.posts = topics.filter(t =>
t.cid &&
(t.cid === category.cid ||
(t.parentCids && t.parentCids.includes(category.cid)) ||
(t.crossposts.some(({ cid }) => parseInt(cid, 10) === category.cid))
(String(t.cid) === categoryCid ||
(t.parentCids && t.parentCids.includes(categoryCid)) ||
(t.crossposts.some(({ cid }) => String(cid) === categoryCid))
))
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, parseInt(category.numRecentReplies, 10));

View File

@@ -75,12 +75,12 @@ async function uploadAsImage(req, uploadedFile) {
let fileObj = await uploadsController.uploadFile(req.uid, uploadedFile);
// sharp can't save svgs skip resize for them
const isSVG = uploadedFile.type === 'image/svg+xml';
if (isSVG || meta.config.resizeImageWidth === 0 || meta.config.resizeImageWidthThreshold === 0) {
return fileObj;
const resizeDisabled = meta.config.resizeImageWidth === 0 || meta.config.resizeImageWidthThreshold === 0;
if (!isSVG && !resizeDisabled) {
fileObj = await resizeImage({ ...fileObj, type: uploadedFile.type });
}
fileObj = await resizeImage({ ...fileObj, type: uploadedFile.type });
return { url: fileObj.url };
return { url: fileObj.url, name: fileObj.name };
}
async function uploadAsFile(req, uploadedFile) {

View File

@@ -85,21 +85,31 @@ describe('Categories', () => {
});
describe('Categories.getRecentTopicReplies', () => {
it('should not throw', (done) => {
Categories.getCategoryById({
it('should not throw', async () => {
const categoryData = await Categories.getCategoryById({
cid: categoryObj.cid,
set: `cid:${categoryObj.cid}:tids`,
reverse: true,
start: 0,
stop: -1,
uid: 0,
}, (err, categoryData) => {
assert.ifError(err);
Categories.getRecentTopicReplies(categoryData, 0, {}, (err) => {
assert.ifError(err);
done();
});
});
await Categories.getRecentTopicReplies([categoryData], 0, {});
});
it('should return posts in child category as teaser on parent category' , async () => {
const { cid: parentCid } = await Categories.create({ name: 'theparent' });
const { cid: childCid } = await Categories.create({ name: 'thechild', parentCid });
await Topics.post({ uid: posterUid, title: 'inparent', content: 'post in parent', cid: parentCid });
await Topics.post({ uid: posterUid, title: 'inchild', content: 'post in child', cid: childCid });
const categoryData = await Categories.getCategories([parentCid, childCid]);
Categories.getTree(categoryData, 0);
await Categories.getRecentTopicReplies(categoryData, 0, {}),
assert.strictEqual(String(categoryData[0].cid), String(parentCid));
assert.strictEqual(categoryData[0].posts[0].uid, posterUid);
assert.strictEqual(categoryData[0].posts[0].content, 'post in child');
});
});

1
test/files/nodebb.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 91.3 511.9 329.3"><g transform="translate(1 1)"><radialGradient id="a" cx="-48.925" cy="655.671" r="10.303" gradientTransform="matrix(-43.5372 31.9464 31.946 43.537 -22741.309 -26892.488)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#2a6cbe"/><stop offset=".387" style="stop-color:#2062bc"/><stop offset=".492" style="stop-color:#1f5fbc"/><stop offset=".666" style="stop-color:#1c5abd"/><stop offset=".854" style="stop-color:#1956bc"/><stop offset="1" style="stop-color:#1851be"/></radialGradient><path d="M248.9 90.4v236.1c0 39.3-.7 66.4-.7 93.1H118.8C41.4 419.6-1 383.4-1 326.5c0-38.1 21.7-65.8 55.6-77.1-27.8-11.8-44.8-38.1-44.8-70.5 0-52.7 42-88.4 115.5-88.4h123.6zm135.7 0c73.6 0 115.5 35.7 115.5 88.4 0 32.4-17 58.8-44.8 70.5 33.9 11.3 55.6 39 55.6 77.1 0 56.9-42.4 93.1-119.8 93.1H261.8c0-26.7-.6-53.8-.7-93.1V90.3h123.5zM182.4 278.5H124c-38.7 0-58 14.6-58 43.3 0 30.1 17.9 45.1 53.3 45.1h30.6c24.5 0 32.5-11.8 32.5-47.5zm203.6 0h-58.5v40.9c0 35.7 8 47.5 32.5 47.5h30.6c35.4 0 53.3-15 53.3-45.1.1-28.7-19.2-43.3-57.9-43.3M182.9 143.1h-53.8c-34.4 0-52.3 15.5-52.3 41.8s17.9 41.8 52.3 41.8h53.8zm198 0h-53.8v83.7h53.8c34.4 0 52.3-15.5 52.3-41.8s-17.9-41.9-52.3-41.9" style="fill:url(#a)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -240,8 +240,9 @@ describe('Topic thumbs', () => {
});
it('should succeed with a valid tid', async () => {
const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF);
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF);
assert.strictEqual(response.statusCode, 200);
assert.deepStrictEqual(Object.keys(body.response.images[0]), ['url', 'name']);
});
it('should succeed with uploader plugins', async () => {

View File

@@ -35,41 +35,23 @@ describe('Upload Controllers', () => {
let regularUid;
let maliciousUid;
before((done) => {
async.series({
category: function (next) {
categories.create({
name: 'Test Category',
description: 'Test category created by testing script',
}, next);
},
adminUid: function (next) {
user.create({ username: 'admin', password: 'barbar' }, next);
},
regularUid: function (next) {
user.create({ username: 'regular', password: 'zugzug' }, next);
},
maliciousUid: function (next) {
user.create({ username: 'malicioususer', password: 'herpderp' }, next);
},
}, (err, results) => {
if (err) {
return done(err);
}
adminUid = results.adminUid;
regularUid = results.regularUid;
maliciousUid = results.maliciousUid;
cid = results.category.cid;
topics.post({ uid: adminUid, title: 'test topic title', content: 'test topic content', cid: results.category.cid }, (err, result) => {
if (err) {
return done(err);
}
tid = result.topicData.tid;
pid = result.postData.pid;
groups.join('administrators', adminUid, done);
});
before(async () => {
const category = await categories.create({
name: 'Test Category',
description: 'Test category created by testing script',
});
cid = category.cid;
adminUid = await user.create({ username: 'admin', password: 'barbar' });
groups.join('administrators', adminUid);
regularUid = await user.create({ username: 'regular', password: 'zugzug' });
maliciousUid = await user.create({ username: 'malicioususer', password: 'herpderp' });
const result = await topics.post({ uid: adminUid, title: 'test topic title', content: 'test topic content', cid });
tid = result.topicData.tid;
pid = result.postData.pid;
});
describe('regular user uploads rate limits', () => {
@@ -119,6 +101,19 @@ describe('Upload Controllers', () => {
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
assert.deepStrictEqual(Object.keys(body.response.images[0]), ['url', 'name']);
});
it('should upload an svg image to a post', async () => {
const oldValue = meta.config.allowedFileExtensions;
meta.config.allowedFileExtensions = 'png,jpg,bmp,html,svg';
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/nodebb.svg'), {}, jar, csrf_token);
assert.equal(response.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
assert.deepStrictEqual(Object.keys(body.response.images[0]), ['url', 'name']);
meta.config.allowedFileExtensions = oldValue;
});
it('should upload an image to a post and then delete the upload', async () => {
@@ -192,6 +187,7 @@ describe('Upload Controllers', () => {
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
assert.deepStrictEqual(Object.keys(body.response.images[0]), ['url', 'name']);
});
it('should upload a file with utf8 characters in the name to a post', async () => {