summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Slaughter <pslaughter@gitlab.com>2019-08-17 05:56:49 +0000
committer🤖 GitLab Bot 🤖 <gitlab-bot@gitlab.com>2019-08-17 13:19:53 +0000
commit655dc109fc41fbb68c18f7d5b8eac91a25144f9c (patch)
tree69523782b92ae3b76c4719ec76444484615c5820
parenteded0c78761f7d90afd38668ed46eaacbe9cd4de (diff)
downloadgitlab-ce-12-2-auto-deploy-20190812.tar.gz
Merge branch '50020-fe-allow-email-notifications-to-be-disabled-for-all-users-of-a-group' into 'master'12-2-auto-deploy-20190812
UI for disabling group/project email notifications Closes #50020 See merge request gitlab-org/gitlab-ce!30961 (cherry picked from commit 1068483f7260e5866c7d54f1f09b716dbf463c80) 7699a87e UI for disabling group/project email notification bad143ff Rename canChangeEmailsDisabled to canDisableEmails 38b3f9ec Hide emails_disabled checkbox if disabled in group 5e1fc2f5 Apply suggestion to... bcd5cee9 Addressed review feedback d602fc12 Minor fixes for gitlab.pot 1ee32758 Update permissions documentation 43c87103 Vue file prettified e5ec00ce Apply suggestion to app/views/shared/notifications/_button.html.haml e2de0db9 Disable the subgroup checkbox dbd7fcbd Add checked to emails_disabled group settings
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue15
-rw-r--r--app/helpers/groups_helper.rb5
-rw-r--r--app/helpers/notifications_helper.rb14
-rw-r--r--app/helpers/projects_helper.rb10
-rw-r--r--app/serializers/issuable_sidebar_basic_entity.rb4
-rw-r--r--app/views/groups/_home_panel.html.haml3
-rw-r--r--app/views/groups/settings/_permissions.html.haml10
-rw-r--r--app/views/profiles/notifications/_group_settings.html.haml9
-rw-r--r--app/views/profiles/notifications/_project_settings.html.haml9
-rw-r--r--app/views/projects/_home_panel.html.haml4
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml6
-rw-r--r--app/views/shared/notifications/_button.html.haml15
-rw-r--r--app/views/shared/notifications/_new_button.html.haml13
-rw-r--r--changelogs/unreleased/50020-fe-allow-email-notifications-to-be-disabled-for-all-users-of-a-group.yml5
-rw-r--r--doc/user/group/index.md11
-rw-r--r--doc/user/permissions.md2
-rw-r--r--doc/user/project/settings/index.md6
-rw-r--r--doc/workflow/notifications.md8
-rw-r--r--locale/gitlab.pot15
-rw-r--r--spec/features/groups/show_spec.rb23
-rw-r--r--spec/features/issues/user_toggles_subscription_spec.rb10
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb8
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb12
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb8
-rw-r--r--spec/helpers/groups_helper_spec.rb40
-rw-r--r--spec/helpers/notifications_helper_spec.rb11
-rw-r--r--spec/helpers/projects_helper_spec.rb25
27 files changed, 278 insertions, 23 deletions
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 627d37bac68..a223a8f5b08 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -28,6 +28,11 @@ export default {
type: Object,
required: true,
},
+ canDisableEmails: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
canChangeVisibilityLevel: {
type: Boolean,
required: false,
@@ -104,6 +109,7 @@ export default {
lfsEnabled: true,
requestAccessEnabled: true,
highlightChangesClass: false,
+ emailsDisabled: false,
};
return { ...defaults, ...this.currentSettings };
@@ -341,5 +347,14 @@ export default {
/>
</project-setting-row>
</div>
+ <project-setting-row v-if="canDisableEmails" class="mb-3">
+ <label class="js-emails-disabled">
+ <input :value="emailsDisabled" type="hidden" name="project[emails_disabled]" />
+ <input v-model="emailsDisabled" type="checkbox" /> {{ __('Disable email notifications') }}
+ </label>
+ <span class="form-text text-muted">{{
+ __('This setting will override user notification preferences for all project members.')
+ }}</span>
+ </project-setting-row>
</div>
</template>
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 160f9ac4793..bd26bd01313 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -31,6 +31,11 @@ module GroupsHelper
can?(current_user, :change_share_with_group_lock, group)
end
+ def can_disable_group_emails?(group)
+ Feature.enabled?(:emails_disabled, group, default_enabled: true) &&
+ can?(current_user, :set_emails_disabled, group) && !group.parent&.emails_disabled?
+ end
+
def group_issues_count(state:)
IssuesFinder
.new(current_user, group_id: @group.id, state: state, non_archived: true, include_subgroups: true)
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index 11b9cf22142..5678304ffcf 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -5,7 +5,7 @@ module NotificationsHelper
def notification_icon_class(level)
case level.to_sym
- when :disabled
+ when :disabled, :owner_disabled
'microphone-slash'
when :participating
'volume-up'
@@ -18,6 +18,16 @@ module NotificationsHelper
end
end
+ def notification_icon_level(notification_setting, emails_disabled = false)
+ if emails_disabled
+ 'owner_disabled'
+ elsif notification_setting.global?
+ current_user.global_notification_setting.level
+ else
+ notification_setting.level
+ end
+ end
+
def notification_icon(level, text = nil)
icon("#{notification_icon_class(level)} fw", text: text)
end
@@ -53,6 +63,8 @@ module NotificationsHelper
_('Use your global notification setting')
when :custom
_('You will only receive notifications for the events you choose')
+ when :owner_disabled
+ _('Notifications have been disabled by the project or group owner')
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 71c9c121e48..33bf2d57fae 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -155,6 +155,12 @@ module ProjectsHelper
end
end
+ def can_disable_emails?(project, current_user)
+ return false if project.group&.emails_disabled?
+
+ can?(current_user, :set_emails_disabled, project) && Feature.enabled?(:emails_disabled, project, default_enabled: true)
+ end
+
def last_push_event
current_user&.recent_push(@project)
end
@@ -541,13 +547,15 @@ module ProjectsHelper
snippetsAccessLevel: feature.snippets_access_level,
pagesAccessLevel: feature.pages_access_level,
containerRegistryEnabled: !!project.container_registry_enabled,
- lfsEnabled: !!project.lfs_enabled
+ lfsEnabled: !!project.lfs_enabled,
+ emailsDisabled: project.emails_disabled?
}
end
def project_permissions_panel_data(project)
{
currentSettings: project_permissions_settings(project),
+ canDisableEmails: can_disable_emails?(project, current_user),
canChangeVisibilityLevel: can_change_visibility_level?(project, current_user),
allowedVisibilityOptions: project_allowed_visibility_levels(project),
visibilityHelpPath: help_page_path('public_access/public_access'),
diff --git a/app/serializers/issuable_sidebar_basic_entity.rb b/app/serializers/issuable_sidebar_basic_entity.rb
index 61de3c93337..c02fd024345 100644
--- a/app/serializers/issuable_sidebar_basic_entity.rb
+++ b/app/serializers/issuable_sidebar_basic_entity.rb
@@ -98,6 +98,10 @@ class IssuableSidebarBasicEntity < Grape::Entity
autocomplete_projects_path(project_id: issuable.project.id)
end
+ expose :project_emails_disabled do |issuable|
+ issuable.project.emails_disabled?
+ end
+
private
def current_user
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index 4daf3683eaf..e50d2b8e994 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -1,4 +1,5 @@
- can_create_subgroups = can?(current_user, :create_subgroup, @group)
+- emails_disabled = @group.emails_disabled?
.group-home-panel
.row.mb-3
@@ -21,7 +22,7 @@
.home-panel-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end
- if current_user
.group-buttons
- = render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn'
+ = render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn', emails_disabled: emails_disabled
- if can? current_user, :create_projects, @group
- new_project_label = _("New project")
- new_subgroup_label = _("New subgroup")
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index d3375e00bad..eff28c844c2 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -11,12 +11,18 @@
.form-check
= f.check_box :share_with_group_lock, disabled: !can_change_share_with_group_lock?(@group), class: 'form-check-input'
= f.label :share_with_group_lock, class: 'form-check-label' do
- %span
+ %span.d-block
- group_link = link_to @group.name, group_path(@group)
= s_('GroupSettings|Prevent sharing a project within %{group} with other groups').html_safe % { group: group_link }
- %br
%span.descr.text-muted= share_with_group_lock_help_text(@group)
+ .form-group.append-bottom-default
+ .form-check
+ = f.check_box :emails_disabled, checked: @group.emails_disabled?, disabled: !can_disable_group_emails?(@group), class: 'form-check-input'
+ = f.label :emails_disabled, class: 'form-check-label' do
+ %span.d-block= s_('GroupSettings|Disable email notifications')
+ %span.text-muted= s_('GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects.')
+
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
= render 'groups/settings/lfs', f: f
= render 'groups/settings/project_creation_level', f: f, group: @group
diff --git a/app/views/profiles/notifications/_group_settings.html.haml b/app/views/profiles/notifications/_group_settings.html.haml
index cf17ee44145..1776d260e19 100644
--- a/app/views/profiles/notifications/_group_settings.html.haml
+++ b/app/views/profiles/notifications/_group_settings.html.haml
@@ -1,16 +1,15 @@
+- emails_disabled = group.emails_disabled?
+
.gl-responsive-table-row.notification-list-item
.table-section.section-40
%span.notification.fa.fa-holder.append-right-5
- - if setting.global?
- = notification_icon(current_user.global_notification_setting.level)
- - else
- = notification_icon(setting.level)
+ = notification_icon(notification_icon_level(setting, emails_disabled))
%span.str-truncated
= link_to group.name, group_path(group)
.table-section.section-30.text-right
- = render 'shared/notifications/button', notification_setting: setting
+ = render 'shared/notifications/button', notification_setting: setting, emails_disabled: emails_disabled
.table-section.section-30
= form_for @user.notification_settings.find { |ns| ns.source == group }, url: profile_notifications_group_path(group), method: :put, html: { class: 'update-notifications' } do |f|
diff --git a/app/views/profiles/notifications/_project_settings.html.haml b/app/views/profiles/notifications/_project_settings.html.haml
index 823fec3fab4..63a77b335b6 100644
--- a/app/views/profiles/notifications/_project_settings.html.haml
+++ b/app/views/profiles/notifications/_project_settings.html.haml
@@ -1,12 +1,11 @@
+- emails_disabled = project.emails_disabled?
+
%li.notification-list-item
%span.notification.fa.fa-holder.append-right-5
- - if setting.global?
- = notification_icon(current_user.global_notification_setting.level)
- - else
- = notification_icon(setting.level)
+ = notification_icon(notification_icon_level(setting, emails_disabled))
%span.str-truncated
= link_to_project(project)
.float-right
- = render 'shared/notifications/button', notification_setting: setting
+ = render 'shared/notifications/button', notification_setting: setting, emails_disabled: emails_disabled
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 824fe3c791d..4783b10cf6d 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,6 +1,8 @@
- empty_repo = @project.empty_repo?
- show_auto_devops_callout = show_auto_devops_callout?(@project)
- max_project_topic_length = 15
+- emails_disabled = @project.emails_disabled?
+
.project-home-panel{ class: [("empty-project" if empty_repo), ("js-keep-hidden-on-navigation" if vue_file_list_enabled?)] }
.row.append-bottom-8
.home-panel-title-row.col-md-12.col-lg-6.d-flex
@@ -41,7 +43,7 @@
.project-repo-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end
- if current_user
.d-inline-flex
- = render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn-xs'
+ = render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn-xs', emails_disabled: emails_disabled
.count-buttons.d-inline-flex
= render 'projects/buttons/star'
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index b4f8377c008..825088a58e7 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -137,7 +137,11 @@
.js-sidebar-participants-entry-point
- if signed_in
- .js-sidebar-subscriptions-entry-point
+ - if issuable_sidebar[:project_emails_disabled]
+ .block.js-emails-disabled
+ = notification_description(:owner_disabled)
+ - else
+ .js-sidebar-subscriptions-entry-point
- project_ref = issuable_sidebar[:reference]
.block.project-reference
diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml
index 749aa258af6..b4266937a4e 100644
--- a/app/views/shared/notifications/_button.html.haml
+++ b/app/views/shared/notifications/_button.html.haml
@@ -1,6 +1,15 @@
-- btn_class = local_assigns.fetch(:btn_class, nil)
+- btn_class = local_assigns.fetch(:btn_class, '')
+- emails_disabled = local_assigns.fetch(:emails_disabled, false)
- if notification_setting
+ - if emails_disabled
+ - button_title = notification_description(:owner_disabled)
+ - aria_label = button_title
+ - btn_class << " disabled"
+ - else
+ - button_title = _("Notification setting")
+ - aria_label = _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }
+
.js-notification-dropdown.notification-dropdown.mr-md-2.home-panel-action-button.dropdown.inline
= form_for notification_setting, remote: true, html: { class: "inline notification-form" } do |f|
= hidden_setting_source_input(notification_setting)
@@ -8,14 +17,14 @@
.js-notification-toggle-btns
%div{ class: ("btn-group" if notification_setting.custom?) }
- if notification_setting.custom?
- %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn.text-left#notifications-button{ type: "button", title: _("Notification setting"), class: "#{btn_class}", "aria-label" => _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
+ %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn.text-left#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
%button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= icon('caret-down')
.sr-only Toggle dropdown
- else
- %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: _("Notification setting"), class: "#{btn_class}", "aria-label" => _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
+ %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
.float-left
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
diff --git a/app/views/shared/notifications/_new_button.html.haml b/app/views/shared/notifications/_new_button.html.haml
index 052e6da5bae..3c8cc023848 100644
--- a/app/views/shared/notifications/_new_button.html.haml
+++ b/app/views/shared/notifications/_new_button.html.haml
@@ -1,6 +1,13 @@
-- btn_class = local_assigns.fetch(:btn_class, nil)
+- btn_class = local_assigns.fetch(:btn_class, '')
+- emails_disabled = local_assigns.fetch(:emails_disabled, false)
- if notification_setting
+ - if emails_disabled
+ - button_title = notification_description(:owner_disabled)
+ - btn_class << " disabled"
+ - else
+ - button_title = _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }
+
.js-notification-dropdown.notification-dropdown.home-panel-action-button.prepend-top-default.append-right-8.dropdown.inline
= form_for notification_setting, remote: true, html: { class: "inline notification-form no-label" } do |f|
= hidden_setting_source_input(notification_setting)
@@ -9,14 +16,14 @@
.js-notification-toggle-btns
%div{ class: ("btn-group" if notification_setting.custom?) }
- if notification_setting.custom?
- %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, class: "#{btn_class}", "aria-label" => _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, data: { container: "body", placement: 'top', toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
+ %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => button_title, data: { container: "body", placement: 'top', toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= notification_setting_icon(notification_setting)
%span.js-notification-loading.fa.hidden
%button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" }, class: "#{btn_class}" }
= sprite_icon("arrow-down", css_class: "icon mr-0")
.sr-only Toggle dropdown
- else
- %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, class: "#{btn_class}", "aria-label" => _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, data: { container: "body", placement: 'top', toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
+ %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => button_title, data: { container: "body", placement: 'top', toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= notification_setting_icon(notification_setting)
%span.js-notification-loading.fa.hidden
= sprite_icon("arrow-down", css_class: "icon")
diff --git a/changelogs/unreleased/50020-fe-allow-email-notifications-to-be-disabled-for-all-users-of-a-group.yml b/changelogs/unreleased/50020-fe-allow-email-notifications-to-be-disabled-for-all-users-of-a-group.yml
new file mode 100644
index 00000000000..a5fe7b1d18e
--- /dev/null
+++ b/changelogs/unreleased/50020-fe-allow-email-notifications-to-be-disabled-for-all-users-of-a-group.yml
@@ -0,0 +1,5 @@
+---
+title: UI for disabling group/project email notifications
+merge_request: 30961
+author: Dustin Spicuzza
+type: added
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 43fd0bfd45a..a037a73c9d7 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -379,6 +379,17 @@ To enable this feature, navigate to the group settings page, expand the
Define project templates at a group level by setting a group as the template source.
[Learn more about group-level project templates](custom_project_templates.md).
+#### Disabling email notifications
+
+You can disable all email notifications related to the group, which also includes
+it's subgroups and projects.
+
+To enable this feature:
+
+1. Navigate to the group's **Settings > General** page.
+1. Expand the **Permissions, LFS, 2FA** section, and select **Disable email notifications**.
+1. Click **Save changes**.
+
### Advanced settings
- **Projects**: View all projects within that group, add members to each project,
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index d92435ef724..6497f52a486 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -125,6 +125,7 @@ The following table depicts the various user permission levels in a project.
| Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ |
| Delete issues | | | | | ✓ |
+| Disable notification emails | | | | | ✓ |
| Force push to protected branches (*4*) | | | | | |
| Remove protected branches (*4*) | | | | | |
@@ -219,6 +220,7 @@ group.
| Remove group | | | | | ✓ |
| Delete group epic **(ULTIMATE)** | | | | | ✓ |
| View group Audit Events | | | | | ✓ |
+| Disable notification emails | | | | | ✓ |
- (1): Groups can be set to [allow either Owners or Owners and
Maintainers to create subgroups](group/subgroups/index.md#creating-a-subgroup)
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 17ec9ecb5d1..4e3db95b6d6 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -32,6 +32,12 @@ links will be missing from the sidebar UI.
You can still access them with direct links if you can access Merge Requests. This is deliberate, if you can see
Issues or Merge Requests, both of which use Labels and Milestones, then you shouldn't be denied access to Labels and Milestones pages.
+#### Disabling email notifications
+
+You can disable all email notifications related to the project by selecting the
+**Disable email notifications** checkbox. Only the project owner is allowed to change
+this setting.
+
### Issue settings
Add an [issue description template](../description_templates.md#description-templates) to your project, so that every new issue will start with a custom template.
diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md
index d82f7c6fdc7..ccb8844aea3 100644
--- a/doc/workflow/notifications.md
+++ b/doc/workflow/notifications.md
@@ -51,6 +51,10 @@ Organization like this is suitable for users that belong to different groups but
same need for being notified for every group they are member of.
These settings can be configured on group page under the name of the group. It will be the dropdown with the bell icon. They can also be configured on the user profile notifications dropdown.
+The group owner can disable email notifications for a group, which also includes
+it's subgroups and projects. If this is the case, you will not receive any corresponding notifications,
+and the notification button will be disabled with an explanatory tooltip.
+
### Project Settings
![notification settings](img/notification_project_settings.png)
@@ -60,6 +64,10 @@ other setting.
This is suitable for users that have different needs for notifications per project basis.
These settings can be configured on project page under the name of the project. It will be the dropdown with the bell icon. They can also be configured on the user profile notifications dropdown.
+The project owner (or it's group owner) can disable email notifications for the project.
+If this is the case, you will not receive any corresponding notifications, and the notification
+button will be disabled with an explanatory tooltip.
+
## Notification events
Below is the table of events users can be notified of:
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 6cafa9c3947..df7fd3de499 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3932,6 +3932,9 @@ msgstr ""
msgid "Disable"
msgstr ""
+msgid "Disable email notifications"
+msgstr ""
+
msgid "Disable for this project"
msgstr ""
@@ -5440,6 +5443,9 @@ msgstr ""
msgid "GroupSettings|Default to Auto DevOps pipeline for all projects within this group"
msgstr ""
+msgid "GroupSettings|Disable email notifications"
+msgstr ""
+
msgid "GroupSettings|Learn more about badges."
msgstr ""
@@ -5467,6 +5473,9 @@ msgstr ""
msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
msgstr ""
+msgid "GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects."
+msgstr ""
+
msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
msgstr ""
@@ -7548,6 +7557,9 @@ msgstr ""
msgid "Notifications"
msgstr ""
+msgid "Notifications have been disabled by the project or group owner"
+msgstr ""
+
msgid "Notifications off"
msgstr ""
@@ -11608,6 +11620,9 @@ msgstr ""
msgid "This setting can be overridden in each project."
msgstr ""
+msgid "This setting will override user notification preferences for all project members."
+msgstr ""
+
msgid "This setting will update the hostname that is used to generate private commit emails. %{learn_more}"
msgstr ""
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index 942a9889488..bcaed2a5f18 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -161,4 +161,27 @@ describe 'Group show page' do
expect(find('.group-row:nth-child(3) .namespace-title > a')).to have_content(project3.title)
end
end
+
+ context 'notification button', :js do
+ let(:maintainer) { create(:user) }
+ let!(:project) { create(:project, namespace: group) }
+
+ before do
+ group.add_maintainer(maintainer)
+ sign_in(maintainer)
+ end
+
+ it 'is enabled by default' do
+ visit path
+
+ expect(page).to have_selector('.notifications-btn:not(.disabled)', visible: true)
+ end
+
+ it 'is disabled if emails are disabled' do
+ group.update_attribute(:emails_disabled, true)
+ visit path
+
+ expect(page).to have_selector('.notifications-btn.disabled', visible: true)
+ end
+ end
end
diff --git a/spec/features/issues/user_toggles_subscription_spec.rb b/spec/features/issues/user_toggles_subscription_spec.rb
index 7a721adc8dd..165d41950da 100644
--- a/spec/features/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/issues/user_toggles_subscription_spec.rb
@@ -27,4 +27,14 @@ describe "User toggles subscription", :js do
# Check we're unsubscribed.
expect(subscription_button).to have_css("button:not(.is-checked)")
end
+
+ context 'when project emails are disabled' do
+ let(:project) { create(:project_empty_repo, :public, emails_disabled: true) }
+
+ it 'is disabled' do
+ expect(page).to have_content('Notifications have been disabled by the project or group owner')
+ expect(page).to have_selector('.js-emails-disabled', visible: true)
+ expect(page).not_to have_selector('.js-issuable-subscribe-button')
+ end
+ end
end
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index 1472cc882a7..d788c0574e2 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -20,4 +20,12 @@ describe 'User visits the notifications tab', :js do
expect(page).to have_selector('#notifications-button', text: 'On mention')
end
+
+ context 'when project emails are disabled' do
+ let(:project) { create(:project, emails_disabled: true) }
+
+ it 'notification button is disabled' do
+ expect(page).to have_selector('.notifications-btn.disabled', visible: true)
+ end
+ end
end
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index 46fd676954d..0e757e647a0 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -59,6 +59,12 @@ describe 'Projects > Settings > Visibility settings', :js do
end
end
end
+
+ context 'disable email notifications' do
+ it 'is visible' do
+ expect(page).to have_selector('.js-emails-disabled', visible: true)
+ end
+ end
end
context 'as maintainer' do
@@ -76,5 +82,11 @@ describe 'Projects > Settings > Visibility settings', :js do
expect(visibility_select_container).to have_selector 'select[name="project[visibility_level]"]:disabled'
expect(visibility_select_container).to have_content 'The project can be accessed by anyone, regardless of authentication.'
end
+
+ context 'disable email notifications' do
+ it 'is not available' do
+ expect(page).not_to have_selector('.js-emails-disabled', visible: true)
+ end
+ end
end
end
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index 5e9c98428cf..851a09cf28a 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -65,4 +65,12 @@ describe 'Projects > Show > User manages notifications', :js do
end
end
end
+
+ context 'when project emails are disabled' do
+ let(:project) { create(:project, :public, :repository, emails_disabled: true) }
+
+ it 'is disabled' do
+ expect(page).to have_selector('.notifications-btn.disabled', visible: true)
+ end
+ end
end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 037b16c90ed..98719697cea 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -262,4 +262,44 @@ describe GroupsHelper do
expect(parent_group_options(group2)).to eq([{ id: group.id, text: group.human_name }].to_json)
end
end
+
+ describe '#can_disable_group_emails?' do
+ let(:current_user) { create(:user) }
+ let(:group) { create(:group, name: 'group') }
+ let(:subgroup) { create(:group, name: 'subgroup', parent: group) }
+
+ before do
+ allow(helper).to receive(:current_user) { current_user }
+ end
+
+ it 'returns true for the group owner' do
+ allow(helper).to receive(:can?).with(current_user, :set_emails_disabled, group) { true }
+
+ expect(helper.can_disable_group_emails?(group)).to be_truthy
+ end
+
+ it 'returns false for anyone else' do
+ allow(helper).to receive(:can?).with(current_user, :set_emails_disabled, group) { false }
+
+ expect(helper.can_disable_group_emails?(group)).to be_falsey
+ end
+
+ context 'when subgroups' do
+ before do
+ allow(helper).to receive(:can?).with(current_user, :set_emails_disabled, subgroup) { true }
+ end
+
+ it 'returns false if parent group is disabling emails' do
+ allow(group).to receive(:emails_disabled?).and_return(true)
+
+ expect(helper.can_disable_group_emails?(subgroup)).to be_falsey
+ end
+
+ it 'returns true if parent group is not disabling emails' do
+ allow(group).to receive(:emails_disabled?).and_return(false)
+
+ expect(helper.can_disable_group_emails?(subgroup)).to be_truthy
+ end
+ end
+ end
end
diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb
index 9ecaabc04ed..5717b15d656 100644
--- a/spec/helpers/notifications_helper_spec.rb
+++ b/spec/helpers/notifications_helper_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe NotificationsHelper do
describe 'notification_icon' do
it { expect(notification_icon(:disabled)).to match('class="fa fa-microphone-slash fa-fw"') }
+ it { expect(notification_icon(:owner_disabled)).to match('class="fa fa-microphone-slash fa-fw"') }
it { expect(notification_icon(:participating)).to match('class="fa fa-volume-up fa-fw"') }
it { expect(notification_icon(:mention)).to match('class="fa fa-at fa-fw"') }
it { expect(notification_icon(:global)).to match('class="fa fa-globe fa-fw"') }
@@ -19,4 +20,14 @@ describe NotificationsHelper do
it { expect(notification_event_name(:success_pipeline)).to match('Successful pipeline') }
it { expect(notification_event_name(:failed_pipeline)).to match('Failed pipeline') }
end
+
+ describe '#notification_icon_level' do
+ let(:user) { create(:user) }
+ let(:global_setting) { user.global_notification_setting }
+ let(:notification_setting) { create(:notification_setting, level: :watch) }
+
+ it { expect(notification_icon_level(notification_setting, true)).to eq 'owner_disabled' }
+ it { expect(notification_icon_level(notification_setting)).to eq 'watch' }
+ it { expect(notification_icon_level(global_setting)).to eq 'participating' }
+ end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 3716879c458..a70bfc2adc7 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -107,6 +107,30 @@ describe ProjectsHelper do
end
end
+ describe '#can_disable_emails?' do
+ let(:project) { create(:project) }
+ let(:user) { create(:project_member, :maintainer, user: create(:user), project: project).user }
+
+ it 'returns true for the project owner' do
+ allow(helper).to receive(:can?).with(project.owner, :set_emails_disabled, project) { true }
+
+ expect(helper.can_disable_emails?(project, project.owner)).to be_truthy
+ end
+
+ it 'returns false for anyone else' do
+ allow(helper).to receive(:can?).with(user, :set_emails_disabled, project) { false }
+
+ expect(helper.can_disable_emails?(project, user)).to be_falsey
+ end
+
+ it 'returns false if group emails disabled' do
+ project = create(:project, group: create(:group))
+ allow(project.group).to receive(:emails_disabled?).and_return(true)
+
+ expect(helper.can_disable_emails?(project, project.owner)).to be_falsey
+ end
+ end
+
describe "readme_cache_key" do
let(:project) { create(:project, :repository) }
@@ -477,6 +501,7 @@ describe ProjectsHelper do
it 'returns the command to push to create project over SSH' do
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'ssh' }
+ allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return('git@localhost:')
expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream git@localhost:john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
end