Commit 1fb499de authored by Luke Bennett's avatar Luke Bennett

Add leave link to access_granted email

Allows users to leave a project/group
that they have been added to.
Add function to leave a namespace by url param
If the `leave` param is present on a project/group show page,
click the leave link.
parent 7be2796e
import Flash from '~/flash';
import { __, sprintf } from '~/locale';
import { getParameterByName } from '~/lib/utils/common_utils';
const PARAMETER_NAME = 'leave';
const LEAVE_LINK_SELECTOR = '.js-leave-link';
export default function leaveByUrl(namespaceType) {
if (!namespaceType) throw new Error('namespaceType not provided');
const param = getParameterByName(PARAMETER_NAME);
if (!param) return;
const leaveLink = document.querySelector(LEAVE_LINK_SELECTOR);
if (leaveLink) {
leaveLink.click();
} else {
Flash(
sprintf(__('You do not have permission to leave this %{namespaceType}.'), { namespaceType }),
);
}
}
import leaveByUrl from '~/namespaces/leave_by_url';
import initGroupDetails from '../shared/group_details'; import initGroupDetails from '../shared/group_details';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
leaveByUrl('group');
initGroupDetails(); initGroupDetails();
}); });
...@@ -9,6 +9,7 @@ import Activities from '~/activities'; ...@@ -9,6 +9,7 @@ import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils'; import { ajaxGet } from '~/lib/utils/common_utils';
import GpgBadges from '~/gpg_badges'; import GpgBadges from '~/gpg_badges';
import initReadMore from '~/read_more'; import initReadMore from '~/read_more';
import leaveByUrl from '~/namespaces/leave_by_url';
import Star from '../../../star'; import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown'; import notificationsDropdown from '../../../notifications_dropdown';
...@@ -44,4 +45,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -44,4 +45,5 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
GpgBadges.fetch(); GpgBadges.fetch();
leaveByUrl('project');
}); });
- link_end = '</a>'.html_safe
- source_type = member_source.model_name.singular
- leave_link = polymorphic_url([member_source], leave: 1)
- source_link = link_to(member_source.human_name, member_source.web_url, target: '_blank', rel: 'noopener noreferrer')
%p %p
You have been granted #{member.human_access} access to the = _('You have been granted %{access_level} access to the %{source_link} %{source_type}.').html_safe % { access_level: member.human_access, source_link: source_link, source_type: source_type }
#{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}. %p
- leave_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: leave_link }
= _('If this was a mistake you can %{leave_link_start}leave the %{source_type}%{link_end}.').html_safe % { source_type: source_type, leave_link_start: leave_link_start, link_end: link_end }
You have been granted <%= member.human_access %> access to the <%= member_source.human_name %> <%= member_source.model_name.singular %>. <% source_type = member_source.model_name.singular %>
<%= _('You have been granted %{access_level} access to the %{source_name} %{source_type}.') % { access_level: member.human_access, source_name: member_source.human_name, source_type: source_type } %>
<%= member_source.web_url %> <%= member_source.web_url %>
<%= _('If this was a mistake you can leave the %{source_type}.') % { source_type: source_type } %>
<%= polymorphic_url([member_source], leave: 1) %>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
= link_to link_text, polymorphic_path([:leave, source, :members]), = link_to link_text, polymorphic_path([:leave, source, :members]),
method: :delete, method: :delete,
data: { confirm: leave_confirmation_message(source) }, data: { confirm: leave_confirmation_message(source) },
class: 'access-request-link' class: 'access-request-link js-leave-link'
- elsif requester = source.requesters.find_by(user_id: current_user.id) # rubocop: disable CodeReuse/ActiveRecord - elsif requester = source.requesters.find_by(user_id: current_user.id) # rubocop: disable CodeReuse/ActiveRecord
= link_to _('Withdraw Access Request'), polymorphic_path([:leave, source, :members]), = link_to _('Withdraw Access Request'), polymorphic_path([:leave, source, :members]),
method: :delete, method: :delete,
......
---
title: Leave project/group from access granted email
merge_request: 27892
author:
type: added
...@@ -4854,6 +4854,12 @@ msgstr "" ...@@ -4854,6 +4854,12 @@ msgstr ""
msgid "If enabled, access to projects will be validated on an external service using their classification label." msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr "" msgstr ""
msgid "If this was a mistake you can %{leave_link_start}leave the %{source_type}%{link_end}."
msgstr ""
msgid "If this was a mistake you can leave the %{source_type}."
msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>." msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr "" msgstr ""
...@@ -10912,6 +10918,9 @@ msgstr "" ...@@ -10912,6 +10918,9 @@ msgstr ""
msgid "You do not have any subscriptions yet" msgid "You do not have any subscriptions yet"
msgstr "" msgstr ""
msgid "You do not have permission to leave this %{namespaceType}."
msgstr ""
msgid "You don't have any applications" msgid "You don't have any applications"
msgstr "" msgstr ""
...@@ -10921,6 +10930,12 @@ msgstr "" ...@@ -10921,6 +10930,12 @@ msgstr ""
msgid "You don't have any deployments right now." msgid "You don't have any deployments right now."
msgstr "" msgstr ""
msgid "You have been granted %{access_level} access to the %{source_link} %{source_type}."
msgstr ""
msgid "You have been granted %{access_level} access to the %{source_name} %{source_type}."
msgstr ""
msgid "You have been granted %{member_human_access} access to %{label}." msgid "You have been granted %{member_human_access} access to %{label}."
msgstr "" msgstr ""
......
...@@ -21,6 +21,20 @@ describe 'Groups > Members > Leave group' do ...@@ -21,6 +21,20 @@ describe 'Groups > Members > Leave group' do
expect(group.users).not_to include(user) expect(group.users).not_to include(user)
end end
it 'guest leaves the group by url param', :js do
group.add_guest(user)
group.add_owner(other_user)
visit group_path(group, leave: 1)
page.accept_confirm
expect(find('.flash-notice')).to have_content "You left the \"#{group.full_name}\" group"
expect(page).to have_content left_group_message(group)
expect(current_path).to eq(dashboard_groups_path)
expect(group.users).not_to include(user)
end
it 'guest leaves the group as last member' do it 'guest leaves the group as last member' do
group.add_guest(user) group.add_guest(user)
...@@ -32,7 +46,7 @@ describe 'Groups > Members > Leave group' do ...@@ -32,7 +46,7 @@ describe 'Groups > Members > Leave group' do
expect(group.users).not_to include(user) expect(group.users).not_to include(user)
end end
it 'owner leaves the group if they is not the last owner' do it 'owner leaves the group if they are not the last owner' do
group.add_owner(user) group.add_owner(user)
group.add_owner(other_user) group.add_owner(other_user)
...@@ -44,7 +58,7 @@ describe 'Groups > Members > Leave group' do ...@@ -44,7 +58,7 @@ describe 'Groups > Members > Leave group' do
expect(group.users).not_to include(user) expect(group.users).not_to include(user)
end end
it 'owner can not leave the group if they is a last owner' do it 'owner can not leave the group if they are the last owner' do
group.add_owner(user) group.add_owner(user)
visit group_path(group) visit group_path(group)
...@@ -56,6 +70,14 @@ describe 'Groups > Members > Leave group' do ...@@ -56,6 +70,14 @@ describe 'Groups > Members > Leave group' do
expect(find(:css, '.project-members-page li', text: user.name)).not_to have_selector(:css, 'a.btn-remove') expect(find(:css, '.project-members-page li', text: user.name)).not_to have_selector(:css, 'a.btn-remove')
end end
it 'owner can not leave the group by url param if they are the last owner', :js do
group.add_owner(user)
visit group_path(group, leave: 1)
expect(find('.flash-alert')).to have_content 'You do not have permission to leave this group'
end
def left_group_message(group) def left_group_message(group)
"You left the \"#{group.name}\"" "You left the \"#{group.name}\""
end end
......
...@@ -8,10 +8,17 @@ describe 'Projects > Members > Group member cannot leave group project' do ...@@ -8,10 +8,17 @@ describe 'Projects > Members > Group member cannot leave group project' do
before do before do
group.add_developer(user) group.add_developer(user)
sign_in(user) sign_in(user)
visit project_path(project)
end end
it 'user does not see a "Leave project" link' do it 'user does not see a "Leave project" link' do
visit project_path(project)
expect(page).not_to have_content 'Leave project' expect(page).not_to have_content 'Leave project'
end end
it 'renders a flash message if attempting to leave by url', :js do
visit project_path(project, leave: 1)
expect(find('.flash-alert')).to have_content 'You do not have permission to leave this project'
end
end end
...@@ -7,13 +7,24 @@ describe 'Projects > Members > Member leaves project' do ...@@ -7,13 +7,24 @@ describe 'Projects > Members > Member leaves project' do
before do before do
project.add_developer(user) project.add_developer(user)
sign_in(user) sign_in(user)
visit project_path(project)
end end
it 'user leaves project' do it 'user leaves project' do
visit project_path(project)
click_link 'Leave project' click_link 'Leave project'
expect(current_path).to eq(dashboard_projects_path) expect(current_path).to eq(dashboard_projects_path)
expect(project.users.exists?(user.id)).to be_falsey expect(project.users.exists?(user.id)).to be_falsey
end end
it 'user leaves project by url param', :js do
visit project_path(project, leave: 1)
page.accept_confirm
expect(find('.flash-notice')).to have_content "You left the \"#{project.full_name}\" project"
expect(current_path).to eq(dashboard_projects_path)
expect(project.users.exists?(user.id)).to be_falsey
end
end end
...@@ -701,6 +701,8 @@ describe Notify do ...@@ -701,6 +701,8 @@ describe Notify do
is_expected.to have_body_text project.full_name is_expected.to have_body_text project.full_name
is_expected.to have_body_text project.web_url is_expected.to have_body_text project.web_url
is_expected.to have_body_text project_member.human_access is_expected.to have_body_text project_member.human_access
is_expected.to have_body_text 'leave the project'
is_expected.to have_body_text project_url(project, leave: 1)
end end
end end
...@@ -1144,6 +1146,8 @@ describe Notify do ...@@ -1144,6 +1146,8 @@ describe Notify do
is_expected.to have_body_text group.name is_expected.to have_body_text group.name
is_expected.to have_body_text group.web_url is_expected.to have_body_text group.web_url
is_expected.to have_body_text group_member.human_access is_expected.to have_body_text group_member.human_access
is_expected.to have_body_text 'leave the group'
is_expected.to have_body_text group_url(group, leave: 1)
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