FIX: ensures users can open channel invites (#24067)

We were incorrectly generating URLs with message id even when it was not provided, resulting in a route ending with "undefined", which was causing an error.

This commit also uses this opportunity to:
- move `invite_users` into a proper controller inside the API namespace
- refactors the code into a service: `Chat::InviteUsersToChannel`
This commit is contained in:
Joffrey JAFFEUX
2023-10-24 18:51:33 +02:00
committed by GitHub
parent 930dc38500
commit 5fec841c19
16 changed files with 355 additions and 175 deletions

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
class Chat::Api::ChannelsInvitesController < Chat::ApiController
def create
with_service(Chat::InviteUsersToChannel) do
on_failed_policy(:can_view_channel) { raise Discourse::InvalidAccess }
on_model_not_found(:channel) { raise Discourse::NotFound }
end
end
end

View File

@@ -114,37 +114,6 @@ module Chat
render json: { chat_enabled: current_user.user_option.chat_enabled }
end
def invite_users
params.require(:user_ids)
users =
User
.includes(:groups)
.joins(:user_option)
.where(user_options: { chat_enabled: true })
.not_suspended
.where(id: params[:user_ids])
users.each do |user|
if user.guardian.can_join_chat_channel?(@chat_channel)
data = {
message: "chat.invitation_notification",
chat_channel_id: @chat_channel.id,
chat_channel_title: @chat_channel.title(user),
chat_channel_slug: @chat_channel.slug,
invited_by_username: current_user.username,
}
data[:chat_message_id] = params[:chat_message_id] if params[:chat_message_id]
user.notifications.create(
notification_type: Notification.types[:chat_invitation],
high_priority: true,
data: data.to_json,
)
end
end
render json: success_json
end
def dismiss_retention_reminder
params.require(:chatable_type)
guardian.ensure_can_chat!

View File

@@ -9,7 +9,7 @@ module Chat
class CreateThread
include Service::Base
# @!method call(thread_id:, channel_id:, guardian:, **params_to_edit)
# @!method call(thread_id:, channel_id:, guardian:, **params_to_create)
# @param [Integer] original_message_id
# @param [Integer] channel_id
# @param [Guardian] guardian

View File

@@ -0,0 +1,76 @@
# frozen_string_literal: true
module Chat
# Invites users to a channel.
#
# @example
# Chat::InviteUsersToChannel.call(channel_id: 2, user_ids: [2, 43], guardian: guardian, **optional_params)
#
class InviteUsersToChannel
include Service::Base
# @!method call(user_ids:, channel_id:, guardian:)
# @param [Array<Integer>] user_ids
# @param [Integer] channel_id
# @param [Guardian] guardian
# @option optional_params [Integer, nil] message_id
# @return [Service::Base::Context]
contract
model :channel
policy :can_view_channel
model :users, optional: true
step :send_invite_notifications
# @!visibility private
class Contract
attribute :user_ids, :array
validates :user_ids, presence: true
attribute :channel_id, :integer
validates :channel_id, presence: true
attribute :message_id, :integer
end
private
def fetch_channel(contract:, **)
::Chat::Channel.find_by(id: contract.channel_id)
end
def can_view_channel(guardian:, channel:, **)
guardian.can_preview_chat_channel?(channel)
end
def fetch_users(contract:, **)
::User
.joins(:user_option)
.where(user_options: { chat_enabled: true })
.not_suspended
.where(id: contract.user_ids)
.limit(50)
end
def send_invite_notifications(channel:, guardian:, users:, contract:, **)
users&.each do |invited_user|
next if !invited_user.guardian.can_join_chat_channel?(channel)
data = {
message: "chat.invitation_notification",
chat_channel_id: channel.id,
chat_channel_title: channel.title(invited_user),
chat_channel_slug: channel.slug,
invited_by_username: guardian.user.username,
}
data[:chat_message_id] = contract.message_id if contract.message_id
invited_user.notifications.create(
notification_type: ::Notification.types[:chat_invitation],
high_priority: true,
data: data.to_json,
)
end
end
end
end