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
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-10 15:09:45 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-10 15:09:45 +0300
commitec0ecba05cf7712bc8095af9363ee8ff8d999654 (patch)
tree703b6290381599c58b502e2b94b2d273cfcb00fe /app
parentb6e10aaed70a798a57a40987b3aafcbb5b2a1f78 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/admin/users/components/actions/activate.vue44
-rw-r--r--app/assets/javascripts/admin/users/components/actions/approve.vue21
-rw-r--r--app/assets/javascripts/admin/users/components/actions/block.vue53
-rw-r--r--app/assets/javascripts/admin/users/components/actions/deactivate.vue60
-rw-r--r--app/assets/javascripts/admin/users/components/actions/delete.vue25
-rw-r--r--app/assets/javascripts/admin/users/components/actions/delete_with_contributions.vue25
-rw-r--r--app/assets/javascripts/admin/users/components/actions/index.js21
-rw-r--r--app/assets/javascripts/admin/users/components/actions/reject.vue21
-rw-r--r--app/assets/javascripts/admin/users/components/actions/shared/shared_delete_action.vue43
-rw-r--r--app/assets/javascripts/admin/users/components/actions/unblock.vue44
-rw-r--r--app/assets/javascripts/admin/users/components/actions/unlock.vue42
-rw-r--r--app/assets/javascripts/admin/users/components/user_actions.vue58
-rw-r--r--app/assets/javascripts/admin/users/constants.js17
-rw-r--r--app/assets/javascripts/issuable_show/components/issuable_header.vue10
-rw-r--r--app/assets/javascripts/jobs/components/job_container_item.vue2
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js14
-rw-r--r--app/assets/javascripts/pages/admin/users/index.js3
-rw-r--r--app/helpers/search_helper.rb25
-rw-r--r--app/models/concerns/repository_storage_movable.rb12
-rw-r--r--app/models/pages_deployment.rb1
-rw-r--r--app/views/projects/no_repo.html.haml6
21 files changed, 506 insertions, 41 deletions
diff --git a/app/assets/javascripts/admin/users/components/actions/activate.vue b/app/assets/javascripts/admin/users/components/actions/activate.vue
new file mode 100644
index 00000000000..99c260bf11e
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/activate.vue
@@ -0,0 +1,44 @@
+<script>
+import { GlDropdownItem } from '@gitlab/ui';
+import { sprintf, s__ } from '~/locale';
+
+export default {
+ components: {
+ GlDropdownItem,
+ },
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ path: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ modalAttributes() {
+ return {
+ 'data-path': this.path,
+ 'data-method': 'put',
+ 'data-modal-attributes': JSON.stringify({
+ title: sprintf(s__('AdminUsers|Activate user %{username}?'), {
+ username: this.username,
+ }),
+ message: s__('AdminUsers|You can always deactivate their account again if needed.'),
+ okVariant: 'confirm',
+ okTitle: s__('AdminUsers|Activate'),
+ }),
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
+ <gl-dropdown-item>
+ <slot></slot>
+ </gl-dropdown-item>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/approve.vue b/app/assets/javascripts/admin/users/components/actions/approve.vue
new file mode 100644
index 00000000000..6fc43c246ea
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/approve.vue
@@ -0,0 +1,21 @@
+<script>
+import { GlDropdownItem } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlDropdownItem,
+ },
+ props: {
+ path: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown-item :href="path" data-method="put">
+ <slot></slot>
+ </gl-dropdown-item>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/block.vue b/app/assets/javascripts/admin/users/components/actions/block.vue
new file mode 100644
index 00000000000..68dfefe14c2
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/block.vue
@@ -0,0 +1,53 @@
+<script>
+import { GlDropdownItem } from '@gitlab/ui';
+import { sprintf, s__ } from '~/locale';
+
+// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
+const messageHtml = `
+ <p>${s__('AdminUsers|Blocking user has the following effects:')}</p>
+ <ul>
+ <li>${s__('AdminUsers|User will not be able to login')}</li>
+ <li>${s__('AdminUsers|User will not be able to access git repositories')}</li>
+ <li>${s__('AdminUsers|Personal projects will be left')}</li>
+ <li>${s__('AdminUsers|Owned groups will be left')}</li>
+ </ul>
+`;
+
+export default {
+ components: {
+ GlDropdownItem,
+ },
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ path: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ modalAttributes() {
+ return {
+ 'data-path': this.path,
+ 'data-method': 'put',
+ 'data-modal-attributes': JSON.stringify({
+ title: sprintf(s__('AdminUsers|Block user %{username}?'), { username: this.username }),
+ okVariant: 'confirm',
+ okTitle: s__('AdminUsers|Block'),
+ messageHtml,
+ }),
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
+ <gl-dropdown-item>
+ <slot></slot>
+ </gl-dropdown-item>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/deactivate.vue b/app/assets/javascripts/admin/users/components/actions/deactivate.vue
new file mode 100644
index 00000000000..7e0c17ba296
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/deactivate.vue
@@ -0,0 +1,60 @@
+<script>
+import { GlDropdownItem } from '@gitlab/ui';
+import { sprintf, s__ } from '~/locale';
+
+// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
+const messageHtml = `
+ <p>${s__('AdminUsers|Deactivating a user has the following effects:')}</p>
+ <ul>
+ <li>${s__('AdminUsers|The user will be logged out')}</li>
+ <li>${s__('AdminUsers|The user will not be able to access git repositories')}</li>
+ <li>${s__('AdminUsers|The user will not be able to access the API')}</li>
+ <li>${s__('AdminUsers|The user will not receive any notifications')}</li>
+ <li>${s__('AdminUsers|The user will not be able to use slash commands')}</li>
+ <li>${s__(
+ 'AdminUsers|When the user logs back in, their account will reactivate as a fully active account',
+ )}</li>
+ <li>${s__('AdminUsers|Personal projects, group and user history will be left intact')}</li>
+ </ul>
+`;
+
+export default {
+ components: {
+ GlDropdownItem,
+ },
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ path: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ modalAttributes() {
+ return {
+ 'data-path': this.path,
+ 'data-method': 'put',
+ 'data-modal-attributes': JSON.stringify({
+ title: sprintf(s__('AdminUsers|Deactivate user %{username}?'), {
+ username: this.username,
+ }),
+ okVariant: 'confirm',
+ okTitle: s__('AdminUsers|Deactivate'),
+ messageHtml,
+ }),
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
+ <gl-dropdown-item>
+ <slot></slot>
+ </gl-dropdown-item>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/delete.vue b/app/assets/javascripts/admin/users/components/actions/delete.vue
new file mode 100644
index 00000000000..725d3dbf388
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/delete.vue
@@ -0,0 +1,25 @@
+<script>
+import SharedDeleteAction from './shared/shared_delete_action.vue';
+
+export default {
+ components: {
+ SharedDeleteAction,
+ },
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ paths: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <shared-delete-action modal-type="delete" :username="username" :paths="paths">
+ <slot></slot>
+ </shared-delete-action>
+</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
new file mode 100644
index 00000000000..0ae15bfbebb
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/delete_with_contributions.vue
@@ -0,0 +1,25 @@
+<script>
+import SharedDeleteAction from './shared/shared_delete_action.vue';
+
+export default {
+ components: {
+ SharedDeleteAction,
+ },
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ paths: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <shared-delete-action modal-type="delete-with-contributions" :username="username" :paths="paths">
+ <slot></slot>
+ </shared-delete-action>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/index.js b/app/assets/javascripts/admin/users/components/actions/index.js
new file mode 100644
index 00000000000..697bf284453
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/index.js
@@ -0,0 +1,21 @@
+import Activate from './activate.vue';
+import Approve from './approve.vue';
+import Block from './block.vue';
+import Deactivate from './deactivate.vue';
+import Delete from './delete.vue';
+import DeleteWithContributions from './delete_with_contributions.vue';
+import Unblock from './unblock.vue';
+import Unlock from './unlock.vue';
+import Reject from './reject.vue';
+
+export default {
+ Activate,
+ Approve,
+ Block,
+ Deactivate,
+ Delete,
+ DeleteWithContributions,
+ Unblock,
+ Unlock,
+ Reject,
+};
diff --git a/app/assets/javascripts/admin/users/components/actions/reject.vue b/app/assets/javascripts/admin/users/components/actions/reject.vue
new file mode 100644
index 00000000000..a80c1ff5458
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/reject.vue
@@ -0,0 +1,21 @@
+<script>
+import { GlDropdownItem } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlDropdownItem,
+ },
+ props: {
+ path: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown-item :href="path" data-method="delete">
+ <slot></slot>
+ </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
new file mode 100644
index 00000000000..9107d9ccdd9
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/shared/shared_delete_action.vue
@@ -0,0 +1,43 @@
+<script>
+import { GlDropdownItem } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlDropdownItem,
+ },
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ paths: {
+ type: Object,
+ required: true,
+ },
+ modalType: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ modalAttributes() {
+ return {
+ 'data-block-user-url': this.paths.block,
+ 'data-delete-user-url': this.paths.delete,
+ 'data-gl-modal-action': this.modalType,
+ 'data-username': this.username,
+ };
+ },
+ },
+};
+</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/actions/unblock.vue b/app/assets/javascripts/admin/users/components/actions/unblock.vue
new file mode 100644
index 00000000000..f2b501caf09
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/unblock.vue
@@ -0,0 +1,44 @@
+<script>
+import { GlDropdownItem } from '@gitlab/ui';
+import { sprintf, s__ } from '~/locale';
+
+export default {
+ components: {
+ GlDropdownItem,
+ },
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ path: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ modalAttributes() {
+ return {
+ 'data-path': this.path,
+ 'data-method': 'put',
+ 'data-modal-attributes': JSON.stringify({
+ title: sprintf(s__('AdminUsers|Unblock user %{username}?'), { username: this.username }),
+ message: s__(
+ 'AdminUsers|You can always unblock their account, their data will remain intact.',
+ ),
+ okVariant: 'confirm',
+ okTitle: s__('AdminUsers|Unblock'),
+ }),
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
+ <gl-dropdown-item>
+ <slot></slot>
+ </gl-dropdown-item>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/unlock.vue b/app/assets/javascripts/admin/users/components/actions/unlock.vue
new file mode 100644
index 00000000000..294aaade7c1
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/unlock.vue
@@ -0,0 +1,42 @@
+<script>
+import { GlDropdownItem } from '@gitlab/ui';
+import { sprintf, s__, __ } from '~/locale';
+
+export default {
+ components: {
+ GlDropdownItem,
+ },
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ path: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ modalAttributes() {
+ return {
+ 'data-path': this.path,
+ 'data-method': 'put',
+ 'data-modal-attributes': JSON.stringify({
+ title: sprintf(s__('AdminUsers|Unlock user %{username}?'), { username: this.username }),
+ message: __('Are you sure?'),
+ okVariant: 'confirm',
+ okTitle: s__('AdminUsers|Unlock'),
+ }),
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
+ <gl-dropdown-item>
+ <slot></slot>
+ </gl-dropdown-item>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/user_actions.vue b/app/assets/javascripts/admin/users/components/user_actions.vue
index 6c7c434cdf4..f6d2f6b7497 100644
--- a/app/assets/javascripts/admin/users/components/user_actions.vue
+++ b/app/assets/javascripts/admin/users/components/user_actions.vue
@@ -6,9 +6,11 @@ import {
GlDropdownSectionHeader,
GlDropdownDivider,
} from '@gitlab/ui';
-import { s__, __ } from '~/locale';
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { convertArrayToCamelCase } from '~/lib/utils/common_utils';
import { generateUserPaths } from '../utils';
+import { I18N_USER_ACTIONS } from '../constants';
+import Actions from './actions';
export default {
components: {
@@ -17,6 +19,7 @@ export default {
GlDropdownItem,
GlDropdownSectionHeader,
GlDropdownDivider,
+ ...Actions,
},
props: {
user: {
@@ -58,21 +61,11 @@ export default {
isLdapAction(action) {
return action === 'ldapBlocked';
},
+ getActionComponent(action) {
+ return Actions[capitalizeFirstCharacter(action)];
+ },
},
- i18n: {
- edit: __('Edit'),
- settings: __('Settings'),
- unlock: __('Unlock'),
- block: s__('AdminUsers|Block'),
- unblock: s__('AdminUsers|Unblock'),
- approve: s__('AdminUsers|Approve'),
- reject: s__('AdminUsers|Reject'),
- deactivate: s__('AdminUsers|Deactivate'),
- activate: s__('AdminUsers|Activate'),
- ldapBlocked: s__('AdminUsers|Cannot unblock LDAP blocked users'),
- delete: s__('AdminUsers|Delete user'),
- deleteWithContributions: s__('AdminUsers|Delete user and contributions'),
- },
+ i18n: I18N_USER_ACTIONS,
};
</script>
@@ -92,24 +85,35 @@ export default {
<gl-dropdown-section-header>{{ $options.i18n.settings }}</gl-dropdown-section-header>
<template v-for="action in dropdownSafeActions">
- <gl-dropdown-item v-if="isLdapAction(action)" :key="action" :data-testid="action">
- {{ $options.i18n.ldap }}
- </gl-dropdown-item>
- <gl-dropdown-item v-else :key="action" :href="userPaths[action]" :data-testid="action">
+ <component
+ :is="getActionComponent(action)"
+ v-if="getActionComponent(action)"
+ :key="action"
+ :path="userPaths[action]"
+ :username="user.name"
+ :data-testid="action"
+ >
+ {{ $options.i18n[action] }}
+ </component>
+ <gl-dropdown-item v-else-if="isLdapAction(action)" :key="action" :data-testid="action">
{{ $options.i18n[action] }}
</gl-dropdown-item>
</template>
<gl-dropdown-divider v-if="hasDeleteActions" />
- <gl-dropdown-item
- v-for="action in dropdownDeleteActions"
- :key="action"
- :href="userPaths[action]"
- :data-testid="`delete-${action}`"
- >
- <span class="gl-text-red-500">{{ $options.i18n[action] }}</span>
- </gl-dropdown-item>
+ <template v-for="action in dropdownDeleteActions">
+ <component
+ :is="getActionComponent(action)"
+ v-if="getActionComponent(action)"
+ :key="action"
+ :paths="userPaths"
+ :username="user.name"
+ :data-testid="`delete-${action}`"
+ >
+ {{ $options.i18n[action] }}
+ </component>
+ </template>
</gl-dropdown>
</div>
</template>
diff --git a/app/assets/javascripts/admin/users/constants.js b/app/assets/javascripts/admin/users/constants.js
index e26643cad60..8ea1bd3ca7a 100644
--- a/app/assets/javascripts/admin/users/constants.js
+++ b/app/assets/javascripts/admin/users/constants.js
@@ -1,5 +1,22 @@
+import { s__, __ } from '~/locale';
+
export const USER_AVATAR_SIZE = 32;
export const SHORT_DATE_FORMAT = 'd mmm, yyyy';
export const LENGTH_OF_USER_NOTE_TOOLTIP = 100;
+
+export const I18N_USER_ACTIONS = {
+ edit: __('Edit'),
+ settings: __('Settings'),
+ unlock: __('Unlock'),
+ block: s__('AdminUsers|Block'),
+ unblock: s__('AdminUsers|Unblock'),
+ approve: s__('AdminUsers|Approve'),
+ reject: s__('AdminUsers|Reject'),
+ deactivate: s__('AdminUsers|Deactivate'),
+ activate: s__('AdminUsers|Activate'),
+ ldapBlocked: s__('AdminUsers|Cannot unblock LDAP blocked users'),
+ delete: s__('AdminUsers|Delete user'),
+ deleteWithContributions: s__('AdminUsers|Delete user and contributions'),
+};
diff --git a/app/assets/javascripts/issuable_show/components/issuable_header.vue b/app/assets/javascripts/issuable_show/components/issuable_header.vue
index 4c6df31a0f3..9cfbc2d9bbc 100644
--- a/app/assets/javascripts/issuable_show/components/issuable_header.vue
+++ b/app/assets/javascripts/issuable_show/components/issuable_header.vue
@@ -3,6 +3,7 @@ import { GlIcon, GlButton, GlTooltipDirective, GlAvatarLink, GlAvatarLabeled } f
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import { isExternal } from '~/lib/utils/url_utility';
export default {
components: {
@@ -49,6 +50,9 @@ export default {
authorId() {
return getIdFromGraphQLId(`${this.author.id}`);
},
+ isAuthorExternal() {
+ return isExternal(this.author.webUrl);
+ },
},
mounted() {
this.toggleSidebarButtonEl = document.querySelector('.js-toggle-right-sidebar-button');
@@ -98,7 +102,11 @@ export default {
:src="author.avatarUrl"
:label="author.name"
class="d-none d-sm-inline-flex gl-ml-1"
- />
+ >
+ <template #meta>
+ <gl-icon v-if="isAuthorExternal" name="external-link" />
+ </template>
+ </gl-avatar-labeled>
<strong class="author d-sm-none d-inline">@{{ author.username }}</strong>
</gl-avatar-link>
</div>
diff --git a/app/assets/javascripts/jobs/components/job_container_item.vue b/app/assets/javascripts/jobs/components/job_container_item.vue
index 6b61dc5902b..6a803c06df2 100644
--- a/app/assets/javascripts/jobs/components/job_container_item.vue
+++ b/app/assets/javascripts/jobs/components/job_container_item.vue
@@ -51,7 +51,7 @@ export default {
v-gl-tooltip
:href="job.status.details_path"
:title="tooltipText"
- class="js-job-link d-flex"
+ class="js-job-link gl-display-flex gl-align-items-center"
>
<gl-icon
v-if="isActive"
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 44d3e78b334..0b920ba8e7a 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -310,6 +310,20 @@ export function isAbsoluteOrRootRelative(url) {
}
/**
+ * Returns true if url is an external URL
+ *
+ * @param {String} url
+ * @returns {Boolean}
+ */
+export function isExternal(url) {
+ if (isRootRelative(url)) {
+ return false;
+ }
+
+ return !url.includes(gon.gitlab_url);
+}
+
+/**
* Converts a relative path to an absolute or a root relative path depending
* on what is passed as a basePath.
*
diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js
index ae5db5f5bdc..42afcf434f6 100644
--- a/app/assets/javascripts/pages/admin/users/index.js
+++ b/app/assets/javascripts/pages/admin/users/index.js
@@ -34,6 +34,8 @@ function loadModalsConfigurationFromHtml(modalsElement) {
document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate);
+ initAdminUsersApp();
+
const modalConfiguration = loadModalsConfigurationFromHtml(
document.querySelector(MODAL_TEXTS_CONTAINER_SELECTOR),
);
@@ -60,7 +62,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
initConfirmModal();
- initAdminUsersApp();
initCohortsEmptyState();
initTabs();
});
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 9654b7cd9a9..1a5b578cc75 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -122,15 +122,24 @@ module SearchHelper
end
def search_sort_options
- options = []
- options << {
- title: _('Created date'),
- sortable: true,
- sortParam: {
- asc: 'created_asc',
- desc: 'created_desc'
+ [
+ {
+ title: _('Created date'),
+ sortable: true,
+ sortParam: {
+ asc: 'created_asc',
+ desc: 'created_desc'
+ }
+ },
+ {
+ title: _('Last updated'),
+ sortable: true,
+ sortParam: {
+ asc: 'updated_asc',
+ desc: 'updated_desc'
+ }
}
- }
+ ]
end
private
diff --git a/app/models/concerns/repository_storage_movable.rb b/app/models/concerns/repository_storage_movable.rb
index e584922025a..8607f0d94f4 100644
--- a/app/models/concerns/repository_storage_movable.rb
+++ b/app/models/concerns/repository_storage_movable.rb
@@ -68,6 +68,18 @@ module RepositoryStorageMovable
storage_move.update_repository_storage(storage_move.destination_storage_name)
end
+ after_transition started: :replicated do |storage_move|
+ # We have several scripts in place that replicate some statistics information
+ # to other databases. Some of them depend on the updated_at column
+ # to identify the models they need to extract.
+ #
+ # If we don't update the `updated_at` of the container after a repository storage move,
+ # the scripts won't know that they need to sync them.
+ #
+ # See https://gitlab.com/gitlab-data/analytics/-/issues/7868
+ storage_move.container.touch
+ end
+
before_transition started: :failed do |storage_move|
storage_move.container.set_repository_writable!
end
diff --git a/app/models/pages_deployment.rb b/app/models/pages_deployment.rb
index 95949eeed9a..d67a92af6af 100644
--- a/app/models/pages_deployment.rb
+++ b/app/models/pages_deployment.rb
@@ -2,6 +2,7 @@
# PagesDeployment stores a zip archive containing GitLab Pages web-site
class PagesDeployment < ApplicationRecord
+ include EachBatch
include FileStoreMounter
MIGRATED_FILE_NAME = "_migrated.zip"
diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml
index ea14e2d6ca5..3c7afff57f6 100644
--- a/app/views/projects/no_repo.html.haml
+++ b/app/views/projects/no_repo.html.haml
@@ -15,14 +15,14 @@
= render 'projects/invite_members_modal', project: @project
.no-repo-actions
- = link_to project_repository_path(@project), method: :post, class: 'btn btn-primary' do
+ = link_to project_repository_path(@project), method: :post, class: 'btn gl-button btn-confirm' do
#{ _('Create empty repository') }
%strong.gl-ml-3.gl-mr-3 or
- = link_to new_project_import_path(@project), class: 'btn' do
+ = link_to new_project_import_path(@project), class: 'btn gl-button btn-default' do
#{ _('Import repository') }
- if can? current_user, :remove_project, @project
.prepend-top-20
- = link_to _('Delete project'), project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "gl-button btn btn-danger btn-danger-secondary float-right"
+ = link_to _('Delete project'), project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn gl-button btn-danger float-right"