Display Group Badge On Topic List?
-
Hello,
I'm trying to create a plugin that shows the group badge next to the username on the topic list. It's very similar to this, but on the topic list.
Thank you in advance!
-
EDIT:
I'm trying to create a plugin that shows the group badge next to or below the topic name. The idea is to notify the readers that a 'dev' or 'admin' has posted/responded to the topic.
Are there any plugins available that can do this? Is it possible to create?
Thank you in advance!
-
This shouldn't be hard to do, listen for
filter:topics.get
and then load the users groups into the user object. Once that is done you can display them onpartials/topics_list.tpl
myPlugin.filterTopicsGet = async function (hookData) { const uids = hookData.topics.map(t => t.uid); const userGroups = await Groups.getUserGroups(uids); hookData.topics.forEach((t, i) => { t.user.groups = userGroups[i]; }); return hookData; };
This would create a
groups
variable on every user object which you can access in the template. -
@baris Thank you for this. I can now see the groups variable for users listed on the topics page. I do have a follow up question. Is there a way for us to access all posts (including users) in a topic from the topics page? Our goal is to dynamically place a badge under the topic title to indicate that a dev had posted/replied in the topic.
-
@Teemberland For this you can use the sorted set
tid:<tid>:posters
it contains all uids who have posted in the topic (including the OP) and the score is the number of posts they have in that topic. So you need to grab the uids from this sorted set and check if any of the uids are in a dev group. -
@baris thank you for your suggestion. I can get the posters now, but I'm not able to get their groups. Here's my code. Can you tell me what I'm missing here?
var db = require.main.require('./src/database'); theme.filterTopicsGetDevActivity = async function(hookData) { hookData.topics.forEach((t, i) => { db.getSortedSetRange('tid:' + t.tid + ':posters', 0, -1, function (err, uids) { t.foobar = uids; }); }); return hookData; }
Here's my hook:
{ "hook": "filter:topics.get", "method": "filterTopicsGetDevActivity" }
My template:
<!-- IF topics.foobar.length --> <!-- BEGIN topics.foobar --> {topics.foobar} <!-- END topics.foobar --> <!-- ENDIF topics.foobar.length -->
I tried using {topics.foobar.groups.slug} to display the group name, but the object becomes empty.
-
Your code is only getting the uids who have posted in the topic it is not loading any group data. It is also loading the uids in a foreach which won't work as you expect it. You need to use promises and assign the data once the call is complete.
You want something like below, adjust as necessary.
myPlugin.filterTopicsGet = async function (hookData) { const groupData = hookData.topics.map(async (t) => { const uids = await db.getSortedSetMembers('tid:' + t.tid + ':posters')); const userGroups = await Groups.getUserGroupMembership('groups:createtime', uids); return userGroups.flat().includes('My developer group'); }); hookData.topics.forEach((t, i) => { t.postedInByDeveloper = groupData[i]; }); return hookData; };
-
@baris thank you! I think we're very close. When I call postedInByDeveloper in my template {topics.postedInByDeveloper} I get an [object Promise] in view.
I also tried making an api call in the console to check the object and found that postedInByDeveloper is empty. See screenshot below.
-
I was writing the code of the top of my head so it had errors, this should work I think.
myPlugin.filterTopicsGet = async function (hookData) { const groupData = await Promise.all(hookData.topics.map(async (t) => { const uids = await db.getSortedSetMembers('tid:' + t.tid + ':posters'); const userGroups = await Groups.getUserGroupMembership('groups:createtime', uids); return userGroups.flat().includes('My developer group'); })); hookData.topics.forEach((t, i) => { t.postedInByDeveloper = groupData[i]; }); return hookData; };
-
@baris I had to update to 1.14.x to get access to getSortedSetMember. I didn't know that this method was new. Also, I had to use a different method for flat() since I'm on the older version of node locally.
Thank you once again for all your help!
-
-
@Teemberland @patsanch I've rewritten the above filter to use less database calls. You can give it a try as well if you don't want to add new hooks from the referenced topic.
myPlugin.filterTopicsGet = async function (hookData) { const tids = hookData.topics.map(t => t && t.tid); const posterUids = await db.getSortedSetsMembers(tids.map(tid => `tid:${tid}:posters`)); const uids = _.uniq(_.flatten(posterUids)); const isMembers = await Groups.isMembers(uids, 'My developer group'); const membershipMap = _.zipObject(uids, isMembers); hookData.topics.forEach((t, i) => { t.postedInByDeveloper = posterUids[i].some(uid => membershipMap[uid]); }); return hookData; };
This runs a lot faster since it's only making 2 database calls.
before took 975.6332 milliseconds took 922.0847 milliseconds took 819.809 milliseconds after took 3.9381 milliseconds took 3.8666 milliseconds took 3.1888 milliseconds