From dd08f75886a9dd1eb1af85ede588f5edc4dc0b91 Mon Sep 17 00:00:00 2001 From: Julie Wu Date: Tue, 18 Feb 2025 23:32:58 -0500 Subject: [PATCH 1/6] Add resolveTopics func, a combination of add/deleteTags, used in topics.reply --- src/topics/create.js | 8 +++++++- src/topics/tags.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/topics/create.js b/src/topics/create.js index 9b0fd4453..49dc29ff2 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -178,9 +178,10 @@ module.exports = function (Topics) { const { tid } = data; const { uid } = data; - const [topicData, isAdmin] = await Promise.all([ + const [topicData, isAdmin, isInstructor] = await Promise.all([ Topics.getTopicData(tid), privileges.users.isAdministrator(uid), + privileges.users.isInstructor(uid), ]); await canReply(data, topicData); @@ -203,6 +204,11 @@ module.exports = function (Topics) { data.timestamp = topicData.lastposttime + 1; } + if (isInstructor) { + console.log('add resolved'); + Topics.resolveTopics([tid]); + } + data.ip = data.req ? data.req.ip : null; let postData = await posts.create(data); postData = await onNewPost(postData, data); diff --git a/src/topics/tags.js b/src/topics/tags.js index daab4e5f7..c6723208f 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -219,6 +219,7 @@ module.exports = function (Topics) { }; Topics.deleteTags = async function (tags) { + console.log('deleteTags'); if (!Array.isArray(tags) || !tags.length) { return; } @@ -246,6 +247,7 @@ module.exports = function (Topics) { }; async function removeTagsFromTopics(tags) { + console.log('removetagsfromtopics'); await async.eachLimit(tags, 50, async (tag) => { const tids = await db.getSortedSetRange(`tag:${tag}:topics`, 0, -1); if (!tids.length) { @@ -272,6 +274,7 @@ module.exports = function (Topics) { } Topics.deleteTag = async function (tag) { + console.log('deletetag'); await Topics.deleteTags([tag]); }; @@ -362,12 +365,14 @@ module.exports = function (Topics) { }; Topics.addTags = async function (tags, tids) { + console.log('addtags'); const topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'timestamp', 'tags']); const bulkAdd = []; const bulkSet = []; topicData.forEach((t) => { const topicTags = t.tags.map(tagItem => tagItem.value); tags.forEach((tag) => { + console.log(tag); bulkAdd.push([`tag:${tag}:topics`, t.timestamp, t.tid]); bulkAdd.push([`cid:${t.cid}:tag:${tag}:topics`, t.timestamp, t.tid]); if (!topicTags.includes(tag)) { @@ -385,6 +390,37 @@ module.exports = function (Topics) { await Topics.updateCategoryTagsCount(_.uniq(topicData.map(t => t.cid)), tags); }; + Topics.resolveTopics = async function (tids) { + console.log('resolve thread'); + const topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'tags']); + const bulkRemove = []; + const bulkAdd = []; + const bulkSet = []; + + topicData.forEach((t) => { + const topicTags = t.tags.map(tagItem => tagItem.value); + bulkAdd.push([`tag:resolved:topics`, t.timestamp, t.tid]); + bulkAdd.push([`cid:${t.cid}:tag:resolved:topics`, t.timestamp, t.tid]); + bulkRemove.push([`tag:unresolved:topics`, t.tid]); + bulkRemove.push([`cid:${t.cid}:tag:unresolved:topics`, t.tid]); + if (!topicTags.includes('resolved')) { + topicTags.push('resolved'); + } + if (topicTags.includes('unresolved')) { + topicTags.splice(topicTags.indexOf('unresolved'), 1); + } + bulkSet.push([`topic:${t.tid}`, { tags: topicTags.join(',') }]); + }); + await Promise.all([ + db.sortedSetAddBulk(bulkAdd), + db.sortedSetRemoveBulk(bulkRemove), + db.setObjectBulk(bulkSet), + ]); + + await Promise.all(['resolved', 'unresolved'].map(updateTagCount)); + await Topics.updateCategoryTagsCount(_.uniq(topicData.map(t => t.cid)), ['resolved', 'unresolved']); + }; + Topics.removeTags = async function (tags, tids) { const topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'tags']); const bulkRemove = []; From 0998093b8dff7ccbf15eded21321f918a2ece21b Mon Sep 17 00:00:00 2001 From: Julie Wu Date: Tue, 18 Feb 2025 23:51:37 -0500 Subject: [PATCH 2/6] Simplify resolveTopics function --- src/topics/tags.js | 42 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/src/topics/tags.js b/src/topics/tags.js index c6723208f..763c60f5e 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -390,37 +390,6 @@ module.exports = function (Topics) { await Topics.updateCategoryTagsCount(_.uniq(topicData.map(t => t.cid)), tags); }; - Topics.resolveTopics = async function (tids) { - console.log('resolve thread'); - const topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'tags']); - const bulkRemove = []; - const bulkAdd = []; - const bulkSet = []; - - topicData.forEach((t) => { - const topicTags = t.tags.map(tagItem => tagItem.value); - bulkAdd.push([`tag:resolved:topics`, t.timestamp, t.tid]); - bulkAdd.push([`cid:${t.cid}:tag:resolved:topics`, t.timestamp, t.tid]); - bulkRemove.push([`tag:unresolved:topics`, t.tid]); - bulkRemove.push([`cid:${t.cid}:tag:unresolved:topics`, t.tid]); - if (!topicTags.includes('resolved')) { - topicTags.push('resolved'); - } - if (topicTags.includes('unresolved')) { - topicTags.splice(topicTags.indexOf('unresolved'), 1); - } - bulkSet.push([`topic:${t.tid}`, { tags: topicTags.join(',') }]); - }); - await Promise.all([ - db.sortedSetAddBulk(bulkAdd), - db.sortedSetRemoveBulk(bulkRemove), - db.setObjectBulk(bulkSet), - ]); - - await Promise.all(['resolved', 'unresolved'].map(updateTagCount)); - await Topics.updateCategoryTagsCount(_.uniq(topicData.map(t => t.cid)), ['resolved', 'unresolved']); - }; - Topics.removeTags = async function (tags, tids) { const topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'tags']); const bulkRemove = []; @@ -446,16 +415,27 @@ module.exports = function (Topics) { await Topics.updateCategoryTagsCount(_.uniq(topicData.map(t => t.cid)), tags); }; + Topics.resolveTopics = async function (tids) { + console.log('resolve thread'); + await Topics.addTags(['resolved'], tids); + await Topics.removeTags(['unresolved'], tids); + }; + Topics.updateTopicTags = async function (tid, tags) { + console.log('udpate??'); + console.log(tags); await Topics.deleteTopicTags(tid); const cid = await Topics.getTopicField(tid, 'cid'); tags = await Topics.filterTags(tags, cid); + console.log('new tags?'); + console.log(tags); await Topics.addTags(tags, [tid]); plugins.hooks.fire('action:topic.updateTags', { tags, tid }); }; Topics.deleteTopicTags = async function (tid) { + console.log('deletetopictags'); const topicData = await Topics.getTopicFields(tid, ['cid', 'tags']); const { cid } = topicData; const tags = topicData.tags.map(tagItem => tagItem.value); From 1f653af5cea32de0bcfcd2113f105698e2e13129 Mon Sep 17 00:00:00 2001 From: Julie Wu Date: Sun, 23 Feb 2025 19:21:46 -0500 Subject: [PATCH 3/6] Add emit and event for topic resolve auto tag update --- public/src/client/topic/events.js | 11 +++++++++++ src/topics/create.js | 20 ++++++++++++++++++-- src/topics/tags.js | 23 +++++++++-------------- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index e091dd69c..c0fac5e8a 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -31,6 +31,8 @@ define('forum/topic/events', [ 'event:topic_moved': onTopicMoved, + 'event:topic_resolved': onTopicResolved, + 'event:post_edited': onPostEdited, 'event:post_purged': onPostPurged, @@ -100,7 +102,16 @@ define('forum/topic/events', [ } } + function onTopicResolved(data) { + if (data.topic && data.topic.tags){ + require(['forum/topic/tag'], function (tag) { + tag.updateTopicTags([data.topic]); + }); + } + } + function onPostEdited(data) { + console.trace(); if (!data || !data.post || parseInt(data.post.tid, 10) !== parseInt(ajaxify.data.tid, 10)) { return; } diff --git a/src/topics/create.js b/src/topics/create.js index 49dc29ff2..9ec9ff7a3 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -205,8 +205,7 @@ module.exports = function (Topics) { } if (isInstructor) { - console.log('add resolved'); - Topics.resolveTopics([tid]); + await resolveTopic(topicData, topicData.tags); } data.ip = data.req ? data.req.ip : null; @@ -239,6 +238,23 @@ module.exports = function (Topics) { return postData; }; + async function resolveTopic(data, tags) { + const { tid } = data; + // Remove unresolved tag and add resolved tag + const topicTags = tags.map(tagItem => tagItem.value); + if (!topicTags.includes('unresolved')) { + return; + } + topicTags.splice(topicTags.indexOf('unresolved'), 1); + topicTags.push('resolved'); + await Topics.updateTopicTags([tid], topicTags); + + // Refresh tags + tags = await Topics.getTopicTagsObjects(tid); + data.tags = tags; + websockets.in(`topic_${tid}`).emit('event:topic_resolved', {topic: data}); + } + async function onNewPost(postData, data) { const { tid, uid } = postData; await Topics.markAsRead([tid], uid); diff --git a/src/topics/tags.js b/src/topics/tags.js index 763c60f5e..c857b05f8 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -219,7 +219,6 @@ module.exports = function (Topics) { }; Topics.deleteTags = async function (tags) { - console.log('deleteTags'); if (!Array.isArray(tags) || !tags.length) { return; } @@ -247,7 +246,6 @@ module.exports = function (Topics) { }; async function removeTagsFromTopics(tags) { - console.log('removetagsfromtopics'); await async.eachLimit(tags, 50, async (tag) => { const tids = await db.getSortedSetRange(`tag:${tag}:topics`, 0, -1); if (!tids.length) { @@ -274,7 +272,6 @@ module.exports = function (Topics) { } Topics.deleteTag = async function (tag) { - console.log('deletetag'); await Topics.deleteTags([tag]); }; @@ -365,14 +362,12 @@ module.exports = function (Topics) { }; Topics.addTags = async function (tags, tids) { - console.log('addtags'); const topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'timestamp', 'tags']); const bulkAdd = []; const bulkSet = []; topicData.forEach((t) => { const topicTags = t.tags.map(tagItem => tagItem.value); tags.forEach((tag) => { - console.log(tag); bulkAdd.push([`tag:${tag}:topics`, t.timestamp, t.tid]); bulkAdd.push([`cid:${t.cid}:tag:${tag}:topics`, t.timestamp, t.tid]); if (!topicTags.includes(tag)) { @@ -415,27 +410,27 @@ module.exports = function (Topics) { await Topics.updateCategoryTagsCount(_.uniq(topicData.map(t => t.cid)), tags); }; - Topics.resolveTopics = async function (tids) { - console.log('resolve thread'); - await Topics.addTags(['resolved'], tids); - await Topics.removeTags(['unresolved'], tids); + Topics.resolveTopics = async function (tid, tags) { + const topicTags = tags.map(tagItem => tagItem.value); + if (!topicTags.includes('unresolved')) { + return; + } + topicTags.splice(topicTags.indexOf('unresolved'), 1); + topicTags.push('resolved'); + await Topics.updateTopicTags(tid, topicTags); + // websockets.in(`topic_${tid}`).emit('event:topic_resolved', editResult); }; Topics.updateTopicTags = async function (tid, tags) { - console.log('udpate??'); - console.log(tags); await Topics.deleteTopicTags(tid); const cid = await Topics.getTopicField(tid, 'cid'); tags = await Topics.filterTags(tags, cid); - console.log('new tags?'); - console.log(tags); await Topics.addTags(tags, [tid]); plugins.hooks.fire('action:topic.updateTags', { tags, tid }); }; Topics.deleteTopicTags = async function (tid) { - console.log('deletetopictags'); const topicData = await Topics.getTopicFields(tid, ['cid', 'tags']); const { cid } = topicData; const tags = topicData.tags.map(tagItem => tagItem.value); From 368bcd97fe1bb71fd4837017f8e5c08bf66d15f0 Mon Sep 17 00:00:00 2001 From: Julie Wu Date: Sun, 23 Feb 2025 19:25:23 -0500 Subject: [PATCH 4/6] Remove old removeTags function from src/topics/tags --- src/topics/tags.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/topics/tags.js b/src/topics/tags.js index c857b05f8..daab4e5f7 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -410,17 +410,6 @@ module.exports = function (Topics) { await Topics.updateCategoryTagsCount(_.uniq(topicData.map(t => t.cid)), tags); }; - Topics.resolveTopics = async function (tid, tags) { - const topicTags = tags.map(tagItem => tagItem.value); - if (!topicTags.includes('unresolved')) { - return; - } - topicTags.splice(topicTags.indexOf('unresolved'), 1); - topicTags.push('resolved'); - await Topics.updateTopicTags(tid, topicTags); - // websockets.in(`topic_${tid}`).emit('event:topic_resolved', editResult); - }; - Topics.updateTopicTags = async function (tid, tags) { await Topics.deleteTopicTags(tid); const cid = await Topics.getTopicField(tid, 'cid'); From 55875995f6831386c6cd0d2e242c8cb1a3bd8106 Mon Sep 17 00:00:00 2001 From: Julie Wu Date: Thu, 27 Feb 2025 23:20:09 -0500 Subject: [PATCH 5/6] Cleaned up lint --- public/src/client/topic/events.js | 2 +- src/topics/create.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index c0fac5e8a..674f690c0 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -103,7 +103,7 @@ define('forum/topic/events', [ } function onTopicResolved(data) { - if (data.topic && data.topic.tags){ + if (data.topic && data.topic.tags) { require(['forum/topic/tag'], function (tag) { tag.updateTopicTags([data.topic]); }); diff --git a/src/topics/create.js b/src/topics/create.js index 9ec9ff7a3..e9b7098c1 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -14,6 +14,7 @@ const posts = require('../posts'); const privileges = require('../privileges'); const categories = require('../categories'); const translator = require('../translator'); +const websockets = require('../socket.io'); module.exports = function (Topics) { Topics.create = async function (data) { @@ -252,7 +253,7 @@ module.exports = function (Topics) { // Refresh tags tags = await Topics.getTopicTagsObjects(tid); data.tags = tags; - websockets.in(`topic_${tid}`).emit('event:topic_resolved', {topic: data}); + websockets.in(`topic_${tid}`).emit('event:topic_resolved', { topic: data }); } async function onNewPost(postData, data) { From 8f515086c8fdc82765f82f442da1150da0ac76c4 Mon Sep 17 00:00:00 2001 From: Julie Wu Date: Thu, 27 Feb 2025 23:24:55 -0500 Subject: [PATCH 6/6] Add topic resolution tests --- test/topics.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/topics.js b/test/topics.js index 8a32e445f..db03afedd 100644 --- a/test/topics.js +++ b/test/topics.js @@ -29,6 +29,8 @@ describe('Topic\'s', () => { let topic; let categoryObj; let adminUid; + let instructorUid; + let studentUid; let adminJar; let csrf_token; let fooUid; @@ -41,6 +43,11 @@ describe('Topic\'s', () => { adminJar = adminLogin.jar; csrf_token = adminLogin.csrf_token; + instructorUid = await User.create({ username: 'prof' }); + await groups.join('Instructors', instructorUid); + studentUid = await User.create({ username: 'student' }); + await groups.join('Students', studentUid); + categoryObj = await categories.create({ name: 'Test Category', description: 'Test category created by testing script', @@ -1676,6 +1683,14 @@ describe('Topic\'s', () => { assert.deepStrictEqual(categoryTags.sort(), ['tag2', 'tag4', 'tag6']); }); + it('should resolve topic', async () => { + const t = await topics.post({ uid: studentUid, tags: [], title: 'topic title 1', content: 'topic 1 content', cid: topic.categoryId }); + const { tid } = t.topicData; + await topics.reply({ uid: instructorUid, content: 'reply 1 content', tid: tid }); + const tags = await topics.getTopicTags(tid); + assert.deepStrictEqual(tags.sort(), ['resolved']); + }); + it('should respect minTags', async () => { const oldValue = meta.config.minimumTagsPerTopic; meta.config.minimumTagsPerTopic = 2;