Commit a3ef8367 authored by Doug Stull's avatar Doug Stull Committed by Bob Van Landuyt

Migrates block user to shared confirm modal

- ease of upkeep to use shared component.
parent d1eae71c
...@@ -5,12 +5,12 @@ import ModalManager from './components/user_modal_manager.vue'; ...@@ -5,12 +5,12 @@ import ModalManager from './components/user_modal_manager.vue';
import DeleteUserModal from './components/delete_user_modal.vue'; import DeleteUserModal from './components/delete_user_modal.vue';
import UserOperationConfirmationModal from './components/user_operation_confirmation_modal.vue'; import UserOperationConfirmationModal from './components/user_operation_confirmation_modal.vue';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import initConfirmModal from '~/confirm_modal';
const MODAL_TEXTS_CONTAINER_SELECTOR = '#modal-texts'; const MODAL_TEXTS_CONTAINER_SELECTOR = '#modal-texts';
const MODAL_MANAGER_SELECTOR = '#user-modal'; const MODAL_MANAGER_SELECTOR = '#user-modal';
const ACTION_MODALS = { const ACTION_MODALS = {
deactivate: UserOperationConfirmationModal, deactivate: UserOperationConfirmationModal,
block: UserOperationConfirmationModal,
delete: DeleteUserModal, delete: DeleteUserModal,
'delete-with-contributions': DeleteUserModal, 'delete-with-contributions': DeleteUserModal,
}; };
...@@ -62,4 +62,6 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -62,4 +62,6 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
}, },
}); });
initConfirmModal();
}); });
<script> <script>
import { GlModal } from '@gitlab/ui'; import { GlModal, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
...@@ -7,6 +7,9 @@ export default { ...@@ -7,6 +7,9 @@ export default {
components: { components: {
GlModal, GlModal,
}, },
directives: {
SafeHtml,
},
props: { props: {
selector: { selector: {
type: String, type: String,
...@@ -71,7 +74,8 @@ export default { ...@@ -71,7 +74,8 @@ export default {
--> -->
<input type="hidden" name="_method" :value="method" /> <input type="hidden" name="_method" :value="method" />
<input type="hidden" name="authenticity_token" :value="$options.csrf.token" /> <input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
<div>{{ modalAttributes.message }}</div> <div v-if="modalAttributes.messageHtml" v-safe-html="modalAttributes.messageHtml"></div>
<div v-else>{{ modalAttributes.message }}</div>
</form> </form>
</gl-modal> </gl-modal>
</template> </template>
...@@ -110,6 +110,32 @@ module UsersHelper ...@@ -110,6 +110,32 @@ module UsersHelper
!user.confirmed? !user.confirmed?
end end
def user_block_data(user, message)
{
path: block_admin_user_path(user),
method: 'put',
modal_attributes: {
title: s_('AdminUsers|Block user %{username}?') % { username: sanitize_name(user.name) },
messageHtml: message,
okVariant: 'warning',
okTitle: s_('AdminUsers|Block')
}.to_json
}
end
def user_block_effects
header = tag.p s_('AdminUsers|Blocking user has the following effects:')
list = tag.ul do
concat tag.li s_('AdminUsers|User will not be able to login')
concat tag.li s_('AdminUsers|User will not be able to access git repositories')
concat tag.li s_('AdminUsers|Personal projects will be left')
concat tag.li s_('AdminUsers|Owned groups will be left')
end
header + list
end
private private
def blocked_user_badge(user) def blocked_user_badge(user)
......
...@@ -2,10 +2,7 @@ ...@@ -2,10 +2,7 @@
.card-header.bg-warning.text-white .card-header.bg-warning.text-white
= s_('AdminUsers|Block this user') = s_('AdminUsers|Block this user')
.card-body .card-body
= render partial: 'admin/users/user_block_effects' = user_block_effects
%br %br
%button.btn.gl-button.btn-warning{ data: { 'gl-modal-action': 'block', %button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_block_data(user, s_('AdminUsers|You can always unblock their account, their data will remain intact.')) }
content: s_('AdminUsers|You can always unblock their account, their data will remain intact.'),
url: block_admin_user_path(user),
username: sanitize_name(user.name) } }
= s_('AdminUsers|Block user') = s_('AdminUsers|Block user')
...@@ -5,11 +5,6 @@ ...@@ -5,11 +5,6 @@
action: s_("AdminUsers|Deactivate") } } action: s_("AdminUsers|Deactivate") } }
= render partial: 'admin/users/user_deactivation_effects' = render partial: 'admin/users/user_deactivation_effects'
%div{ data: { modal: "block",
title: s_("AdminUsers|Block user %{username}?"),
action: s_("AdminUsers|Block") } }
= render partial: 'admin/users/user_block_effects'
%div{ data: { modal: "delete", %div{ data: { modal: "delete",
title: s_("AdminUsers|Delete User %{username}?"), title: s_("AdminUsers|Delete User %{username}?"),
action: s_('AdminUsers|Delete user'), action: s_('AdminUsers|Delete user'),
......
...@@ -24,10 +24,10 @@ ...@@ -24,10 +24,10 @@
.table-action-buttons .table-action-buttons
= link_to _('Edit'), edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn gl-button btn-default' = link_to _('Edit'), edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn gl-button btn-default'
- unless user == current_user - unless user == current_user
%button.dropdown-new.btn.gl-button.btn-default{ type: 'button', data: { toggle: 'dropdown' } } %button.dropdown-new.btn.gl-button.btn-default{ type: 'button', data: { testid: "user-action-button-#{user.id}", toggle: 'dropdown' } }
= sprite_icon('settings') = sprite_icon('settings')
= sprite_icon('chevron-down') = sprite_icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right %ul.dropdown-menu.dropdown-menu-right{ data: { testid: "user-action-dropdown-#{user.id}" } }
%li.dropdown-header %li.dropdown-header
= _('Settings') = _('Settings')
%li %li
...@@ -37,16 +37,12 @@ ...@@ -37,16 +37,12 @@
- elsif user.blocked? - elsif user.blocked?
- if user.blocked_pending_approval? - if user.blocked_pending_approval?
= link_to s_('AdminUsers|Approve'), approve_admin_user_path(user), method: :put = link_to s_('AdminUsers|Approve'), approve_admin_user_path(user), method: :put
%button.btn.btn-default-tertiary{ data: { 'gl-modal-action': 'block', %button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_block_data(user, user_block_effects) }
url: block_admin_user_path(user),
username: sanitize_name(user.name) } }
= s_('AdminUsers|Block') = s_('AdminUsers|Block')
- else - else
= link_to _('Unblock'), unblock_admin_user_path(user), method: :put = link_to _('Unblock'), unblock_admin_user_path(user), method: :put
- else - else
%button.btn.btn-default-tertiary{ data: { 'gl-modal-action': 'block', %button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_block_data(user, user_block_effects) }
url: block_admin_user_path(user),
username: sanitize_name(user.name) } }
= s_('AdminUsers|Block') = s_('AdminUsers|Block')
- if user.can_be_deactivated? - if user.can_be_deactivated?
%li %li
......
%p
= s_('AdminUsers|Blocking user has the following effects:')
%ul
%li
= s_('AdminUsers|User will not be able to login')
%li
= s_('AdminUsers|User will not be able to access git repositories')
%li
= s_('AdminUsers|Personal projects will be left')
%li
= s_('AdminUsers|Owned groups will be left')
...@@ -204,6 +204,32 @@ RSpec.describe "Admin::Users" do ...@@ -204,6 +204,32 @@ RSpec.describe "Admin::Users" do
expect(page).to have_content(user.email) expect(page).to have_content(user.email)
end end
end end
context 'when blocking a user' do
it 'shows confirmation and allows blocking', :js do
expect(page).to have_content(user.email)
find("[data-testid='user-action-button-#{user.id}']").click
within find("[data-testid='user-action-dropdown-#{user.id}']") do
find('li button', text: 'Block').click
end
wait_for_requests
expect(page).to have_content('Block user')
expect(page).to have_content('Blocking user has the following effects')
expect(page).to have_content('User will not be able to login')
expect(page).to have_content('Owned groups will be left')
find('.modal-footer button', text: 'Block').click
wait_for_requests
expect(page).to have_content('Successfully blocked')
expect(page).not_to have_content(user.email)
end
end
end end
describe "GET /admin/users/new" do describe "GET /admin/users/new" do
...@@ -362,6 +388,26 @@ RSpec.describe "Admin::Users" do ...@@ -362,6 +388,26 @@ RSpec.describe "Admin::Users" do
end end
end end
context 'when blocking the user' do
it 'shows confirmation and allows blocking', :js do
visit admin_user_path(user)
find('button', text: 'Block user').click
wait_for_requests
expect(page).to have_content('Block user')
expect(page).to have_content('You can always unblock their account, their data will remain intact.')
find('.modal-footer button', text: 'Block').click
wait_for_requests
expect(page).to have_content('Successfully blocked')
expect(page).to have_content('This user is blocked')
end
end
describe 'Impersonation' do describe 'Impersonation' do
let(:another_user) { create(:user) } let(:another_user) { create(:user) }
......
...@@ -62,7 +62,7 @@ describe('vue_shared/components/confirm_modal', () => { ...@@ -62,7 +62,7 @@ describe('vue_shared/components/confirm_modal', () => {
wrapper.vm.modalAttributes = MOCK_MODAL_DATA.modalAttributes; wrapper.vm.modalAttributes = MOCK_MODAL_DATA.modalAttributes;
}); });
it('renders GlModal wtih data', () => { it('renders GlModal with data', () => {
expect(findModal().exists()).toBeTruthy(); expect(findModal().exists()).toBeTruthy();
expect(findModal().attributes()).toEqual( expect(findModal().attributes()).toEqual(
expect.objectContaining({ expect.objectContaining({
...@@ -72,6 +72,24 @@ describe('vue_shared/components/confirm_modal', () => { ...@@ -72,6 +72,24 @@ describe('vue_shared/components/confirm_modal', () => {
); );
}); });
}); });
describe.each`
desc | attrs | expectation
${'when message is simple text'} | ${{}} | ${`<div>${MOCK_MODAL_DATA.modalAttributes.message}</div>`}
${'when message has html'} | ${{ messageHtml: '<p>Header</p><ul onhover="alert(1)"><li>First</li></ul>' }} | ${'<p>Header</p><ul><li>First</li></ul>'}
`('$desc', ({ attrs, expectation }) => {
beforeEach(() => {
createComponent();
wrapper.vm.modalAttributes = {
...MOCK_MODAL_DATA.modalAttributes,
...attrs,
};
});
it('renders message', () => {
expect(findForm().element.innerHTML).toContain(expectation);
});
});
}); });
describe('methods', () => { describe('methods', () => {
......
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