discourse/app/controllers/composer_controller.rb

183 lines
5.7 KiB
Ruby

# frozen_string_literal: true
class ComposerController < ApplicationController
requires_login
def mentions
@names = params.require(:names)
raise Discourse::InvalidParameters.new(:names) if !@names.kind_of?(Array) || @names.size > 20
if params[:topic_id].present?
@topic = Topic.find_by(id: params[:topic_id])
guardian.ensure_can_see!(@topic)
end
# allowed_names is necessary just for new private messages.
@allowed_names =
if params[:allowed_names].present?
raise Discourse.InvalidParameters(:allowed_names) if !params[:allowed_names].is_a?(Array)
params[:allowed_names] << current_user.username
else
[]
end
user_reasons = {}
group_reasons = {}
@names.each do |name|
if user = users[name]
reason = user_reason(user)
user_reasons[name] = reason if reason.present?
elsif group = groups[name]
reason = group_reason(group)
group_reasons[name] = reason if reason.present?
end
end
if @topic && @names.include?(SiteSetting.here_mention) && guardian.can_mention_here?
here_count =
PostAlerter.new.expand_here_mention(@topic.first_post, exclude_ids: [current_user.id]).size
end
serialized_groups =
groups
.values
.reduce({}) do |hash, group|
serialized_group = { user_count: group.user_count }
if group_reasons[group.name] == :not_allowed &&
members_visible_group_ids.include?(group.id) &&
(@topic&.private_message? || @allowed_names.present?)
# Find users that are notified already because they have been invited
# directly or via a group
notified_count =
GroupUser
# invited directly
.where(user_id: topic_allowed_user_ids)
.or(
# invited via a group
GroupUser.where(
user_id: GroupUser.where(group_id: topic_allowed_group_ids).select(:user_id),
),
)
.where(group_id: group.id)
.select(:user_id)
.distinct
.count
if notified_count > 0
if notified_count == group.user_count
group_reasons.delete(group.name)
else
group_reasons[group.name] = :some_not_allowed
serialized_group[:notified_count] = notified_count
end
end
end
hash[group.name] = serialized_group
hash
end
render json: {
users: users.keys,
user_reasons: user_reasons,
groups: serialized_groups,
group_reasons: group_reasons,
here_count: here_count,
max_users_notified_per_group_mention: SiteSetting.max_users_notified_per_group_mention,
}
end
private
def user_reason(user)
reason =
if @topic && !user.guardian.can_see?(@topic)
@topic.private_message? ? :private : :category
elsif @allowed_names.present? &&
!is_user_allowed?(user, topic_allowed_user_ids, topic_allowed_group_ids)
# This would normally be handled by the previous if, but that does not work for new private messages.
:private
elsif topic_muted_by.include?(user.id)
:muted_topic
elsif @topic&.private_message? &&
!is_user_allowed?(user, topic_allowed_user_ids, topic_allowed_group_ids)
# Admins can see the topic, but they will not be mentioned if they were not invited.
:not_allowed
end
# Non-staff users can see only basic information why the users cannot see the topic.
reason = nil if !guardian.is_staff? && reason != :private && reason != :category
reason
end
def group_reason(group)
if !mentionable_group_ids.include?(group.id)
:not_mentionable
elsif (@topic&.private_message? || @allowed_names.present?) &&
!topic_allowed_group_ids.include?(group.id)
:not_allowed
end
end
def is_user_allowed?(user, user_ids, group_ids)
user_ids.include?(user.id) || user.group_ids.any? { |group_id| group_ids.include?(group_id) }
end
def users
@users ||=
User.not_staged.where(username_lower: @names.map(&:downcase)).index_by(&:username_lower)
end
def groups
@groups ||=
Group
.visible_groups(current_user)
.where("lower(name) IN (?)", @names.map(&:downcase))
.index_by(&:name)
end
def mentionable_group_ids
@mentionable_group_ids ||=
Group.mentionable(current_user, include_public: false).where(name: @names).pluck(:id).to_set
end
def members_visible_group_ids
@members_visible_group_ids ||=
Group.members_visible_groups(current_user).where(name: @names).pluck(:id).to_set
end
def topic_muted_by
@topic_muted_by ||=
if @topic.present?
TopicUser
.where(topic: @topic)
.where(user_id: users.values.map(&:id))
.where(notification_level: TopicUser.notification_levels[:muted])
.pluck(:user_id)
.to_set
else
Set.new
end
end
def topic_allowed_user_ids
@topic_allowed_user_ids ||=
if @allowed_names.present?
User.where(username_lower: @allowed_names.map(&:downcase)).pluck(:id).to_set
elsif @topic&.private_message?
TopicAllowedUser.where(topic: @topic).pluck(:user_id).to_set
end
end
def topic_allowed_group_ids
@topic_allowed_group_ids ||=
if @allowed_names.present?
Group.messageable(current_user).where(name: @allowed_names).pluck(:id).to_set
elsif @topic&.private_message?
TopicAllowedGroup.where(topic: @topic).pluck(:group_id).to_set
end
end
end