Skip to content
Snippets Groups Projects
Commit 655dc109 authored by Paul Slaughter's avatar Paul Slaughter Committed by 🤖 GitLab Bot 🤖
Browse files

Merge branch...

Merge branch '50020-fe-allow-email-notifications-to-be-disabled-for-all-users-of-a-group' into 'master'

UI for disabling group/project email notifications

Closes #50020

See merge request gitlab-org/gitlab-ce!30961

(cherry picked from commit 1068483f)

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
parent eded0c78
No related branches found
No related tags found
No related merge requests found
Showing
with 164 additions and 23 deletions
Loading
Loading
@@ -28,6 +28,11 @@ export default {
type: Object,
required: true,
},
canDisableEmails: {
type: Boolean,
required: false,
default: false,
},
canChangeVisibilityLevel: {
type: Boolean,
required: false,
Loading
Loading
@@ -104,6 +109,7 @@ export default {
lfsEnabled: true,
requestAccessEnabled: true,
highlightChangesClass: false,
emailsDisabled: false,
};
 
return { ...defaults, ...this.currentSettings };
Loading
Loading
@@ -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>
Loading
Loading
@@ -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)
Loading
Loading
Loading
Loading
@@ -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'
Loading
Loading
@@ -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
Loading
Loading
@@ -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
 
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
@@ -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'),
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
- can_create_subgroups = can?(current_user, :create_subgroup, @group)
- emails_disabled = @group.emails_disabled?
 
.group-home-panel
.row.mb-3
Loading
Loading
@@ -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")
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
- 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|
Loading
Loading
- 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
- 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
Loading
Loading
@@ -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'
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
- 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)
Loading
Loading
@@ -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)
Loading
Loading
- 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)
Loading
Loading
@@ -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")
Loading
Loading
---
title: UI for disabling group/project email notifications
merge_request: 30961
author: Dustin Spicuzza
type: added
Loading
Loading
@@ -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,
Loading
Loading
Loading
Loading
@@ -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*) | | | | | |
 
Loading
Loading
@@ -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)
Loading
Loading
Loading
Loading
@@ -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.
Loading
Loading
Loading
Loading
@@ -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)
Loading
Loading
@@ -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:
Loading
Loading
Loading
Loading
@@ -3932,6 +3932,9 @@ msgstr ""
msgid "Disable"
msgstr ""
 
msgid "Disable email notifications"
msgstr ""
msgid "Disable for this project"
msgstr ""
 
Loading
Loading
@@ -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 ""
 
Loading
Loading
@@ -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 ""
 
Loading
Loading
@@ -7548,6 +7557,9 @@ msgstr ""
msgid "Notifications"
msgstr ""
 
msgid "Notifications have been disabled by the project or group owner"
msgstr ""
msgid "Notifications off"
msgstr ""
 
Loading
Loading
@@ -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 ""
 
Loading
Loading
Loading
Loading
@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment