同じグループのユーザーにのみフルネームを表示するプラグイン

こんにちは、皆さん。

数週間前からこの問題で Google 検索を続けていますが、もう諦めそうになっています。

背景:
私はスポーツクラブ向けのマルチテナント管理パネルを開発・保守しています。
セルフホスト型の 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. 対象ユーザーのグループを取得する方法

これらが分かれば、2 つの配列を clubGroups と比較して、ユーザーの名前を表示するかどうかを判断できます。

もう一つの課題は、自分自身の名前を編集する機能を無効にすることですが、それはまた別の話です。

GroupUser モデルには、ユーザーのグループ所属情報が含まれています。

現在のユーザーのグループ ID を取得するには、以下のようにします: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
}