mirror of
https://github.com/discourse/discourse.git
synced 2026-08-05 17:25:34 +08:00
When a group is visible but its members are hidden, some group payloads still included the exact member count. This PR serializes group member counts when the requester can see group members, and we update the public API schemas to match the conditional field.
211 lines
6.3 KiB
Ruby
Vendored
211 lines
6.3 KiB
Ruby
Vendored
# 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
|
|
@names = names.map { |n| n.to_s.downcase }
|
|
|
|
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?
|
|
if !params[:allowed_names].is_a?(Array)
|
|
raise Discourse::InvalidParameters.new(:allowed_names)
|
|
end
|
|
(params[:allowed_names] + [current_user.username]).map(&:downcase)
|
|
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.downcase) && 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
|
|
.each_with_object({}) do |group, hash|
|
|
name = group.name.downcase
|
|
members_visible = members_visible_group_ids.include?(group.id)
|
|
serialized_group = members_visible ? { user_count: group.user_count } : {}
|
|
|
|
if group_reasons[name] == :not_allowed && members_visible &&
|
|
(@topic&.private_message? || @allowed_names.present?)
|
|
notified_count = already_notified_member_count(group)
|
|
|
|
if notified_count > 0
|
|
if notified_count == group.user_count
|
|
group_reasons.delete(name)
|
|
else
|
|
group_reasons[name] = :some_not_allowed
|
|
serialized_group[:notified_count] = notified_count
|
|
end
|
|
end
|
|
end
|
|
|
|
hash[name] = serialized_group
|
|
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
|
|
|
|
DiscoursePluginRegistry.apply_modifier(:composer_mention_user_reason, reason, user)
|
|
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 already_notified_member_count(group)
|
|
GroupUser
|
|
.where(user_id: topic_allowed_user_ids)
|
|
.or(
|
|
GroupUser.where(
|
|
user_id: GroupUser.where(group_id: topic_allowed_group_ids).select(:user_id),
|
|
),
|
|
)
|
|
.where(group:)
|
|
.select(:user_id)
|
|
.distinct
|
|
.count
|
|
end
|
|
|
|
def is_user_allowed?(user, user_ids, group_ids)
|
|
user_ids.include?(user.id) ||
|
|
user.group_ids.any? do |group_id|
|
|
group_ids.include?(group_id) && visible_group_ids_for_allowed_check.include?(group_id)
|
|
end
|
|
end
|
|
|
|
def visible_group_ids_for_allowed_check
|
|
@visible_group_ids_for_allowed_check ||=
|
|
if @allowed_names.present?
|
|
Group
|
|
.members_visible_groups(current_user)
|
|
.where("LOWER(name) IN (?)", @allowed_names)
|
|
.pluck(:id)
|
|
.to_set
|
|
else
|
|
topic_allowed_group_ids || Set.new
|
|
end
|
|
end
|
|
|
|
def users
|
|
@users ||= User.not_staged.where(username_lower: @names).index_by(&:username_lower)
|
|
end
|
|
|
|
def groups
|
|
@groups ||=
|
|
Group
|
|
.visible_groups(current_user)
|
|
.where("LOWER(name) IN (?)", @names)
|
|
.index_by { |g| g.name.downcase }
|
|
end
|
|
|
|
def mentionable_group_ids
|
|
@mentionable_group_ids ||=
|
|
Group
|
|
.mentionable(current_user, include_public: false)
|
|
.where("LOWER(name) IN (?)", @names)
|
|
.pluck(:id)
|
|
.to_set
|
|
end
|
|
|
|
def members_visible_group_ids
|
|
@members_visible_group_ids ||=
|
|
Group
|
|
.members_visible_groups(current_user)
|
|
.where("LOWER(name) IN (?)", @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).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("LOWER(name) IN (?)", @allowed_names)
|
|
.pluck(:id)
|
|
.to_set
|
|
elsif @topic&.private_message?
|
|
TopicAllowedGroup.where(topic: @topic).pluck(:group_id).to_set
|
|
end
|
|
end
|
|
end
|