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:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue173
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue106
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue2
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss24
-rw-r--r--app/controllers/admin/groups_controller.rb1
-rw-r--r--app/services/projects/hashed_storage/migrate_repository_service.rb28
-rw-r--r--app/services/projects/hashed_storage/rollback_repository_service.rb28
8 files changed, 210 insertions, 154 deletions
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js
index 746e38e98e8..00c54313292 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js
@@ -3,5 +3,3 @@ export const DropdownVariant = {
Standalone: 'standalone',
Embedded: 'embedded',
};
-
-export const LIST_BUFFER_SIZE = 5;
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
index c8dee81d746..353dee862d0 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
@@ -1,23 +1,25 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
-import { GlLoadingIcon, GlButton, GlSearchBoxByType, GlLink } from '@gitlab/ui';
+import {
+ GlIntersectionObserver,
+ GlLoadingIcon,
+ GlButton,
+ GlSearchBoxByType,
+ GlLink,
+} from '@gitlab/ui';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
-import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
import LabelItem from './label_item.vue';
-import { LIST_BUFFER_SIZE } from './constants';
-
export default {
- LIST_BUFFER_SIZE,
components: {
+ GlIntersectionObserver,
GlLoadingIcon,
GlButton,
GlSearchBoxByType,
GlLink,
- SmartVirtualList,
LabelItem,
},
data() {
@@ -46,15 +48,8 @@ export default {
}
return this.labels;
},
- showListContainer() {
- if (this.isDropdownVariantSidebar) {
- return !this.labelsFetchInProgress;
- }
-
- return true;
- },
showNoMatchingResultsMessage() {
- return !this.labelsFetchInProgress && !this.visibleLabels.length;
+ return Boolean(this.searchKey) && this.visibleLabels.length === 0;
},
},
watch: {
@@ -67,14 +62,12 @@ export default {
}
},
},
- mounted() {
- this.fetchLabels();
- },
methods: {
...mapActions([
'toggleDropdownContents',
'toggleDropdownContentsCreateView',
'fetchLabels',
+ 'receiveLabelsSuccess',
'updateSelectedLabels',
'toggleDropdownContents',
]),
@@ -100,6 +93,17 @@ export default {
}
},
/**
+ * We want to remove loaded labels to ensure component
+ * fetches fresh set of labels every time when shown.
+ */
+ handleComponentDisappear() {
+ this.receiveLabelsSuccess([]);
+ },
+ handleCreateLabelClick() {
+ this.receiveLabelsSuccess([]);
+ this.toggleDropdownContentsCreateView();
+ },
+ /**
* This method enables keyboard navigation support for
* the dropdown.
*/
@@ -135,84 +139,75 @@ export default {
</script>
<template>
- <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown">
- <gl-loading-icon
- v-if="labelsFetchInProgress"
- class="labels-fetch-loading position-absolute gl-display-flex gl-align-items-center w-100 h-100"
- size="md"
- />
- <div
- v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
- class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"
- data-testid="dropdown-title"
- >
- <span class="flex-grow-1">{{ labelsListTitle }}</span>
- <gl-button
- :aria-label="__('Close')"
- variant="link"
- size="small"
- class="dropdown-header-button gl-p-0!"
- icon="close"
- @click="toggleDropdownContents"
- />
- </div>
- <div class="dropdown-input" @click.stop="() => {}">
- <gl-search-box-by-type
- v-model="searchKey"
- :autofocus="true"
- data-qa-selector="dropdown_input_field"
- />
- </div>
- <div
- v-show="showListContainer"
- ref="labelsListContainer"
- class="dropdown-content"
- data-testid="dropdown-content"
- >
- <smart-virtual-list
- :length="visibleLabels.length"
- :remain="$options.LIST_BUFFER_SIZE"
- :size="$options.LIST_BUFFER_SIZE"
- wclass="list-unstyled mb-0"
- wtag="ul"
- class="h-100"
+ <gl-intersection-observer @appear="fetchLabels" @disappear="handleComponentDisappear">
+ <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown">
+ <div
+ v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
+ class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"
+ data-testid="dropdown-title"
>
- <li v-for="(label, index) in visibleLabels" :key="label.id" class="d-block text-left">
+ <span class="flex-grow-1">{{ labelsListTitle }}</span>
+ <gl-button
+ :aria-label="__('Close')"
+ variant="link"
+ size="small"
+ class="dropdown-header-button gl-p-0!"
+ icon="close"
+ @click="toggleDropdownContents"
+ />
+ </div>
+ <div class="dropdown-input" @click.stop="() => {}">
+ <gl-search-box-by-type
+ v-model="searchKey"
+ :autofocus="true"
+ :disabled="labelsFetchInProgress"
+ data-qa-selector="dropdown_input_field"
+ />
+ </div>
+ <div ref="labelsListContainer" class="dropdown-content" data-testid="dropdown-content">
+ <gl-loading-icon
+ v-if="labelsFetchInProgress"
+ class="labels-fetch-loading gl-align-items-center w-100 h-100"
+ size="md"
+ />
+ <ul v-else class="list-unstyled mb-0">
<label-item
+ v-for="(label, index) in visibleLabels"
+ :key="label.id"
:label="label"
:is-label-set="label.set"
:highlight="index === currentHighlightItem"
@clickLabel="handleLabelClick(label)"
/>
- </li>
- <li v-show="showNoMatchingResultsMessage" class="gl-p-3 gl-text-center">
- {{ __('No matching results') }}
- </li>
- </smart-virtual-list>
- </div>
- <div
- v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
- class="dropdown-footer"
- data-testid="dropdown-footer"
- >
- <ul class="list-unstyled">
- <li v-if="allowLabelCreate">
- <gl-link
- class="gl-display-flex w-100 flex-row text-break-word label-item"
- @click="toggleDropdownContentsCreateView"
- >
- {{ footerCreateLabelTitle }}
- </gl-link>
- </li>
- <li>
- <gl-link
- :href="labelsManagePath"
- class="gl-display-flex flex-row text-break-word label-item"
- >
- {{ footerManageLabelTitle }}
- </gl-link>
- </li>
- </ul>
+ <li v-show="showNoMatchingResultsMessage" class="gl-p-3 gl-text-center">
+ {{ __('No matching results') }}
+ </li>
+ </ul>
+ </div>
+ <div
+ v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
+ class="dropdown-footer"
+ data-testid="dropdown-footer"
+ >
+ <ul class="list-unstyled">
+ <li v-if="allowLabelCreate">
+ <gl-link
+ class="gl-display-flex w-100 flex-row text-break-word label-item"
+ @click="handleCreateLabelClick"
+ >
+ {{ footerCreateLabelTitle }}
+ </gl-link>
+ </li>
+ <li>
+ <gl-link
+ :href="labelsManagePath"
+ class="gl-display-flex flex-row text-break-word label-item"
+ >
+ {{ footerManageLabelTitle }}
+ </gl-link>
+ </li>
+ </ul>
+ </div>
</div>
- </div>
+ </gl-intersection-observer>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue
index 002e741ab96..e431fd000a6 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue
@@ -1,11 +1,8 @@
<script>
-import { GlIcon, GlLink } from '@gitlab/ui';
+import { GlLink, GlIcon } from '@gitlab/ui';
export default {
- components: {
- GlIcon,
- GlLink,
- },
+ functional: true,
props: {
label: {
type: Object,
@@ -21,46 +18,65 @@ export default {
default: false,
},
},
- data() {
- return {
- isSet: this.isLabelSet,
- };
- },
- computed: {
- labelBoxStyle() {
- return {
- backgroundColor: this.label.color,
- };
- },
- },
- watch: {
- /**
- * This watcher assures that if user used
- * `Enter` key to set/unset label, changes
- * are reflected here too.
- */
- isLabelSet(value) {
- this.isSet = value;
- },
- },
- methods: {
- handleClick() {
- this.isSet = !this.isSet;
- this.$emit('clickLabel', this.label);
- },
+ render(h, { props, listeners }) {
+ const { label, highlight, isLabelSet } = props;
+
+ const labelColorBox = h('span', {
+ class: 'dropdown-label-box',
+ style: {
+ backgroundColor: label.color,
+ },
+ attrs: {
+ 'data-testid': 'label-color-box',
+ },
+ });
+
+ const checkedIcon = h(GlIcon, {
+ class: {
+ 'mr-2 align-self-center': true,
+ hidden: !isLabelSet,
+ },
+ props: {
+ name: 'mobile-issue-close',
+ },
+ });
+
+ const noIcon = h('span', {
+ class: {
+ 'mr-3 pr-2': true,
+ hidden: isLabelSet,
+ },
+ attrs: {
+ 'data-testid': 'no-icon',
+ },
+ });
+
+ const labelTitle = h('span', label.title);
+
+ const labelLink = h(
+ GlLink,
+ {
+ class: 'd-flex align-items-baseline text-break-word label-item',
+ on: {
+ click: () => {
+ listeners.clickLabel(label);
+ },
+ },
+ },
+ [noIcon, checkedIcon, labelColorBox, labelTitle],
+ );
+
+ return h(
+ 'li',
+ {
+ class: {
+ 'd-block': true,
+ 'text-left': true,
+ 'is-focused': highlight,
+ },
+ },
+ [labelLink],
+ );
},
};
</script>
-
-<template>
- <gl-link
- class="d-flex align-items-baseline text-break-word label-item"
- :class="{ 'is-focused': highlight }"
- @click="handleClick"
- >
- <gl-icon v-show="isSet" name="mobile-issue-close" class="mr-2 align-self-center" />
- <span v-show="!isSet" data-testid="no-icon" class="mr-3 pr-2"></span>
- <span class="dropdown-label-box" data-testid="label-color-box" :style="labelBoxStyle"></span>
- <span>{{ label.title }}</span>
- </gl-link>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
index c651013c5f5..2f71907f772 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
@@ -266,7 +266,7 @@ export default {
</dropdown-value>
<dropdown-button v-show="dropdownButtonVisible" class="gl-mt-2" />
<dropdown-contents
- v-if="dropdownButtonVisible && showDropdownContents"
+ v-show="dropdownButtonVisible && showDropdownContents"
ref="dropdownContents"
/>
</template>
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index e8d37fcf40b..ca20b18f851 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -1016,6 +1016,23 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu {
}
}
+ li {
+ &:hover,
+ &.is-focused {
+ .label-item {
+ @include dropdown-item-hover;
+
+ text-decoration: none;
+ }
+ }
+ }
+
+ .labels-select-dropdown-button {
+ .gl-button-text {
+ width: 100%;
+ }
+ }
+
.labels-select-dropdown-contents {
min-height: $dropdown-min-height;
max-height: 330px;
@@ -1049,13 +1066,6 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu {
.label-item {
padding: 8px 20px;
-
- &:hover,
- &.is-focused {
- @include dropdown-item-hover;
-
- text-decoration: none;
- }
}
.color-input-container {
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 032e449f995..0a1c85eef3f 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -41,6 +41,7 @@ class Admin::GroupsController < Admin::ApplicationController
if @group.save
@group.add_owner(current_user)
+ @group.create_namespace_settings
redirect_to [:admin, @group], notice: _('Group %{group_name} was successfully created.') % { group_name: @group.name }
else
render "new"
diff --git a/app/services/projects/hashed_storage/migrate_repository_service.rb b/app/services/projects/hashed_storage/migrate_repository_service.rb
index fd62ac37d27..adc7e38e4d5 100644
--- a/app/services/projects/hashed_storage/migrate_repository_service.rb
+++ b/app/services/projects/hashed_storage/migrate_repository_service.rb
@@ -21,14 +21,32 @@ module Projects
project.storage_version = nil
end
- project.repository_read_only = false
- project.save!(validate: false)
-
- if result && block_given?
- yield
+ project.transaction do
+ project.save!(validate: false)
+ project.set_repository_writable!
end
result
+ rescue Gitlab::Git::CommandError => e
+ logger.error("Repository #{project.full_path} failed to upgrade (PROJECT_ID=#{project.id}). Git operation failed: #{e.inspect}")
+
+ rollback_migration!
+
+ false
+ rescue OpenSSL::Cipher::CipherError => e
+ logger.error("Repository #{project.full_path} failed to upgrade (PROJECT_ID=#{project.id}). There is a problem with encrypted attributes: #{e.inspect}")
+
+ rollback_migration!
+
+ false
+ end
+
+ private
+
+ def rollback_migration!
+ rollback_folder_move
+ project.storage_version = nil
+ project.set_repository_writable!
end
end
end
diff --git a/app/services/projects/hashed_storage/rollback_repository_service.rb b/app/services/projects/hashed_storage/rollback_repository_service.rb
index d6646e3765e..6ab49630603 100644
--- a/app/services/projects/hashed_storage/rollback_repository_service.rb
+++ b/app/services/projects/hashed_storage/rollback_repository_service.rb
@@ -21,14 +21,32 @@ module Projects
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
end
- project.repository_read_only = false
- project.save!(validate: false)
-
- if result && block_given?
- yield
+ project.transaction do
+ project.save!(validate: false)
+ project.set_repository_writable!
end
result
+ rescue Gitlab::Git::CommandError => e
+ logger.error("Repository #{project.full_path} failed to rollback (PROJECT_ID=#{project.id}). Git operation failed: #{e.inspect}")
+
+ rollback_migration!
+
+ false
+ rescue OpenSSL::Cipher::CipherError => e
+ logger.error("Repository #{project.full_path} failed to rollback (PROJECT_ID=#{project.id}). There is a problem with encrypted attributes: #{e.inspect}")
+
+ rollback_migration!
+
+ false
+ end
+
+ private
+
+ def rollback_migration!
+ rollback_folder_move
+ project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
+ project.set_repository_writable!
end
end
end