Commit ad2b9baa authored by Fabio Huser's avatar Fabio Huser

feat(groups): implement group mentionings protections

parent 14a16e2e
......@@ -167,12 +167,13 @@ class GfmAutoComplete {
alias: 'users',
displayTpl(value) {
let tmpl = GfmAutoComplete.Loading.template;
const { avatarTag, username, title } = value;
const { avatarTag, username, title, icon } = value;
if (username != null) {
tmpl = GfmAutoComplete.Members.templateFunction({
avatarTag,
username,
title,
icon,
});
}
return tmpl;
......@@ -192,7 +193,7 @@ class GfmAutoComplete {
return m;
}
title = m.name;
if (m.count) {
if (m.count && !m.mentioningsDisabled) {
title += ` (${m.count})`;
}
......@@ -203,12 +204,14 @@ class GfmAutoComplete {
const rectAvatarClass = m.type === GROUP_TYPE ? 'rect-avatar' : '';
const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar ${rectAvatarClass} avatar-inline center s26"/>`;
const txtAvatar = `<div class="avatar ${rectAvatarClass} center avatar-inline s26">${autoCompleteAvatar}</div>`;
const avatarIcon = m.mentioningsDisabled ? '<i class="fa fa-bell-slash"></i>' : '';
return {
username: m.username,
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
title: sanitize(title),
search: sanitize(`${m.username} ${m.name}`),
icon: avatarIcon,
};
});
},
......@@ -624,8 +627,8 @@ GfmAutoComplete.Emoji = {
};
// Team Members
GfmAutoComplete.Members = {
templateFunction({ avatarTag, username, title }) {
return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small></li>`;
templateFunction({ avatarTag, username, title, icon }) {
return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small> ${icon}</li>`;
},
};
GfmAutoComplete.Labels = {
......
......@@ -181,6 +181,7 @@ class GroupsController < Groups::ApplicationController
:avatar,
:description,
:emails_disabled,
:mentionings_disabled,
:lfs_enabled,
:name,
:path,
......
......@@ -55,7 +55,8 @@ module Users
username: group.full_path,
name: group.full_name,
avatar_url: group.avatar_url,
count: group_counts.fetch(group.id, 0)
count: group_counts.fetch(group.id, 0),
mentioningsDisabled: group.mentionings_disabled
}
end
end
......
......@@ -23,6 +23,13 @@
%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.')
.form-group.append-bottom-default
.form-check
= f.check_box :mentionings_disabled, checked: @group.mentionings_disabled?, class: 'form-check-input'
= f.label :mentionings_disabled, class: 'form-check-label' do
%span.d-block= s_('GroupSettings|Disable group mentionings')
%span.text-muted= s_('GroupSettings|This setting will prevent group members from getting notified if the group is mentioned.')
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
= render 'groups/settings/lfs', f: f
......
# frozen_string_literal: true
class AddMentioningsDisabledToNamespaces < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :namespaces, :mentionings_disabled, :boolean
end
end
......@@ -2603,6 +2603,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
t.boolean "emails_disabled"
t.integer "max_pages_size"
t.integer "max_artifacts_size"
t.boolean "mentionings_disabled"
t.index ["created_at"], name: "index_namespaces_on_created_at"
t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)"
t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id"
......
......@@ -431,6 +431,23 @@ To enable this feature:
1. Expand the **Permissions, LFS, 2FA** section, and select **Disable email notifications**.
1. Click **Save changes**.
#### Disabling group mentionings
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/21301) in GitLab 12.6.
You can prevent users from being added to a conversation and getting notified when
one mentions a group in which those users are members.
Groups with disabled mentionings are visualised accordingly in the auto-completion menu.
This is particularly helpful, if a group has large amount of users.
To enable this feature:
1. Navigate to the group's **Settings > General** page.
1. Expand the **Permissions, LFS, 2FA** section, and select **Disable group mentionings**.
1. Click **Save changes**.
### Advanced settings
- **Projects**: View all projects within that group, add members to each project,
......
......@@ -97,7 +97,9 @@ module Banzai
def find_users_for_groups(ids)
return [] if ids.empty?
User.joins(:group_members).where(members: { source_id: ids }).to_a
User.joins(:group_members).where(members: {
source_id: Namespace.where(id: ids, mentionings_disabled: [false, nil]).select(:id)
}).to_a
end
def find_users_for_projects(ids)
......
......@@ -8860,6 +8860,9 @@ msgstr ""
msgid "GroupSettings|Disable email notifications"
msgstr ""
msgid "GroupSettings|Disable group mentionings"
msgstr ""
msgid "GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility."
msgstr ""
......@@ -8908,6 +8911,9 @@ msgstr ""
msgid "GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects."
msgstr ""
msgid "GroupSettings|This setting will prevent group members from getting notified if the group is mentioned."
msgstr ""
msgid "GroupSettings|Transfer group"
msgstr ""
......
......@@ -298,6 +298,41 @@ describe('GfmAutoComplete', () => {
});
});
describe('Members.templateFunction', () => {
it('should return html with avatarTag and username', () => {
expect(
GfmAutoComplete.Members.templateFunction({
avatarTag: 'IMG',
username: 'my-group',
title: '',
icon: '',
}),
).toBe('<li>IMG my-group <small></small> </li>');
});
it('should add icon if icon is set', () => {
expect(
GfmAutoComplete.Members.templateFunction({
avatarTag: 'IMG',
username: 'my-group',
title: '',
icon: '<i class="icon"/>',
}),
).toBe('<li>IMG my-group <small></small> <i class="icon"/></li>');
});
it('should add escaped title if title is set', () => {
expect(
GfmAutoComplete.Members.templateFunction({
avatarTag: 'IMG',
username: 'my-group',
title: 'MyGroup+',
icon: '<i class="icon"/>',
}),
).toBe('<li>IMG my-group <small>MyGroup+</small> <i class="icon"/></li>');
});
});
describe('labels', () => {
const dataSources = {
labels: `${TEST_HOST}/autocomplete_sources/labels`,
......
......@@ -19,15 +19,23 @@ describe Banzai::ReferenceParser::UserParser do
link['data-group'] = project.group.id.to_s
end
it 'returns the users of the group' do
create(:group_member, group: group, user: user)
expect(subject.referenced_by([link])).to eq([user])
end
it 'returns an empty Array when the group has no users' do
expect(subject.referenced_by([link])).to eq([])
end
context 'when group has members' do
let!(:group_member) { create(:group_member, group: group, user: user) }
it 'returns the users of the group' do
expect(subject.referenced_by([link])).to eq([user])
end
it 'returns an empty Array when the group has mentionings disabled' do
group.update!(mentionings_disabled: true)
expect(subject.referenced_by([link])).to eq([])
end
end
end
context 'using a non-existing group ID' do
......
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