Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
b1334ed9
Commit
b1334ed9
authored
Jul 20, 2017
by
http://jneen.net/
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
move the builders to classes with a #build
parent
cce1bc9a
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
257 additions
and
219 deletions
+257
-219
app/services/notification_recipient_service.rb
app/services/notification_recipient_service.rb
+257
-219
No files found.
app/services/notification_recipient_service.rb
View file @
b1334ed9
...
@@ -20,297 +20,335 @@ class NotificationRecipientService
...
@@ -20,297 +20,335 @@ class NotificationRecipientService
@project
=
project
@project
=
project
end
end
def
build_recipients
(
target
,
current_user
,
action
:,
previous_assignee:
nil
,
skip_current_user:
true
)
module
Builder
custom_action
=
build_custom_key
(
action
,
target
)
recipients
=
participants
(
target
,
current_user
)
recipients
=
add_project_watchers
(
recipients
)
recipients
=
add_custom_notifications
(
recipients
,
custom_action
)
recipients
=
reject_mention_users
(
recipients
)
# 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
# the "on mention" notification level
case
custom_action
when
:reassign_merge_request
recipients
<<
previous_assignee
if
previous_assignee
recipients
<<
target
.
assignee
when
:reassign_issue
previous_assignees
=
Array
(
previous_assignee
)
recipients
.
concat
(
previous_assignees
)
recipients
.
concat
(
target
.
assignees
)
end
recipients
=
reject_muted_users
(
recipients
)
recipients
=
add_subscribed_users
(
recipients
,
target
)
if
[
:new_issue
,
:new_merge_request
].
include?
(
custom_action
)
class
Base
recipients
=
add_labels_subscribers
(
recipients
,
target
)
attr_reader
:project
end
def
initialize
(
project
)
@project
=
project
end
recipients
=
reject_unsubscribed_users
(
recipients
,
target
)
def
build
(
*
)
recipients
=
reject_users_without_access
(
recipients
,
target
)
raise
'abstract'
end
recipients
.
delete
(
current_user
)
if
skip_current_user
&&
!
current_user
.
notified_of_own_activity?
# Remove users with disabled notifications from array
# Also remove duplications and nil recipients
def
reject_muted_users
(
users
)
reject_users
(
users
,
:disabled
)
end
recipients
.
uniq
protected
end
def
build_pipeline_recipients
(
target
,
current_user
,
action
:)
# Ensure that if we modify this array, we aren't modifying the memoised
return
[]
unless
current_user
# participants on the target.
def
participants
(
target
,
user
)
return
unless
target
.
respond_to?
(
:participants
)
custom_action
=
target
.
participants
(
user
).
dup
case
action
.
to_s
when
'failed'
:failed_pipeline
when
'success'
:success_pipeline
end
end
notification_setting
=
NotificationRecipientService
.
notification_setting_for_user_project
(
current_user
,
target
.
project
)
# Get project/group users with CUSTOM notification level
def
add_custom_notifications
(
recipients
,
action
)
user_ids
=
[]
return
[]
if
notification_setting
.
mention?
||
notification_setting
.
disabled?
# Users with a notification setting on group or project
user_ids
+=
user_ids_notifiable_on
(
project
,
:custom
,
action
)
user_ids
+=
user_ids_notifiable_on
(
project
.
group
,
:custom
,
action
)
return
[]
if
notification_setting
.
custom?
&&
!
notification_setting
.
event_enabled?
(
custom_action
)
# Users with global level custom
user_ids_with_project_level_global
=
user_ids_notifiable_on
(
project
,
:global
)
user_ids_with_group_level_global
=
user_ids_notifiable_on
(
project
.
group
,
:global
)
return
[]
if
(
notification_setting
.
watch?
||
notification_setting
.
participating?
)
&&
NotificationSetting
::
EXCLUDED_WATCHER_EVENTS
.
include?
(
custom_action
)
global_users_ids
=
user_ids_with_project_level_global
.
concat
(
user_ids_with_group_level_global
)
user_ids
+=
user_ids_with_global_level_custom
(
global_users_ids
,
action
)
reject_users_without_access
([
current_user
],
target
)
recipients
.
concat
(
User
.
find
(
user_ids
)
)
end
end
def
build_relabeled_recipients
(
target
,
current_user
,
labels
:)
def
add_project_watchers
(
recipients
)
recipients
=
add_labels_subscribers
([],
target
,
labels:
labels
)
recipients
.
concat
(
project_watchers
).
compact
recipients
=
reject_unsubscribed_users
(
recipients
,
target
)
end
recipients
=
reject_users_without_access
(
recipients
,
target
)
recipients
.
delete
(
current_user
)
unless
current_user
.
notified_of_own_activity?
recipients
.
uniq
end
def
build_new_note_recipients
(
note
)
# Get project users with WATCH notification level
target
=
note
.
noteable
def
project_watchers
project_members_ids
=
user_ids_notifiable_on
(
project
)
ability
,
subject
=
if
note
.
for_personal_snippet?
user_ids_with_project_global
=
user_ids_notifiable_on
(
project
,
:global
)
[
:read_personal_snippet
,
note
.
noteable
]
user_ids_with_group_global
=
user_ids_notifiable_on
(
project
.
group
,
:global
)
else
[
:read_project
,
note
.
project
]
end
mentioned_users
=
note
.
mentioned_users
.
select
{
|
user
|
user
.
can?
(
ability
,
subject
)
}
user_ids
=
user_ids_with_global_level_watch
((
user_ids_with_project_global
+
user_ids_with_group_global
).
uniq
)
# Add all users participating in the thread (author, assignee, comment author
s)
user_ids_with_project_setting
=
select_project_members_ids
(
project
,
user_ids_with_project_global
,
user_id
s
)
recipients
=
participants
(
target
,
note
.
author
)
||
mentioned_users
user_ids_with_group_setting
=
select_group_members_ids
(
project
.
group
,
project_members_ids
,
user_ids_with_group_global
,
user_ids
)
unless
note
.
for_personal_snippet?
User
.
where
(
id:
user_ids_with_project_setting
.
concat
(
user_ids_with_group_setting
).
uniq
).
to_a
# Merge project watchers
end
recipients
=
add_project_watchers
(
recipients
)
# Merge project with custom notification
# Remove users with notification level 'Mentioned'
recipients
=
add_custom_notifications
(
recipients
,
:new_note
)
def
reject_mention_users
(
users
)
end
reject_users
(
users
,
:mention
)
end
# Reject users with Mention notification level, except those mentioned in _this_ note.
def
add_subscribed_users
(
recipients
,
target
)
recipients
=
reject_mention_users
(
recipients
-
mentioned_users
)
return
recipients
unless
target
.
respond_to?
:subscribers
recipients
=
recipients
+
mentioned_users
recipients
=
reject_muted_users
(
recipients
)
recipients
+
target
.
subscribers
(
project
)
end
recipients
=
add_subscribed_users
(
recipients
,
note
.
noteable
)
def
user_ids_notifiable_on
(
resource
,
notification_level
=
nil
,
action
=
nil
)
recipients
=
reject_unsubscribed_users
(
recipients
,
note
.
noteable
)
return
[]
unless
resource
recipients
=
reject_users_without_access
(
recipients
,
note
.
noteable
)
recipients
.
delete
(
note
.
author
)
unless
note
.
author
.
notified_of_own_activity?
if
notification_level
recipients
.
uniq
settings
=
resource
.
notification_settings
.
where
(
level:
NotificationSetting
.
levels
[
notification_level
])
end
settings
=
settings
.
select
{
|
setting
|
setting
.
event_enabled?
(
action
)
}
if
action
.
present?
settings
.
map
(
&
:user_id
)
else
resource
.
notification_settings
.
pluck
(
:user_id
)
end
end
# Remove users with disabled notifications from array
# Build a list of user_ids based on project notification settings
# Also remove duplications and nil recipients
def
select_project_members_ids
(
project
,
global_setting
,
user_ids_global_level_watch
)
def
reject_muted_users
(
users
)
user_ids
=
user_ids_notifiable_on
(
project
,
:watch
)
reject_users
(
users
,
:disabled
)
end
protected
# If project setting is global, add to watch list if global setting is watch
global_setting
.
each
do
|
user_id
|
if
user_ids_global_level_watch
.
include?
(
user_id
)
user_ids
<<
user_id
end
end
# Ensure that if we modify this array, we aren't modifying the memoised
user_ids
# participants on the target.
end
def
participants
(
target
,
user
)
return
unless
target
.
respond_to?
(
:participants
)
target
.
participants
(
user
).
dup
# Build a list of user_ids based on group notification settings
end
def
select_group_members_ids
(
group
,
project_members
,
global_setting
,
user_ids_global_level_watch
)
uids
=
user_ids_notifiable_on
(
group
,
:watch
)
# Group setting is watch, add to user_ids list if user is not project member
user_ids
=
[]
uids
.
each
do
|
user_id
|
if
project_members
.
exclude?
(
user_id
)
user_ids
<<
user_id
end
end
# Group setting is global, add to user_ids list if global setting is watch
global_setting
.
each
do
|
user_id
|
if
project_members
.
exclude?
(
user_id
)
&&
user_ids_global_level_watch
.
include?
(
user_id
)
user_ids
<<
user_id
end
end
user_ids
end
# Get project/group users with CUSTOM notification level
def
user_ids_with_global_level_watch
(
ids
)
def
add_custom_notifications
(
recipients
,
action
)
settings_with_global_level_of
(
:watch
,
ids
).
pluck
(
:user_id
)
user_ids
=
[]
end
# Users with a notification setting on group or project
def
user_ids_with_global_level_custom
(
ids
,
action
)
user_ids
+=
user_ids_notifiable_on
(
project
,
:custom
,
action
)
settings
=
settings_with_global_level_of
(
:custom
,
ids
)
user_ids
+=
user_ids_notifiable_on
(
project
.
group
,
:custom
,
action
)
settings
=
settings
.
select
{
|
setting
|
setting
.
event_enabled?
(
action
)
}
settings
.
map
(
&
:user_id
)
end
# Users with global level custom
def
settings_with_global_level_of
(
level
,
ids
)
user_ids_with_project_level_global
=
user_ids_notifiable_on
(
project
,
:global
)
NotificationSetting
.
where
(
user_ids_with_group_level_global
=
user_ids_notifiable_on
(
project
.
group
,
:global
)
user_id:
ids
,
source_type:
nil
,
level:
NotificationSetting
.
levels
[
level
]
)
end
global_users_ids
=
user_ids_with_project_level_global
.
concat
(
user_ids_with_group_level_global
)
# Reject users which has certain notification level
user_ids
+=
user_ids_with_global_level_custom
(
global_users_ids
,
action
)
#
# Example:
# reject_users(users, :watch, project)
#
def
reject_users
(
users
,
level
)
level
=
level
.
to_s
recipients
.
concat
(
User
.
find
(
user_ids
))
unless
NotificationSetting
.
levels
.
keys
.
include?
(
level
)
end
raise
'Invalid notification level'
end
def
add_project_watchers
(
recipients
)
users
=
users
.
to_a
.
compact
.
uniq
recipients
.
concat
(
project_watchers
).
compact
end
# Get project users with WATCH notification level
users
.
reject
do
|
user
|
def
project_watchers
setting
=
NotificationRecipientService
.
notification_setting_for_user_project
(
user
,
project
)
project_members_ids
=
user_ids_notifiable_on
(
project
)
setting
.
present?
&&
setting
.
level
==
level
end
end
user_ids_with_project_global
=
user_ids_notifiable_on
(
project
,
:global
)
def
reject_unsubscribed_users
(
recipients
,
target
)
user_ids_with_group_global
=
user_ids_notifiable_on
(
project
.
group
,
:global
)
return
recipients
unless
target
.
respond_to?
:subscriptions
user_ids
=
user_ids_with_global_level_watch
((
user_ids_with_project_global
+
user_ids_with_group_global
).
uniq
)
recipients
.
reject
do
|
user
|
subscription
=
target
.
subscriptions
.
find_by_user_id
(
user
.
id
)
subscription
&&
!
subscription
.
subscribed
end
end
user_ids_with_project_setting
=
select_project_members_ids
(
project
,
user_ids_with_project_global
,
user_ids
)
def
reject_users_without_access
(
recipients
,
target
)
user_ids_with_group_setting
=
select_group_members_ids
(
project
.
group
,
project_members_ids
,
user_ids_with_group_global
,
user_ids
)
recipients
=
recipients
.
select
{
|
u
|
u
.
can?
(
:receive_notifications
)
}
User
.
where
(
id:
user_ids_with_project_setting
.
concat
(
user_ids_with_group_setting
).
uniq
).
to_a
ability
=
case
target
end
when
Issuable
:"read_
#{
target
.
to_ability_name
}
"
when
Ci
::
Pipeline
:read_build
# We have build trace in pipeline emails
end
# Remove users with notification level 'Mentioned'
return
recipients
unless
ability
def
reject_mention_users
(
users
)
reject_users
(
users
,
:mention
)
end
def
add_subscribed_users
(
recipients
,
target
)
recipients
.
select
do
|
user
|
return
recipients
unless
target
.
respond_to?
:subscribers
user
.
can?
(
ability
,
target
)
end
end
recipients
+
target
.
subscribers
(
project
)
def
add_labels_subscribers
(
recipients
,
target
,
labels:
nil
)
end
return
recipients
unless
target
.
respond_to?
:labels
def
user_ids_notifiable_on
(
resource
,
notification_level
=
nil
,
action
=
nil
)
(
labels
||
target
.
labels
).
each
do
|
label
|
return
[]
unless
resource
recipients
+=
label
.
subscribers
(
project
)
end
if
notification_level
recipients
settings
=
resource
.
notification_settings
.
where
(
level:
NotificationSetting
.
levels
[
notification_level
])
end
settings
=
settings
.
select
{
|
setting
|
setting
.
event_enabled?
(
action
)
}
if
action
.
present?
settings
.
map
(
&
:user_id
)
else
resource
.
notification_settings
.
pluck
(
:user_id
)
end
end
# Build a list of user_ids based on project notification settings
# Build event key to search on custom notification level
def
select_project_members_ids
(
project
,
global_setting
,
user_ids_global_level_watch
)
# Check NotificationSetting::EMAIL_EVENTS
user_ids
=
user_ids_notifiable_on
(
project
,
:watch
)
def
build_custom_key
(
action
,
object
)
"
#{
action
}
_
#{
object
.
class
.
model_name
.
name
.
underscore
}
"
.
to_sym
end
end
# If project setting is global, add to watch list if global setting is watch
class
Default
<
Base
global_setting
.
each
do
|
user_id
|
def
build
(
target
,
current_user
,
action
:,
previous_assignee:
nil
,
skip_current_user:
true
)
if
user_ids_global_level_watch
.
include?
(
user_id
)
custom_action
=
build_custom_key
(
action
,
target
)
user_ids
<<
user_id
recipients
=
participants
(
target
,
current_user
)
recipients
=
add_project_watchers
(
recipients
)
recipients
=
add_custom_notifications
(
recipients
,
custom_action
)
recipients
=
reject_mention_users
(
recipients
)
# 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
# the "on mention" notification level
case
custom_action
when
:reassign_merge_request
recipients
<<
previous_assignee
if
previous_assignee
recipients
<<
target
.
assignee
when
:reassign_issue
previous_assignees
=
Array
(
previous_assignee
)
recipients
.
concat
(
previous_assignees
)
recipients
.
concat
(
target
.
assignees
)
end
recipients
=
reject_muted_users
(
recipients
)
recipients
=
add_subscribed_users
(
recipients
,
target
)
if
[
:new_issue
,
:new_merge_request
].
include?
(
custom_action
)
recipients
=
add_labels_subscribers
(
recipients
,
target
)
end
recipients
=
reject_unsubscribed_users
(
recipients
,
target
)
recipients
=
reject_users_without_access
(
recipients
,
target
)
recipients
.
delete
(
current_user
)
if
skip_current_user
&&
!
current_user
.
notified_of_own_activity?
recipients
.
uniq
end
end
end
end
user_ids
class
Pipeline
<
Base
end
def
build
(
target
,
current_user
,
action
:)
return
[]
unless
current_user
custom_action
=
case
action
.
to_s
when
'failed'
:failed_pipeline
when
'success'
:success_pipeline
end
notification_setting
=
NotificationRecipientService
.
notification_setting_for_user_project
(
current_user
,
target
.
project
)
return
[]
if
notification_setting
.
mention?
||
notification_setting
.
disabled?
# Build a list of user_ids based on group notification settings
return
[]
if
notification_setting
.
custom?
&&
!
notification_setting
.
event_enabled?
(
custom_action
)
def
select_group_members_ids
(
group
,
project_members
,
global_setting
,
user_ids_global_level_watch
)
uids
=
user_ids_notifiable_on
(
group
,
:watch
)
# Group setting is watch, add to user_ids list if user is not project member
return
[]
if
(
notification_setting
.
watch?
||
notification_setting
.
participating?
)
&&
NotificationSetting
::
EXCLUDED_WATCHER_EVENTS
.
include?
(
custom_action
)
user_ids
=
[]
uids
.
each
do
|
user_id
|
reject_users_without_access
([
current_user
],
target
)
if
project_members
.
exclude?
(
user_id
)
user_ids
<<
user_id
end
end
end
end
# Group setting is global, add to user_ids list if global setting is watch
class
Relabeled
<
Base
global_setting
.
each
do
|
user_id
|
def
build
(
target
,
current_user
,
labels
:)
if
project_members
.
exclude?
(
user_id
)
&&
user_ids_global_level_watch
.
include?
(
user_id
)
recipients
=
add_labels_subscribers
([],
target
,
labels:
labels
)
user_ids
<<
user_id
recipients
=
reject_unsubscribed_users
(
recipients
,
target
)
recipients
=
reject_users_without_access
(
recipients
,
target
)
recipients
.
delete
(
current_user
)
unless
current_user
.
notified_of_own_activity?
recipients
.
uniq
end
end
end
end
user_ids
class
NewNote
<
Base
end
def
build
(
note
)
target
=
note
.
noteable
def
user_ids_with_global_level_watch
(
ids
)
ability
,
subject
=
if
note
.
for_personal_snippet?
settings_with_global_level_of
(
:watch
,
ids
).
pluck
(
:user_id
)
[
:read_personal_snippet
,
note
.
noteable
]
end
else
[
:read_project
,
note
.
project
]
end
def
user_ids_with_global_level_custom
(
ids
,
action
)
mentioned_users
=
note
.
mentioned_users
.
select
{
|
user
|
user
.
can?
(
ability
,
subject
)
}
settings
=
settings_with_global_level_of
(
:custom
,
ids
)
settings
=
settings
.
select
{
|
setting
|
setting
.
event_enabled?
(
action
)
}
settings
.
map
(
&
:user_id
)
end
def
settings_with_global_level_of
(
level
,
ids
)
# Add all users participating in the thread (author, assignee, comment authors)
NotificationSetting
.
where
(
recipients
=
participants
(
target
,
note
.
author
)
||
mentioned_users
user_id:
ids
,
source_type:
nil
,
level:
NotificationSetting
.
levels
[
level
]
)
end
# Reject users which has certain notification level
unless
note
.
for_personal_snippet?
#
# Merge project watchers
# Example:
recipients
=
add_project_watchers
(
recipients
)
# reject_users(users, :watch, project)
#
def
reject_users
(
users
,
level
)
level
=
level
.
to_s
unless
NotificationSetting
.
levels
.
keys
.
include?
(
level
)
# Merge project with custom notification
raise
'Invalid notification level'
recipients
=
add_custom_notifications
(
recipients
,
:new_note
)
end
end
users
=
users
.
to_a
.
compact
.
uniq
# Reject users with Mention notification level, except those mentioned in _this_ note.
recipients
=
reject_mention_users
(
recipients
-
mentioned_users
)
recipients
=
recipients
+
mentioned_users
users
.
reject
do
|
user
|
recipients
=
reject_muted_users
(
recipients
)
setting
=
NotificationRecipientService
.
notification_setting_for_user_project
(
user
,
project
)
setting
.
present?
&&
setting
.
level
==
level
end
end
def
reject_unsubscribed_users
(
recipients
,
target
)
recipients
=
add_subscribed_users
(
recipients
,
note
.
noteable
)
return
recipients
unless
target
.
respond_to?
:subscriptions
recipients
=
reject_unsubscribed_users
(
recipients
,
note
.
noteable
)
recipients
=
reject_users_without_access
(
recipients
,
note
.
noteable
)
recipients
.
reject
do
|
user
|
recipients
.
delete
(
note
.
author
)
unless
note
.
author
.
notified_of_own_activity?
subscription
=
target
.
subscriptions
.
find_by_user_id
(
user
.
id
)
recipients
.
uniq
subscription
&&
!
subscription
.
subscribe
d
en
d
end
end
end
end
def
reject_users_without_access
(
recipients
,
target
)
def
build_recipients
(
*
a
)
recipients
=
recipients
.
select
{
|
u
|
u
.
can?
(
:receive_notifications
)
}
Builder
::
Default
.
new
(
@project
).
build
(
*
a
)
ability
=
case
target
when
Issuable
:"read_
#{
target
.
to_ability_name
}
"
when
Ci
::
Pipeline
:read_build
# We have build trace in pipeline emails
end
return
recipients
unless
ability
recipients
.
select
do
|
user
|
user
.
can?
(
ability
,
target
)
end
end
end
def
add_labels_subscribers
(
recipients
,
target
,
labels:
nil
)
def
build_pipeline_recipients
(
*
a
)
return
recipients
unless
target
.
respond_to?
:labels
Builder
::
Pipeline
.
new
(
@project
).
build
(
*
a
)
end
(
labels
||
target
.
labels
).
each
do
|
label
|
recipients
+=
label
.
subscribers
(
project
)
end
recipients
def
build_relabeled_recipients
(
*
a
)
Builder
::
Relabeled
.
new
(
@project
).
build
(
*
a
)
end
end
# Build event key to search on custom notification level
def
build_new_note_recipients
(
note
)
# Check NotificationSetting::EMAIL_EVENTS
Builder
::
NewNote
.
new
(
@project
).
build
(
note
)
def
build_custom_key
(
action
,
object
)
"
#{
action
}
_
#{
object
.
class
.
model_name
.
name
.
underscore
}
"
.
to_sym
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment