Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
17a47c3e30
commit
79c94e595b
|
|
@ -5,7 +5,7 @@ workflow:
|
|||
name: $PIPELINE_NAME
|
||||
|
||||
include:
|
||||
- component: "gitlab.com/gitlab-org/quality/pipeline-common/allure-report@7.13.1"
|
||||
- component: "gitlab.com/gitlab-org/quality/pipeline-common/allure-report@7.13.2"
|
||||
inputs:
|
||||
job_name: "e2e-test-report"
|
||||
job_stage: "report"
|
||||
|
|
|
|||
|
|
@ -2427,7 +2427,6 @@ RSpec/FeatureCategory:
|
|||
- 'spec/helpers/invite_members_helper_spec.rb'
|
||||
- 'spec/helpers/issuables_description_templates_helper_spec.rb'
|
||||
- 'spec/helpers/issues_helper_spec.rb'
|
||||
- 'spec/helpers/json_helper_spec.rb'
|
||||
- 'spec/helpers/keyset_helper_spec.rb'
|
||||
- 'spec/helpers/labels_helper_spec.rb'
|
||||
- 'spec/helpers/lazy_image_tag_helper_spec.rb'
|
||||
|
|
|
|||
|
|
@ -1612,7 +1612,6 @@ RSpec/NamedSubject:
|
|||
- 'spec/helpers/hooks_helper_spec.rb'
|
||||
- 'spec/helpers/integrations_helper_spec.rb'
|
||||
- 'spec/helpers/jira_connect_helper_spec.rb'
|
||||
- 'spec/helpers/json_helper_spec.rb'
|
||||
- 'spec/helpers/labels_helper_spec.rb'
|
||||
- 'spec/helpers/listbox_helper_spec.rb'
|
||||
- 'spec/helpers/markup_helper_spec.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
6cb3173f9a1468c93d5c3fa9327e9b560de1329b
|
||||
38a79230add5dc453d8fafa48f7f3e218a70f0be
|
||||
|
|
|
|||
|
|
@ -20,23 +20,11 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'jobLogEndpoint',
|
||||
'jobLog',
|
||||
'isJobLogComplete',
|
||||
'isScrolledToBottomBeforeReceivingJobLog',
|
||||
]),
|
||||
...mapState(['jobLogEndpoint', 'jobLog', 'isJobLogComplete']),
|
||||
highlightedLines() {
|
||||
return this.searchResults.map((result) => result.lineNumber);
|
||||
},
|
||||
},
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
if (!window.location.hash) {
|
||||
this.handleScrollDown();
|
||||
}
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
if (window.location.hash) {
|
||||
const lineNumber = getLocationHash();
|
||||
|
|
@ -57,20 +45,6 @@ export default {
|
|||
handleOnClickCollapsibleLine(section) {
|
||||
this.toggleCollapsibleLine(section);
|
||||
},
|
||||
/**
|
||||
* The job log is sent in HTML, which means we need to use `v-html` to render it
|
||||
* Using the updated hook with $nextTick is not enough to wait for the DOM to be updated
|
||||
* in this case because it runs before `v-html` has finished running, since there's no
|
||||
* Vue binding.
|
||||
* In order to scroll the page down after `v-html` has finished, we need to use setTimeout
|
||||
*/
|
||||
handleScrollDown() {
|
||||
if (this.isScrolledToBottomBeforeReceivingJobLog) {
|
||||
setTimeout(() => {
|
||||
this.scrollBottom();
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
isHighlighted({ lineNumber }) {
|
||||
return this.highlightedLines.includes(lineNumber);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ export default {
|
|||
'isJobLogSizeVisible',
|
||||
'isScrollBottomDisabled',
|
||||
'isScrollTopDisabled',
|
||||
'isScrolledToBottomBeforeReceivingJobLog',
|
||||
'hasError',
|
||||
'selectedStage',
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -150,40 +150,47 @@ export const enableScrollTop = ({ commit }) => commit(types.ENABLE_SCROLL_TOP);
|
|||
export const toggleScrollAnimation = ({ commit }, toggle) =>
|
||||
commit(types.TOGGLE_SCROLL_ANIMATION, toggle);
|
||||
|
||||
/**
|
||||
* Responsible to handle automatic scroll
|
||||
*/
|
||||
export const toggleScrollisInBottom = ({ commit }, toggle) => {
|
||||
commit(types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG, toggle);
|
||||
};
|
||||
|
||||
export const requestJobLog = ({ commit }) => commit(types.REQUEST_JOB_LOG);
|
||||
|
||||
export const fetchJobLog = ({ dispatch, state }) =>
|
||||
// update trace endpoint once BE compeletes trace re-naming in #340626
|
||||
axios
|
||||
.get(`${state.jobLogEndpoint}/trace.json`, {
|
||||
params: { state: state.jobLogState },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
dispatch('toggleScrollisInBottom', isScrolledToBottom());
|
||||
dispatch('receiveJobLogSuccess', data);
|
||||
export const fetchJobLog = ({ commit, dispatch, state }) => {
|
||||
let isScrolledToBottomBeforeReceivingJobLog;
|
||||
|
||||
if (data.complete) {
|
||||
dispatch('stopPollingJobLog');
|
||||
dispatch('requestTestSummary');
|
||||
} else if (!state.jobLogTimeout) {
|
||||
dispatch('startPollingJobLog');
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.status === HTTP_STATUS_FORBIDDEN) {
|
||||
dispatch('receiveJobLogUnauthorizedError');
|
||||
} else {
|
||||
reportToSentry('job_actions', e);
|
||||
dispatch('receiveJobLogError');
|
||||
}
|
||||
});
|
||||
// update trace endpoint once BE completes trace re-naming in #340626
|
||||
return (
|
||||
axios
|
||||
.get(`${state.jobLogEndpoint}/trace.json`, {
|
||||
params: { state: state.jobLogState },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
isScrolledToBottomBeforeReceivingJobLog = isScrolledToBottom();
|
||||
|
||||
commit(types.RECEIVE_JOB_LOG_SUCCESS, data);
|
||||
|
||||
if (data.complete) {
|
||||
dispatch('stopPollingJobLog');
|
||||
dispatch('requestTestSummary');
|
||||
} else if (!state.jobLogTimeout) {
|
||||
dispatch('startPollingJobLog');
|
||||
}
|
||||
})
|
||||
// place `scrollBottom` in a separate `then()` block
|
||||
// to wait on related components to update
|
||||
// after the RECEIVE_JOB_LOG_SUCCESS commit
|
||||
.then(() => {
|
||||
if (isScrolledToBottomBeforeReceivingJobLog) {
|
||||
dispatch('scrollBottom');
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.status === HTTP_STATUS_FORBIDDEN) {
|
||||
dispatch('receiveJobLogUnauthorizedError');
|
||||
} else {
|
||||
reportToSentry('job_actions', e);
|
||||
dispatch('receiveJobLogError');
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const startPollingJobLog = ({ dispatch, commit }) => {
|
||||
const jobLogTimeout = setTimeout(() => {
|
||||
|
|
@ -200,8 +207,6 @@ export const stopPollingJobLog = ({ state, commit }) => {
|
|||
commit(types.STOP_POLLING_JOB_LOG);
|
||||
};
|
||||
|
||||
export const receiveJobLogSuccess = ({ commit }, log) => commit(types.RECEIVE_JOB_LOG_SUCCESS, log);
|
||||
|
||||
export const receiveJobLogError = ({ dispatch }) => {
|
||||
dispatch('stopPollingJobLog');
|
||||
createAlert({
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ export const ENABLE_SCROLL_BOTTOM = 'ENABLE_SCROLL_BOTTOM';
|
|||
export const ENABLE_SCROLL_TOP = 'ENABLE_SCROLL_TOP';
|
||||
export const TOGGLE_SCROLL_ANIMATION = 'TOGGLE_SCROLL_ANIMATION';
|
||||
|
||||
export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
|
||||
|
||||
export const REQUEST_JOB = 'REQUEST_JOB';
|
||||
export const RECEIVE_JOB_SUCCESS = 'RECEIVE_JOB_SUCCESS';
|
||||
export const RECEIVE_JOB_ERROR = 'RECEIVE_JOB_ERROR';
|
||||
|
|
|
|||
|
|
@ -111,11 +111,6 @@ export default {
|
|||
[types.TOGGLE_SCROLL_ANIMATION](state, toggle) {
|
||||
state.isScrollingDown = toggle;
|
||||
},
|
||||
|
||||
[types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG](state, toggle) {
|
||||
state.isScrolledToBottomBeforeReceivingJobLog = toggle;
|
||||
},
|
||||
|
||||
[types.REQUEST_JOBS_FOR_STAGE](state, stage = {}) {
|
||||
state.isLoadingJobs = true;
|
||||
state.selectedStage = stage.name;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@ export default () => ({
|
|||
isScrollBottomDisabled: true,
|
||||
isScrollTopDisabled: true,
|
||||
|
||||
// Used to check if we should keep the automatic scroll
|
||||
isScrolledToBottomBeforeReceivingJobLog: true,
|
||||
|
||||
jobLog: [],
|
||||
isJobLogComplete: false,
|
||||
jobLogSize: 0,
|
||||
|
|
|
|||
|
|
@ -1,183 +0,0 @@
|
|||
<script>
|
||||
import { GlLoadingIcon, GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import AccessorUtilities from '~/lib/utils/accessor';
|
||||
import {
|
||||
mapVuexModuleState,
|
||||
mapVuexModuleActions,
|
||||
mapVuexModuleGetters,
|
||||
} from '~/lib/utils/vuex_module_mappers';
|
||||
import Tracking from '~/tracking';
|
||||
import { FREQUENT_ITEMS, STORAGE_KEY } from '../constants';
|
||||
import eventHub from '../event_hub';
|
||||
import { isMobile, updateExistingFrequentItem, sanitizeItem } from '../utils';
|
||||
import FrequentItemsList from './frequent_items_list.vue';
|
||||
import frequentItemsMixin from './frequent_items_mixin';
|
||||
import FrequentItemsSearchInput from './frequent_items_search_input.vue';
|
||||
|
||||
const trackingMixin = Tracking.mixin();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FrequentItemsSearchInput,
|
||||
FrequentItemsList,
|
||||
GlLoadingIcon,
|
||||
GlButton,
|
||||
GlIcon,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [frequentItemsMixin, trackingMixin],
|
||||
inject: ['vuexModule'],
|
||||
props: {
|
||||
currentUserName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
currentItem: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
searchClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapVuexModuleState((vm) => vm.vuexModule, [
|
||||
'searchQuery',
|
||||
'isLoadingItems',
|
||||
'isItemsListEditable',
|
||||
'isFetchFailed',
|
||||
'isItemRemovalFailed',
|
||||
'items',
|
||||
]),
|
||||
...mapVuexModuleGetters((vm) => vm.vuexModule, ['hasSearchQuery']),
|
||||
translations() {
|
||||
return this.getTranslations(['loadingMessage', 'header', 'headerEditToggle']);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
const { namespace, currentUserName, currentItem } = this;
|
||||
const storageKey = `${currentUserName}/${STORAGE_KEY[namespace]}`;
|
||||
|
||||
this.setNamespace(namespace);
|
||||
this.setStorageKey(storageKey);
|
||||
|
||||
if (currentItem.id) {
|
||||
this.logItemAccess(storageKey, currentItem);
|
||||
}
|
||||
|
||||
eventHub.$on(`${this.namespace}-dropdownOpen`, this.dropdownOpenHandler);
|
||||
|
||||
// As we init it through requestIdleCallback it could be that the dropdown is already open
|
||||
const namespaceDropdown = document.getElementById(`nav-${this.namespace}-dropdown`);
|
||||
if (namespaceDropdown && namespaceDropdown.classList.contains('show')) {
|
||||
this.dropdownOpenHandler();
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off(`${this.namespace}-dropdownOpen`, this.dropdownOpenHandler);
|
||||
},
|
||||
methods: {
|
||||
...mapVuexModuleActions((vm) => vm.vuexModule, [
|
||||
'setNamespace',
|
||||
'setStorageKey',
|
||||
'toggleItemsListEditablity',
|
||||
'fetchFrequentItems',
|
||||
]),
|
||||
toggleItemsListEditablityTracked() {
|
||||
this.track('click_button', {
|
||||
label: 'toggle_edit_frequent_items',
|
||||
property: 'navigation_top',
|
||||
});
|
||||
this.toggleItemsListEditablity();
|
||||
},
|
||||
dropdownOpenHandler() {
|
||||
if (this.searchQuery === '' || isMobile()) {
|
||||
this.fetchFrequentItems();
|
||||
}
|
||||
},
|
||||
logItemAccess(storageKey, unsanitizedItem) {
|
||||
const item = sanitizeItem(unsanitizedItem);
|
||||
|
||||
if (!AccessorUtilities.canUseLocalStorage()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if there's any frequent items list set
|
||||
const storedRawItems = localStorage.getItem(storageKey);
|
||||
const storedFrequentItems = storedRawItems
|
||||
? JSON.parse(storedRawItems)
|
||||
: [{ ...item, frequency: 1 }]; // No frequent items list set, set one up.
|
||||
|
||||
// Check if item already exists in list
|
||||
const itemMatchIndex = storedFrequentItems.findIndex(
|
||||
(frequentItem) => frequentItem.id === item.id,
|
||||
);
|
||||
|
||||
if (itemMatchIndex > -1) {
|
||||
storedFrequentItems[itemMatchIndex] = updateExistingFrequentItem(
|
||||
storedFrequentItems[itemMatchIndex],
|
||||
item,
|
||||
);
|
||||
} else {
|
||||
if (storedFrequentItems.length === FREQUENT_ITEMS.MAX_COUNT) {
|
||||
storedFrequentItems.shift();
|
||||
}
|
||||
|
||||
storedFrequentItems.push({ ...item, frequency: 1 });
|
||||
}
|
||||
|
||||
return localStorage.setItem(storageKey, JSON.stringify(storedFrequentItems));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-display-flex gl-flex-direction-column gl-flex-align-items-stretch gl-h-full">
|
||||
<frequent-items-search-input
|
||||
:namespace="namespace"
|
||||
:class="searchClass"
|
||||
data-testid="frequent-items-search-input"
|
||||
/>
|
||||
<gl-loading-icon
|
||||
v-if="isLoadingItems"
|
||||
:label="translations.loadingMessage"
|
||||
size="lg"
|
||||
class="loading-animation prepend-top-20"
|
||||
data-testid="loading"
|
||||
/>
|
||||
<div
|
||||
v-if="!isLoadingItems && !hasSearchQuery"
|
||||
class="section-header gl-display-flex"
|
||||
data-testid="header"
|
||||
>
|
||||
<span class="gl-flex-grow-1">{{ translations.header }}</span>
|
||||
<gl-button
|
||||
v-if="items.length"
|
||||
v-gl-tooltip.left
|
||||
size="small"
|
||||
category="tertiary"
|
||||
:aria-label="translations.headerEditToggle"
|
||||
:title="translations.headerEditToggle"
|
||||
:class="{ 'gl-bg-gray-100!': isItemsListEditable }"
|
||||
class="gl-p-2!"
|
||||
@click="toggleItemsListEditablityTracked"
|
||||
>
|
||||
<gl-icon name="pencil" :class="{ 'gl-text-gray-900!': isItemsListEditable }" />
|
||||
</gl-button>
|
||||
</div>
|
||||
<frequent-items-list
|
||||
v-if="!isLoadingItems"
|
||||
:items="items"
|
||||
:namespace="namespace"
|
||||
:has-search-query="hasSearchQuery"
|
||||
:is-fetch-failed="isFetchFailed"
|
||||
:is-item-removal-failed="isItemRemovalFailed"
|
||||
:matcher="searchQuery"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
<script>
|
||||
import { sanitizeItem } from '../utils';
|
||||
import FrequentItemsListItem from './frequent_items_list_item.vue';
|
||||
import frequentItemsMixin from './frequent_items_mixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FrequentItemsListItem,
|
||||
},
|
||||
mixins: [frequentItemsMixin],
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
hasSearchQuery: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
isFetchFailed: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
isItemRemovalFailed: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
matcher: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
translations() {
|
||||
return this.getTranslations([
|
||||
'itemListEmptyMessage',
|
||||
'itemListErrorMessage',
|
||||
'searchListEmptyMessage',
|
||||
'searchListErrorMessage',
|
||||
]);
|
||||
},
|
||||
isListEmpty() {
|
||||
return this.items.length === 0;
|
||||
},
|
||||
showListEmptyMessage() {
|
||||
return this.isListEmpty || this.isItemRemovalFailed;
|
||||
},
|
||||
listEmptyMessage() {
|
||||
if (this.hasSearchQuery) {
|
||||
return this.isFetchFailed
|
||||
? this.translations.searchListErrorMessage
|
||||
: this.translations.searchListEmptyMessage;
|
||||
}
|
||||
|
||||
return this.isFetchFailed || this.isItemRemovalFailed
|
||||
? this.translations.itemListErrorMessage
|
||||
: this.translations.itemListEmptyMessage;
|
||||
},
|
||||
sanitizedItems() {
|
||||
return this.items.map(sanitizeItem);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="frequent-items-list-container">
|
||||
<ul data-testid="frequent-items-list" class="list-unstyled">
|
||||
<li
|
||||
v-if="showListEmptyMessage"
|
||||
:class="{ 'section-failure': isFetchFailed }"
|
||||
class="section-empty gl-mb-3"
|
||||
data-testid="frequent-items-list-empty"
|
||||
>
|
||||
{{ listEmptyMessage }}
|
||||
</li>
|
||||
<frequent-items-list-item
|
||||
v-for="item in sanitizedItems"
|
||||
v-else
|
||||
:key="item.id"
|
||||
:item-id="item.id"
|
||||
:item-name="item.name"
|
||||
:namespace="item.namespace"
|
||||
:web-url="item.webUrl"
|
||||
:avatar-url="item.avatarUrl"
|
||||
:matcher="matcher"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
<script>
|
||||
import { GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui';
|
||||
import SafeHtml from '~/vue_shared/directives/safe_html';
|
||||
import highlight from '~/lib/utils/highlight';
|
||||
import { truncateNamespace } from '~/lib/utils/text_utility';
|
||||
import { mapVuexModuleState, mapVuexModuleActions } from '~/lib/utils/vuex_module_mappers';
|
||||
import Tracking from '~/tracking';
|
||||
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
|
||||
|
||||
const trackingMixin = Tracking.mixin();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
GlButton,
|
||||
ProjectAvatar,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
inject: ['vuexModule'],
|
||||
props: {
|
||||
matcher: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
itemId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
itemName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
namespace: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
webUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
avatarUrl: {
|
||||
required: true,
|
||||
validator(value) {
|
||||
return value === null || typeof value === 'string';
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapVuexModuleState((vm) => vm.vuexModule, ['dropdownType', 'isItemsListEditable']),
|
||||
truncatedNamespace() {
|
||||
return truncateNamespace(this.namespace);
|
||||
},
|
||||
highlightedItemName() {
|
||||
return highlight(this.itemName, this.matcher);
|
||||
},
|
||||
itemTrackingLabel() {
|
||||
return `${this.dropdownType}_dropdown_frequent_items_list_item`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
removeFrequentItemTracked(item) {
|
||||
this.track('click_button', {
|
||||
label: `${this.dropdownType}_dropdown_remove_frequent_item`,
|
||||
property: 'navigation_top',
|
||||
});
|
||||
this.removeFrequentItem(item);
|
||||
},
|
||||
...mapVuexModuleActions((vm) => vm.vuexModule, ['removeFrequentItem']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li class="frequent-items-list-item-container gl-relative">
|
||||
<gl-button
|
||||
category="tertiary"
|
||||
:href="webUrl"
|
||||
class="gl-text-left gl-w-full"
|
||||
button-text-classes="gl-display-flex gl-w-full"
|
||||
data-testid="frequent-item-link"
|
||||
@click="track('click_link', { label: itemTrackingLabel, property: 'navigation_top' })"
|
||||
>
|
||||
<div class="gl-flex-grow-1">
|
||||
<project-avatar
|
||||
class="gl-float-left gl-mr-3"
|
||||
:project-avatar-url="avatarUrl"
|
||||
:project-id="itemId"
|
||||
:project-name="itemName"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div
|
||||
data-testid="frequent-items-item-metadata-container"
|
||||
class="frequent-items-item-metadata-container"
|
||||
>
|
||||
<div
|
||||
v-safe-html="highlightedItemName"
|
||||
data-testid="frequent-items-item-title"
|
||||
:title="itemName"
|
||||
class="frequent-items-item-title"
|
||||
></div>
|
||||
<div
|
||||
v-if="namespace"
|
||||
data-testid="frequent-items-item-namespace"
|
||||
:title="namespace"
|
||||
class="frequent-items-item-namespace"
|
||||
>
|
||||
{{ truncatedNamespace }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-if="isItemsListEditable"
|
||||
v-gl-tooltip.left
|
||||
size="small"
|
||||
category="tertiary"
|
||||
:aria-label="__('Remove')"
|
||||
:title="__('Remove')"
|
||||
class="gl-align-self-center gl-p-1! gl-absolute! gl-w-auto! gl-right-4 gl-top-half gl-translate-y-n50"
|
||||
data-testid="item-remove"
|
||||
@click.stop.prevent="removeFrequentItemTracked(itemId)"
|
||||
>
|
||||
<gl-icon name="close" />
|
||||
</gl-button>
|
||||
</li>
|
||||
</template>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import { TRANSLATION_KEYS } from '../constants';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
namespace: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getTranslations(keys) {
|
||||
const translationStrings = keys.reduce(
|
||||
(acc, key) => ({
|
||||
...acc,
|
||||
[key]: TRANSLATION_KEYS[this.namespace][key],
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
return translationStrings;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
<script>
|
||||
import { GlSearchBoxByType } from '@gitlab/ui';
|
||||
import { debounce } from 'lodash';
|
||||
import { mapVuexModuleActions, mapVuexModuleState } from '~/lib/utils/vuex_module_mappers';
|
||||
import Tracking from '~/tracking';
|
||||
import frequentItemsMixin from './frequent_items_mixin';
|
||||
|
||||
const trackingMixin = Tracking.mixin();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlSearchBoxByType,
|
||||
},
|
||||
mixins: [frequentItemsMixin, trackingMixin],
|
||||
inject: ['vuexModule'],
|
||||
data() {
|
||||
return {
|
||||
searchQuery: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapVuexModuleState((vm) => vm.vuexModule, ['dropdownType']),
|
||||
translations() {
|
||||
return this.getTranslations(['searchInputPlaceholder']);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
searchQuery: debounce(function debounceSearchQuery() {
|
||||
this.track('type_search_query', {
|
||||
label: `${this.dropdownType}_dropdown_frequent_items_search_input`,
|
||||
property: 'navigation_top',
|
||||
});
|
||||
this.setSearchQuery(this.searchQuery);
|
||||
}, 500),
|
||||
},
|
||||
methods: {
|
||||
...mapVuexModuleActions((vm) => vm.vuexModule, ['setSearchQuery']),
|
||||
trackFocus() {
|
||||
this.track('focus_input', {
|
||||
label: `${this.dropdownType}_dropdown_frequent_items_search_input`,
|
||||
property: 'navigation_top',
|
||||
});
|
||||
},
|
||||
trackBlur() {
|
||||
this.track('blur_input', {
|
||||
label: `${this.dropdownType}_dropdown_frequent_items_search_input`,
|
||||
property: 'navigation_top',
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search-input-container">
|
||||
<gl-search-box-by-type
|
||||
v-model="searchQuery"
|
||||
:placeholder="translations.searchInputPlaceholder"
|
||||
@focus="trackFocus"
|
||||
@blur="trackBlur"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
export const FREQUENT_ITEMS = {
|
||||
MAX_COUNT: 20,
|
||||
LIST_COUNT_DESKTOP: 5,
|
||||
LIST_COUNT_MOBILE: 3,
|
||||
ELIGIBLE_FREQUENCY: 3,
|
||||
};
|
||||
|
||||
export const FIFTEEN_MINUTES_IN_MS = 900000;
|
||||
|
||||
export const STORAGE_KEY = {
|
||||
projects: 'frequent-projects',
|
||||
groups: 'frequent-groups',
|
||||
};
|
||||
|
||||
export const TRANSLATION_KEYS = {
|
||||
projects: {
|
||||
loadingMessage: s__('ProjectsDropdown|Loading projects'),
|
||||
header: s__('ProjectsDropdown|Frequently visited'),
|
||||
headerEditToggle: s__('ProjectsDropdown|Toggle edit mode'),
|
||||
itemListErrorMessage: s__(
|
||||
'ProjectsDropdown|This feature requires browser localStorage support',
|
||||
),
|
||||
itemListEmptyMessage: s__('ProjectsDropdown|Projects you visit often will appear here'),
|
||||
searchListErrorMessage: s__('ProjectsDropdown|Something went wrong on our end.'),
|
||||
searchListEmptyMessage: s__('ProjectsDropdown|Sorry, no projects matched your search'),
|
||||
searchInputPlaceholder: s__('ProjectsDropdown|Search your projects'),
|
||||
},
|
||||
groups: {
|
||||
loadingMessage: s__('GroupsDropdown|Loading groups'),
|
||||
header: s__('GroupsDropdown|Frequently visited'),
|
||||
headerEditToggle: s__('GroupsDropdown|Toggle edit mode'),
|
||||
itemListErrorMessage: s__('GroupsDropdown|This feature requires browser localStorage support'),
|
||||
itemListEmptyMessage: s__('GroupsDropdown|Groups you visit often will appear here'),
|
||||
searchListErrorMessage: s__('GroupsDropdown|Something went wrong on our end.'),
|
||||
searchListEmptyMessage: s__('GroupsDropdown|Sorry, no groups matched your search'),
|
||||
searchInputPlaceholder: s__('GroupsDropdown|Search your groups'),
|
||||
},
|
||||
};
|
||||
|
||||
export const FREQUENT_ITEMS_PROJECTS = {
|
||||
namespace: 'projects',
|
||||
key: 'project',
|
||||
vuexModule: 'frequentProjects',
|
||||
};
|
||||
|
||||
export const FREQUENT_ITEMS_GROUPS = {
|
||||
namespace: 'groups',
|
||||
key: 'group',
|
||||
vuexModule: 'frequentGroups',
|
||||
};
|
||||
|
||||
export const FREQUENT_ITEMS_DROPDOWNS = [FREQUENT_ITEMS_PROJECTS, FREQUENT_ITEMS_GROUPS];
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import createEventHub from '~/helpers/event_hub_factory';
|
||||
|
||||
export default createEventHub();
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
import AccessorUtilities from '~/lib/utils/accessor';
|
||||
import { isLoggedIn } from '~/lib/utils/common_utils';
|
||||
import { getGroups, getProjects } from '~/rest_api';
|
||||
import { getTopFrequentItems } from '../utils';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
export const setNamespace = ({ commit }, namespace) => {
|
||||
commit(types.SET_NAMESPACE, namespace);
|
||||
};
|
||||
|
||||
export const setStorageKey = ({ commit }, key) => {
|
||||
commit(types.SET_STORAGE_KEY, key);
|
||||
};
|
||||
|
||||
export const toggleItemsListEditablity = ({ commit }) => {
|
||||
commit(types.TOGGLE_ITEMS_LIST_EDITABILITY);
|
||||
};
|
||||
|
||||
export const requestFrequentItems = ({ commit }) => {
|
||||
commit(types.REQUEST_FREQUENT_ITEMS);
|
||||
};
|
||||
export const receiveFrequentItemsSuccess = ({ commit }, data) => {
|
||||
commit(types.RECEIVE_FREQUENT_ITEMS_SUCCESS, data);
|
||||
};
|
||||
export const receiveFrequentItemsError = ({ commit }) => {
|
||||
commit(types.RECEIVE_FREQUENT_ITEMS_ERROR);
|
||||
};
|
||||
|
||||
export const fetchFrequentItems = ({ state, dispatch }) => {
|
||||
dispatch('requestFrequentItems');
|
||||
|
||||
if (AccessorUtilities.canUseLocalStorage()) {
|
||||
const storedFrequentItems = JSON.parse(localStorage.getItem(state.storageKey));
|
||||
|
||||
dispatch(
|
||||
'receiveFrequentItemsSuccess',
|
||||
!storedFrequentItems ? [] : getTopFrequentItems(storedFrequentItems),
|
||||
);
|
||||
} else {
|
||||
dispatch('receiveFrequentItemsError');
|
||||
}
|
||||
};
|
||||
|
||||
export const requestSearchedItems = ({ commit }) => {
|
||||
commit(types.REQUEST_SEARCHED_ITEMS);
|
||||
};
|
||||
export const receiveSearchedItemsSuccess = ({ commit }, data) => {
|
||||
commit(types.RECEIVE_SEARCHED_ITEMS_SUCCESS, data);
|
||||
};
|
||||
export const receiveSearchedItemsError = ({ commit }) => {
|
||||
commit(types.RECEIVE_SEARCHED_ITEMS_ERROR);
|
||||
};
|
||||
export const fetchSearchedItems = ({ state, dispatch }, searchQuery) => {
|
||||
dispatch('requestSearchedItems');
|
||||
|
||||
const params = {
|
||||
simple: true,
|
||||
per_page: 20,
|
||||
membership: isLoggedIn(),
|
||||
};
|
||||
|
||||
let searchFunction;
|
||||
if (state.namespace === 'projects') {
|
||||
searchFunction = getProjects;
|
||||
params.order_by = 'last_activity_at';
|
||||
} else {
|
||||
searchFunction = getGroups;
|
||||
}
|
||||
|
||||
return searchFunction(searchQuery, params)
|
||||
.then((results) => {
|
||||
dispatch('receiveSearchedItemsSuccess', results);
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch('receiveSearchedItemsError');
|
||||
});
|
||||
};
|
||||
|
||||
export const setSearchQuery = ({ commit, dispatch }, query) => {
|
||||
commit(types.SET_SEARCH_QUERY, query);
|
||||
|
||||
if (query) {
|
||||
dispatch('fetchSearchedItems', query);
|
||||
} else {
|
||||
dispatch('fetchFrequentItems');
|
||||
}
|
||||
};
|
||||
|
||||
export const removeFrequentItemSuccess = ({ commit }, itemId) => {
|
||||
commit(types.RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS, itemId);
|
||||
};
|
||||
|
||||
export const removeFrequentItemError = ({ commit }) => {
|
||||
commit(types.RECEIVE_REMOVE_FREQUENT_ITEM_ERROR);
|
||||
};
|
||||
|
||||
export const removeFrequentItem = ({ state, dispatch }, itemId) => {
|
||||
if (AccessorUtilities.canUseLocalStorage()) {
|
||||
try {
|
||||
const storedRawItems = JSON.parse(localStorage.getItem(state.storageKey));
|
||||
localStorage.setItem(
|
||||
state.storageKey,
|
||||
JSON.stringify(storedRawItems.filter((item) => item.id !== itemId)),
|
||||
);
|
||||
dispatch('removeFrequentItemSuccess', itemId);
|
||||
} catch {
|
||||
dispatch('removeFrequentItemError');
|
||||
}
|
||||
} else {
|
||||
dispatch('removeFrequentItemError');
|
||||
}
|
||||
};
|
||||
|
|
@ -1 +0,0 @@
|
|||
export const hasSearchQuery = (state) => state.searchQuery !== '';
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import { FREQUENT_ITEMS_DROPDOWNS } from '../constants';
|
||||
import * as actions from './actions';
|
||||
import * as getters from './getters';
|
||||
import mutations from './mutations';
|
||||
import state from './state';
|
||||
|
||||
export const createFrequentItemsModule = (initState = {}) => ({
|
||||
namespaced: true,
|
||||
actions,
|
||||
getters,
|
||||
mutations,
|
||||
state: state(initState),
|
||||
});
|
||||
|
||||
export const createStoreOptions = () => ({
|
||||
modules: FREQUENT_ITEMS_DROPDOWNS.reduce(
|
||||
(acc, { namespace, vuexModule }) =>
|
||||
Object.assign(acc, {
|
||||
[vuexModule]: createFrequentItemsModule({ dropdownType: namespace }),
|
||||
}),
|
||||
{},
|
||||
),
|
||||
});
|
||||
|
||||
export const createStore = () => {
|
||||
return new Vuex.Store(createStoreOptions());
|
||||
};
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
export const SET_NAMESPACE = 'SET_NAMESPACE';
|
||||
export const SET_STORAGE_KEY = 'SET_STORAGE_KEY';
|
||||
export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY';
|
||||
export const TOGGLE_ITEMS_LIST_EDITABILITY = 'TOGGLE_ITEMS_LIST_EDITABILITY';
|
||||
export const REQUEST_FREQUENT_ITEMS = 'REQUEST_FREQUENT_ITEMS';
|
||||
export const RECEIVE_FREQUENT_ITEMS_SUCCESS = 'RECEIVE_FREQUENT_ITEMS_SUCCESS';
|
||||
export const RECEIVE_FREQUENT_ITEMS_ERROR = 'RECEIVE_FREQUENT_ITEMS_ERROR';
|
||||
export const REQUEST_SEARCHED_ITEMS = 'REQUEST_SEARCHED_ITEMS';
|
||||
export const RECEIVE_SEARCHED_ITEMS_SUCCESS = 'RECEIVE_SEARCHED_ITEMS_SUCCESS';
|
||||
export const RECEIVE_SEARCHED_ITEMS_ERROR = 'RECEIVE_SEARCHED_ITEMS_ERROR';
|
||||
export const RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS = 'RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS';
|
||||
export const RECEIVE_REMOVE_FREQUENT_ITEM_ERROR = 'RECEIVE_REMOVE_FREQUENT_ITEM_ERROR';
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
[types.SET_NAMESPACE](state, namespace) {
|
||||
Object.assign(state, {
|
||||
namespace,
|
||||
});
|
||||
},
|
||||
[types.SET_STORAGE_KEY](state, storageKey) {
|
||||
Object.assign(state, {
|
||||
storageKey,
|
||||
});
|
||||
},
|
||||
[types.SET_SEARCH_QUERY](state, searchQuery) {
|
||||
const hasSearchQuery = searchQuery !== '';
|
||||
|
||||
Object.assign(state, {
|
||||
searchQuery,
|
||||
isLoadingItems: true,
|
||||
hasSearchQuery,
|
||||
});
|
||||
},
|
||||
[types.TOGGLE_ITEMS_LIST_EDITABILITY](state) {
|
||||
Object.assign(state, {
|
||||
isItemsListEditable: !state.isItemsListEditable,
|
||||
});
|
||||
},
|
||||
[types.REQUEST_FREQUENT_ITEMS](state) {
|
||||
Object.assign(state, {
|
||||
isLoadingItems: true,
|
||||
hasSearchQuery: false,
|
||||
});
|
||||
},
|
||||
[types.RECEIVE_FREQUENT_ITEMS_SUCCESS](state, rawItems) {
|
||||
Object.assign(state, {
|
||||
items: rawItems,
|
||||
isLoadingItems: false,
|
||||
hasSearchQuery: false,
|
||||
isFetchFailed: false,
|
||||
});
|
||||
},
|
||||
[types.RECEIVE_FREQUENT_ITEMS_ERROR](state) {
|
||||
Object.assign(state, {
|
||||
isLoadingItems: false,
|
||||
hasSearchQuery: false,
|
||||
isFetchFailed: true,
|
||||
});
|
||||
},
|
||||
[types.REQUEST_SEARCHED_ITEMS](state) {
|
||||
Object.assign(state, {
|
||||
isLoadingItems: true,
|
||||
hasSearchQuery: true,
|
||||
});
|
||||
},
|
||||
[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](state, results) {
|
||||
const rawItems = results.data;
|
||||
Object.assign(state, {
|
||||
items: rawItems.map((rawItem) => ({
|
||||
id: rawItem.id,
|
||||
name: rawItem.name,
|
||||
namespace: rawItem.name_with_namespace || rawItem.full_name,
|
||||
webUrl: rawItem.web_url,
|
||||
avatarUrl: rawItem.avatar_url,
|
||||
})),
|
||||
isLoadingItems: false,
|
||||
hasSearchQuery: true,
|
||||
isFetchFailed: false,
|
||||
});
|
||||
},
|
||||
[types.RECEIVE_SEARCHED_ITEMS_ERROR](state) {
|
||||
Object.assign(state, {
|
||||
isLoadingItems: false,
|
||||
hasSearchQuery: true,
|
||||
isFetchFailed: true,
|
||||
});
|
||||
},
|
||||
[types.RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS](state, itemId) {
|
||||
Object.assign(state, {
|
||||
items: state.items.filter((item) => item.id !== itemId),
|
||||
isItemRemovalFailed: false,
|
||||
});
|
||||
},
|
||||
[types.RECEIVE_REMOVE_FREQUENT_ITEM_ERROR](state) {
|
||||
Object.assign(state, {
|
||||
isItemRemovalFailed: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
export default ({ dropdownType = '' } = {}) => ({
|
||||
namespace: '',
|
||||
dropdownType,
|
||||
storageKey: '',
|
||||
searchQuery: '',
|
||||
isLoadingItems: false,
|
||||
isFetchFailed: false,
|
||||
isItemsListEditable: false,
|
||||
isItemRemovalFailed: false,
|
||||
items: [],
|
||||
});
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
|
||||
import { take } from 'lodash';
|
||||
import { sanitize } from '~/lib/dompurify';
|
||||
import { FREQUENT_ITEMS, FIFTEEN_MINUTES_IN_MS } from './constants';
|
||||
|
||||
export const isMobile = () => ['md', 'sm', 'xs'].includes(bp.getBreakpointSize());
|
||||
|
||||
export const getTopFrequentItems = (items) => {
|
||||
if (!items) {
|
||||
return [];
|
||||
}
|
||||
const frequentItemsCount = isMobile()
|
||||
? FREQUENT_ITEMS.LIST_COUNT_MOBILE
|
||||
: FREQUENT_ITEMS.LIST_COUNT_DESKTOP;
|
||||
|
||||
const frequentItems = items.filter((item) => item.frequency >= FREQUENT_ITEMS.ELIGIBLE_FREQUENCY);
|
||||
|
||||
if (!frequentItems || frequentItems.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
frequentItems.sort((itemA, itemB) => {
|
||||
// Sort all frequent items in decending order of frequency
|
||||
// and then by lastAccessedOn with recent most first
|
||||
if (itemA.frequency !== itemB.frequency) {
|
||||
return itemB.frequency - itemA.frequency;
|
||||
}
|
||||
if (itemA.lastAccessedOn !== itemB.lastAccessedOn) {
|
||||
return itemB.lastAccessedOn - itemA.lastAccessedOn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return take(frequentItems, frequentItemsCount);
|
||||
};
|
||||
|
||||
export const updateExistingFrequentItem = (frequentItem, item) => {
|
||||
// `frequentItem` comes from localStorage and it's possible it doesn't have a `lastAccessedOn`
|
||||
const neverAccessed = !frequentItem.lastAccessedOn;
|
||||
const shouldUpdate =
|
||||
neverAccessed ||
|
||||
Math.abs(item.lastAccessedOn - frequentItem.lastAccessedOn) / FIFTEEN_MINUTES_IN_MS > 1;
|
||||
|
||||
return {
|
||||
...item,
|
||||
frequency: shouldUpdate ? frequentItem.frequency + 1 : frequentItem.frequency,
|
||||
lastAccessedOn: shouldUpdate ? Date.now() : frequentItem.lastAccessedOn,
|
||||
};
|
||||
};
|
||||
|
||||
export const sanitizeItem = (item) => {
|
||||
// Only sanitize if the key exists on the item
|
||||
const maybeSanitize = (key) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(item, key)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { [key]: sanitize(item[key].toString(), { ALLOWED_TAGS: [] }) };
|
||||
};
|
||||
|
||||
return {
|
||||
...item,
|
||||
...maybeSanitize('name'),
|
||||
...maybeSanitize('namespace'),
|
||||
};
|
||||
};
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
import { sprintf, s__, __ } from '~/locale';
|
||||
import { getParameterValues } from '~/lib/utils/url_utility';
|
||||
|
||||
import ImportDetailsTable from '~/import/details/components/import_details_table.vue';
|
||||
|
||||
export default {
|
||||
name: 'BulkImportDetailsApp',
|
||||
components: { ImportDetailsTable },
|
||||
components: {
|
||||
ImportDetailsTable,
|
||||
},
|
||||
|
||||
fields: [
|
||||
{
|
||||
|
|
@ -28,12 +32,25 @@ export default {
|
|||
],
|
||||
|
||||
LOCAL_STORAGE_KEY: 'gl-bulk-import-details-page-size',
|
||||
|
||||
gitlabLogo: window.gon.gitlab_logo,
|
||||
|
||||
computed: {
|
||||
title() {
|
||||
const id = getParameterValues('entity_id')[0];
|
||||
|
||||
return sprintf(s__('BulkImport|Items that failed to be imported for %{id}'), { id });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>{{ s__('Import|GitLab Migration details') }}</h1>
|
||||
<h1 class="gl-font-size-h1 gl-my-0 gl-py-4 gl-display-flex gl-align-items-center gl-gap-3">
|
||||
<img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6" />
|
||||
<span>{{ title }}</span>
|
||||
</h1>
|
||||
|
||||
<import-details-table
|
||||
bulk-import
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<gl-table :fields="fields" :items="items" class="gl-mt-5" :busy="loading" show-empty>
|
||||
<gl-table :fields="fields" :items="items" :busy="loading" show-empty>
|
||||
<template #table-busy>
|
||||
<gl-loading-icon size="lg" class="gl-my-5" />
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -591,11 +591,11 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="gl-display-flex gl-align-items-center gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1"
|
||||
class="gl-display-flex gl-align-items-center gl-border-solid gl-border-gray-100 gl-border-0 gl-border-b-1"
|
||||
>
|
||||
<h1 class="gl-my-0 gl-py-4 gl-font-size-h1gl-display-flex">
|
||||
<img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" />
|
||||
{{ s__('BulkImport|Import groups from GitLab') }}
|
||||
<h1 class="gl-font-size-h1 gl-my-0 gl-py-4 gl-display-flex gl-align-items-center gl-gap-3">
|
||||
<img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6" />
|
||||
<span>{{ s__('BulkImport|Import groups by direct transfer') }}</span>
|
||||
</h1>
|
||||
<gl-link :href="historyPath" class="gl-ml-auto">{{ s__('BulkImport|History') }}</gl-link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ export default {
|
|||
:loading="isToggleStateButtonLoading"
|
||||
placement="right"
|
||||
>
|
||||
<template v-if="showMovedSidebarOptions">
|
||||
<template v-if="showMovedSidebarOptions && !glFeatures.notificationsTodosButtons">
|
||||
<sidebar-subscriptions-widget
|
||||
:iid="String(iid)"
|
||||
:full-path="fullPath"
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
import { mapValues, isString } from 'lodash';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
|
||||
export const REQUIRE_STRING_ERROR_MESSAGE =
|
||||
'`vuex_module_mappers` can only be used with an array of strings, or an object with string values. Consider using the regular `vuex` map helpers instead.';
|
||||
|
||||
const normalizeFieldsToObject = (fields) => {
|
||||
return Array.isArray(fields)
|
||||
? fields.reduce((acc, key) => Object.assign(acc, { [key]: key }), {})
|
||||
: fields;
|
||||
};
|
||||
|
||||
const mapVuexModuleFields = ({ namespaceSelector, fields, vuexHelper, selector } = {}) => {
|
||||
// The `vuexHelper` needs an object which maps keys to field selector functions.
|
||||
const map = mapValues(normalizeFieldsToObject(fields), (value) => {
|
||||
if (!isString(value)) {
|
||||
throw new Error(REQUIRE_STRING_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
// We need to use a good ol' function to capture the right "this".
|
||||
return function mappedFieldSelector(...args) {
|
||||
const namespace = namespaceSelector(this);
|
||||
|
||||
return selector(namespace, value, ...args);
|
||||
};
|
||||
});
|
||||
|
||||
return vuexHelper(map);
|
||||
};
|
||||
|
||||
/**
|
||||
* Like `mapState`, but takes a function in the first param for selecting a namespace.
|
||||
*
|
||||
* ```
|
||||
* computed: {
|
||||
* ...mapVuexModuleState(vm => vm.vuexModule, ['foo']),
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {Function} namespaceSelector
|
||||
* @param {Array|Object} fields
|
||||
*/
|
||||
export const mapVuexModuleState = (namespaceSelector, fields) =>
|
||||
mapVuexModuleFields({
|
||||
namespaceSelector,
|
||||
fields,
|
||||
vuexHelper: mapState,
|
||||
selector: (namespace, value, state) => state[namespace][value],
|
||||
});
|
||||
|
||||
/**
|
||||
* Like `mapActions`, but takes a function in the first param for selecting a namespace.
|
||||
*
|
||||
* ```
|
||||
* methods: {
|
||||
* ...mapVuexModuleActions(vm => vm.vuexModule, ['fetchFoos']),
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {Function} namespaceSelector
|
||||
* @param {Array|Object} fields
|
||||
*/
|
||||
export const mapVuexModuleActions = (namespaceSelector, fields) =>
|
||||
mapVuexModuleFields({
|
||||
namespaceSelector,
|
||||
fields,
|
||||
vuexHelper: mapActions,
|
||||
selector: (namespace, value, dispatch, ...args) => dispatch(`${namespace}/${value}`, ...args),
|
||||
});
|
||||
|
||||
/**
|
||||
* Like `mapGetters`, but takes a function in the first param for selecting a namespace.
|
||||
*
|
||||
* ```
|
||||
* computed: {
|
||||
* ...mapGetters(vm => vm.vuexModule, ['hasSearchInfo']),
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {Function} namespaceSelector
|
||||
* @param {Array|Object} fields
|
||||
*/
|
||||
export const mapVuexModuleGetters = (namespaceSelector, fields) =>
|
||||
mapVuexModuleFields({
|
||||
namespaceSelector,
|
||||
fields,
|
||||
// `mapGetters` does not let us pass an object which maps to functions. Thankfully `mapState` does
|
||||
// and gives us access to the getters.
|
||||
vuexHelper: mapState,
|
||||
selector: (namespace, value, state, getters) => getters[`${namespace}/${value}`],
|
||||
});
|
||||
|
|
@ -233,14 +233,11 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-display-flex gl-align-items-center"
|
||||
>
|
||||
<h1 class="gl-my-0 gl-py-4 gl-font-size-h1">
|
||||
<img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" />
|
||||
{{ s__('BulkImport|GitLab Migration history') }}
|
||||
</h1>
|
||||
</div>
|
||||
<h1 class="gl-font-size-h1 gl-my-0 gl-py-4 gl-display-flex gl-align-items-center gl-gap-3">
|
||||
<img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6" />
|
||||
<span>{{ s__('BulkImport|Direct transfer history') }}</span>
|
||||
</h1>
|
||||
|
||||
<gl-loading-icon v-if="loading" size="lg" class="gl-mt-5" />
|
||||
<gl-empty-state
|
||||
v-else-if="!hasHistoryItems"
|
||||
|
|
|
|||
|
|
@ -154,9 +154,7 @@ export default {
|
|||
</gl-link>
|
||||
<span v-else>{{ item.import_url }}</span>
|
||||
</template>
|
||||
<span v-else>{{
|
||||
s__('BulkImport|Template / File-based import / GitLab Migration')
|
||||
}}</span>
|
||||
<span v-else>{{ s__('BulkImport|Template / File-based import / Direct transfer') }}</span>
|
||||
</template>
|
||||
<template #cell(destination)="{ item }">
|
||||
<gl-link :href="item.http_url_to_repo">
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import ServiceDeskSetting from './service_desk_setting.vue';
|
|||
const CustomEmailWrapper = () => import('./custom_email_wrapper.vue');
|
||||
|
||||
export default {
|
||||
serviceDeskEmailHelpPath: helpPagePath('/user/project/service_desk.html', {
|
||||
serviceDeskEmailHelpPath: helpPagePath('/user/project/service_desk/configure.html', {
|
||||
anchor: 'use-an-additional-service-desk-alias-email',
|
||||
}),
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -132,12 +132,12 @@ export default {
|
|||
return this.serviceDeskEmail && this.serviceDeskEmail !== this.incomingEmail;
|
||||
},
|
||||
emailSuffixHelpUrl() {
|
||||
return helpPagePath('user/project/service_desk.html', {
|
||||
return helpPagePath('user/project/service_desk/configure.html', {
|
||||
anchor: 'configure-a-suffix-for-service-desk-alias-email',
|
||||
});
|
||||
},
|
||||
serviceDeskEmailAddressHelpUrl() {
|
||||
return helpPagePath('user/project/service_desk.html', {
|
||||
return helpPagePath('user/project/service_desk/configure.html', {
|
||||
anchor: 'use-an-additional-service-desk-alias-email',
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -57,3 +57,15 @@ export const DROPDOWN_Y_OFFSET = 4;
|
|||
export const NAV_ITEM_LINK_ACTIVE_CLASS = 'gl-bg-t-gray-a-08';
|
||||
|
||||
export const IMPERSONATING_OFFSET = 34;
|
||||
|
||||
// Frequent items constants
|
||||
export const FREQUENT_ITEMS = {
|
||||
MAX_COUNT: 20,
|
||||
ELIGIBLE_FREQUENCY: 3,
|
||||
};
|
||||
|
||||
export const FIFTEEN_MINUTES_IN_MS = 900000;
|
||||
|
||||
export const STORAGE_KEY = {
|
||||
projects: 'frequent-projects',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import AccessorUtilities from '~/lib/utils/accessor';
|
||||
import { FREQUENT_ITEMS, FIFTEEN_MINUTES_IN_MS } from '~/frequent_items/constants';
|
||||
import { FREQUENT_ITEMS, FIFTEEN_MINUTES_IN_MS } from '~/super_sidebar/constants';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export default {
|
|||
placement="right"
|
||||
searchable
|
||||
size="small"
|
||||
class="comment-template-dropdown gl-mr-3"
|
||||
class="comment-template-dropdown gl-mr-2"
|
||||
positioning-strategy="fixed"
|
||||
:searching="$apollo.queries.savedReplies.loading"
|
||||
@shown="fetchCommentTemplates"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { updateText } from '~/lib/utils/text_markdown';
|
|||
import ToolbarButton from './toolbar_button.vue';
|
||||
import DrawioToolbarButton from './drawio_toolbar_button.vue';
|
||||
import CommentTemplatesDropdown from './comment_templates_dropdown.vue';
|
||||
import HeaderDivider from './header_divider.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -30,6 +31,7 @@ export default {
|
|||
DrawioToolbarButton,
|
||||
CommentTemplatesDropdown,
|
||||
AiActionsDropdown: () => import('ee_component/ai/components/ai_actions_dropdown.vue'),
|
||||
HeaderDivider,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
|
@ -261,249 +263,271 @@ export default {
|
|||
<div class="gl-display-flex gl-align-items-center gl-flex-wrap">
|
||||
<div
|
||||
data-testid="md-header-toolbar"
|
||||
class="md-header-toolbar gl-display-flex gl-py-3 gl-flex-wrap gl-row-gap-3"
|
||||
class="md-header-toolbar gl-display-flex gl-py-3 gl-row-gap-2 gl-flex-grow-1 gl-align-items-flex-start"
|
||||
>
|
||||
<gl-button
|
||||
v-if="enablePreview"
|
||||
data-testid="preview-toggle"
|
||||
:value="previewMarkdown ? 'preview' : 'edit'"
|
||||
:label="$options.i18n.previewTabTitle"
|
||||
class="js-md-preview-button gl-flex-direction-row-reverse gl-align-items-center gl-font-weight-normal! gl-mr-2"
|
||||
size="small"
|
||||
category="tertiary"
|
||||
@click="switchPreview"
|
||||
>{{ previewMarkdown ? $options.i18n.hidePreview : $options.i18n.preview }}</gl-button
|
||||
>
|
||||
<template v-if="!previewMarkdown && canSuggest">
|
||||
<toolbar-button
|
||||
ref="suggestButton"
|
||||
:tag="mdSuggestion"
|
||||
:prepend="true"
|
||||
:button-title="__('Insert suggestion')"
|
||||
:cursor-offset="4"
|
||||
:tag-content="lineContent"
|
||||
tracking-property="codeSuggestion"
|
||||
icon="doc-code"
|
||||
data-testid="suggestion-button"
|
||||
class="js-suggestion-btn"
|
||||
@click="handleSuggestDismissed"
|
||||
/>
|
||||
<gl-popover
|
||||
v-if="suggestPopoverVisible"
|
||||
:target="$refs.suggestButton.$el"
|
||||
:css-classes="['diff-suggest-popover']"
|
||||
placement="bottom"
|
||||
:show="suggestPopoverVisible"
|
||||
triggers=""
|
||||
<div class="gl-display-flex gl-flex-wrap gl-row-gap-2">
|
||||
<gl-button
|
||||
v-if="enablePreview"
|
||||
data-testid="preview-toggle"
|
||||
:value="previewMarkdown ? 'preview' : 'edit'"
|
||||
:label="$options.i18n.previewTabTitle"
|
||||
class="js-md-preview-button gl-flex-direction-row-reverse gl-align-items-center gl-font-weight-normal!"
|
||||
size="small"
|
||||
category="tertiary"
|
||||
@click="switchPreview"
|
||||
>{{ previewMarkdown ? $options.i18n.hidePreview : $options.i18n.preview }}</gl-button
|
||||
>
|
||||
<strong>{{ __('New! Suggest changes directly') }}</strong>
|
||||
<p class="mb-2">
|
||||
{{
|
||||
__(
|
||||
'Suggest code changes which can be immediately applied in one click. Try it out!',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<gl-button
|
||||
variant="confirm"
|
||||
category="primary"
|
||||
size="small"
|
||||
data-testid="dismiss-suggestion-popover-button"
|
||||
@click="handleSuggestDismissed"
|
||||
<template v-if="!previewMarkdown && canSuggest">
|
||||
<div class="gl-display-flex gl-row-gap-2">
|
||||
<header-divider :preview-markdown="previewMarkdown" />
|
||||
<toolbar-button
|
||||
ref="suggestButton"
|
||||
:tag="mdSuggestion"
|
||||
:prepend="true"
|
||||
:button-title="__('Insert suggestion')"
|
||||
:cursor-offset="4"
|
||||
:tag-content="lineContent"
|
||||
tracking-property="codeSuggestion"
|
||||
icon="doc-code"
|
||||
data-testid="suggestion-button"
|
||||
class="js-suggestion-btn"
|
||||
@click="handleSuggestDismissed"
|
||||
/>
|
||||
<gl-popover
|
||||
v-if="suggestPopoverVisible"
|
||||
:target="$refs.suggestButton.$el"
|
||||
:css-classes="['diff-suggest-popover']"
|
||||
placement="bottom"
|
||||
:show="suggestPopoverVisible"
|
||||
triggers=""
|
||||
>
|
||||
<strong>{{ __('New! Suggest changes directly') }}</strong>
|
||||
<p class="mb-2">
|
||||
{{
|
||||
__(
|
||||
'Suggest code changes which can be immediately applied in one click. Try it out!',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<gl-button
|
||||
variant="confirm"
|
||||
category="primary"
|
||||
size="small"
|
||||
data-testid="dismiss-suggestion-popover-button"
|
||||
@click="handleSuggestDismissed"
|
||||
>
|
||||
{{ __('Got it') }}
|
||||
</gl-button>
|
||||
</gl-popover>
|
||||
</div>
|
||||
</template>
|
||||
<div class="gl-display-flex gl-row-gap-2">
|
||||
<div
|
||||
v-if="!previewMarkdown && editorAiActions.length"
|
||||
class="gl-display-flex gl-row-gap-2"
|
||||
>
|
||||
{{ __('Got it') }}
|
||||
</gl-button>
|
||||
</gl-popover>
|
||||
</template>
|
||||
<ai-actions-dropdown
|
||||
v-if="!previewMarkdown && editorAiActions.length"
|
||||
:actions="editorAiActions"
|
||||
@input="insertAIAction"
|
||||
@replace="replaceTextarea"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-show="!previewMarkdown"
|
||||
tag="**"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), {
|
||||
modifierKey,
|
||||
}) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
"
|
||||
:shortcuts="$options.shortcuts.bold"
|
||||
icon="bold"
|
||||
tracking-property="bold"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-show="!previewMarkdown"
|
||||
tag="_"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), {
|
||||
modifierKey,
|
||||
}) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
"
|
||||
:shortcuts="$options.shortcuts.italic"
|
||||
icon="italic"
|
||||
tracking-property="italic"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('strikethrough')"
|
||||
v-show="!previewMarkdown"
|
||||
tag="~~"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}%{shiftKey}X)'), {
|
||||
modifierKey,
|
||||
shiftKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
|
||||
})
|
||||
"
|
||||
:shortcuts="$options.shortcuts.strikethrough"
|
||||
icon="strikethrough"
|
||||
tracking-property="strike"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('quote')"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
:tag="tag"
|
||||
:button-title="__('Insert a quote')"
|
||||
icon="quote"
|
||||
tracking-property="blockquote"
|
||||
@click="handleQuote"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-show="!previewMarkdown"
|
||||
tag="`"
|
||||
tag-block="```"
|
||||
:button-title="__('Insert code')"
|
||||
icon="code"
|
||||
tracking-property="code"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-show="!previewMarkdown"
|
||||
tag="[{text}](url)"
|
||||
tag-select="url"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), {
|
||||
modifierKey,
|
||||
}) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
"
|
||||
:shortcuts="$options.shortcuts.link"
|
||||
icon="link"
|
||||
tracking-property="link"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('bullet-list')"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
tag="- "
|
||||
:button-title="__('Add a bullet list')"
|
||||
icon="list-bulleted"
|
||||
tracking-property="bulletList"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('numbered-list')"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
tag="1. "
|
||||
:button-title="__('Add a numbered list')"
|
||||
icon="list-numbered"
|
||||
tracking-property="orderedList"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('task-list')"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
tag="- [ ] "
|
||||
:button-title="__('Add a checklist')"
|
||||
icon="list-task"
|
||||
tracking-property="taskList"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('indent')"
|
||||
v-show="!previewMarkdown"
|
||||
class="gl-display-none"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Indent line (%{modifierKey}])'), {
|
||||
modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
|
||||
})
|
||||
"
|
||||
:shortcuts="$options.shortcuts.indent"
|
||||
command="indentLines"
|
||||
icon="list-indent"
|
||||
tracking-property="indent"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('outdent')"
|
||||
v-show="!previewMarkdown"
|
||||
class="gl-display-none"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Outdent line (%{modifierKey}[)'), {
|
||||
modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
|
||||
})
|
||||
"
|
||||
:shortcuts="$options.shortcuts.outdent"
|
||||
command="outdentLines"
|
||||
icon="list-outdent"
|
||||
tracking-property="outdent"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('collapsible-section')"
|
||||
v-show="!previewMarkdown"
|
||||
:tag="mdCollapsibleSection"
|
||||
:prepend="true"
|
||||
tag-select="Click to expand"
|
||||
:button-title="__('Add a collapsible section')"
|
||||
icon="details-block"
|
||||
tracking-property="details"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('table')"
|
||||
v-show="!previewMarkdown"
|
||||
:tag="mdTable"
|
||||
:prepend="true"
|
||||
:button-title="__('Add a table')"
|
||||
icon="table"
|
||||
tracking-property="table"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!previewMarkdown && !restrictedToolBarItems.includes('attach-file')"
|
||||
data-testid="button-attach-file"
|
||||
data-button-type="attach-file"
|
||||
:button-title="__('Attach a file or image')"
|
||||
icon="paperclip"
|
||||
class="gl-mr-3"
|
||||
tracking-property="upload"
|
||||
@click="handleAttachFile"
|
||||
/>
|
||||
<drawio-toolbar-button
|
||||
v-if="!previewMarkdown && drawioEnabled"
|
||||
:uploads-path="uploadsPath"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
/>
|
||||
<!-- TODO Add icon and trigger functionality from here -->
|
||||
<toolbar-button
|
||||
v-if="supportsQuickActions"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
tag="/"
|
||||
:button-title="__('Add a quick action')"
|
||||
icon="quick-actions"
|
||||
tracking-property="quickAction"
|
||||
/>
|
||||
<comment-templates-dropdown
|
||||
v-if="!previewMarkdown && newCommentTemplatePath"
|
||||
:new-comment-template-path="newCommentTemplatePath"
|
||||
@select="insertSavedReply"
|
||||
/>
|
||||
<div v-if="!previewMarkdown" class="full-screen">
|
||||
<header-divider :preview-markdown="previewMarkdown" />
|
||||
<ai-actions-dropdown
|
||||
:actions="editorAiActions"
|
||||
@input="insertAIAction"
|
||||
@replace="replaceTextarea"
|
||||
/>
|
||||
</div>
|
||||
<header-divider :preview-markdown="previewMarkdown" />
|
||||
</div>
|
||||
<toolbar-button
|
||||
v-show="!previewMarkdown"
|
||||
tag="**"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), {
|
||||
modifierKey,
|
||||
}) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
"
|
||||
:shortcuts="$options.shortcuts.bold"
|
||||
icon="bold"
|
||||
tracking-property="bold"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-show="!previewMarkdown"
|
||||
tag="_"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), {
|
||||
modifierKey,
|
||||
}) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
"
|
||||
:shortcuts="$options.shortcuts.italic"
|
||||
icon="italic"
|
||||
tracking-property="italic"
|
||||
/>
|
||||
<div class="gl-display-flex gl-row-gap-2">
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('strikethrough')"
|
||||
v-show="!previewMarkdown"
|
||||
tag="~~"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}%{shiftKey}X)'), {
|
||||
modifierKey,
|
||||
shiftKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
|
||||
})
|
||||
"
|
||||
:shortcuts="$options.shortcuts.strikethrough"
|
||||
icon="strikethrough"
|
||||
tracking-property="strike"
|
||||
/>
|
||||
<header-divider :preview-markdown="previewMarkdown" />
|
||||
</div>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('quote')"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
:tag="tag"
|
||||
:button-title="__('Insert a quote')"
|
||||
icon="quote"
|
||||
tracking-property="blockquote"
|
||||
@click="handleQuote"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-show="!previewMarkdown"
|
||||
tag="`"
|
||||
tag-block="```"
|
||||
:button-title="__('Insert code')"
|
||||
icon="code"
|
||||
tracking-property="code"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-show="!previewMarkdown"
|
||||
tag="[{text}](url)"
|
||||
tag-select="url"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), {
|
||||
modifierKey,
|
||||
}) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
"
|
||||
:shortcuts="$options.shortcuts.link"
|
||||
icon="link"
|
||||
tracking-property="link"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('bullet-list')"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
tag="- "
|
||||
:button-title="__('Add a bullet list')"
|
||||
icon="list-bulleted"
|
||||
tracking-property="bulletList"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('numbered-list')"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
tag="1. "
|
||||
:button-title="__('Add a numbered list')"
|
||||
icon="list-numbered"
|
||||
tracking-property="orderedList"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('task-list')"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
tag="- [ ] "
|
||||
:button-title="__('Add a checklist')"
|
||||
icon="list-task"
|
||||
tracking-property="taskList"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('indent')"
|
||||
v-show="!previewMarkdown"
|
||||
class="gl-display-none"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Indent line (%{modifierKey}])'), {
|
||||
modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
|
||||
})
|
||||
"
|
||||
:shortcuts="$options.shortcuts.indent"
|
||||
command="indentLines"
|
||||
icon="list-indent"
|
||||
tracking-property="indent"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('outdent')"
|
||||
v-show="!previewMarkdown"
|
||||
class="gl-display-none"
|
||||
:button-title="
|
||||
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
|
||||
sprintf(s__('MarkdownEditor|Outdent line (%{modifierKey}[)'), {
|
||||
modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
|
||||
})
|
||||
"
|
||||
:shortcuts="$options.shortcuts.outdent"
|
||||
command="outdentLines"
|
||||
icon="list-outdent"
|
||||
tracking-property="outdent"
|
||||
/>
|
||||
<div class="gl-display-flex gl-row-gap-2">
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('collapsible-section')"
|
||||
v-show="!previewMarkdown"
|
||||
:tag="mdCollapsibleSection"
|
||||
:prepend="true"
|
||||
tag-select="Click to expand"
|
||||
:button-title="__('Add a collapsible section')"
|
||||
icon="details-block"
|
||||
tracking-property="details"
|
||||
/>
|
||||
<header-divider :preview-markdown="previewMarkdown" />
|
||||
</div>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('table')"
|
||||
v-show="!previewMarkdown"
|
||||
:tag="mdTable"
|
||||
:prepend="true"
|
||||
:button-title="__('Add a table')"
|
||||
icon="table"
|
||||
tracking-property="table"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="!previewMarkdown && !restrictedToolBarItems.includes('attach-file')"
|
||||
data-testid="button-attach-file"
|
||||
data-button-type="attach-file"
|
||||
:button-title="__('Attach a file or image')"
|
||||
icon="paperclip"
|
||||
class="gl-mr-2"
|
||||
tracking-property="upload"
|
||||
@click="handleAttachFile"
|
||||
/>
|
||||
<drawio-toolbar-button
|
||||
v-if="!previewMarkdown && drawioEnabled"
|
||||
:uploads-path="uploadsPath"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
/>
|
||||
<!-- TODO Add icon and trigger functionality from here -->
|
||||
<toolbar-button
|
||||
v-if="supportsQuickActions"
|
||||
v-show="!previewMarkdown"
|
||||
:prepend="true"
|
||||
tag="/"
|
||||
:button-title="__('Add a quick action')"
|
||||
icon="quick-actions"
|
||||
tracking-property="quickAction"
|
||||
/>
|
||||
<comment-templates-dropdown
|
||||
v-if="!previewMarkdown && newCommentTemplatePath"
|
||||
:new-comment-template-path="newCommentTemplatePath"
|
||||
@select="insertSavedReply"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="!previewMarkdown"
|
||||
class="full-screen gl-flex-grow-1 gl-justify-content-end gl-display-flex"
|
||||
>
|
||||
<toolbar-button
|
||||
v-if="!restrictedToolBarItems.includes('full-screen')"
|
||||
class="js-zen-enter"
|
||||
class="js-zen-enter gl-mr-0!"
|
||||
icon="maximize"
|
||||
:button-title="__('Go full screen')"
|
||||
:prepend="true"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
previewMarkdown: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="!previewMarkdown" class="md-toolbar-divider gl-display-flex gl-py-2">
|
||||
<div class="gl-border-l gl-pl-3 gl-ml-2"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -111,7 +111,7 @@ export default {
|
|||
type="button"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
class="js-md gl-mr-3"
|
||||
class="js-md gl-mr-2"
|
||||
data-container="body"
|
||||
@click="$emit('click', $event)"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
@import './pages/hierarchy';
|
||||
@import './pages/issues';
|
||||
@import './pages/note_form';
|
||||
@import './pages/notes';
|
||||
@import './pages/pipelines';
|
||||
@import './pages/profile';
|
||||
@import './pages/registry';
|
||||
|
|
|
|||
|
|
@ -836,95 +836,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.frequent-items-dropdown-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: $grid-size * 40;
|
||||
|
||||
.frequent-items-dropdown-content {
|
||||
@include gl-pt-3;
|
||||
}
|
||||
|
||||
.loading-animation {
|
||||
color: $gray-950;
|
||||
}
|
||||
|
||||
.frequent-items-dropdown-content {
|
||||
position: relative;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.section-header,
|
||||
.frequent-items-list-container li.section-empty {
|
||||
color: $gl-text-color-secondary;
|
||||
font-size: $gl-font-size;
|
||||
}
|
||||
|
||||
.frequent-items-list-container {
|
||||
padding: 8px 0;
|
||||
overflow-y: auto;
|
||||
|
||||
li.section-empty.section-failure {
|
||||
color: $red-700;
|
||||
}
|
||||
|
||||
.frequent-items-list-item-container .gl-button {
|
||||
&:active,
|
||||
&:focus,
|
||||
&:focus:active,
|
||||
&.is-focused {
|
||||
@include gl-focus($inset: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-weight: 700;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.frequent-items-list-item-container {
|
||||
.frequent-items-item-metadata-container {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.frequent-items-item-title,
|
||||
.frequent-items-item-namespace {
|
||||
max-width: 220px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.frequent-items-item-title {
|
||||
font-size: $gl-font-size;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.frequent-items-item-namespace {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
.frequent-items-item-metadata-container {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.frequent-items-item-title,
|
||||
.frequent-items-item-namespace {
|
||||
max-width: 250px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-content-faded-mask {
|
||||
position: relative;
|
||||
|
||||
|
|
|
|||
|
|
@ -249,10 +249,6 @@
|
|||
font-size: 10px;
|
||||
}
|
||||
|
||||
.frequent-items-item-select-holder {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.impersonation i {
|
||||
color: $red-500;
|
||||
}
|
||||
|
|
@ -523,16 +519,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.top-nav-container-view {
|
||||
.gl-dropdown & .gl-search-box-by-type {
|
||||
@include gl-m-0;
|
||||
}
|
||||
|
||||
.frequent-items-list-item-container > a:hover {
|
||||
background-color: $nav-active-bg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.top-nav-toggle {
|
||||
.dropdown-chevron {
|
||||
top: 0;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
@import 'framework/notes';
|
||||
@import 'framework/buttons';
|
||||
|
||||
$avatar-icon-size: 2rem;
|
||||
$avatar-m-top: 0.5rem;
|
||||
$avatar-m-ratio: 2;
|
||||
|
|
@ -985,7 +989,7 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
|
||||
.disabled-comment {
|
||||
background-color: $gray-light;
|
||||
border-radius: $border-radius-base;
|
||||
border-radius: $gl-border-radius-base;
|
||||
border: 1px solid $border-gray-normal;
|
||||
color: $note-disabled-comment-color;
|
||||
padding: $gl-padding-8 0;
|
||||
|
|
@ -13,6 +13,21 @@ module Packages
|
|||
project.packages.installable
|
||||
end
|
||||
|
||||
# /!\ This function doesn't check user permissions
|
||||
# at the package level.
|
||||
def packages_for(user, within_group:)
|
||||
return ::Packages::Package.none unless within_group
|
||||
return ::Packages::Package.none unless Ability.allowed?(user, :read_group, within_group)
|
||||
|
||||
projects = if user.is_a?(DeployToken)
|
||||
user.accessible_projects
|
||||
else
|
||||
within_group.all_projects
|
||||
end
|
||||
|
||||
::Packages::Package.for_projects(projects).installable
|
||||
end
|
||||
|
||||
def packages_visible_to_user(user, within_group:, with_package_registry_enabled: false)
|
||||
return ::Packages::Package.none unless within_group
|
||||
return ::Packages::Package.none unless Ability.allowed?(user, :read_group, within_group)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
module Packages
|
||||
module Maven
|
||||
class PackageFinder < ::Packages::GroupOrProjectPackageFinder
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
def execute
|
||||
packages
|
||||
end
|
||||
|
|
@ -15,6 +17,15 @@ module Packages
|
|||
|
||||
matching_packages
|
||||
end
|
||||
|
||||
override :group_packages
|
||||
def group_packages
|
||||
if Feature.enabled?(:maven_remove_permissions_check_from_finder, @project_or_group)
|
||||
packages_for(@current_user, within_group: @project_or_group)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module JsonHelper
|
||||
# These two JSON helpers are short-form wrappers for the Gitlab::Json
|
||||
# class, which should be used in place of .to_json calls or calls to
|
||||
# the JSON class.
|
||||
def json_generate(...)
|
||||
Gitlab::Json.generate(...)
|
||||
end
|
||||
|
||||
def json_parse(...)
|
||||
Gitlab::Json.parse(...)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
- page_title s_('WorkItem|Work items')
|
||||
- add_page_specific_style 'page_bundles/issuable_list'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
|
||||
.js-work-items-list-root{ data: work_items_list_data(@group) }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
- add_to_breadcrumbs _('New group'), new_group_path
|
||||
- add_to_breadcrumbs _('Import group'), new_group_path(anchor: 'import-group-pane')
|
||||
- page_title s_('Import|GitLab Migration details')
|
||||
- add_to_breadcrumbs s_('BulkImport|Direct transfer history'), history_import_bulk_imports_path
|
||||
- if params[:id].present?
|
||||
- add_to_breadcrumbs params[:id], history_import_bulk_imports_path(bulk_import_id: params[:id])
|
||||
- page_title format(s_('Import|Failures for %{id}'), id: params[:entity_id])
|
||||
|
||||
.js-bulk-import-details
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
- add_to_breadcrumbs _('New group'), new_group_path
|
||||
- add_to_breadcrumbs _('Import group'), new_group_path(anchor: 'import-group-pane')
|
||||
- if params[:bulk_import_id].present?
|
||||
- add_to_breadcrumbs s_('BulkImport|Direct transfer history'), history_import_bulk_imports_path
|
||||
- breadcrumb_title params[:bulk_import_id]
|
||||
- page_title s_('BulkImport|Direct transfer history')
|
||||
- add_page_specific_style 'page_bundles/import'
|
||||
- page_title _('Import history')
|
||||
|
||||
#import-history-mount-element{ data: { details_path: details_import_bulk_imports_path, realtime_changes_path: realtime_changes_import_bulk_imports_path(format: :json) } }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
- add_to_breadcrumbs _('New group'), new_group_path
|
||||
- add_to_breadcrumbs _('Import group'), new_group_path(anchor: 'import-group-pane')
|
||||
- breadcrumb_title s_('BulkImport|Direct transfer')
|
||||
- page_title s_('BulkImport|Import groups by direct transfer')
|
||||
- add_page_specific_style 'page_bundles/import'
|
||||
- page_title _('Import groups')
|
||||
|
||||
#import-groups-mount-element{ data: { status_path: status_import_bulk_imports_path(format: :json),
|
||||
default_target_namespace: @namespace&.id,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
- site_name = _('GitLab')
|
||||
- omit_og = sign_in_with_redirect?
|
||||
|
||||
-# This is a temporary place for the page specific style migrations to be included on all pages like page_specific_files
|
||||
- if Feature.disabled?(:page_specific_styles, current_user)
|
||||
- add_page_specific_style('page_bundles/notes')
|
||||
|
||||
%head{ omit_og ? { } : { prefix: "og: http://ogp.me/ns#" } }
|
||||
%meta{ charset: "utf-8" }
|
||||
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Service Desk')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
- link_start = "<a href='#{help_page_path('user/project/service_desk')}' target='_blank' rel='noopener noreferrer'>".html_safe
|
||||
- link_start = "<a href='#{help_page_path('user/project/service_desk/index')}' target='_blank' rel='noopener noreferrer'>".html_safe
|
||||
%p.gl-text-secondary= _('Enable and disable Service Desk. Some additional configuration might be required. %{link_start}Learn more%{link_end}.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
|
||||
.settings-content
|
||||
- if ::Gitlab::ServiceDesk.supported?
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
- page_title "#{@commit.title} (#{@commit.short_id})", _('Commits')
|
||||
- page_description @commit.description
|
||||
- add_page_specific_style 'page_bundles/pipelines'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
|
||||
.container-fluid{ class: [container_class] }
|
||||
= render "commit_box"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
- add_page_specific_style 'page_bundles/tree'
|
||||
- add_page_specific_style 'page_bundles/merge_request'
|
||||
- add_page_specific_style 'page_bundles/projects'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
- page_title _("Commits"), @ref
|
||||
|
||||
= content_for :meta_tags do
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
- return unless show_moved_service_desk_issue_warning?(issue)
|
||||
- service_desk_link_url = help_page_path('user/project/service_desk')
|
||||
- service_desk_link_url = help_page_path('user/project/service_desk/index')
|
||||
- service_desk_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: service_desk_link_url }
|
||||
|
||||
= render Pajamas::AlertComponent.new(variant: :warning,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
%code= @project.service_desk_address
|
||||
|
||||
%span= s_("ServiceDesk|Issues created from Service Desk emails will appear here. Each comment becomes part of the email conversation.")
|
||||
= link_to _('Learn more.'), help_page_path('user/project/service_desk')
|
||||
= link_to _('Learn more.'), help_page_path('user/project/service_desk/index')
|
||||
|
||||
- if can_edit_project_settings && !service_desk_enabled
|
||||
.text-center
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
%code= @project.service_desk_address
|
||||
|
||||
%span= s_("ServiceDesk|Issues created from Service Desk emails will appear here. Each comment becomes part of the email conversation.")
|
||||
= link_to _('Learn more.'), help_page_path('user/project/service_desk')
|
||||
= link_to _('Learn more.'), help_page_path('user/project/service_desk/index')
|
||||
|
||||
- if can_edit_project_settings && !service_desk_enabled
|
||||
.gl-mt-3
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
- add_page_specific_style 'page_bundles/issuable'
|
||||
- add_page_specific_style 'page_bundles/issues_show'
|
||||
- add_page_specific_style 'page_bundles/work_items'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
|
||||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
- add_page_specific_style 'page_bundles/pipelines'
|
||||
- add_page_specific_style 'page_bundles/ci_status'
|
||||
- add_page_specific_style 'page_bundles/merge_request'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
|
||||
- conflicting_mr = @merge_request.existing_mrs_targeting_same_branch.first
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
- add_page_specific_style 'page_bundles/merge_request'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
|
||||
= render 'page'
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
- new_merge_request_email = @project.new_issuable_address(current_user, 'merge_request')
|
||||
- add_page_specific_style 'page_bundles/issuable_list'
|
||||
- add_page_specific_style 'page_bundles/merge_request'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
|
||||
= content_for :meta_tags do
|
||||
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} merge requests")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
- add_page_specific_style 'page_bundles/merge_request'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
|
||||
= render 'page'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
- page_title "##{request.params['iid']}"
|
||||
- add_to_breadcrumbs _("Issues"), project_issues_path(@project)
|
||||
- add_page_specific_style 'page_bundles/work_items'
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
- @gfm_form = true
|
||||
- @noteable_type = 'WorkItem'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
- breadcrumb_title _("Edit Snippet")
|
||||
- page_title _("Edit"), "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
- content_for :prefetch_asset_tags do
|
||||
- webpack_preload_asset_tag('monaco')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
- page_title _("New Snippet")
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
|
||||
.page-title-holder.d-flex.align-items-center
|
||||
%h1.page-title.gl-font-size-h-display= _('New Snippet')
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
- add_to_breadcrumbs _("Snippets"), explore_snippets_path
|
||||
- breadcrumb_title @snippet.to_reference
|
||||
- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
|
||||
- add_page_specific_style 'page_bundles/notes'
|
||||
- content_for :prefetch_asset_tags do
|
||||
- webpack_preload_asset_tag('monaco', prefetch: true)
|
||||
|
||||
|
|
|
|||
|
|
@ -362,6 +362,7 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/work_items.css"
|
||||
config.assets.precompile << "page_bundles/xterm.css"
|
||||
config.assets.precompile << "page_bundles/labels.css"
|
||||
config.assets.precompile << "page_bundles/notes.css"
|
||||
config.assets.precompile << "lazy_bundles/cropper.css"
|
||||
config.assets.precompile << "lazy_bundles/gridstack.css"
|
||||
config.assets.precompile << "performance_bar.css"
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
description: "Open frequent items dropdown"
|
||||
category: default
|
||||
action: click_link
|
||||
label_description: "`[dropdown_type]_dropdown_frequent_items_list_item`"
|
||||
property_description: ""
|
||||
value_description: ""
|
||||
extra_properties:
|
||||
identifiers:
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::editor
|
||||
milestone: "13.7"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47589
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: maven_remove_permissions_check_from_finder
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135494
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/430267
|
||||
milestone: '16.7'
|
||||
type: development
|
||||
group: group::package registry
|
||||
default_enabled: false
|
||||
|
|
@ -181,6 +181,7 @@ options:
|
|||
- p_ci_templates_terraform_module_base
|
||||
- p_ci_templates_terraform_module
|
||||
- p_ci_templates_pages_zola
|
||||
- p_ci_templates_diffblue_cover
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_diffblue_cover_monthly
|
||||
description: Count of pipelines using the Diffblue Cover template
|
||||
product_section: ci
|
||||
product_stage: pipeline_authoring
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.7"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137047
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_diffblue_cover
|
||||
|
|
@ -182,6 +182,7 @@ options:
|
|||
- p_ci_templates_terraform_module_base
|
||||
- p_ci_templates_terraform_module
|
||||
- p_ci_templates_pages_zola
|
||||
- p_ci_templates_diffblue_cover
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_diffblue_cover_weekly
|
||||
description: Count of pipelines using the Diffblue Cover template
|
||||
product_section: ci
|
||||
product_stage: pipeline_authoring
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.7"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137047
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_diffblue_cover
|
||||
|
|
@ -22,8 +22,7 @@ You can administer all projects in the GitLab instance from the Admin Area's Pro
|
|||
|
||||
To access the Projects page:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Projects**.
|
||||
1. Select the **All**, **Private**, **Internal**, or **Public** tab to list only
|
||||
projects of that criteria.
|
||||
|
|
@ -74,8 +73,7 @@ You can combine the filter options. For example, to list only public projects wi
|
|||
|
||||
You can administer all users in the GitLab instance from the Admin Area's Users page:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Users**.
|
||||
|
||||
To list users matching a specific criteria, select one of the following tabs on the **Users** page:
|
||||
|
|
@ -138,8 +136,7 @@ By default, impersonation is enabled. GitLab can be configured to [disable imper
|
|||
|
||||
When using authentication providers, administrators can see the identities for a user:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Users**.
|
||||
1. From the list of users, select a user.
|
||||
1. Select **Identities**.
|
||||
|
|
@ -185,8 +182,7 @@ GitLab billing is based on the number of [**Billable users**](../subscriptions/s
|
|||
|
||||
You must be an administrator to manually add emails to users:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Users**.
|
||||
1. Locate the user and select them.
|
||||
1. Select **Edit**.
|
||||
|
|
@ -202,8 +198,7 @@ The [Cohorts](user_cohorts.md) tab displays the monthly cohorts of new users and
|
|||
|
||||
By default, users can create top level groups. To prevent a user from creating a top level group:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Users**.
|
||||
1. Locate the user and select them.
|
||||
1. Select **Edit**.
|
||||
|
|
@ -218,8 +213,7 @@ You can administer all groups in the GitLab instance from the Admin Area's Group
|
|||
|
||||
To access the Groups page:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Groups**.
|
||||
|
||||
For each group, the page displays their name, description, size, number of projects in the group,
|
||||
|
|
@ -244,16 +238,14 @@ To [Create a new group](../user/group/index.md#create-a-group) select **New grou
|
|||
|
||||
To view all topics in the GitLab instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Topics**.
|
||||
|
||||
For each topic, the page displays its name and the number of projects labeled with the topic.
|
||||
|
||||
### Search for topics
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Topics**.
|
||||
1. In the search box, enter your search criteria.
|
||||
The topic search is case-insensitive and applies partial matching.
|
||||
|
|
@ -262,8 +254,7 @@ For each topic, the page displays its name and the number of projects labeled wi
|
|||
|
||||
To create a topic:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Topics**.
|
||||
1. Select **New topic**.
|
||||
1. Enter the **Topic slug (name)** and **Topic title**.
|
||||
|
|
@ -282,8 +273,7 @@ Do not include sensitive information in the name of a topic.
|
|||
You can edit a topic's name, title, description, and avatar at any time.
|
||||
To edit a topic:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Topics**.
|
||||
1. Select **Edit** in that topic's row.
|
||||
1. Edit the topic slug (name), title, description, or avatar.
|
||||
|
|
@ -294,8 +284,7 @@ To edit a topic:
|
|||
If you no longer need a topic, you can permanently remove it.
|
||||
To remove a topic:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Topics**.
|
||||
1. To remove a topic, select **Remove** in that topic's row.
|
||||
|
||||
|
|
@ -307,8 +296,7 @@ After a merged topic is deleted, you cannot restore it.
|
|||
|
||||
To merge topics:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Topics**.
|
||||
1. Select **Merge topics**.
|
||||
1. From the **Source topic** dropdown list, select the topic you want to merge and remove.
|
||||
|
|
@ -322,8 +310,7 @@ page. For more details, see [Gitaly](gitaly/index.md).
|
|||
|
||||
To access the **Gitaly Servers** page:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Gitaly Servers**.
|
||||
|
||||
For each Gitaly server, the following details are listed:
|
||||
|
|
@ -347,8 +334,7 @@ You can administer all runners in the GitLab instance from the Admin Area's **Ru
|
|||
|
||||
To access the **Runners** page:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Runners**.
|
||||
|
||||
#### Search and filter runners
|
||||
|
|
@ -374,8 +360,7 @@ You can also filter runners by status, type, and tag. To filter:
|
|||
|
||||
You can delete multiple runners at the same time.
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Runners**.
|
||||
1. To the left of the runners you want to delete, select the checkbox.
|
||||
To select all of the runners on the page, select the checkbox above
|
||||
|
|
@ -405,8 +390,7 @@ You can administer all jobs in the GitLab instance from the Admin Area's Jobs pa
|
|||
|
||||
To access the Jobs page:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **CI/CD > Jobs**. All jobs are listed, in descending order of job ID.
|
||||
1. Select the **All** tab to list all jobs. Select the **Pending**, **Running**, or **Finished**
|
||||
tab to list only jobs of that status.
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ from planning to monitoring.
|
|||
|
||||
To see DevOps Reports:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Analytics > DevOps Reports**.
|
||||
|
||||
## DevOps Score
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ Prerequisites:
|
|||
|
||||
To view instance-level analytics:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Analytics**, then one of the available analytics:
|
||||
|
||||
- [DevOps Reports](dev_ops_reports.md): Provides an overview of your entire instance's feature usage.
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ Usage Trends data refreshes daily.
|
|||
|
||||
To view Usage Trends:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Analytics > Usage Trends**.
|
||||
|
||||
## Total counts
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
Several options are available for customizing the appearance of a self-managed instance
|
||||
of GitLab. To access these settings:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > Appearance**.
|
||||
|
||||
## Navigation bar
|
||||
|
|
@ -83,8 +82,7 @@ description, and icon.
|
|||
|
||||
To configure the PWA settings:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > Appearance**.
|
||||
1. Scroll to the **Progressive Web App (PWA)** section.
|
||||
1. Complete the fields.
|
||||
|
|
|
|||
|
|
@ -357,8 +357,7 @@ Prerequisites:
|
|||
|
||||
To add a streaming destination for an instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select **Add streaming destination** and select **HTTP endpoint** to show the section for adding destinations.
|
||||
|
|
@ -377,8 +376,7 @@ Prerequisites:
|
|||
|
||||
To list the streaming destinations for an instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select the stream to expand it and see all the custom HTTP headers.
|
||||
|
|
@ -391,8 +389,7 @@ Prerequisites:
|
|||
|
||||
To update a instance streaming destination's name:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select the stream to expand.
|
||||
|
|
@ -401,8 +398,7 @@ To update a instance streaming destination's name:
|
|||
|
||||
To update a instance streaming destination's custom HTTP headers:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select the stream to expand.
|
||||
|
|
@ -424,8 +420,7 @@ Prerequisites:
|
|||
|
||||
To delete the streaming destinations for an instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select the **Streams** tab.
|
||||
1. Select the stream to expand.
|
||||
|
|
@ -434,8 +429,7 @@ To delete the streaming destinations for an instance:
|
|||
|
||||
To delete only the custom HTTP headers for a streaming destination:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select the **Streams** tab.
|
||||
1. To the right of the item, **Edit** (**{pencil}**).
|
||||
|
|
@ -462,8 +456,7 @@ Prerequisites:
|
|||
|
||||
To list streaming destinations for an instance and see the verification tokens:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select the **Streams** tab.
|
||||
1. View the verification token on the right side of each item.
|
||||
|
|
@ -479,8 +472,7 @@ A streaming destination that has an event type filter set has a **filtered** (**
|
|||
|
||||
To update a streaming destination's event filters:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select the **Streams** tab.
|
||||
1. Select the stream to expand.
|
||||
|
|
@ -521,8 +513,7 @@ Prerequisites:
|
|||
|
||||
To add Google Cloud Logging streaming destinations to an instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select **Add streaming destination** and select **Google Cloud Logging** to show the section for adding destinations.
|
||||
|
|
@ -539,8 +530,7 @@ Prerequisites:
|
|||
|
||||
To list Google Cloud Logging streaming destinations for an instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select the Google Cloud Logging stream to expand and see all the fields.
|
||||
|
|
@ -553,8 +543,7 @@ Prerequisites:
|
|||
|
||||
To update Google Cloud Logging streaming destinations to an instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select the Google Cloud Logging stream to expand.
|
||||
|
|
@ -572,8 +561,7 @@ Prerequisites:
|
|||
|
||||
To delete Google Cloud Logging streaming destinations to an instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select the Google Cloud Logging stream to expand.
|
||||
|
|
|
|||
|
|
@ -55,8 +55,7 @@ Project audit events can also be accessed using the [Project Audit Events API](.
|
|||
You can view audit events from user actions across an entire GitLab instance.
|
||||
To view instance audit events:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. Filter by the following:
|
||||
- Member of the project (user) who performed the action
|
||||
|
|
@ -82,8 +81,7 @@ After upgrading to a paid tier, you can also see successful sign-in events on au
|
|||
You can export the current view (including filters) of your instance audit events as a
|
||||
CSV(comma-separated values) file. To export the instance audit events to CSV:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Audit Events**.
|
||||
1. Select the available search filters.
|
||||
1. Select **Export as CSV**.
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ To create a new user account with auditor access (or change an existing user):
|
|||
|
||||
To create a user account with auditor access:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Overview > Users**.
|
||||
1. Create a new user or edit an existing one. Set **Access Level** to **Auditor**.
|
||||
1. If you created a user, select **Create user**. For an existing user, select **Save changes**.
|
||||
|
|
|
|||
|
|
@ -225,8 +225,7 @@ field contains no data:
|
|||
|
||||
To resolve this:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, go to **Settings > General**.
|
||||
1. Expand both of the following:
|
||||
- **Account and limit**.
|
||||
|
|
|
|||
|
|
@ -501,8 +501,7 @@ When global group memberships lock is enabled:
|
|||
To enable global group memberships lock:
|
||||
|
||||
1. [Configure LDAP](index.md#configure-ldap).
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Expand the **Visibility and access controls** section.
|
||||
1. Ensure the **Lock memberships to LDAP synchronization** checkbox is selected.
|
||||
|
|
@ -514,8 +513,7 @@ By default, group members with the Owner role can manage [LDAP group synchroniza
|
|||
GitLab administrators can remove this permission from group Owners:
|
||||
|
||||
1. [Configure LDAP](index.md#configure-ldap).
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Ensure the **Allow group owners to manage LDAP-related settings** checkbox is not checked.
|
||||
|
|
|
|||
|
|
@ -25,208 +25,8 @@ For detailed information on restoring GitLab, see [Restore GitLab](restore_gitla
|
|||
|
||||
## Migrate to a new server
|
||||
|
||||
<!-- some details borrowed from GitLab.com move from Azure to GCP detailed at https://gitlab.com/gitlab-com/migration/-/blob/master/.gitlab/issue_templates/failover.md -->
|
||||
|
||||
You can use GitLab backup and restore to migrate your instance to a new server. This section outlines a typical procedure for a GitLab deployment running on a single server.
|
||||
If you're running GitLab Geo, an alternative option is [Geo disaster recovery for planned failover](../geo/disaster_recovery/planned_failover.md).
|
||||
|
||||
WARNING:
|
||||
Avoid uncoordinated data processing by both the new and old servers, where multiple
|
||||
servers could connect concurrently and process the same data. For example, when using
|
||||
[incoming email](../incoming_email.md), if both GitLab instances are
|
||||
processing email at the same time, then both instances miss some data.
|
||||
This type of problem can occur with other services as well, such as a
|
||||
[non-packaged database](https://docs.gitlab.com/omnibus/settings/database.html#using-a-non-packaged-postgresql-database-management-server),
|
||||
a non-packaged Redis instance, or non-packaged Sidekiq.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Some time before your migration, consider notifying your users of upcoming
|
||||
scheduled maintenance with a [broadcast message banner](../broadcast_messages.md).
|
||||
- Ensure your backups are complete and current. Create a complete system-level backup, or
|
||||
take a snapshot of all servers involved in the migration, in case destructive commands
|
||||
(like `rm`) are run incorrectly.
|
||||
|
||||
### Prepare the new server
|
||||
|
||||
To prepare the new server:
|
||||
|
||||
1. Copy the
|
||||
[SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079)
|
||||
from the old server to avoid man-in-the-middle attack warnings.
|
||||
See [Manually replicate the primary site's SSH host keys](../geo/replication/configuration.md#step-2-manually-replicate-the-primary-sites-ssh-host-keys) for example steps.
|
||||
1. [Install and configure GitLab](https://about.gitlab.com/install/) except
|
||||
[incoming email](../incoming_email.md):
|
||||
1. Install GitLab.
|
||||
1. Configure by copying `/etc/gitlab` files from the old server to the new server, and update as necessary.
|
||||
Read the
|
||||
[Linux package installation backup and restore instructions](https://docs.gitlab.com/omnibus/settings/backups.html) for more detail.
|
||||
1. If applicable, disable [incoming email](../incoming_email.md).
|
||||
1. Block new CI/CD jobs from starting upon initial startup after the backup and restore.
|
||||
Edit `/etc/gitlab/gitlab.rb` and set the following:
|
||||
|
||||
```ruby
|
||||
nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n deny all;\n return 503;\n }\n"
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
1. Stop GitLab to avoid any potential unnecessary and unintentional data processing:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl stop
|
||||
```
|
||||
|
||||
1. Configure the new server to allow receiving the Redis database and GitLab backup files:
|
||||
|
||||
```shell
|
||||
sudo rm -f /var/opt/gitlab/redis/dump.rdb
|
||||
sudo chown <your-linux-username> /var/opt/gitlab/redis /var/opt/gitlab/backups
|
||||
```
|
||||
|
||||
### Prepare and transfer content from the old server
|
||||
|
||||
1. Ensure you have an up-to-date system-level backup or snapshot of the old server.
|
||||
1. Enable [maintenance mode](../maintenance_mode/index.md),
|
||||
if supported by your GitLab edition.
|
||||
1. Block new CI/CD jobs from starting:
|
||||
1. Edit `/etc/gitlab/gitlab.rb`, and set the following:
|
||||
|
||||
```ruby
|
||||
nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n deny all;\n return 503;\n }\n"
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
1. Disable periodic background jobs:
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Background Jobs**.
|
||||
1. Under the Sidekiq dashboard, select **Cron** tab and then
|
||||
**Disable All**.
|
||||
1. Wait for the currently running CI/CD jobs to finish, or accept that jobs that have not completed may be lost.
|
||||
To view jobs currently running, on the left sidebar, select **Overviews > Jobs**,
|
||||
and then select **Running**.
|
||||
1. Wait for Sidekiq jobs to finish:
|
||||
1. On the left sidebar, select **Monitoring > Background Jobs**.
|
||||
1. Under the Sidekiq dashboard, select **Queues** and then **Live Poll**.
|
||||
Wait for **Busy** and **Enqueued** to drop to 0.
|
||||
These queues contain work that has been submitted by your users;
|
||||
shutting down before these jobs complete may cause the work to be lost.
|
||||
Make note of the numbers shown in the Sidekiq dashboard for post-migration verification.
|
||||
1. Flush the Redis database to disk, and stop GitLab other than the services needed for migration:
|
||||
|
||||
```shell
|
||||
sudo /opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket save && sudo gitlab-ctl stop && sudo gitlab-ctl start postgresql && sudo gitlab-ctl start gitaly
|
||||
```
|
||||
|
||||
1. Create a GitLab backup:
|
||||
|
||||
```shell
|
||||
sudo gitlab-backup create
|
||||
```
|
||||
|
||||
1. Disable the following GitLab services and prevent unintentional restarts by adding the following to the bottom of `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
alertmanager['enable'] = false
|
||||
gitlab_exporter['enable'] = false
|
||||
gitlab_pages['enable'] = false
|
||||
gitlab_workhorse['enable'] = false
|
||||
grafana['enable'] = false
|
||||
logrotate['enable'] = false
|
||||
gitlab_rails['incoming_email_enabled'] = false
|
||||
nginx['enable'] = false
|
||||
node_exporter['enable'] = false
|
||||
postgres_exporter['enable'] = false
|
||||
postgresql['enable'] = false
|
||||
prometheus['enable'] = false
|
||||
puma['enable'] = false
|
||||
redis['enable'] = false
|
||||
redis_exporter['enable'] = false
|
||||
registry['enable'] = false
|
||||
sidekiq['enable'] = false
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
1. Verify everything is stopped, and confirm no services are running:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl status
|
||||
```
|
||||
|
||||
1. Stop Redis on the **new server** before transferring the Redis database backup:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl stop redis
|
||||
```
|
||||
|
||||
1. Transfer the Redis database and GitLab backups to the new server:
|
||||
|
||||
```shell
|
||||
sudo scp /var/opt/gitlab/redis/dump.rdb <your-linux-username>@new-server:/var/opt/gitlab/redis
|
||||
sudo scp /var/opt/gitlab/backups/your-backup.tar <your-linux-username>@new-server:/var/opt/gitlab/backups
|
||||
```
|
||||
|
||||
### Restore data on the new server
|
||||
|
||||
1. Restore appropriate file system permissions:
|
||||
|
||||
```shell
|
||||
sudo chown gitlab-redis /var/opt/gitlab/redis
|
||||
sudo chown gitlab-redis:gitlab-redis /var/opt/gitlab/redis/dump.rdb
|
||||
sudo chown git:root /var/opt/gitlab/backups
|
||||
sudo chown git:git /var/opt/gitlab/backups/your-backup.tar
|
||||
```
|
||||
|
||||
1. Start Redis:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl start redis
|
||||
```
|
||||
|
||||
1. [Restore the GitLab backup](#restore-gitlab).
|
||||
1. Verify that the Redis database restored correctly:
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Background Jobs**.
|
||||
1. Under the Sidekiq dashboard, verify that the numbers
|
||||
match with what was shown on the old server.
|
||||
1. While still under the Sidekiq dashboard, select **Cron** and then **Enable All**
|
||||
to re-enable periodic background jobs.
|
||||
1. Test that read-only operations on the GitLab instance work as expected. For example, browse through project repository files, merge requests, and issues.
|
||||
1. Disable [Maintenance Mode](../maintenance_mode/index.md), if previously enabled.
|
||||
1. Test that the GitLab instance is working as expected.
|
||||
1. If applicable, re-enable [incoming email](../incoming_email.md) and test it is working as expected.
|
||||
1. Update your DNS or load balancer to point at the new server.
|
||||
1. Unblock new CI/CD jobs from starting by removing the custom NGINX configuration
|
||||
you added previously:
|
||||
|
||||
```ruby
|
||||
# The following line must be removed
|
||||
nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n deny all;\n return 503;\n }\n"
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
1. Remove the scheduled maintenance [broadcast message banner](../broadcast_messages.md).
|
||||
For detailed information on using back up and restore to migrate to a new server, see
|
||||
[Migrate to a new server](migrate_to_new_server.md).
|
||||
|
||||
## Additional notes
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
stage: Systems
|
||||
group: Geo
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Migrate to a new server
|
||||
|
||||
<!-- some details borrowed from GitLab.com move from Azure to GCP detailed at https://gitlab.com/gitlab-com/migration/-/blob/master/.gitlab/issue_templates/failover.md -->
|
||||
|
||||
You can use GitLab backup and restore to migrate your instance to a new server. This section outlines a typical procedure for a GitLab deployment running on a single server.
|
||||
If you're running GitLab Geo, an alternative option is [Geo disaster recovery for planned failover](../geo/disaster_recovery/planned_failover.md).
|
||||
|
||||
WARNING:
|
||||
Avoid uncoordinated data processing by both the new and old servers, where multiple
|
||||
servers could connect concurrently and process the same data. For example, when using
|
||||
[incoming email](../incoming_email.md), if both GitLab instances are
|
||||
processing email at the same time, then both instances miss some data.
|
||||
This type of problem can occur with other services as well, such as a
|
||||
[non-packaged database](https://docs.gitlab.com/omnibus/settings/database.html#using-a-non-packaged-postgresql-database-management-server),
|
||||
a non-packaged Redis instance, or non-packaged Sidekiq.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Some time before your migration, consider notifying your users of upcoming
|
||||
scheduled maintenance with a [broadcast message banner](../broadcast_messages.md).
|
||||
- Ensure your backups are complete and current. Create a complete system-level backup, or
|
||||
take a snapshot of all servers involved in the migration, in case destructive commands
|
||||
(like `rm`) are run incorrectly.
|
||||
|
||||
## Prepare the new server
|
||||
|
||||
To prepare the new server:
|
||||
|
||||
1. Copy the
|
||||
[SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079)
|
||||
from the old server to avoid man-in-the-middle attack warnings.
|
||||
See [Manually replicate the primary site's SSH host keys](../geo/replication/configuration.md#step-2-manually-replicate-the-primary-sites-ssh-host-keys) for example steps.
|
||||
1. [Install and configure GitLab](https://about.gitlab.com/install/) except
|
||||
[incoming email](../incoming_email.md):
|
||||
1. Install GitLab.
|
||||
1. Configure by copying `/etc/gitlab` files from the old server to the new server, and update as necessary.
|
||||
Read the
|
||||
[Linux package installation backup and restore instructions](https://docs.gitlab.com/omnibus/settings/backups.html) for more detail.
|
||||
1. If applicable, disable [incoming email](../incoming_email.md).
|
||||
1. Block new CI/CD jobs from starting upon initial startup after the backup and restore.
|
||||
Edit `/etc/gitlab/gitlab.rb` and set the following:
|
||||
|
||||
```ruby
|
||||
nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n deny all;\n return 503;\n }\n"
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
1. Stop GitLab to avoid any potential unnecessary and unintentional data processing:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl stop
|
||||
```
|
||||
|
||||
1. Configure the new server to allow receiving the Redis database and GitLab backup files:
|
||||
|
||||
```shell
|
||||
sudo rm -f /var/opt/gitlab/redis/dump.rdb
|
||||
sudo chown <your-linux-username> /var/opt/gitlab/redis /var/opt/gitlab/backups
|
||||
```
|
||||
|
||||
## Prepare and transfer content from the old server
|
||||
|
||||
1. Ensure you have an up-to-date system-level backup or snapshot of the old server.
|
||||
1. Enable [maintenance mode](../maintenance_mode/index.md),
|
||||
if supported by your GitLab edition.
|
||||
1. Block new CI/CD jobs from starting:
|
||||
1. Edit `/etc/gitlab/gitlab.rb`, and set the following:
|
||||
|
||||
```ruby
|
||||
nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n deny all;\n return 503;\n }\n"
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
1. Disable periodic background jobs:
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Background Jobs**.
|
||||
1. Under the Sidekiq dashboard, select **Cron** tab and then
|
||||
**Disable All**.
|
||||
1. Wait for the currently running CI/CD jobs to finish, or accept that jobs that have not completed may be lost.
|
||||
To view jobs currently running, on the left sidebar, select **Overviews > Jobs**,
|
||||
and then select **Running**.
|
||||
1. Wait for Sidekiq jobs to finish:
|
||||
1. On the left sidebar, select **Monitoring > Background Jobs**.
|
||||
1. Under the Sidekiq dashboard, select **Queues** and then **Live Poll**.
|
||||
Wait for **Busy** and **Enqueued** to drop to 0.
|
||||
These queues contain work that has been submitted by your users;
|
||||
shutting down before these jobs complete may cause the work to be lost.
|
||||
Make note of the numbers shown in the Sidekiq dashboard for post-migration verification.
|
||||
1. Flush the Redis database to disk, and stop GitLab other than the services needed for migration:
|
||||
|
||||
```shell
|
||||
sudo /opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket save && sudo gitlab-ctl stop && sudo gitlab-ctl start postgresql && sudo gitlab-ctl start gitaly
|
||||
```
|
||||
|
||||
1. Create a GitLab backup:
|
||||
|
||||
```shell
|
||||
sudo gitlab-backup create
|
||||
```
|
||||
|
||||
1. Disable the following GitLab services and prevent unintentional restarts by adding the following to the bottom of `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
alertmanager['enable'] = false
|
||||
gitlab_exporter['enable'] = false
|
||||
gitlab_pages['enable'] = false
|
||||
gitlab_workhorse['enable'] = false
|
||||
grafana['enable'] = false
|
||||
logrotate['enable'] = false
|
||||
gitlab_rails['incoming_email_enabled'] = false
|
||||
nginx['enable'] = false
|
||||
node_exporter['enable'] = false
|
||||
postgres_exporter['enable'] = false
|
||||
postgresql['enable'] = false
|
||||
prometheus['enable'] = false
|
||||
puma['enable'] = false
|
||||
redis['enable'] = false
|
||||
redis_exporter['enable'] = false
|
||||
registry['enable'] = false
|
||||
sidekiq['enable'] = false
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
1. Verify everything is stopped, and confirm no services are running:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl status
|
||||
```
|
||||
|
||||
1. Stop Redis on the **new server** before transferring the Redis database backup:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl stop redis
|
||||
```
|
||||
|
||||
1. Transfer the Redis database and GitLab backups to the new server:
|
||||
|
||||
```shell
|
||||
sudo scp /var/opt/gitlab/redis/dump.rdb <your-linux-username>@new-server:/var/opt/gitlab/redis
|
||||
sudo scp /var/opt/gitlab/backups/your-backup.tar <your-linux-username>@new-server:/var/opt/gitlab/backups
|
||||
```
|
||||
|
||||
## Restore data on the new server
|
||||
|
||||
1. Restore appropriate file system permissions:
|
||||
|
||||
```shell
|
||||
sudo chown gitlab-redis /var/opt/gitlab/redis
|
||||
sudo chown gitlab-redis:gitlab-redis /var/opt/gitlab/redis/dump.rdb
|
||||
sudo chown git:root /var/opt/gitlab/backups
|
||||
sudo chown git:git /var/opt/gitlab/backups/your-backup.tar
|
||||
```
|
||||
|
||||
1. Start Redis:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl start redis
|
||||
```
|
||||
|
||||
1. [Restore the GitLab backup](restore_gitlab.md).
|
||||
1. Verify that the Redis database restored correctly:
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Background Jobs**.
|
||||
1. Under the Sidekiq dashboard, verify that the numbers
|
||||
match with what was shown on the old server.
|
||||
1. While still under the Sidekiq dashboard, select **Cron** and then **Enable All**
|
||||
to re-enable periodic background jobs.
|
||||
1. Test that read-only operations on the GitLab instance work as expected. For example, browse through project repository files, merge requests, and issues.
|
||||
1. Disable [Maintenance Mode](../maintenance_mode/index.md), if previously enabled.
|
||||
1. Test that the GitLab instance is working as expected.
|
||||
1. If applicable, re-enable [incoming email](../incoming_email.md) and test it is working as expected.
|
||||
1. Update your DNS or load balancer to point at the new server.
|
||||
1. Unblock new CI/CD jobs from starting by removing the custom NGINX configuration
|
||||
you added previously:
|
||||
|
||||
```ruby
|
||||
# The following line must be removed
|
||||
nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n deny all;\n return 503;\n }\n"
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
1. Remove the scheduled maintenance [broadcast message banner](../broadcast_messages.md).
|
||||
|
|
@ -57,8 +57,7 @@ To display messages to users on your GitLab instance, add a broadcast message.
|
|||
|
||||
To add a broadcast message:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Messages**.
|
||||
1. Select **Add new message**.
|
||||
1. Add the text for the message to the **Message** field. You can style a message's content using Markdown, emoji, and the `a` and `br` HTML tags.
|
||||
|
|
@ -86,8 +85,7 @@ If you must make changes to a broadcast message, you can edit it.
|
|||
|
||||
To edit a broadcast message:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Messages**.
|
||||
1. From the list of broadcast messages, select the edit button for the message.
|
||||
1. After making the required changes, select **Update broadcast message**.
|
||||
|
|
@ -101,8 +99,7 @@ You can delete a broadcast message while it's active.
|
|||
|
||||
To delete a broadcast message:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Messages**.
|
||||
1. From the list of broadcast messages, select the delete button for the message.
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@ You can also [revoke](#revoke-a-users-personal-access-token), [delete](#delete-a
|
|||
|
||||
You can revoke a user's personal access token.
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Credentials**.
|
||||
1. By the personal access token, select **Revoke**.
|
||||
|
||||
|
|
@ -58,8 +57,7 @@ When a PAT is revoked from the credentials inventory, the instance notifies the
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/243833) in GitLab 14.8.
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Credentials**.
|
||||
1. Select the **Project Access Tokens** tab.
|
||||
1. By the project access token, select **Revoke**.
|
||||
|
|
@ -72,8 +70,7 @@ The project access token is revoked and a background worker is queued to delete
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225248) in GitLab 13.5.
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Credentials**.
|
||||
1. Select the **SSH Keys** tab.
|
||||
1. By the SSH key, select **Delete**.
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ might modify the template projects without understanding the side effects.
|
|||
|
||||
To select the group to manage the project templates for your instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Settings > Templates**.
|
||||
1. Expand **Custom project templates**.
|
||||
1. Select a group to use.
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ set values are presented as **Too large** are cannot be expanded in the UI.
|
|||
|
||||
To configure these values:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Diff limits**.
|
||||
1. Enter a value for the diff limit.
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ You can send email notifications once every 10 minutes.
|
|||
|
||||
To send an email:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Overview > Users**.
|
||||
1. In the upper-right corner, select **Send email to users** (**{mail}**).
|
||||
1. Complete the fields. The email body supports only plain text and does not support HTML, Markdown, or other rich text formats.
|
||||
|
|
|
|||
|
|
@ -56,8 +56,7 @@ Additionally, users can be set as external users using:
|
|||
By default, new users are not set as external users. This behavior can be changed
|
||||
by an administrator:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand the **Account and limit** section.
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ the site more time before scheduling a planned failover.
|
|||
|
||||
On the **primary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Sites**.
|
||||
1. Expand **Verification information** tab for that site to view automatic checksumming
|
||||
status for repositories and wikis. Successes are shown in green, pending work
|
||||
|
|
@ -38,8 +37,7 @@ On the **primary** site:
|
|||
|
||||
On the **secondary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Sites**.
|
||||
1. Expand **Verification information** tab for that site to view automatic checksumming
|
||||
status for repositories and wikis. Successes are shown in green, pending work
|
||||
|
|
@ -67,8 +65,7 @@ increase load and vice versa.
|
|||
|
||||
On the **primary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Sites**.
|
||||
1. Select **Edit** for the **primary** site to customize the minimum
|
||||
re-verification interval:
|
||||
|
|
|
|||
|
|
@ -154,8 +154,7 @@ ensure these processes are close to 100% as possible during active use.
|
|||
|
||||
On the **secondary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Sites**.
|
||||
Replicated objects (shown in green) should be close to 100%,
|
||||
and there should be no failures (shown in red). If a large proportion of
|
||||
|
|
@ -183,8 +182,7 @@ This [content was moved to another location](background_verification.md).
|
|||
|
||||
On the **primary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Messages**.
|
||||
1. Add a message notifying users on the maintenance window.
|
||||
You can check under **Geo > Sites** to estimate how long it
|
||||
|
|
@ -197,8 +195,7 @@ To ensure that all data is replicated to a secondary site, updates (write reques
|
|||
be disabled on the **primary** site:
|
||||
|
||||
1. Enable [maintenance mode](../../maintenance_mode/index.md) on the **primary** site.
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Monitoring > Background Jobs**.
|
||||
1. On the Sidekiq dashboard, select **Cron**.
|
||||
1. Select `Disable All` to disable non-Geo periodic background jobs.
|
||||
|
|
|
|||
|
|
@ -65,8 +65,7 @@ promote a Geo replica and perform a failover.
|
|||
|
||||
On the **secondary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Sites** to see its status.
|
||||
Replicated objects (shown in green) should be close to 100%,
|
||||
and there should be no failures (shown in red). If a large proportion of
|
||||
|
|
|
|||
|
|
@ -159,8 +159,7 @@ public URL of the primary site is used.
|
|||
|
||||
To update the internal URL of the primary Geo site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Sites**.
|
||||
1. Select **Edit** on the primary site.
|
||||
1. Change the **Internal URL**, then select **Save changes**.
|
||||
|
|
@ -249,6 +248,8 @@ Pausing and resuming replication is done through a command-line tool from the no
|
|||
|
||||
If `postgresql` is on a standalone database node, ensure that `gitlab.rb` on that node contains the configuration line `gitlab_rails['geo_node_name'] = 'node_name'`, where `node_name` is the same as the `geo_node_name` on the application node.
|
||||
|
||||
Also, be aware that if PostgreSQL is restarted after pausing replication (either by restarting the VM or restarting the service with `gitlab-ctl restart postgresql`), PostgreSQL automatically resumes replication, which is something you wouldn't want during an upgrade or in a planned failover scenario.
|
||||
|
||||
**To Pause: (from secondary)**
|
||||
|
||||
```shell
|
||||
|
|
|
|||
|
|
@ -327,8 +327,7 @@ method to be enabled. This is enabled by default, but if converting an existing
|
|||
|
||||
On the **primary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. If using Git over SSH, then:
|
||||
|
|
@ -341,8 +340,7 @@ On the **primary** site:
|
|||
You can sign in to the **secondary** site with the same credentials you used with
|
||||
the **primary** site. After you sign in:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Sites**.
|
||||
1. Verify that it's correctly identified as a **secondary** Geo site, and that
|
||||
Geo is enabled.
|
||||
|
|
|
|||
|
|
@ -166,8 +166,7 @@ For each application and Sidekiq node on the **secondary** site:
|
|||
|
||||
To verify container registry replication is working, on the **secondary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Nodes**.
|
||||
The initial replication, or "backfill", is probably still in progress.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ to do that.
|
|||
To remove the **primary** site:
|
||||
|
||||
1. [Remove all secondary Geo sites](remove_geo_site.md)
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Nodes**.
|
||||
1. Select **Remove** for the **primary** node.
|
||||
1. Confirm by selecting **Remove** when the prompt appears.
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ whether they are stored on the local file system or in object storage.
|
|||
|
||||
To enable GitLab replication:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Nodes**.
|
||||
1. Select **Edit** on the **secondary** site.
|
||||
1. In the **Synchronization Settings** section, find the **Allow this secondary node to replicate content on Object Storage**
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ type: howto
|
|||
|
||||
**Secondary** sites can be removed from the Geo cluster using the Geo administration page of the **primary** site. To remove a **secondary** site:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, at the bottom, select **Admin Area**.
|
||||
1. On the left sidebar, select **Geo > Nodes**.
|
||||
1. For the **secondary** site you want to remove, select **Remove**.
|
||||
1. Confirm by selecting **Remove** when the prompt appears.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue