Creating a User - Tag relation plugin

Hello, I am currently working on a specific plugin but couldn’t figure where to start yet (I read the introductory material here and installed some simple plugins and played with their source a bit).

The setup: I have a csv file that contains several usernames from the system and some tags they are related to. For example, an entry “user1, tag#1” reads: “user1 knows about tag#1”. Many users can be related to one tag and one user can be related to many tags. Moreover, this csv will be updated time to time (once almost everyday). The file is in the same server that hosts the discourse instance.

How would the plugin ideally work: I will explain this using an example csv and a picture.

CSV:

userOP, tag#1
userOP, tag#2
user2, tag#1

Given this CSV, whenever there is a topic created with any of the tags included in the CSV (The topic author might not be on the list). The ideal output should be as follows:

Moreover a user can see their known tags as clickable links on their user panel with an additional tab called Known Tags:

Ideally, this tab should be quickly accessible by the top left menu:

That is, based on the relation defined in the csv, there should be small text or a badge for any user (OP or others) that posts under the topic with a tag they are related.

I am open to suggestions about how to accomplish this.

One thing you’ll need to do on the rails side is add those user/tag relations to the serializer so that those data are available to the front end. Look for some other plugins that call add_to_serializer or search for it in the discourse source, as it’s well documented there.

Then you’ll be able to use a plugin outlet in ember to add that to the page. The theme component documentation has examples of that.

I would probably add a route to manage the custom user fields rather than use a csv file, but if you want to go the csv way, you might want a job that reads it every hour or something, so find a plugin that has something in jobs/scheduled.

There is a Discourse repo called all_the_plugins that I sometimes grep to find examples of stuff. If you wade through github.com/discourse you should be able to find it.

Thank you for the information. The mental model that I have right now is, I should add a custom field to tags (like tag_description) called attached_users. For the above example, tag#1 should have attached_users: [“userOP”, “user1”].

Then, I should somehow add this to the serializer and figure how to display it in the post.

While thinking about this, I had another idea. If I use discourse assign (Discourse Assign) to assign these users to topics based on tags. I can already retrieve this information from the topic and I guess displaying it would be easier. However, discourse assign doesn’t provide any way of programmatically adding users.

Using assign seems like a good idea. I’m pretty sure that you could add users with the API. See How to reverse engineer the Discourse API

I wonder if it would put too much weight on the server. Because there will be many assignments and in the previous idea, it is essentially just an assignment to tags and doesn’t have to be assigned to each topic seperately.

Using an existing plugin that is maintained will be much easier than developing and maintaining your own.

You can do this with a simple plugin.

What you need to do

The plugin will need to do the following:

  1. Add a user custom field called known_tags, a list of strings.

  2. Add an interface in the user profile where the user can edit known_tags. I don’t think it’s necessary to add an entirely new profile tab for this, but you could if you wanted to. If you don’t want users editing it themselves, just make it only editable by admins and just go into users profiles and update it based on your CSV file.

  3. Add an event hook which uses the before_create_post or post_created events in the PostCreator to add the content you want to the post based on the tags in the topic.

How to do it

Parts 1 and 2 are very similar to the example plugins for other models in the topic linked below. See if you can figure it out by analogy. If you get really stuck ask me for a pointer there.

Part 3 will also go in your plugin.rb file. It’ll look something like this

on(:post_created) |post, opts, user|
  if post.is_first_post? && post.topic.tags.present?
     user_ids = UserCustomField.where(name: 'known_tags', value: post.topic.tags).pluck(:user_id)
     usernames = User.where(id: user_ids).pluck(:username)
     new_raw = post.raw + "something something #{usernames}"
     PostRevisor.new(post).revise!(
      user,
      {
        raw: new_raw,
        edit_reason: "some reason"
      },
      skip_validations: true,
      bypass_bump: true
    )
  end 
end

Give it a royal go yourself. If you get really stuck I’ll help. I’m always more inclined to help if there’s strong evidence you’re trying to figure it out yourself :slight_smile:

2 Likes

Thanks a lot for providing a very nice roadmap, I am on it! Small question:

Does this part have to be manual using this approach (by manual, I mean adding entries one by one via an interface)? Ideally, I would like to add this using my admin API key since the csv file is a bit loaded and will grow over time.

No it doesn’t need to be manual. There are quite a few different ways you could fill out the user custom fields based on the CSV. It really depends on how you want to handle the updates. I’d suggest doing the other parts first and just testing this out with a small dataset. Then if you’re happy with how it’s working add an automatic import for the CSV. Making that part efficient is a secondary step.

1 Like