Commit cdc4b4d7 authored by http://jneen.net/'s avatar http://jneen.net/

make recipients mutative during the build

parent 734e2105
...@@ -34,24 +34,35 @@ class NotificationRecipientService ...@@ -34,24 +34,35 @@ class NotificationRecipientService
raise 'abstract' raise 'abstract'
end end
def recipients
@recipients ||= []
end
def to_a
return recipients if @already_built
@already_built = true
build
recipients.uniq!
recipients.freeze
recipients
end
# Remove users with disabled notifications from array # Remove users with disabled notifications from array
# Also remove duplications and nil recipients # Also remove duplications and nil recipients
def reject_muted_users(users) def reject_muted_users
reject_users(users, :disabled) reject_users(:disabled)
end end
protected protected
# Ensure that if we modify this array, we aren't modifying the memoised def add_participants(user)
# participants on the target.
def participants(user)
return unless target.respond_to?(:participants) return unless target.respond_to?(:participants)
target.participants(user).dup recipients.concat(target.participants(user))
end end
# Get project/group users with CUSTOM notification level # Get project/group users with CUSTOM notification level
def add_custom_notifications(recipients, action) def add_custom_notifications(action)
user_ids = [] user_ids = []
# Users with a notification setting on group or project # Users with a notification setting on group or project
...@@ -68,8 +79,9 @@ class NotificationRecipientService ...@@ -68,8 +79,9 @@ class NotificationRecipientService
recipients.concat(User.find(user_ids)) recipients.concat(User.find(user_ids))
end end
def add_project_watchers(recipients) def add_project_watchers
recipients.concat(project_watchers).compact recipients.concat(project_watchers)
recipients.compact!
end end
# Get project users with WATCH notification level # Get project users with WATCH notification level
...@@ -88,14 +100,14 @@ class NotificationRecipientService ...@@ -88,14 +100,14 @@ class NotificationRecipientService
end end
# Remove users with notification level 'Mentioned' # Remove users with notification level 'Mentioned'
def reject_mention_users(users) def reject_mention_users
reject_users(users, :mention) reject_users(:mention)
end end
def add_subscribed_users(recipients) def add_subscribed_users
return recipients unless target.respond_to? :subscribers return unless target.respond_to? :subscribers
recipients + target.subscribers(project) recipients.concat(target.subscribers(project))
end end
def user_ids_notifiable_on(resource, notification_level = nil, action = nil) def user_ids_notifiable_on(resource, notification_level = nil, action = nil)
...@@ -167,34 +179,35 @@ class NotificationRecipientService ...@@ -167,34 +179,35 @@ class NotificationRecipientService
# Reject users which has certain notification level # Reject users which has certain notification level
# #
# Example: # Example:
# reject_users(users, :watch, project) # reject_users(:watch, project)
# #
def reject_users(users, level) def reject_users(level)
level = level.to_s level = level.to_s
unless NotificationSetting.levels.keys.include?(level) unless NotificationSetting.levels.keys.include?(level)
raise 'Invalid notification level' raise 'Invalid notification level'
end end
users = users.to_a.compact.uniq recipients.compact!
recipients.uniq!
users.reject do |user| recipients.reject! do |user|
setting = NotificationRecipientService.notification_setting_for_user_project(user, project) setting = NotificationRecipientService.notification_setting_for_user_project(user, project)
setting.present? && setting.level == level setting.present? && setting.level == level
end end
end end
def reject_unsubscribed_users(recipients) def reject_unsubscribed_users
return recipients unless target.respond_to? :subscriptions return unless target.respond_to? :subscriptions
recipients.reject do |user| recipients.reject! do |user|
subscription = target.subscriptions.find_by_user_id(user.id) subscription = target.subscriptions.find_by_user_id(user.id)
subscription && !subscription.subscribed subscription && !subscription.subscribed
end end
end end
def reject_users_without_access(recipients) def reject_users_without_access
recipients = recipients.select { |u| u.can?(:receive_notifications) } recipients.select! { |u| u.can?(:receive_notifications) }
ability = case target ability = case target
when Issuable when Issuable
...@@ -203,21 +216,19 @@ class NotificationRecipientService ...@@ -203,21 +216,19 @@ class NotificationRecipientService
:read_build # We have build trace in pipeline emails :read_build # We have build trace in pipeline emails
end end
return recipients unless ability return unless ability
recipients.select do |user| recipients.select! do |user|
user.can?(ability, target) user.can?(ability, target)
end end
end end
def add_labels_subscribers(recipients, labels: nil) def add_labels_subscribers(labels: nil)
return recipients unless target.respond_to? :labels return unless target.respond_to? :labels
(labels || target.labels).each do |label| (labels || target.labels).each do |label|
recipients += label.subscribers(project) recipients.concat(label.subscribers(project))
end end
recipients
end end
end end
...@@ -238,10 +249,10 @@ class NotificationRecipientService ...@@ -238,10 +249,10 @@ class NotificationRecipientService
end end
def build def build
recipients = participants(current_user) add_participants(current_user)
recipients = add_project_watchers(recipients) add_project_watchers
recipients = add_custom_notifications(recipients, custom_action) add_custom_notifications(custom_action)
recipients = reject_mention_users(recipients) reject_mention_users
# Re-assign is considered as a mention of the new assignee so we add the # Re-assign is considered as a mention of the new assignee so we add the
# new assignee to the list of recipients after we rejected users with # new assignee to the list of recipients after we rejected users with
...@@ -256,19 +267,17 @@ class NotificationRecipientService ...@@ -256,19 +267,17 @@ class NotificationRecipientService
recipients.concat(target.assignees) recipients.concat(target.assignees)
end end
recipients = reject_muted_users(recipients) reject_muted_users
recipients = add_subscribed_users(recipients) add_subscribed_users
if [:new_issue, :new_merge_request].include?(custom_action) if [:new_issue, :new_merge_request].include?(custom_action)
recipients = add_labels_subscribers(recipients) add_labels_subscribers
end end
recipients = reject_unsubscribed_users(recipients) reject_unsubscribed_users
recipients = reject_users_without_access(recipients) reject_users_without_access
recipients.delete(current_user) if skip_current_user && !current_user.notified_of_own_activity? recipients.delete(current_user) if skip_current_user && !current_user.notified_of_own_activity?
recipients.uniq
end end
# Build event key to search on custom notification level # Build event key to search on custom notification level
...@@ -303,13 +312,14 @@ class NotificationRecipientService ...@@ -303,13 +312,14 @@ class NotificationRecipientService
notification_setting = NotificationRecipientService.notification_setting_for_user_project(current_user, target.project) notification_setting = NotificationRecipientService.notification_setting_for_user_project(current_user, target.project)
return [] if notification_setting.mention? || notification_setting.disabled? return if notification_setting.mention? || notification_setting.disabled?
return [] if notification_setting.custom? && !notification_setting.event_enabled?(custom_action) return if notification_setting.custom? && !notification_setting.event_enabled?(custom_action)
return [] if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action) return if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action)
reject_users_without_access([current_user]) recipients << current_user
reject_users_without_access
end end
end end
...@@ -326,11 +336,10 @@ class NotificationRecipientService ...@@ -326,11 +336,10 @@ class NotificationRecipientService
end end
def build def build
recipients = add_labels_subscribers([], labels: labels) add_labels_subscribers(labels: labels)
recipients = reject_unsubscribed_users(recipients) reject_unsubscribed_users
recipients = reject_users_without_access(recipients) reject_users_without_access
recipients.delete(current_user) unless current_user.notified_of_own_activity? recipients.delete(current_user) unless current_user.notified_of_own_activity?
recipients.uniq
end end
end end
...@@ -344,7 +353,7 @@ class NotificationRecipientService ...@@ -344,7 +353,7 @@ class NotificationRecipientService
@target = note.noteable @target = note.noteable
end end
def build(note) def build
ability, subject = if note.for_personal_snippet? ability, subject = if note.for_personal_snippet?
[:read_personal_snippet, note.noteable] [:read_personal_snippet, note.noteable]
else else
...@@ -354,45 +363,45 @@ class NotificationRecipientService ...@@ -354,45 +363,45 @@ class NotificationRecipientService
mentioned_users = note.mentioned_users.select { |user| user.can?(ability, subject) } mentioned_users = note.mentioned_users.select { |user| user.can?(ability, subject) }
# Add all users participating in the thread (author, assignee, comment authors) # Add all users participating in the thread (author, assignee, comment authors)
recipients = participants(note.author) || mentioned_users add_participants(note.author)
recipients.concat(mentioned_users) if recipients.empty?
unless note.for_personal_snippet? unless note.for_personal_snippet?
# Merge project watchers # Merge project watchers
recipients = add_project_watchers(recipients) add_project_watchers
# Merge project with custom notification # Merge project with custom notification
recipients = add_custom_notifications(recipients, :new_note) add_custom_notifications(:new_note)
end end
# Reject users with Mention notification level, except those mentioned in _this_ note. # Reject users with Mention notification level, except those mentioned in _this_ note.
recipients = reject_mention_users(recipients - mentioned_users) reject_mention_users
recipients = recipients + mentioned_users recipients.concat(mentioned_users)
recipients = reject_muted_users(recipients) reject_muted_users
recipients = add_subscribed_users(recipients) add_subscribed_users
recipients = reject_unsubscribed_users(recipients) reject_unsubscribed_users
recipients = reject_users_without_access(recipients) reject_users_without_access
recipients.delete(note.author) unless note.author.notified_of_own_activity? recipients.delete(note.author) unless note.author.notified_of_own_activity?
recipients.uniq
end end
end end
end end
def build_recipients(*a) def build_recipients(*a)
Builder::Default.new(@project, *a).build Builder::Default.new(@project, *a).to_a
end end
def build_pipeline_recipients(*a) def build_pipeline_recipients(*a)
Builder::Pipeline.new(@project, *a).build Builder::Pipeline.new(@project, *a).to_a
end end
def build_relabeled_recipients(*a) def build_relabeled_recipients(*a)
Builder::Relabeled.new(@project, *a).build Builder::Relabeled.new(@project, *a).to_a
end end
def build_new_note_recipients(*a) def build_new_note_recipients(*a)
Builder::NewNote.new(@project, *a).build Builder::NewNote.new(@project, *a).to_a
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment