mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-26 09:19:55 +01:00
Merge commit 'cf4f5447bb168b9bac32ac7ddbe567f273966b88' into v2.x
This commit is contained in:
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,17 +1,28 @@
|
||||
#### v2.5.2 (2022-09-04)
|
||||
|
||||
##### Chores
|
||||
|
||||
* incrementing version number - v2.5.1 (ce3aa950)
|
||||
* update changelog for v2.5.1 (2b2fd4f3)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
* registration regression, closes #10875 (f6f37dc1)
|
||||
|
||||
##### Other Changes
|
||||
|
||||
* fix lint error (b45e2413)
|
||||
|
||||
##### Tests
|
||||
|
||||
* disable nbbpm test temporarily (1dc79d76)
|
||||
|
||||
#### v2.5.1 (2022-09-02)
|
||||
|
||||
##### Chores
|
||||
|
||||
* incrementing version number - v2.5.0 (01d276cb)
|
||||
* update changelog for v2.5.0 (1076285d)
|
||||
* incrementing version number - v2.4.5 (dd3e1a28)
|
||||
* incrementing version number - v2.4.4 (d5525c87)
|
||||
* incrementing version number - v2.4.3 (9c647c6c)
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
@@ -34,15 +45,7 @@
|
||||
* remove client-side js file for tpl that no longer exists (bc2ea860)
|
||||
* incrementing version number - v2.4.5 (dd3e1a28)
|
||||
* update changelog for v2.4.5 (d505cc47)
|
||||
* incrementing version number - v2.4.4 (d5525c87)
|
||||
* incrementing version number - v2.4.3 (9c647c6c)
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* update changelog for v2.4.1 (20a661e1)
|
||||
* incrementing version number - v2.4.1 (fecf31bd)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
* **deps:**
|
||||
* update dependency eslint to v8.22.0 (#10835) (8fce68d3)
|
||||
* update mongo docker tag to v3.7 (8afaed22)
|
||||
@@ -105,12 +108,6 @@
|
||||
|
||||
* incrementing version number - v2.4.4 (d5525c87)
|
||||
* update changelog for v2.4.4 (77e492b8)
|
||||
* incrementing version number - v2.4.3 (9c647c6c)
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
@@ -123,11 +120,6 @@
|
||||
|
||||
* incrementing version number - v2.4.3 (9c647c6c)
|
||||
* update changelog for v2.4.3 (06da15a5)
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
@@ -139,10 +131,6 @@
|
||||
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* update changelog for v2.4.2 (ba7a3466)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
@@ -154,9 +142,6 @@
|
||||
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* update changelog for v2.4.1 (4b6baabb)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Documentation Changes
|
||||
|
||||
@@ -181,8 +166,6 @@
|
||||
* update docker/setup-buildx-action action to v2 (371ac032)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* update changelog for v2.4.0 (c4714ff7)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
@@ -200,7 +183,6 @@
|
||||
* opt-out of dependabot, due to conflicts with renovate (70d60289)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* update changelog for v2.3.1 (2f487175)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
* **i18n:**
|
||||
* fallback strings for new resources: nodebb.admin-settings-email (cdaa8f21)
|
||||
* fallback strings for new resources: nodebb.admin-settings-email (3e56c547)
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
"@nodebb/bootswatch": "3.4.2",
|
||||
"nconf": "0.12.0",
|
||||
"nodebb-plugin-2factor": "5.0.2",
|
||||
"nodebb-plugin-composer-default": "9.1.0",
|
||||
"nodebb-plugin-composer-default": "9.1.1",
|
||||
"nodebb-plugin-dbsearch": "5.1.5",
|
||||
"nodebb-plugin-emoji": "4.0.4",
|
||||
"nodebb-plugin-emoji-android": "3.0.0",
|
||||
@@ -186,4 +186,4 @@
|
||||
"url": "https://github.com/barisusakli"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ define('forum/topic/diffs', ['api', 'bootbox', 'alerts', 'forum/topic/images'],
|
||||
posts: [data],
|
||||
}, function ($html) {
|
||||
$postContainer.empty().append($html);
|
||||
$postContainer.find('.timeago').timeago();
|
||||
});
|
||||
}).catch(alerts.error);
|
||||
};
|
||||
|
||||
@@ -158,7 +158,7 @@ define('forum/topic/events', [
|
||||
hooks.fire('action:posts.edited', data);
|
||||
}
|
||||
|
||||
if (data.topic.tags && tagsUpdated(data.topic.tags)) {
|
||||
if (data.topic.tags && data.topic.tagsupdated) {
|
||||
Benchpress.render('partials/topic/tags', { tags: data.topic.tags }).then(function (html) {
|
||||
const tags = $('.tags');
|
||||
|
||||
@@ -171,19 +171,6 @@ define('forum/topic/events', [
|
||||
postTools.removeMenu(components.get('post', 'pid', data.post.pid));
|
||||
}
|
||||
|
||||
function tagsUpdated(tags) {
|
||||
if (tags.length !== $('.tags').first().children().length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tags.length; i += 1) {
|
||||
if (!$('.tags .tag-item[data-tag="' + tags[i].value + '"]').length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onPostPurged(postData) {
|
||||
if (!postData || parseInt(postData.tid, 10) !== parseInt(ajaxify.data.tid, 10)) {
|
||||
return;
|
||||
|
||||
@@ -151,7 +151,7 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t
|
||||
|
||||
const taskbarEl = $('<li></li>')
|
||||
.addClass(data.options.className)
|
||||
.html('<a href="#"' + (data.options.image ? ' style="background-image: url(\'' + data.options.image + '\'); background-size: cover;"' : '') + '>' +
|
||||
.html('<a href="#"' + (data.options.image ? ' style="background-image: url(\'' + data.options.image.replace(///g, '/') + '\'); background-size: cover;"' : '') + '>' +
|
||||
(data.options.icon ? '<i class="fa ' + data.options.icon + '"></i> ' : '') +
|
||||
'<span aria-label="' + title + '" component="taskbar/title">' + title + '</span>' +
|
||||
'</a>')
|
||||
@@ -186,7 +186,7 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t
|
||||
element.find('i').attr('class', 'fa fa-' + value);
|
||||
break;
|
||||
case 'image':
|
||||
element.find('a').css('background-image', value ? 'url("' + value + '")' : '');
|
||||
element.find('a').css('background-image', value ? 'url("' + value.replace(///g, '/') + '")' : '');
|
||||
break;
|
||||
case 'background-color':
|
||||
element.find('a').css('background-color', value);
|
||||
|
||||
@@ -165,7 +165,11 @@ module.exports = function (middleware) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
|
||||
const uid = await user.getUidByUserslug(req.params.userslug);
|
||||
if (!['uid', 'userslug'].some(param => req.params.hasOwnProperty(param))) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
|
||||
const uid = req.params.uid || await user.getUidByUserslug(req.params.userslug);
|
||||
let allowed = await privileges.users.canEdit(req.uid, uid);
|
||||
if (allowed) {
|
||||
return next();
|
||||
|
||||
@@ -7,7 +7,7 @@ const db = require('../database');
|
||||
const meta = require('../meta');
|
||||
const plugins = require('../plugins');
|
||||
const translator = require('../translator');
|
||||
|
||||
const topics = require('../topics');
|
||||
|
||||
module.exports = function (Posts) {
|
||||
const Diffs = {};
|
||||
@@ -38,16 +38,24 @@ module.exports = function (Posts) {
|
||||
};
|
||||
|
||||
Diffs.save = async function (data) {
|
||||
const { pid, uid, oldContent, newContent, edited } = data;
|
||||
const { pid, uid, oldContent, newContent, edited, topic } = data;
|
||||
const editTimestamp = edited || Date.now();
|
||||
const patch = diff.createPatch('', newContent, oldContent);
|
||||
const diffData = {
|
||||
uid: uid,
|
||||
pid: pid,
|
||||
};
|
||||
if (oldContent !== newContent) {
|
||||
diffData.patch = diff.createPatch('', newContent, oldContent);
|
||||
}
|
||||
if (topic.renamed) {
|
||||
diffData.title = topic.oldTitle;
|
||||
}
|
||||
if (topic.tagsupdated && Array.isArray(topic.oldTags)) {
|
||||
diffData.tags = topic.oldTags.map(tag => tag && tag.value).filter(Boolean).join(',');
|
||||
}
|
||||
await Promise.all([
|
||||
db.listPrepend(`post:${pid}:diffs`, editTimestamp),
|
||||
db.setObject(`diff:${pid}.${editTimestamp}`, {
|
||||
uid: uid,
|
||||
pid: pid,
|
||||
patch: patch,
|
||||
}),
|
||||
db.setObject(`diff:${pid}.${editTimestamp}`, diffData),
|
||||
]);
|
||||
};
|
||||
|
||||
@@ -71,6 +79,8 @@ module.exports = function (Posts) {
|
||||
content: post.content,
|
||||
req: req,
|
||||
timestamp: since,
|
||||
title: post.topic.title,
|
||||
tags: post.topic.tags.map(tag => tag.value),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -130,6 +140,16 @@ module.exports = function (Posts) {
|
||||
// Replace content with re-constructed content from that point in time
|
||||
post[0].content = diffs.reduce(applyPatch, validator.unescape(post[0].content));
|
||||
|
||||
const titleDiffs = diffs.filter(d => d.hasOwnProperty('title') && d.title);
|
||||
if (titleDiffs.length && post[0].topic) {
|
||||
post[0].topic.title = validator.unescape(String(titleDiffs[titleDiffs.length - 1].title));
|
||||
}
|
||||
const tagDiffs = diffs.filter(d => d.hasOwnProperty('tags') && d.tags);
|
||||
if (tagDiffs.length && post[0].topic) {
|
||||
const tags = tagDiffs[tagDiffs.length - 1].tags.split(',').map(tag => ({ value: tag }));
|
||||
post[0].topic.tags = await topics.getTagData(tags);
|
||||
}
|
||||
|
||||
return post[0];
|
||||
}
|
||||
|
||||
@@ -144,9 +164,12 @@ module.exports = function (Posts) {
|
||||
}
|
||||
|
||||
function applyPatch(content, aDiff) {
|
||||
const result = diff.applyPatch(content, aDiff.patch, {
|
||||
fuzzFactor: 1,
|
||||
});
|
||||
return typeof result === 'string' ? result : content;
|
||||
if (aDiff && aDiff.patch) {
|
||||
const result = diff.applyPatch(content, aDiff.patch, {
|
||||
fuzzFactor: 1,
|
||||
});
|
||||
return typeof result === 'string' ? result : content;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,7 +29,9 @@ module.exports = function (Posts) {
|
||||
throw new Error('[[error:no-post]]');
|
||||
}
|
||||
|
||||
const topicData = await topics.getTopicFields(postData.tid, ['cid', 'mainPid', 'title', 'timestamp', 'scheduled', 'slug']);
|
||||
const topicData = await topics.getTopicFields(postData.tid, [
|
||||
'cid', 'mainPid', 'title', 'timestamp', 'scheduled', 'slug', 'tags',
|
||||
]);
|
||||
|
||||
await scheduledTopicCheck(data, topicData);
|
||||
|
||||
@@ -53,7 +55,10 @@ module.exports = function (Posts) {
|
||||
]);
|
||||
|
||||
await Posts.setPostFields(data.pid, result.post);
|
||||
const contentChanged = data.content !== oldContent;
|
||||
const contentChanged = data.content !== oldContent ||
|
||||
topic.renamed ||
|
||||
topic.tagsupdated;
|
||||
|
||||
if (meta.config.enablePostHistory === 1 && contentChanged) {
|
||||
await Posts.diffs.save({
|
||||
pid: data.pid,
|
||||
@@ -61,6 +66,7 @@ module.exports = function (Posts) {
|
||||
oldContent: oldContent,
|
||||
newContent: data.content,
|
||||
edited: editPostData.edited,
|
||||
topic,
|
||||
});
|
||||
}
|
||||
await Posts.uploads.sync(data.pid);
|
||||
@@ -109,6 +115,7 @@ module.exports = function (Posts) {
|
||||
title: validator.escape(String(topicData.title)),
|
||||
isMainPost: false,
|
||||
renamed: false,
|
||||
tagsupdated: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,15 +131,16 @@ module.exports = function (Posts) {
|
||||
newTopicData.slug = `${tid}/${slugify(title) || 'topic'}`;
|
||||
}
|
||||
|
||||
data.tags = data.tags || [];
|
||||
const tagsupdated = Array.isArray(data.tags) &&
|
||||
!_.isEqual(data.tags, topicData.tags.map(tag => tag.value));
|
||||
|
||||
if (data.tags.length) {
|
||||
if (tagsupdated) {
|
||||
const canTag = await privileges.categories.can('topics:tag', topicData.cid, data.uid);
|
||||
if (!canTag) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
await topics.validateTags(data.tags, topicData.cid, data.uid, tid);
|
||||
}
|
||||
await topics.validateTags(data.tags, topicData.cid, data.uid, tid);
|
||||
|
||||
const results = await plugins.hooks.fire('filter:topic.edit', {
|
||||
req: data.req,
|
||||
@@ -140,7 +148,9 @@ module.exports = function (Posts) {
|
||||
data: data,
|
||||
});
|
||||
await db.setObject(`topic:${tid}`, results.topic);
|
||||
await topics.updateTopicTags(tid, data.tags);
|
||||
if (tagsupdated) {
|
||||
await topics.updateTopicTags(tid, data.tags);
|
||||
}
|
||||
const tags = await topics.getTopicTagsObjects(tid);
|
||||
|
||||
if (rescheduling(data, topicData)) {
|
||||
@@ -149,7 +159,7 @@ module.exports = function (Posts) {
|
||||
|
||||
newTopicData.tags = data.tags;
|
||||
newTopicData.oldTitle = topicData.title;
|
||||
const renamed = translator.escape(validator.escape(String(title))) !== topicData.title;
|
||||
const renamed = title && translator.escape(validator.escape(String(title))) !== topicData.title;
|
||||
plugins.hooks.fire('action:topic.edit', { topic: newTopicData, uid: data.uid });
|
||||
return {
|
||||
tid: tid,
|
||||
@@ -160,8 +170,10 @@ module.exports = function (Posts) {
|
||||
slug: newTopicData.slug || topicData.slug,
|
||||
isMainPost: true,
|
||||
renamed: renamed,
|
||||
rescheduled: rescheduling(data, topicData),
|
||||
tagsupdated: tagsupdated,
|
||||
tags: tags,
|
||||
oldTags: topicData.tags,
|
||||
rescheduled: rescheduling(data, topicData),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -76,9 +76,15 @@ module.exports = function (Posts) {
|
||||
}
|
||||
|
||||
async function getTopicAndCategories(tids) {
|
||||
const topicsData = await topics.getTopicsFields(tids, ['uid', 'tid', 'title', 'cid', 'slug', 'deleted', 'scheduled', 'postcount', 'mainPid', 'teaserPid']);
|
||||
const topicsData = await topics.getTopicsFields(tids, [
|
||||
'uid', 'tid', 'title', 'cid', 'tags', 'slug',
|
||||
'deleted', 'scheduled', 'postcount', 'mainPid', 'teaserPid',
|
||||
]);
|
||||
const cids = _.uniq(topicsData.map(topic => topic && topic.cid));
|
||||
const categoriesData = await categories.getCategoriesFields(cids, ['cid', 'name', 'icon', 'slug', 'parentCid', 'bgColor', 'color', 'backgroundImage', 'imageClass']);
|
||||
const categoriesData = await categories.getCategoriesFields(cids, [
|
||||
'cid', 'name', 'icon', 'slug', 'parentCid',
|
||||
'bgColor', 'color', 'backgroundImage', 'imageClass',
|
||||
]);
|
||||
return { topics: topicsData, categories: categoriesData };
|
||||
}
|
||||
|
||||
|
||||
@@ -194,8 +194,11 @@ describe('API', async () => {
|
||||
const socketAdmin = require('../src/socket.io/admin');
|
||||
// export data for admin user
|
||||
await socketUser.exportProfile({ uid: adminUid }, { uid: adminUid });
|
||||
await wait(2000);
|
||||
await socketUser.exportPosts({ uid: adminUid }, { uid: adminUid });
|
||||
await wait(2000);
|
||||
await socketUser.exportUploads({ uid: adminUid }, { uid: adminUid });
|
||||
await wait(2000);
|
||||
await socketAdmin.user.exportUsersCSV({ uid: adminUid }, {});
|
||||
// wait for export child process to complete
|
||||
await wait(5000);
|
||||
|
||||
@@ -241,8 +241,7 @@ describe('Admin Controllers', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
/*
|
||||
TODO: renable after nbbpm is fixed
|
||||
|
||||
it('should load /admin/extend/plugins', function (done) {
|
||||
this.timeout(50000);
|
||||
request(`${nconf.get('url')}/api/admin/extend/plugins`, { jar: jar, json: true }, (err, res, body) => {
|
||||
@@ -254,7 +253,7 @@ describe('Admin Controllers', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
it('should load /admin/manage/users', (done) => {
|
||||
request(`${nconf.get('url')}/api/admin/manage/users`, { jar: jar, json: true }, (err, res, body) => {
|
||||
assert.ifError(err);
|
||||
|
||||
@@ -425,6 +425,7 @@ describe('Post\'s', () => {
|
||||
cid: cid,
|
||||
title: 'topic to edit',
|
||||
content: 'A post to edit',
|
||||
tags: ['nodebb'],
|
||||
}, (err, data) => {
|
||||
assert.ifError(err);
|
||||
pid = data.postData.pid;
|
||||
|
||||
Reference in New Issue
Block a user