Плагин для отображения полного имени только пользователям одной группы

Всем привет,

Я уже несколько недель гуглю свою проблему и почти готов сдаться.

Контекст:
Я разрабатываю и поддерживаю многопользовательскую панель администратора для спортивных клубов.
Мой форум на Discourse, размещенный на собственном хостинге, доступен только членам этих клубов. Я хотел бы сделать его общедоступным. Моя собственная интеграция SSO автоматически присваивает имя пользователя из моей базы данных пользователю Discourse после входа в систему.

Проблема:
Сейчас я не могу сделать свой форум общедоступным, потому что полное имя пользователя должно быть видно только пользователям того же клуба.

Мой опыт разработки на Ruby равен нулю (я разработчик на JS), но пока что я разобрался с этой частью кода:

# plugin.rb

after_initialize {
  require_dependency 'basic_user_serializer'
  require_dependency 'current_user'

  class ::BasicUserSerializer
    attributes :name

    def name
      # фиксированный набор названий групп, указывающих на принадлежность к клубу
      clubGroups = Array['foo', 'bar', 'baz']
      
      # ЧАСТЬ, КОТОРУЮ Я НЕ МОГУ РАЗОБРАТЬСЯ:
      # Показывать имя пользователя ТОЛЬКО если они принадлежат к одной из тех же `clubGroups`, что и текущий вошедший пользователь,
      # например, я вошел как пользователь, принадлежащий к группе 'bar', что означает, что я принадлежу к клубу Bar. Я должен иметь возможность видеть имена только тех пользователей, которые также принадлежат к группе 'bar'
      ???

    end
  end
}

Я не могу разобраться в следующих вещах:

  1. как получить группы текущего пользователя
  2. как получить группы пользователя, чье имя отображается

Как только у меня будет это, я смогу сравнить два массива с clubGroups и решить, показывать ли имя пользователя или нет.

Еще одной проблемой для меня будет отключение возможности редактировать свое имя, но это уже другая история.

Модель GroupUser содержит информацию о членстве пользователя в группах.

Вам нужно что-то вроде: GroupUser.where(user_id: current_user.id), чтобы получить идентификаторы групп для текущего пользователя.

Это вернёт отношение Active Record.

Затем вы можете преобразовать его в массив с помощью map:

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

Примените тот же подход для вашего избранного пользователя.

Отлично! Спасибо :slight_smile:

Пока не знаю, как превратить это в рабочее решение, но это уже серьёзная подсказка.

Никогда не сдавайтесь. Просто идите вперёд. :rocket:

Ещё одно препятствие:

При попытке получить ID текущего пользователя для передачи в запрос, я не совсем понимаю, как это сделать.
Когда я перехожу к модели CurrentUser в коде, единственный метод, который выглядит подходящим для моих задач, — это current_user, но при выполнении этого:

pp CurrentUser.current_user

я получаю ошибку:

undefined method `current_user' for CurrentUser:Module

Кажется, мне не хватает каких-то базовых знаний о Ruby, но, возможно, вы сможете легко помочь мне это решить?

Возможно, стоит посмотреть код этого плагина. Он добавляет аватар-флер для пользователей из одних и тех же групп, чтобы они могли узнавать друг друга как участников группы. Это немного другая задача, но в коде есть проверка, находится ли просматривающий пользователь в той же группе, что и автор сообщения.

Ключ к разгадке — в этом методе:

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

Пользователь, который сериализуется, — это object.

Текущий пользователь находится в scope.user, поэтому:

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

скорее всего, подойдёт для текущего пользователя.

и так далее.

Полезные советы:

  1. Изучите исходный код Discourse
  2. Как отметил @RGJ, посмотрите на несколько плагинов с открытым исходным кодом, чтобы понять, как они решают подобные задачи
  3. Протестируйте идеи в консоли Rails: rails c --sandbox

Удивительно! Много отличных знаний. Спасибо, ребята :heart_eyes:

Я погружаюсь в это :dealwithit:

Ради будущих посетителей этой темы, вот окончательное решение, к которому я пришёл.

Оно не идеально и не оптимизировано, но работает! А это первый и самый важный шаг :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: сделать этот список редактируемым
        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
}