Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-03-23 15:07:27 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-03-23 15:07:27 +0300
commit3e68d3848770b492d314f8e2967c37f7fdd5d143 (patch)
tree01bd69a759c55ddf4ea1e5549a253cb0fd564854 /app/assets/javascripts/admin
parent52192e0f19ca790dc9f44bc45730434100f83d90 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/admin')
-rw-r--r--app/assets/javascripts/admin/users/components/actions/delete.vue39
-rw-r--r--app/assets/javascripts/admin/users/components/actions/delete_with_contributions.vue39
-rw-r--r--app/assets/javascripts/admin/users/components/actions/shared/shared_delete_action.vue52
-rw-r--r--app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue112
-rw-r--r--app/assets/javascripts/admin/users/components/modals/delete_user_modal_event_hub.js5
-rw-r--r--app/assets/javascripts/admin/users/components/modals/user_modal_manager.vue77
-rw-r--r--app/assets/javascripts/admin/users/constants.js6
-rw-r--r--app/assets/javascripts/admin/users/index.js47
8 files changed, 114 insertions, 263 deletions
diff --git a/app/assets/javascripts/admin/users/components/actions/delete.vue b/app/assets/javascripts/admin/users/components/actions/delete.vue
index e6dde5898e7..ae0c6731271 100644
--- a/app/assets/javascripts/admin/users/components/actions/delete.vue
+++ b/app/assets/javascripts/admin/users/components/actions/delete.vue
@@ -1,9 +1,11 @@
<script>
-import SharedDeleteAction from './shared/shared_delete_action.vue';
+import { GlDropdownItem } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import eventHub, { EVENT_OPEN_DELETE_USER_MODAL } from '../modals/delete_user_modal_event_hub';
export default {
components: {
- SharedDeleteAction,
+ GlDropdownItem,
},
props: {
username: {
@@ -20,17 +22,32 @@ export default {
default: () => [],
},
},
+ methods: {
+ onClick() {
+ const { username, paths, userDeletionObstacles } = this;
+ eventHub.$emit(EVENT_OPEN_DELETE_USER_MODAL, {
+ username,
+ blockPath: paths.block,
+ deletePath: paths.delete,
+ userDeletionObstacles,
+ i18n: {
+ title: s__('AdminUsers|Delete User %{username}?'),
+ primaryButtonLabel: s__('AdminUsers|Delete user'),
+ messageBody: s__(`AdminUsers|You are about to permanently delete the user %{username}. Issues, merge requests,
+ and groups linked to them will be transferred to a system-wide "Ghost-user". To avoid data loss,
+ consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd},
+ it cannot be undone or recovered.`),
+ },
+ });
+ },
+ },
};
</script>
<template>
- <shared-delete-action
- modal-type="delete"
- :username="username"
- :paths="paths"
- :delete-path="paths.delete"
- :user-deletion-obstacles="userDeletionObstacles"
- >
- <slot></slot>
- </shared-delete-action>
+ <gl-dropdown-item @click="onClick">
+ <span class="gl-text-red-500">
+ <slot></slot>
+ </span>
+ </gl-dropdown-item>
</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/delete_with_contributions.vue b/app/assets/javascripts/admin/users/components/actions/delete_with_contributions.vue
index bd920a91516..a39df1cbfb6 100644
--- a/app/assets/javascripts/admin/users/components/actions/delete_with_contributions.vue
+++ b/app/assets/javascripts/admin/users/components/actions/delete_with_contributions.vue
@@ -1,9 +1,11 @@
<script>
-import SharedDeleteAction from './shared/shared_delete_action.vue';
+import { GlDropdownItem } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import eventHub, { EVENT_OPEN_DELETE_USER_MODAL } from '../modals/delete_user_modal_event_hub';
export default {
components: {
- SharedDeleteAction,
+ GlDropdownItem,
},
props: {
username: {
@@ -20,17 +22,32 @@ export default {
default: () => [],
},
},
+ methods: {
+ onClick() {
+ const { username, paths, userDeletionObstacles } = this;
+ eventHub.$emit(EVENT_OPEN_DELETE_USER_MODAL, {
+ username,
+ blockPath: paths.block,
+ deletePath: paths.deleteWithContributions,
+ userDeletionObstacles,
+ i18n: {
+ title: s__('AdminUsers|Delete User %{username} and contributions?'),
+ primaryButtonLabel: s__('AdminUsers|Delete user and contributions'),
+ messageBody: s__(`AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues,
+ merge requests, and groups linked to them. To avoid data loss,
+ consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd},
+ it cannot be undone or recovered.`),
+ },
+ });
+ },
+ },
};
</script>
<template>
- <shared-delete-action
- modal-type="delete-with-contributions"
- :username="username"
- :paths="paths"
- :delete-path="paths.deleteWithContributions"
- :user-deletion-obstacles="userDeletionObstacles"
- >
- <slot></slot>
- </shared-delete-action>
+ <gl-dropdown-item @click="onClick">
+ <span class="gl-text-red-500">
+ <slot></slot>
+ </span>
+ </gl-dropdown-item>
</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/shared/shared_delete_action.vue b/app/assets/javascripts/admin/users/components/actions/shared/shared_delete_action.vue
deleted file mode 100644
index c9f29b55dbf..00000000000
--- a/app/assets/javascripts/admin/users/components/actions/shared/shared_delete_action.vue
+++ /dev/null
@@ -1,52 +0,0 @@
-<script>
-import { GlDropdownItem } from '@gitlab/ui';
-
-export default {
- components: {
- GlDropdownItem,
- },
- props: {
- username: {
- type: String,
- required: true,
- },
- paths: {
- type: Object,
- required: true,
- },
- deletePath: {
- type: String,
- required: true,
- },
- modalType: {
- type: String,
- required: true,
- },
- userDeletionObstacles: {
- type: Array,
- required: true,
- },
- },
- computed: {
- modalAttributes() {
- return {
- 'data-block-user-url': this.paths.block,
- 'data-delete-user-url': this.deletePath,
- 'data-gl-modal-action': this.modalType,
- 'data-username': this.username,
- 'data-user-deletion-obstacles': JSON.stringify(this.userDeletionObstacles),
- };
- },
- },
-};
-</script>
-
-<template>
- <div class="js-delete-user-modal-button" v-bind="{ ...modalAttributes }">
- <gl-dropdown-item>
- <span class="gl-text-red-500">
- <slot></slot>
- </span>
- </gl-dropdown-item>
- </div>
-</template>
diff --git a/app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue b/app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue
index d7c08096376..31fe86775ee 100644
--- a/app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue
+++ b/app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue
@@ -1,8 +1,8 @@
<script>
import { GlModal, GlButton, GlFormInput, GlSprintf } from '@gitlab/ui';
-import * as Sentry from '@sentry/browser';
import { s__, sprintf } from '~/locale';
import UserDeletionObstaclesList from '~/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.vue';
+import eventHub, { EVENT_OPEN_DELETE_USER_MODAL } from './delete_user_modal_event_hub';
export default {
components: {
@@ -13,47 +13,23 @@ export default {
UserDeletionObstaclesList,
},
props: {
- title: {
- type: String,
- required: true,
- },
- content: {
- type: String,
- required: true,
- },
- action: {
- type: String,
- required: true,
- },
- secondaryAction: {
- type: String,
- required: true,
- },
- deleteUserUrl: {
- type: String,
- required: true,
- },
- blockUserUrl: {
- type: String,
- required: true,
- },
- username: {
- type: String,
- required: true,
- },
csrfToken: {
type: String,
required: true,
},
- userDeletionObstacles: {
- type: String,
- required: false,
- default: '[]',
- },
},
data() {
return {
enteredUsername: '',
+ username: '',
+ blockPath: '',
+ deletePath: '',
+ userDeletionObstacles: [],
+ i18n: {
+ title: '',
+ primaryButtonLabel: '',
+ messageBody: '',
+ },
};
},
computed: {
@@ -61,75 +37,80 @@ export default {
return this.username.trim();
},
modalTitle() {
- return sprintf(this.title, { username: this.trimmedUsername }, false);
- },
- secondaryButtonLabel() {
- return s__('AdminUsers|Block user');
+ return sprintf(this.i18n.title, { username: this.trimmedUsername }, false);
},
canSubmit() {
- return this.enteredUsername === this.trimmedUsername;
+ return this.enteredUsername && this.enteredUsername === this.trimmedUsername;
},
- obstacles() {
- try {
- return JSON.parse(this.userDeletionObstacles);
- } catch (e) {
- Sentry.captureException(e);
- }
- return [];
+ secondaryButtonLabel() {
+ return s__('AdminUsers|Block user');
},
},
+ mounted() {
+ eventHub.$on(EVENT_OPEN_DELETE_USER_MODAL, this.onOpenEvent);
+ },
+ destroyed() {
+ eventHub.$off(EVENT_OPEN_DELETE_USER_MODAL, this.onOpenEvent);
+ },
methods: {
- show() {
+ onOpenEvent({ username, blockPath, deletePath, userDeletionObstacles, i18n }) {
+ this.username = username;
+ this.blockPath = blockPath;
+ this.deletePath = deletePath;
+ this.userDeletionObstacles = userDeletionObstacles;
+ this.i18n = i18n;
+ this.openModal();
+ },
+ openModal() {
this.$refs.modal.show();
},
+ onSubmit() {
+ this.$refs.form.submit();
+ this.enteredUsername = '';
+ },
onCancel() {
this.enteredUsername = '';
this.$refs.modal.hide();
},
onSecondaryAction() {
const { form } = this.$refs;
-
- form.action = this.blockUserUrl;
+ form.action = this.blockPath;
this.$refs.method.value = 'put';
-
form.submit();
},
- onSubmit() {
- this.$refs.form.submit();
- this.enteredUsername = '';
- },
},
};
</script>
-
<template>
<gl-modal ref="modal" modal-id="delete-user-modal" :title="modalTitle" kind="danger">
<p>
- <gl-sprintf :message="content">
+ <gl-sprintf :message="i18n.messageBody">
<template #username>
- <strong>{{ trimmedUsername }}</strong>
+ <strong data-testid="message-username">{{ trimmedUsername }}</strong>
</template>
- <template #strong="props">
- <strong>{{ props.content }}</strong>
+ <template #strong="{ content }">
+ <strong>{{ content }}</strong>
</template>
</gl-sprintf>
</p>
<user-deletion-obstacles-list
- v-if="obstacles.length"
- :obstacles="obstacles"
+ v-if="userDeletionObstacles.length"
+ :obstacles="userDeletionObstacles"
:user-name="trimmedUsername"
/>
<p>
<gl-sprintf :message="s__('AdminUsers|To confirm, type %{username}')">
<template #username>
- <code class="gl-white-space-pre-wrap">{{ trimmedUsername }}</code>
+ <code data-testid="confirm-username" class="gl-white-space-pre-wrap">{{
+ trimmedUsername
+ }}</code>
</template>
</gl-sprintf>
</p>
- <form ref="form" :action="deleteUserUrl" method="post" @submit.prevent>
+ <form ref="form" :action="deletePath" method="post" @submit.prevent>
<input ref="method" type="hidden" name="_method" value="delete" />
<input :value="csrfToken" type="hidden" name="authenticity_token" />
<gl-form-input
@@ -140,6 +121,7 @@ export default {
autocomplete="off"
/>
</form>
+
<template #modal-footer>
<gl-button @click="onCancel">{{ __('Cancel') }}</gl-button>
<gl-button
@@ -148,10 +130,10 @@ export default {
variant="danger"
@click="onSecondaryAction"
>
- {{ secondaryAction }}
+ {{ secondaryButtonLabel }}
</gl-button>
<gl-button :disabled="!canSubmit" category="primary" variant="danger" @click="onSubmit">{{
- action
+ i18n.primaryButtonLabel
}}</gl-button>
</template>
</gl-modal>
diff --git a/app/assets/javascripts/admin/users/components/modals/delete_user_modal_event_hub.js b/app/assets/javascripts/admin/users/components/modals/delete_user_modal_event_hub.js
new file mode 100644
index 00000000000..001061dcc6b
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/modals/delete_user_modal_event_hub.js
@@ -0,0 +1,5 @@
+import createEventHub from '~/helpers/event_hub_factory';
+
+export default createEventHub();
+
+export const EVENT_OPEN_DELETE_USER_MODAL = Symbol('OPEN');
diff --git a/app/assets/javascripts/admin/users/components/modals/user_modal_manager.vue b/app/assets/javascripts/admin/users/components/modals/user_modal_manager.vue
deleted file mode 100644
index 1dfea3f1e7b..00000000000
--- a/app/assets/javascripts/admin/users/components/modals/user_modal_manager.vue
+++ /dev/null
@@ -1,77 +0,0 @@
-<script>
-import DeleteUserModal from './delete_user_modal.vue';
-
-export default {
- components: { DeleteUserModal },
- props: {
- modalConfiguration: {
- required: true,
- type: Object,
- },
- csrfToken: {
- required: true,
- type: String,
- },
- selector: {
- required: true,
- type: String,
- },
- },
- data() {
- return {
- currentModalData: null,
- };
- },
- computed: {
- activeModal() {
- return Boolean(this.currentModalData);
- },
-
- modalProps() {
- const { glModalAction: requestedAction } = this.currentModalData;
- return {
- ...this.modalConfiguration[requestedAction],
- ...this.currentModalData,
- csrfToken: this.csrfToken,
- };
- },
- },
-
- mounted() {
- /*
- * Here we're looking for every button that needs to launch a modal
- * on click, and then attaching a click event handler to show the modal
- * if it's correctly configured.
- *
- * TODO: Replace this with integrated modal components https://gitlab.com/gitlab-org/gitlab/-/issues/320922
- */
- document.querySelectorAll(this.selector).forEach((button) => {
- button.addEventListener('click', (e) => {
- if (!button.dataset.glModalAction) return;
-
- e.preventDefault();
- this.show(button.dataset);
- });
- });
- },
-
- methods: {
- show(modalData) {
- const { glModalAction: requestedAction } = modalData;
-
- if (!this.modalConfiguration[requestedAction]) {
- throw new Error(`Modal action ${requestedAction} has no configuration in HTML`);
- }
-
- this.currentModalData = modalData;
-
- return this.$nextTick().then(() => {
- this.$refs.modal.show();
- });
- },
- },
-};
-</script>
-<template>
- <delete-user-modal v-if="activeModal" ref="modal" v-bind="modalProps" />
-</template>
diff --git a/app/assets/javascripts/admin/users/constants.js b/app/assets/javascripts/admin/users/constants.js
index 4636c8705a5..9cd61d6b1db 100644
--- a/app/assets/javascripts/admin/users/constants.js
+++ b/app/assets/javascripts/admin/users/constants.js
@@ -20,9 +20,3 @@ export const I18N_USER_ACTIONS = {
ban: s__('AdminUsers|Ban user'),
unban: s__('AdminUsers|Unban user'),
};
-
-export const CONFIRM_DELETE_BUTTON_SELECTOR = '.js-delete-user-modal-button';
-
-export const MODAL_TEXTS_CONTAINER_SELECTOR = '#js-modal-texts';
-
-export const MODAL_MANAGER_SELECTOR = '#js-delete-user-modal';
diff --git a/app/assets/javascripts/admin/users/index.js b/app/assets/javascripts/admin/users/index.js
index 0c485d2a239..2bd37d3fffe 100644
--- a/app/assets/javascripts/admin/users/index.js
+++ b/app/assets/javascripts/admin/users/index.js
@@ -4,13 +4,8 @@ import createDefaultClient from '~/lib/graphql';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf';
import AdminUsersApp from './components/app.vue';
-import ModalManager from './components/modals/user_modal_manager.vue';
+import DeleteUserModal from './components/modals/delete_user_modal.vue';
import UserActions from './components/user_actions.vue';
-import {
- CONFIRM_DELETE_BUTTON_SELECTOR,
- MODAL_TEXTS_CONTAINER_SELECTOR,
- MODAL_MANAGER_SELECTOR,
-} from './constants';
Vue.use(VueApollo);
@@ -46,43 +41,13 @@ export const initAdminUserActions = (el = document.querySelector('#js-admin-user
initApp(el, UserActions, 'user', { showButtonLabels: true });
export const initDeleteUserModals = () => {
- const modalsMountElement = document.querySelector(MODAL_TEXTS_CONTAINER_SELECTOR);
-
- if (!modalsMountElement) {
- return;
- }
-
- const modalConfiguration = Array.from(modalsMountElement.children).reduce((accumulator, node) => {
- const { modal, ...config } = node.dataset;
-
- return {
- ...accumulator,
- [modal]: {
- title: node.dataset.title,
- ...config,
- content: node.innerHTML,
- },
- };
- }, {});
-
- // eslint-disable-next-line no-new
- new Vue({
- el: MODAL_MANAGER_SELECTOR,
+ return new Vue({
functional: true,
- methods: {
- show(...args) {
- this.$refs.manager.show(...args);
- },
- },
- render(h) {
- return h(ModalManager, {
- ref: 'manager',
+ render: (createElement) =>
+ createElement(DeleteUserModal, {
props: {
- selector: CONFIRM_DELETE_BUTTON_SELECTOR,
- modalConfiguration,
csrfToken: csrf.token,
},
- });
- },
- });
+ }),
+ }).$mount();
};