diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d715d5795..312cd0d726 100644 --- a/CHANGELOG.md +++ b/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) diff --git a/install/package.json b/install/package.json index 14a7e80dfb..3a1c865b5e 100644 --- a/install/package.json +++ b/install/package.json @@ -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" } ] -} \ No newline at end of file +} diff --git a/public/src/client/topic/diffs.js b/public/src/client/topic/diffs.js index f75375b195..ea492348cb 100644 --- a/public/src/client/topic/diffs.js +++ b/public/src/client/topic/diffs.js @@ -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); }; diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index fe08787232..53037e8bfa 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -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; diff --git a/public/src/modules/taskbar.js b/public/src/modules/taskbar.js index 67b36d8156..9fa8fd0fb8 100644 --- a/public/src/modules/taskbar.js +++ b/public/src/modules/taskbar.js @@ -151,7 +151,7 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t const taskbarEl = $('
  • ') .addClass(data.options.className) - .html('' + + .html('' + (data.options.icon ? ' ' : '') + '' + title + '' + '') @@ -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); diff --git a/src/middleware/user.js b/src/middleware/user.js index db3a97ef08..c3871bd023 100644 --- a/src/middleware/user.js +++ b/src/middleware/user.js @@ -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(); diff --git a/src/posts/diffs.js b/src/posts/diffs.js index 9d541fe0aa..eb687195d3 100644 --- a/src/posts/diffs.js +++ b/src/posts/diffs.js @@ -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; } }; diff --git a/src/posts/edit.js b/src/posts/edit.js index f325e23269..453a10e9dd 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -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), }; } diff --git a/src/posts/summary.js b/src/posts/summary.js index cefbb8cc15..82468a17d5 100644 --- a/src/posts/summary.js +++ b/src/posts/summary.js @@ -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 }; } diff --git a/test/api.js b/test/api.js index ad3bcadfad..ab8094cc76 100644 --- a/test/api.js +++ b/test/api.js @@ -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); diff --git a/test/controllers-admin.js b/test/controllers-admin.js index 15dff752d7..48137ddd81 100644 --- a/test/controllers-admin.js +++ b/test/controllers-admin.js @@ -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); diff --git a/test/posts.js b/test/posts.js index 4858167336..f40af0c0d8 100644 --- a/test/posts.js +++ b/test/posts.js @@ -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;