Class: UserCommScreener

Inherits:
Object
  • Object
show all
Defined in:
lib/user_comm_screener.rb

Overview

There are various ways within Discourse that a user can prevent other users communicating with them. The purpose of this class is to find which of the target users are ignoring, muting, or preventing private messages from the acting user, so we can take alternative action (such as raising an error or showing a helpful message) if so.

Users may Mute another user (the actor), which will:

* Prevent PMs from the actor
* Prevent notifications from the actor

Users may Ignore another user (the actor), which will:

* Do everything that Mute does as well as suppressing content made by
  the actor (such as posts) from the UI

Users may also either:

a) disallow PMs from being sent to them or
b) disallow PMs except from a certain allowlist of users

A user may have this preference but have no Muted or Ignored users, which necessitates the difference between methods in this class.

An important note is that **all of these settings do not apply when the actor is a staff member**. So admins and moderators can PM and notify anyone they please.

The secondary usage of this class is to determine which users the actor themselves are muting, ignoring, or preventing private messages from. This is useful when wanting to alert the actor to these users in the UI in various ways, or prevent the actor from communicating with users they prefer not to talk with.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(acting_user: nil, acting_user_id: nil, target_user_ids:) ⇒ UserCommScreener

Returns a new instance of UserCommScreener.

Raises:

  • (ArgumentError)


104
105
106
107
108
109
110
# File 'lib/user_comm_screener.rb', line 104

def initialize(acting_user: nil, acting_user_id: nil, target_user_ids:)
  raise ArgumentError if acting_user.blank? && acting_user_id.blank?
  @acting_user = acting_user.present? ? acting_user : User.find(acting_user_id)
  target_user_ids = Array.wrap(target_user_ids) - [@acting_user.id]
  @target_users = User.where(id: target_user_ids).pluck(:id, :username).to_h
  @preferences = load_preference_map
end

Instance Attribute Details

#acting_userObject (readonly)

Returns the value of attribute acting_user.



35
36
37
# File 'lib/user_comm_screener.rb', line 35

def acting_user
  @acting_user
end

#preferencesObject (readonly)

Returns the value of attribute preferences.



35
36
37
# File 'lib/user_comm_screener.rb', line 35

def preferences
  @preferences
end

Instance Method Details

#actor_allowing_communicationObject



146
147
148
# File 'lib/user_comm_screener.rb', line 146

def actor_allowing_communication
  @target_users.keys - actor_preventing_communication
end

#actor_disallowing_all_pms?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/user_comm_screener.rb', line 187

def actor_disallowing_all_pms?
  !acting_user.user_option.allow_private_messages
end

#actor_disallowing_any_pms?(user_ids) ⇒ Boolean

Returns:

  • (Boolean)


183
184
185
# File 'lib/user_comm_screener.rb', line 183

def actor_disallowing_any_pms?(user_ids)
  user_ids.any? { |user_id| actor_disallowing_pms?(user_id) }
end

#actor_disallowing_pms?(user_id) ⇒ Boolean

Returns:

  • (Boolean)


176
177
178
179
180
181
# File 'lib/user_comm_screener.rb', line 176

def actor_disallowing_pms?(user_id)
  validate_user_id!(user_id)
  return true if actor_disallowing_all_pms?
  return false if !acting_user.user_option.enable_allowed_pm_users
  actor_preferences[:disallowed_pms_from].include?(user_id)
end

#actor_ignoring?(user_id) ⇒ Boolean

The actor methods below are more fine-grained than the user ones, since we may want to display more detailed messages to the actor about their preferences than we do when we are informing the actor that they cannot communicate with certain users.

In this spirit, actor_disallowing_pms? is intentionally different from disallowing_pms_from_actor? above.

Returns:

  • (Boolean)


166
167
168
169
# File 'lib/user_comm_screener.rb', line 166

def actor_ignoring?(user_id)
  validate_user_id!(user_id)
  actor_preferences[:ignoring].include?(user_id)
end

#actor_muting?(user_id) ⇒ Boolean

Returns:

  • (Boolean)


171
172
173
174
# File 'lib/user_comm_screener.rb', line 171

def actor_muting?(user_id)
  validate_user_id!(user_id)
  actor_preferences[:muting].include?(user_id)
end

#actor_preventing_communicationObject



150
151
152
153
154
155
# File 'lib/user_comm_screener.rb', line 150

def actor_preventing_communication
  (
    actor_preferences[:ignoring] + actor_preferences[:muting] +
      actor_preferences[:disallowed_pms_from]
  ).uniq
end

#allowing_actor_communicationObject

Users who have preferences are the only ones initially loaded by the query, so implicitly the leftover users have no preferences that mute, ignore, or disallow PMs from any other user.



116
117
118
# File 'lib/user_comm_screener.rb', line 116

def allowing_actor_communication
  (preferences.allowing_actor_communication.map(&:user_id) + users_with_no_preference).uniq
end

#disallowing_pms_from_actor?(user_id) ⇒ Boolean

Whether the user is disallowing PMs from the actor specifically or in general, meaning the actor cannot send PMs to this target user. Ignoring or muting implicitly disallows PMs, so we need to take into account those preferences here too.

Returns:

  • (Boolean)


141
142
143
144
# File 'lib/user_comm_screener.rb', line 141

def disallowing_pms_from_actor?(user_id)
  validate_user_id!(user_id)
  preferences.disallowing_pms?(user_id) || ignoring_or_muting_actor?(user_id)
end

#ignoring_or_muting_actor?(user_id) ⇒ Boolean

Whether the user is ignoring or muting the actor, meaning the actor cannot PM or send notifications to this target user.

Returns:

  • (Boolean)


131
132
133
134
# File 'lib/user_comm_screener.rb', line 131

def ignoring_or_muting_actor?(user_id)
  validate_user_id!(user_id)
  preferences.ignoring_or_muting?(user_id)
end

#preventing_actor_communicationObject

Any users who are either ignoring, muting, or disallowing PMs from the actor. Ignoring and muting implicitly ignore PMs which is why they fall under this umbrella as well.



124
125
126
# File 'lib/user_comm_screener.rb', line 124

def preventing_actor_communication
  preferences.preventing_actor_communication.map(&:user_id)
end