Plugin to show full name only to users of the same group

Hi everyone,

I’m googling my problem for a few weeks already and I’m pretty close to giving up.

The background:
I develop and maintain a multi-tenant admin panel for sport clubs.
My self-hosted discourse forum is available only to members of those clubs. I would like it to be open for public. My own SSO integration automatically assigns users’ full name from my database to the discourse user once logged in.

The problem:
Right now I cannot make my forum public because the full name of a user should be only visible to users of the same club.

My experience as a Ruby developer is zero (I’m a JS dev) but so far I’ve figured out this part of code:

# plugin.rb

after_initialize {
  require_dependency 'basic_user_serializer'
  require_dependency 'current_user'

  class ::BasicUserSerializer
    attributes :name

    def name
      # fixed set of group names that indicate belonging to a club
      clubGroups = Array['foo', 'bar', 'baz']
      
      # PART THAT I CAN'T FIGURE OUT:
      # Show the name of the user ONLY if they share one of the same of the currently logged user's `clubGroups`,
      # e.g. I'm logged in as a user who belongs to 'bar' group, meaning I belong to the Bar club. I should be able to see names of only those users who also belong to the 'bar' group
      ???

    end
  end
}

I can’t figure out the following things:

  1. how to fetch current user’s groups
  2. how to fetch featured user’s groups

Once I have the above, I could compare the 2 arrays with the clubGroups and decide wether to show the user’s name or not.

Another challenge for me will be turning off the possibility to edit your own name, but that’s a different story.

1 Like

The GroupUser model contains the Group membership by User.

You want something like: GroupUser.where(user_id: current_user.id) to return the Group ids for the current user.

That will give you an Active Record Relation.

Then you might want to map it into an array:

GroupUser.where(user_id: myuser.id).map {|gu| gu.group_id}

Follow the same approach for your featured user.

1 Like

Perfect! Thanks :slight_smile:

I don’t know yet how to turn it into a working solution but that’s already a major hint.

1 Like

Never give up. Just keep going. :rocket:

1 Like

Another obstacle:

When trying to fetch current user’s ID to pass to the query, I don’t really know how I can fetch it.
When I navigate to the CurrentUser model in the code, the only method that looks relevant to my requirements is current_user but when I do this:

pp CurrentUser.current_user

it throws me:

undefined method `current_user' for CurrentUser:Module

I think I’m definitely missing some basic knowledge about Ruby here but maybe you can easily solve it for me?

You might want to look at the code of this plugin. It adds avatar flair for people in the same groups so they are able to recognize each other as group members. That’s a different thing but it does contain the code that checks if the viewing user is in the same group as the user that posted.

1 Like

The clue here is this method:

  def user_is_current_user
    object.id == scope.user&.id
  end

The user being serialized is object

The current user is actually in scope.user, so:

GroupUser.where(user_id: scope.user&.id).map {|gu| gu.group_id}

probably works for current user.

etc. etc.

Good tips:

  1. Read the Discourse source code
  2. As per @RGJ look at a few open source plugins to see how they do things
  3. Prototype stuff in the rails console rails c --sandbox

Amazing! Lots of great knowledge. Thanks guys :heart_eyes:

I’m diving into it :dealwithit:

1 Like

For the sake of future visitors of this topic, here’s the final solution that I came up with.

It’s not perfect, nor optimised but working! And that’s the first and most important step :wink:

after_initialize {
  require_dependency 'basic_user_serializer'

  class ::BasicUserSerializer
    attributes :name

    def name
      if user_is_current_user
        return _old_name_method
      else
        # TODO: make this list editable
        clubGroups = Array['...']

        if scope.user
          ownGroups = GroupUser.where(user_id: scope.user.id)
                               .map { |gu| gu.group_id }
                               .map { |group_id| Group.find_by(id: group_id) }
                               .filter { |group| clubGroups.include? group.name }

          if ownGroups.length > 0
            ownGroupsIds = ownGroups.map { |g| g.id }

            if GroupUser.exists?(user_id: user.id, group_id: ownGroupsIds)
              return _old_name_method
            end
          end
        end
      end

      return ''
    end

    private def _old_name_method
      return Hash === user ? user[:name] : user.try(:name)
    end
  end
}
2 Likes