-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modified code in src/topics to allow topics to be categorized as "answered" and "unanswered" #16
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
2f770f9
Added answered.js to src/topics to allow users to mark topics as "ans…
bingbhakdibhumi 0346dfa
Added line 21 in posts.js and line 36 in index.js to let questions be…
bingbhakdibhumi 4c69c73
Added comments to src/topics/answered.js to clarify the source of hel…
bingbhakdibhumi 7df8ad4
Removed unused constant in line 3 of answered.js
bingbhakdibhumi 9319285
Converted indentation from spaces to tabs in answered.js
bingbhakdibhumi 97b5fc2
Removed whitespace and unused variables in answered.js
bingbhakdibhumi 6375500
Removed erroneous white space in posts.js
bingbhakdibhumi f37ac83
Added getTids function from unread.js to answered.js to retrieve topi…
bingbhakdibhumi 450e190
Added constants of topics functionalities to answered.js and removed …
bingbhakdibhumi 5d91317
Fixed syntax error on line 124 in answered.js
bingbhakdibhumi 53d4d1f
Fixed variable being referenced before initialization error in answer…
bingbhakdibhumi 3be1afc
Added constant unreadTopics to answered.js to fix referencing issue
bingbhakdibhumi a89b4ad
Removed unused references to ignoredTids in answered.js
bingbhakdibhumi 22d0bb2
added dummy front end headers to topic templates
bingbhakdibhumi 56a57b6
removed dummy label in the header
bingbhakdibhumi 164dbae
added a "mark topic as answered" button in the tools dropdown
bingbhakdibhumi 9fba030
changed icon for "mark as answered/unanswered" in the dropdown topic …
bingbhakdibhumi 29b11b9
Created test file "answered.js" to test categorizing functionality
bingbhakdibhumi 524296f
added "answered" button to the topic drop down menu
bingbhakdibhumi 0e3f3a8
removed filler header in templates/topic.tpl
bingbhakdibhumi 463be00
Created UserGuide.md to the specifications on the writeup
bingbhakdibhumi 3762e7d
Merge remote-tracking branch 'origin/main' into categorize-questions
bingbhakdibhumi 1ce6d9f
Merged and resolved conflicts for UserGuide.md
bingbhakdibhumi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
'use strict'; | ||
|
||
const _ = require('lodash'); | ||
|
||
const db = require('../database'); | ||
const user = require('../user'); | ||
const categories = require('../categories'); | ||
const privileges = require('../privileges'); | ||
const plugins = require('../plugins'); | ||
|
||
|
||
module.exports = function (Topics) { | ||
Topics.markAsAnswered = async function (uid, tid) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's better to first check if uid and tid is valid here |
||
const topicData = await Topics.getTopicFields(tid, ['answered']); | ||
if (!topicData || !topicData.cid) { | ||
throw new Error('[[error:no-topic]]'); | ||
} | ||
// function from ../data.js | ||
await Topics.setTopicField(tid, 'answered', 1); | ||
plugins.hooks.fire('action:topic.answered', { tid: tid, uid: uid }); | ||
return topicData; | ||
}; | ||
Topics.markAsUnanswered = async function (uid, tid) { | ||
const topicData = await Topics.getTopicFields(tid, ['answered']); | ||
if (!topicData || !topicData.cid) { | ||
throw new Error('[[error:no-topic]]'); | ||
} | ||
|
||
await Topics.setTopicField(tid, 'answered', 0); | ||
plugins.hooks.fire('action:topic.answered', { tid: tid, uid: uid }); | ||
return topicData; | ||
}; | ||
// based on getUnread functions from ../unread.js | ||
Topics.getUnansweredTopics = async function (params) { | ||
const unansweredTopics = { | ||
showSelect: true, | ||
nextStart: 0, | ||
topics: [], | ||
}; | ||
let tids = await Topics.getUnansweredTids(params); | ||
unansweredTopics.topicCount = tids.length; | ||
|
||
if (!tids.length) { | ||
return unansweredTopics; | ||
} | ||
|
||
tids = tids.slice(params.start, params.stop !== -1 ? params.stop + 1 : undefined); | ||
|
||
const topicData = await Topics.getTopicsByTids(tids, params.uid); | ||
if (!topicData.length) { | ||
return unansweredTopics; | ||
} | ||
Topics.calculateTopicIndices(topicData, params.start); | ||
unansweredTopics.topics = topicData; | ||
unansweredTopics.nextStart = params.stop + 1; | ||
return unansweredTopics; | ||
}; | ||
Topics.getUnAnsweredTids = async function (params) { | ||
const results = await Topics.getAnsweredData(params); | ||
return params.count ? results.counts : results.tids; | ||
}; | ||
Topics.getAnsweredData = async function (params) { | ||
const uid = parseInt(params.uid, 10); | ||
|
||
params.filter = params.filter || ''; | ||
|
||
if (params.cid && !Array.isArray(params.cid)) { | ||
params.cid = [params.cid]; | ||
} | ||
|
||
if (params.tag && !Array.isArray(params.tag)) { | ||
params.tag = [params.tag]; | ||
} | ||
|
||
const data = await getTids(params); | ||
if (uid <= 0) { | ||
return data; | ||
} | ||
|
||
const result = await plugins.hooks.fire('filter:topics.getAnsweredTids', { | ||
uid: uid, | ||
tids: data.tids, | ||
counts: data.counts, | ||
tidsByFilter: data.tidsByFilter, | ||
answeredCids: data.answeredCids, | ||
cid: params.cid, | ||
filter: params.filter, | ||
query: params.query || {}, | ||
}); | ||
return result; | ||
}; | ||
// from unread.js | ||
async function getTids(params) { | ||
const counts = { '': 0, new: 0, watched: 0, unreplied: 0 }; | ||
const tidsByFilter = { '': [], new: [], watched: [], unreplied: [] }; | ||
const unreadCids = []; | ||
if (params.uid <= 0) { | ||
return { counts, tids: [], tidsByFilter, unreadCids }; | ||
} | ||
|
||
params.cutoff = await Topics.unreadCutoff(params.uid); | ||
|
||
const [followedTids, userScores, tids_unread] = await Promise.all([ | ||
getFollowedTids(params), | ||
db.getSortedSetRevRangeByScoreWithScores(`uid:${params.uid}:tids_read`, 0, -1, '+inf', params.cutoff), | ||
db.getSortedSetRevRangeWithScores(`uid:${params.uid}:tids_unread`, 0, -1), | ||
]); | ||
|
||
const userReadTimes = _.mapValues(_.keyBy(userScores, 'value'), 'score'); | ||
const isTopicsFollowed = {}; | ||
followedTids.forEach((t) => { | ||
isTopicsFollowed[t.value] = true; | ||
}); | ||
const unreadFollowed = await db.isSortedSetMembers( | ||
`uid:${params.uid}:followed_tids`, tids_unread.map(t => t.value) | ||
); | ||
|
||
tids_unread.forEach((t, i) => { | ||
isTopicsFollowed[t.value] = unreadFollowed[i]; | ||
}); | ||
|
||
const unreadTopics = followedTids; | ||
const blockedUids = await user.blocks.list(params.uid); | ||
let tids = _.uniq(unreadTopics.map(topic => topic.value)).slice(0, 200); | ||
|
||
tids = await privileges.topics.filterTids('topics:read', tids, params.uid); | ||
const topicData = (await Topics.getTopicsFields(tids, ['tid', 'cid', 'uid', 'postcount', 'deleted', 'scheduled', 'tags'])) | ||
.filter(t => t.scheduled || !t.deleted); | ||
const topicCids = _.uniq(topicData.map(topic => topic.cid)).filter(Boolean); | ||
|
||
const categoryWatchState = await categories.getWatchState(topicCids, params.uid); | ||
const userCidState = _.zipObject(topicCids, categoryWatchState); | ||
|
||
const filterCids = params.cid && params.cid.map(cid => parseInt(cid, 10)); | ||
const filterTags = params.tag && params.tag.map(tag => String(tag)); | ||
|
||
topicData.forEach((topic) => { | ||
if (topic && topic.cid && | ||
(!filterCids || filterCids.includes(topic.cid)) && | ||
(!filterTags || filterTags.every(tag => topic.tags.find(topicTag => topicTag.value === tag))) && | ||
!blockedUids.includes(topic.uid)) { | ||
if (isTopicsFollowed[topic.tid] || | ||
[categories.watchStates.watching, categories.watchStates.tracking].includes(userCidState[topic.cid])) { | ||
tidsByFilter[''].push(topic.tid); | ||
unreadCids.push(topic.cid); | ||
} | ||
|
||
if (isTopicsFollowed[topic.tid]) { | ||
tidsByFilter.watched.push(topic.tid); | ||
} | ||
|
||
if (topic.postcount <= 1) { | ||
tidsByFilter.unreplied.push(topic.tid); | ||
} | ||
|
||
if (!userReadTimes[topic.tid]) { | ||
tidsByFilter.new.push(topic.tid); | ||
} | ||
} | ||
}); | ||
|
||
counts[''] = tidsByFilter[''].length; | ||
counts.watched = tidsByFilter.watched.length; | ||
counts.unreplied = tidsByFilter.unreplied.length; | ||
counts.new = tidsByFilter.new.length; | ||
|
||
return { | ||
counts: counts, | ||
tids: tidsByFilter[params.filter], | ||
tidsByFilter: tidsByFilter, | ||
unreadCids: unreadCids, | ||
}; | ||
} | ||
async function getFollowedTids(params) { | ||
const keys = params.cid ? | ||
params.cid.map(cid => `cid:${cid}:tids:lastposttime`) : | ||
'topics:recent'; | ||
|
||
const recentTopicData = await db.getSortedSetRevRangeByScoreWithScores(keys, 0, -1, '+inf', params.cutoff); | ||
const isFollowed = await db.isSortedSetMembers(`uid:${params.uid}:followed_tids`, recentTopicData.map(t => t.tid)); | ||
return recentTopicData.filter((t, i) => isFollowed[i]); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
'use strict'; | ||
|
||
const path = require('path'); | ||
const assert = require('assert'); | ||
const validator = require('validator'); | ||
const mockdate = require('mockdate'); | ||
const nconf = require('nconf'); | ||
const util = require('util'); | ||
|
||
const sleep = util.promisify(setTimeout); | ||
|
||
const db = require('./mocks/databasemock'); | ||
const file = require('../src/file'); | ||
const topics = require('../src/topics'); | ||
const posts = require('../src/posts'); | ||
const categories = require('../src/categories'); | ||
const privileges = require('../src/privileges'); | ||
const meta = require('../src/meta'); | ||
const User = require('../src/user'); | ||
const groups = require('../src/groups'); | ||
const utils = require('../src/utils'); | ||
const helpers = require('./helpers'); | ||
const socketTopics = require('../src/socket.io/topics'); | ||
const apiTopics = require('../src/api/topics'); | ||
const apiPosts = require('../src/api/posts'); | ||
const request = require('../src/request'); | ||
|
||
|
||
const answered = async function (pid, uid) { | ||
try { | ||
privileges.posts.can('posts:answered', pid, uid); | ||
} catch (e) { } | ||
return { answered: true }; | ||
}; | ||
|
||
describe('Categorize questions as Answered', () => { | ||
let topicAnswered; | ||
let topicUnanswered; | ||
let categoryAnswered; | ||
let categoryUnanswered; | ||
let testUid; | ||
let fooUid; | ||
let adminJar; | ||
let csrf_token; | ||
|
||
// modified from ./topics.js | ||
before(async () => { | ||
testUid = await User.create({ username: 'test', password: 'password' }); | ||
fooUid = await User.create({ username: 'foo' }); | ||
await groups.join('administrators', testUid); | ||
const adminLogin = await helpers.loginUser('test', 'password'); | ||
adminJar = adminLogin.jar; | ||
csrf_token = adminLogin.csrf_token; | ||
categoryAnswered = await categories.create({ | ||
name: 'Answered Category', | ||
description: 'Answered category created by testing script', | ||
}); | ||
categoryUnanswered = await categories.create({ | ||
name: 'Unanswered Category', | ||
description: 'Unanswered category created by testing script', | ||
}); | ||
topicAnswered = { | ||
userId: testUid, | ||
categoryId: categoryAnswered.cid, | ||
title: 'Test Answered', | ||
content: 'Post has been answered', | ||
}; | ||
topicUnanswered = { | ||
userId: testUid, | ||
categoryId: categoryUnanswered.cid, | ||
title: 'Test Unanswered', | ||
content: 'Post has been unanswered', | ||
}; | ||
}); | ||
|
||
it('should categorize a topic as answered', (done) => { | ||
topics.post({ | ||
uid: topicAnswered.userId, | ||
title: topicAnswered.title, | ||
content: topicAnswered.content, | ||
cid: topicAnswered.categoryId, | ||
}, (err, result) => { | ||
assert.ifError(err); | ||
assert(result); | ||
topicAnswered.tid = result.topicData.tid; | ||
done(); | ||
}); | ||
}); | ||
it('should categorize a topic as unanswered', (done) => { | ||
topics.post({ | ||
uid: topicUnanswered.userId, | ||
title: topicUnanswered.title, | ||
content: topicUnanswered.content, | ||
cid: topicUnanswered.categoryId, | ||
}, (err, result) => { | ||
assert.ifError(err); | ||
assert(result); | ||
topicUnanswered.tid = result.topicData.tid; | ||
done(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add some comments in this file explaining particular functions