{{ name }}
-+ {{ s__('PackageRegistry|This NuGet package has no dependencies.') }} +
++ {{ s__('PackageRegistry|There are no other versions of this package.') }} +
+-
- #{{ pipeline.id }}
+ #{{ pipeline.id }}
-
- {{ __('locked') }} + {{ s__('Runners|locked') }} - {{ __('Cannot be assigned to other projects.') }} -
- {{ __('paused') }} + {{ s__('Runners|paused') }} - {{ __('Not available to run jobs.') }}
diff --git a/app/assets/javascripts/runner/components/runner_update_form.vue b/app/assets/javascripts/runner/components/runner_update_form.vue
index 0c1b83b683..85d14547ef 100644
--- a/app/assets/javascripts/runner/components/runner_update_form.vue
+++ b/app/assets/javascripts/runner/components/runner_update_form.vue
@@ -7,42 +7,26 @@ import {
GlFormInputGroup,
GlTooltipDirective,
} from '@gitlab/ui';
+import {
+ modelToUpdateMutationVariables,
+ runnerToModel,
+} from 'ee_else_ce/runner/runner_details/runner_update_form_utils';
import createFlash, { FLASH_TYPES } from '~/flash';
import { __ } from '~/locale';
+import { captureException } from '~/runner/sentry_utils';
import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants';
import runnerUpdateMutation from '../graphql/runner_update.mutation.graphql';
-const runnerToModel = (runner) => {
- const {
- id,
- description,
- maximumTimeout,
- accessLevel,
- active,
- locked,
- runUntagged,
- tagList = [],
- } = runner || {};
-
- return {
- id,
- description,
- maximumTimeout,
- accessLevel,
- active,
- locked,
- runUntagged,
- tagList: tagList.join(', '),
- };
-};
-
export default {
+ name: 'RunnerUpdateForm',
components: {
GlButton,
GlForm,
GlFormCheckbox,
GlFormGroup,
GlFormInputGroup,
+ RunnerUpdateCostFactorFields: () =>
+ import('ee_component/runner/components/runner_update_cost_factor_fields.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -67,18 +51,6 @@ export default {
readonlyIpAddress() {
return this.runner?.ipAddress;
},
- updateMutationInput() {
- const { maximumTimeout, tagList } = this.model;
-
- return {
- ...this.model,
- maximumTimeout: maximumTimeout !== '' ? maximumTimeout : null,
- tagList: tagList
- .split(',')
- .map((tag) => tag.trim())
- .filter((tag) => Boolean(tag)),
- };
- },
},
watch: {
runner(newVal, oldVal) {
@@ -98,31 +70,32 @@ export default {
},
} = await this.$apollo.mutate({
mutation: runnerUpdateMutation,
- variables: {
- input: this.updateMutationInput,
- },
+ variables: modelToUpdateMutationVariables(this.model),
});
if (errors?.length) {
- this.onError(new Error(errors[0]));
+ // Validation errors need not be thrown
+ createFlash({ message: errors[0] });
return;
}
this.onSuccess();
- } catch (e) {
- this.onError(e);
+ } catch (error) {
+ const { message } = error;
+ createFlash({ message });
+
+ this.reportToSentry(error);
} finally {
this.saving = false;
}
},
- onError(error) {
- const { message } = error;
- createFlash({ message });
- },
onSuccess() {
createFlash({ message: __('Changes saved.'), type: FLASH_TYPES.SUCCESS });
this.model = runnerToModel(this.runner);
},
+ reportToSentry(error) {
+ captureException({ error, component: this.$options.name });
+ },
},
ACCESS_LEVEL_NOT_PROTECTED,
ACCESS_LEVEL_REF_PROTECTED,
@@ -213,6 +186,8 @@ export default {
{{ $options.strings.modalBody }}
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue index 1c4413bef7..0b0560f63c 100644 --- a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue +++ b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue @@ -225,11 +225,21 @@ export default { { name: 'success', data: this.mergeLabelsAndValues(labels, success), + areaStyle: { + color: this.$options.successColor, + }, + lineStyle: { + color: this.$options.successColor, + }, + itemStyle: { + color: this.$options.successColor, + }, }, ], }; }, }, + successColor: '#608b2f', chartContainerHeight: CHART_CONTAINER_HEIGHT, timesChartOptions: { height: INNER_CHART_HEIGHT, diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index 04ea6f760f..ee02f44679 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -74,6 +74,7 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => { const bindEvents = () => { const $newProjectForm = $('#new_project'); const $projectImportUrl = $('#project_import_url'); + const $projectImportUrlWarning = $('.js-import-url-warning'); const $projectPath = $('.tab-pane.active #project_path'); const $useTemplateBtn = $('.template-button > input'); const $projectFieldsForm = $('.project-fields-form'); @@ -134,7 +135,25 @@ const bindEvents = () => { $projectPath.val($projectPath.val().trim()); }); - $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl)); + function updateUrlPathWarningVisibility() { + const url = $projectImportUrl.val(); + const URL_PATTERN = /(?:git|https?):\/\/.*\/.*\.git$/; + const isUrlValid = URL_PATTERN.test(url); + $projectImportUrlWarning.toggleClass('hide', isUrlValid); + } + + let isProjectImportUrlDirty = false; + $projectImportUrl.on('blur', () => { + isProjectImportUrlDirty = true; + updateUrlPathWarningVisibility(); + }); + $projectImportUrl.on('keyup', () => { + deriveProjectPathFromUrl($projectImportUrl); + // defer error message till first input blur + if (isProjectImportUrlDirty) { + updateUrlPathWarningVisibility(); + } + }); $('.js-import-git-toggle-button').on('click', () => { const $projectMirror = $('#project_mirror'); diff --git a/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue b/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue index 0786a74f6b..e4edb950a1 100644 --- a/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue +++ b/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue @@ -1,15 +1,23 @@ + +{{ $options.i18n.description }}
+{{ downloadText }}
+{{ $options.i18n.securityConfiguration }}
-
+
+
-
+
+
-
-
{{ s__('Terraform|States') }}
-
@@ -194,19 +202,8 @@ export default {
{{ s__('mrWidget|The source branch has been deleted') }}
- {{ s__('mrWidget|You can delete the source branch now') }}
-
-
{{ __('No Matching Results') }}
-
+
{{ selectedAction.text }}
diff --git a/app/assets/javascripts/vue_shared/components/awards_list.vue b/app/assets/javascripts/vue_shared/components/awards_list.vue
index e6d9a38d1f..f4c73d1292 100644
--- a/app/assets/javascripts/vue_shared/components/awards_list.vue
+++ b/app/assets/javascripts/vue_shared/components/awards_list.vue
@@ -93,12 +93,12 @@ export default {
return {
name,
list,
- title: this.getAwardListTitle(list),
+ title: this.getAwardListTitle(list, name),
classes: this.getAwardClassBindings(list),
html: glEmojiTag(name),
};
},
- getAwardListTitle(awardsList) {
+ getAwardListTitle(awardsList, name) {
if (!awardsList.length) {
return '';
}
@@ -128,7 +128,7 @@ export default {
// We have 10+ awarded user, join them with comma and add `and x more`.
if (remainingAwardList.length) {
title = sprintf(
- __(`%{listToShow}, and %{awardsListLength} more.`),
+ __(`%{listToShow}, and %{awardsListLength} more`),
{
listToShow: namesToShow.join(', '),
awardsListLength: remainingAwardList.length,
@@ -146,7 +146,7 @@ export default {
title = namesToShow.join(__(' and '));
}
- return title;
+ return title + sprintf(__(' reacted with :%{name}:'), { name });
},
handleAward(awardName) {
if (!this.canAwardEmoji) {
diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/mixins.js b/app/assets/javascripts/vue_shared/components/blob_viewers/mixins.js
index 9c2ed5abf0..0c1d55ae70 100644
--- a/app/assets/javascripts/vue_shared/components/blob_viewers/mixins.js
+++ b/app/assets/javascripts/vue_shared/components/blob_viewers/mixins.js
@@ -5,7 +5,13 @@ export default {
props: {
content: {
type: String,
- required: true,
+ required: false,
+ default: null,
+ },
+ richViewer: {
+ type: String,
+ default: '',
+ required: false,
},
type: {
type: String,
diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue b/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
index a8a053c0d9..dc4d1bd56e 100644
--- a/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
@@ -18,5 +18,5 @@ export default {
};
- {{ note.body }}{{ heading }}
{{ $options.i18n.cardHeaderTitle }}
+
+
+
-
-
+
-
-
+
- {{ __('Artifact') }}
-
- {{ __('Job') }}
-
+
+
+
+
+
+
-
- {{ __('Artifact') }}
+ {{ __('Job') }}
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
{{ $options.i18n.architecture }}
-
-
@{{ user.username }}
{{ title }}
@@ -43,11 +38,12 @@ export default {
{{ __("What's new") }}
diff --git a/app/assets/stylesheets/application_dark.scss b/app/assets/stylesheets/application_dark.scss
index 90aab7ce34..7d6ccc4027 100644
--- a/app/assets/stylesheets/application_dark.scss
+++ b/app/assets/stylesheets/application_dark.scss
@@ -57,4 +57,8 @@ body.gl-dark {
}
}
}
+
+ .md :not(pre.code) > code {
+ background-color: $gray-200;
+ }
}
diff --git a/app/assets/stylesheets/components/avatar.scss b/app/assets/stylesheets/components/avatar.scss
index c8f69bfdba..3885134e27 100644
--- a/app/assets/stylesheets/components/avatar.scss
+++ b/app/assets/stylesheets/components/avatar.scss
@@ -67,15 +67,12 @@ $avatar-sizes: (
)
);
-$identicon-backgrounds: $identicon-red, $identicon-purple, $identicon-indigo, $identicon-blue, $identicon-teal,
- $identicon-orange, $identicon-gray;
-
.avatar,
.avatar-container {
float: left;
margin-right: $gl-padding;
border-radius: $avatar-radius;
- border: 1px solid $gray-normal;
+ border: 1px solid $t-gray-a-08;
@each $size, $size-config in $avatar-sizes {
&.s#{$size} {
@@ -125,8 +122,8 @@ $identicon-backgrounds: $identicon-red, $identicon-purple, $identicon-indigo, $i
.identicon {
text-align: center;
vertical-align: top;
- color: $identicon-text-color;
- background-color: $identicon-gray;
+ color: $gray-900;
+ background-color: $gray-50;
// Sizes
@each $size, $size-config in $avatar-sizes {
@@ -143,9 +140,9 @@ $identicon-backgrounds: $identicon-red, $identicon-purple, $identicon-indigo, $i
}
// Background colors
- @for $i from 1 through length($identicon-backgrounds) {
+ @for $i from 1 through length($gl-avatar-identicon-bgs) {
&.bg#{$i} {
- background-color: nth($identicon-backgrounds, $i);
+ background-color: nth($gl-avatar-identicon-bgs, $i);
}
}
}
diff --git a/app/assets/stylesheets/components/batch_comments/review_bar.scss b/app/assets/stylesheets/components/batch_comments/review_bar.scss
index d769ea7310..bcd0697481 100644
--- a/app/assets/stylesheets/components/batch_comments/review_bar.scss
+++ b/app/assets/stylesheets/components/batch_comments/review_bar.scss
@@ -2,13 +2,15 @@
position: fixed;
bottom: 0;
left: 0;
- width: 100%;
- background: $white;
z-index: $zindex-dropdown-menu;
- padding: 7px 0 6px; // to keep aligned with "collapse sidebar" button on the left sidebar
- border-top: 1px solid $border-color;
+ display: flex;
+ align-items: center;
+ width: 100%;
+ height: $toggle-sidebar-height;
padding-left: $contextual-sidebar-width;
padding-right: $gutter_collapsed_width;
+ background: $white;
+ border-top: 1px solid $border-color;
transition: padding $sidebar-transition-duration;
.page-with-icon-sidebar & {
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 2fbdaaaf46..804cc20527 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -39,7 +39,7 @@
@import 'framework/selects';
@import 'framework/sidebar';
@import 'framework/contextual_sidebar_header';
-@import 'framework/contextual_sidebar_refactoring/contextual_sidebar';
+@import 'framework/contextual_sidebar';
@import 'framework/tables';
@import 'framework/notes';
@import 'framework/tabs';
@@ -69,5 +69,5 @@
@import 'framework/system_messages';
@import 'framework/spinner';
@import 'framework/card';
-@import 'framework/editor-lite';
+@import 'framework/source_editor';
@import 'framework/diffs';
diff --git a/app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar_variant.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
similarity index 71%
rename from app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar_variant.scss
rename to app/assets/stylesheets/framework/contextual_sidebar.scss
index 1ea5028120..f5002a342b 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar_variant.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -1,80 +1,3 @@
-//
-// VARIABLES
-//
-
-$top-level-item-color: $purple-900;
-
-//
-// TEMPORARY OVERRIDES
-// Needed while we serve both *_base and *_variant stylesheets
-// TODO: These have to be removed during the ':sidebar_refactor' flag rollout
-//
-&.gl-dark .nav-sidebar li.active {
- box-shadow: none;
-}
-
-&.gl-dark .nav-sidebar .sidebar-sub-level-items {
- box-shadow: none;
- border: 1px solid $border-color;
-}
-
-&.gl-dark .sidebar-top-level-items .context-header a .avatar-container.rect-avatar .avatar.s32 {
- color: $white;
-}
-
-&.gl-dark .nav-sidebar li a,
-&.gl-dark .toggle-sidebar-button .collapse-text,
-&.gl-dark .toggle-sidebar-button .icon-chevron-double-lg-left,
-&.gl-dark .toggle-sidebar-button .icon-chevron-double-lg-right,
-&.gl-dark .sidebar-top-level-items .context-header a .sidebar-context-title,
-&.gl-dark .nav-sidebar-inner-scroll > div.context-header a .sidebar-context-title,
-&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a,
-&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a:hover,
-&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item.active a,
-&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item .fly-out-top-item-container {
- color: $gray-darkest;
-}
-
-&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a,
-&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a:hover,
-&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item.active a,
-&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item .fly-out-top-item-container {
- @include gl-mt-0;
-}
-
-&.gl-dark .nav-sidebar a:not(.has-sub-items) + .sidebar-sub-level-items .fly-out-top-item a,
-&.gl-dark .nav-sidebar a:not(.has-sub-items) + .sidebar-sub-level-items .fly-out-top-item a:hover,
-&.gl-dark .nav-sidebar a:not(.has-sub-items) + .sidebar-sub-level-items .fly-out-top-item.active a,
-&.gl-dark .nav-sidebar a:not(.has-sub-items) + .sidebar-sub-level-items .fly-out-top-item .fly-out-top-item-container {
- background: $white;
- color: $gray-darkest;
-
- &::before {
- border-right-color: $white;
- }
-}
-
-&.gl-dark .nav-sidebar .sidebar-sub-level-items {
- background-color: $white;
-}
-
-&.ui-indigo .nav-sidebar li.active:not(.fly-out-top-item) > a {
- color: $top-level-item-color;
-}
-
-&.ui-indigo .nav-sidebar li.active .nav-icon-container svg {
- fill: $top-level-item-color;
-}
-
-.nav-sidebar {
- box-shadow: none;
-
- li.active {
- background-color: transparent;
- box-shadow: none !important; // TODO: This should be updated in `theme_helper.scss` together with ':sidebar_refactor' rollout
- }
-}
-
//
// MIXINS
//
@@ -112,7 +35,6 @@ $top-level-item-color: $purple-900;
.icon-chevron-double-lg-left {
@include gl-rotate-180;
- @include gl-display-block; // TODO: shouldn't be needed after the flag roll out
@include gl-m-0;
}
}
@@ -219,15 +141,10 @@ $top-level-item-color: $purple-900;
.avatar.s32 {
@extend .rect-avatar.s32;
- //color: $gray-900;
box-shadow: $avatar-box-shadow;
}
}
}
-
- .sidebar-context-title {
- color: $top-level-item-color;
- }
}
@mixin top-level-item {
@@ -258,8 +175,6 @@ $top-level-item-color: $purple-900;
@include gl-cursor-default;
@include gl-pointer-events-none;
@include gl-font-sm;
- background-color: $purple-900;
- color: $white;
@if $has-sub-items {
@include gl-mt-0;
@@ -269,7 +184,8 @@ $top-level-item-color: $purple-900;
@include gl-my-n2;
@include gl-mt-0;
@include gl-relative;
- background-color: $black;
+ @include gl-text-white;
+ background: var(--black, $black);
strong {
@include gl-font-weight-normal;
@@ -287,6 +203,7 @@ $top-level-item-color: $purple-900;
border-top: $gl-spacing-scale-2 solid transparent;
border-bottom: $gl-spacing-scale-2 solid transparent;
border-right: $gl-spacing-scale-2 solid $black;
+ border-right-color: var(--black, $black);
}
}
}
@@ -343,7 +260,7 @@ $top-level-item-color: $purple-900;
a {
@include gl-text-decoration-none;
- color: $top-level-item-color;
+ color: $gray-900;
}
li {
@@ -392,13 +309,19 @@ $top-level-item-color: $purple-900;
}
a.has-sub-items + .sidebar-sub-level-items {
- @include gl-mt-n2;
-
.fly-out-top-item {
@include fly-out-top-item($has-sub-items: true);
}
}
+ a.has-sub-items + .sidebar-sub-level-items.fly-out-list {
+ @include gl-mt-n2;
+
+ &.is-above {
+ @include gl-mt-2;
+ }
+ }
+
@media (min-width: map-get($grid-breakpoints, md)) and (max-width: map-get($grid-breakpoints, xl) - 1px) {
&:not(.sidebar-expanded-mobile) {
@include collapse-contextual-sidebar;
@@ -445,8 +368,8 @@ $top-level-item-color: $purple-900;
}
.badge.badge-pill {
- @include gl-font-weight-normal; // TODO: update in `theme_helper.scss`
- color: $blue-700; // TODO: update in `theme_helper.scss`
+ @include gl-font-weight-normal;
+ color: $blue-700;
}
}
}
@@ -484,7 +407,6 @@ $top-level-item-color: $purple-900;
@include side-panel-toggle;
background-color: $gray-50;
border-top: 1px solid $border-color;
- color: $top-level-item-color;
position: fixed;
bottom: 0;
width: $contextual-sidebar-width;
@@ -538,14 +460,10 @@ $top-level-item-color: $purple-900;
//
// PANELS-SPECIFIC
-// TODO: Check whether we can remove these in favor of the utility-classes
//
.settings-avatar {
- background-color: $white;
-
svg {
- fill: $gl-text-color-secondary;
margin: auto;
}
}
diff --git a/app/assets/stylesheets/framework/contextual_sidebar_header.scss b/app/assets/stylesheets/framework/contextual_sidebar_header.scss
index fdd03f4cdc..7159dadf7c 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar_header.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar_header.scss
@@ -32,6 +32,7 @@
.sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
+ color: $gray-900;
&.text-secondary {
font-weight: normal;
diff --git a/app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar.scss
deleted file mode 100644
index 905ac26020..0000000000
--- a/app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-body:not(.sidebar-refactoring) {
- @import 'contextual_sidebar_base';
-}
-
-body.sidebar-refactoring {
- @import 'contextual_sidebar_variant';
-}
diff --git a/app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar_base.scss b/app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar_base.scss
deleted file mode 100644
index 306a9b74eb..0000000000
--- a/app/assets/stylesheets/framework/contextual_sidebar_refactoring/contextual_sidebar_base.scss
+++ /dev/null
@@ -1,386 +0,0 @@
-@mixin collapse-contextual-sidebar-content {
-
- @include context-header-collapsed;
-
- .sidebar-top-level-items > li {
- .sidebar-sub-level-items {
- &:not(.flyout-list) {
- display: none;
- }
- }
- }
-
- .nav-icon-container {
- margin-right: 0;
- }
-
- .toggle-sidebar-button {
- padding: 16px;
- width: $contextual-sidebar-collapsed-width - 1px;
-
- .collapse-text,
- .icon-chevron-double-lg-left {
- display: none;
- }
-
- .icon-chevron-double-lg-right {
- display: block;
- margin: 0;
- }
- }
-}
-
-@mixin collapse-contextual-sidebar {
- width: $contextual-sidebar-collapsed-width;
-
- .nav-sidebar-inner-scroll {
- overflow-x: hidden;
- }
-
- .badge.badge-pill:not(.fly-out-badge),
- .nav-item-name {
- @include gl-sr-only;
- }
-
- .sidebar-top-level-items > li > a {
- min-height: 45px;
- }
-
- .fly-out-top-item {
- display: block;
- }
-
- .avatar-container {
- margin: 0 auto;
- }
-}
-
-@at-root {
- .page-with-contextual-sidebar {
- transition: padding-left $sidebar-transition-duration;
-
- @include media-breakpoint-up(md) {
- padding-left: $contextual-sidebar-collapsed-width;
- }
-
- @include media-breakpoint-up(xl) {
- padding-left: $contextual-sidebar-width;
- }
-
- .issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header {
- padding: 10px 0 15px;
- }
- }
-
- .page-with-icon-sidebar {
- @include media-breakpoint-up(md) {
- padding-left: $contextual-sidebar-collapsed-width;
- }
- }
-
- .settings-avatar {
- background-color: $white;
-
- svg {
- fill: $gl-text-color-secondary;
- margin: auto;
- }
- }
-
- .nav-sidebar {
- transition: width $sidebar-transition-duration, left $sidebar-transition-duration;
- position: fixed;
- z-index: 600;
- width: $contextual-sidebar-width;
- top: $header-height;
- bottom: 0;
- left: 0;
- background-color: $gray-light;
- box-shadow: inset -1px 0 0 $border-color;
- transform: translate3d(0, 0, 0);
-
- &:not(.sidebar-collapsed-desktop) {
- @media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
- box-shadow: inset -1px 0 0 $border-color, 2px 1px 3px $dropdown-shadow-color;
- }
- }
-
- &.sidebar-collapsed-desktop {
- @include collapse-contextual-sidebar;
- }
-
- &.sidebar-expanded-mobile {
- left: 0;
- }
-
- a {
- text-decoration: none;
- }
-
- ul {
- padding-left: 0;
- list-style: none;
- }
-
- li {
- white-space: nowrap;
-
- a {
- transition: padding $sidebar-transition-duration;
- display: flex;
- align-items: center;
- padding: 12px $gl-padding;
- color: $gl-text-color-secondary;
- }
-
- .nav-item-name {
- flex: 1;
- }
-
- &.active {
- > a {
- font-weight: $gl-font-weight-bold;
- }
- }
- }
-
- @include media-breakpoint-down(sm) {
- left: (-$contextual-sidebar-width);
- }
-
- .nav-icon-container {
- display: flex;
- margin-right: 8px;
- }
-
- .fly-out-top-item {
- display: none;
- }
-
- svg {
- height: 16px;
- width: 16px;
- }
-
- @media (min-width: map-get($grid-breakpoints, md)) and (max-width: map-get($grid-breakpoints, xl) - 1px) {
- &:not(.sidebar-expanded-mobile) {
- @include collapse-contextual-sidebar;
- @include collapse-contextual-sidebar-content;
- }
- }
- }
-
- .nav-sidebar-inner-scroll {
- height: 100%;
- width: 100%;
- overflow: auto;
- }
-
- .sidebar-sub-level-items {
- display: none;
- padding-bottom: 8px;
-
- > li {
- a {
- padding: 8px 16px 8px 40px;
-
- &:hover,
- &:focus {
- background: $link-active-background;
- color: $gl-text-color;
- }
- }
-
- &.active {
- a {
- &,
- &:hover,
- &:focus {
- background: $link-active-background;
- }
- }
- }
- }
- }
-
- .sidebar-top-level-items {
- margin-bottom: 60px;
-
- > li {
- > a {
- @include media-breakpoint-up(sm) {
- margin-right: 1px;
- }
-
- &:hover {
- color: $gl-text-color;
- }
- }
-
- &.is-showing-fly-out {
- > a {
- margin-right: 1px;
- }
-
- .sidebar-sub-level-items {
- @include media-breakpoint-up(sm) {
- position: fixed;
- top: 0;
- left: 0;
- min-width: 150px;
- margin-top: -1px;
- padding: 4px 1px;
- background-color: $white;
- box-shadow: 2px 1px 3px $dropdown-shadow-color;
- border: 1px solid $gray-darker;
- border-left: 0;
- border-radius: 0 3px 3px 0;
-
- &::before {
- content: '';
- position: absolute;
- top: -30px;
- bottom: -30px;
- left: -10px;
- right: -30px;
- z-index: -1;
- }
-
- &.is-above {
- margin-top: 1px;
- }
-
- .divider {
- height: 1px;
- margin: 4px -1px;
- padding: 0;
- background-color: $dropdown-divider-bg;
- }
-
- > .active {
- box-shadow: none;
-
- > a {
- background-color: transparent;
- }
- }
-
- a {
- padding: 8px 16px;
- color: $gl-text-color;
-
- &:hover,
- &:focus {
- background-color: $gray-darker;
- }
- }
- }
- }
- }
-
- .badge.badge-pill {
- background-color: $inactive-badge-background;
- color: $gl-text-color-secondary;
- }
-
- &.active {
- background: $link-active-background;
-
- > a {
- margin-left: 4px;
- // Subtract width of left border on active element
- padding-left: $gl-padding-12;
- }
-
- .badge.badge-pill {
- font-weight: $gl-font-weight-bold;
- }
-
- .sidebar-sub-level-items:not(.is-fly-out-only) {
- display: block;
- }
- }
-
- &.active > a:hover,
- &.is-over > a {
- background-color: $link-hover-background;
- }
- }
- }
-
- // Collapsed nav
-
- .toggle-sidebar-button,
- .close-nav-button {
- @include side-panel-toggle;
- }
-
- .toggle-sidebar-button,
- .close-nav-button {
- position: fixed;
- bottom: 0;
- width: $contextual-sidebar-width - 1px;
- border-top: 1px solid $border-color;
-
- svg {
- margin-right: 8px;
- }
-
- .icon-chevron-double-lg-right {
- display: none;
- }
- }
-
- .collapse-text {
- white-space: nowrap;
- overflow: hidden;
- }
-
- .sidebar-collapsed-desktop {
- @include collapse-contextual-sidebar-content;
- }
-
- .fly-out-top-item {
- > a {
- display: flex;
- }
-
- .fly-out-badge {
- margin-left: 8px;
- }
- }
-
- .fly-out-top-item-name {
- flex: 1;
- }
-
- // Mobile nav
-
- .close-nav-button {
- display: none;
- }
-
- @include media-breakpoint-down(sm) {
- .close-nav-button {
- display: flex;
- }
-
- .toggle-sidebar-button {
- display: none;
- }
-
- .mobile-overlay {
- display: none;
-
- &.mobile-nav-open {
- display: block;
- position: fixed;
- background-color: $black-transparent;
- height: 100%;
- width: 100%;
- z-index: $zindex-dropdown-menu;
- }
- }
- }
-}
-
diff --git a/app/assets/stylesheets/framework/diffs.scss b/app/assets/stylesheets/framework/diffs.scss
index c0e9289309..f8b1735207 100644
--- a/app/assets/stylesheets/framework/diffs.scss
+++ b/app/assets/stylesheets/framework/diffs.scss
@@ -847,8 +847,6 @@ table.code {
.commit-stat-summary {
@include media-breakpoint-up(sm) {
- margin-left: -$gl-padding;
- padding-left: $gl-padding;
background-color: $white;
}
}
@@ -1190,3 +1188,9 @@ table.code {
margin-top: 0;
}
}
+
+// Note: Prevents tall files from appearing above sticky tabs
+.diffs .vue-recycle-scroller__item-view > div:not(.active) {
+ position: absolute;
+ bottom: 100vh;
+}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 894eddbe1a..144a396ea6 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -30,7 +30,9 @@
.dropdown-menu {
@include set-visible;
min-height: $dropdown-min-height;
- max-height: $dropdown-max-height;
+ // Prevents double scrollbar on dropdowns that also
+ // have max-height set on an inner scrollable element
+ max-height: $dropdown-max-height-lg;
overflow-y: auto;
&.dropdown-extended-height {
@@ -239,7 +241,7 @@
max-width: 500px;
margin-top: $dropdown-vertical-offset;
margin-bottom: 24px;
- font-size: 14px;
+ font-size: 0.875rem;
font-weight: $gl-font-weight-normal;
padding: 8px 0;
background-color: $white;
@@ -931,13 +933,9 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu {
}
.frequent-items-list-item-container {
- .frequent-items-item-avatar-container,
- .frequent-items-item-metadata-container {
- flex-shrink: 0;
- }
-
.frequent-items-item-metadata-container {
display: flex;
+ flex-shrink: 0;
flex-direction: column;
justify-content: center;
}
@@ -949,12 +947,6 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu {
white-space: nowrap;
}
- &:hover {
- .frequent-items-item-avatar-container .avatar {
- border-color: $gray-50;
- }
- }
-
.frequent-items-item-title {
font-size: $gl-font-size;
font-weight: 400;
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index bda123fa7e..5ad7ceecb2 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -508,3 +508,25 @@ span.idiff {
}
}
}
+
+//
+// IMPORTANT PERFORMANCE OPTIMIZATION BELOW
+//
+// * :nth-of-type(1n+70) - makes sure we do not render lines 71+ right
+// away. Even though the HTML is injected in the DOM, as long as we do
+// not render those lines, the browser doesn't need to spend resources
+// calculating and repainting what's hidden.
+//
+// * :not(:last-of-type) makes sure that we output the last line of the
+// blob's snippet. This is important because the column with the line
+// numbers has auto width and is expanding based on the content in it.
+// This leads to unnecessary layout shift when the last lines of the
+// snippet are longer than two (2) digits.
+// EXAMPLE: Let's say, we have a blob with 100 lines. If we output 70
+// lines, and then, the remaining 30 (incl the line 100), it will lead
+// to the layout reflow and styles recalculation when we output line
+// 100 (because the width of '100' is always bigger than '70'). By
+// outputting the last line right away, we prevent that as the column
+// will always be expanded to the maximum needed width.
+.blob-viewer[data-loading] .file-content.code .line:nth-of-type(1n+70):not(:last-of-type),
+.blob-viewer[data-loading] .file-content.code .file-line-num:nth-of-type(1n+70):not(:last-of-type) {display: none !important;}
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index d5f7ec6845..30a1c8af41 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -56,24 +56,19 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
}
.flash-alert {
- background-color: $red-100;
- color: $red-700;
+ background-color: $red-50;
}
.flash-notice {
- background-color: $blue-100;
- color: $blue-700;
+ background-color: $blue-50;
}
.flash-success {
- background-color: $theme-green-100;
- color: $green-700;
+ background-color: $green-50;
}
.flash-warning {
background-color: $orange-50;
- color: $gray-900;
- cursor: default;
}
.flash-text,
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 8639b9a7f8..65d914e47c 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -555,7 +555,8 @@ $top-nav-hover-bg: var(--indigo-900-alpha-008, $indigo-900-alpha-008) !important
visibility: visible;
}
-.with-performance-bar .navbar-gitlab {
+.with-performance-bar .navbar-gitlab,
+.with-performance-bar .fixed-top {
top: $performance-bar-height;
}
@@ -563,7 +564,7 @@ $top-nav-hover-bg: var(--indigo-900-alpha-008, $indigo-900-alpha-008) !important
justify-content: center;
height: $header-height;
background: $white;
- border-bottom: 1px solid $white-normal;
+ border-bottom: 1px solid $gray-100;
.tanuki-logo,
.brand-header-logo {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 9fe9f9a845..d2bb1e3d55 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -149,12 +149,6 @@ ul.content-list {
margin-right: $grid-size;
display: inline-block;
- &.btn-ldap-override {
- @include media-breakpoint-up(sm) {
- margin-bottom: 0;
- }
- }
-
&.has-tooltip,
&:last-child {
margin-right: 0;
diff --git a/app/assets/stylesheets/framework/editor-lite.scss b/app/assets/stylesheets/framework/source_editor.scss
similarity index 96%
rename from app/assets/stylesheets/framework/editor-lite.scss
rename to app/assets/stylesheets/framework/source_editor.scss
index 05b53e0c3d..a967d9a71f 100644
--- a/app/assets/stylesheets/framework/editor-lite.scss
+++ b/app/assets/stylesheets/framework/source_editor.scss
@@ -21,11 +21,11 @@
}
}
-[id^='editor-lite-'] {
+[id^='source-editor-'] {
height: 500px;
}
-.monaco-editor.gl-editor-lite {
+.monaco-editor.gl-source-editor {
.margin-view-overlays {
.line-numbers {
@include gl-display-flex;
diff --git a/app/assets/stylesheets/framework/system_messages.scss b/app/assets/stylesheets/framework/system_messages.scss
index 437915d503..1cb34bea06 100644
--- a/app/assets/stylesheets/framework/system_messages.scss
+++ b/app/assets/stylesheets/framework/system_messages.scss
@@ -60,7 +60,8 @@
// System Header
&.with-performance-bar {
// main navigation
- header.navbar-gitlab {
+ header.navbar-gitlab,
+ .fixed-top {
top: $performance-bar-height + $system-header-height;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index d3976cfa8c..726f8e28ef 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -633,18 +633,6 @@ $note-disabled-comment-color: #b2b2b2;
$note-targe3-outside: #fffff0;
$note-targe3-inside: #ffffd3;
-/*
-* Identicon
-*/
-$identicon-text-color: #525252 !default;
-$identicon-red: #ffebee !default;
-$identicon-purple: #f3e5f5 !default;
-$identicon-indigo: #e8eaf6 !default;
-$identicon-blue: #e3f2fd !default;
-$identicon-teal: #e0f2f1 !default;
-$identicon-orange: #fbe9e7 !default;
-$identicon-gray: #eee !default;
-
/*
* Calendar
*/
diff --git a/app/assets/stylesheets/page_bundles/admin/application_settings_metrics_and_profiling.scss b/app/assets/stylesheets/page_bundles/admin/application_settings_metrics_and_profiling.scss
index 41bb6d107f..db81cc7fdd 100644
--- a/app/assets/stylesheets/page_bundles/admin/application_settings_metrics_and_profiling.scss
+++ b/app/assets/stylesheets/page_bundles/admin/application_settings_metrics_and_profiling.scss
@@ -1,3 +1,3 @@
-.usage-data {
+.service-data-payload-container {
max-height: 400px;
}
diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss
index a00a71b07e..428bd90ddd 100644
--- a/app/assets/stylesheets/page_bundles/boards.scss
+++ b/app/assets/stylesheets/page_bundles/boards.scss
@@ -472,6 +472,10 @@
.sidebar-collapsed-icon {
display: none;
}
+
+ .gl-drawer-header {
+ align-items: flex-start;
+ }
}
.board-header-collapsed-info-icon:hover {
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index 009019a45d..47580e37ec 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -771,6 +771,12 @@ $ide-commit-header-height: 48px;
}
.dropdown-menu-toggle {
+ background-color: var(--ide-input-background, transparent);
+
+ &:hover {
+ background-color: var(--ide-dropdown-btn-hover-background, $white-normal);
+ }
+
svg {
vertical-align: middle;
@@ -779,16 +785,6 @@ $ide-commit-header-height: 48px;
color: var(--ide-text-color-secondary, $gray-500);
}
}
-
- &:hover {
- background-color: var(--ide-dropdown-btn-hover-background, $white-normal);
- }
- }
-
- &.show {
- .dropdown-menu-toggle {
- background-color: var(--ide-input-background, $white-dark);
- }
}
}
diff --git a/app/assets/stylesheets/page_bundles/members.scss b/app/assets/stylesheets/page_bundles/members.scss
index 7b4c74b825..62dd3dcb9c 100644
--- a/app/assets/stylesheets/page_bundles/members.scss
+++ b/app/assets/stylesheets/page_bundles/members.scss
@@ -1,10 +1,5 @@
@import 'mixins_and_variables_and_functions';
-.project-members-title {
- padding-bottom: 10px;
- border-bottom: 1px solid $border-color;
-}
-
.invite-users-form {
.btn-success {
margin-right: 10px;
@@ -12,12 +7,6 @@
}
.member {
- &.is-overridden {
- .btn-ldap-override {
- display: none !important;
- }
- }
-
.controls {
@include media-breakpoint-up(sm) {
display: flex;
@@ -31,111 +20,12 @@
.form-group {
margin-bottom: 0;
}
-
- &.existing-title {
- @include media-breakpoint-up(sm) {
- float: left;
- }
- }
-}
-
-.member-form-control {
- @include media-breakpoint-down(xs) {
- margin-right: 0;
- width: auto;
- }
-}
-
-.member-search-btn {
- position: absolute;
- right: 4px;
- top: 0;
- height: $input-height;
- padding-left: 10px;
- padding-right: 10px;
- color: $gray-darkest;
- background: transparent;
- border: 0;
- outline: 0;
}
.members-ldap {
align-self: center;
}
-.alert-member-ldap {
- background-color: $orange-50;
-
- @include media-breakpoint-up(sm) {
- line-height: 40px;
- }
-
- > p {
- float: left;
- margin-bottom: 10px;
- color: $orange-600;
-
- @include media-breakpoint-up(sm) {
- padding-left: 55px;
- margin-bottom: 0;
- }
- }
-
- .controls {
- width: 100%;
-
- @include media-breakpoint-up(sm) {
- width: auto;
- }
- }
-}
-
-.btn-ldap-override {
- width: 100%;
-
- @include media-breakpoint-up(sm) {
- margin-left: 10px;
- width: auto;
- }
-}
-
-.flex-project-members-panel {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: center;
-
- @include media-breakpoint-down(sm) {
- display: block;
-
- .flex-project-title {
- vertical-align: top;
- display: inline-block;
- max-width: 90%;
- }
- }
-
- .flex-project-title {
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
-
- .badge.badge-pill {
- height: 17px;
- line-height: 16px;
- margin-right: 5px;
- padding-top: 1px;
- padding-bottom: 1px;
- }
-
- .flex-users-form {
- flex-wrap: nowrap;
- white-space: nowrap;
- margin-left: auto;
- }
-}
-
.card {
.card-header {
.badge.badge-pill {
@@ -168,33 +58,11 @@
word-break: break-all;
}
- .form-control {
- width: inherit;
- }
-
- .btn {
- align-self: flex-start;
- }
-
@include media-breakpoint-down(sm) {
.member-access-text {
margin: 0 0 $gl-padding-4 ($grid-size * 6);
}
}
-
- @include media-breakpoint-down(xs) {
- display: block;
-
- .controls > .btn,
- .controls .member-form-control {
- margin: 0 0 $gl-padding-8;
- display: block;
- }
-
- .form-control {
- width: 100%;
- }
- }
}
@@ -231,25 +99,5 @@
float: none;
display: block;
}
-
- .dropdown-menu-toggle,
- .dropdown-menu,
- .form-control,
- .list-item-name {
- width: 100%;
- }
-
- .dropdown-menu {
- margin-top: 0;
- }
-
- .member-form-control {
- margin: 5px 0;
- }
-
- .btn {
- width: 100%;
- margin-left: 0;
- }
}
}
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index 5e9dd88363..6a20ff3b3f 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -7,6 +7,7 @@
.diff-files-holder {
flex: 1;
min-width: 0;
+ z-index: 203;
.vue-recycle-scroller__item-wrapper {
overflow: visible;
diff --git a/app/assets/stylesheets/page_bundles/milestone.scss b/app/assets/stylesheets/page_bundles/milestone.scss
index 03dd12ec23..08d9d24d24 100644
--- a/app/assets/stylesheets/page_bundles/milestone.scss
+++ b/app/assets/stylesheets/page_bundles/milestone.scss
@@ -65,15 +65,32 @@ $status-box-line-height: 26px;
line-height: $line-height-base;
padding: 14px 16px;
display: flex;
+ justify-content: space-between;
.title {
flex: 1;
flex-grow: 2;
}
- .counter {
- flex: 0;
- padding-left: 16px;
+ .issuable-count-weight {
+ white-space: nowrap;
+
+ .counter,
+ .weight {
+ color: var(--gray-500, $gray-500);
+ font-weight: $gl-font-weight-bold;
+ }
+ }
+
+ &.text-white {
+ .issuable-count-weight svg {
+ fill: $white;
+ }
+
+ .issuable-count-weight .counter,
+ .weight {
+ color: var(--white, $white);
+ }
}
}
}
diff --git a/app/assets/stylesheets/page_bundles/new_namespace.scss b/app/assets/stylesheets/page_bundles/new_namespace.scss
index 60aa3c8f29..189f010bdb 100644
--- a/app/assets/stylesheets/page_bundles/new_namespace.scss
+++ b/app/assets/stylesheets/page_bundles/new_namespace.scss
@@ -8,10 +8,11 @@ $new-namespace-panel-height: 240px;
}
.new-namespace-panel-wrapper {
- @include media-breakpoint-down(md) {
+ width: 50%;
+
+ @include media-breakpoint-down(lg) {
width: 100%;
}
- width: 50%;
}
.new-namespace-panel {
diff --git a/app/assets/stylesheets/page_bundles/pipelines.scss b/app/assets/stylesheets/page_bundles/pipelines.scss
index 1081dd8f6d..7b54be5c91 100644
--- a/app/assets/stylesheets/page_bundles/pipelines.scss
+++ b/app/assets/stylesheets/page_bundles/pipelines.scss
@@ -62,6 +62,12 @@
.pipeline-tags .label-container {
white-space: normal;
}
+
+ .dark-mode-override {
+ .gl-dark & {
+ background-color: $white;
+ }
+ }
}
// Mini Pipelines
diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss
index 98074f8af2..d233adbf3d 100644
--- a/app/assets/stylesheets/pages/clusters.scss
+++ b/app/assets/stylesheets/pages/clusters.scss
@@ -87,6 +87,20 @@
width: 145px;
}
+ .empty-state--agent {
+ .text-content {
+ @include gl-max-w-full;
+ @include media-breakpoint-up(lg) {
+ max-width: 70%;
+ }
+ }
+
+ .gl-alert-actions {
+ @include gl-mt-0;
+ @include gl-flex-wrap;
+ }
+ }
+
.top-area .nav-controls > .btn.btn-add-cluster {
margin-right: 0;
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index a114a1dc82..5173aeb824 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -183,6 +183,8 @@
}
.commit-nav-buttons {
+ margin: 0 0.5rem;
+
a.btn,
button {
// See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/730
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 1bc6dfbd84..ee97e8af29 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -175,7 +175,8 @@
}
}
- .block {
+ .block,
+ .issuable-sidebar-header {
@include clearfix;
padding: $gl-padding 0;
border-bottom: 1px solid $border-gray-normal;
@@ -184,11 +185,6 @@
width: $gutter-inner-width;
// --
- &.issuable-sidebar-header {
- padding-top: 0;
- padding-bottom: 10px;
- }
-
&:last-child {
border: 0;
}
@@ -273,10 +269,6 @@
padding: 0 20px;
}
- .issuable-sidebar-header {
- padding-top: 10px;
- }
-
&:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
.issuable-sidebar-header {
display: none;
@@ -302,7 +294,6 @@
}
.gutter-toggle {
- margin-top: 7px;
border-left: 1px solid $border-gray-normal;
text-align: center;
}
@@ -331,20 +322,21 @@
width: $gutter-collapsed-width;
padding: 0;
- .block {
+ .block,
+ .issuable-sidebar-header {
width: $gutter-collapsed-width - 2px;
padding: 0;
border-bottom: 0;
overflow: hidden;
+ }
+ .block,
+ .gutter-toggle,
+ .sidebar-collapsed-container {
&.with-sub-blocks .sub-block:hover,
&:not(.with-sub-blocks):hover {
background-color: $gray-100;
}
-
- &.issuable-sidebar-header {
- padding-top: 0;
- }
}
.participants {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 1abaff40bc..8807ab5e59 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -87,6 +87,10 @@ $tabs-holder-z-index: 250;
border: 1px solid $border-color;
border-radius: $border-radius-default;
background: var(--white, $white);
+
+ > .mr-widget-border-top:first-of-type {
+ border-top: 0;
+ }
}
.mr-widget-body,
@@ -702,7 +706,7 @@ $tabs-holder-z-index: 250;
.mr-version-dropdown,
.mr-version-compare-dropdown {
- margin: 0 7px;
+ margin: 0 0.5rem;
}
.dropdown-title {
@@ -711,7 +715,7 @@ $tabs-holder-z-index: 250;
// Shortening button height by 1px to make compare-versions
// header 56px and fit into our 8px design grid
- button {
+ .btn {
height: 34px;
}
@@ -885,7 +889,7 @@ $tabs-holder-z-index: 250;
.media-body {
min-width: 0;
font-size: 12px;
- margin-left: 40px;
+ margin-left: 32px;
}
&:not(:last-child) {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 01739c7eb3..4a86648980 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -763,6 +763,7 @@ $system-note-svg-size: 16px;
.note-button.add-diff-note {
@include btn-comment-icon;
opacity: 0;
+ will-change: opacity;
&[disabled] {
background: $white;
diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss
index ad040f65f3..d38c1818f5 100644
--- a/app/assets/stylesheets/snippets.scss
+++ b/app/assets/stylesheets/snippets.scss
@@ -54,6 +54,8 @@
white-space: pre;
word-wrap: normal;
border-left: $border-style;
+ text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%; /* stylelint-disable-line property-no-vendor-prefix */
}
code {
@@ -65,7 +67,7 @@
}
.line-numbers {
- padding: 10px;
+ padding: 10px 10px 10px 0;
text-align: right;
float: left;
@@ -86,18 +88,24 @@
}
}
+ .file-actions {
+ flex-shrink: 0;
+ }
+
.file-title-flex-parent {
display: flex;
- align-items: center;
+ align-items: flex-start;
justify-content: space-between;
background-color: $gray-light;
border: $border-style;
border-bottom: 0;
- padding: $gl-padding-top $gl-padding;
+ padding: $gl-padding;
margin: 0;
border-radius: $border-radius-default $border-radius-default 0 0;
.file-header-content {
+ max-width: 75%;
+
.file-title-name {
font-weight: $gl-font-weight-bold;
}
@@ -105,6 +113,7 @@
.gitlab-embedded-snippets-title {
text-decoration: none;
color: $gl-text-color;
+ word-break: break-word;
&:hover {
text-decoration: underline;
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index 00a6ee579d..a497f56f3b 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -13,6 +13,10 @@ body.gl-dark {
--orange-400: #ab6100;
--gl-text-color: #fafafa;
--border-color: #4f4f4f;
+ --black: #fff;
+}
+.nav-sidebar li.active {
+ box-shadow: none;
}
:root {
--white: #333;
@@ -145,10 +149,6 @@ h1 {
color: transparent;
text-shadow: 0 0 0 #fafafa;
}
-.form-control::-ms-input-placeholder {
- color: #bfbfbf;
- opacity: 1;
-}
.form-control::placeholder {
color: #bfbfbf;
opacity: 1;
@@ -175,7 +175,6 @@ h1 {
color: #fafafa;
text-align: center;
vertical-align: middle;
- -moz-user-select: none;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
@@ -459,8 +458,7 @@ a {
border-top: 1px solid #404040;
}
.toggle-sidebar-button .collapse-text,
-.toggle-sidebar-button .icon-chevron-double-lg-left,
-.toggle-sidebar-button .icon-chevron-double-lg-right {
+.toggle-sidebar-button .icon-chevron-double-lg-left {
color: #999;
}
svg {
@@ -546,7 +544,7 @@ body {
max-width: 500px;
margin-top: 4px;
margin-bottom: 24px;
- font-size: 14px;
+ font-size: 0.875rem;
font-weight: 400;
padding: 8px 0;
background-color: #333;
@@ -626,9 +624,6 @@ input {
border-radius: 4px;
padding: 6px 10px;
}
-.form-control::-ms-input-placeholder {
- color: #868686;
-}
.form-control::placeholder {
color: #868686;
}
@@ -933,6 +928,7 @@ input {
.context-header .sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
+ color: #fafafa;
}
@media (min-width: 768px) {
.page-with-contextual-sidebar {
@@ -951,20 +947,14 @@ input {
}
.nav-sidebar {
position: fixed;
+ bottom: 0;
+ left: 0;
z-index: 600;
width: 220px;
top: 40px;
- bottom: 0;
- left: 0;
background-color: #303030;
- box-shadow: inset -1px 0 0 #404040;
transform: translate3d(0, 0, 0);
}
-@media (min-width: 576px) and (max-width: 576px) {
- .nav-sidebar:not(.sidebar-collapsed-desktop) {
- box-shadow: inset -1px 0 0 #404040, 2px 1px 3px rgba(0, 0, 0, 0.1);
- }
-}
.nav-sidebar.sidebar-collapsed-desktop {
width: 48px;
}
@@ -972,7 +962,8 @@ input {
overflow-x: hidden;
}
.nav-sidebar.sidebar-collapsed-desktop .badge.badge-pill:not(.fly-out-badge),
-.nav-sidebar.sidebar-collapsed-desktop .nav-item-name {
+.nav-sidebar.sidebar-collapsed-desktop .nav-item-name,
+.nav-sidebar.sidebar-collapsed-desktop .collapse-text {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
@@ -984,36 +975,50 @@ input {
width: 1px;
}
.nav-sidebar.sidebar-collapsed-desktop .sidebar-top-level-items > li > a {
- min-height: 45px;
+ min-height: unset;
}
-.nav-sidebar.sidebar-collapsed-desktop .fly-out-top-item {
- display: block;
+.nav-sidebar.sidebar-collapsed-desktop .fly-out-top-item:not(.divider) {
+ display: block !important;
}
.nav-sidebar.sidebar-collapsed-desktop .avatar-container {
margin: 0 auto;
}
+.nav-sidebar.sidebar-collapsed-desktop li.active:not(.fly-out-top-item) > a {
+ background-color: rgba(41, 41, 97, 0.08);
+}
.nav-sidebar a {
text-decoration: none;
-}
-.nav-sidebar ul {
- padding-left: 0;
- list-style: none;
+ color: #fafafa;
}
.nav-sidebar li {
white-space: nowrap;
}
-.nav-sidebar li a {
- display: flex;
- align-items: center;
- padding: 12px 16px;
- color: #999;
-}
.nav-sidebar li .nav-item-name {
flex: 1;
}
+.nav-sidebar li > a,
+.nav-sidebar li > .fly-out-top-item-container {
+ padding-left: 0.75rem;
+ padding-right: 0.75rem;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ display: flex;
+ align-items: center;
+ border-radius: 0.25rem;
+ width: auto;
+ line-height: 1rem;
+ margin: 1px 4px;
+}
.nav-sidebar li.active > a {
font-weight: 600;
}
+.nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) {
+ background-color: rgba(41, 41, 97, 0.08);
+}
+.nav-sidebar ul {
+ padding-left: 0;
+ list-style: none;
+}
@media (max-width: 767.98px) {
.nav-sidebar {
left: -220px;
@@ -1023,12 +1028,113 @@ input {
display: flex;
margin-right: 8px;
}
-.nav-sidebar .fly-out-top-item {
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item {
display: none;
}
-.nav-sidebar svg {
- height: 16px;
- width: 16px;
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ a,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item.active
+ a,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ .fly-out-top-item-container {
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ cursor: default;
+ pointer-events: none;
+ font-size: 0.75rem;
+ margin-top: -0.25rem;
+ margin-bottom: -0.25rem;
+ margin-top: 0;
+ position: relative;
+ color: #333;
+ background: var(--black, #fff);
+}
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ a
+ strong,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item.active
+ a
+ strong,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ .fly-out-top-item-container
+ strong {
+ font-weight: 400;
+}
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ a::before,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item.active
+ a::before,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ .fly-out-top-item-container::before {
+ position: absolute;
+ content: "";
+ display: block;
+ top: 50%;
+ left: -0.25rem;
+ margin-top: -0.25rem;
+ width: 0;
+ height: 0;
+ border-top: 0.25rem solid transparent;
+ border-bottom: 0.25rem solid transparent;
+ border-right: 0.25rem solid #fff;
+ border-right-color: var(--black, #fff);
+}
+.nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item {
+ display: none;
+}
+.nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a,
+.nav-sidebar
+ a.has-sub-items
+ + .sidebar-sub-level-items
+ .fly-out-top-item.active
+ a,
+.nav-sidebar
+ a.has-sub-items
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ .fly-out-top-item-container {
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ cursor: default;
+ pointer-events: none;
+ font-size: 0.75rem;
+ margin-top: 0;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
}
@media (min-width: 768px) and (max-width: 1199px) {
.nav-sidebar:not(.sidebar-expanded-mobile) {
@@ -1039,7 +1145,8 @@ input {
}
.nav-sidebar:not(.sidebar-expanded-mobile)
.badge.badge-pill:not(.fly-out-badge),
- .nav-sidebar:not(.sidebar-expanded-mobile) .nav-item-name {
+ .nav-sidebar:not(.sidebar-expanded-mobile) .nav-item-name,
+ .nav-sidebar:not(.sidebar-expanded-mobile) .collapse-text {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
@@ -1051,14 +1158,19 @@ input {
width: 1px;
}
.nav-sidebar:not(.sidebar-expanded-mobile) .sidebar-top-level-items > li > a {
- min-height: 45px;
+ min-height: unset;
}
- .nav-sidebar:not(.sidebar-expanded-mobile) .fly-out-top-item {
- display: block;
+ .nav-sidebar:not(.sidebar-expanded-mobile) .fly-out-top-item:not(.divider) {
+ display: block !important;
}
.nav-sidebar:not(.sidebar-expanded-mobile) .avatar-container {
margin: 0 auto;
}
+ .nav-sidebar:not(.sidebar-expanded-mobile)
+ li.active:not(.fly-out-top-item)
+ > a {
+ background-color: rgba(41, 41, 97, 0.08);
+ }
.nav-sidebar:not(.sidebar-expanded-mobile) .context-header {
height: 60px;
width: 48px;
@@ -1077,6 +1189,12 @@ input {
white-space: nowrap;
width: 1px;
}
+ .nav-sidebar:not(.sidebar-expanded-mobile) .context-header {
+ height: auto;
+ }
+ .nav-sidebar:not(.sidebar-expanded-mobile) .context-header a {
+ padding: 0.25rem;
+ }
.nav-sidebar:not(.sidebar-expanded-mobile)
.sidebar-top-level-items
> li
@@ -1087,21 +1205,17 @@ input {
margin-right: 0;
}
.nav-sidebar:not(.sidebar-expanded-mobile) .toggle-sidebar-button {
- padding: 16px;
- width: 47px;
+ width: 48px;
}
.nav-sidebar:not(.sidebar-expanded-mobile)
.toggle-sidebar-button
- .collapse-text,
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .toggle-sidebar-button
- .icon-chevron-double-lg-left {
+ .collapse-text {
display: none;
}
.nav-sidebar:not(.sidebar-expanded-mobile)
.toggle-sidebar-button
- .icon-chevron-double-lg-right {
- display: block;
+ .icon-chevron-double-lg-left {
+ transform: rotate(180deg);
margin: 0;
}
}
@@ -1110,43 +1224,89 @@ input {
width: 100%;
overflow: auto;
}
-.sidebar-sub-level-items {
- display: none;
- padding-bottom: 8px;
+.nav-sidebar-inner-scroll > div.context-header {
+ margin-top: 0.25rem;
}
-.sidebar-sub-level-items > li a {
- padding: 8px 16px 8px 40px;
+.nav-sidebar-inner-scroll > div.context-header a {
+ padding-left: 0.75rem;
+ padding-right: 0.75rem;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ display: flex;
+ align-items: center;
+ border-radius: 0.25rem;
+ width: auto;
+ line-height: 1rem;
+ margin: 1px 4px;
+ padding: 0.25rem;
+ margin-bottom: 0.25rem;
+ margin-top: 0;
}
-.sidebar-sub-level-items > li.active a {
- background: rgba(255, 255, 255, 0.04);
+.nav-sidebar-inner-scroll > div.context-header a .avatar-container {
+ font-weight: 400;
+ flex: none;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
+}
+.nav-sidebar-inner-scroll > div.context-header a .avatar-container.rect-avatar {
+ border-style: none;
+}
+.nav-sidebar-inner-scroll
+ > div.context-header
+ a
+ .avatar-container.rect-avatar
+ .avatar.s32 {
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.sidebar-top-level-items {
+ margin-top: 0.25rem;
margin-bottom: 60px;
}
-@media (min-width: 576px) {
- .sidebar-top-level-items > li > a {
- margin-right: 1px;
- }
+.sidebar-top-level-items .context-header a {
+ padding: 0.25rem;
+ margin-bottom: 0.25rem;
+ margin-top: 0;
+}
+.sidebar-top-level-items .context-header a .avatar-container {
+ font-weight: 400;
+ flex: none;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
+}
+.sidebar-top-level-items .context-header a .avatar-container.rect-avatar {
+ border-style: none;
+}
+.sidebar-top-level-items
+ .context-header
+ a
+ .avatar-container.rect-avatar
+ .avatar.s32 {
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.sidebar-top-level-items > li .badge.badge-pill {
- background-color: rgba(255, 255, 255, 0.08);
- color: #999;
-}
-.sidebar-top-level-items > li.active {
- background: rgba(255, 255, 255, 0.04);
-}
-.sidebar-top-level-items > li.active > a {
- margin-left: 4px;
- padding-left: 12px;
-}
-.sidebar-top-level-items > li.active .badge.badge-pill {
- font-weight: 600;
+ border-radius: 0.5rem;
+ padding-top: 0.125rem;
+ padding-bottom: 0.125rem;
+ padding-left: 0.5rem;
+ padding-right: 0.5rem;
+ background-color: #064787;
+ color: #9dc7f1;
}
.sidebar-top-level-items
> li.active
.sidebar-sub-level-items:not(.is-fly-out-only) {
display: block;
}
+.sidebar-top-level-items > li.active .badge.badge-pill {
+ font-weight: 400;
+ color: #9dc7f1;
+}
+.sidebar-sub-level-items {
+ padding-top: 0;
+ padding-bottom: 0;
+ display: none;
+}
+.sidebar-sub-level-items:not(.fly-out-list) li > a {
+ padding-left: 2.25rem;
+}
.toggle-sidebar-button,
.close-nav-button {
height: 48px;
@@ -1156,21 +1316,17 @@ input {
color: #999;
display: flex;
align-items: center;
-}
-.toggle-sidebar-button,
-.close-nav-button {
+ background-color: #303030;
+ border-top: 1px solid #404040;
position: fixed;
bottom: 0;
- width: 219px;
- border-top: 1px solid #404040;
+ width: 220px;
}
-.toggle-sidebar-button svg,
-.close-nav-button svg {
- margin-right: 8px;
-}
-.toggle-sidebar-button .icon-chevron-double-lg-right,
-.close-nav-button .icon-chevron-double-lg-right {
- display: none;
+.toggle-sidebar-button .collapse-text,
+.toggle-sidebar-button .icon-chevron-double-lg-left,
+.close-nav-button .collapse-text,
+.close-nav-button .icon-chevron-double-lg-left {
+ color: inherit;
}
.collapse-text {
white-space: nowrap;
@@ -1194,6 +1350,12 @@ input {
white-space: nowrap;
width: 1px;
}
+.sidebar-collapsed-desktop .context-header {
+ height: auto;
+}
+.sidebar-collapsed-desktop .context-header a {
+ padding: 0.25rem;
+}
.sidebar-collapsed-desktop
.sidebar-top-level-items
> li
@@ -1204,28 +1366,15 @@ input {
margin-right: 0;
}
.sidebar-collapsed-desktop .toggle-sidebar-button {
- padding: 16px;
- width: 47px;
+ width: 48px;
}
-.sidebar-collapsed-desktop .toggle-sidebar-button .collapse-text,
-.sidebar-collapsed-desktop .toggle-sidebar-button .icon-chevron-double-lg-left {
+.sidebar-collapsed-desktop .toggle-sidebar-button .collapse-text {
display: none;
}
-.sidebar-collapsed-desktop
- .toggle-sidebar-button
- .icon-chevron-double-lg-right {
- display: block;
+.sidebar-collapsed-desktop .toggle-sidebar-button .icon-chevron-double-lg-left {
+ transform: rotate(180deg);
margin: 0;
}
-.fly-out-top-item > a {
- display: flex;
-}
-.fly-out-top-item .fly-out-badge {
- margin-left: 8px;
-}
-.fly-out-top-item-name {
- flex: 1;
-}
.close-nav-button {
display: none;
}
@@ -1237,729 +1386,6 @@ input {
display: none;
}
}
-body.sidebar-refactoring.gl-dark .nav-sidebar li.active {
- box-shadow: none;
-}
-body.sidebar-refactoring.gl-dark .nav-sidebar .sidebar-sub-level-items {
- box-shadow: none;
- border: 1px solid #404040;
-}
-body.sidebar-refactoring.gl-dark
- .sidebar-top-level-items
- .context-header
- a
- .avatar-container.rect-avatar
- .avatar.s32 {
- color: #333;
-}
-body.sidebar-refactoring.gl-dark .nav-sidebar li a,
-body.sidebar-refactoring.gl-dark .toggle-sidebar-button .collapse-text,
-body.sidebar-refactoring.gl-dark
- .toggle-sidebar-button
- .icon-chevron-double-lg-left,
-body.sidebar-refactoring.gl-dark
- .toggle-sidebar-button
- .icon-chevron-double-lg-right,
-body.sidebar-refactoring.gl-dark
- .sidebar-top-level-items
- .context-header
- a
- .sidebar-context-title,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .sidebar-context-title,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item
- a,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container {
- color: #c4c4c4;
-}
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item
- a,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container {
- margin-top: 0;
-}
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- a,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container {
- background: #333;
- color: #c4c4c4;
-}
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- a::before,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a::before,
-body.sidebar-refactoring.gl-dark
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container::before {
- border-right-color: #333;
-}
-body.sidebar-refactoring.gl-dark .nav-sidebar .sidebar-sub-level-items {
- background-color: #333;
-}
-body.sidebar-refactoring.ui-indigo
- .nav-sidebar
- li.active:not(.fly-out-top-item)
- > a {
- color: #2f2a6b;
-}
-body.sidebar-refactoring.ui-indigo
- .nav-sidebar
- li.active
- .nav-icon-container
- svg {
- fill: #2f2a6b;
-}
-body.sidebar-refactoring .nav-sidebar {
- box-shadow: none;
-}
-body.sidebar-refactoring .nav-sidebar li.active {
- background-color: transparent;
- box-shadow: none !important;
-}
-@media (min-width: 768px) {
- body.sidebar-refactoring .page-with-contextual-sidebar {
- padding-left: 48px;
- }
-}
-@media (min-width: 1200px) {
- body.sidebar-refactoring .page-with-contextual-sidebar {
- padding-left: 220px;
- }
-}
-@media (min-width: 768px) {
- body.sidebar-refactoring .page-with-icon-sidebar {
- padding-left: 48px;
- }
-}
-body.sidebar-refactoring .nav-sidebar {
- position: fixed;
- bottom: 0;
- left: 0;
- z-index: 600;
- width: 220px;
- top: 40px;
- background-color: #303030;
- transform: translate3d(0, 0, 0);
-}
-body.sidebar-refactoring .nav-sidebar.sidebar-collapsed-desktop {
- width: 48px;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .nav-sidebar-inner-scroll {
- overflow-x: hidden;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .badge.badge-pill:not(.fly-out-badge),
-body.sidebar-refactoring .nav-sidebar.sidebar-collapsed-desktop .nav-item-name,
-body.sidebar-refactoring .nav-sidebar.sidebar-collapsed-desktop .collapse-text {
- border: 0;
- clip: rect(0, 0, 0, 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- white-space: nowrap;
- width: 1px;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .sidebar-top-level-items
- > li
- > a {
- min-height: unset;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .fly-out-top-item:not(.divider) {
- display: block !important;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .avatar-container {
- margin: 0 auto;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- li.active:not(.fly-out-top-item)
- > a {
- background-color: rgba(41, 41, 97, 0.08);
-}
-body.sidebar-refactoring .nav-sidebar a {
- text-decoration: none;
- color: #2f2a6b;
-}
-body.sidebar-refactoring .nav-sidebar li {
- white-space: nowrap;
-}
-body.sidebar-refactoring .nav-sidebar li .nav-item-name {
- flex: 1;
-}
-body.sidebar-refactoring .nav-sidebar li > a,
-body.sidebar-refactoring .nav-sidebar li > .fly-out-top-item-container {
- padding-left: 0.75rem;
- padding-right: 0.75rem;
- padding-top: 0.5rem;
- padding-bottom: 0.5rem;
- display: flex;
- align-items: center;
- border-radius: 0.25rem;
- width: auto;
- line-height: 1rem;
- margin: 1px 4px;
-}
-body.sidebar-refactoring .nav-sidebar li.active > a {
- font-weight: 600;
-}
-body.sidebar-refactoring
- .nav-sidebar
- li.active:not(.fly-out-top-item)
- > a:not(.has-sub-items) {
- background-color: rgba(41, 41, 97, 0.08);
-}
-body.sidebar-refactoring .nav-sidebar ul {
- padding-left: 0;
- list-style: none;
-}
-@media (max-width: 767.98px) {
- body.sidebar-refactoring .nav-sidebar {
- left: -220px;
- }
-}
-body.sidebar-refactoring .nav-sidebar .nav-icon-container {
- display: flex;
- margin-right: 8px;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item {
- display: none;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- a,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container {
- margin-left: 0;
- margin-right: 0;
- padding-left: 1rem;
- padding-right: 1rem;
- cursor: default;
- pointer-events: none;
- font-size: 0.75rem;
- background-color: #2f2a6b;
- color: #333;
- margin-top: -0.25rem;
- margin-bottom: -0.25rem;
- margin-top: 0;
- position: relative;
- background-color: #fff;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- a
- strong,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a
- strong,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container
- strong {
- font-weight: 400;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- a::before,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a::before,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container::before {
- position: absolute;
- content: "";
- display: block;
- top: 50%;
- left: -0.25rem;
- margin-top: -0.25rem;
- width: 0;
- height: 0;
- border-top: 0.25rem solid transparent;
- border-bottom: 0.25rem solid transparent;
- border-right: 0.25rem solid #fff;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items {
- margin-top: -0.25rem;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item {
- display: none;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item
- a,
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a,
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container {
- margin-left: 0;
- margin-right: 0;
- padding-left: 1rem;
- padding-right: 1rem;
- cursor: default;
- pointer-events: none;
- font-size: 0.75rem;
- background-color: #2f2a6b;
- color: #333;
- margin-top: 0;
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
-}
-@media (min-width: 768px) and (max-width: 1199px) {
- body.sidebar-refactoring .nav-sidebar:not(.sidebar-expanded-mobile) {
- width: 48px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .nav-sidebar-inner-scroll {
- overflow-x: hidden;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .badge.badge-pill:not(.fly-out-badge),
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .nav-item-name,
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .collapse-text {
- border: 0;
- clip: rect(0, 0, 0, 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- white-space: nowrap;
- width: 1px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .sidebar-top-level-items
- > li
- > a {
- min-height: unset;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .fly-out-top-item:not(.divider) {
- display: block !important;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .avatar-container {
- margin: 0 auto;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- li.active:not(.fly-out-top-item)
- > a {
- background-color: rgba(41, 41, 97, 0.08);
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .context-header {
- height: 60px;
- width: 48px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .context-header
- a {
- padding: 10px 4px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .sidebar-context-title {
- border: 0;
- clip: rect(0, 0, 0, 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- white-space: nowrap;
- width: 1px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .context-header {
- height: auto;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .context-header
- a {
- padding: 0.25rem;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .sidebar-top-level-items
- > li
- .sidebar-sub-level-items:not(.flyout-list) {
- display: none;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .nav-icon-container {
- margin-right: 0;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .toggle-sidebar-button {
- width: 48px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .toggle-sidebar-button
- .collapse-text {
- display: none;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .toggle-sidebar-button
- .icon-chevron-double-lg-left {
- transform: rotate(180deg);
- display: block;
- margin: 0;
- }
-}
-body.sidebar-refactoring .nav-sidebar-inner-scroll {
- height: 100%;
- width: 100%;
- overflow: auto;
-}
-body.sidebar-refactoring .nav-sidebar-inner-scroll > div.context-header {
- margin-top: 0.25rem;
-}
-body.sidebar-refactoring .nav-sidebar-inner-scroll > div.context-header a {
- padding-left: 0.75rem;
- padding-right: 0.75rem;
- padding-top: 0.5rem;
- padding-bottom: 0.5rem;
- display: flex;
- align-items: center;
- border-radius: 0.25rem;
- width: auto;
- line-height: 1rem;
- margin: 1px 4px;
- padding: 0.25rem;
- margin-bottom: 0.25rem;
- margin-top: 0;
-}
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .avatar-container {
- font-weight: 400;
- flex: none;
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
-}
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .avatar-container.rect-avatar {
- border-style: none;
-}
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .avatar-container.rect-avatar
- .avatar.s32 {
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
-}
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .sidebar-context-title {
- color: #2f2a6b;
-}
-body.sidebar-refactoring .sidebar-top-level-items {
- margin-top: 0.25rem;
- margin-bottom: 60px;
-}
-body.sidebar-refactoring .sidebar-top-level-items .context-header a {
- padding: 0.25rem;
- margin-bottom: 0.25rem;
- margin-top: 0;
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- .context-header
- a
- .avatar-container {
- font-weight: 400;
- flex: none;
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- .context-header
- a
- .avatar-container.rect-avatar {
- border-style: none;
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- .context-header
- a
- .avatar-container.rect-avatar
- .avatar.s32 {
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- .context-header
- a
- .sidebar-context-title {
- color: #2f2a6b;
-}
-body.sidebar-refactoring .sidebar-top-level-items > li .badge.badge-pill {
- border-radius: 0.5rem;
- padding-top: 0.125rem;
- padding-bottom: 0.125rem;
- padding-left: 0.5rem;
- padding-right: 0.5rem;
- background-color: #064787;
- color: #9dc7f1;
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- > li.active
- .sidebar-sub-level-items:not(.is-fly-out-only) {
- display: block;
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- > li.active
- .badge.badge-pill {
- font-weight: 400;
- color: #9dc7f1;
-}
-body.sidebar-refactoring .sidebar-sub-level-items {
- padding-top: 0;
- padding-bottom: 0;
- display: none;
-}
-body.sidebar-refactoring .sidebar-sub-level-items:not(.fly-out-list) li > a {
- padding-left: 2.25rem;
-}
-body.sidebar-refactoring .toggle-sidebar-button,
-body.sidebar-refactoring .close-nav-button {
- height: 48px;
- padding: 0 16px;
- background-color: #303030;
- border: 0;
- color: #999;
- display: flex;
- align-items: center;
- background-color: #303030;
- border-top: 1px solid #404040;
- color: #2f2a6b;
- position: fixed;
- bottom: 0;
- width: 220px;
-}
-body.sidebar-refactoring .toggle-sidebar-button .collapse-text,
-body.sidebar-refactoring .toggle-sidebar-button .icon-chevron-double-lg-left,
-body.sidebar-refactoring .toggle-sidebar-button .icon-chevron-double-lg-right,
-body.sidebar-refactoring .close-nav-button .collapse-text,
-body.sidebar-refactoring .close-nav-button .icon-chevron-double-lg-left,
-body.sidebar-refactoring .close-nav-button .icon-chevron-double-lg-right {
- color: inherit;
-}
-body.sidebar-refactoring .collapse-text {
- white-space: nowrap;
- overflow: hidden;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .context-header {
- height: 60px;
- width: 48px;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .context-header a {
- padding: 10px 4px;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .sidebar-context-title {
- border: 0;
- clip: rect(0, 0, 0, 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- white-space: nowrap;
- width: 1px;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .context-header {
- height: auto;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .context-header a {
- padding: 0.25rem;
-}
-body.sidebar-refactoring
- .sidebar-collapsed-desktop
- .sidebar-top-level-items
- > li
- .sidebar-sub-level-items:not(.flyout-list) {
- display: none;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .nav-icon-container {
- margin-right: 0;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .toggle-sidebar-button {
- width: 48px;
-}
-body.sidebar-refactoring
- .sidebar-collapsed-desktop
- .toggle-sidebar-button
- .collapse-text {
- display: none;
-}
-body.sidebar-refactoring
- .sidebar-collapsed-desktop
- .toggle-sidebar-button
- .icon-chevron-double-lg-left {
- transform: rotate(180deg);
- display: block;
- margin: 0;
-}
-body.sidebar-refactoring .close-nav-button {
- display: none;
-}
-@media (max-width: 767.98px) {
- body.sidebar-refactoring .close-nav-button {
- display: flex;
- }
- body.sidebar-refactoring .toggle-sidebar-button {
- display: none;
- }
-}
input::-moz-placeholder {
color: #868686;
opacity: 1;
@@ -2037,7 +1463,6 @@ svg.s16 {
top: 4px;
}
.search .search-input-wrap .search-icon {
- -moz-user-select: none;
user-select: none;
}
.search .search-input-wrap .clear-icon {
@@ -2066,7 +1491,7 @@ svg.s16 {
float: left;
margin-right: 16px;
border-radius: 50%;
- border: 1px solid #333;
+ border: 1px solid rgba(255, 255, 255, 0.08);
}
.avatar.s16,
.avatar-container.s16 {
@@ -2086,12 +1511,6 @@ svg.s16 {
height: 32px;
margin-right: 8px;
}
-.avatar.s40,
-.avatar-container.s40 {
- width: 40px;
- height: 40px;
- margin-right: 8px;
-}
.avatar {
transition-property: none;
width: 40px;
@@ -2108,8 +1527,8 @@ svg.s16 {
.identicon {
text-align: center;
vertical-align: top;
- color: #525252;
- background-color: #eee;
+ color: #fafafa;
+ background-color: #303030;
}
.identicon.s16 {
font-size: 10px;
@@ -2119,30 +1538,26 @@ svg.s16 {
font-size: 14px;
line-height: 32px;
}
-.identicon.s40 {
- font-size: 16px;
- line-height: 38px;
-}
.identicon.bg1 {
- background-color: #ffebee;
+ background-color: #660e00;
}
.identicon.bg2 {
- background-color: #f3e5f5;
+ background-color: #f4f0ff;
}
.identicon.bg3 {
- background-color: #e8eaf6;
+ background-color: #f1f1ff;
}
.identicon.bg4 {
- background-color: #e3f2fd;
+ background-color: #033464;
}
.identicon.bg5 {
- background-color: #e0f2f1;
+ background-color: #0a4020;
}
.identicon.bg6 {
- background-color: #fbe9e7;
+ background-color: #5c2900;
}
.identicon.bg7 {
- background-color: #eee;
+ background-color: #303030;
}
.avatar-container {
overflow: hidden;
@@ -2162,10 +1577,6 @@ svg.s16 {
margin: 0;
align-self: center;
}
-.avatar-container.s40 {
- min-width: 40px;
- min-height: 40px;
-}
.rect-avatar {
border-radius: 2px;
}
@@ -2176,23 +1587,18 @@ svg.s16 {
border-radius: 2px;
}
.rect-avatar.s32,
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
+.nav-sidebar-inner-scroll
> div.context-header
a
.avatar-container.rect-avatar
.avatar.s32,
-body.sidebar-refactoring
- .sidebar-top-level-items
+.sidebar-top-level-items
.context-header
a
.avatar-container.rect-avatar
.avatar.s32 {
border-radius: 4px;
}
-.rect-avatar.s40 {
- border-radius: 4px;
-}
body.gl-dark .navbar-gitlab {
background-color: #fafafa;
}
@@ -2253,9 +1659,6 @@ body.gl-dark
body.gl-dark .search form {
background-color: rgba(250, 250, 250, 0.2);
}
-body.gl-dark .search .search-input::-ms-input-placeholder {
- color: rgba(250, 250, 250, 0.8);
-}
body.gl-dark .search .search-input::placeholder {
color: rgba(250, 250, 250, 0.8);
}
@@ -2263,17 +1666,14 @@ body.gl-dark .search .search-input-wrap .search-icon,
body.gl-dark .search .search-input-wrap .clear-icon {
fill: rgba(250, 250, 250, 0.8);
}
-body.gl-dark .nav-sidebar li.active {
- box-shadow: inset 4px 0 0 #999;
-}
body.gl-dark .nav-sidebar li.active > a {
color: #f0f0f0;
}
-body.gl-dark .nav-sidebar li.active .nav-icon-container svg {
- fill: #f0f0f0;
-}
-body.gl-dark .sidebar-top-level-items > li.active .badge.badge-pill {
- color: #f0f0f0;
+body.gl-dark .nav-sidebar .fly-out-top-item a,
+body.gl-dark .nav-sidebar .fly-out-top-item.active a,
+body.gl-dark .nav-sidebar .fly-out-top-item .fly-out-top-item-container {
+ background-color: #2f2a6b;
+ color: var(--black, #333);
}
body.gl-dark .logo-text svg {
fill: var(--gl-text-color);
@@ -2373,6 +1773,9 @@ body.gl-dark {
--black: #fff;
--svg-status-bg: #333;
}
+.nav-sidebar li.active {
+ box-shadow: none;
+}
.tab-width-8 {
-moz-tab-size: 8;
tab-size: 8;
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 4605b6de56..76d1030030 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -130,10 +130,6 @@ h1 {
color: transparent;
text-shadow: 0 0 0 #303030;
}
-.form-control::-ms-input-placeholder {
- color: #5e5e5e;
- opacity: 1;
-}
.form-control::placeholder {
color: #5e5e5e;
opacity: 1;
@@ -160,7 +156,6 @@ h1 {
color: #303030;
text-align: center;
vertical-align: middle;
- -moz-user-select: none;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
@@ -444,8 +439,7 @@ a {
border-top: 1px solid #dbdbdb;
}
.toggle-sidebar-button .collapse-text,
-.toggle-sidebar-button .icon-chevron-double-lg-left,
-.toggle-sidebar-button .icon-chevron-double-lg-right {
+.toggle-sidebar-button .icon-chevron-double-lg-left {
color: #666;
}
svg {
@@ -531,7 +525,7 @@ body {
max-width: 500px;
margin-top: 4px;
margin-bottom: 24px;
- font-size: 14px;
+ font-size: 0.875rem;
font-weight: 400;
padding: 8px 0;
background-color: #fff;
@@ -611,9 +605,6 @@ input {
border-radius: 4px;
padding: 6px 10px;
}
-.form-control::-ms-input-placeholder {
- color: #868686;
-}
.form-control::placeholder {
color: #868686;
}
@@ -918,6 +909,7 @@ input {
.context-header .sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
+ color: #303030;
}
@media (min-width: 768px) {
.page-with-contextual-sidebar {
@@ -936,20 +928,14 @@ input {
}
.nav-sidebar {
position: fixed;
+ bottom: 0;
+ left: 0;
z-index: 600;
width: 220px;
top: 40px;
- bottom: 0;
- left: 0;
- background-color: #fafafa;
- box-shadow: inset -1px 0 0 #dbdbdb;
+ background-color: #f0f0f0;
transform: translate3d(0, 0, 0);
}
-@media (min-width: 576px) and (max-width: 576px) {
- .nav-sidebar:not(.sidebar-collapsed-desktop) {
- box-shadow: inset -1px 0 0 #dbdbdb, 2px 1px 3px rgba(0, 0, 0, 0.1);
- }
-}
.nav-sidebar.sidebar-collapsed-desktop {
width: 48px;
}
@@ -957,7 +943,8 @@ input {
overflow-x: hidden;
}
.nav-sidebar.sidebar-collapsed-desktop .badge.badge-pill:not(.fly-out-badge),
-.nav-sidebar.sidebar-collapsed-desktop .nav-item-name {
+.nav-sidebar.sidebar-collapsed-desktop .nav-item-name,
+.nav-sidebar.sidebar-collapsed-desktop .collapse-text {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
@@ -969,36 +956,50 @@ input {
width: 1px;
}
.nav-sidebar.sidebar-collapsed-desktop .sidebar-top-level-items > li > a {
- min-height: 45px;
+ min-height: unset;
}
-.nav-sidebar.sidebar-collapsed-desktop .fly-out-top-item {
- display: block;
+.nav-sidebar.sidebar-collapsed-desktop .fly-out-top-item:not(.divider) {
+ display: block !important;
}
.nav-sidebar.sidebar-collapsed-desktop .avatar-container {
margin: 0 auto;
}
+.nav-sidebar.sidebar-collapsed-desktop li.active:not(.fly-out-top-item) > a {
+ background-color: rgba(41, 41, 97, 0.08);
+}
.nav-sidebar a {
text-decoration: none;
-}
-.nav-sidebar ul {
- padding-left: 0;
- list-style: none;
+ color: #303030;
}
.nav-sidebar li {
white-space: nowrap;
}
-.nav-sidebar li a {
- display: flex;
- align-items: center;
- padding: 12px 16px;
- color: #666;
-}
.nav-sidebar li .nav-item-name {
flex: 1;
}
+.nav-sidebar li > a,
+.nav-sidebar li > .fly-out-top-item-container {
+ padding-left: 0.75rem;
+ padding-right: 0.75rem;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ display: flex;
+ align-items: center;
+ border-radius: 0.25rem;
+ width: auto;
+ line-height: 1rem;
+ margin: 1px 4px;
+}
.nav-sidebar li.active > a {
font-weight: 600;
}
+.nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) {
+ background-color: rgba(41, 41, 97, 0.08);
+}
+.nav-sidebar ul {
+ padding-left: 0;
+ list-style: none;
+}
@media (max-width: 767.98px) {
.nav-sidebar {
left: -220px;
@@ -1008,12 +1009,113 @@ input {
display: flex;
margin-right: 8px;
}
-.nav-sidebar .fly-out-top-item {
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item {
display: none;
}
-.nav-sidebar svg {
- height: 16px;
- width: 16px;
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ a,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item.active
+ a,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ .fly-out-top-item-container {
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ cursor: default;
+ pointer-events: none;
+ font-size: 0.75rem;
+ margin-top: -0.25rem;
+ margin-bottom: -0.25rem;
+ margin-top: 0;
+ position: relative;
+ color: #fff;
+ background: var(--black, #000);
+}
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ a
+ strong,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item.active
+ a
+ strong,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ .fly-out-top-item-container
+ strong {
+ font-weight: 400;
+}
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ a::before,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item.active
+ a::before,
+.nav-sidebar
+ a:not(.has-sub-items)
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ .fly-out-top-item-container::before {
+ position: absolute;
+ content: "";
+ display: block;
+ top: 50%;
+ left: -0.25rem;
+ margin-top: -0.25rem;
+ width: 0;
+ height: 0;
+ border-top: 0.25rem solid transparent;
+ border-bottom: 0.25rem solid transparent;
+ border-right: 0.25rem solid #000;
+ border-right-color: var(--black, #000);
+}
+.nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item {
+ display: none;
+}
+.nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a,
+.nav-sidebar
+ a.has-sub-items
+ + .sidebar-sub-level-items
+ .fly-out-top-item.active
+ a,
+.nav-sidebar
+ a.has-sub-items
+ + .sidebar-sub-level-items
+ .fly-out-top-item
+ .fly-out-top-item-container {
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ cursor: default;
+ pointer-events: none;
+ font-size: 0.75rem;
+ margin-top: 0;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
}
@media (min-width: 768px) and (max-width: 1199px) {
.nav-sidebar:not(.sidebar-expanded-mobile) {
@@ -1024,7 +1126,8 @@ input {
}
.nav-sidebar:not(.sidebar-expanded-mobile)
.badge.badge-pill:not(.fly-out-badge),
- .nav-sidebar:not(.sidebar-expanded-mobile) .nav-item-name {
+ .nav-sidebar:not(.sidebar-expanded-mobile) .nav-item-name,
+ .nav-sidebar:not(.sidebar-expanded-mobile) .collapse-text {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
@@ -1036,14 +1139,19 @@ input {
width: 1px;
}
.nav-sidebar:not(.sidebar-expanded-mobile) .sidebar-top-level-items > li > a {
- min-height: 45px;
+ min-height: unset;
}
- .nav-sidebar:not(.sidebar-expanded-mobile) .fly-out-top-item {
- display: block;
+ .nav-sidebar:not(.sidebar-expanded-mobile) .fly-out-top-item:not(.divider) {
+ display: block !important;
}
.nav-sidebar:not(.sidebar-expanded-mobile) .avatar-container {
margin: 0 auto;
}
+ .nav-sidebar:not(.sidebar-expanded-mobile)
+ li.active:not(.fly-out-top-item)
+ > a {
+ background-color: rgba(41, 41, 97, 0.08);
+ }
.nav-sidebar:not(.sidebar-expanded-mobile) .context-header {
height: 60px;
width: 48px;
@@ -1062,6 +1170,12 @@ input {
white-space: nowrap;
width: 1px;
}
+ .nav-sidebar:not(.sidebar-expanded-mobile) .context-header {
+ height: auto;
+ }
+ .nav-sidebar:not(.sidebar-expanded-mobile) .context-header a {
+ padding: 0.25rem;
+ }
.nav-sidebar:not(.sidebar-expanded-mobile)
.sidebar-top-level-items
> li
@@ -1072,21 +1186,17 @@ input {
margin-right: 0;
}
.nav-sidebar:not(.sidebar-expanded-mobile) .toggle-sidebar-button {
- padding: 16px;
- width: 47px;
+ width: 48px;
}
.nav-sidebar:not(.sidebar-expanded-mobile)
.toggle-sidebar-button
- .collapse-text,
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .toggle-sidebar-button
- .icon-chevron-double-lg-left {
+ .collapse-text {
display: none;
}
.nav-sidebar:not(.sidebar-expanded-mobile)
.toggle-sidebar-button
- .icon-chevron-double-lg-right {
- display: block;
+ .icon-chevron-double-lg-left {
+ transform: rotate(180deg);
margin: 0;
}
}
@@ -1095,43 +1205,89 @@ input {
width: 100%;
overflow: auto;
}
-.sidebar-sub-level-items {
- display: none;
- padding-bottom: 8px;
+.nav-sidebar-inner-scroll > div.context-header {
+ margin-top: 0.25rem;
}
-.sidebar-sub-level-items > li a {
- padding: 8px 16px 8px 40px;
+.nav-sidebar-inner-scroll > div.context-header a {
+ padding-left: 0.75rem;
+ padding-right: 0.75rem;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ display: flex;
+ align-items: center;
+ border-radius: 0.25rem;
+ width: auto;
+ line-height: 1rem;
+ margin: 1px 4px;
+ padding: 0.25rem;
+ margin-bottom: 0.25rem;
+ margin-top: 0;
}
-.sidebar-sub-level-items > li.active a {
- background: rgba(0, 0, 0, 0.04);
+.nav-sidebar-inner-scroll > div.context-header a .avatar-container {
+ font-weight: 400;
+ flex: none;
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
+}
+.nav-sidebar-inner-scroll > div.context-header a .avatar-container.rect-avatar {
+ border-style: none;
+}
+.nav-sidebar-inner-scroll
+ > div.context-header
+ a
+ .avatar-container.rect-avatar
+ .avatar.s32 {
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}
.sidebar-top-level-items {
+ margin-top: 0.25rem;
margin-bottom: 60px;
}
-@media (min-width: 576px) {
- .sidebar-top-level-items > li > a {
- margin-right: 1px;
- }
+.sidebar-top-level-items .context-header a {
+ padding: 0.25rem;
+ margin-bottom: 0.25rem;
+ margin-top: 0;
+}
+.sidebar-top-level-items .context-header a .avatar-container {
+ font-weight: 400;
+ flex: none;
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
+}
+.sidebar-top-level-items .context-header a .avatar-container.rect-avatar {
+ border-style: none;
+}
+.sidebar-top-level-items
+ .context-header
+ a
+ .avatar-container.rect-avatar
+ .avatar.s32 {
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}
.sidebar-top-level-items > li .badge.badge-pill {
- background-color: rgba(0, 0, 0, 0.08);
- color: #666;
-}
-.sidebar-top-level-items > li.active {
- background: rgba(0, 0, 0, 0.04);
-}
-.sidebar-top-level-items > li.active > a {
- margin-left: 4px;
- padding-left: 12px;
-}
-.sidebar-top-level-items > li.active .badge.badge-pill {
- font-weight: 600;
+ border-radius: 0.5rem;
+ padding-top: 0.125rem;
+ padding-bottom: 0.125rem;
+ padding-left: 0.5rem;
+ padding-right: 0.5rem;
+ background-color: #cbe2f9;
+ color: #0b5cad;
}
.sidebar-top-level-items
> li.active
.sidebar-sub-level-items:not(.is-fly-out-only) {
display: block;
}
+.sidebar-top-level-items > li.active .badge.badge-pill {
+ font-weight: 400;
+ color: #0b5cad;
+}
+.sidebar-sub-level-items {
+ padding-top: 0;
+ padding-bottom: 0;
+ display: none;
+}
+.sidebar-sub-level-items:not(.fly-out-list) li > a {
+ padding-left: 2.25rem;
+}
.toggle-sidebar-button,
.close-nav-button {
height: 48px;
@@ -1141,21 +1297,17 @@ input {
color: #666;
display: flex;
align-items: center;
-}
-.toggle-sidebar-button,
-.close-nav-button {
+ background-color: #f0f0f0;
+ border-top: 1px solid #dbdbdb;
position: fixed;
bottom: 0;
- width: 219px;
- border-top: 1px solid #dbdbdb;
+ width: 220px;
}
-.toggle-sidebar-button svg,
-.close-nav-button svg {
- margin-right: 8px;
-}
-.toggle-sidebar-button .icon-chevron-double-lg-right,
-.close-nav-button .icon-chevron-double-lg-right {
- display: none;
+.toggle-sidebar-button .collapse-text,
+.toggle-sidebar-button .icon-chevron-double-lg-left,
+.close-nav-button .collapse-text,
+.close-nav-button .icon-chevron-double-lg-left {
+ color: inherit;
}
.collapse-text {
white-space: nowrap;
@@ -1179,6 +1331,12 @@ input {
white-space: nowrap;
width: 1px;
}
+.sidebar-collapsed-desktop .context-header {
+ height: auto;
+}
+.sidebar-collapsed-desktop .context-header a {
+ padding: 0.25rem;
+}
.sidebar-collapsed-desktop
.sidebar-top-level-items
> li
@@ -1189,28 +1347,15 @@ input {
margin-right: 0;
}
.sidebar-collapsed-desktop .toggle-sidebar-button {
- padding: 16px;
- width: 47px;
+ width: 48px;
}
-.sidebar-collapsed-desktop .toggle-sidebar-button .collapse-text,
-.sidebar-collapsed-desktop .toggle-sidebar-button .icon-chevron-double-lg-left {
+.sidebar-collapsed-desktop .toggle-sidebar-button .collapse-text {
display: none;
}
-.sidebar-collapsed-desktop
- .toggle-sidebar-button
- .icon-chevron-double-lg-right {
- display: block;
+.sidebar-collapsed-desktop .toggle-sidebar-button .icon-chevron-double-lg-left {
+ transform: rotate(180deg);
margin: 0;
}
-.fly-out-top-item > a {
- display: flex;
-}
-.fly-out-top-item .fly-out-badge {
- margin-left: 8px;
-}
-.fly-out-top-item-name {
- flex: 1;
-}
.close-nav-button {
display: none;
}
@@ -1222,612 +1367,6 @@ input {
display: none;
}
}
-body.sidebar-refactoring.ui-indigo
- .nav-sidebar
- li.active:not(.fly-out-top-item)
- > a {
- color: #2f2a6b;
-}
-body.sidebar-refactoring.ui-indigo
- .nav-sidebar
- li.active
- .nav-icon-container
- svg {
- fill: #2f2a6b;
-}
-body.sidebar-refactoring .nav-sidebar {
- box-shadow: none;
-}
-body.sidebar-refactoring .nav-sidebar li.active {
- background-color: transparent;
- box-shadow: none !important;
-}
-@media (min-width: 768px) {
- body.sidebar-refactoring .page-with-contextual-sidebar {
- padding-left: 48px;
- }
-}
-@media (min-width: 1200px) {
- body.sidebar-refactoring .page-with-contextual-sidebar {
- padding-left: 220px;
- }
-}
-@media (min-width: 768px) {
- body.sidebar-refactoring .page-with-icon-sidebar {
- padding-left: 48px;
- }
-}
-body.sidebar-refactoring .nav-sidebar {
- position: fixed;
- bottom: 0;
- left: 0;
- z-index: 600;
- width: 220px;
- top: 40px;
- background-color: #f0f0f0;
- transform: translate3d(0, 0, 0);
-}
-body.sidebar-refactoring .nav-sidebar.sidebar-collapsed-desktop {
- width: 48px;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .nav-sidebar-inner-scroll {
- overflow-x: hidden;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .badge.badge-pill:not(.fly-out-badge),
-body.sidebar-refactoring .nav-sidebar.sidebar-collapsed-desktop .nav-item-name,
-body.sidebar-refactoring .nav-sidebar.sidebar-collapsed-desktop .collapse-text {
- border: 0;
- clip: rect(0, 0, 0, 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- white-space: nowrap;
- width: 1px;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .sidebar-top-level-items
- > li
- > a {
- min-height: unset;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .fly-out-top-item:not(.divider) {
- display: block !important;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- .avatar-container {
- margin: 0 auto;
-}
-body.sidebar-refactoring
- .nav-sidebar.sidebar-collapsed-desktop
- li.active:not(.fly-out-top-item)
- > a {
- background-color: rgba(41, 41, 97, 0.08);
-}
-body.sidebar-refactoring .nav-sidebar a {
- text-decoration: none;
- color: #2f2a6b;
-}
-body.sidebar-refactoring .nav-sidebar li {
- white-space: nowrap;
-}
-body.sidebar-refactoring .nav-sidebar li .nav-item-name {
- flex: 1;
-}
-body.sidebar-refactoring .nav-sidebar li > a,
-body.sidebar-refactoring .nav-sidebar li > .fly-out-top-item-container {
- padding-left: 0.75rem;
- padding-right: 0.75rem;
- padding-top: 0.5rem;
- padding-bottom: 0.5rem;
- display: flex;
- align-items: center;
- border-radius: 0.25rem;
- width: auto;
- line-height: 1rem;
- margin: 1px 4px;
-}
-body.sidebar-refactoring .nav-sidebar li.active > a {
- font-weight: 600;
-}
-body.sidebar-refactoring
- .nav-sidebar
- li.active:not(.fly-out-top-item)
- > a:not(.has-sub-items) {
- background-color: rgba(41, 41, 97, 0.08);
-}
-body.sidebar-refactoring .nav-sidebar ul {
- padding-left: 0;
- list-style: none;
-}
-@media (max-width: 767.98px) {
- body.sidebar-refactoring .nav-sidebar {
- left: -220px;
- }
-}
-body.sidebar-refactoring .nav-sidebar .nav-icon-container {
- display: flex;
- margin-right: 8px;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item {
- display: none;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- a,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container {
- margin-left: 0;
- margin-right: 0;
- padding-left: 1rem;
- padding-right: 1rem;
- cursor: default;
- pointer-events: none;
- font-size: 0.75rem;
- background-color: #2f2a6b;
- color: #fff;
- margin-top: -0.25rem;
- margin-bottom: -0.25rem;
- margin-top: 0;
- position: relative;
- background-color: #000;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- a
- strong,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a
- strong,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container
- strong {
- font-weight: 400;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- a::before,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a::before,
-body.sidebar-refactoring
- .nav-sidebar
- a:not(.has-sub-items)
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container::before {
- position: absolute;
- content: "";
- display: block;
- top: 50%;
- left: -0.25rem;
- margin-top: -0.25rem;
- width: 0;
- height: 0;
- border-top: 0.25rem solid transparent;
- border-bottom: 0.25rem solid transparent;
- border-right: 0.25rem solid #000;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items {
- margin-top: -0.25rem;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item {
- display: none;
-}
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item
- a,
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item.active
- a,
-body.sidebar-refactoring
- .nav-sidebar
- a.has-sub-items
- + .sidebar-sub-level-items
- .fly-out-top-item
- .fly-out-top-item-container {
- margin-left: 0;
- margin-right: 0;
- padding-left: 1rem;
- padding-right: 1rem;
- cursor: default;
- pointer-events: none;
- font-size: 0.75rem;
- background-color: #2f2a6b;
- color: #fff;
- margin-top: 0;
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
-}
-@media (min-width: 768px) and (max-width: 1199px) {
- body.sidebar-refactoring .nav-sidebar:not(.sidebar-expanded-mobile) {
- width: 48px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .nav-sidebar-inner-scroll {
- overflow-x: hidden;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .badge.badge-pill:not(.fly-out-badge),
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .nav-item-name,
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .collapse-text {
- border: 0;
- clip: rect(0, 0, 0, 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- white-space: nowrap;
- width: 1px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .sidebar-top-level-items
- > li
- > a {
- min-height: unset;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .fly-out-top-item:not(.divider) {
- display: block !important;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .avatar-container {
- margin: 0 auto;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- li.active:not(.fly-out-top-item)
- > a {
- background-color: rgba(41, 41, 97, 0.08);
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .context-header {
- height: 60px;
- width: 48px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .context-header
- a {
- padding: 10px 4px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .sidebar-context-title {
- border: 0;
- clip: rect(0, 0, 0, 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- white-space: nowrap;
- width: 1px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .context-header {
- height: auto;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .context-header
- a {
- padding: 0.25rem;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .sidebar-top-level-items
- > li
- .sidebar-sub-level-items:not(.flyout-list) {
- display: none;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .nav-icon-container {
- margin-right: 0;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .toggle-sidebar-button {
- width: 48px;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .toggle-sidebar-button
- .collapse-text {
- display: none;
- }
- body.sidebar-refactoring
- .nav-sidebar:not(.sidebar-expanded-mobile)
- .toggle-sidebar-button
- .icon-chevron-double-lg-left {
- transform: rotate(180deg);
- display: block;
- margin: 0;
- }
-}
-body.sidebar-refactoring .nav-sidebar-inner-scroll {
- height: 100%;
- width: 100%;
- overflow: auto;
-}
-body.sidebar-refactoring .nav-sidebar-inner-scroll > div.context-header {
- margin-top: 0.25rem;
-}
-body.sidebar-refactoring .nav-sidebar-inner-scroll > div.context-header a {
- padding-left: 0.75rem;
- padding-right: 0.75rem;
- padding-top: 0.5rem;
- padding-bottom: 0.5rem;
- display: flex;
- align-items: center;
- border-radius: 0.25rem;
- width: auto;
- line-height: 1rem;
- margin: 1px 4px;
- padding: 0.25rem;
- margin-bottom: 0.25rem;
- margin-top: 0;
-}
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .avatar-container {
- font-weight: 400;
- flex: none;
- box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
-}
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .avatar-container.rect-avatar {
- border-style: none;
-}
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .avatar-container.rect-avatar
- .avatar.s32 {
- box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
-}
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
- > div.context-header
- a
- .sidebar-context-title {
- color: #2f2a6b;
-}
-body.sidebar-refactoring .sidebar-top-level-items {
- margin-top: 0.25rem;
- margin-bottom: 60px;
-}
-body.sidebar-refactoring .sidebar-top-level-items .context-header a {
- padding: 0.25rem;
- margin-bottom: 0.25rem;
- margin-top: 0;
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- .context-header
- a
- .avatar-container {
- font-weight: 400;
- flex: none;
- box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- .context-header
- a
- .avatar-container.rect-avatar {
- border-style: none;
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- .context-header
- a
- .avatar-container.rect-avatar
- .avatar.s32 {
- box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- .context-header
- a
- .sidebar-context-title {
- color: #2f2a6b;
-}
-body.sidebar-refactoring .sidebar-top-level-items > li .badge.badge-pill {
- border-radius: 0.5rem;
- padding-top: 0.125rem;
- padding-bottom: 0.125rem;
- padding-left: 0.5rem;
- padding-right: 0.5rem;
- background-color: #cbe2f9;
- color: #0b5cad;
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- > li.active
- .sidebar-sub-level-items:not(.is-fly-out-only) {
- display: block;
-}
-body.sidebar-refactoring
- .sidebar-top-level-items
- > li.active
- .badge.badge-pill {
- font-weight: 400;
- color: #0b5cad;
-}
-body.sidebar-refactoring .sidebar-sub-level-items {
- padding-top: 0;
- padding-bottom: 0;
- display: none;
-}
-body.sidebar-refactoring .sidebar-sub-level-items:not(.fly-out-list) li > a {
- padding-left: 2.25rem;
-}
-body.sidebar-refactoring .toggle-sidebar-button,
-body.sidebar-refactoring .close-nav-button {
- height: 48px;
- padding: 0 16px;
- background-color: #fafafa;
- border: 0;
- color: #666;
- display: flex;
- align-items: center;
- background-color: #f0f0f0;
- border-top: 1px solid #dbdbdb;
- color: #2f2a6b;
- position: fixed;
- bottom: 0;
- width: 220px;
-}
-body.sidebar-refactoring .toggle-sidebar-button .collapse-text,
-body.sidebar-refactoring .toggle-sidebar-button .icon-chevron-double-lg-left,
-body.sidebar-refactoring .toggle-sidebar-button .icon-chevron-double-lg-right,
-body.sidebar-refactoring .close-nav-button .collapse-text,
-body.sidebar-refactoring .close-nav-button .icon-chevron-double-lg-left,
-body.sidebar-refactoring .close-nav-button .icon-chevron-double-lg-right {
- color: inherit;
-}
-body.sidebar-refactoring .collapse-text {
- white-space: nowrap;
- overflow: hidden;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .context-header {
- height: 60px;
- width: 48px;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .context-header a {
- padding: 10px 4px;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .sidebar-context-title {
- border: 0;
- clip: rect(0, 0, 0, 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- white-space: nowrap;
- width: 1px;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .context-header {
- height: auto;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .context-header a {
- padding: 0.25rem;
-}
-body.sidebar-refactoring
- .sidebar-collapsed-desktop
- .sidebar-top-level-items
- > li
- .sidebar-sub-level-items:not(.flyout-list) {
- display: none;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .nav-icon-container {
- margin-right: 0;
-}
-body.sidebar-refactoring .sidebar-collapsed-desktop .toggle-sidebar-button {
- width: 48px;
-}
-body.sidebar-refactoring
- .sidebar-collapsed-desktop
- .toggle-sidebar-button
- .collapse-text {
- display: none;
-}
-body.sidebar-refactoring
- .sidebar-collapsed-desktop
- .toggle-sidebar-button
- .icon-chevron-double-lg-left {
- transform: rotate(180deg);
- display: block;
- margin: 0;
-}
-body.sidebar-refactoring .close-nav-button {
- display: none;
-}
-@media (max-width: 767.98px) {
- body.sidebar-refactoring .close-nav-button {
- display: flex;
- }
- body.sidebar-refactoring .toggle-sidebar-button {
- display: none;
- }
-}
input::-moz-placeholder {
color: #868686;
opacity: 1;
@@ -1905,7 +1444,6 @@ svg.s16 {
top: 4px;
}
.search .search-input-wrap .search-icon {
- -moz-user-select: none;
user-select: none;
}
.search .search-input-wrap .clear-icon {
@@ -1934,7 +1472,7 @@ svg.s16 {
float: left;
margin-right: 16px;
border-radius: 50%;
- border: 1px solid #f5f5f5;
+ border: 1px solid rgba(0, 0, 0, 0.08);
}
.avatar.s16,
.avatar-container.s16 {
@@ -1954,12 +1492,6 @@ svg.s16 {
height: 32px;
margin-right: 8px;
}
-.avatar.s40,
-.avatar-container.s40 {
- width: 40px;
- height: 40px;
- margin-right: 8px;
-}
.avatar {
transition-property: none;
width: 40px;
@@ -1976,8 +1508,8 @@ svg.s16 {
.identicon {
text-align: center;
vertical-align: top;
- color: #525252;
- background-color: #eee;
+ color: #303030;
+ background-color: #f0f0f0;
}
.identicon.s16 {
font-size: 10px;
@@ -1987,30 +1519,26 @@ svg.s16 {
font-size: 14px;
line-height: 32px;
}
-.identicon.s40 {
- font-size: 16px;
- line-height: 38px;
-}
.identicon.bg1 {
- background-color: #ffebee;
+ background-color: #fcf1ef;
}
.identicon.bg2 {
- background-color: #f3e5f5;
+ background-color: #f4f0ff;
}
.identicon.bg3 {
- background-color: #e8eaf6;
+ background-color: #f1f1ff;
}
.identicon.bg4 {
- background-color: #e3f2fd;
+ background-color: #e9f3fc;
}
.identicon.bg5 {
- background-color: #e0f2f1;
+ background-color: #ecf4ee;
}
.identicon.bg6 {
- background-color: #fbe9e7;
+ background-color: #fdf1dd;
}
.identicon.bg7 {
- background-color: #eee;
+ background-color: #f0f0f0;
}
.avatar-container {
overflow: hidden;
@@ -2030,10 +1558,6 @@ svg.s16 {
margin: 0;
align-self: center;
}
-.avatar-container.s40 {
- min-width: 40px;
- min-height: 40px;
-}
.rect-avatar {
border-radius: 2px;
}
@@ -2044,23 +1568,18 @@ svg.s16 {
border-radius: 2px;
}
.rect-avatar.s32,
-body.sidebar-refactoring
- .nav-sidebar-inner-scroll
+.nav-sidebar-inner-scroll
> div.context-header
a
.avatar-container.rect-avatar
.avatar.s32,
-body.sidebar-refactoring
- .sidebar-top-level-items
+.sidebar-top-level-items
.context-header
a
.avatar-container.rect-avatar
.avatar.s32 {
border-radius: 4px;
}
-.rect-avatar.s40 {
- border-radius: 4px;
-}
.tab-width-8 {
-moz-tab-size: 8;
diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss
index 81a8774285..070ab36e0b 100644
--- a/app/assets/stylesheets/startup/startup-signin.scss
+++ b/app/assets/stylesheets/startup/startup-signin.scss
@@ -198,10 +198,6 @@ hr {
color: transparent;
text-shadow: 0 0 0 #303030;
}
-.form-control::-ms-input-placeholder {
- color: #5e5e5e;
- opacity: 1;
-}
.form-control::placeholder {
color: #5e5e5e;
opacity: 1;
@@ -229,7 +225,6 @@ hr {
color: #303030;
text-align: center;
vertical-align: middle;
- -moz-user-select: none;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
@@ -294,11 +289,6 @@ fieldset:disabled a.btn {
.mb-3 {
margin-bottom: 1rem !important;
}
-@media (min-width: 576px) {
- .mt-sm-0 {
- margin-top: 0 !important;
- }
-}
.text-center {
text-align: center !important;
}
@@ -324,13 +314,6 @@ fieldset:disabled a.btn {
appearance: none;
-moz-appearance: none;
}
-.gl-form-input:not(.form-control-plaintext):-moz-read-only,
-.gl-form-input.form-control:not(.form-control-plaintext):-moz-read-only {
- background-color: #fafafa;
- color: #868686;
- box-shadow: inset 0 0 0 1px #dbdbdb;
- cursor: not-allowed;
-}
.gl-form-input:disabled,
.gl-form-input:not(.form-control-plaintext):read-only,
.gl-form-input.form-control:disabled,
@@ -340,10 +323,6 @@ fieldset:disabled a.btn {
box-shadow: inset 0 0 0 1px #dbdbdb;
cursor: not-allowed;
}
-.gl-form-input::-ms-input-placeholder,
-.gl-form-input.form-control::-ms-input-placeholder {
- color: #868686;
-}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
color: #868686;
@@ -500,7 +479,6 @@ hr {
z-index: 1;
}
.flash-container.sticky {
- position: -webkit-sticky;
position: sticky;
top: 48px;
z-index: 251;
@@ -526,9 +504,6 @@ label.label-bold {
border-radius: 4px;
padding: 6px 10px;
}
-.form-control::-ms-input-placeholder {
- color: #868686;
-}
.form-control::placeholder {
color: #868686;
}
@@ -542,7 +517,7 @@ label.label-bold {
justify-content: center;
height: 40px;
background: #fff;
- border-bottom: 1px solid #f0f0f0;
+ border-bottom: 1px solid #dbdbdb;
}
.navbar-empty .tanuki-logo,
.navbar-empty .brand-header-logo {
@@ -796,9 +771,15 @@ svg {
.gl-display-flex {
display: flex;
}
+.gl-display-block {
+ display: block;
+}
.gl-align-items-center {
align-items: center;
}
+.gl-w-full {
+ width: 100%;
+}
.gl-p-2 {
padding: 0.25rem;
}
@@ -817,6 +798,11 @@ svg {
.gl-mb-5 {
margin-bottom: 1rem;
}
+@media (min-width: 36rem) {
+ .gl-sm-mt-0 {
+ margin-top: 0;
+ }
+}
.gl-text-left {
text-align: left;
}
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index 9d98fe5c73..ea7aaaa8ec 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -253,3 +253,14 @@ $well-inner-border: $gray-200;
color: $gray-900;
border-color: $gray-800;
}
+
+.nav-sidebar {
+ li.active {
+ box-shadow: none;
+ }
+
+ .sidebar-sub-level-items.fly-out-list {
+ box-shadow: none;
+ border: 1px solid $border-color;
+ }
+}
diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss
index 6a60978b95..a94169ab49 100644
--- a/app/assets/stylesheets/themes/theme_helper.scss
+++ b/app/assets/stylesheets/themes/theme_helper.scss
@@ -174,20 +174,20 @@
}
// Sidebar
- .nav-sidebar li.active {
- box-shadow: inset 4px 0 0 $border-and-box-shadow;
-
- > a {
- color: $sidebar-text;
- }
-
- .nav-icon-container svg {
- fill: $sidebar-text;
- }
+ .nav-sidebar li.active > a {
+ color: $sidebar-text;
}
- .sidebar-top-level-items > li.active .badge.badge-pill {
- color: $sidebar-text;
+ .nav-sidebar {
+ .fly-out-top-item {
+ a,
+ a:hover,
+ &.active a,
+ .fly-out-top-item-container {
+ background-color: $purple-900;
+ color: var(--black, $white);
+ }
+ }
}
.nav-links li {
@@ -213,7 +213,6 @@
.ide-sidebar-link {
&.active {
color: $border-and-box-shadow;
- box-shadow: inset 3px 0 $border-and-box-shadow;
&.is-right {
box-shadow: inset -3px 0 $border-and-box-shadow;
diff --git a/app/assets/stylesheets/themes/theme_indigo.scss b/app/assets/stylesheets/themes/theme_indigo.scss
index bbf14afcca..9566c9c600 100644
--- a/app/assets/stylesheets/themes/theme_indigo.scss
+++ b/app/assets/stylesheets/themes/theme_indigo.scss
@@ -6,7 +6,7 @@ body {
$indigo-200,
$indigo-500,
$indigo-700,
- $indigo-800,
+ $purple-900,
$indigo-900,
$white
);
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index cabbe5834c..10334d771b 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -65,6 +65,8 @@
min-width: 0;
}
+// .gl-font-size-inherit will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1466
+.gl-font-size-inherit,
.font-size-inherit { font-size: inherit; }
.gl-w-8 { width: px-to-rem($grid-size); }
.gl-w-16 { width: px-to-rem($grid-size * 2); }
@@ -85,6 +87,12 @@
padding-bottom: $gl-spacing-scale-8;
}
+// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1495
+.gl-py-13 {
+ padding-top: $gl-spacing-scale-13;
+ padding-bottom: $gl-spacing-scale-13;
+}
+
.gl-transition-property-stroke-opacity {
transition-property: stroke-opacity;
}
@@ -117,6 +125,25 @@
}
}
+// Will be moved to @gitlab/ui (without the !important) in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1462
+// We only need the bang (!) version until the non-bang version is added to
+// @gitlab/ui utitlities.scss. Once there, it will get loaded in the correct
+// order to properly override `.gl-mt-6` which is used for narrower screen
+// widths (currently that style gets added to the application.css stylesheet
+// after this one, so it takes precedence).
+.gl-md-mt-11\! {
+ @media (min-width: $breakpoint-md) {
+ margin-top: $gl-spacing-scale-11 !important;
+ }
+}
+
+// Same as above (also without the !important) but for overriding `.gl-pt-6`
+.gl-md-pt-11\! {
+ @media (min-width: $breakpoint-md) {
+ padding-top: $gl-spacing-scale-11 !important;
+ }
+}
+
// This is used to help prevent issues with margin collapsing.
// See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing.
.gl-force-block-formatting-context::after {
@@ -200,3 +227,23 @@ $gl-line-height-42: px-to-rem(42px);
.gl-max-h-none\! {
max-height: none !important;
}
+
+// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1465
+.gl-popover {
+ .popover-header {
+ .gl-button.close {
+ margin-top: -$gl-spacing-scale-3;
+ margin-right: -$gl-spacing-scale-4;
+ }
+ }
+}
+
+// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1490
+.gl-w-grid-size-28 {
+ width: $grid-size * 28;
+}
+
+// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1491
+.gl-min-w-8 {
+ min-width: $gl-spacing-scale-8;
+}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 7960e5d64d..8039fac02e 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -2,7 +2,7 @@
class Admin::ApplicationSettingsController < Admin::ApplicationController
include InternalRedirect
- include ServicesHelper
+ include IntegrationsHelper
# NOTE: Use @application_setting in this controller when you need to access
# application_settings after it has been modified. This is because the
@@ -27,7 +27,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
feature_category :source_code_management, [:repository, :clear_repository_check_states]
feature_category :continuous_integration, [:ci_cd, :reset_registration_token]
- feature_category :usage_ping, [:usage_data]
+ feature_category :service_ping, [:usage_data]
feature_category :integrations, [:integrations]
feature_category :pages, [:lets_encrypt_terms_of_service]
@@ -207,6 +207,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
params[:application_setting][:import_sources]&.delete("")
+ params[:application_setting][:valid_runner_registrars]&.delete("")
params[:application_setting][:restricted_visibility_levels]&.delete("")
if params[:application_setting].key?(:required_instance_ci_template)
@@ -245,7 +246,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
disabled_oauth_sign_in_sources: [],
import_sources: [],
restricted_visibility_levels: [],
- repository_storages_weighted: {}
+ repository_storages_weighted: {},
+ valid_runner_registrars: []
]
end
diff --git a/app/controllers/admin/background_migrations_controller.rb b/app/controllers/admin/background_migrations_controller.rb
index c1dffbf423..65b47308e4 100644
--- a/app/controllers/admin/background_migrations_controller.rb
+++ b/app/controllers/admin/background_migrations_controller.rb
@@ -15,6 +15,20 @@ class Admin::BackgroundMigrationsController < Admin::ApplicationController
@successful_rows_counts = batched_migration_class.successful_rows_counts(@migrations.map(&:id))
end
+ def pause
+ migration = batched_migration_class.find(params[:id])
+ migration.paused!
+
+ redirect_back fallback_location: { action: 'index' }
+ end
+
+ def resume
+ migration = batched_migration_class.find(params[:id])
+ migration.active!
+
+ redirect_back fallback_location: { action: 'index' }
+ end
+
private
def batched_migration_class
diff --git a/app/controllers/admin/ci/variables_controller.rb b/app/controllers/admin/ci/variables_controller.rb
index f30ee37fa5..d4b7d75075 100644
--- a/app/controllers/admin/ci/variables_controller.rb
+++ b/app/controllers/admin/ci/variables_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Admin::Ci::VariablesController < Admin::ApplicationController
- feature_category :continuous_integration
+ feature_category :pipeline_authoring
def show
respond_to do |format|
diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb
index 8163f062b6..e750b5c5ad 100644
--- a/app/controllers/admin/cohorts_controller.rb
+++ b/app/controllers/admin/cohorts_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Admin::CohortsController < Admin::ApplicationController
- include Analytics::UniqueVisitsHelper
+ include RedisTracking
feature_category :devops_reports
@@ -21,8 +21,6 @@ class Admin::CohortsController < Admin::ApplicationController
end
def track_cohorts_visit
- if request.format.html? && request.headers['DNT'] != '1'
- track_visit('i_analytics_cohorts')
- end
+ track_unique_redis_hll_event('i_analytics_cohorts') if trackable_html_request?
end
end
diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb
index 316e6d9aa7..76c1c46e0e 100644
--- a/app/controllers/admin/integrations_controller.rb
+++ b/app/controllers/admin/integrations_controller.rb
@@ -2,7 +2,7 @@
class Admin::IntegrationsController < Admin::ApplicationController
include IntegrationsActions
- include ServicesHelper
+ include IntegrationsHelper
before_action :not_found, unless: -> { instance_level_integrations? }
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index bf9cfa3acf..d1c91d9617 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -8,7 +8,7 @@ class Admin::RunnersController < Admin::ApplicationController
push_frontend_feature_flag(:runner_list_view_vue_ui, current_user, default_enabled: :yaml)
end
- feature_category :continuous_integration
+ feature_category :runner
NUMBER_OF_RUNNERS_PER_PAGE = 30
diff --git a/app/controllers/admin/usage_trends_controller.rb b/app/controllers/admin/usage_trends_controller.rb
index 7073f71a1a..0b31551759 100644
--- a/app/controllers/admin/usage_trends_controller.rb
+++ b/app/controllers/admin/usage_trends_controller.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
class Admin::UsageTrendsController < Admin::ApplicationController
- include Analytics::UniqueVisitsHelper
+ include RedisTracking
- track_unique_visits :index, target_id: 'i_analytics_instance_statistics'
+ track_redis_hll_event :index, name: 'i_analytics_instance_statistics'
feature_category :devops_reports
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 700acc46d8..41ef79c6d3 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -44,10 +44,11 @@ class Admin::UsersController < Admin::ApplicationController
end
def impersonate
- if can?(user, :log_in)
+ if can?(user, :log_in) && !impersonation_in_progress?
session[:impersonator_id] = current_user.id
warden.set_user(user, scope: :user)
+ clear_access_token_session_keys!
log_impersonation_event
@@ -56,7 +57,9 @@ class Admin::UsersController < Admin::ApplicationController
redirect_to root_path
else
flash[:alert] =
- if user.blocked?
+ if impersonation_in_progress?
+ _("You are already impersonating another user")
+ elsif user.blocked?
_("You cannot impersonate a blocked user")
elsif user.internal?
_("You cannot impersonate an internal user")
@@ -304,7 +307,7 @@ class Admin::UsersController < Admin::ApplicationController
end
def user
- @user ||= find_routable!(User, params[:id])
+ @user ||= find_routable!(User, params[:id], request.path_info)
end
def build_canonical_path(user)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 07ecde1181..34bad74a9f 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -106,10 +106,6 @@ class ApplicationController < ActionController::Base
redirect_back(fallback_location: default, **options)
end
- def check_if_gl_com_or_dev
- render_404 unless ::Gitlab.dev_env_or_com?
- end
-
def not_found
render_404
end
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
index 003ed45adb..f0f074792e 100644
--- a/app/controllers/boards/issues_controller.rb
+++ b/app/controllers/boards/issues_controller.rb
@@ -136,7 +136,7 @@ module Boards
def issue_params
params.require(:issue)
.permit(:title, :milestone_id, :project_id)
- .merge(board_id: params[:board_id], list_id: params[:list_id], request: request)
+ .merge(board_id: params[:board_id], list_id: params[:list_id])
end
def serializer
diff --git a/app/controllers/chaos_controller.rb b/app/controllers/chaos_controller.rb
index 1cfcd2905f..4e5af1945a 100644
--- a/app/controllers/chaos_controller.rb
+++ b/app/controllers/chaos_controller.rb
@@ -31,7 +31,7 @@ class ChaosController < ActionController::Base
gc_stat = Gitlab::Chaos.run_gc
render json: {
- worker_id: Prometheus::PidProvider.worker_id,
+ worker_id: ::Prometheus::PidProvider.worker_id,
gc_stat: gc_stat
}
end
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 4f4b204def..da5b7ccfbf 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -151,14 +151,14 @@ module AuthenticatesWithTwoFactor
def handle_two_factor_failure(user, method, message)
user.increment_failed_attempts!
- log_failed_two_factor(user, method, request.remote_ip)
+ log_failed_two_factor(user, method)
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}")
flash.now[:alert] = message
prompt_for_two_factor(user)
end
- def log_failed_two_factor(user, method, ip_address)
+ def log_failed_two_factor(user, method)
# overridden in EE
end
diff --git a/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb
index a8155f1e63..574fc6c0f3 100644
--- a/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb
@@ -98,7 +98,7 @@ module AuthenticatesWithTwoFactorForAdminMode
def admin_handle_two_factor_failure(user, method, message)
user.increment_failed_attempts!
- log_failed_two_factor(user, method, request.remote_ip)
+ log_failed_two_factor(user, method)
Gitlab::AppLogger.info("Failed Admin Mode Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}")
flash.now[:alert] = message
diff --git a/app/controllers/concerns/impersonation.rb b/app/controllers/concerns/impersonation.rb
index a4f2c263eb..539dd9ad69 100644
--- a/app/controllers/concerns/impersonation.rb
+++ b/app/controllers/concerns/impersonation.rb
@@ -3,6 +3,12 @@
module Impersonation
include Gitlab::Utils::StrongMemoize
+ SESSION_KEYS_TO_DELETE = %w(
+ github_access_token gitea_access_token gitlab_access_token
+ bitbucket_token bitbucket_refresh_token bitbucket_server_personal_access_token
+ bulk_import_gitlab_access_token fogbugz_token
+ ).freeze
+
def current_user
user = super
@@ -14,7 +20,7 @@ module Impersonation
protected
def check_impersonation_availability
- return unless session[:impersonator_id]
+ return unless impersonation_in_progress?
unless Gitlab.config.gitlab.impersonation_enabled
stop_impersonation
@@ -27,14 +33,25 @@ module Impersonation
warden.set_user(impersonator, scope: :user)
session[:impersonator_id] = nil
+ clear_access_token_session_keys!
current_user
end
+ def impersonation_in_progress?
+ session[:impersonator_id].present?
+ end
+
def log_impersonation_event
Gitlab::AppLogger.info("User #{impersonator.username} has stopped impersonating #{current_user.username}")
end
+ def clear_access_token_session_keys!
+ access_tokens_keys = session.keys & SESSION_KEYS_TO_DELETE
+
+ access_tokens_keys.each { |key| session.delete(key) }
+ end
+
def impersonator
strong_memoize(:impersonator) do
User.find(session[:impersonator_id]) if session[:impersonator_id]
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index 929e60a9e7..2664a7b715 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -3,6 +3,7 @@
module IssuableActions
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize
+ include Gitlab::Cache::Helpers
included do
before_action :authorize_destroy_issuable!, only: :destroy
@@ -129,7 +130,11 @@ module IssuableActions
discussions = Discussion.build_collection(notes, issuable)
- render json: discussion_serializer.represent(discussions, context: self)
+ if issuable.is_a?(MergeRequest) && Feature.enabled?(:merge_request_discussion_cache, issuable.target_project, default_enabled: :yaml)
+ render_cached(discussions, with: discussion_serializer, context: self)
+ else
+ render json: discussion_serializer.represent(discussions, context: self)
+ end
end
# rubocop:enable CodeReuse/ActiveRecord
diff --git a/app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb b/app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb
index e0e3f628cc..65237b552c 100644
--- a/app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb
+++ b/app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb
@@ -16,7 +16,7 @@ module Metrics::Dashboard::PrometheusApiProxy
return error_response(variable_substitution_result)
end
- prometheus_result = Prometheus::ProxyService.new(
+ prometheus_result = ::Prometheus::ProxyService.new(
proxyable,
proxy_method,
proxy_path,
diff --git a/app/controllers/concerns/project_unauthorized.rb b/app/controllers/concerns/project_unauthorized.rb
index 7238840440..b58f6589f9 100644
--- a/app/controllers/concerns/project_unauthorized.rb
+++ b/app/controllers/concerns/project_unauthorized.rb
@@ -3,7 +3,7 @@
module ProjectUnauthorized
module ControllerActions
def self.on_routable_not_found
- lambda do |routable|
+ lambda do |routable, path_info|
return unless routable.is_a?(Project)
label = routable.external_authorization_classification_label
diff --git a/app/controllers/concerns/redis_tracking.rb b/app/controllers/concerns/redis_tracking.rb
index 3155208f47..c1135d2f75 100644
--- a/app/controllers/concerns/redis_tracking.rb
+++ b/app/controllers/concerns/redis_tracking.rb
@@ -12,12 +12,13 @@
# You can also pass custom conditions using `if:`, using the same format as with Rails callbacks.
# You can also pass an optional block that calculates and returns a custom id to track.
module RedisTracking
+ include Gitlab::Tracking::Helpers
extend ActiveSupport::Concern
class_methods do
def track_redis_hll_event(*controller_actions, name:, if: nil, &block)
custom_conditions = Array.wrap(binding.local_variable_get('if'))
- conditions = [:trackable_request?, *custom_conditions]
+ conditions = [:trackable_html_request?, *custom_conditions]
after_action only: controller_actions, if: conditions do
track_unique_redis_hll_event(name, &block)
@@ -37,10 +38,6 @@ module RedisTracking
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: unique_id)
end
- def trackable_request?
- request.format.html? && request.headers['DNT'] != '1'
- end
-
def visitor_id
return cookies[:visitor_id] if cookies[:visitor_id].present?
return unless current_user
diff --git a/app/controllers/concerns/routable_actions.rb b/app/controllers/concerns/routable_actions.rb
index 7257378f46..57108369c6 100644
--- a/app/controllers/concerns/routable_actions.rb
+++ b/app/controllers/concerns/routable_actions.rb
@@ -3,13 +3,13 @@
module RoutableActions
extend ActiveSupport::Concern
- def find_routable!(routable_klass, requested_full_path, extra_authorization_proc: nil)
- routable = routable_klass.find_by_full_path(requested_full_path, follow_redirects: request.get?)
+ def find_routable!(routable_klass, routable_full_path, path_info, extra_authorization_proc: nil)
+ routable = routable_klass.find_by_full_path(routable_full_path, follow_redirects: request.get?)
if routable_authorized?(routable, extra_authorization_proc)
- ensure_canonical_path(routable, requested_full_path)
+ ensure_canonical_path(routable, routable_full_path)
routable
else
- perform_not_found_actions(routable, not_found_actions)
+ perform_not_found_actions(routable, not_found_actions, path_info)
route_not_found unless performed?
@@ -21,11 +21,11 @@ module RoutableActions
[ProjectUnauthorized::ControllerActions.on_routable_not_found]
end
- def perform_not_found_actions(routable, actions)
+ def perform_not_found_actions(routable, actions, path_info)
actions.each do |action|
break if performed?
- instance_exec(routable, &action)
+ instance_exec(routable, path_info, &action)
end
end
@@ -42,13 +42,13 @@ module RoutableActions
end
end
- def ensure_canonical_path(routable, requested_full_path)
+ def ensure_canonical_path(routable, routable_full_path)
return unless request.get?
canonical_path = routable.full_path
- if canonical_path != requested_full_path
- if !request.xhr? && request.format.html? && canonical_path.casecmp(requested_full_path) != 0
- flash[:notice] = "#{routable.class.to_s.titleize} '#{requested_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path."
+ if canonical_path != routable_full_path
+ if !request.xhr? && request.format.html? && canonical_path.casecmp(routable_full_path) != 0
+ flash[:notice] = "#{routable.class.to_s.titleize} '#{routable_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path."
end
redirect_to build_canonical_path(routable), status: :moved_permanently
diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb
index 9e861d2859..eb1223f22a 100644
--- a/app/controllers/concerns/spammable_actions.rb
+++ b/app/controllers/concerns/spammable_actions.rb
@@ -47,31 +47,16 @@ module SpammableActions
end
end
- def spammable_params
- # NOTE: For the legacy reCAPTCHA implementation based on the HTML/HAML form, the
- # 'g-recaptcha-response' field name comes from `Recaptcha::ClientHelper#recaptcha_tags` in the
- # recaptcha gem, which is called from the HAML `_recaptcha_form.html.haml` form.
- #
- # It is used in the `Recaptcha::Verify#verify_recaptcha` to extract the value from `params`,
- # if the `response` option is not passed explicitly.
- #
- # Instead of relying on this behavior, we are extracting and passing it explicitly. This will
- # make it consistent with the newer, modern reCAPTCHA verification process as it will be
- # implemented via the GraphQL API and in Vue components via the native reCAPTCHA Javascript API,
- # which requires that the recaptcha response param be obtained and passed explicitly.
- #
- # It can also be expanded to multiple fields when we move to future alternative captcha
- # implementations such as FriendlyCaptcha. See https://gitlab.com/gitlab-org/gitlab/-/issues/273480
-
- # After this newer GraphQL/JS API process is fully supported by the backend, we can remove the
- # check for the 'g-recaptcha-response' field and other HTML/HAML form-specific support.
- captcha_response = params['g-recaptcha-response'] || params[:captcha_response]
-
- {
- request: request,
- spam_log_id: params[:spam_log_id],
- captcha_response: captcha_response
- }
+ # TODO: This method is currently only needed for issue create, to convert spam/CAPTCHA values from
+ # params, and instead be passed as headers, as the spam services now all expect. It can be removed
+ # when issue create is is converted to a client/JS based approach instead of the legacy HAML
+ # `_recaptcha_form.html.haml` which is rendered via the `projects/issues/verify` template.
+ # In that case, which is based on the legacy reCAPTCHA implementation using the HTML/HAML form,
+ # the 'g-recaptcha-response' field name comes from `Recaptcha::ClientHelper#recaptcha_tags` in the
+ # recaptcha gem, which is called from the HAML `_recaptcha_form.html.haml` form.
+ def extract_legacy_spam_params_to_headers
+ request.headers['X-GitLab-Captcha-Response'] = params['g-recaptcha-response'] || params[:captcha_response]
+ request.headers['X-GitLab-Spam-Log-Id'] = params[:spam_log_id]
end
def spammable
diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb
index b7f6691ef4..848b7ee44c 100644
--- a/app/controllers/concerns/wiki_actions.rb
+++ b/app/controllers/concerns/wiki_actions.rb
@@ -14,8 +14,7 @@ module WikiActions
before_action { respond_to :html }
before_action :authorize_read_wiki!
- before_action :authorize_create_wiki!, only: [:edit, :create]
- before_action :authorize_admin_wiki!, only: :destroy
+ before_action :authorize_create_wiki!, only: [:edit, :create, :destroy]
before_action :wiki
before_action :page, only: [:show, :edit, :update, :history, :destroy, :diff]
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index e17b16c26a..1369e82a69 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -13,7 +13,7 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
@milestones = milestones.page(params[:page])
end
format.json do
- render json: milestones.to_json(only: [:id, :title], methods: :name)
+ render json: milestones.to_json(only: [:id, :title, :due_date], methods: :name)
end
end
end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 5948d9b118..21bbb4d0c9 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -3,7 +3,6 @@
class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
include PaginatedCollection
- include Analytics::UniqueVisitsHelper
before_action :authorize_read_project!, only: :index
before_action :authorize_read_group!, only: :index
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index a3bbfc8be0..69081835c4 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -13,10 +13,18 @@ class Groups::ApplicationController < ApplicationController
before_action :set_sorting
requires_cross_project_access
+ helper_method :can_manage_members?
+
private
+ def can_manage_members?(group = @group)
+ strong_memoize(:can_manage_members) do
+ can?(current_user, :admin_group_member, group)
+ end
+ end
+
def group
- @group ||= find_routable!(Group, params[:group_id] || params[:id])
+ @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info)
end
def group_projects
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index 3d8cdd766b..04b4d8ea9a 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -8,6 +8,7 @@ class Groups::BoardsController < Groups::ApplicationController
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: false)
+ push_frontend_feature_flag(:issue_boards_filtered_search, group, default_enabled: :yaml)
push_frontend_feature_flag(:board_multi_select, group, default_enabled: :yaml)
push_frontend_feature_flag(:swimlanes_buffered_rendering, group, default_enabled: :yaml)
push_frontend_feature_flag(:iteration_cadences, group, default_enabled: :yaml)
diff --git a/app/controllers/groups/clusters/applications_controller.rb b/app/controllers/groups/clusters/applications_controller.rb
index 8dd8a01cf4..ce6fda4143 100644
--- a/app/controllers/groups/clusters/applications_controller.rb
+++ b/app/controllers/groups/clusters/applications_controller.rb
@@ -13,6 +13,6 @@ class Groups::Clusters::ApplicationsController < Clusters::ApplicationsControlle
end
def group
- @group ||= find_routable!(Group, params[:group_id] || params[:id])
+ @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info)
end
end
diff --git a/app/controllers/groups/clusters/integrations_controller.rb b/app/controllers/groups/clusters/integrations_controller.rb
index e8c8a14c16..61b308f7d1 100644
--- a/app/controllers/groups/clusters/integrations_controller.rb
+++ b/app/controllers/groups/clusters/integrations_controller.rb
@@ -13,6 +13,6 @@ class Groups::Clusters::IntegrationsController < Clusters::IntegrationsControlle
end
def group
- @group ||= find_routable!(Group, params[:group_id] || params[:id])
+ @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info)
end
end
diff --git a/app/controllers/groups/clusters_controller.rb b/app/controllers/groups/clusters_controller.rb
index 33bfc24885..6f3eecf829 100644
--- a/app/controllers/groups/clusters_controller.rb
+++ b/app/controllers/groups/clusters_controller.rb
@@ -15,7 +15,7 @@ class Groups::ClustersController < Clusters::ClustersController
end
def group
- @group ||= find_routable!(Group, params[:group_id] || params[:id])
+ @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info)
end
def metrics_dashboard_params
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 8d9059d271..d5e7653dea 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -22,8 +22,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
feature_category :authentication_and_authorization
- helper_method :can_manage_members?
-
def index
@sort = params[:sort].presence || sort_value_name
@@ -53,12 +51,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
private
- def can_manage_members?
- strong_memoize(:can_manage_members) do
- can?(current_user, :admin_group_member, @group)
- end
- end
-
def present_invited_members(invited_members)
present_members(invited_members
.page(params[:invited_members_page])
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index e9dce3947d..63eff750d1 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -15,7 +15,7 @@ class Groups::MilestonesController < Groups::ApplicationController
@milestones = milestones.page(params[:page])
end
format.json do
- render json: milestones.to_json(only: [:id, :title], methods: :name)
+ render json: milestones.to_json(only: [:id, :title, :due_date], methods: :name)
end
end
end
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index b02b0e85d3..1cff658dd5 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -7,7 +7,7 @@ class Groups::RunnersController < Groups::ApplicationController
before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
- feature_category :continuous_integration
+ feature_category :runner
def show
end
diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb
index 00ddb8d736..9dbbd385ea 100644
--- a/app/controllers/groups/variables_controller.rb
+++ b/app/controllers/groups/variables_controller.rb
@@ -6,7 +6,7 @@ module Groups
skip_cross_project_access_check :show, :update
- feature_category :continuous_integration
+ feature_category :pipeline_authoring
def show
respond_to do |format|
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index 06906001ef..a1fb74cf27 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -13,7 +13,7 @@ class HelpController < ApplicationController
def index
# Remove YAML frontmatter so that it doesn't look weird
- @help_index = File.read(Rails.root.join('doc', 'README.md')).sub(YAML_FRONT_MATTER_REGEXP, '')
+ @help_index = File.read(Rails.root.join('doc', 'index.md')).sub(YAML_FRONT_MATTER_REGEXP, '')
# Prefix Markdown links with `help/` unless they are external links.
# '//' not necessarily part of URL, e.g., mailto:mail@example.com
diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb
index 9d6c0a003c..e99b8cfa0c 100644
--- a/app/controllers/import/bulk_imports_controller.rb
+++ b/app/controllers/import/bulk_imports_controller.rb
@@ -10,7 +10,7 @@ class Import::BulkImportsController < ApplicationController
POLLING_INTERVAL = 3_000
- rescue_from BulkImports::Clients::HTTP::ConnectionError, with: :bulk_import_connection_error
+ rescue_from BulkImports::Error, with: :bulk_import_connection_error
def configure
session[access_token_key] = configure_params[access_token_key]&.strip
@@ -87,7 +87,7 @@ class Import::BulkImportsController < ApplicationController
def client
@client ||= BulkImports::Clients::HTTP.new(
- uri: session[url_key],
+ url: session[url_key],
token: session[access_token_key],
per_page: params[:per_page],
page: params[:page]
diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb
index 5a4eef352b..32c9da67e9 100644
--- a/app/controllers/import/gitea_controller.rb
+++ b/app/controllers/import/gitea_controller.rb
@@ -66,11 +66,13 @@ class Import::GiteaController < Import::GithubController
override :client_options
def client_options
- { host: provider_url, api_version: 'v1' }
+ verified_url, provider_hostname = verify_blocked_uri
+
+ { host: verified_url.scheme == 'https' ? provider_url : verified_url.to_s, api_version: 'v1', hostname: provider_hostname }
end
def verify_blocked_uri
- Gitlab::UrlBlocker.validate!(
+ @verified_url_and_hostname ||= Gitlab::UrlBlocker.validate!(
provider_url,
allow_localhost: allow_local_requests?,
allow_local_network: allow_local_requests?,
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index cec2f0e4d8..3c81b69854 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -86,18 +86,7 @@ class InvitesController < ApplicationController
if user_sign_up?
set_session_invite_params
- experiment(:invite_signup_page_interaction, actor: member) do |experiment_instance|
- set_originating_member_id if experiment_instance.enabled?
-
- experiment_instance.use do
- redirect_to new_user_registration_path(invite_email: member.invite_email), notice: _("To accept this invitation, create an account or sign in.")
- end
- experiment_instance.try do
- redirect_to new_users_sign_up_invite_path(invite_email: member.invite_email)
- end
-
- experiment_instance.track(:view)
- end
+ redirect_to new_user_registration_path(invite_email: member.invite_email), notice: _("To accept this invitation, create an account or sign in.")
else
redirect_to new_user_session_path(sign_in_redirect_params), notice: sign_in_notice
end
@@ -106,11 +95,7 @@ class InvitesController < ApplicationController
def set_session_invite_params
session[:invite_email] = member.invite_email
- set_originating_member_id if Members::InviteEmailExperiment.initial_invite_email?(params[:invite_type])
- end
-
- def set_originating_member_id
- session[:originating_member_id] = member.id
+ session[:originating_member_id] = member.id if Members::InviteEmailExperiment.initial_invite_email?(params[:invite_type])
end
def sign_in_redirect_params
diff --git a/app/controllers/jira_connect/events_controller.rb b/app/controllers/jira_connect/events_controller.rb
index d833491b8f..fe66e742c4 100644
--- a/app/controllers/jira_connect/events_controller.rb
+++ b/app/controllers/jira_connect/events_controller.rb
@@ -19,7 +19,7 @@ class JiraConnect::EventsController < JiraConnect::ApplicationController
end
def uninstalled
- if current_jira_installation.destroy
+ if JiraConnectInstallations::DestroyService.execute(current_jira_installation, jira_connect_base_path, jira_connect_events_uninstalled_path)
head :ok
else
head :unprocessable_entity
diff --git a/app/controllers/metrics_controller.rb b/app/controllers/metrics_controller.rb
index 1ef1e12bb0..a0c307a0a0 100644
--- a/app/controllers/metrics_controller.rb
+++ b/app/controllers/metrics_controller.rb
@@ -30,7 +30,7 @@ class MetricsController < ActionController::Base
def system_metrics
Gitlab::Metrics::System.summary.merge(
- worker_id: Prometheus::PidProvider.worker_id
+ worker_id: ::Prometheus::PidProvider.worker_id
)
end
end
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index 7f04927f51..9e16d195b0 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -22,7 +22,7 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
end
def destroy
- @gpg_key.destroy
+ GpgKeys::DestroyService.new(current_user).execute(@gpg_key)
respond_to do |format|
format.html { redirect_to profile_gpg_keys_url, status: :found }
diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/profiles/groups_controller.rb
index e76ee0a6ce..2571e92e07 100644
--- a/app/controllers/profiles/groups_controller.rb
+++ b/app/controllers/profiles/groups_controller.rb
@@ -6,7 +6,7 @@ class Profiles::GroupsController < Profiles::ApplicationController
feature_category :users
def update
- group = find_routable!(Group, params[:id])
+ group = find_routable!(Group, params[:id], request.path_info)
notification_setting = current_user.notification_settings_for(group)
if notification_setting.update(update_params)
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index 85e901eb3e..c8c2dd1c7d 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -47,6 +47,8 @@ class Profiles::PasswordsController < Profiles::ApplicationController
password_attributes[:password_automatically_set] = false
unless @user.password_automatically_set || @user.valid_password?(user_params[:current_password])
+ handle_invalid_current_password_attempt!
+
redirect_to edit_profile_password_path, alert: _('You must provide a valid current password')
return
end
@@ -85,6 +87,12 @@ class Profiles::PasswordsController < Profiles::ApplicationController
render_404 unless @user.allow_password_authentication?
end
+ def handle_invalid_current_password_attempt!
+ Gitlab::AppLogger.info(message: 'Invalid current password when attempting to update user password', username: @user.username, ip: request.remote_ip)
+
+ @user.increment_failed_attempts!
+ end
+
def user_params
params.require(:user).permit(:current_password, :password, :password_confirmation)
end
diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb
index ba539ef808..8dc9697c56 100644
--- a/app/controllers/profiles/personal_access_tokens_controller.rb
+++ b/app/controllers/profiles/personal_access_tokens_controller.rb
@@ -9,7 +9,11 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
def index
set_index_vars
- @personal_access_token = finder.build
+ scopes = params[:scopes].split(',').map(&:squish).select(&:present?).map(&:to_sym) unless params[:scopes].nil?
+ @personal_access_token = finder.build(
+ name: params[:name],
+ scopes: scopes
+ )
end
def create
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index e2f8baa822..5cbab55722 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -2,6 +2,9 @@
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
skip_before_action :check_two_factor_requirement
+
+ before_action :validate_current_password, only: [:create, :codes, :destroy]
+
before_action do
push_frontend_feature_flag(:webauthn)
end
@@ -129,6 +132,14 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
private
+ def validate_current_password
+ return if current_user.valid_password?(params[:current_password])
+
+ current_user.increment_failed_attempts!
+
+ redirect_to profile_two_factor_auth_path, alert: _('You must provide a valid current password')
+ end
+
def build_qr_code
uri = current_user.otp_provisioning_uri(account_string, issuer: issuer_host)
RQRCode.render_qrcode(uri, :svg, level: :m, unit: 3)
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index ca2692438e..cf2ecb0673 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -26,7 +26,7 @@ class Projects::ApplicationController < ApplicationController
path = File.join(params[:namespace_id], params[:project_id] || params[:id])
auth_proc = ->(project) { !project.pending_delete? }
- @project = find_routable!(Project, path, extra_authorization_proc: auth_proc)
+ @project = find_routable!(Project, path, request.path_info, extra_authorization_proc: auth_proc)
end
def build_canonical_path(project)
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index f6a92b0729..7bb3ed1d10 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -15,7 +15,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
MAX_PER_PAGE = 20
- feature_category :continuous_integration
+ feature_category :build_artifacts
def index
# Loading artifacts is very expensive in projects with a lot of artifacts.
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index c6c9237292..08066acb45 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -23,6 +23,10 @@ class Projects::BlobController < Projects::ApplicationController
# We need to assign the blob vars before `authorize_edit_tree!` so we can
# validate access to a specific ref.
before_action :assign_blob_vars
+
+ # Since BlobController doesn't use assign_ref_vars, we have to call this explicitly
+ before_action :rectify_renamed_default_branch!, only: [:show]
+
before_action :authorize_edit_tree!, only: [:new, :create, :update, :destroy]
before_action :commit, except: [:new, :create]
@@ -140,11 +144,15 @@ class Projects::BlobController < Projects::ApplicationController
end
def commit
- @commit = @repository.commit(@ref)
+ @commit ||= @repository.commit(@ref)
return render_404 unless @commit
end
+ def redirect_renamed_default_branch?
+ action_name == 'show'
+ end
+
def assign_blob_vars
@id = params[:id]
@ref, @path = extract_ref(@id)
@@ -152,6 +160,12 @@ class Projects::BlobController < Projects::ApplicationController
render_404
end
+ def rectify_renamed_default_branch!
+ @commit ||= @repository.commit(@ref)
+
+ super
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def after_edit_path
from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).find_by(iid: params[:from_merge_request_iid])
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 43c9046f85..035b76abfd 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -9,6 +9,7 @@ class Projects::BoardsController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:swimlanes_buffered_rendering, project, default_enabled: :yaml)
push_frontend_feature_flag(:graphql_board_lists, project, default_enabled: :yaml)
+ push_frontend_feature_flag(:issue_boards_filtered_search, project, default_enabled: :yaml)
push_frontend_feature_flag(:board_multi_select, project, default_enabled: :yaml)
push_frontend_feature_flag(:iteration_cadences, project&.group, default_enabled: :yaml)
end
diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb
index 148080a71f..d5655d4042 100644
--- a/app/controllers/projects/build_artifacts_controller.rb
+++ b/app/controllers/projects/build_artifacts_controller.rb
@@ -8,7 +8,7 @@ class Projects::BuildArtifactsController < Projects::ApplicationController
before_action :extract_ref_name_and_path
before_action :validate_artifacts!, except: [:download]
- feature_category :continuous_integration
+ feature_category :build_artifacts
def download
redirect_to download_project_job_artifacts_path(project, job, params: request.query_parameters)
diff --git a/app/controllers/projects/clusters/applications_controller.rb b/app/controllers/projects/clusters/applications_controller.rb
index 2a04b00730..6c5778124e 100644
--- a/app/controllers/projects/clusters/applications_controller.rb
+++ b/app/controllers/projects/clusters/applications_controller.rb
@@ -10,6 +10,6 @@ class Projects::Clusters::ApplicationsController < Clusters::ApplicationsControl
end
def project
- @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]))
+ @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info)
end
end
diff --git a/app/controllers/projects/clusters/integrations_controller.rb b/app/controllers/projects/clusters/integrations_controller.rb
index 94b8dd653e..eed6c1dccc 100644
--- a/app/controllers/projects/clusters/integrations_controller.rb
+++ b/app/controllers/projects/clusters/integrations_controller.rb
@@ -10,6 +10,6 @@ class Projects::Clusters::IntegrationsController < ::Clusters::IntegrationsContr
end
def project
- @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]))
+ @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info)
end
end
diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb
index 8acf5235c1..0aef497d28 100644
--- a/app/controllers/projects/clusters_controller.rb
+++ b/app/controllers/projects/clusters_controller.rb
@@ -17,7 +17,7 @@ class Projects::ClustersController < Clusters::ClustersController
end
def project
- @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]))
+ @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info)
end
def repository
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 3d2398f7ee..6748be06de 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -52,7 +52,8 @@ class Projects::CommitController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def pipelines
@pipelines = @commit.pipelines.order(id: :desc)
- @pipelines = @pipelines.where(ref: params[:ref]).page(params[:page]).per(30) if params[:ref]
+ @pipelines = @pipelines.where(ref: params[:ref]) if params[:ref]
+ @pipelines = @pipelines.page(params[:page])
respond_to do |format|
format.html
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index d1d27286c6..db5ba51ee0 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -4,15 +4,19 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TextHelper
include CycleAnalyticsParams
- include Analytics::UniqueVisitsHelper
include GracefulTimeoutHandling
+ include RedisTracking
before_action :authorize_read_cycle_analytics!
- track_unique_visits :show, target_id: 'p_analytics_valuestream'
+ track_redis_hll_event :show, name: 'p_analytics_valuestream'
feature_category :planning_analytics
+ before_action do
+ push_licensed_feature(:cycle_analytics_for_groups) if project.licensed_feature_available?(:cycle_analytics_for_groups)
+ end
+
def show
@cycle_analytics = Analytics::CycleAnalytics::ProjectLevel.new(project: @project, options: options(cycle_analytics_project_params))
diff --git a/app/controllers/projects/environments/prometheus_api_controller.rb b/app/controllers/projects/environments/prometheus_api_controller.rb
index 97810d7d43..94fe67b5e8 100644
--- a/app/controllers/projects/environments/prometheus_api_controller.rb
+++ b/app/controllers/projects/environments/prometheus_api_controller.rb
@@ -14,6 +14,6 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon
end
def proxy_variable_substitution_service
- Prometheus::ProxyVariableSubstitutionService
+ ::Prometheus::ProxyVariableSubstitutionService
end
end
diff --git a/app/controllers/projects/feature_flags_controller.rb b/app/controllers/projects/feature_flags_controller.rb
index a59824b108..b99c233411 100644
--- a/app/controllers/projects/feature_flags_controller.rb
+++ b/app/controllers/projects/feature_flags_controller.rb
@@ -13,10 +13,6 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
before_action :ensure_flag_writable!, only: [:update]
before_action :exclude_legacy_flags_check, only: [:edit]
- before_action do
- push_frontend_feature_flag(:feature_flag_permissions)
- end
-
feature_category :feature_flags
def index
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index 9e42d218ce..0f00fda468 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -17,7 +17,7 @@ class Projects::ForksController < Projects::ApplicationController
feature_category :source_code_management
before_action do
- push_frontend_feature_flag(:fork_project_form)
+ push_frontend_feature_flag(:fork_project_form, @project, default_enabled: :yaml)
end
def index
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index ad39b317b3..7a7961c28b 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -2,14 +2,14 @@
class Projects::GraphsController < Projects::ApplicationController
include ExtractsPath
- include Analytics::UniqueVisitsHelper
+ include RedisTracking
# Authorize
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_read_repository_graphs!
- track_unique_visits :charts, target_id: 'p_analytics_repo'
+ track_redis_hll_event :charts, name: 'p_analytics_repo'
feature_category :source_code_management
diff --git a/app/controllers/projects/import/jira_controller.rb b/app/controllers/projects/import/jira_controller.rb
index 8418a60765..46c4761b0e 100644
--- a/app/controllers/projects/import/jira_controller.rb
+++ b/app/controllers/projects/import/jira_controller.rb
@@ -25,9 +25,9 @@ module Projects
false
end
- def jira_service
- strong_memoize(:jira_service) do
- @project.jira_service
+ def jira_integration
+ strong_memoize(:jira_integration) do
+ @project.jira_integration
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 295213bd38..5d38e431c8 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -50,10 +50,9 @@ class Projects::IssuesController < Projects::ApplicationController
end
before_action only: :show do
- real_time_feature_flag = :real_time_issue_sidebar
- real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(real_time_feature_flag, @project)
+ real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:real_time_issue_sidebar, @project)
- push_to_gon_attributes(:features, real_time_feature_flag, real_time_enabled)
+ push_to_gon_attributes(:features, :real_time_issue_sidebar, real_time_enabled)
push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml)
push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml)
push_frontend_feature_flag(:labels_widget, @project, default_enabled: :yaml)
@@ -130,12 +129,14 @@ class Projects::IssuesController < Projects::ApplicationController
end
def create
- create_params = issue_params.merge(spammable_params).merge(
+ extract_legacy_spam_params_to_headers
+ create_params = issue_params.merge(
merge_request_to_resolve_discussions_of: params[:merge_request_to_resolve_discussions_of],
discussion_to_resolve: params[:discussion_to_resolve]
)
- service = ::Issues::CreateService.new(project: project, current_user: current_user, params: create_params)
+ spam_params = ::Spam::SpamParams.new_from_request(request: request)
+ service = ::Issues::CreateService.new(project: project, current_user: current_user, params: create_params, spam_params: spam_params)
@issue = service.execute
create_vulnerability_issue_feedback(issue)
@@ -335,8 +336,8 @@ class Projects::IssuesController < Projects::ApplicationController
end
def update_service
- update_params = issue_params.merge(spammable_params)
- ::Issues::UpdateService.new(project: project, current_user: current_user, params: update_params)
+ spam_params = ::Spam::SpamParams.new_from_request(request: request)
+ ::Issues::UpdateService.new(project: project, current_user: current_user, params: issue_params, spam_params: spam_params)
end
def finder_type
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 92442fd4e2..49687a50ff 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -17,6 +17,10 @@ class Projects::JobsController < Projects::ApplicationController
before_action :verify_proxy_request!, only: :proxy_websocket_authorize
before_action :push_jobs_table_vue, only: [:index]
+ before_action do
+ push_frontend_feature_flag(:infinitely_collapsible_sections, @project, default_enabled: :yaml)
+ end
+
layout 'project'
feature_category :continuous_integration
diff --git a/app/controllers/projects/mattermosts_controller.rb b/app/controllers/projects/mattermosts_controller.rb
index ac20442788..ebba20b285 100644
--- a/app/controllers/projects/mattermosts_controller.rb
+++ b/app/controllers/projects/mattermosts_controller.rb
@@ -7,7 +7,7 @@ class Projects::MattermostsController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_admin_project!
- before_action :service
+ before_action :integration
before_action :teams, only: [:new]
feature_category :integrations
@@ -16,11 +16,11 @@ class Projects::MattermostsController < Projects::ApplicationController
end
def create
- result, message = @service.configure(current_user, configure_params)
+ result, message = integration.configure(current_user, configure_params)
if result
flash[:notice] = 'This service is now configured'
- redirect_to edit_project_service_path(@project, service)
+ redirect_to edit_project_service_path(@project, integration)
else
flash[:alert] = message || 'Failed to configure service'
redirect_to new_project_mattermost_path(@project)
@@ -31,15 +31,15 @@ class Projects::MattermostsController < Projects::ApplicationController
def configure_params
params.require(:mattermost).permit(:trigger, :team_id).merge(
- url: service_trigger_url(@service),
+ url: service_trigger_url(integration),
icon_url: asset_url('slash-command-logo.png', skip_pipeline: true))
end
def teams
- @teams, @teams_error_message = @service.list_teams(current_user)
+ @teams, @teams_error_message = integration.list_teams(current_user)
end
- def service
- @service ||= @project.find_or_initialize_service('mattermost_slash_commands')
+ def integration
+ @integration ||= @project.find_or_initialize_integration('mattermost_slash_commands')
end
end
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index f125952cb9..88423bec91 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -3,6 +3,7 @@
class Projects::MergeRequests::DiffsController < Projects::MergeRequests::ApplicationController
include DiffHelper
include RendersNotes
+ include Gitlab::Cache::Helpers
before_action :commit
before_action :define_diff_vars
@@ -40,7 +41,16 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
pagination_data: diffs.pagination_data
}
- render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options)
+ if diff_options_hash[:paths].blank? && Feature.enabled?(:diffs_batch_render_cached, project, default_enabled: :yaml)
+ render_cached(
+ diffs,
+ with: PaginatedDiffSerializer.new(current_user: current_user),
+ cache_context: -> (_) { [diff_view, params[:w], params[:expanded], params[:per_page], params[:page]] },
+ **options
+ )
+ else
+ render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options)
+ end
end
def diffs_metadata
@@ -193,7 +203,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
end
def track_viewed_diffs_events
- return if request.headers['DNT'] == '1'
+ return if dnt_enabled?
Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter
.track_mr_diffs_action(merge_request: @merge_request)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 5958c7f66e..cfa64bbc16 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -30,20 +30,15 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action :check_user_can_push_to_source_branch!, only: [:rebase]
before_action only: [:show] do
push_frontend_feature_flag(:file_identifier_hash)
- push_frontend_feature_flag(:approvals_commented_by, @project, default_enabled: true)
push_frontend_feature_flag(:merge_request_widget_graphql, @project, default_enabled: :yaml)
- push_frontend_feature_flag(:drag_comment_selection, @project, default_enabled: true)
- push_frontend_feature_flag(:unified_diff_components, @project, default_enabled: true)
push_frontend_feature_flag(:default_merge_ref_for_diffs, @project, default_enabled: :yaml)
push_frontend_feature_flag(:core_security_mr_widget_counts, @project)
- push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true)
push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml)
push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml)
push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml)
push_frontend_feature_flag(:usage_data_i_testing_summary_widget_total, @project, default_enabled: :yaml)
push_frontend_feature_flag(:improved_emoji_picker, project, default_enabled: :yaml)
push_frontend_feature_flag(:diffs_virtual_scrolling, project, default_enabled: :yaml)
- push_frontend_feature_flag(:codequality_mr_diff_annotations, project, default_enabled: :yaml)
# Usage data feature flags
push_frontend_feature_flag(:users_expanding_widgets_usage_data, @project, default_enabled: :yaml)
@@ -170,7 +165,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def pipelines
set_pipeline_variables
- @pipelines = @pipelines.page(params[:page]).per(30)
+ @pipelines = @pipelines.page(params[:page])
Gitlab::PollingInterval.set_header(response, interval: 10_000)
@@ -223,7 +218,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def codequality_mr_diff_reports
- reports_response(@merge_request.find_codequality_mr_diff_reports)
+ reports_response(@merge_request.find_codequality_mr_diff_reports, head_pipeline)
end
def codequality_reports
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index dcdda18784..630e7ccd43 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -33,7 +33,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestones = @milestones.page(params[:page])
end
format.json do
- render json: @milestones.to_json(only: [:id, :title], methods: :name)
+ render json: @milestones.to_json(only: [:id, :title, :due_date], methods: :name)
end
end
end
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index deb1500cee..ba7c86434e 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -2,7 +2,7 @@
class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize
- include Analytics::UniqueVisitsHelper
+ include RedisTracking
before_action :disable_query_limiting, only: [:create, :retry]
before_action :pipeline, except: [:index, :new, :create, :charts, :config_variables]
@@ -14,7 +14,6 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action do
push_frontend_feature_flag(:pipeline_graph_layers_view, project, type: :development, default_enabled: :yaml)
- push_frontend_feature_flag(:pipeline_filter_jobs, project, default_enabled: :yaml)
push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml)
push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml)
end
@@ -25,7 +24,7 @@ class Projects::PipelinesController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
- track_unique_visits :charts, target_id: 'p_analytics_pipelines'
+ track_redis_hll_event :charts, name: 'p_analytics_pipelines'
wrap_parameters Ci::Pipeline
@@ -43,13 +42,11 @@ class Projects::PipelinesController < Projects::ApplicationController
.new(project, current_user, index_params)
.execute
.page(params[:page])
- .per(20)
@pipelines_count = limited_pipelines_count(project)
respond_to do |format|
format.html do
- enable_pipeline_empty_state_templates_experiment
enable_code_quality_walkthrough_experiment
enable_ci_runner_templates_experiment
end
@@ -301,18 +298,6 @@ class Projects::PipelinesController < Projects::ApplicationController
params.permit(:scope, :username, :ref, :status)
end
- def enable_pipeline_empty_state_templates_experiment
- experiment(:pipeline_empty_state_templates, namespace: project.root_ancestor) do |e|
- e.exclude! unless current_user
- e.exclude! if @pipelines_count.to_i > 0
- e.exclude! if helpers.has_gitlab_ci?(project)
-
- e.control {}
- e.candidate {}
- e.record!
- end
- end
-
def enable_code_quality_walkthrough_experiment
experiment(:code_quality_walkthrough, namespace: project.root_ancestor) do |e|
e.exclude! unless current_user
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 370cd2b02a..90d93c2a92 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -34,13 +34,13 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end
def import
- @projects = current_user.authorized_projects.order_id_desc
+ @projects = Project.visible_to_user_and_access_level(current_user, Gitlab::Access::MAINTAINER).order_id_desc
end
def apply_import
source_project = Project.find(params[:source_project_id])
- if can?(current_user, :read_project_member, source_project)
+ if can?(current_user, :admin_project_member, source_project)
status = @project.team.import(source_project, current_user)
notice = status ? "Successfully imported" : "Import failed"
else
diff --git a/app/controllers/projects/prometheus/metrics_controller.rb b/app/controllers/projects/prometheus/metrics_controller.rb
index d70d29a341..f3a3d22244 100644
--- a/app/controllers/projects/prometheus/metrics_controller.rb
+++ b/app/controllers/projects/prometheus/metrics_controller.rb
@@ -66,7 +66,7 @@ module Projects
)
if @metric.persisted?
- redirect_to edit_project_service_path(project, ::PrometheusService),
+ redirect_to edit_project_service_path(project, ::Integrations::Prometheus),
notice: _('Metric was successfully added.')
else
render 'new'
@@ -77,7 +77,7 @@ module Projects
@metric = update_metrics_service(prometheus_metric).execute
if @metric.persisted?
- redirect_to edit_project_service_path(project, ::PrometheusService),
+ redirect_to edit_project_service_path(project, ::Integrations::Prometheus),
notice: _('Metric was successfully updated.')
else
render 'edit'
@@ -93,7 +93,7 @@ module Projects
respond_to do |format|
format.html do
- redirect_to edit_project_service_path(project, ::PrometheusService), status: :see_other
+ redirect_to edit_project_service_path(project, ::Integrations::Prometheus), status: :see_other
end
format.json do
head :ok
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index f01d10f4af..be2abc5cdd 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -25,12 +25,6 @@ class Projects::ReleasesController < Projects::ApplicationController
end
end
- def new
- unless Feature.enabled?(:new_release_page, project, default_enabled: true)
- redirect_to(new_project_tag_path(@project))
- end
- end
-
def downloads
redirect_to link.url
end
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index 8f64a8aa1d..8beebb5298 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -117,7 +117,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
# from Redis.
def extract_ref_and_filename(id)
path = id.strip
- data = path.match(/(.*)\/(.*)/)
+ data = path.match(%r{(.*)/(.*)})
if data
[data[1], data[2]]
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index ec1f57f090..e841c3e3d4 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -6,7 +6,7 @@ class Projects::RunnersController < Projects::ApplicationController
layout 'project_settings'
- feature_category :continuous_integration
+ feature_category :runner
def index
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings')
@@ -51,14 +51,14 @@ class Projects::RunnersController < Projects::ApplicationController
end
def toggle_shared_runners
- if !project.shared_runners_enabled && project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable'
- render json: { error: _('Cannot enable shared runners because parent group does not allow it') }, status: :unauthorized
- return
+ update_params = { shared_runners_enabled: !project.shared_runners_enabled }
+ result = Projects::UpdateService.new(project, current_user, update_params).execute
+
+ if result[:status] == :success
+ render json: {}, status: :ok
+ else
+ render json: { error: result[:message] }, status: :unauthorized
end
-
- project.toggle!(:shared_runners_enabled)
-
- render json: {}, status: :ok
end
def toggle_group_runners
diff --git a/app/controllers/projects/service_hook_logs_controller.rb b/app/controllers/projects/service_hook_logs_controller.rb
index 5c814ea139..88de0b7ba0 100644
--- a/app/controllers/projects/service_hook_logs_controller.rb
+++ b/app/controllers/projects/service_hook_logs_controller.rb
@@ -1,20 +1,23 @@
# frozen_string_literal: true
class Projects::ServiceHookLogsController < Projects::HookLogsController
- before_action :service, only: [:show, :retry]
+ extend Gitlab::Utils::Override
+
+ before_action :integration, only: [:show, :retry]
def retry
execute_hook
- redirect_to edit_project_service_path(@project, @service)
+ redirect_to edit_project_service_path(@project, @integration)
end
private
- def hook
- @hook ||= service.service_hook
+ def integration
+ @integration ||= @project.find_or_initialize_integration(params[:service_id])
end
- def service
- @service ||= @project.find_or_initialize_service(params[:service_id])
+ override :hook
+ def hook
+ @hook ||= integration.service_hook || not_found
end
end
diff --git a/app/controllers/projects/usage_ping_controller.rb b/app/controllers/projects/service_ping_controller.rb
similarity index 79%
rename from app/controllers/projects/usage_ping_controller.rb
rename to app/controllers/projects/service_ping_controller.rb
index 77ee53f2e5..00530c09be 100644
--- a/app/controllers/projects/usage_ping_controller.rb
+++ b/app/controllers/projects/service_ping_controller.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-class Projects::UsagePingController < Projects::ApplicationController
+class Projects::ServicePingController < Projects::ApplicationController
before_action :authenticate_user!
- feature_category :usage_ping
+ feature_category :service_ping
def web_ide_clientside_preview
return render_404 unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index cad13d7e70..ef6d96e873 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -9,8 +9,8 @@ class Projects::ServicesController < Projects::ApplicationController
before_action :ensure_service_enabled
before_action :integration
before_action :web_hook_logs, only: [:edit, :update]
- before_action :set_deprecation_notice_for_prometheus_service, only: [:edit, :update]
- before_action :redirect_deprecated_prometheus_service, only: [:update]
+ before_action :set_deprecation_notice_for_prometheus_integration, only: [:edit, :update]
+ before_action :redirect_deprecated_prometheus_integration, only: [:update]
respond_to :html
@@ -46,7 +46,7 @@ class Projects::ServicesController < Projects::ApplicationController
end
def test
- if integration.can_test?
+ if integration.testable?
render json: service_test_response, status: :ok
else
render json: {}, status: :not_found
@@ -84,7 +84,7 @@ class Projects::ServicesController < Projects::ApplicationController
end
def integration
- @integration ||= @project.find_or_initialize_service(params[:id])
+ @integration ||= @project.find_or_initialize_integration(params[:id])
end
alias_method :service, :integration
@@ -104,15 +104,15 @@ class Projects::ServicesController < Projects::ApplicationController
.merge(errors: integration.errors.as_json)
end
- def redirect_deprecated_prometheus_service
- redirect_to edit_project_service_path(project, integration) if integration.is_a?(::PrometheusService) && Feature.enabled?(:settings_operations_prometheus_service, project)
+ def redirect_deprecated_prometheus_integration
+ redirect_to edit_project_service_path(project, integration) if integration.is_a?(::Integrations::Prometheus) && Feature.enabled?(:settings_operations_prometheus_service, project)
end
- def set_deprecation_notice_for_prometheus_service
- return if !integration.is_a?(::PrometheusService) || !Feature.enabled?(:settings_operations_prometheus_service, project)
+ def set_deprecation_notice_for_prometheus_integration
+ return if !integration.is_a?(::Integrations::Prometheus) || !Feature.enabled?(:settings_operations_prometheus_service, project)
operations_link_start = ""
- message = s_('PrometheusService|You can now manage your Prometheus settings on the %{operations_link_start}Operations%{operations_link_end} page. Fields on this page has been deprecated.') % { operations_link_start: operations_link_start, operations_link_end: "" }
+ message = s_('PrometheusService|You can now manage your Prometheus settings on the %{operations_link_start}Operations%{operations_link_end} page. Fields on this page have been deprecated.') % { operations_link_start: operations_link_start, operations_link_end: "'.html_safe, code_close: '
'.html_safe }
+ },
+ {
+ type: 'text',
+ name: 'room',
+ title: _('Campfire room ID (optional)'),
+ placeholder: '123456',
+ help: s_('CampfireService|From the end of the room URL.')
+ }
]
end
diff --git a/app/models/integrations/confluence.rb b/app/models/integrations/confluence.rb
index 30f7349699..7f111f482d 100644
--- a/app/models/integrations/confluence.rb
+++ b/app/models/integrations/confluence.rb
@@ -58,7 +58,7 @@ module Integrations
]
end
- def can_test?
+ def testable?
false
end
diff --git a/app/models/integrations/datadog.rb b/app/models/integrations/datadog.rb
index 8ded6b76f3..0b97ff2cd6 100644
--- a/app/models/integrations/datadog.rb
+++ b/app/models/integrations/datadog.rb
@@ -2,9 +2,12 @@
module Integrations
class Datadog < Integration
- DEFAULT_SITE = 'datadoghq.com'
- URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_site}/v1/input/'
- URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_SITE}/account_management/api-app-keys/"
+ include HasWebHook
+ extend Gitlab::Utils::Override
+
+ DEFAULT_DOMAIN = 'datadoghq.com'
+ URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_domain}/api/v2/webhook'
+ URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_DOMAIN}/account_management/api-app-keys/"
SUPPORTED_EVENTS = %w[
pipeline job
@@ -20,12 +23,10 @@ module Integrations
validates :api_url, presence: true, unless: -> (obj) { obj.datadog_site.present? }
end
- after_save :compose_service_hook, if: :activated?
-
def initialize_properties
super
- self.datadog_site ||= DEFAULT_SITE
+ self.datadog_site ||= DEFAULT_DOMAIN
end
def self.supported_events
@@ -61,7 +62,7 @@ module Integrations
{
type: 'text',
name: 'datadog_site',
- placeholder: DEFAULT_SITE,
+ placeholder: DEFAULT_DOMAIN,
help: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site',
required: false
},
@@ -97,29 +98,25 @@ module Integrations
]
end
- def compose_service_hook
- hook = service_hook || build_service_hook
- hook.url = hook_url
- hook.save
- end
-
+ override :hook_url
def hook_url
- url = api_url.presence || sprintf(URL_TEMPLATE, datadog_site: datadog_site)
+ url = api_url.presence || sprintf(URL_TEMPLATE, datadog_domain: datadog_domain)
url = URI.parse(url)
- url.path = File.join(url.path || '/', api_key)
- query = { service: datadog_service.presence, env: datadog_env.presence }.compact
- url.query = query.to_query unless query.empty?
+ query = {
+ "dd-api-key" => api_key,
+ service: datadog_service.presence,
+ env: datadog_env.presence
+ }.compact
+ url.query = query.to_query
url.to_s
end
def execute(data)
- return if project.disabled_services.include?(to_param)
-
object_kind = data[:object_kind]
object_kind = 'job' if object_kind == 'build'
return unless supported_events.include?(object_kind)
- service_hook.execute(data, "#{object_kind} hook")
+ execute_web_hook!(data, "#{object_kind} hook")
end
def test(data)
@@ -132,5 +129,14 @@ module Integrations
{ success: true, result: result[:message] }
end
+
+ private
+
+ def datadog_domain
+ # Transparently ignore "app" prefix from datadog_site as the official docs table in
+ # https://docs.datadoghq.com/getting_started/site/ is confusing for internal URLs.
+ # US3 needs to keep a prefix but other datacenters cannot have the listed "app" prefix
+ datadog_site.delete_prefix("app.")
+ end
end
end
diff --git a/app/models/integrations/discord.rb b/app/models/integrations/discord.rb
index ef6d46fd3d..76160a61bc 100644
--- a/app/models/integrations/discord.rb
+++ b/app/models/integrations/discord.rb
@@ -54,6 +54,8 @@ module Integrations
builder.add_embed do |embed|
embed.author = Discordrb::Webhooks::EmbedAuthor.new(name: message.user_name, icon_url: message.user_avatar)
embed.description = (message.pretext + "\n" + Array.wrap(message.attachments).join("\n")).gsub(ATTACHMENT_REGEX, " \\k'.html_safe, code_end: '
'.html_safe }
.form-group
.form-text.text-muted
- = _("If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database.")
+ = _("If you get a lot of false alarms from repository checks, you can clear all repository check information from the database.")
- clear_repository_checks_link = _('Clear all repository checks')
- - clear_repository_checks_message = _('This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?')
+ - clear_repository_checks_message = _('This clears repository check states for all projects in the database and cannot be undone. Are you sure?')
= link_to clear_repository_checks_link, clear_repository_check_states_admin_application_settings_path, data: { confirm: clear_repository_checks_message }, method: :put, class: "gl-button btn btn-sm btn-danger gl-mt-3"
.sub-section
@@ -25,29 +24,31 @@
.form-check
= f.check_box :housekeeping_enabled, class: 'form-check-input'
= f.label :housekeeping_enabled, class: 'form-check-label' do
- = _("Enable automatic repository housekeeping (git repack, git gc)")
+ = _("Enable automatic repository housekeeping")
.form-text.text-muted
- = _("If you keep automatic housekeeping disabled for a long time Git repository access on your GitLab server will become slower and your repositories will use more disk space. We recommend to always leave this enabled.")
+ = _("Leaving this setting enabled is recommended.")
+ = link_to s_('Learn more.'), help_page_path('administration/housekeeping.md', anchor: 'housekeeping-options'), target: '_blank', rel: 'noopener noreferrer'
.form-check
= f.check_box :housekeeping_bitmaps_enabled, class: 'form-check-input'
= f.label :housekeeping_bitmaps_enabled, class: 'form-check-label' do
= _("Enable Git pack file bitmap creation")
.form-text.text-muted
- = _("Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance.")
+ = _("Improves Git cloning performance.")
+ = link_to s_('Learn more.'), help_page_path('administration/housekeeping.md', anchor: 'housekeeping-options'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-bold'
= f.number_field :housekeeping_incremental_repack_period, class: 'form-control gl-form-input'
.form-text.text-muted
- = _("Number of Git pushes after which an incremental 'git repack' is run.")
+ = html_escape(s_('Number of Git pushes after which an incremental %{code_start}git repack%{code_end} is run.')) % { code_start: ''.html_safe, code_end: '
'.html_safe }
.form-group
= f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-bold'
= f.number_field :housekeeping_full_repack_period, class: 'form-control gl-form-input'
.form-text.text-muted
- = _("Number of Git pushes after which a full 'git repack' is run.")
+ = html_escape(s_('Number of Git pushes after which a full %{code_start}git repack%{code_end} is run.')) % { code_start: ''.html_safe, code_end: '
'.html_safe }
.form-group
= f.label :housekeeping_gc_period, _('Git GC period'), class: 'label-bold'
= f.number_field :housekeeping_gc_period, class: 'form-control gl-form-input'
.form-text.text-muted
- = _("Number of Git pushes after which 'git gc' is run.")
+ = html_escape(s_('Number of Git pushes after which %{code_start}git gc%{code_end} is run.')) % { code_start: ''.html_safe, code_end: '
'.html_safe }
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_repository_mirrors_form.html.haml b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
index a0076a2f75..0c9b04c02d 100644
--- a/app/views/admin/application_settings/_repository_mirrors_form.html.haml
+++ b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
@@ -3,14 +3,13 @@
%fieldset
.form-group
- = f.label :mirror_available, _('Enable mirror configuration'), class: 'label-bold'
+ = f.label :mirror_available, _('Repository mirroring configuration'), class: 'label-bold'
.form-check
= f.check_box :mirror_available, class: 'form-check-input'
= f.label :mirror_available, class: 'form-check-label' do
- = _('Allow repository mirroring to be configured by project maintainers')
+ = _('Allow project maintainers to configure repository mirroring')
%span.form-text.text-muted
- = _('If disabled, only admins will be able to configure repository mirroring.')
- = link_to sprite_icon('question-o'), help_page_path('user/project/repository/repository_mirroring.md')
+ = _('If disabled, only administrators can configure repository mirroring.')
= render_if_exists 'admin/application_settings/mirror_settings', form: f
diff --git a/app/views/admin/application_settings/_repository_storage.html.haml b/app/views/admin/application_settings/_repository_storage.html.haml
index ab1b2bab57..62a90e173e 100644
--- a/app/views/admin/application_settings/_repository_storage.html.haml
+++ b/app/views/admin/application_settings/_repository_storage.html.haml
@@ -3,20 +3,24 @@
%fieldset
.sub-section
- %h4= _("Hashed repository storage paths")
+ %h4= _('Hashed repository storage paths')
.form-group
.form-check
= f.check_box :hashed_storage_enabled, class: 'form-check-input qa-hashed-storage-checkbox', disabled: @application_setting.hashed_storage_enabled?
- = f.label :hashed_storage_enabled, _("Use hashed storage"), class: 'label-bold form-check-label'
+ = f.label :hashed_storage_enabled, _('Use hashed storage'), class: 'label-bold form-check-label'
.form-text.text-muted
- = _("Use hashed storage paths for newly created and renamed repositories. Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents repositories from having to be moved or renamed when the Repository URL changes and may improve disk I/O performance. (Always enabled since 13.0)")
+ = _('Use hashed storage paths for newly created and renamed repositories. Always enabled since 13.0.')
+ = link_to s_('Learn more.'), help_page_path('administration/repository_storage_types.md', anchor: 'hashed-storage'), target: '_blank', rel: 'noopener noreferrer'
+
.sub-section
%h4= _("Storage nodes for new repositories")
.form-group
.form-text
%p.text-secondary
- = _('Enter weights for storages for new repositories.')
- = link_to sprite_icon('question-o'), help_page_path('administration/repository_storage_paths')
+ - weights_link_url = help_page_path('administration/repository_storage_paths.md', anchor: 'configure-where-new-repositories-are-stored')
+ - weights_link_start = ''.html_safe % { url: weights_link_url }
+ = html_escape(s_('Enter %{weights_link_start}weights%{weights_link_end} for storages for new repositories. Configured storages appear below.')) % { weights_link_start: weights_link_start, weights_link_end: ''.html_safe }
+ = link_to s_('Learn more.'), help_page_path('administration/repository_storage_paths.md'), target: '_blank', rel: 'noopener noreferrer'
.form-check
= f.fields_for :repository_storages_weighted, storage_weights do |storage_form|
- Gitlab.config.repositories.storages.keys.each do |storage|
diff --git a/app/views/admin/application_settings/_runner_registrars_form.html.haml b/app/views/admin/application_settings/_runner_registrars_form.html.haml
new file mode 100644
index 0000000000..b7ab896533
--- /dev/null
+++ b/app/views/admin/application_settings/_runner_registrars_form.html.haml
@@ -0,0 +1,16 @@
+= form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-runner-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
+
+ %fieldset
+ .form-group
+ = hidden_field_tag "application_setting[valid_runner_registrars][]", nil
+ - ApplicationSetting::VALID_RUNNER_REGISTRAR_TYPES.each do |type|
+ .form-check
+ = f.check_box(:valid_runner_registrars, { multiple: true, checked: valid_runner_registrars.include?(type), class: 'form-check-input' }, type, nil)
+ = f.label :valid_runner_registrars, class: 'form-check-label' do
+ = s_("Runners|Members of the %{type} can register runners") % { type: type }
+ %span.form-text.gl-text-gray-600
+ = _('If no options are selected, only administrators can register runners.')
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'runner-registration'), target: '_blank', rel: 'noopener noreferrer'
+
+ = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_snowplow.html.haml b/app/views/admin/application_settings/_snowplow.html.haml
index 5daf220d81..8c98778147 100644
--- a/app/views/admin/application_settings/_snowplow.html.haml
+++ b/app/views/admin/application_settings/_snowplow.html.haml
@@ -6,7 +6,8 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _('Configure the %{link} integration.').html_safe % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank') }
+ - link_start = ''.html_safe % { url: help_page_path('development/snowplow/index') }
+ = html_escape(_('Configure %{link} to track events. %{link_start}Learn more.%{link_end}')) % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank').html_safe, link_start: link_start, link_end: ''.html_safe }
.settings-content
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-snowplow-settings'), html: { class: 'fieldset-form', id: 'snowplow-settings' } do |f|
= form_errors(@application_setting) if expanded
@@ -15,15 +16,21 @@
.form-group
.form-check
= f.check_box :snowplow_enabled, class: 'form-check-input'
- = f.label :snowplow_enabled, _('Enable snowplow tracking'), class: 'form-check-label'
+ = f.label :snowplow_enabled, _('Enable Snowplow tracking'), class: 'form-check-label'
.form-group
= f.label :snowplow_collector_hostname, _('Collector hostname'), class: 'label-light'
= f.text_field :snowplow_collector_hostname, class: 'form-control gl-form-input', placeholder: 'snowplow.example.com'
+ .form-text.text-muted
+ = _('The hostname of your Snowplow collector.')
.form-group
= f.label :snowplow_app_id, _('App ID'), class: 'label-light'
- = f.text_field :snowplow_app_id, class: 'form-control gl-form-input'
+ = f.text_field :snowplow_app_id, class: 'form-control gl-form-input', placeholder: 'gitlab'
+ .form-text.text-muted
+ = _('The ID of the application.')
.form-group
= f.label :snowplow_cookie_domain, _('Cookie domain'), class: 'label-light'
- = f.text_field :snowplow_cookie_domain, class: 'form-control gl-form-input'
+ = f.text_field :snowplow_cookie_domain, class: 'form-control gl-form-input', placeholder: '.your-gitlab-instance.com'
+ .form-text.text-muted
+ = _('The Snowplow cookie domain.')
= f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
diff --git a/app/views/admin/application_settings/_third_party_offers.html.haml b/app/views/admin/application_settings/_third_party_offers.html.haml
index 5df2454ed2..9a34400092 100644
--- a/app/views/admin/application_settings/_third_party_offers.html.haml
+++ b/app/views/admin/application_settings/_third_party_offers.html.haml
@@ -2,11 +2,11 @@
%section.settings.as-third-party-offers.no-animate#js-third-party-offers-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
- = _('Third party offers')
+ = _('Third-party offers')
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _('Control the display of third party offers.')
+ = _('Control whether to display third-party offers in GitLab.')
.settings-content
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-third-party-offers-settings'), html: { class: 'fieldset-form', id: 'third-party-offers-settings' } do |f|
= form_errors(@application_setting) if expanded
@@ -15,6 +15,6 @@
.form-group
.form-check
= f.check_box :hide_third_party_offers, class: 'form-check-input'
- = f.label :hide_third_party_offers, _('Do not display offers from third parties within GitLab'), class: 'form-check-label'
+ = f.label :hide_third_party_offers, _('Do not display offers from third parties'), class: 'form-check-label'
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_usage.html.haml b/app/views/admin/application_settings/_usage.html.haml
index 64e8751bf3..ddd0abb4c3 100644
--- a/app/views/admin/application_settings/_usage.html.haml
+++ b/app/views/admin/application_settings/_usage.html.haml
@@ -1,4 +1,4 @@
-- payload_class = 'js-usage-ping-payload'
+- payload_class = 'js-service-ping-payload'
= form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
@@ -17,23 +17,44 @@
.form-check
= f.check_box :usage_ping_enabled, disabled: !can_be_configured, class: 'form-check-input'
= f.label :usage_ping_enabled, class: 'form-check-label' do
- = _('Enable usage ping')
+ = _('Enable service ping')
.form-text.text-muted
- if can_be_configured
%p.mb-2= _('To help improve GitLab and its user experience, GitLab will periodically collect usage information.')
- - usage_ping_path = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'usage-ping')
- - usage_ping_link_start = ''.html_safe % { url: usage_ping_path }
- %p.mb-2= s_('%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc.').html_safe % { usage_ping_link_start: usage_ping_link_start, usage_ping_link_end: ''.html_safe }
+ - service_ping_path = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'service-ping')
+ - service_ping_link_start = ''.html_safe % { url: service_ping_path }
+ %p.mb-2= s_('%{service_ping_link_start}Learn more%{service_ping_link_end} about what information is shared with GitLab Inc.').html_safe % { service_ping_link_start: service_ping_link_start, service_ping_link_end: ''.html_safe }
%button.gl-button.btn.btn-default.js-payload-preview-trigger{ type: 'button', data: { payload_selector: ".#{payload_class}" } }
.gl-spinner.js-spinner.gl-display-none.gl-mr-2
.js-text.d-inline= _('Preview payload')
- %pre.usage-data.js-syntax-highlight.code.highlight.mt-2.d-none{ class: payload_class, data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
+ %pre.service-data-payload-container.js-syntax-highlight.code.highlight.mt-2.d-none{ class: payload_class, data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
- else
- = _('The usage ping is disabled, and cannot be configured through this form.')
- - deactivating_usage_ping_path = help_page_path('development/usage_ping/index.md', anchor: 'disable-usage-ping')
- - deactivating_usage_ping_link_start = ''.html_safe % { url: deactivating_usage_ping_path }
- = s_('For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}.').html_safe % { deactivating_usage_ping_link_start: deactivating_usage_ping_link_start, deactivating_usage_ping_link_end: ''.html_safe }
+ = _('Service ping is disabled in your configuration file, and cannot be enabled through this form.')
+ - deactivating_service_ping_path = help_page_path('development/service_ping/index.md', anchor: 'disable-service-ping-using-the-configuration-file')
+ - deactivating_service_ping_link_start = ''.html_safe % { url: deactivating_service_ping_path }
+ = s_('For more information, see the documentation on %{deactivating_service_ping_link_start}deactivating service ping%{deactivating_service_ping_link_end}.').html_safe % { deactivating_service_ping_link_start: deactivating_service_ping_link_start, deactivating_service_ping_link_end: ''.html_safe }
+ .form-group
+ - usage_ping_enabled = @application_setting.usage_ping_enabled?
+ .form-check
+ = f.check_box :usage_ping_features_enabled?, disabled: !usage_ping_enabled, class: 'form-check-input'
+ = f.label :usage_ping_features_enabled?, class: 'form-check-label gl-cursor-not-allowed', id: 'service_ping_features_label' do
+ = _('Enable Registration Features')
+ = link_to sprite_icon('question-o'), help_page_path('development/service_ping/index.md', anchor: 'registration-features-program')
+ .form-text.text-muted
+ - if usage_ping_enabled
+ %p.gl-mb-3.text-muted{ id: 'service_ping_features_helper_text' }= _('You can enable Registration Features because Service Ping is enabled. To continue using Registration Features in the future, you will also need to register with GitLab via a new cloud licensing service.')
+ - else
+ %p.gl-mb-3.text-muted{ id: 'service_ping_features_helper_text' }= _('To enable Registration Features, make sure "Enable service ping" is checked.')
+
+ %p.gl-mb-3.text-muted= _('Registration Features include:')
+ .form-text
+ - email_from_gitlab_path = help_page_path('tools/email.md')
+ - link_end = ''.html_safe
+ - email_from_gitlab_link = ''.html_safe % { url: email_from_gitlab_path }
+ %ul
+ %li
+ = _('Email from GitLab - email users right from the Admin Area. %{link_start}Learn more%{link_end}.').html_safe % { link_start: email_from_gitlab_link, link_end: link_end }
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/ci/_header.html.haml b/app/views/admin/application_settings/ci/_header.html.haml
index 40486e9a9e..1298be9a6c 100644
--- a/app/views/admin/application_settings/ci/_header.html.haml
+++ b/app/views/admin/application_settings/ci/_header.html.haml
@@ -8,7 +8,7 @@
%p
= _('Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables.')
- = link_to s_('Learn more.'), help_page_path('ci/variables/README', anchor: 'add-a-cicd-variable-to-an-instance'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to s_('Learn more.'), help_page_path('ci/variables/index', anchor: 'add-a-cicd-variable-to-an-instance'), target: '_blank', rel: 'noopener noreferrer'
%p
= _('Variables can be:')
%ul
@@ -16,4 +16,4 @@
= html_escape(_('%{code_open}Protected:%{code_close} Only exposed to protected branches or tags.')) % { code_open: ''.html_safe, code_close: '
'.html_safe }
%li
= html_escape(_('%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements.')) % { code_open: ''.html_safe, code_close: '
'.html_safe }
- = link_to _('Learn more.'), help_page_path('ci/variables/README', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
diff --git a/app/views/admin/application_settings/ci_cd.html.haml b/app/views/admin/application_settings/ci_cd.html.haml
index 127ab8ea1f..18ec43407c 100644
--- a/app/views/admin/application_settings/ci_cd.html.haml
+++ b/app/views/admin/application_settings/ci_cd.html.haml
@@ -8,7 +8,7 @@
.settings-content
- if ci_variable_protected_by_default?
%p.settings-message.text-center
- - link_start = ''.html_safe % { url: help_page_path('ci/variables/README', anchor: 'protect-a-cicd-variable') }
+ - link_start = ''.html_safe % { url: help_page_path('ci/variables/index', anchor: 'protect-a-cicd-variable') }
= s_('Environment variables on this GitLab instance are configured to be %{link_start}protected%{link_end} by default.').html_safe % { link_start: link_start, link_end: ''.html_safe }
#js-instance-variables{ data: { endpoint: admin_ci_variables_path, group: 'true', maskable_regex: ci_variable_maskable_regex, protected_by_default: ci_variable_protected_by_default?.to_s} }
@@ -38,3 +38,13 @@
= _('Various container registry settings.')
.settings-content
= render 'registry'
+
+- if Feature.enabled?(:runner_registration_control)
+ %section.settings.as-runner.no-animate#js-runner-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = s_('Runners|Runner registration')
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? 'Collapse' : 'Expand'
+ .settings-content
+ = render 'runner_registrars_form'
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml
index 0fbbef0261..53bdbcd713 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/general.html.haml
@@ -107,6 +107,7 @@
= render_if_exists 'admin/application_settings/maintenance_mode_settings_form'
= render 'admin/application_settings/gitpod'
= render 'admin/application_settings/kroki'
+= render 'admin/application_settings/mailgun'
= render 'admin/application_settings/plantuml'
= render 'admin/application_settings/sourcegraph'
= render_if_exists 'admin/application_settings/slack'
diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml
index 7a81d53c08..d818c587b7 100644
--- a/app/views/admin/application_settings/integrations.html.haml
+++ b/app/views/admin/application_settings/integrations.html.haml
@@ -1,9 +1,9 @@
-- breadcrumb_title _('Integrations')
-- page_title _('Integrations')
+- breadcrumb_title s_('Integrations|Instance-level integration management')
+- page_title s_('Integrations|Instance-level integration management')
- @content_class = 'limit-container-width' unless fluid_layout
-%h3= s_('Integrations|Project integration management')
+%h3= s_('Integrations|Instance-level integration management')
- integrations_link_start = ''.html_safe % { url: integrations_help_page_path }
-%p= s_("Integrations|GitLab administrators can set up integrations that all projects inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a group or project if the settings are necessary at that level. Learn more about %{integrations_link_start}project integration management%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, link_end: "".html_safe }
+%p= s_("Integrations|GitLab administrators can set up integrations that all groups and projects inherit and use by default. These integrations apply to all groups and projects that don't already use custom settings. You can override custom settings for a group or project if the settings are necessary at that level. Learn more about %{integrations_link_start}instance-level integration management%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, link_end: "".html_safe }
= render 'shared/integrations/index', integrations: @integrations
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
index 113ff20e91..14483e4e55 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -47,10 +47,8 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Enable or disable version check and usage ping.')
+ = _('Enable or disable version check and service ping.')
.settings-content
= render 'usage'
-= render_if_exists 'admin/application_settings/seat_link_setting', expanded: expanded_by_default?
-
= render_if_exists 'admin/application_settings/pseudonymizer_settings', expanded: expanded_by_default?
diff --git a/app/views/admin/application_settings/preferences.html.haml b/app/views/admin/application_settings/preferences.html.haml
index 17bf9ba84a..0dfc3d7a60 100644
--- a/app/views/admin/application_settings/preferences.html.haml
+++ b/app/views/admin/application_settings/preferences.html.haml
@@ -20,18 +20,19 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _("Configure What's new drawer and content.")
+ = _("Configure %{italic_start}What's new%{italic_end} drawer and content.").html_safe % { italic_start: ''.html_safe, italic_end: ''.html_safe }
.settings-content
= render 'whats_new'
%section.settings.as-help-page.no-animate#js-help-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
- = _('Help page')
+ = _('Sign-in and Help page')
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Help page text and support page url.')
+ = _('Additional text for the sign-in and Help page.')
+ = link_to s_('Learn more.'), help_page_path('user/admin_area/settings/help_page.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'help_page'
@@ -60,11 +61,13 @@
%section.settings.as-gitaly.no-animate#js-gitaly-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
- = _('Gitaly')
+ = _('Gitaly timeouts')
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Configure Gitaly timeouts.')
+ %span
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/gitaly_timeouts.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'gitaly'
@@ -75,6 +78,6 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Various localization settings.')
+ = _('Configure the default first day of the week and time tracking units.')
.settings-content
= render 'localization'
diff --git a/app/views/admin/application_settings/repository.html.haml b/app/views/admin/application_settings/repository.html.haml
index 111cc9c5d7..2a9fba1aef 100644
--- a/app/views/admin/application_settings/repository.html.haml
+++ b/app/views/admin/application_settings/repository.html.haml
@@ -9,7 +9,7 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Set the default name of the initial branch when creating new repositories through the user interface.')
+ = s_('AdminSettings|The default name for the initial branch of new repositories created in the instance.')
.settings-content
= render 'initial_branch_name'
@@ -21,6 +21,7 @@
= expanded_by_default? ? 'Collapse' : 'Expand'
%p
= _('Configure repository mirroring.')
+ = link_to s_('Learn more.'), help_page_path('user/project/repository/repository_mirroring.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render partial: 'repository_mirrors_form'
@@ -31,7 +32,8 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Configure storage path settings.')
+ = _('Configure repository storage.')
+ = link_to s_('Learn more.'), help_page_path('administration/repository_storage_paths.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'repository_storage'
@@ -42,7 +44,11 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Configure automatic git checks and housekeeping on repositories.')
+ - repository_checks_link_url = help_page_path('administration/repository_checks.md')
+ - repository_checks_link_start = ''.html_safe % { url: repository_checks_link_url }
+ - housekeeping_link_url = help_page_path('administration/housekeeping.md')
+ - housekeeping_link_start = ''.html_safe % { url: housekeeping_link_url }
+ = html_escape(s_('Configure %{repository_checks_link_start}repository checks%{link_end} and %{housekeeping_link_start}housekeeping%{link_end} on repositories.')) % { repository_checks_link_start: repository_checks_link_start, housekeeping_link_start: housekeeping_link_start, link_end: ''.html_safe }
.settings-content
= render 'repository_check'
diff --git a/app/views/admin/background_migrations/_migration.html.haml b/app/views/admin/background_migrations/_migration.html.haml
index 40860ea940..ddb2eb2770 100644
--- a/app/views/admin/background_migrations/_migration.html.haml
+++ b/app/views/admin/background_migrations/_migration.html.haml
@@ -8,3 +8,12 @@
= _('Unknown')
%td{ role: 'cell', data: { label: _('Status') } }
%span.badge.badge-pill.gl-badge.sm{ class: batched_migration_status_badge_class_name(migration) }= migration.status.humanize
+ %td{ role: 'cell', data: { label: _('Action') } }
+ - if migration.active?
+ = button_to pause_admin_background_migration_path(migration),
+ class: 'gl-button btn btn-icon has-tooltip', title: _('Pause'), 'aria-label' => _('Pause') do
+ = sprite_icon('pause', css_class: 'gl-button-icon gl-icon')
+ - elsif migration.paused?
+ = button_to resume_admin_background_migration_path(migration),
+ class: 'gl-button btn btn-icon has-tooltip', title: _('Resume'), 'aria-label' => _('Resume') do
+ = sprite_icon('play', css_class: 'gl-button-icon gl-icon')
diff --git a/app/views/admin/background_migrations/index.html.haml b/app/views/admin/background_migrations/index.html.haml
index 2a372c8991..9ccbdfb5f2 100644
--- a/app/views/admin/background_migrations/index.html.haml
+++ b/app/views/admin/background_migrations/index.html.haml
@@ -29,6 +29,7 @@
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Migration')
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Progress')
%th.table-th-transparent.border-bottom{ role: 'cell' }= _('Status')
+ %th.table-th-transparent.border-bottom{ role: 'cell' }
%tbody{ role: 'rowgroup' }
= render partial: 'migration', collection: @migrations
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 58c65bdc8c..ec3daf6c49 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -3,15 +3,14 @@
- billable_users_url = help_page_path('subscriptions/self_managed/index', anchor: 'billable-users')
- billable_users_link_start = ''.html_safe % { url: billable_users_url }
+= render_if_exists 'shared/qrtly_reconciliation_alert'
+
- if @notices
- @notices.each do |notice|
.js-vue-alert{ 'v-cloak': true, data: { variant: notice[:type],
dismissible: true.to_s } }
= notice[:message].html_safe
-- if Gitlab.ee? && display_upcoming_reconciliation_alert?
- #js-qrtly-reconciliation-alert{ data: upcoming_reconciliation_hash }
-
- if @license.present?
.license-panel.gl-mt-5
= render_if_exists 'admin/licenses/summary'
diff --git a/app/views/admin/dev_ops_report/_callout.html.haml b/app/views/admin/dev_ops_report/_callout.html.haml
index f313865478..2b4c258a00 100644
--- a/app/views/admin/dev_ops_report/_callout.html.haml
+++ b/app/views/admin/dev_ops_report/_callout.html.haml
@@ -8,6 +8,6 @@
%h4
= _('Introducing Your DevOps Report')
%p
- = _('Your DevOps Report gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers.')
+ = _('Your DevOps Report gives an overview of how you are using GitLab from a feature perspective. Use it to view how you compare with other organizations.')
.svg-container.devops
= custom_icon('dev_ops_report_overview')
diff --git a/app/views/admin/dev_ops_report/_report.html.haml b/app/views/admin/dev_ops_report/_report.html.haml
index dbd0020e38..0b26548d6e 100644
--- a/app/views/admin/dev_ops_report/_report.html.haml
+++ b/app/views/admin/dev_ops_report/_report.html.haml
@@ -1,10 +1,9 @@
-- usage_ping_enabled = Gitlab::CurrentSettings.usage_ping_enabled
+- service_ping_enabled = Gitlab::CurrentSettings.usage_ping_enabled
-- if usage_ping_enabled && show_callout?('dev_ops_report_intro_callout_dismissed')
+- if service_ping_enabled && show_callout?('dev_ops_report_intro_callout_dismissed')
= render 'callout'
-- if !usage_ping_enabled
- #js-devops-usage-ping-disabled{ data: { is_admin: current_user&.admin.to_s, empty_state_svg_path: image_path('illustrations/convdev/convdev_no_index.svg'), enable_usage_ping_link: metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), docs_link: help_page_path('development/usage_ping/index.md') } }
+- if !service_ping_enabled
+ #js-devops-service-ping-disabled{ data: { is_admin: current_user&.admin.to_s, empty_state_svg_path: image_path('illustrations/convdev/convdev_no_index.svg'), enable_service_ping_path: metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), docs_link: help_page_path('development/service_ping/index.md') } }
- else
#js-devops-score{ data: { devops_score_metrics: devops_score_metrics(@metric).to_json, devops_report_docs_path: help_page_path('user/admin_area/analytics/dev_ops_report'), no_data_image_path: image_path('dev_ops_report_no_data.svg') } }
-
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 5f8ec5086b..9b42e1b496 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -128,16 +128,12 @@
.card-header
= html_escape(_("%{group_name} group members")) % { group_name: "#{html_escape(@group.name)}".html_safe }
%span.badge.badge-pill= @group.users_count
- .float-right
- = link_to group_group_members_path(@group), class: 'btn btn-default gl-button btn-sm' do
- = sprite_icon('pencil-square', css_class: 'gl-icon')
- = _('Manage access')
+ = render 'shared/members/manage_access_button', path: group_group_members_path(@group)
%ul.content-list.group-users-list.content-list.members-list
= render partial: 'shared/members/member',
collection: @members, as: :member,
locals: { membership_source: @group,
group: @group,
- show_controls: false,
current_user_is_group_owner: current_user_is_group_owner }
- unless @members.size < Kaminari.config.default_per_page
.card-footer
diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml
index a6d562dad3..d85ab47669 100644
--- a/app/views/admin/identities/index.html.haml
+++ b/app/views/admin/identities/index.html.haml
@@ -15,3 +15,5 @@
= render @identities
- else
%h4= _('This user has no identities')
+
+= render partial: 'admin/users/modals'
diff --git a/app/views/admin/impersonation_tokens/index.html.haml b/app/views/admin/impersonation_tokens/index.html.haml
index ec393fdd79..1609687fc8 100644
--- a/app/views/admin/impersonation_tokens/index.html.haml
+++ b/app/views/admin/impersonation_tokens/index.html.haml
@@ -19,7 +19,8 @@
path: admin_user_impersonation_tokens_path,
impersonation: true,
token: @impersonation_token,
- scopes: @scopes
+ scopes: @scopes,
+ help_path: help_page_path('api/index', anchor: 'impersonation-tokens')
= render 'shared/access_tokens/table',
type: type,
@@ -27,3 +28,5 @@
impersonation: true,
active_tokens: @active_impersonation_tokens,
revoke_route_helper: ->(token) { revoke_admin_user_impersonation_token_path(token.user, token) }
+
+= render partial: 'admin/users/modals'
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 40443fb340..5c92cbf957 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -15,8 +15,11 @@
- if @project.last_repository_check_failed?
.row
.col-md-12
- .gl-alert.gl-alert-danger.gl-mb-5{ data: { testid: 'last-repository-check-failed-alert' } }
- = sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ = render 'shared/global_alert',
+ variant: :danger,
+ alert_class: 'gl-mb-5',
+ alert_data: { testid: 'last-repository-check-failed-alert' },
+ is_container: true do
.gl-alert-body
- last_check_message = _("Last repository check (%{last_check_timestamp}) failed. See the 'repocheck.log' file for error messages.")
- last_check_message = last_check_message % { last_check_timestamp: time_ago_with_tooltip(@project.last_repository_check_at) }
@@ -180,37 +183,29 @@
%strong= @group.name
= _('group members')
%span.badge.badge-pill= @group_members.size
- .float-right
- = link_to admin_group_path(@group), class: 'btn btn-default gl-button btn-sm' do
- = sprite_icon('pencil-square', css_class: 'gl-icon')
- = _('Manage access')
+ = render 'shared/members/manage_access_button', path: group_group_members_path(@group)
%ul.content-list.members-list
= render partial: 'shared/members/member',
collection: @group_members, as: :member,
locals: { membership_source: @project,
group: @group,
- show_controls: false,
current_user_is_group_owner: current_user_is_group_owner }
.card-footer
= paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
- = render 'shared/members/requests', membership_source: @project, group: @group, requesters: @requesters, force_mobile_view: true
+ = render 'shared/members/requests', membership_source: @project, group: @group, requesters: @requesters
.card
.card-header
%strong= @project.name
= _('project members')
%span.badge.badge-pill= @project.users.size
- .float-right
- = link_to project_project_members_path(@project), class: 'btn btn-default gl-button btn-sm' do
- = sprite_icon('pencil-square', css_class: 'gl-icon')
- = _('Manage access')
+ = render 'shared/members/manage_access_button', path: project_project_members_path(@project)
%ul.content-list.project_members.members-list
= render partial: 'shared/members/member',
collection: @project_members, as: :member,
locals: { membership_source: @project,
group: @group,
- show_controls: false,
current_user_is_group_owner: current_user_is_group_owner }
.card-footer
= paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml
index da2fcd5a4a..ce143a6b15 100644
--- a/app/views/admin/runners/_runner.html.haml
+++ b/app/views/admin/runners/_runner.html.haml
@@ -6,15 +6,15 @@
.table-mobile-header{ role: 'rowheader' }= _('Type')
.table-mobile-content
- if runner.instance_type?
- %span.badge.badge-pill.gl-badge.sm.badge-success= _("shared")
+ %span.badge.badge-pill.gl-badge.sm.badge-success= s_('Runners|shared')
- elsif runner.group_type?
- %span.badge.badge-pill.gl-badge.sm.badge-success= _("group")
+ %span.badge.badge-pill.gl-badge.sm.badge-success= s_('Runners|group')
- else
- %span.badge.badge-pill.gl-badge.sm.badge-info= _("specific")
+ %span.badge.badge-pill.gl-badge.sm.badge-info= s_('Runners|specific')
- if runner.locked?
- %span.badge.badge-pill.gl-badge.sm.badge-warning= _("locked")
+ %span.badge.badge-pill.gl-badge.sm.badge-warning= s_('Runners|locked')
- unless runner.active?
- %span.badge.badge-pill.gl-badge.sm.badge-danger= _("paused")
+ %span.badge.badge-pill.gl-badge.sm.badge-danger= s_('Runners|paused')
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }= s_('Runners|Runner')
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 07fbc3e539..f9c52d9316 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -17,23 +17,23 @@
%span= _('Runners can be:')
%ul
%li
- %span.badge.badge-pill.gl-badge.sm.badge-success shared
+ %span.badge.badge-pill.gl-badge.sm.badge-success= s_('Runners|shared')
\-
= _('Runs jobs from all unassigned projects.')
%li
- %span.badge.badge-pill.gl-badge.sm.badge-success group
+ %span.badge.badge-pill.gl-badge.sm.badge-success= s_('Runners|group')
\-
= _('Runs jobs from all unassigned projects in its group.')
%li
- %span.badge.badge-pill.gl-badge.sm.badge-info specific
+ %span.badge.badge-pill.gl-badge.sm.badge-info= s_('Runners|specific')
\-
= _('Runs jobs from assigned projects.')
%li
- %span.badge.badge-pill.gl-badge.sm.badge-warning locked
+ %span.badge.badge-pill.gl-badge.sm.badge-warning= s_('Runners|locked')
\-
= _('Cannot be assigned to other projects.')
%li
- %span.badge.badge-pill.gl-badge.sm.badge-danger paused
+ %span.badge.badge-pill.gl-badge.sm.badge-danger= s_('Runners|paused')
\-
= _('Not available to run jobs.')
@@ -41,7 +41,7 @@
.bs-callout
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token,
- type: 'shared',
+ type: s_('Runners|shared'),
reset_token_url: reset_registration_token_admin_application_settings_path,
project_path: '',
group_path: '' }
diff --git a/app/views/admin/users/_approve_user.html.haml b/app/views/admin/users/_approve_user.html.haml
deleted file mode 100644
index f61c9fa4b8..0000000000
--- a/app/views/admin/users/_approve_user.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-.card.border-info
- .card-header.gl-bg-blue-500.gl-text-white
- = s_('AdminUsers|This user has requested access')
- .card-body
- = render partial: 'admin/users/user_approve_effects'
- %br
- = link_to s_('AdminUsers|Approve user'), approve_admin_user_path(user), method: :put, class: "btn gl-button btn-info", data: { confirm: s_('AdminUsers|Are you sure?'), qa_selector: 'approve_user_button' }
diff --git a/app/views/admin/users/_ban_user.html.haml b/app/views/admin/users/_ban_user.html.haml
deleted file mode 100644
index 229c88adb7..0000000000
--- a/app/views/admin/users/_ban_user.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-- if ban_feature_available?
- .card.border-warning
- .card-header.bg-warning.gl-text-white
- = s_('AdminUsers|Ban user')
- .card-body
- = user_ban_effects
- %br
- %button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_ban_data(user) }
- = s_('AdminUsers|Ban user')
diff --git a/app/views/admin/users/_block_user.html.haml b/app/views/admin/users/_block_user.html.haml
deleted file mode 100644
index 2902998634..0000000000
--- a/app/views/admin/users/_block_user.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-.card.border-warning
- .card-header.bg-warning.text-white
- = s_('AdminUsers|Block this user')
- .card-body
- = user_block_effects
- %br
- %button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_block_data(user, s_('AdminUsers|You can always unblock their account, their data will remain intact.')) }
- = s_('AdminUsers|Block user')
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index a7f821a273..f4b1a2853f 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -1,33 +1,38 @@
-%h3.page-title
- = @user.name
- - if @user.blocked_pending_approval?
- %span.cred
- = s_('AdminUsers|(Pending approval)')
- - elsif @user.banned?
- %span.cred
- = s_('AdminUsers|(Banned)')
- - elsif @user.blocked?
- %span.cred
- = s_('AdminUsers|(Blocked)')
- - if @user.internal?
- %span.cred
- = s_('AdminUsers|(Internal)')
- - if @user.admin
- %span.cred
- = s_('AdminUsers|(Admin)')
- - if @user.deactivated?
- %span.cred
- = s_('AdminUsers|(Deactivated)')
- = render_if_exists 'admin/users/auditor_user_badge'
- = render_if_exists 'admin/users/gma_user_badge'
+.gl-display-flex.gl-flex-wrap.gl-justify-content-space-between.gl-align-items-center.gl-py-3.gl-mb-5.gl-border-b-solid.gl-border-gray-100.gl-border-b-1
+ .gl-my-3
+ %h3.page-title.gl-m-0
+ = @user.name
+ - if @user.blocked_pending_approval?
+ %span.cred
+ = s_('AdminUsers|(Pending approval)')
+ - elsif @user.banned?
+ %span.cred
+ = s_('AdminUsers|(Banned)')
+ - elsif @user.blocked?
+ %span.cred
+ = s_('AdminUsers|(Blocked)')
+ - if @user.internal?
+ %span.cred
+ = s_('AdminUsers|(Internal)')
+ - if @user.admin
+ %span.cred
+ = s_('AdminUsers|(Admin)')
+ - if @user.deactivated?
+ %span.cred
+ = s_('AdminUsers|(Deactivated)')
+ = render_if_exists 'admin/users/auditor_user_badge'
+ = render_if_exists 'admin/users/gma_user_badge'
- .float-right
- - if impersonation_enabled? && @user != current_user && @user.can?(:log_in)
- = link_to _('Impersonate'), impersonate_admin_user_path(@user), method: :post, class: "btn btn-info gl-button btn-grouped", data: { qa_selector: 'impersonate_user_link' }
- = link_to edit_admin_user_path(@user), class: "btn btn-default gl-button btn-grouped" do
- = sprite_icon('pencil-square', css_class: 'gl-icon gl-button-icon')
- = _('Edit')
-%hr
+ .gl-my-3.gl-display-flex.gl-flex-wrap.gl-my-n2.gl-mx-n2
+ .gl-p-2
+ #js-admin-user-actions{ data: admin_user_actions_data_attributes(@user) }
+ - if @user != current_user
+ .gl-p-2
+ - if impersonation_enabled? && @user.can?(:log_in)
+ = link_to _('Impersonate'), impersonate_admin_user_path(@user), method: :post, class: "btn btn-default gl-button", data: { qa_selector: 'impersonate_user_link' }
+ - if can_force_email_confirmation?(@user)
+ %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: confirm_user_data(@user) }
+ = _('Confirm user')
%ul.nav-links.nav.nav-tabs
= nav_link(path: 'users#show') do
= link_to _("Account"), admin_user_path(@user)
diff --git a/app/views/admin/users/_reject_pending_user.html.haml b/app/views/admin/users/_reject_pending_user.html.haml
deleted file mode 100644
index 1710842733..0000000000
--- a/app/views/admin/users/_reject_pending_user.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-.card.border-danger
- .card-header.bg-danger.gl-text-white
- = s_('AdminUsers|This user has requested access')
- .card-body
- = render partial: 'admin/users/user_reject_effects'
- %br
- = link_to s_('AdminUsers|Reject request'), reject_admin_user_path(user), method: :delete, class: "btn gl-button btn-danger", data: { confirm: s_('AdminUsers|Are you sure?') }
diff --git a/app/views/admin/users/_user_activation_effects.html.haml b/app/views/admin/users/_user_activation_effects.html.haml
deleted file mode 100644
index 244836dac1..0000000000
--- a/app/views/admin/users/_user_activation_effects.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-%p
- = s_('AdminUsers|Reactivating a user will:')
-%ul
- %li
- = s_('AdminUsers|Restore user access to the account, including web, Git and API.')
- = render_if_exists 'admin/users/user_activation_effects_on_seats'
diff --git a/app/views/admin/users/_user_approve_effects.html.haml b/app/views/admin/users/_user_approve_effects.html.haml
deleted file mode 100644
index 54e51bf346..0000000000
--- a/app/views/admin/users/_user_approve_effects.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-%p
- = s_('AdminUsers|Approved users can:')
-%ul
- %li
- = s_('AdminUsers|Log in')
- %li
- = s_('AdminUsers|Access Git repositories')
- %li
- = s_('AdminUsers|Access the API')
- %li
- = s_('AdminUsers|Be added to groups and projects')
diff --git a/app/views/admin/users/_user_detail_note.html.haml b/app/views/admin/users/_user_detail_note.html.haml
index 4f2a682c5c..cc4827327c 100644
--- a/app/views/admin/users/_user_detail_note.html.haml
+++ b/app/views/admin/users/_user_detail_note.html.haml
@@ -1,7 +1,7 @@
- if @user.note.present?
- text = @user.note
- .card.border-info
- .card-header.bg-info.text-white
+ .card
+ .card-header
= _('Admin Note')
.card-body
%p= text
diff --git a/app/views/admin/users/_user_reject_effects.html.haml b/app/views/admin/users/_user_reject_effects.html.haml
deleted file mode 100644
index 17b6862b0c..0000000000
--- a/app/views/admin/users/_user_reject_effects.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-%p
- = s_('AdminUsers|Rejected users:')
-%ul
- %li
- = s_('AdminUsers|Cannot sign in or access instance information')
- %li
- = s_('AdminUsers|Will be deleted')
-%p
- - link_start = ''.html_safe % { url: help_page_path("user/profile/account/delete_account", anchor: "associated-records") }
- = s_('AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}').html_safe % { link_start: link_start, link_end: ''.html_safe }
diff --git a/app/views/admin/users/keys.html.haml b/app/views/admin/users/keys.html.haml
index 5f9d11af7c..28024ae084 100644
--- a/app/views/admin/users/keys.html.haml
+++ b/app/views/admin/users/keys.html.haml
@@ -3,3 +3,4 @@
- page_title _("SSH Keys"), @user.name, _("Users")
= render 'admin/users/head'
= render 'profiles/keys/key_table', admin: true
+= render partial: 'admin/users/modals'
diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml
index 3ff726e194..8c56e888dc 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -48,3 +48,5 @@
- if member.respond_to? :project
= link_to project_project_member_path(project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn btn-sm btn-danger gl-button btn-icon gl-ml-3", title: _('Remove user from project') do
= sprite_icon('close', size: 16, css_class: 'gl-icon')
+
+= render partial: 'admin/users/modals'
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 08c1e089f2..ad8d9d1f04 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -16,8 +16,10 @@
%strong
= link_to user_path(@user) do
= @user.username
- = render 'admin/users/profile', user: @user
-
+ -# Rendered on mobile only so order of cards can be different on desktop vs mobile
+ .gl-md-display-none
+ = render 'admin/users/profile', user: @user
+ = render 'admin/users/user_detail_note'
.card
.card-header
= _('Account:')
@@ -139,123 +141,8 @@
= render 'shared/custom_attributes', custom_attributes: @user.custom_attributes
- .col-md-6
- - unless @user == current_user
- - if can_force_email_confirmation?(@user)
- .gl-card.border-info.gl-mb-5
- .gl-card-header.bg-info.text-white
- = _('Confirm user')
- .gl-card-body
- - if @user.unconfirmed_email.present?
- - email = " (#{@user.unconfirmed_email})"
- %p= _('This user has an unconfirmed email address %{email}. You may force a confirmation.') % { email: email }
- %br
- = link_to _('Confirm user'), confirm_admin_user_path(@user), method: :put, class: "btn gl-button btn-info", data: { confirm: _('Are you sure?'), qa_selector: 'confirm_user_button' }
-
- = render 'admin/users/user_detail_note'
-
- - unless @user.internal?
- - if @user.deactivated?
- .gl-card.border-info.gl-mb-5
- .gl-card-header.bg-info.text-white
- = _('Reactivate this user')
- .gl-card-body
- = render partial: 'admin/users/user_activation_effects'
- %br
- %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_activation_data(@user) }
- = s_('AdminUsers|Activate user')
- - elsif @user.can_be_deactivated?
- .gl-card.border-warning.gl-mb-5
- .gl-card-header.bg-warning.text-white
- = _('Deactivate this user')
- .gl-card-body
- = user_deactivation_effects
- %br
- %button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_deactivation_data(@user, s_('AdminUsers|You can always re-activate their account, their data will remain intact.')) }
- = s_('AdminUsers|Deactivate user')
- - if @user.blocked?
- - if @user.blocked_pending_approval?
- = render 'admin/users/approve_user', user: @user
- = render 'admin/users/reject_pending_user', user: @user
- - elsif @user.banned?
- .gl-card.border-info.gl-mb-5
- .gl-card-header.gl-bg-blue-500.gl-text-white
- = _('This user is banned')
- .gl-card-body
- %p= _('A banned user cannot:')
- %ul
- %li= _('Log in')
- %li= _('Access Git repositories')
- - link_start = ''.html_safe % { url: help_page_path("user/admin_area/moderate_users", anchor: "ban-a-user") }
- = s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: ''.html_safe }
- %p
- %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_unban_data(@user) }
- = s_('AdminUsers|Unban user')
- - else
- .gl-card.border-info.gl-mb-5
- .gl-card-header.gl-bg-blue-500.gl-text-white
- = _('This user is blocked')
- .gl-card-body
- %p= _('A blocked user cannot:')
- %ul
- %li= _('Log in')
- %li= _('Access Git repositories')
- %br
- %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_unblock_data(@user) }
- = s_('AdminUsers|Unblock user')
- - elsif !@user.internal?
- = render 'admin/users/block_user', user: @user
- = render 'admin/users/ban_user', user: @user
-
- - if @user.access_locked?
- .card.border-info.gl-mb-5
- .card-header.bg-info.text-white
- = _('This account has been locked')
- .card-body
- %p= _('This user has been temporarily locked due to excessive number of failed logins. You may manually unlock the account.')
- %br
- = link_to _('Unlock user'), unlock_admin_user_path(@user), method: :put, class: "btn gl-button btn-info", data: { confirm: _('Are you sure?') }
- - if !@user.blocked_pending_approval?
- .gl-card.border-danger.gl-mb-5
- .gl-card-header.bg-danger.text-white
- = s_('AdminUsers|Delete user')
- .gl-card-body
- - if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
- %p= _('Deleting a user has the following effects:')
- = render 'users/deletion_guidance', user: @user
- %br
- %button.js-delete-user-modal-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete',
- delete_user_url: admin_user_path(@user),
- block_user_url: block_admin_user_path(@user),
- username: sanitize_name(@user.name) } }
- = s_('AdminUsers|Delete user')
- - else
- - if @user.solo_owned_groups.present?
- %p
- = _('This user is currently an owner in these groups:')
- %strong= @user.solo_owned_groups.map(&:name).join(', ')
- %p
- = _('You must transfer ownership or delete these groups before you can delete this user.')
- - else
- %p
- = _("You don't have access to delete this user.")
-
- .gl-card.border-danger
- .gl-card-header.bg-danger.text-white
- = s_('AdminUsers|Delete user and contributions')
- .gl-card-body
- - if can?(current_user, :destroy_user, @user)
- %p
- - link_to_ghost_user = link_to(_("system ghost user"), help_page_path("user/profile/account/delete_account"))
- = _("This option deletes the user and any contributions that would usually be moved to the %{link_to_ghost_user}. As well as the user's personal projects, groups owned solely by the user, and projects in them, will also be removed. Commits to other projects are unaffected.").html_safe % { link_to_ghost_user: link_to_ghost_user }
- %br
- %button.js-delete-user-modal-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete-with-contributions',
- delete_user_url: admin_user_path(@user, hard_delete: true),
- block_user_url: block_admin_user_path(@user),
- username: @user.name } }
- = s_('AdminUsers|Delete user and contributions')
- - else
- %p
- = _("You don't have access to delete this user.")
-
+ -# Rendered on desktop only so order of cards can be different on desktop vs mobile
+ .col-md-6.gl-display-none.gl-md-display-block
+ = render 'admin/users/profile', user: @user
+ = render 'admin/users/user_detail_note'
= render partial: 'admin/users/modals'
diff --git a/app/views/ci/token_access/_index.html.haml b/app/views/ci/token_access/_index.html.haml
new file mode 100644
index 0000000000..e6f21fc4ea
--- /dev/null
+++ b/app/views/ci/token_access/_index.html.haml
@@ -0,0 +1 @@
+#js-ci-token-access-app{ data: { full_path: @project.full_path } }
diff --git a/app/views/ci/variables/_content.html.haml b/app/views/ci/variables/_content.html.haml
index 5eded970bf..8a2a479486 100644
--- a/app/views/ci/variables/_content.html.haml
+++ b/app/views/ci/variables/_content.html.haml
@@ -1,5 +1,5 @@
= _('Variables store information, like passwords and secret keys, that you can use in job scripts.')
-= link_to s_('Learn more.'), help_page_path('ci/variables/README'), target: '_blank', rel: 'noopener noreferrer'
+= link_to s_('Learn more.'), help_page_path('ci/variables/index'), target: '_blank', rel: 'noopener noreferrer'
%p
= _('Variables can be:')
%ul
@@ -7,4 +7,4 @@
= html_escape(_('%{code_open}Protected:%{code_close} Only exposed to protected branches or tags.')) % { code_open: ''.html_safe, code_close: '
'.html_safe }
%li
= html_escape(_('%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements.')) % { code_open: ''.html_safe, code_close: '
'.html_safe }
- = link_to _('Learn more.'), help_page_path('ci/variables/README', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml
index f5d28adfa6..9db5ee23c3 100644
--- a/app/views/ci/variables/_index.html.haml
+++ b/app/views/ci/variables/_index.html.haml
@@ -2,7 +2,7 @@
- if ci_variable_protected_by_default?
%p.settings-message.text-center
- - link_start = ''.html_safe % { url: help_page_path('ci/variables/README', anchor: 'protect-a-cicd-variable') }
+ - link_start = ''.html_safe % { url: help_page_path('ci/variables/index', anchor: 'protect-a-cicd-variable') }
= s_('Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default.').html_safe % { link_start: link_start, link_end: ''.html_safe }
- is_group = !@group.nil?
@@ -16,8 +16,8 @@
aws_tip_deploy_link: help_page_path('ci/cloud_deployment/index.md', anchor: 'deploy-your-application-to-the-aws-elastic-container-service-ecs'),
aws_tip_commands_link: help_page_path('ci/cloud_deployment/index.md', anchor: 'run-aws-commands-from-gitlab-cicd'),
aws_tip_learn_link: help_page_path('ci/cloud_deployment/index.md', anchor: 'aws'),
- protected_environment_variables_link: help_page_path('ci/variables/README', anchor: 'protect-a-cicd-variable'),
- masked_environment_variables_link: help_page_path('ci/variables/README', anchor: 'mask-a-cicd-variable'),
+ protected_environment_variables_link: help_page_path('ci/variables/index', anchor: 'protect-a-cicd-variable'),
+ masked_environment_variables_link: help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'),
} }
- if !@group && @project.group
diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml
index 856d03ba25..3a7f7a241a 100644
--- a/app/views/ci/variables/_variable_row.html.haml
+++ b/app/views/ci/variables/_variable_row.html.haml
@@ -39,7 +39,7 @@
= value
%p.masking-validation-error.gl-field-error.hide
= s_("CiVariables|Cannot use Masked Variable with current value")
- = link_to sprite_icon('question-o'), help_page_path('ci/variables/README', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to sprite_icon('question-o'), help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
- unless only_key_value
.ci-variable-body-item.ci-variable-protected-item.table-section.section-20.mr-0.border-top-0
.gl-mr-3
diff --git a/app/views/clusters/clusters/_applications.html.haml b/app/views/clusters/clusters/_applications.html.haml
deleted file mode 100644
index f83a414a0a..0000000000
--- a/app/views/clusters/clusters/_applications.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-.cluster-applications-table#js-cluster-applications
diff --git a/app/views/clusters/clusters/_applications_tab.html.haml b/app/views/clusters/clusters/_applications_tab.html.haml
deleted file mode 100644
index e1455b0f60..0000000000
--- a/app/views/clusters/clusters/_applications_tab.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- active = params[:tab] == 'apps'
-
-%li.nav-item{ role: 'presentation' }
- %a#cluster-apps-tab.nav-link.qa-applications{ class: active_when(active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'apps'}) }
- %span= _('Applications')
diff --git a/app/views/clusters/clusters/_banner.html.haml b/app/views/clusters/clusters/_banner.html.haml
index 6d902132c7..1ca4f9c670 100644
--- a/app/views/clusters/clusters/_banner.html.haml
+++ b/app/views/clusters/clusters/_banner.html.haml
@@ -6,17 +6,19 @@
%span.gl-spinner.gl-spinner-dark{ 'aria-label': 'Loading' }
%span.gl-ml-2= s_('ClusterIntegration|Kubernetes cluster is being created...')
-.hidden.row.js-cluster-api-unreachable.gl-alert.gl-alert-warning{ role: 'alert' }
- = sprite_icon('warning', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
- = sprite_icon('close', css_class: 'gl-icon')
+= render 'shared/global_alert',
+ variant: :warning,
+ alert_class: 'hidden js-cluster-api-unreachable',
+ is_contained: true,
+ close_button_class: 'js-close' do
.gl-alert-body
= s_('ClusterIntegration|Your cluster API is unreachable. Please ensure your API URL is correct.')
-.hidden.js-cluster-authentication-failure.js-cluster-api-unreachable.gl-alert.gl-alert-warning{ role: 'alert' }
- = sprite_icon('warning', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
- = sprite_icon('close', css_class: 'gl-icon')
+= render 'shared/global_alert',
+ variant: :warning,
+ alert_class: 'hidden js-cluster-authentication-failure js-cluster-api-unreachable',
+ is_contained: true,
+ close_button_class: 'js-close' do
.gl-alert-body
= s_('ClusterIntegration|There was a problem authenticating with your cluster. Please ensure your CA Certificate and Token are valid.')
diff --git a/app/views/clusters/clusters/_multiple_clusters_message.html.haml b/app/views/clusters/clusters/_multiple_clusters_message.html.haml
index da3e128ba3..f235435d90 100644
--- a/app/views/clusters/clusters/_multiple_clusters_message.html.haml
+++ b/app/views/clusters/clusters/_multiple_clusters_message.html.haml
@@ -1,4 +1,4 @@
-- autodevops_help_url = help_page_path('topics/autodevops/index.md', anchor: 'using-multiple-kubernetes-clusters')
+- autodevops_help_url = help_page_path('topics/autodevops/index.md', anchor: 'use-multiple-kubernetes-clusters')
- help_link_start = ''.html_safe
- help_link_end = ''.html_safe
diff --git a/app/views/clusters/clusters/_provider_details_form.html.haml b/app/views/clusters/clusters/_provider_details_form.html.haml
index a936cdc04d..fe3d199823 100644
--- a/app/views/clusters/clusters/_provider_details_form.html.haml
+++ b/app/views/clusters/clusters/_provider_details_form.html.haml
@@ -43,13 +43,13 @@
label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Allow GitLab to manage namespaces and service accounts for this cluster.')
- = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank'
+ = link_to _('More information'), help_page_path('user/project/clusters/gitlab_managed_clusters.md'), target: '_blank'
.form-group
= field.check_box :namespace_per_environment, { label: s_('ClusterIntegration|Namespace per environment'), label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared.')
- = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'custom-namespace'), target: '_blank'
+ = link_to _('More information'), help_page_path('user/project/clusters/deploy_to_cluster.md', anchor: 'custom-namespace'), target: '_blank'
- if cluster.allow_user_defined_namespace?
= render('clusters/clusters/namespace', platform_field: platform_field, field: field)
diff --git a/app/views/clusters/clusters/aws/_new.html.haml b/app/views/clusters/clusters/aws/_new.html.haml
index 93e8b1241a..93db7db06b 100644
--- a/app/views/clusters/clusters/aws/_new.html.haml
+++ b/app/views/clusters/clusters/aws/_new.html.haml
@@ -3,8 +3,8 @@
anchor: 'additional-requirements-for-self-managed-instances') }
= s_('Amazon authentication is not %{link_start}correctly configured%{link_end}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_start: documentation_link_start, link_end: ''.html_safe }
- else
- .js-create-eks-cluster-form-container{ data: { 'gitlab-managed-cluster-help-path' => help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'),
- 'namespace-per-environment-help-path' => help_page_path('user/project/clusters/index.md', anchor: 'custom-namespace'),
+ .js-create-eks-cluster-form-container{ data: { 'gitlab-managed-cluster-help-path' => help_page_path('user/project/clusters/gitlab_managed_clusters.md'),
+ 'namespace-per-environment-help-path' => help_page_path('user/project/clusters/deploy_to_cluster.md', anchor: 'custom-namespace'),
'create-role-path' => clusterable.authorize_aws_role_path,
'create-cluster-path' => clusterable.create_aws_clusters_path,
'account-id' => Gitlab::CurrentSettings.eks_account_id,
@@ -12,6 +12,6 @@
'role-arn' => @aws_role.role_arn,
'instance-types' => @instance_types,
'kubernetes-integration-help-path' => help_page_path('user/project/clusters/index'),
- 'account-and-external-ids-help-path' => help_page_path('user/project/clusters/add_eks_clusters.md', anchor: 'new-eks-cluster'),
- 'create-role-arn-help-path' => help_page_path('user/project/clusters/add_eks_clusters.md', anchor: 'new-eks-cluster'),
+ 'account-and-external-ids-help-path' => help_page_path('user/project/clusters/add_eks_clusters.md', anchor: 'create-a-new-certificate-based-eks-cluster'),
+ 'create-role-arn-help-path' => help_page_path('user/project/clusters/add_eks_clusters.md', anchor: 'create-a-new-certificate-based-eks-cluster'),
'external-link-icon' => sprite_icon('external-link') } }
diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml
index 73a09f00fd..5266fad927 100644
--- a/app/views/clusters/clusters/gcp/_form.html.haml
+++ b/app/views/clusters/clusters/gcp/_form.html.haml
@@ -74,13 +74,13 @@
label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Allow GitLab to manage namespaces and service accounts for this cluster.')
- = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank'
+ = link_to _('More information'), help_page_path('user/project/clusters/gitlab_managed_clusters.md', anchor: 'gitlab-managed-clusters'), target: '_blank'
.form-group
= field.check_box :namespace_per_environment, { label: s_('ClusterIntegration|Namespace per environment'), label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared.')
- = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'custom-namespace'), target: '_blank'
+ = link_to _('More information'), help_page_path('user/project/clusters/deploy_to_cluster.md', anchor: 'custom-namespace'), target: '_blank'
.form-group.js-gke-cluster-creation-submit-container
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'),
diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml
index 7336b9fe86..0a482f1eb0 100644
--- a/app/views/clusters/clusters/show.html.haml
+++ b/app/views/clusters/clusters/show.html.haml
@@ -2,21 +2,10 @@
- add_to_breadcrumbs _('Kubernetes Clusters'), clusterable.index_path
- breadcrumb_title @cluster.name
- page_title _('Kubernetes Cluster')
-- manage_prometheus_path = edit_project_service_path(@cluster.project, 'prometheus') if @project
- cluster_environments_path = clusterable.environments_cluster_path(@cluster)
- status_path = clusterable.cluster_status_cluster_path(@cluster.id, format: :json) if can?(current_user, :admin_cluster, @cluster)
.edit-cluster-form.js-edit-cluster-form{ data: { status_path: status_path,
- install_helm_path: clusterable.install_applications_cluster_path(@cluster, :helm),
- install_ingress_path: clusterable.install_applications_cluster_path(@cluster, :ingress),
- install_cert_manager_path: clusterable.install_applications_cluster_path(@cluster, :cert_manager),
- install_crossplane_path: clusterable.install_applications_cluster_path(@cluster, :crossplane),
- install_prometheus_path: clusterable.install_applications_cluster_path(@cluster, :prometheus),
- install_runner_path: clusterable.install_applications_cluster_path(@cluster, :runner),
- install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
- install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
- update_knative_path: clusterable.update_applications_cluster_path(@cluster, :knative),
- install_elastic_stack_path: clusterable.install_applications_cluster_path(@cluster, :elastic_stack),
cluster_environments_path: cluster_environments_path,
toggle_status: @cluster.enabled? ? 'true': 'false',
has_rbac: has_rbac_enabled?(@cluster) ? 'true': 'false',
@@ -24,15 +13,11 @@
cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason,
provider_type: @cluster.provider_type,
- pre_installed_knative: @cluster.knative_pre_installed? ? 'true': 'false',
help_path: help_page_path('user/project/clusters/index.md'),
environments_help_path: help_page_path('ci/environments/index.md', anchor: 'create-a-static-environment'),
clusters_help_path: help_page_path('user/project/clusters/index.md', anchor: 'deploying-to-a-kubernetes-cluster'),
deploy_boards_help_path: help_page_path('user/project/deploy_boards.md', anchor: 'enabling-deploy-boards'),
- cloud_run_help_path: help_page_path('user/project/clusters/add_gke_clusters.md', anchor: 'cloud-run-for-anthos'),
- manage_prometheus_path: manage_prometheus_path,
- cluster_id: @cluster.id,
- cilium_help_path: help_page_path('user/clusters/applications.md', anchor: 'install-cilium-using-gitlab-cicd')} }
+ cluster_id: @cluster.id } }
.js-cluster-application-notice
.flash-container
diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml
index 7d82fe0679..e9b84952c1 100644
--- a/app/views/clusters/clusters/user/_form.html.haml
+++ b/app/views/clusters/clusters/user/_form.html.haml
@@ -47,13 +47,13 @@
label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Allow GitLab to manage namespaces and service accounts for this cluster.')
- = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank'
+ = link_to _('More information'), help_page_path('user/project/clusters/gitlab_managed_clusters.md'), target: '_blank'
.form-group
= field.check_box :namespace_per_environment, { label: s_('ClusterIntegration|Namespace per environment'), label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared.')
- = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'custom-namespace'), target: '_blank'
+ = link_to _('More information'), help_page_path('user/project/clusters/deploy_to_cluster.md', anchor: 'custom-namespace'), target: '_blank'
= field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field|
- if @user_cluster.allow_user_defined_namespace?
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index c24d386c41..4252b60514 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -3,15 +3,6 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
-- if show_customize_homepage_banner?
- = content_for :customize_homepage_banner do
- .gl-display-none.gl-md-display-block{ class: "gl-pt-6! gl-pb-2! #{(container_class unless @no_container)} #{@content_class}" }
- .js-customize-homepage-banner{ data: { svg_path: image_path('illustrations/monitoring/getting_started.svg'),
- preferences_behavior_path: profile_preferences_path(anchor: 'behavior'),
- callouts_path: user_callouts_path,
- callouts_feature_id: UserCalloutsHelper::CUSTOMIZE_HOMEPAGE,
- track_label: 'home_page' } }
-
= render_dashboard_ultimate_trial(current_user)
- page_title _("Projects")
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 09b7f24745..a313ad7d23 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -61,7 +61,7 @@
- if show_recaptcha_sign_up?
= recaptcha_tags nonce: content_security_policy_nonce
.submit-container
- = f.submit button_text, class: 'btn gl-button btn-confirm', data: { qa_selector: 'new_user_register_button' }
+ = f.submit button_text, class: 'btn gl-button btn-confirm gl-display-block gl-w-full', data: { qa_selector: 'new_user_register_button' }
= render 'devise/shared/terms_of_service_notice', button_text: button_text
- if show_omniauth_providers && omniauth_providers_placement == :bottom
= render 'devise/shared/signup_omniauth_providers'
diff --git a/app/views/groups/_delete_project_button.html.haml b/app/views/groups/_delete_project_button.html.haml
new file mode 100644
index 0000000000..54a9931941
--- /dev/null
+++ b/app/views/groups/_delete_project_button.html.haml
@@ -0,0 +1 @@
+= link_to _('Delete'), project, data: { confirm: remove_project_message(project) }, method: :delete, class: "btn gl-button btn-danger"
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index 624d0a21b8..b7c2b4d86b 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -23,6 +23,10 @@
.home-panel-buttons.col-md-12.col-lg-6
- if current_user
.gl-display-flex.gl-flex-wrap.gl-lg-justify-content-end.gl-mx-n2{ data: { testid: 'group-buttons' } }
+ - if current_user.admin?
+ = link_to [:admin, @group], class: 'btn btn-default gl-button btn-icon gl-mt-3 gl-mr-2', title: s_('View group in admin area'),
+ data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = sprite_icon('admin')
- if @notification_setting
.js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mx-2 gl-mt-3 gl-vertical-align-top' } }
- if can_create_subgroups
diff --git a/app/views/groups/_import_group_from_file_panel.html.haml b/app/views/groups/_import_group_from_file_panel.html.haml
index c70cc2c493..3bc2146b31 100644
--- a/app/views/groups/_import_group_from_file_panel.html.haml
+++ b/app/views/groups/_import_group_from_file_panel.html.haml
@@ -33,8 +33,7 @@
title: _('Please choose a group URL with no special characters.'),
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
%p.validation-error.gl-field-error.field-validation.hide
- = _('Group path is already taken. Suggestions: ')
- %span.gl-path-suggestions
+ = _("Group path is already taken. We've suggested one that is available.")
%p.validation-success.gl-field-success.field-validation.hide= _('Group path is available.')
%p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group path availability...')
.form-group
diff --git a/app/views/groups/_invite_members_modal.html.haml b/app/views/groups/_invite_members_modal.html.haml
index f4f3c8ce8f..3be1a142ca 100644
--- a/app/views/groups/_invite_members_modal.html.haml
+++ b/app/views/groups/_invite_members_modal.html.haml
@@ -1,7 +1,8 @@
-- if can?(current_user, :admin_group_member, group)
- .js-invite-members-modal{ data: { id: group.id,
- name: group.name,
- is_project: 'false',
- access_levels: GroupMember.access_level_roles.to_json,
- default_access_level: Gitlab::Access::GUEST,
- help_link: help_page_url('user/permissions') }.merge(group_select_data(group)) }
+- return unless can_manage_members?(group)
+
+.js-invite-members-modal{ data: { id: group.id,
+ name: group.name,
+ is_project: 'false',
+ access_levels: GroupMember.access_level_roles.to_json,
+ default_access_level: Gitlab::Access::GUEST,
+ help_link: help_page_url('user/permissions') }.merge(group_select_data(group)) }
diff --git a/app/views/groups/_project_badges.html.haml b/app/views/groups/_project_badges.html.haml
new file mode 100644
index 0000000000..1f7895e216
--- /dev/null
+++ b/app/views/groups/_project_badges.html.haml
@@ -0,0 +1,2 @@
+- if project.archived
+ %span.badge.badge-warning.badge-pill.gl-badge.md= _('archived')
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 33f836c2de..ad916e3aee 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -2,7 +2,7 @@
- page_title _("Merge requests")
-- if @merge_requests&.size == 0
+- if issuables_count_for_state(:merge_requests, :all) == 0
= render 'shared/empty_states/merge_requests', project_select_button: true
- else
.top-area
diff --git a/app/views/groups/milestones/_form.html.haml b/app/views/groups/milestones/_form.html.haml
index 259e96901f..a630256918 100644
--- a/app/views/groups/milestones/_form.html.haml
+++ b/app/views/groups/milestones/_form.html.haml
@@ -1,25 +1,23 @@
= form_for [@group, @milestone], html: { class: 'milestone-form common-note-form js-quick-submit js-requires-input' } do |f|
= form_errors(@milestone)
- .row
- .col-md-6
- .form-group.row
- .col-form-label.col-sm-2
- = f.label :title, _("Title")
- .col-sm-10
- = f.text_field :title, maxlength: 255, class: "form-control", data: { qa_selector: "milestone_title_field" }, required: true, autofocus: true
- .form-group.row.milestone-description
- .col-form-label.col-sm-2
- = f.label :description, _("Description")
- .col-sm-10
- = render layout: 'shared/md_preview', locals: { url: group_preview_markdown_path } do
- = render 'shared/zen', f: f, attr: :description,
- classes: 'note-textarea',
- qa_selector: 'milestone_description_field',
- supports_autocomplete: true,
- placeholder: _('Write milestone description...')
- .clearfix
- .error-alert
- = render "shared/milestones/form_dates", f: f
+ .form-group.row
+ .col-form-label.col-sm-2
+ = f.label :title, _("Title")
+ .col-sm-10
+ = f.text_field :title, maxlength: 255, class: "form-control", data: { qa_selector: "milestone_title_field" }, required: true, autofocus: true
+ = render "shared/milestones/form_dates", f: f
+ .form-group.row.milestone-description
+ .col-form-label.col-sm-2
+ = f.label :description, _("Description")
+ .col-sm-10
+ = render layout: 'shared/md_preview', locals: { url: group_preview_markdown_path } do
+ = render 'shared/zen', f: f, attr: :description,
+ classes: 'note-textarea',
+ qa_selector: 'milestone_description_field',
+ supports_autocomplete: true,
+ placeholder: _('Write milestone description...')
+ .clearfix
+ .error-alert
.form-actions
- if @milestone.new_record?
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index 9d595d1977..9dbf60b119 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -15,13 +15,12 @@
.controls
= link_to _('Members'), project_project_members_path(project), id: "edit_#{dom_id(project)}", class: "btn gl-button"
= link_to _('Edit'), edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn gl-button"
- = link_to _('Delete'), project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn gl-button btn-danger"
+ = render 'delete_project_button', project: project
.stats
%span.badge.badge-pill
= storage_counter(project.statistics&.storage_size)
- - if project.archived
- %span.badge.badge-warning archived
+ = render 'project_badges', project: project
.title
= link_to(project_path(project)) do
diff --git a/app/views/groups/runners/_group_runners.html.haml b/app/views/groups/runners/_group_runners.html.haml
index 823d908c5e..49e297ee13 100644
--- a/app/views/groups/runners/_group_runners.html.haml
+++ b/app/views/groups/runners/_group_runners.html.haml
@@ -9,19 +9,24 @@
-# Proper policies should be implemented per
-# https://gitlab.com/gitlab-org/gitlab-foss/issues/45894
-- if can?(current_user, :admin_pipeline, @group)
- = render partial: 'ci/runner/how_to_setup_runner_automatically',
- locals: { type: 'group',
- clusters_path: group_clusters_path(@group) }
- - if params[:ci_runner_templates]
+.bs-callout.help-callout
+ - if can?(current_user, :admin_pipeline, @group) && valid_runner_registrars.include?('group')
+ = render partial: 'ci/runner/how_to_setup_runner_automatically',
+ locals: { type: 'group',
+ clusters_path: group_clusters_path(@group) }
+ - if params[:ci_runner_templates]
+ %hr
+ = render partial: 'ci/runner/setup_runner_in_aws',
+ locals: { registration_token: @group.runners_token }
%hr
- = render partial: 'ci/runner/setup_runner_in_aws',
- locals: { registration_token: @group.runners_token }
- %hr
- = render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: @group.runners_token,
- type: 'group',
- reset_token_url: reset_registration_token_group_settings_ci_cd_path,
- project_path: '',
- group_path: @group.full_path }
- %br
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: @group.runners_token,
+ type: 'group',
+ reset_token_url: reset_registration_token_group_settings_ci_cd_path,
+ project_path: '',
+ group_path: @group.full_path }
+ %br
+ - else
+ = _('Please contact an admin to register runners.')
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'runner-registration'), target: '_blank', rel: 'noopener noreferrer'
+
diff --git a/app/views/groups/runners/_runner.html.haml b/app/views/groups/runners/_runner.html.haml
index 89e32c0999..13da229298 100644
--- a/app/views/groups/runners/_runner.html.haml
+++ b/app/views/groups/runners/_runner.html.haml
@@ -7,16 +7,16 @@
.table-mobile-content
- if runner.group_type?
%span.badge.badge-pill.gl-badge.sm.badge-success
- = _('group')
+ = s_('Runners|group')
- else
%span.badge.badge-pill.gl-badge.sm.badge-info
- = _('specific')
+ = s_('Runners|specific')
- if runner.locked?
%span.badge.badge-pill.gl-badge.sm.badge-warning
- = _('locked')
+ = s_('Runners|locked')
- unless runner.active?
%span.badge.badge-pill.gl-badge.sm.badge-danger
- = _('paused')
+ = s_('Runners|paused')
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }= s_('Runners|Runner')
diff --git a/app/views/groups/runners/_index.html.haml b/app/views/groups/runners/_settings.html.haml
similarity index 100%
rename from app/views/groups/runners/_index.html.haml
rename to app/views/groups/runners/_settings.html.haml
diff --git a/app/views/groups/settings/_advanced.html.haml b/app/views/groups/settings/_advanced.html.haml
index d7a145924d..fea0736ffc 100644
--- a/app/views/groups/settings/_advanced.html.haml
+++ b/app/views/groups/settings/_advanced.html.haml
@@ -24,21 +24,6 @@
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
= f.submit s_('GroupSettings|Change group URL'), class: 'btn gl-button btn-warning'
-.sub-section
- %h4.warning-title= s_('GroupSettings|Transfer group')
- = form_for @group, url: transfer_group_path(@group), method: :put, html: { class: 'js-group-transfer-form' } do |f|
- .form-group
- = dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', data: { data: parent_group_options(@group), qa_selector: 'select_group_dropdown' } })
- = hidden_field_tag 'new_parent_group_id'
-
- %ul
- - side_effects_link_start = ''.html_safe
- - warning_text = s_("GroupSettings|Be careful. Changing a group's parent can have unintended %{side_effects_link_start}side effects%{side_effects_link_end}.") % { side_effects_link_start: side_effects_link_start, side_effects_link_end: ''.html_safe }
- %li= warning_text.html_safe
- %li= s_('GroupSettings|You can only transfer the group to a group you manage.')
- %li= s_('GroupSettings|You will need to update your local repositories to point to the new location.')
- %li= s_("GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.")
- = f.submit s_('GroupSettings|Transfer group'), class: 'btn gl-button btn-warning', data: { qa_selector: "transfer_group_button" }
-
+= render 'groups/settings/transfer', group: @group
= render 'groups/settings/remove', group: @group
= render_if_exists 'groups/settings/restore', group: @group
diff --git a/app/views/groups/settings/_pages_settings.html.haml b/app/views/groups/settings/_pages_settings.html.haml
index a7b1813e4f..456e0b0f1d 100644
--- a/app/views/groups/settings/_pages_settings.html.haml
+++ b/app/views/groups/settings/_pages_settings.html.haml
@@ -2,4 +2,4 @@
= render_if_exists 'shared/pages/max_pages_size_input', form: f
.gl-mt-3
- = f.submit s_('GitLabPages|Save'), class: 'btn gl-button btn-confirm'
+ = f.submit s_('GitLabPages|Save changes'), class: 'btn gl-button btn-confirm'
diff --git a/app/views/groups/settings/_transfer.html.haml b/app/views/groups/settings/_transfer.html.haml
new file mode 100644
index 0000000000..1472ae4215
--- /dev/null
+++ b/app/views/groups/settings/_transfer.html.haml
@@ -0,0 +1,22 @@
+.sub-section
+ %h4.warning-title= s_('GroupSettings|Transfer group')
+ = form_for group, url: transfer_group_path(group), method: :put, html: { class: 'js-group-transfer-form' } do |f|
+ .form-group
+ = dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', disabled: group.paid?, data: { data: parent_group_options(group), qa_selector: 'select_group_dropdown' } })
+ = hidden_field_tag 'new_parent_group_id'
+
+ %ul
+ - side_effects_link_start = ''.html_safe
+ - warning_text = s_("GroupSettings|Be careful. Changing a group's parent can have unintended %{side_effects_link_start}side effects%{side_effects_link_end}.") % { side_effects_link_start: side_effects_link_start, side_effects_link_end: ''.html_safe }
+ %li= warning_text.html_safe
+ %li= s_('GroupSettings|You can only transfer the group to a group you manage.')
+ %li= s_('GroupSettings|You will need to update your local repositories to point to the new location.')
+ %li= s_("GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.")
+
+ - if group.paid?
+ .gl-alert.gl-alert-info.gl-mb-5{ data: { testid: 'group-to-transfer-has-linked-subscription-alert' } }
+ = sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ .gl-alert-body
+ = html_escape(_("This group can't be transfered because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "".html_safe, linkEnd: ''.html_safe }
+
+ = f.submit s_('GroupSettings|Transfer group'), class: 'btn gl-button btn-warning', data: { qa_selector: "transfer_group_button" }
diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml
index 3c6514b95b..018dd4c424 100644
--- a/app/views/groups/settings/ci_cd/show.html.haml
+++ b/app/views/groups/settings/ci_cd/show.html.haml
@@ -32,9 +32,9 @@
= expanded ? _('Collapse') : _('Expand')
%p
= _("Runners are processes that pick up and execute CI/CD jobs for GitLab.")
- = link_to s_('How do I configure runners?'), help_page_path('ci/runners/README'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to s_('How do I configure runners?'), help_page_path('ci/runners/index'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
- = render 'groups/runners/index'
+ = render 'groups/runners/settings'
%section.settings#auto-devops-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
diff --git a/app/views/groups/settings/integrations/index.html.haml b/app/views/groups/settings/integrations/index.html.haml
index 7a81d53c08..1db8edb040 100644
--- a/app/views/groups/settings/integrations/index.html.haml
+++ b/app/views/groups/settings/integrations/index.html.haml
@@ -1,9 +1,9 @@
-- breadcrumb_title _('Integrations')
-- page_title _('Integrations')
+- breadcrumb_title s_('Integrations|Group-level integration management')
+- page_title s_('Integrations|Group-level integration management')
- @content_class = 'limit-container-width' unless fluid_layout
-%h3= s_('Integrations|Project integration management')
+%h3= s_('Integrations|Group-level integration management')
- integrations_link_start = ''.html_safe % { url: integrations_help_page_path }
-%p= s_("Integrations|GitLab administrators can set up integrations that all projects inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a group or project if the settings are necessary at that level. Learn more about %{integrations_link_start}project integration management%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, link_end: "".html_safe }
+%p= s_("Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, link_end: "".html_safe }
= render 'shared/integrations/index', integrations: @integrations
diff --git a/app/views/groups/settings/repository/_initial_branch_name.html.haml b/app/views/groups/settings/repository/_initial_branch_name.html.haml
index 23ac7d51e4..5299c38576 100644
--- a/app/views/groups/settings/repository/_initial_branch_name.html.haml
+++ b/app/views/groups/settings/repository/_initial_branch_name.html.haml
@@ -5,7 +5,7 @@
%button.gl-button.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Set the default name of the initial branch when creating new repositories through the user interface.')
+ = s_('GroupSettings|The default name for the initial branch of new repositories created in the group.')
.settings-content
= form_for @group, url: group_path(@group, anchor: 'js-default-branch-name'), html: { class: 'fieldset-form' } do |f|
= form_errors(@group)
@@ -16,7 +16,7 @@
= f.label :default_branch_name, _('Default initial branch name'), class: 'label-light'
= f.text_field :default_branch_name, value: group.namespace_settings&.default_branch_name, placeholder: Gitlab::DefaultBranch.value(object: @group), class: 'form-control'
%span.form-text.text-muted
- = (_("Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used.") % { branch_name_default: fallback_branch_name }).html_safe
+ = (s_("GroupSettings|If not specified at the group or instance level, the default is %{default_initial_branch_name}. Does not affect existing repositories.") % { default_initial_branch_name: fallback_branch_name }).html_safe
= f.hidden_field :redirect_target, value: "repository_settings"
= f.submit _('Save changes'), class: 'btn gl-button btn-confirm'
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 628425bf46..76850f0a88 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -6,6 +6,8 @@
- if show_thanks_for_purchase_banner?
= render_if_exists 'shared/thanks_for_purchase_banner', plan_title: plan_title, quantity: params[:purchased_quantity].to_i
+= render_if_exists 'shared/qrtly_reconciliation_alert', group: @group
+
- if show_invite_banner?(@group)
= content_for :group_invite_members_banner do
.container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" }
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index a56eaaf685..9588896394 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -38,7 +38,7 @@
.card-header
= _('Quick help')
%ul.content-list
- %li= link_to _('See our website for getting help'), support_url
+ %li= link_to _('See our website for help'), support_url
%li
%button.btn-blank.btn-link.js-trigger-search-bar{ type: 'button' }
= _('Use the search bar on the top of this page')
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index 8daddbb004..028268482c 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -21,5 +21,5 @@
= file_field_tag :file, class: ''
.row
.form-actions.col-sm-12
- = submit_tag _('Import project'), class: 'gl-button btn btn-confirm'
+ = submit_tag _('Import project'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'import_project_button' }
= link_to _('Cancel'), new_project_path, class: 'gl-button btn btn-default btn-cancel'
diff --git a/app/views/import/shared/_new_project_form.html.haml b/app/views/import/shared/_new_project_form.html.haml
index 7de8b0ee10..16526382f4 100644
--- a/app/views/import/shared/_new_project_form.html.haml
+++ b/app/views/import/shared/_new_project_form.html.haml
@@ -1,7 +1,7 @@
.row
.form-group.project-name.col-sm-12
= label_tag :name, _('Project name'), class: 'label-bold'
- = text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control gl-form-input input-lg", autofocus: true, required: true, aria: { required: true }
+ = text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control gl-form-input input-lg", autofocus: true, required: true, aria: { required: true }, data: { qa_selector: 'project_name_field' }
.form-group.col-12.col-sm-6
= label_tag :namespace_id, _('Project URL'), class: 'label-bold'
.form-group
@@ -18,4 +18,4 @@
= hidden_field_tag :namespace_id, current_user.namespace_id
.form-group.col-12.col-sm-6.project-path
= label_tag :path, _('Project slug'), class: 'label-bold'
- = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control gl-form-input", required: true, aria: { required: true }
+ = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control gl-form-input", required: true, aria: { required: true }, data: { qa_selector: 'project_slug_field' }
diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml
index 433337602f..a302fa605e 100644
--- a/app/views/layouts/_flash.html.haml
+++ b/app/views/layouts/_flash.html.haml
@@ -4,6 +4,8 @@
- flash.each do |key, value|
- if key == 'toast' && value
.js-toast-message{ data: { message: value } }
+ - elsif value == I18n.t('devise.failure.unconfirmed')
+ = render 'shared/confirm_your_email_alert'
- elsif value
%div{ class: "flash-#{key} mb-2" }
= sprite_icon(icons[key], css_class: 'align-middle mr-1') unless icons[key].nil?
diff --git a/app/views/layouts/_loading_hints.html.haml b/app/views/layouts/_loading_hints.html.haml
index cd1a236b6b..c431f05c21 100644
--- a/app/views/layouts/_loading_hints.html.haml
+++ b/app/views/layouts/_loading_hints.html.haml
@@ -7,5 +7,5 @@
- else
%link{ { rel: 'preload', href: stylesheet_url('application'), as: 'style' }, ActionController::Base.asset_host ? { crossorigin: 'anonymous' } : {} }
%link{ { rel: 'preload', href: stylesheet_url("highlight/themes/#{user_color_scheme}"), as: 'style' }, ActionController::Base.asset_host ? { crossorigin: 'anonymous' } : {} }
- - if Gitlab::CurrentSettings.snowplow_enabled? && Gitlab::CurrentSettings.snowplow_collector_hostname
+ - if Gitlab::Tracking.enabled? && Gitlab::CurrentSettings.snowplow_collector_hostname
%link{ rel: 'preconnect', href: Gitlab::CurrentSettings.snowplow_collector_hostname, crossorigin: '' }
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 2b63e2c647..6c959f5e60 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -14,11 +14,12 @@
= render "layouts/header/service_templates_deprecation_callout"
= render "layouts/nav/classification_level_banner"
= yield :flash_message
- = render "shared/ping_consent"
+ = render "shared/service_ping_consent"
= render_account_recovery_regular_check
= render_if_exists "layouts/header/ee_subscribable_banner"
= render_if_exists "shared/namespace_storage_limit_alert"
= render_if_exists "shared/new_user_signups_cap_reached_alert"
+ = yield :page_level_alert
= yield :customize_homepage_banner
- unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs"
@@ -27,5 +28,6 @@
= render "layouts/flash", extra_flash_class: 'limit-container-width'
= yield :before_content
= yield
+ = yield :after_content
= render "layouts/nav/top_nav_responsive", class: 'layout-page content-wrapper-margin'
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index e617b4358e..5ce275d4a4 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -3,14 +3,14 @@
.search-input-container
.search-input-wrap
.dropdown{ data: { url: search_autocomplete_path } }
- = search_field_tag 'search', nil, placeholder: _('Search or jump to…'),
+ = search_field_tag 'search', nil, placeholder: _('Search GitLab'),
class: 'search-input dropdown-menu-toggle no-outline js-search-dashboard-options',
spellcheck: false,
autocomplete: 'off',
data: { issues_path: issues_dashboard_path,
mr_path: merge_requests_dashboard_path,
qa_selector: 'search_term_field' },
- aria: { label: _('Search or jump to…') }
+ aria: { label: _('Search GitLab') }
%button.hidden.js-dropdown-search-toggle{ type: 'button', data: { toggle: 'dropdown' } }
.dropdown-menu.dropdown-select{ data: { testid: 'dashboard-search-options' } }
= dropdown_content do
diff --git a/app/views/layouts/_snowplow.html.haml b/app/views/layouts/_snowplow.html.haml
index bdce4eac75..9c0384e5fa 100644
--- a/app/views/layouts/_snowplow.html.haml
+++ b/app/views/layouts/_snowplow.html.haml
@@ -1,4 +1,4 @@
-- return unless Gitlab::CurrentSettings.snowplow_enabled?
+- return unless Gitlab::Tracking.enabled?
= javascript_tag do
:plain
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 47c092e199..899bf65de4 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,7 +1,6 @@
- page_classes = page_class << @html_class
- page_classes = page_classes.flatten.compact
- body_classes = [user_application_theme, user_tab_width, @body_class, client_class_list]
-- body_classes << 'sidebar-refactoring' if sidebar_refactor_enabled?
!!! 5
%html{ lang: I18n.locale, class: page_classes }
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index ae7c160c06..5c9c6a06ac 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -14,28 +14,29 @@
.row.mt-3
.col-sm-12
%h1.mb-3.font-weight-normal
- = current_appearance&.title.presence || "GitLab"
+ = current_appearance&.title.presence || _('GitLab')
.row.mb-3
.col-sm-7.order-12.order-sm-1.brand-holder
- = brand_image
- - if current_appearance&.description?
- = brand_text
- - else
- %h3.mt-sm-0
- = _('A complete DevOps platform')
+ - unless recently_confirmed_com?
+ = brand_image
+ - if current_appearance&.description?
+ = brand_text
+ - else
+ %h3.gl-sm-mt-0
+ = _('A complete DevOps platform')
- %p
- = _('GitLab is a single application for the entire software development lifecycle. From project planning and source code management to CI/CD, monitoring, and security.')
+ %p
+ = _('GitLab is a single application for the entire software development lifecycle. From project planning and source code management to CI/CD, monitoring, and security.')
- %p
- = _('This is a self-managed instance of GitLab.')
+ %p
+ = _('This is a self-managed instance of GitLab.')
- if Gitlab::CurrentSettings.sign_in_text.present?
= markdown_field(Gitlab::CurrentSettings.current_application_settings, :sign_in_text)
= render_if_exists 'layouts/devise_help_text'
- .col-sm-5.order-1.order-sm-12.new-session-forms-container
+ .col-sm-5.order-1.new-session-forms-container{ class: recently_confirmed_com? ? 'order-sm-first' : 'order-sm-12' }
= yield
= render 'devise/shared/footer', footer_message: footer_message
diff --git a/app/views/layouts/header/_new_dropdown.html.haml b/app/views/layouts/header/_new_dropdown.html.haml
index c5f43fb2c1..0be87ad963 100644
--- a/app/views/layouts/header/_new_dropdown.html.haml
+++ b/app/views/layouts/header/_new_dropdown.html.haml
@@ -6,7 +6,7 @@
- return if menu_sections.empty?
-%li.header-new.dropdown{ class: top_class, data: { track_label: "new_dropdown", track_event: "click_dropdown", track_experiment: "new_repo" } }
+%li.header-new.dropdown{ class: top_class, data: { track_label: "new_dropdown", track_event: "click_dropdown" } }
= link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip", id: "js-onboarding-new-project-link", title: title, ref: 'tooltip', aria: { label: title }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static', qa_selector: 'new_menu_toggle' } do
= sprite_icon('plus-square')
= sprite_icon('chevron-down', css_class: 'caret-down')
diff --git a/app/views/layouts/header/_registration_enabled_callout.html.haml b/app/views/layouts/header/_registration_enabled_callout.html.haml
index 9266702e44..25a7f7ba9d 100644
--- a/app/views/layouts/header/_registration_enabled_callout.html.haml
+++ b/app/views/layouts/header/_registration_enabled_callout.html.haml
@@ -7,8 +7,8 @@
alert_data: { feature_id: UserCalloutsHelper::REGISTRATION_ENABLED_CALLOUT, dismiss_endpoint: user_callouts_path },
close_button_data: { testid: 'close-registration-enabled-callout' } do
.gl-alert-body
- = html_escape(_('%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance.')) % { anchorOpen: "".html_safe, anchorClose: ''.html_safe }
+ = html_escape(_('%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance.')) % { anchorOpen: "".html_safe, anchorClose: ''.html_safe }
.gl-alert-actions
- = link_to general_admin_application_settings_path(anchor: 'js-signup-settings'), class: 'btn gl-alert-action btn-info btn-md gl-button' do
+ = link_to general_admin_application_settings_path(anchor: 'js-signup-settings'), class: 'btn gl-alert-action btn-confirm btn-md gl-button' do
%span.gl-button-text
= _('View setting')
diff --git a/app/views/layouts/minimal.html.haml b/app/views/layouts/minimal.html.haml
new file mode 100644
index 0000000000..b5cb8f2af3
--- /dev/null
+++ b/app/views/layouts/minimal.html.haml
@@ -0,0 +1,18 @@
+- page_classes = page_class.push(@html_class).flatten.compact
+
+!!! 5
+%html{ lang: I18n.locale, class: page_classes }
+ = render "layouts/head"
+ %body{ data: body_data }
+ = header_message
+ = render 'peek/bar'
+ = render "layouts/header/empty"
+ .layout-page
+ .content-wrapper.content-wrapper-margin.gl-pt-6{ class: 'gl-md-pt-11!' }
+ .alert-wrapper.gl-force-block-formatting-context
+ = render "layouts/broadcast"
+ .limit-container-width{ class: container_class }
+ %main#content-body.content
+ = render "layouts/flash" unless @hide_flash
+ = yield
+ = footer_message
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 117382d87b..e4cdb4e1b0 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -4,7 +4,7 @@
-# [1]: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587
%ul.list-unstyled.navbar-sub-nav
- if dashboard_nav_link?(:projects)
- = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects", data: { track_label: "projects_dropdown", track_event: "click_dropdown", track_experiment: "new_repo" } }) do
+ = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects", data: { track_label: "projects_dropdown", track_event: "click_dropdown" } }) do
%button{ type: 'button', data: { toggle: "dropdown" } }
= _('Projects')
= sprite_icon('chevron-down', css_class: 'caret-down')
diff --git a/app/views/layouts/nav/projects_dropdown/_show.html.haml b/app/views/layouts/nav/projects_dropdown/_show.html.haml
index 4607097556..f16aab92a9 100644
--- a/app/views/layouts/nav/projects_dropdown/_show.html.haml
+++ b/app/views/layouts/nav/projects_dropdown/_show.html.haml
@@ -15,21 +15,12 @@
= nav_link(path: 'projects#trending') do
= link_to explore_root_path, data: { track_label: "projects_dropdown_explore_projects", track_event: "click_link" } do
= _('Explore projects')
- - experiment(:new_repo, user: current_user) do |e|
- - e.use do
- = nav_link(path: 'projects/new#blank_project', html_options: { class: 'gl-border-0 gl-border-t-1 gl-border-solid gl-border-gray-100' }) do
- = link_to new_project_path(anchor: 'blank_project'), data: { track_label: "projects_dropdown_blank_project", track_event: "click_link", track_experiment: "new_repo", qa_selector: "create_project_link" } do
- = _('Create blank project')
- = nav_link(path: 'projects/new#import_project') do
- = link_to new_project_path(anchor: 'import_project'), data: { track_label: "projects_dropdown_import_project", track_event: "click_link", track_experiment: "new_repo", qa_selector: "import_project_link" } do
- = _('Import project')
- - e.try do
- = nav_link(path: 'projects/new#blank_project', html_options: { class: 'gl-border-0 gl-border-t-1 gl-border-solid gl-border-gray-100' }) do
- = link_to new_project_path(anchor: 'blank_project'), data: { track_label: "projects_dropdown_blank_project", track_event: "click_link", track_experiment: "new_repo" } do
- = _('Create blank project/repository')
- = nav_link(path: 'projects/new#import_project') do
- = link_to new_project_path(anchor: 'import_project'), data: { track_label: "projects_dropdown_import_project", track_event: "click_link", track_experiment: "new_repo" } do
- = _('Import project/repository')
+ = nav_link(path: 'projects/new#blank_project', html_options: { class: 'gl-border-0 gl-border-t-1 gl-border-solid gl-border-gray-100' }) do
+ = link_to new_project_path(anchor: 'blank_project'), data: { track_label: "projects_dropdown_blank_project", track_event: "click_link", qa_selector: "create_project_link" } do
+ = _('Create blank project')
+ = nav_link(path: 'projects/new#import_project') do
+ = link_to new_project_path(anchor: 'import_project'), data: { track_label: "projects_dropdown_import_project", track_event: "click_link", qa_selector: "import_project_link" } do
+ = _('Import project')
= nav_link(path: 'projects/new#create_from_template') do
= link_to new_project_path(anchor: 'create_from_template'), data: { track_label: "projects_dropdown_create_from_template", track_event: "click_link" } do
= _('Create from template')
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 21c3d7cb7e..d0b73a3364 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -1,12 +1,9 @@
-- avatar_size = sidebar_refactor_disabled? ? 24 : 18
-- avatar_size_class = sidebar_refactor_disabled? ? 's40' : 's32'
-
%aside.nav-sidebar.qa-admin-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), 'aria-label': _('Admin navigation') }
.nav-sidebar-inner-scroll
.context-header
= link_to admin_root_path, title: _('Admin Overview') do
- %span{ class: ['avatar-container', 'settings-avatar', 'rect-avatar', avatar_size_class] }
- = sprite_icon('admin', size: avatar_size)
+ %span{ class: ['avatar-container', 'settings-avatar', 'rect-avatar', 's32'] }
+ = sprite_icon('admin', size: 18)
%span.sidebar-context-title
= _('Admin Area')
%ul.sidebar-top-level-items{ data: { qa_selector: 'admin_sidebar_overview_submenu_content' } }
@@ -209,19 +206,6 @@
= render_if_exists 'layouts/nav/sidebar/credentials_link'
- - if show_service_templates_nav_link?
- = nav_link(controller: :services) do
- = link_to admin_application_settings_services_path do
- .nav-icon-container
- = sprite_icon('template')
- %span.nav-item-name
- = _('Service Templates')
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :services, html_options: { class: "fly-out-top-item" } ) do
- = link_to admin_application_settings_services_path do
- %strong.fly-out-top-item-name
- = _('Service Templates')
-
= nav_link(controller: :labels) do
= link_to admin_labels_path do
.nav-icon-container
diff --git a/app/views/layouts/nav/sidebar/_analytics_links.html.haml b/app/views/layouts/nav/sidebar/_analytics_links.html.haml
index 58989d6afc..92a7b97203 100644
--- a/app/views/layouts/nav/sidebar/_analytics_links.html.haml
+++ b/app/views/layouts/nav/sidebar/_analytics_links.html.haml
@@ -1,6 +1,6 @@
- navbar_links = links.sort_by(&:title)
- all_paths = navbar_links.map(&:path)
-- analytics_link = navbar_links.find { |link| link.title == _('Value Stream') } || navbar_links.first
+- analytics_link = navbar_links.find { |link| link.title == _('Value stream') } || navbar_links.first
- if navbar_links.any?
= nav_link(path: all_paths) do
diff --git a/app/views/layouts/nav/sidebar/_context_menu_body.html.haml b/app/views/layouts/nav/sidebar/_context_menu_body.html.haml
deleted file mode 100644
index 321bcda570..0000000000
--- a/app/views/layouts/nav/sidebar/_context_menu_body.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-- avatar_size_class = sidebar_refactor_disabled? ? 's40' : 's32'
-- avatar_classes = ['avatar-container', 'rect-avatar', 'group-avatar']
-- avatar_classes << avatar_size_class
-
-= link_to group_path(@group), title: @group.name, data: { qa_selector: 'group_scope_link' } do
- %span{ class: avatar_classes }
- = group_icon(@group, class: ['avatar', 'avatar-tile', avatar_size_class])
- %span.sidebar-context-title
- = @group.name
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 0ce1d48a2d..980730bc3b 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -1,207 +1,3 @@
-- issues_count = cached_issuables_count(@group, type: :issues)
-- merge_requests_count = cached_issuables_count(@group, type: :merge_requests)
-- aside_title = @group.subgroup? ? _('Subgroup navigation') : _('Group navigation')
-
-%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **sidebar_tracking_attributes_by_object(@group), 'aria-label': aside_title }
- .nav-sidebar-inner-scroll
- - if sidebar_refactor_disabled?
- .context-header
- = render 'layouts/nav/sidebar/context_menu_body'
-
- %ul.sidebar-top-level-items.qa-group-sidebar
- - if sidebar_refactor_enabled?
- = nav_link(path: ['groups#show', 'groups#details'], html_options: { class: 'context-header' }) do
- = render 'layouts/nav/sidebar/context_menu_body'
-
- = render_if_exists 'layouts/nav/sidebar/group_trial_status_widget', group: @group
-
- - if group_sidebar_link?(:overview)
- - paths = group_overview_nav_link_paths
- = nav_link(path: paths, unless: -> { current_path?('groups/contribution_analytics#show') }, html_options: { class: 'home' }) do
- - information_link = sidebar_refactor_enabled? ? activity_group_path(@group) : group_path(@group)
- = link_to information_link, class: 'has-sub-items', data: { qa_selector: 'group_information_link' } do
- .nav-icon-container
- - sprite = sidebar_refactor_enabled? ? 'group' : 'home'
- = sprite_icon(sprite)
- %span.nav-item-name
- = group_information_title(@group)
-
- %ul.sidebar-sub-level-items{ data: { qa_selector: 'group_information_submenu'} }
- = nav_link(path: paths, html_options: { class: "fly-out-top-item" } ) do
- = link_to information_link do
- %strong.fly-out-top-item-name
- = group_information_title(@group)
- %li.divider.fly-out-top-item
-
- - if sidebar_refactor_disabled?
- = nav_link(path: ['groups#show', 'groups#details', 'groups#subgroups'], html_options: { class: 'home' }) do
- = link_to details_group_path(@group), title: _('Group details') do
- %span
- = _('Details')
-
- - if group_sidebar_link?(:activity)
- = nav_link(path: 'groups#activity') do
- = link_to activity_group_path(@group), title: _('Activity') do
- %span
- = _('Activity')
-
- - if group_sidebar_link?(:labels) && sidebar_refactor_enabled?
- = nav_link(path: 'labels#index') do
- = link_to group_labels_path(@group), title: _('Labels') do
- %span
- = _('Labels')
-
- - if sidebar_refactor_enabled?
- - if group_sidebar_link?(:group_members)
- = nav_link(path: 'group_members#index') do
- = link_to group_group_members_path(@group), title: _('Members'), data: { qa_selector: 'group_members_item' } do
- %span
- = _('Members')
-
- = render_if_exists "layouts/nav/ee/epic_link", group: @group
-
- - if group_sidebar_link?(:issues)
- = nav_link(path: group_issues_sub_menu_items, unless: -> { current_path?('issues_analytics#show') }) do
- = link_to issues_group_path(@group), data: { qa_selector: 'group_issues_item' }, class: 'has-sub-items' do
- .nav-icon-container
- = sprite_icon('issues')
- %span.nav-item-name
- = _('Issues')
- %span.badge.badge-pill.count= issues_count
-
- %ul.sidebar-sub-level-items{ data: { qa_selector: 'group_issues_sidebar_submenu'} }
- = nav_link(path: group_issues_sub_menu_items, html_options: { class: "fly-out-top-item" } ) do
- = link_to issues_group_path(@group) do
- %strong.fly-out-top-item-name
- = _('Issues')
- %span.badge.badge-pill.count.issue_counter.fly-out-badge= issues_count
-
- %li.divider.fly-out-top-item
- = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
- = link_to issues_group_path(@group), title: _('List') do
- %span
- = _('List')
-
- - if group_sidebar_link?(:boards)
- = nav_link(path: ['boards#index', 'boards#show']) do
- = link_to group_boards_path(@group), title: boards_link_text, data: { qa_selector: 'group_issue_boards_link' } do
- %span
- = boards_link_text
-
- - if group_sidebar_link?(:labels) && sidebar_refactor_disabled?
- = nav_link(path: 'labels#index') do
- = link_to group_labels_path(@group), title: _('Labels') do
- %span
- = _('Labels')
-
- - if group_sidebar_link?(:milestones)
- = nav_link(path: 'milestones#index') do
- = link_to group_milestones_path(@group), title: _('Milestones'), data: { qa_selector: 'group_milestones_link' } do
- %span
- = _('Milestones')
-
- = render_if_exists 'layouts/nav/sidebar/group_iterations_link'
-
- - if group_sidebar_link?(:merge_requests)
- = nav_link(path: 'groups#merge_requests') do
- = link_to merge_requests_group_path(@group) do
- .nav-icon-container
- = sprite_icon('git-merge')
- %span.nav-item-name
- = _('Merge requests')
- %span.badge.badge-pill.count= merge_requests_count
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
- = link_to merge_requests_group_path(@group) do
- %strong.fly-out-top-item-name
- = _('Merge requests')
- %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= merge_requests_count
-
- = render_if_exists "layouts/nav/ee/security_link" # EE-specific
-
- = render_if_exists "layouts/nav/ee/push_rules_link" # EE-specific
-
- - if group_sidebar_link?(:kubernetes)
- = nav_link(controller: [:clusters]) do
- = link_to group_clusters_path(@group) do
- .nav-icon-container
- = sprite_icon('cloud-gear')
- %span.nav-item-name
- = _('Kubernetes')
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: [:clusters], html_options: { class: "fly-out-top-item" } ) do
- = link_to group_clusters_path(@group), title: _('Kubernetes'), class: 'shortcuts-kubernetes' do
- %strong.fly-out-top-item-name
- = _('Kubernetes')
-
- = render 'groups/sidebar/packages'
-
- = render 'layouts/nav/sidebar/analytics_links', links: group_analytics_navbar_links(@group, current_user)
-
- - if group_sidebar_link?(:wiki)
- = render 'layouts/nav/sidebar/wiki_link', wiki_url: @group.wiki.web_url
-
- - if sidebar_refactor_disabled?
- - if group_sidebar_link?(:group_members)
- = nav_link(path: 'group_members#index') do
- = link_to group_group_members_path(@group) do
- .nav-icon-container
- = sprite_icon('users')
- %span.nav-item-name.qa-group-members-item
- = _('Members')
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
- = link_to group_group_members_path(@group) do
- %strong.fly-out-top-item-name
- = _('Members')
-
- - if group_sidebar_link?(:settings)
- = nav_link(path: group_settings_nav_link_paths) do
- = link_to edit_group_path(@group), class: 'has-sub-items' do
- .nav-icon-container
- = sprite_icon('settings')
- %span.nav-item-name{ data: { qa_selector: 'group_settings' } }
- = _('Settings')
- %ul.sidebar-sub-level-items.qa-group-sidebar-submenu{ data: { testid: 'group-settings-menu' } }
- = nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show groups/applications#index], html_options: { class: "fly-out-top-item" } ) do
- = link_to edit_group_path(@group) do
- %strong.fly-out-top-item-name
- = _('Settings')
- %li.divider.fly-out-top-item
- = nav_link(path: 'groups#edit') do
- = link_to edit_group_path(@group), title: _('General'), data: { qa_selector: 'general_settings_link' } do
- %span
- = _('General')
-
- = nav_link(controller: :integrations) do
- = link_to group_settings_integrations_path(@group), title: _('Integrations') do
- %span
- = _('Integrations')
-
- = nav_link(path: 'groups#projects') do
- = link_to projects_group_path(@group), title: _('Projects') do
- %span
- = _('Projects')
-
- = nav_link(controller: :repository) do
- = link_to group_settings_repository_path(@group), title: _('Repository') do
- %span
- = _('Repository')
-
- = nav_link(controller: [:ci_cd, 'groups/runners']) do
- = link_to group_settings_ci_cd_path(@group), title: _('CI/CD') do
- %span
- = _('CI/CD')
-
- = nav_link(controller: :applications) do
- = link_to group_settings_applications_path(@group), title: _('Applications') do
- %span
- = _('Applications')
-
- = render 'groups/sidebar/packages_settings'
-
- = render_if_exists "groups/ee/settings_nav"
-
- = render_if_exists "groups/ee/administration_nav"
-
- = render 'shared/sidebar_toggle_button'
+-# We're migration the group sidebar to a logical model based structure. If you need to update
+-# any of the existing menus, you can find them in app/views/layouts/nav/sidebar/_group_menus.html.haml.
+= render partial: 'shared/nav/sidebar', object: Sidebars::Groups::Panel.new(group_sidebar_context(@group, current_user))
diff --git a/app/views/layouts/nav/sidebar/_group_menus.html.haml b/app/views/layouts/nav/sidebar/_group_menus.html.haml
new file mode 100644
index 0000000000..5738c8becd
--- /dev/null
+++ b/app/views/layouts/nav/sidebar/_group_menus.html.haml
@@ -0,0 +1,166 @@
+- issues_count = cached_issuables_count(@group, type: :issues)
+- merge_requests_count = cached_issuables_count(@group, type: :merge_requests)
+
+= render_if_exists 'layouts/nav/sidebar/group_trial_status_widget', group: @group
+
+- if group_sidebar_link?(:overview)
+ - paths = group_overview_nav_link_paths
+ = nav_link(path: paths, unless: -> { current_path?('groups/contribution_analytics#show') }, html_options: { class: 'home' }) do
+ = link_to activity_group_path(@group), class: 'has-sub-items', data: { qa_selector: 'group_information_link' } do
+ .nav-icon-container
+ = sprite_icon('group')
+ %span.nav-item-name
+ = group_information_title(@group)
+
+ %ul.sidebar-sub-level-items{ data: { qa_selector: 'group_information_submenu'} }
+ = nav_link(path: paths, html_options: { class: "fly-out-top-item" } ) do
+ = link_to activity_group_path(@group) do
+ %strong.fly-out-top-item-name
+ = group_information_title(@group)
+ %li.divider.fly-out-top-item
+
+ - if group_sidebar_link?(:activity)
+ = nav_link(path: 'groups#activity') do
+ = link_to activity_group_path(@group), title: _('Activity') do
+ %span
+ = _('Activity')
+
+ - if group_sidebar_link?(:labels)
+ = nav_link(path: 'labels#index') do
+ = link_to group_labels_path(@group), title: _('Labels') do
+ %span
+ = _('Labels')
+
+ - if group_sidebar_link?(:group_members)
+ = nav_link(path: 'group_members#index') do
+ = link_to group_group_members_path(@group), title: _('Members'), data: { qa_selector: 'group_members_item' } do
+ %span
+ = _('Members')
+
+= render_if_exists "layouts/nav/ee/epic_link", group: @group
+
+- if group_sidebar_link?(:issues)
+ = nav_link(path: group_issues_sub_menu_items, unless: -> { current_path?('issues_analytics#show') }) do
+ = link_to issues_group_path(@group), data: { qa_selector: 'group_issues_item' }, class: 'has-sub-items' do
+ .nav-icon-container
+ = sprite_icon('issues')
+ %span.nav-item-name
+ = _('Issues')
+ %span.badge.badge-pill.count= issues_count
+
+ %ul.sidebar-sub-level-items{ data: { qa_selector: 'group_issues_sidebar_submenu'} }
+ = nav_link(path: group_issues_sub_menu_items, html_options: { class: "fly-out-top-item" } ) do
+ = link_to issues_group_path(@group) do
+ %strong.fly-out-top-item-name
+ = _('Issues')
+ %span.badge.badge-pill.count.issue_counter.fly-out-badge= issues_count
+
+ %li.divider.fly-out-top-item
+ = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
+ = link_to issues_group_path(@group), title: _('List') do
+ %span
+ = _('List')
+
+ - if group_sidebar_link?(:boards)
+ = nav_link(path: ['boards#index', 'boards#show']) do
+ = link_to group_boards_path(@group), title: boards_link_text, data: { qa_selector: 'group_issue_boards_link' } do
+ %span
+ = boards_link_text
+
+ - if group_sidebar_link?(:milestones)
+ = nav_link(path: 'milestones#index') do
+ = link_to group_milestones_path(@group), title: _('Milestones'), data: { qa_selector: 'group_milestones_link' } do
+ %span
+ = _('Milestones')
+
+ = render_if_exists 'layouts/nav/sidebar/group_iterations_link'
+
+- if group_sidebar_link?(:merge_requests)
+ = nav_link(path: 'groups#merge_requests') do
+ = link_to merge_requests_group_path(@group) do
+ .nav-icon-container
+ = sprite_icon('git-merge')
+ %span.nav-item-name
+ = _('Merge requests')
+ %span.badge.badge-pill.count= merge_requests_count
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
+ = link_to merge_requests_group_path(@group) do
+ %strong.fly-out-top-item-name
+ = _('Merge requests')
+ %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= merge_requests_count
+
+= render_if_exists "layouts/nav/ee/security_link" # EE-specific
+
+= render_if_exists "layouts/nav/ee/push_rules_link" # EE-specific
+
+- if group_sidebar_link?(:kubernetes)
+ = nav_link(controller: [:clusters]) do
+ = link_to group_clusters_path(@group) do
+ .nav-icon-container
+ = sprite_icon('cloud-gear')
+ %span.nav-item-name
+ = _('Kubernetes')
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(controller: [:clusters], html_options: { class: "fly-out-top-item" } ) do
+ = link_to group_clusters_path(@group), title: _('Kubernetes'), class: 'shortcuts-kubernetes' do
+ %strong.fly-out-top-item-name
+ = _('Kubernetes')
+
+= render 'groups/sidebar/packages'
+
+= render 'layouts/nav/sidebar/analytics_links', links: group_analytics_navbar_links(@group, current_user)
+
+- if group_sidebar_link?(:wiki)
+ = render 'layouts/nav/sidebar/wiki_link', wiki_url: @group.wiki.web_url
+
+- if group_sidebar_link?(:settings)
+ = nav_link(path: group_settings_nav_link_paths) do
+ = link_to edit_group_path(@group), class: 'has-sub-items' do
+ .nav-icon-container
+ = sprite_icon('settings')
+ %span.nav-item-name{ data: { qa_selector: 'group_settings' } }
+ = _('Settings')
+ %ul.sidebar-sub-level-items{ data: { testid: 'group-settings-menu', qa_selector: 'group_sidebar_submenu' } }
+ = nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show groups/applications#index], html_options: { class: "fly-out-top-item" } ) do
+ = link_to edit_group_path(@group) do
+ %strong.fly-out-top-item-name
+ = _('Settings')
+ %li.divider.fly-out-top-item
+ = nav_link(path: 'groups#edit') do
+ = link_to edit_group_path(@group), title: _('General'), data: { qa_selector: 'general_settings_link' } do
+ %span
+ = _('General')
+
+ = nav_link(controller: :integrations) do
+ = link_to group_settings_integrations_path(@group), title: _('Integrations') do
+ %span
+ = _('Integrations')
+
+ = nav_link(path: 'groups#projects') do
+ = link_to projects_group_path(@group), title: _('Projects') do
+ %span
+ = _('Projects')
+
+ = nav_link(controller: :repository) do
+ = link_to group_settings_repository_path(@group), title: _('Repository') do
+ %span
+ = _('Repository')
+
+ = nav_link(controller: [:ci_cd, 'groups/runners']) do
+ = link_to group_settings_ci_cd_path(@group), title: _('CI/CD') do
+ %span
+ = _('CI/CD')
+
+ = nav_link(controller: :applications) do
+ = link_to group_settings_applications_path(@group), title: _('Applications') do
+ %span
+ = _('Applications')
+
+ = render 'groups/sidebar/packages_settings'
+
+ = render_if_exists "groups/ee/settings_nav"
+
+= render_if_exists "groups/ee/administration_nav"
+
+= render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index daafabdb79..4db1e532ba 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -1,12 +1,9 @@
-- avatar_size = sidebar_refactor_disabled? ? 40 : 32
-- avatar_size_class = sidebar_refactor_disabled? ? 's40' : 's32'
-
%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **sidebar_tracking_attributes_by_object(current_user), 'aria-label': _('User settings') }
.nav-sidebar-inner-scroll
.context-header
= link_to profile_path, title: _('Profile Settings') do
- %span{ class: ['avatar-container', 'settings-avatar', avatar_size_class] }
- = image_tag avatar_icon_for_user(current_user, avatar_size), class: ['avatar', 'avatar-tile', 'js-sidebar-user-avatar', avatar_size_class], alt: current_user.name, data: { testid: 'sidebar-user-avatar' }
+ %span{ class: ['avatar-container', 'settings-avatar', 's32'] }
+ = image_tag avatar_icon_for_user(current_user, 32), class: ['avatar', 'avatar-tile', 'js-sidebar-user-avatar', 's32'], alt: current_user.name, data: { testid: 'sidebar-user-avatar' }
%span.sidebar-context-title= _('User Settings')
%ul.sidebar-top-level-items
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
diff --git a/app/views/layouts/welcome.html.haml b/app/views/layouts/welcome.html.haml
deleted file mode 100644
index 944f524d69..0000000000
--- a/app/views/layouts/welcome.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-!!! 5
-%html.subscriptions-layout-html{ lang: 'en' }
- = render 'layouts/head'
- %body.ui-indigo.gl-display-flex.vh-100
- = render "layouts/header/logo_with_title"
- = render "layouts/broadcast"
- .container.gl-display-flex.gl-flex-grow-1
- = yield
diff --git a/app/views/notify/access_token_about_to_expire_email.html.haml b/app/views/notify/access_token_about_to_expire_email.html.haml
index ea27f72764..fc318de4c4 100644
--- a/app/views/notify/access_token_about_to_expire_email.html.haml
+++ b/app/views/notify/access_token_about_to_expire_email.html.haml
@@ -8,4 +8,4 @@
%li= token
%p
- pat_link_start = ''.html_safe % { url: @target_url }
- = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe }
+ = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings.')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe }
diff --git a/app/views/notify/access_token_about_to_expire_email.text.erb b/app/views/notify/access_token_about_to_expire_email.text.erb
index dc9b1379e4..39608f0d6b 100644
--- a/app/views/notify/access_token_about_to_expire_email.text.erb
+++ b/app/views/notify/access_token_about_to_expire_email.text.erb
@@ -6,4 +6,4 @@
- <%= token %>
<% end %>
-<%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}') % { pat_link: @target_url } %>
+<%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}.') % { pat_link: @target_url } %>
diff --git a/app/views/notify/access_token_expired_email.html.haml b/app/views/notify/access_token_expired_email.html.haml
index b26431cce9..1e7c07c228 100644
--- a/app/views/notify/access_token_expired_email.html.haml
+++ b/app/views/notify/access_token_expired_email.html.haml
@@ -4,4 +4,4 @@
= _('One or more of your personal access tokens has expired.')
%p
- pat_link_start = ''.html_safe % { url: @target_url }
- = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe }
+ = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings.')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe }
diff --git a/app/views/notify/access_token_expired_email.text.erb b/app/views/notify/access_token_expired_email.text.erb
index d44f993d09..4dc67e85dc 100644
--- a/app/views/notify/access_token_expired_email.text.erb
+++ b/app/views/notify/access_token_expired_email.text.erb
@@ -2,4 +2,4 @@
<%= _('One or more of your personal access tokens has expired.') %>
-<%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}') % { pat_link: @target_url } %>
+<%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}.') % { pat_link: @target_url } %>
diff --git a/app/views/notify/in_product_marketing_email.html.haml b/app/views/notify/in_product_marketing_email.html.haml
index 45b002757e..6382718480 100644
--- a/app/views/notify/in_product_marketing_email.html.haml
+++ b/app/views/notify/in_product_marketing_email.html.haml
@@ -3,7 +3,7 @@
%head
%meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" }
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }
- %link{ href: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600", rel: "stylesheet", type: "text/css" }
+ %link{ href: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600", rel: "stylesheet", type: "text/css", data: { premailer: 'ignore' } }
%title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
diff --git a/app/views/notify/send_admin_notification.html.haml b/app/views/notify/send_admin_notification.html.haml
new file mode 100644
index 0000000000..f7f1528f33
--- /dev/null
+++ b/app/views/notify/send_admin_notification.html.haml
@@ -0,0 +1,7 @@
+= simple_format @body
+
+\----
+
+%p
+ Don't want to receive updates from GitLab administrators?
+ = link_to 'Unsubscribe', @unsubscribe_url
diff --git a/app/views/notify/send_admin_notification.text.haml b/app/views/notify/send_admin_notification.text.haml
new file mode 100644
index 0000000000..bfacbd3dcb
--- /dev/null
+++ b/app/views/notify/send_admin_notification.text.haml
@@ -0,0 +1,6 @@
+= h @body
+
+\-----
+
+Don't want to receive updates from GitLab administrators?
+Unsubscribe here: #{@unsubscribe_url}
diff --git a/app/views/notify/send_unsubscribed_notification.html.haml b/app/views/notify/send_unsubscribed_notification.html.haml
new file mode 100644
index 0000000000..9f68feeaa3
--- /dev/null
+++ b/app/views/notify/send_unsubscribed_notification.html.haml
@@ -0,0 +1,2 @@
+%p
+ You have been unsubscribed from receiving GitLab administrator notifications.
diff --git a/app/views/notify/send_unsubscribed_notification.text.haml b/app/views/notify/send_unsubscribed_notification.text.haml
new file mode 100644
index 0000000000..5edc1ddcda
--- /dev/null
+++ b/app/views/notify/send_unsubscribed_notification.text.haml
@@ -0,0 +1 @@
+You have been unsubscribed from receiving GitLab administrator notifications.
diff --git a/app/views/notify/user_deactivated_email.html.haml b/app/views/notify/user_deactivated_email.html.haml
new file mode 100644
index 0000000000..a9262cab21
--- /dev/null
+++ b/app/views/notify/user_deactivated_email.html.haml
@@ -0,0 +1,17 @@
+= email_default_heading(_('Hello %{name},') % { name: @name })
+%p
+ = _('Your account has been deactivated. You will not be able to: ')
+%ul
+ %li
+ = _('Access Git repositories or the API.')
+ %li
+ = _('Receive any notifications from GitLab.')
+ %li
+ = _('Use slash commands.')
+%p
+ - gitlab_link_start = ''.html_safe % { url: root_url }
+ - link_end = ''.html_safe
+ = _('To reactivate your account, %{gitlab_link_start}sign in to GitLab.%{link_end}').html_safe % { gitlab_link_start: gitlab_link_start, link_end: link_end}
+
+%p
+ = _('Please contact your GitLab administrator if you think this is an error.')
diff --git a/app/views/notify/user_deactivated_email.text.erb b/app/views/notify/user_deactivated_email.text.erb
new file mode 100644
index 0000000000..9e7d00f4ad
--- /dev/null
+++ b/app/views/notify/user_deactivated_email.text.erb
@@ -0,0 +1,10 @@
+<%= _('Hello %{name},') % { name: @name } %>
+
+<%= _('Your account has been deactivated. You will not be able to: ') %>
+ - <%= _('Access Git repositories or the API.') %>
+ - <%= _('Receive any notifications from GitLab.') %>
+ - <%= _('Use slash commands.') %>
+
+<%= _('To reactivate your account, sign in to GitLab at %{gitlab_url}.') % { gitlab_url: root_url } %>
+
+<%= _('Please contact your GitLab administrator if you think this is an error.') %>
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index c4de47f276..809dc3320f 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -2,16 +2,20 @@
- @content_class = "limit-container-width" unless fluid_layout
- if current_user.ldap_user?
- .gl-alert.gl-alert-info.gl-my-5
- = sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ = render 'shared/global_alert',
+ variant: :info,
+ alert_class: 'gl-my-5',
+ is_contained: true,
+ dismissible: false do
.gl-alert-body
= s_('Profiles|Some options are unavailable for LDAP accounts')
- if params[:two_factor_auth_enabled_successfully]
- .gl-alert.gl-alert-success.gl-my-5{ role: 'alert' }
- = sprite_icon('check-circle', size: 16, css_class: 'gl-alert-icon gl-alert-icon-no-title')
- %button.gl-alert-dismiss.js-close-2fa-enabled-success-alert{ type: 'button', aria: { label: _('Close') } }
- = sprite_icon('close', size: 16)
+ = render 'shared/global_alert',
+ variant: :success,
+ alert_class: 'gl-my-5',
+ is_contained: true,
+ close_button_class: 'js-close-2fa-enabled-success-alert' do
.gl-alert-body
= html_escape(_('You have set up 2FA for your account! If you lose access to your 2FA device, you can use your recovery codes to access your account. Alternatively, if you upload an SSH key, you can %{anchorOpen}use that key to generate additional recovery codes%{anchorClose}.')) % { anchorOpen: ''.html_safe % { href: help_page_path('user/profile/account/two_factor_authentication', anchor: 'generate-new-recovery-codes-using-ssh') }, anchorClose: ''.html_safe }
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 69b8d2ddaf..584bd44e38 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -11,8 +11,8 @@
%h5.gl-mt-0
= _('Add an SSH key')
%p.profile-settings-content
- - generate_link_url = help_page_path("ssh/README", anchor: 'generate-an-ssh-key-pair')
- - existing_link_url = help_page_path("ssh/README", anchor: 'see-if-you-have-an-existing-ssh-key-pair')
+ - generate_link_url = help_page_path("ssh/index", anchor: 'generate-an-ssh-key-pair')
+ - existing_link_url = help_page_path("ssh/index", anchor: 'see-if-you-have-an-existing-ssh-key-pair')
- generate_link_start = ''.html_safe % { url: generate_link_url }
- existing_link_start = ''.html_safe % { url: existing_link_url }
= _('To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}.').html_safe % { generate_link_start: generate_link_start, existing_link_start: existing_link_start, link_end: ''.html_safe }
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index 3661b93e33..7c1f28345f 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -24,7 +24,8 @@
type: type,
path: profile_personal_access_tokens_path,
token: @personal_access_token,
- scopes: @scopes
+ scopes: @scopes,
+ help_path: help_page_path('user/profile/personal_access_tokens.md', anchor: 'personal-access-token-scopes')
= render 'shared/access_tokens/table',
type: type,
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 0adad6b64a..e52a345bd8 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -128,29 +128,27 @@
= f.label :first_day_of_week, class: 'label-bold' do
= _('First day of the week')
= f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'select2'
- - if Feature.enabled?(:user_time_settings)
- .col-sm-12
- %hr
- .col-lg-4.profile-settings-sidebar
- %h4.gl-mt-0= s_('Preferences|Time preferences')
- %p= s_('Preferences|These settings will update how dates and times are displayed for you.')
- .col-lg-8
- .form-group
- %h5= s_('Preferences|Time format')
- .checkbox-icon-inline-wrapper
- - time_format_label = capture do
- = s_('Preferences|Display time in 24-hour format')
- = f.check_box :time_format_in_24h
- = f.label :time_format_in_24h do
- = time_format_label
- %h5= s_('Preferences|Time display')
- .checkbox-icon-inline-wrapper
- - time_display_label = capture do
- = s_('Preferences|Use relative times')
- = f.check_box :time_display_relative
- = f.label :time_display_relative do
- = time_display_label
- .form-text.text-muted
- = s_('Preferences|For example: 30 mins ago.')
+ .col-sm-12
+ %hr
+ .row.js-preferences-form.js-search-settings-section
+ .col-lg-4.profile-settings-sidebar#time-preferences
+ %h4.gl-mt-0
+ = s_('Preferences|Time preferences')
+ %p
+ = s_('Preferences|Configure how dates and times display for you.')
+ = succeed '.' do
+ = link_to _('Learn more'), help_page_path('user/profile/preferences', anchor: 'time-preferences'), target: '_blank'
+ .col-lg-8
+ .form-group.form-check
+ = f.check_box :time_display_relative, class: 'form-check-input'
+ = f.label :time_display_relative, class: 'form-check-label' do
+ = s_('Preferences|Use relative times')
+ .form-text.text-muted
+ = s_('Preferences|For example: 30 minutes ago.')
+ - if Feature.enabled?(:user_time_settings)
+ .form-group.form-check
+ = f.check_box :time_format_in_24h, class: 'form-check-input'
+ = f.label :time_format_in_24h, class: 'form-check-label' do
+ = s_('Preferences|Display time in 24-hour format')
#js-profile-preferences-app{ data: data_attributes }
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 411954aed6..0328fa5c28 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -72,9 +72,8 @@
.checkbox-icon-inline-wrapper
= status_form.check_box :availability, { data: { testid: "user-availability-checkbox" }, label: s_("Profiles|Busy"), wrapper_class: 'gl-mr-0 gl-font-weight-bold' }, availability["busy"], availability["not_set"]
.gl-text-gray-600.gl-ml-5= s_('Profiles|"Busy" will be shown next to your name')
- - if Feature.enabled?(:user_time_settings)
- .col-lg-12
- %hr
+ .col-lg-12
+ %hr
.row.user-time-preferences.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0= s_("Profiles|Time settings")
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 3cd571c23d..d1d6b6301b 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -1,5 +1,6 @@
+- breadcrumb_title _('Two-Factor Authentication')
- page_title _('Two-Factor Authentication'), _('Account')
-- add_to_breadcrumbs(_('Two-Factor Authentication'), profile_account_path)
+- add_to_breadcrumbs _('Account'), profile_account_path
- @content_class = "limit-container-width" unless fluid_layout
- webauthn_enabled = Feature.enabled?(:webauthn)
@@ -16,13 +17,7 @@
= _("You've already enabled two-factor authentication using one time password authenticators. In order to register a different device, you must first disable two-factor authentication.")
%p
= _('If you lose your recovery codes you can generate new ones, invalidating all previous codes.')
- %div
- = link_to _('Disable two-factor authentication'), profile_two_factor_auth_path,
- method: :delete,
- data: { confirm: webauthn_enabled ? _('Are you sure? This will invalidate your registered applications and U2F / WebAuthn devices.') : _('Are you sure? This will invalidate your registered applications and U2F devices.') },
- class: 'gl-button btn btn-danger gl-mr-3'
- = form_tag codes_profile_two_factor_auth_path, {style: 'display: inline-block', method: :post} do |f|
- = submit_tag _('Regenerate recovery codes'), class: 'gl-button btn btn-default'
+ .js-manage-two-factor-form{ data: { webauthn_enabled: webauthn_enabled, profile_two_factor_auth_path: profile_two_factor_auth_path, profile_two_factor_auth_method: 'delete', codes_profile_two_factor_auth_path: codes_profile_two_factor_auth_path, codes_profile_two_factor_auth_method: 'post' } }
- else
%p
@@ -52,6 +47,11 @@
.form-group
= label_tag :pin_code, _('Pin code'), class: "label-bold"
= text_field_tag :pin_code, nil, class: "form-control gl-form-input", required: true, data: { qa_selector: 'pin_code_field' }
+ .form-group
+ = label_tag :current_password, _('Current password'), class: 'label-bold'
+ = password_field_tag :current_password, nil, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'current_password_field' }
+ %p.form-text.text-muted
+ = _('Your current password is required to register a two-factor authenticator app.')
.gl-mt-3
= submit_tag _('Register with two-factor app'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'register_2fa_app_button' }
diff --git a/app/views/projects/_export.html.haml b/app/views/projects/_export.html.haml
index eb4630b84d..97f5cdb54e 100644
--- a/app/views/projects/_export.html.haml
+++ b/app/views/projects/_export.html.haml
@@ -16,6 +16,7 @@
%li= _('Job logs and artifacts')
%li= _('Container registry images')
%li= _('CI variables')
+ %li= _('Pipeline triggers')
%li= _('Webhooks')
%li= _('Any encrypted tokens')
- if project.export_status == :finished
diff --git a/app/views/projects/_flash_messages.html.haml b/app/views/projects/_flash_messages.html.haml
index f9222387e9..7395495b53 100644
--- a/app/views/projects/_flash_messages.html.haml
+++ b/app/views/projects/_flash_messages.html.haml
@@ -9,3 +9,4 @@
= render 'shared/auto_devops_implicitly_enabled_banner', project: project
= render_if_exists 'projects/above_size_limit_warning', project: project
= render_if_exists 'shared/shared_runners_minutes_limit', project: project, classes: [container_class, ("limit-container-width" unless fluid_layout)]
+ = render_if_exists 'projects/terraform_banner', project: project
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 26291c0358..8617249911 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -47,6 +47,10 @@
= cache_if(cache_enabled, [@project, :buttons, current_user, @notification_setting], expires_in: 1.day) do
.project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5
- if current_user
+ - if current_user.admin?
+ = link_to [:admin, @project], class: 'btn gl-button btn-icon gl-align-self-start gl-py-2! gl-mr-3', title: s_('View project in admin area'),
+ data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = sprite_icon('admin')
.gl-display-flex.gl-align-items-start.gl-mr-3
- if @notification_setting
.js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id } }
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index e6ded3ad91..2055f1c7f6 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -7,7 +7,7 @@
= _("Import project from")
.import-buttons
- if gitlab_project_import_enabled?
- .import_gitlab_project.has-tooltip{ data: { container: 'body' } }
+ .import_gitlab_project.has-tooltip{ data: { container: 'body', qa_selector: 'gitlab_import_button' } }
= link_to new_import_gitlab_project_path, class: 'gl-button btn-default btn btn_import_gitlab_project', **tracking_attrs(track_label, 'click_button', 'gitlab_export') do
.gl-button-icon
= sprite_icon('tanuki')
diff --git a/app/views/projects/_invite_members.html.haml b/app/views/projects/_invite_members_empty_project.html.haml
similarity index 82%
rename from app/views/projects/_invite_members.html.haml
rename to app/views/projects/_invite_members_empty_project.html.haml
index ab630d3450..ee2215b0fb 100644
--- a/app/views/projects/_invite_members.html.haml
+++ b/app/views/projects/_invite_members_empty_project.html.haml
@@ -6,7 +6,8 @@
.js-invite-members-trigger{ data: { variant: 'confirm',
classes: 'gl-mb-8 gl-xs-w-full',
display_text: s_('InviteMember|Invite members'),
+ trigger_source: 'project-empty-page',
event: 'click_button',
label: 'invite_members_empty_project' } }
-= render 'shared/issuable/invite_members_trigger', project: @project
+= render 'projects/invite_members_modal', project: @project
diff --git a/app/views/projects/_invite_members_modal.html.haml b/app/views/projects/_invite_members_modal.html.haml
index 00f823b901..5dd6ec0add 100644
--- a/app/views/projects/_invite_members_modal.html.haml
+++ b/app/views/projects/_invite_members_modal.html.haml
@@ -1,7 +1,8 @@
-- if can_invite_members_for_project?(project)
- .js-invite-members-modal{ data: { id: project.id,
- name: project.name,
- is_project: 'true',
- access_levels: ProjectMember.access_level_roles.to_json,
- default_access_level: Gitlab::Access::GUEST,
- help_link: help_page_url('user/permissions') } }
+- return unless can_import_members?
+
+.js-invite-members-modal{ data: { id: project.id,
+ name: project.name,
+ is_project: 'true',
+ access_levels: ProjectMember.access_level_roles.to_json,
+ default_access_level: Gitlab::Access::GUEST,
+ help_link: help_page_url('user/permissions') } }
diff --git a/app/views/projects/_merge_request_merge_checks_settings.html.haml b/app/views/projects/_merge_request_merge_checks_settings.html.haml
index 694d192d07..fbc58283cb 100644
--- a/app/views/projects/_merge_request_merge_checks_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_checks_settings.html.haml
@@ -8,7 +8,7 @@
= form.label :only_allow_merge_if_pipeline_succeeds, class: 'form-check-label' do
= s_('ProjectSettings|Pipelines must succeed')
.text-secondary
- - configuring_pipelines_for_merge_requests_help_link_url = help_page_path('ci/merge_request_pipelines/index.md', anchor: 'configuring-pipelines-for-merge-requests')
+ - configuring_pipelines_for_merge_requests_help_link_url = help_page_path('ci/pipelines/merge_request_pipelines.md', anchor: 'configure-pipelines-for-merge-requests')
- configuring_pipelines_for_merge_requests_help_link_start = ''.html_safe % { url: configuring_pipelines_for_merge_requests_help_link_url }
= s_('ProjectSettings|To enable this feature, configure pipelines. %{link_start}How to configure pipelines for merge requests?%{link_end}').html_safe % { link_start: configuring_pipelines_for_merge_requests_help_link_start, link_end: ''.html_safe }
.form-check.mb-2
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index 66fc313213..55696337bc 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -23,7 +23,7 @@
display_path: true,
extra_group: namespace_id),
{},
- { class: 'select2 js-select-namespace qa-project-namespace-select block-truncated', data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_path", track_value: "" }})
+ { class: 'select2 js-select-namespace qa-project-namespace-select block-truncated', data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_path", track_value: "", qa_selector: "select_namespace_dropdown" }})
- else
.input-group-prepend.static-namespace.flex-shrink-0.has-tooltip{ title: user_url(current_user.username) + '/' }
diff --git a/app/views/projects/_remove.html.haml b/app/views/projects/_remove.html.haml
index e991a9b0ec..cb0ec9f19c 100644
--- a/app/views/projects/_remove.html.haml
+++ b/app/views/projects/_remove.html.haml
@@ -3,7 +3,7 @@
.sub-section
%h4.danger-title= _('Delete project')
%p
- %strong= _('Deleting the project will delete its repository and all related resources including issues, merge requests, etc.')
+ %strong= _('Deleting the project will delete its repository and all related resources, including issues and merge requests.')
= link_to _('Learn more.'), help_page_path('user/project/settings/index', anchor: 'removing-a-fork-relationship'), target: '_blank', rel: 'noopener noreferrer'
%p
%strong= _('Deleted projects cannot be restored!')
diff --git a/app/views/projects/_terraform_banner.html.haml b/app/views/projects/_terraform_banner.html.haml
new file mode 100644
index 0000000000..a30c4a2d62
--- /dev/null
+++ b/app/views/projects/_terraform_banner.html.haml
@@ -0,0 +1,5 @@
+- @content_class = "container-limited limit-container-width" unless fluid_layout
+
+- if show_terraform_banner?(project)
+ .container-fluid{ class: @content_class }
+ .js-terraform-notification{ data: { project_id: project.id } }
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index cba63d5e6d..8fe9c9e5c5 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,5 +1,4 @@
- page_title _("Blame"), @blob.path, @ref
-- link_icon = sprite_icon("link", size: 12)
#blob-content-holder.tree-holder
= render "projects/blob/breadcrumb", blob: @blob, blame: true
@@ -48,8 +47,8 @@
%td.line-numbers
- (current_line...(current_line + line_count)).each do |i|
%a.diff-line-num.gl-justify-content-end{ href: "#L#{i}", id: "L#{i}", 'data-line-number' => i, class: "gl-display-flex!" }
- = link_icon
- = i
+ .file-line-num
+ = i
\
%td.lines
diff --git a/app/views/projects/blob/_pipeline_tour_success.html.haml b/app/views/projects/blob/_pipeline_tour_success.html.haml
index ef1fe25ba1..8f1c2f9316 100644
--- a/app/views/projects/blob/_pipeline_tour_success.html.haml
+++ b/app/views/projects/blob/_pipeline_tour_success.html.haml
@@ -1,6 +1,6 @@
.js-success-pipeline-modal{ data: { 'commit-cookie': suggest_pipeline_commit_cookie_name,
'go-to-pipelines-path': project_pipelines_path(@project),
'project-merge-requests-path': project_merge_requests_path(@project),
- 'example-link': help_page_path('ci/examples/README.md', anchor: 'gitlab-cicd-examples'),
+ 'example-link': help_page_path('ci/examples/index.md', anchor: 'gitlab-cicd-examples'),
'code-quality-link': help_page_path('user/project/merge_requests/code_quality'),
'human-access': @project.team.human_max_access(current_user&.id) } }
diff --git a/app/views/projects/blob/viewers/_csv.html.haml b/app/views/projects/blob/viewers/_csv.html.haml
new file mode 100644
index 0000000000..3a58bc9902
--- /dev/null
+++ b/app/views/projects/blob/viewers/_csv.html.haml
@@ -0,0 +1 @@
+.file-content#js-csv-viewer{ data: { data: viewer.blob.data } }
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 6cb2c435a3..6de50d4872 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -2,10 +2,10 @@
- default_ref = params[:ref] || @project.default_branch
- if @error
- .gl-alert.gl-alert-danger
- = sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
- = sprite_icon('close', size: 16, css_class: 'gl-icon')
+ = render 'shared/global_alert',
+ variant: :danger,
+ close_button_class: 'js-close',
+ is_contained: true do
.gl-alert-body
= @error
%h3.page-title
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index c1f6cfc40c..3c9762e200 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -1,6 +1,6 @@
- page_title _("Value Stream Analytics")
- add_page_specific_style 'page_bundles/cycle_analytics'
- svgs = { empty_state_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_data_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_access_svg_path: image_path("illustrations/analytics/no-access.svg") }
-- initial_data = { request_path: project_cycle_analytics_path(@project), full_path: @project.full_path }.merge!(svgs)
+- initial_data = { project_id: @project.id, group_path: @project.group&.path, request_path: project_cycle_analytics_path(@project), full_path: @project.full_path }.merge!(svgs)
#js-cycle-analytics{ data: initial_data }
diff --git a/app/views/projects/default_branch/_show.html.haml b/app/views/projects/default_branch/_show.html.haml
index 9e9fc08dac..68ca318e88 100644
--- a/app/views/projects/default_branch/_show.html.haml
+++ b/app/views/projects/default_branch/_show.html.haml
@@ -17,7 +17,7 @@
- else
.form-group
= f.label :default_branch, "Default branch", class: 'label-bold'
- = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'})
+ = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide', data: { qa_selector: 'default_branch_dropdown' }})
.form-group
.form-check
@@ -28,4 +28,4 @@
= _("When merge requests and commits in the default branch close, any issues they reference also close.")
= link_to sprite_icon('question-o'), help_page_path('user/project/issues/managing_issues.md', anchor: 'disabling-automatic-issue-closing'), target: '_blank'
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml
index 30b0631b46..1d9b1b13d5 100644
--- a/app/views/projects/diffs/_warning.html.haml
+++ b/app/views/projects/diffs/_warning.html.haml
@@ -1,9 +1,8 @@
-.gl-alert.gl-alert-warning.gl-mb-5
- %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
- = sprite_icon('close', size: 16, css_class: 'gl-icon')
- = sprite_icon('warning', size: 16, css_class: 'gl-icon gl-alert-icon')
- %h4.gl-alert-title
- = _("Too many changes to show.")
+= render 'shared/global_alert',
+ title: _('Too many changes to show.'),
+ variant: :warning,
+ is_contained: true,
+ alert_class: 'gl-mb-5' do
.gl-alert-body
= html_escape(_("To preserve performance only %{strong_open}%{display_size} of %{real_size}%{strong_close} files are displayed.")) % { display_size: diff_files.size, real_size: diff_files.real_size, strong_open: ''.html_safe, strong_close: ''.html_safe }
.gl-alert-actions
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index d9055f01dd..70d86e7928 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -7,7 +7,7 @@
= render "home_panel"
= render "archived_notice", project: @project
-= render "invite_members" if can_import_members?
+= render 'invite_members_empty_project' if can_import_members?
%h4.gl-mt-0.gl-mb-3
= _('The repository for this project is empty')
diff --git a/app/views/projects/feature_flags/edit.html.haml b/app/views/projects/feature_flags/edit.html.haml
index 1549f5cf6d..f71c97c2a8 100644
--- a/app/views/projects/feature_flags/edit.html.haml
+++ b/app/views/projects/feature_flags/edit.html.haml
@@ -8,9 +8,6 @@
project_id: @project.id,
feature_flags_path: project_feature_flags_path(@project),
environments_endpoint: search_project_environments_path(@project, format: :json),
- user_callouts_path: user_callouts_path,
- user_callout_id: UserCalloutsHelper::FEATURE_FLAGS_NEW_VERSION,
- show_user_callout: show_feature_flags_new_version?.to_s,
strategy_type_docs_page_path: help_page_path('operations/feature_flags', anchor: 'feature-flag-strategies'),
environments_scope_docs_path: help_page_path('ci/environments/index.md', anchor: 'scoping-environments-with-specs'),
feature_flag_issues_endpoint: feature_flag_issues_links_endpoint(@project, @feature_flag, current_user) } }
diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml
index 0c15796b66..30e2e9f19d 100644
--- a/app/views/projects/forks/error.html.haml
+++ b/app/views/projects/forks/error.html.haml
@@ -1,10 +1,11 @@
- page_title _("Fork project")
- if @forked_project && !@forked_project.saved?
- .gl-alert.gl-alert-danger.gl-mt-5
- = sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon')
- %h4.gl-alert-title
- = sprite_icon('fork')
- = _("Fork Error!")
+ = render 'shared/global_alert',
+ title: _('Fork Error!'),
+ variant: :danger,
+ alert_class: 'gl-mt-5',
+ is_contained: true,
+ dismissible: false do
.gl-alert-body
%p
= _("You tried to fork %{link_to_the_project} but it failed for the following reason:").html_safe % { link_to_the_project: link_to_project(@project) }
@@ -14,9 +15,9 @@
–
- error = @forked_project.errors.full_messages.first
- if error.include?("already been taken")
- = _("Name has already been taken")
+ = _('Name has already been taken')
- else
= error
.gl-alert-actions
- = link_to _("Try to fork again"), new_project_fork_path(@project), title: _("Fork"), class: "btn gl-alert-action btn-info btn-md gl-button"
+ = link_to _('Try to fork again'), new_project_fork_path(@project), title: _("Fork"), class: "btn gl-alert-action btn-info btn-md gl-button"
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 0716eda79a..8848fbae9c 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -1,6 +1,6 @@
- page_title s_("ForkProject|Fork project")
-- if Feature.enabled?(:fork_project_form)
+- if Feature.enabled?(:fork_project_form, @project, default_enabled: :yaml)
#fork-groups-mount-element{ data: { fork_illustration: image_path('illustrations/project-create-new-sm.svg'),
endpoint: new_project_fork_path(@project, format: :json),
new_group_path: new_group_path,
diff --git a/app/views/projects/import/jira/show.html.haml b/app/views/projects/import/jira/show.html.haml
index 3c0664e4d5..29296ce23c 100644
--- a/app/views/projects/import/jira/show.html.haml
+++ b/app/views/projects/import/jira/show.html.haml
@@ -1,7 +1,7 @@
.js-jira-import-root{ data: { project_path: @project.full_path,
issues_path: project_issues_path(@project),
jira_integration_path: edit_project_service_path(@project, :jira),
- is_jira_configured: @project.jira_service&.active? && @project.jira_service&.valid_connection?.to_s,
+ is_jira_configured: @project.jira_integration&.configured?.to_s,
in_progress_illustration: image_path('illustrations/export-import.svg'),
project_id: @project.id,
setup_illustration: image_path('illustrations/manual_action.svg') } }
diff --git a/app/views/projects/issues/_alert_moved_from_service_desk.html.haml b/app/views/projects/issues/_alert_moved_from_service_desk.html.haml
index 9b142b0857..662270fb8e 100644
--- a/app/views/projects/issues/_alert_moved_from_service_desk.html.haml
+++ b/app/views/projects/issues/_alert_moved_from_service_desk.html.haml
@@ -2,9 +2,10 @@
- service_desk_link_url = help_page_path('user/project/service_desk')
- service_desk_link_start = ''.html_safe % { url: service_desk_link_url }
-.hide.gl-alert.gl-alert-warning.js-alert-moved-from-service-desk-warning.gl-mt-5{ role: 'alert' }
- = sprite_icon('warning', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
- = sprite_icon('close', css_class: 'gl-icon')
+= render 'shared/global_alert',
+ variant: :warning,
+ is_contained: true,
+ close_button_class: 'js-close',
+ alert_class: 'hide js-alert-moved-from-service-desk-warning gl-mt-5' do
.gl-alert-body.gl-mr-3
= s_('This project does not have %{service_desk_link_start}Service Desk%{service_desk_link_end} enabled, so the user who created the issue will no longer receive email notifications about new activity.').html_safe % { service_desk_link_start: service_desk_link_start, service_desk_link_end: ''.html_safe }
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 3e8442eee8..ecf10cd482 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -9,7 +9,7 @@
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} issues")
.js-jira-issues-import-status{ data: { can_edit: can?(current_user, :admin_project, @project).to_s,
- is_jira_configured: @project.jira_service.present?.to_s,
+ is_jira_configured: @project.jira_integration.present?.to_s,
issues_path: project_issues_path(@project),
project_path: @project.full_path } }
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index a465f59c55..6c6f98e0b2 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -4,4 +4,4 @@
- page_title "#{@issue.title} (#{@issue.to_reference})", _("Issues")
= render 'projects/issuable/show', issuable: @issue, api_awards_path: award_emoji_issue_api_path(@issue)
-= render 'shared/issuable/invite_members_trigger', project: @project
+= render 'projects/invite_members_modal', project: @project
diff --git a/app/views/projects/mattermosts/_no_teams.html.haml b/app/views/projects/mattermosts/_no_teams.html.haml
index f375222e31..adef11f315 100644
--- a/app/views/projects/mattermosts/_no_teams.html.haml
+++ b/app/views/projects/mattermosts/_no_teams.html.haml
@@ -15,4 +15,4 @@
and try again.
%hr
.clearfix
- = link_to 'Go back', edit_project_service_path(@project, @service), class: 'gl-button btn btn-lg float-right'
+ = link_to 'Go back', edit_project_service_path(@project, @integration), class: 'gl-button btn btn-lg float-right'
diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml
index ea04a55a77..4109fdfc13 100644
--- a/app/views/projects/mattermosts/_team_selection.html.haml
+++ b/app/views/projects/mattermosts/_team_selection.html.haml
@@ -2,7 +2,7 @@
This service will be installed on the Mattermost instance at
%strong= link_to Gitlab.config.mattermost.host, Gitlab.config.mattermost.host
%hr
-= form_for(:mattermost, method: :post, url: project_mattermost_path(@project), html: { class: 'js-requires-input'} ) do |f|
+= form_for(:mattermost, method: :post, url: project_mattermost_path(@project), html: { class: 'js-requires-input' }) do |f|
%h4 Team
%p
= @teams.one? ? 'The team' : 'Select the team'
@@ -42,5 +42,5 @@
%hr
.clearfix
.float-right
- = link_to 'Cancel', edit_project_service_path(@project, @service), class: 'gl-button btn btn-lg'
+ = link_to 'Cancel', edit_project_service_path(@project, @integration), class: 'gl-button btn btn-lg'
= f.submit 'Install', class: 'gl-button btn btn-success btn-lg'
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 691ce8dc5f..c4ee522bfa 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -96,5 +96,5 @@
#js-review-bar
-= render 'shared/issuable/invite_members_trigger', project: @project
+= render 'projects/invite_members_modal', project: @project
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index dfb9defb91..5f2057df4a 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,27 +1,25 @@
= form_for [@project, @milestone],
html: { class: 'milestone-form common-note-form js-quick-submit js-requires-input' } do |f|
= form_errors(@milestone)
- .row
- .col-md-6
- .form-group.row
- .col-form-label.col-sm-2
- = f.label :title, _('Title')
- .col-sm-10
- = f.text_field :title, maxlength: 255, class: 'form-control gl-form-input', data: { qa_selector: 'milestone_title_field' }, required: true, autofocus: true
- .form-group.row.milestone-description
- .col-form-label.col-sm-2
- = f.label :description, _('Description')
- .col-sm-10
- = render layout: 'shared/md_preview', locals: { url: preview_markdown_path(@project) } do
- = render 'shared/zen', f: f, attr: :description,
- classes: 'note-textarea',
- qa_selector: 'milestone_description_field',
- supports_autocomplete: true,
- placeholder: _('Write milestone description...')
- = render 'shared/notes/hints'
- .clearfix
- .error-alert
- = render 'shared/milestones/form_dates', f: f
+ .form-group.row
+ .col-form-label.col-sm-2
+ = f.label :title, _('Title')
+ .col-sm-10
+ = f.text_field :title, maxlength: 255, class: 'form-control gl-form-input', data: { qa_selector: 'milestone_title_field' }, required: true, autofocus: true
+ = render 'shared/milestones/form_dates', f: f
+ .form-group.row.milestone-description
+ .col-form-label.col-sm-2
+ = f.label :description, _('Description')
+ .col-sm-10
+ = render layout: 'shared/md_preview', locals: { url: preview_markdown_path(@project) } do
+ = render 'shared/zen', f: f, attr: :description,
+ classes: 'note-textarea',
+ qa_selector: 'milestone_description_field',
+ supports_autocomplete: true,
+ placeholder: _('Write milestone description...')
+ = render 'shared/notes/hints'
+ .clearfix
+ .error-alert
.form-actions
- if @milestone.new_record?
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 99fe64723d..dbde3346b8 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -12,19 +12,17 @@
= render_if_exists 'shared/milestones/burndown', milestone: @milestone, project: @project
- if can?(current_user, :read_issue, @project) && @milestone.total_issues_count == 0
- .gl-alert.gl-alert-info.gl-mt-3.gl-mb-5{ data: { testid: 'no-issues-alert' } }
- .gl-alert-container
- = sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- .gl-alert-content
- .gl-alert-body
- %span= _('Assign some issues to this milestone.')
-- elsif @milestone.complete? && @milestone.active?
- .gl-alert.gl-alert-success.gl-mt-3.gl-mb-5{ data: { testid: 'all-issues-closed-alert' } }
- .gl-alert-container
- = sprite_icon('check-circle', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- .gl-alert-content
- .gl-alert-body
- %span= _('All issues for this milestone are closed. You may close this milestone now.')
+ = render 'shared/global_alert',
+ variant: :info,
+ dismissible: false,
+ is_contained: true,
+ alert_data: { testid: 'no-issues-alert' },
+ alert_class: 'gl-mt-3 gl-mb-5' do
+ .gl-alert-body
+ = _('Assign some issues to this milestone.')
+- else
+ = render 'shared/milestones/milestone_complete_alert', milestone: @milestone do
+ = _('All issues for this milestone are closed. You may close this milestone now.')
= render 'shared/milestones/tabs', milestone: @milestone
= render 'shared/milestones/sidebar', milestone: @milestone, project: @project, affix_offset: 153
diff --git a/app/views/projects/packages/packages/show.html.haml b/app/views/projects/packages/packages/show.html.haml
index aeca3f5b3e..76eb22109a 100644
--- a/app/views/projects/packages/packages/show.html.haml
+++ b/app/views/projects/packages/packages/show.html.haml
@@ -6,23 +6,7 @@
.row
.col-12
- #js-vue-packages-detail{ data: { package: package_from_presenter(@package),
- can_delete: can?(current_user, :destroy_package, @project).to_s,
- svg_path: image_path('illustrations/no-packages.svg'),
- npm_path: package_registry_instance_url(:npm),
- npm_help_path: help_page_path('user/packages/npm_registry/index'),
- maven_path: package_registry_project_url(@project.id, :maven),
- maven_help_path: help_page_path('user/packages/maven_repository/index'),
- conan_path: package_registry_project_url(@project.id, :conan),
- conan_help_path: help_page_path('user/packages/conan_repository/index'),
- nuget_path: nuget_package_registry_url(@project.id),
- nuget_help_path: help_page_path('user/packages/nuget_repository/index'),
- pypi_path: pypi_registry_url(@project.id),
- pypi_setup_path: package_registry_project_url(@project.id, :pypi),
- pypi_help_path: help_page_path('user/packages/pypi_repository/index'),
- composer_path: composer_registry_url(@project&.group&.id),
- composer_help_path: help_page_path('user/packages/composer_repository/index'),
- project_name: @project.name,
- project_list_url: project_packages_path(@project),
- group_list_url: @project.group ? group_packages_path(@project.group) : '',
- composer_config_repository_name: composer_config_repository_name(@project.group&.id)} }
+ - if Feature.enabled?(:package_details_apollo)
+ #js-vue-packages-detail-new{ data: package_details_data(@project) }
+ - else
+ #js-vue-packages-detail{ data: package_details_data(@project, @package) }
diff --git a/app/views/projects/pages/_pages_settings.html.haml b/app/views/projects/pages/_pages_settings.html.haml
index 483f192109..2db44528d5 100644
--- a/app/views/projects/pages/_pages_settings.html.haml
+++ b/app/views/projects/pages/_pages_settings.html.haml
@@ -1,5 +1,8 @@
= form_for @project, url: project_pages_path(@project), html: { class: 'inline', title: pages_https_only_title } do |f|
- = render_if_exists 'shared/pages/max_pages_size_input', form: f
+ - if can?(current_user, :update_max_pages_size)
+ = render_if_exists 'shared/pages/max_pages_size_input', form: f
+ .gl-mt-3
+ = f.submit s_('GitLabPages|Save changes'), class: 'btn btn-confirm gl-button'
- if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https
@@ -15,4 +18,4 @@
= s_("GitLabPages|When enabled, all attempts to visit your website through HTTP are automatically redirected to HTTPS using a response with status code 301. Requires a valid certificate for all domains. %{docs_link_start}Learn more.%{link_end}").html_safe % { docs_link_start: docs_link_start, link_end: link_end }
.gl-mt-3
- = f.submit s_('GitLabPages|Save'), class: 'btn btn-confirm gl-button'
+ = f.submit s_('GitLabPages|Save changes'), class: 'btn btn-confirm gl-button'
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index e56a240c48..c1d4899250 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -78,7 +78,7 @@
= build_summary(build)
#js-tab-dag.tab-pane
- #js-pipeline-dag-vue{ data: { pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid, empty_svg_path: image_path('illustrations/empty-state/empty-dag-md.svg'), about_dag_doc_path: help_page_path('ci/directed_acyclic_graph/index.md'), dag_doc_path: help_page_path('ci/yaml/README.md', anchor: 'needs')} }
+ #js-pipeline-dag-vue{ data: { pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid, empty_svg_path: image_path('illustrations/empty-state/empty-dag-md.svg'), about_dag_doc_path: help_page_path('ci/directed_acyclic_graph/index.md'), dag_doc_path: help_page_path('ci/yaml/index.md', anchor: 'needs')} }
#js-tab-tests.tab-pane
#js-pipeline-tests-detail{ data: { summary_endpoint: summary_project_pipeline_tests_path(@project, @pipeline, format: :json),
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 9669b2e72d..ae76d4905e 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -22,7 +22,7 @@
"ci-lint-path" => can?(current_user, :create_pipeline, @project) && project_ci_lint_path(@project),
"reset-cache-path" => can?(current_user, :admin_pipeline, @project) && reset_cache_project_settings_ci_cd_path(@project),
"has-gitlab-ci" => has_gitlab_ci?(@project).to_s,
- "add-ci-yml-path" => can?(current_user, :create_pipeline, @project) && @project.present(current_user: current_user).add_ci_yml_path,
- "suggested-ci-templates" => experiment_suggested_ci_templates.to_json,
+ "pipeline-editor-path" => can?(current_user, :create_pipeline, @project) && project_ci_pipeline_editor_path(@project),
+ "suggested-ci-templates" => suggested_ci_templates.to_json,
"code-quality-page-path" => @project.present(current_user: current_user).add_code_quality_ci_yml_path,
"ci-runner-settings-path" => project_settings_ci_cd_path(@project, ci_runner_templates: true, anchor: 'js-runners-settings') } }
diff --git a/app/views/projects/prometheus/metrics/edit.html.haml b/app/views/projects/prometheus/metrics/edit.html.haml
index 15a9c922ca..d308824571 100644
--- a/app/views/projects/prometheus/metrics/edit.html.haml
+++ b/app/views/projects/prometheus/metrics/edit.html.haml
@@ -1,6 +1,6 @@
- add_to_breadcrumbs _("Settings"), edit_project_path(@project)
- add_to_breadcrumbs _("Integrations"), project_settings_integrations_path(@project)
-- add_to_breadcrumbs "Prometheus", edit_project_service_path(@project, PrometheusService)
+- add_to_breadcrumbs "Prometheus", edit_project_service_path(@project, ::Integrations::Prometheus)
- breadcrumb_title s_('Metrics|Edit metric')
- page_title @metric.title, s_('Metrics|Edit metric')
= render 'form', project: @project, metric: @metric
diff --git a/app/views/projects/prometheus/metrics/new.html.haml b/app/views/projects/prometheus/metrics/new.html.haml
index fa925d090c..8415ec9ee4 100644
--- a/app/views/projects/prometheus/metrics/new.html.haml
+++ b/app/views/projects/prometheus/metrics/new.html.haml
@@ -1,6 +1,6 @@
- add_to_breadcrumbs _("Settings"), edit_project_path(@project)
- add_to_breadcrumbs _("Integrations"), project_settings_integrations_path(@project)
-- add_to_breadcrumbs "Prometheus", edit_project_service_path(@project, PrometheusService)
+- add_to_breadcrumbs "Prometheus", edit_project_service_path(@project, ::Integrations::Prometheus)
- breadcrumb_title s_('Metrics|New metric')
- page_title s_('Metrics|New metric')
= render 'form', project: @project, metric: @metric
diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
index ad131b2292..57fc9a16c0 100644
--- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
@@ -10,7 +10,7 @@
.col-md-10
= render partial: "projects/protected_branches/shared/dropdown", locals: { f: f }
.form-text.text-muted
- - wildcards_url = help_page_url('user/project/protected_branches', anchor: 'wildcard-protected-branches')
+ - wildcards_url = help_page_url('user/project/protected_branches', anchor: 'configure-multiple-protected-branches-by-using-a-wildcard')
- wildcards_link_start = ''.html_safe % { url: wildcards_url }
= (s_("ProtectedBranch|%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported.") % { wildcards_link_start: wildcards_link_start, wildcards_link_end: '', code_tag_start: '', code_tag_end: '
' }).html_safe
.form-group.row
diff --git a/app/views/projects/protected_branches/shared/_index.html.haml b/app/views/projects/protected_branches/shared/_index.html.haml
index 08246a173d..2e9a9357fb 100644
--- a/app/views/projects/protected_branches/shared/_index.html.haml
+++ b/app/views/projects/protected_branches/shared/_index.html.haml
@@ -3,19 +3,16 @@
%section.settings.no-animate#js-protected-branches-settings{ class: ('expanded' if expanded), data: { qa_selector: 'protected_branches_settings_content' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
- Protected branches
+ = s_("ProtectedBranch|Protected branches")
%button.btn.gl-button.btn-default.js-settings-toggle.qa-expand-protected-branches{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
- Keep stable branches secure, and force developers to use merge requests. #{link_to "What are protected branches?", help_page_path("user/project/protected_branches")}
+ = s_("ProtectedBranch|Keep stable branches secure and force developers to use merge requests.")
+ = link_to s_("ProtectedBranch|What are protected branches?"), help_page_path("user/project/protected_branches")
.settings-content
%p
- By default, protected branches protect your code and:
- %ul
- %li Allow only users with Maintainer #{link_to "permissions", help_page_path("user/permissions")} to create new protected branches.
- %li Allow only users with Maintainer permissions to push code.
- %li Prevent anyone from #{link_to "force-pushing", help_page_path('topics/git/git_rebase', anchor: 'force-push')} to the branch.
- %li Prevent anyone from deleting the branch.
+ = s_("ProtectedBranch|By default, protected branches restrict who can modify the branch.")
+ = link_to s_("ProtectedBranch|Learn more."), help_page_path("user/project/protected_branches", anchor: "who-can-modify-a-protected-branch")
- if can? current_user, :admin_project, @project
= content_for :create_protected_branch
diff --git a/app/views/projects/protected_tags/shared/_index.html.haml b/app/views/projects/protected_tags/shared/_index.html.haml
index aab4d36660..fe63f92178 100644
--- a/app/views/projects/protected_tags/shared/_index.html.haml
+++ b/app/views/projects/protected_tags/shared/_index.html.haml
@@ -3,18 +3,16 @@
%section.settings.no-animate#js-protected-tags-settings{ class: ('expanded' if expanded), data: { qa_selector: 'protected_tag_settings_content' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
- Protected tags
+ = s_("ProtectedTag|Protected tags")
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
- Limit access to creating and updating tags. #{link_to "What are protected tags?", help_page_path("user/project/protected_tags")}
+ = s_("ProtectedTag|Limit access to creating and updating tags.")
+ = link_to s_("ProtectedTag|What are protected tags?"), help_page_path("user/project/protected_tags")
.settings-content
%p
- By default, protected tags protect your code and:
- %ul
- %li Allow only users with Maintainer #{link_to "permissions", help_page_path("user/permissions")} to create tags.
- %li Prevent anyone from updating tags.
- %li Prevent anyone from deleting tags.
+ = s_("ProtectedTag|By default, protected branches restrict who can modify the tag.")
+ = link_to s_("ProtectedTag|Learn more."), help_page_path("user/project/protected_tags", anchor: "who-can-modify-a-protected-tag")
- if can? current_user, :admin_project, @project
= yield :create_protected_tag
diff --git a/app/views/projects/registry/settings/_index.haml b/app/views/projects/registry/settings/_index.haml
deleted file mode 100644
index a4d4a1bb2d..0000000000
--- a/app/views/projects/registry/settings/_index.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-#js-registry-settings{ data: { project_id: @project.id,
- project_path: @project.full_path,
- cadence_options: cadence_options.to_json,
- keep_n_options: keep_n_options.to_json,
- older_than_options: older_than_options.to_json,
- is_admin: current_user&.admin.to_s,
- admin_settings_path: ci_cd_admin_application_settings_path(anchor: 'js-registry-settings'),
- enable_historic_entries: container_expiration_policies_historic_entry_enabled?(@project).to_s,
- tags_regex_help_page_path: help_page_path('user/packages/container_registry/index', anchor: 'regex-pattern-examples') } }
diff --git a/app/views/projects/releases/index.html.haml b/app/views/projects/releases/index.html.haml
index 4d5b8cc80f..9ddf2201fa 100644
--- a/app/views/projects/releases/index.html.haml
+++ b/app/views/projects/releases/index.html.haml
@@ -1,3 +1,5 @@
- page_title _('Releases')
+- if use_startup_query_for_index_page?
+ - add_page_startup_graphql_call('releases/all_releases', index_page_startup_query_variables)
#js-releases-page{ data: data_for_releases_page }
diff --git a/app/views/projects/runners/_index.html.haml b/app/views/projects/runners/_settings.html.haml
similarity index 100%
rename from app/views/projects/runners/_index.html.haml
rename to app/views/projects/runners/_settings.html.haml
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 210cc41400..eb376ff796 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -2,22 +2,26 @@
= _('Specific runners')
.bs-callout.help-callout
- = _('These runners are specific to this project.')
- %hr
- = render partial: 'ci/runner/how_to_setup_runner_automatically',
- locals: { type: 'specific',
- clusters_path: project_clusters_path(@project) }
- - if params[:ci_runner_templates]
+ - if valid_runner_registrars.include?('project')
+ = _('These runners are specific to this project.')
%hr
- = render partial: 'ci/runner/setup_runner_in_aws',
- locals: { registration_token: @project.runners_token }
- %hr
- = render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: @project.runners_token,
- type: 'specific',
- reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path,
- project_path: @project.path_with_namespace,
- group_path: '' }
+ = render partial: 'ci/runner/how_to_setup_runner_automatically',
+ locals: { type: s_('Runners|specific'),
+ clusters_path: project_clusters_path(@project) }
+ - if params[:ci_runner_templates]
+ %hr
+ = render partial: 'ci/runner/setup_runner_in_aws',
+ locals: { registration_token: @project.runners_token }
+ %hr
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: @project.runners_token,
+ type: s_('Runners|specific'),
+ reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path,
+ project_path: @project.path_with_namespace,
+ group_path: '' }
+ - else
+ = _('Please contact an admin to register runners.')
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'runner-registration'), target: '_blank', rel: 'noopener noreferrer'
%hr
diff --git a/app/views/projects/security/configuration/show.html.haml b/app/views/projects/security/configuration/show.html.haml
index 4d6feb9de6..d4a85893fa 100644
--- a/app/views/projects/security/configuration/show.html.haml
+++ b/app/views/projects/security/configuration/show.html.haml
@@ -1,4 +1,6 @@
- breadcrumb_title _("Security Configuration")
- page_title _("Security Configuration")
+- redesign_enabled = ::Feature.enabled?(:security_configuration_redesign, @project, default_enabled: :yaml)
+- @content_class = "limit-container-width" unless fluid_layout || !redesign_enabled
#js-security-configuration-static{ data: { project_path: @project.full_path, upgrade_path: security_upgrade_path } }
diff --git a/app/views/projects/settings/access_tokens/index.html.haml b/app/views/projects/settings/access_tokens/index.html.haml
index 1bf252b628..52ef2e7d1e 100644
--- a/app/views/projects/settings/access_tokens/index.html.haml
+++ b/app/views/projects/settings/access_tokens/index.html.haml
@@ -33,12 +33,17 @@
= render 'shared/access_tokens/form',
type: type,
path: project_settings_access_tokens_path(@project),
+ project: @project,
token: @project_access_token,
scopes: @scopes,
- prefix: :project_access_token
+ access_levels: ProjectMember.access_level_roles,
+ default_access_level: Gitlab::Access::MAINTAINER,
+ prefix: :project_access_token,
+ help_path: help_page_path('user/project/settings/project_access_tokens', anchor: 'limiting-scopes-of-a-project-access-token')
= render 'shared/access_tokens/table',
active_tokens: @active_project_access_tokens,
+ project: @project,
type: type,
type_plural: type_plural,
revoke_route_helper: ->(token) { revoke_namespace_project_settings_access_token_path(id: token) },
diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
index 68e4bed8b9..8563f28eb3 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -6,7 +6,7 @@
- kubernetes_cluster_path = help_page_path('user/project/clusters/index')
- kubernetes_cluster_link_start = link_start % { url: kubernetes_cluster_path }
-- base_domain_path = help_page_path('user/project/clusters/index', anchor: 'base-domain')
+- base_domain_path = help_page_path('user/project/clusters/gitlab_managed_clusters', anchor: 'base-domain')
- base_domain_link_start = link_start % { url: base_domain_path }
.row
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index c89c9879f4..a91c12d01a 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -10,7 +10,7 @@
%strong= _("Public pipelines")
.form-text.text-muted
= _("Allow public access to pipelines and job details, including output logs and artifacts.")
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'visibility-of-pipelines'), target: '_blank'
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'change-which-users-can-view-your-pipelines'), target: '_blank'
.form-group
.form-check
@@ -36,7 +36,7 @@
= f.text_field :ci_config_path, class: 'form-control', placeholder: '.gitlab-ci.yml'
%p.form-text.text-muted
= html_escape(_("The name of the CI/CD configuration file. A path relative to the root directory is optional (for example %{code_open}my/path/.myfile.yml%{code_close}).")) % { code_open: ''.html_safe, code_close: '
'.html_safe }
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'custom-cicd-configuration-file'), target: '_blank'
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'specify-a-custom-cicd-configuration-file'), target: '_blank'
%hr
.form-group
@@ -44,7 +44,7 @@
= _("Git strategy")
%p
= _("Choose which Git strategy to use when fetching the project.")
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'git-strategy'), target: '_blank'
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'choose-the-default-git-strategy'), target: '_blank'
.form-check
= f.radio_button :build_allow_git_fetch, 'false', { class: 'form-check-input' }
= f.label :build_allow_git_fetch_false, class: 'form-check-label' do
@@ -66,7 +66,7 @@
= form.number_field :default_git_depth, { class: 'form-control gl-form-input', min: 0, max: 1000 }
%p.form-text.text-muted
= html_escape(_('The number of changes to fetch from GitLab when cloning a repository. Lower values can speed up pipeline execution. Set to %{code_open}0%{code_close} or blank to fetch all branches and tags for each job')) % { code_open: ''.html_safe, code_close: '
'.html_safe }
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'git-shallow-clone'), target: '_blank'
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'limit-the-number-of-changes-fetched-during-clone'), target: '_blank'
%hr
.form-group
@@ -74,7 +74,7 @@
= f.text_field :build_timeout_human_readable, class: 'form-control gl-form-input'
%p.form-text.text-muted
= html_escape(_('Jobs fail if they run longer than the timeout time. Input value is in seconds by default. Human readable input is also accepted, for example %{code_open}1 hour%{code_close}.')) % { code_open: ''.html_safe, code_close: '
'.html_safe }
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'timeout'), target: '_blank'
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'set-a-limit-for-how-long-jobs-can-run'), target: '_blank'
- if can?(current_user, :update_max_artifacts_size, @project)
.form-group
@@ -94,7 +94,7 @@
.input-group-text /
%p.form-text.text-muted
= html_escape(_('The regular expression used to find test coverage output in the job log. For example, use %{regex} for Simplecov (Ruby). Leave blank to disable.')) % { regex: '\(\d+.\d+%\)
'.html_safe }
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'test-coverage-parsing'), target: '_blank'
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'add-test-coverage-results-to-a-merge-request'), target: '_blank'
= f.submit _('Save changes'), class: "btn gl-button btn-confirm", data: { qa_selector: 'save_general_pipelines_changes_button' }
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index ade3d40a8d..70626636ac 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -41,9 +41,9 @@
= expanded ? _('Collapse') : _('Expand')
%p
= _("Runners are processes that pick up and execute CI/CD jobs for GitLab.")
- = link_to s_('How do I configure runners?'), help_page_path('ci/runners/README'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to s_('How do I configure runners?'), help_page_path('ci/runners/index'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
- = render 'projects/runners/index'
+ = render 'projects/runners/settings'
- if Gitlab::CurrentSettings.current_application_settings.keep_latest_artifact?
%section.settings.no-animate#js-artifacts-settings{ class: ('expanded' if expanded) }
@@ -71,13 +71,10 @@
= expanded ? _('Collapse') : _('Expand')
%p
= _("Trigger a pipeline for a branch or tag by generating a trigger token and using it with an API call. The token impersonates a user's project access and permissions.")
- = link_to _('Learn more.'), help_page_path('ci/triggers/README'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('ci/triggers/index'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'projects/triggers/index'
-- if settings_container_registry_expiration_policy_available?(@project)
- = render 'projects/registry/settings/index'
-
= render_if_exists 'projects/settings/ci_cd/auto_rollback', expanded: expanded
- if can?(current_user, :create_freeze_period, @project)
@@ -98,3 +95,16 @@
.settings-content
= render 'ci/deploy_freeze/index'
+
+- if Feature.enabled?(:ci_scoped_job_token, @project, default_enabled: :yaml)
+ %section.settings.no-animate#js-token-access{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
+ = _("Token Access")
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _("Control which projects can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API.")
+ = link_to _('Learn more'), help_page_path('api/index', anchor: 'limit-gitlab-cicd-job-token-access'), target: '_blank', rel: 'noopener noreferrer'
+ .settings-content
+ = render 'ci/token_access/index'
diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml
index e2c1a00a58..215448be3d 100644
--- a/app/views/projects/settings/operations/show.html.haml
+++ b/app/views/projects/settings/operations/show.html.haml
@@ -1,7 +1,6 @@
- @content_class = 'limit-container-width' unless fluid_layout
-- title = Feature.enabled?(:sidebar_refactor, current_user, default_enabled: :yaml) ? _('Monitor Settings') : _('Operations Settings')
-- page_title title
-- breadcrumb_title title
+- page_title _('Monitor Settings')
+- breadcrumb_title _('Monitor Settings')
= render 'projects/settings/operations/metrics_dashboard'
= render 'projects/settings/operations/tracing'
@@ -10,4 +9,4 @@
= render 'projects/settings/operations/incidents'
= render 'projects/settings/operations/grafana_integration'
= render_if_exists 'projects/settings/operations/status_page'
-= render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service)
+= render 'projects/settings/operations/prometheus', service: prometheus_integration if Feature.enabled?(:settings_operations_prometheus_service)
diff --git a/app/views/registrations/experience_levels/show.html.haml b/app/views/registrations/experience_levels/show.html.haml
index f878245a48..16e5975714 100644
--- a/app/views/registrations/experience_levels/show.html.haml
+++ b/app/views/registrations/experience_levels/show.html.haml
@@ -1,4 +1,5 @@
- page_title _('What’s your experience level?')
+- @hide_flash = true
.gl-display-flex.gl-flex-direction-column.gl-align-items-center
= image_tag 'learn-gitlab-avatar.jpg', width: '90'
diff --git a/app/views/registrations/invites/new.html.haml b/app/views/registrations/invites/new.html.haml
deleted file mode 100644
index 0feae9b17e..0000000000
--- a/app/views/registrations/invites/new.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-- page_title _('Join your team')
-- add_page_specific_style 'page_bundles/signup'
-- content_for :page_specific_javascripts do
- = render "layouts/google_tag_manager_head"
-= render "layouts/google_tag_manager_body"
-
-%h2.center.pt-6.pb-3.gl-mb-0
- = _('Join your team')
-%p.gl-text-center= _('Create your own profile to collaborate with your teammates in issues, merge requests, and more.')
-
-.signup-page
- = render 'devise/shared/signup_box',
- url: users_sign_up_invites_path,
- button_text: _('Continue'),
- show_omniauth_providers: social_signin_enabled?,
- omniauth_providers_placement: :top
- = render 'devise/shared/sign_in_link'
diff --git a/app/views/registrations/welcome/show.html.haml b/app/views/registrations/welcome/show.html.haml
index e85ce1ba6a..9356b6ad49 100644
--- a/app/views/registrations/welcome/show.html.haml
+++ b/app/views/registrations/welcome/show.html.haml
@@ -1,10 +1,11 @@
+- @html_class = "subscriptions-layout-html"
- page_title _('Your profile')
- add_page_specific_style 'page_bundles/signup'
- gitlab_experience_text = _('To personalize your GitLab experience, we\'d like to know a bit more about you')
.row.gl-flex-grow-1
- .d-flex.gl-flex-direction-column.gl-align-items-center.gl-w-full.gl-p-5
- .edit-profile.login-page.d-flex.flex-column.gl-align-items-center.pt-lg-3
+ .d-flex.gl-flex-direction-column.gl-align-items-center.gl-w-full.gl-px-5.gl-pb-5
+ .edit-profile.login-page.d-flex.flex-column.gl-align-items-center
= render_if_exists "registrations/welcome/progress_bar"
%h2.gl-text-center= html_escape(_('Welcome to GitLab,%{br_tag}%{name}!')) % { name: html_escape(current_user.first_name), br_tag: '
'.html_safe }
- if Gitlab.com?
diff --git a/app/views/root/index.html.haml b/app/views/root/index.html.haml
new file mode 100644
index 0000000000..97dd8e133f
--- /dev/null
+++ b/app/views/root/index.html.haml
@@ -0,0 +1,10 @@
+- if show_customize_homepage_banner?
+ = content_for :customize_homepage_banner do
+ .gl-display-none.gl-md-display-block{ class: "gl-pt-6! gl-pb-2! #{(container_class unless @no_container)} #{@content_class}" }
+ .js-customize-homepage-banner{ data: { svg_path: image_path('illustrations/monitoring/getting_started.svg'),
+ preferences_behavior_path: profile_preferences_path(anchor: 'behavior'),
+ callouts_path: user_callouts_path,
+ callouts_feature_id: UserCalloutsHelper::CUSTOMIZE_HOMEPAGE,
+ track_label: 'home_page' } }
+
+= render template: 'dashboard/projects/index'
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 4ba906dd02..d5d3cd753f 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -1,20 +1,16 @@
- search_bar_classes = 'search-sidebar gl-display-flex gl-flex-direction-column gl-mr-4'
+= render_if_exists 'shared/promotions/promote_advanced_search'
+= render partial: 'search/results_status', locals: { search_service: @search_service } unless @search_objects.to_a.empty?
-- if @search_objects.to_a.empty?
- .gl-md-display-flex
- - if %w(issues merge_requests).include?(@scope)
- #js-search-sidebar{ class: search_bar_classes }
- .gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
+.results.gl-md-display-flex.gl-mt-3
+ - if %w(issues merge_requests).include?(@scope)
+ #js-search-sidebar{ class: search_bar_classes }
+ .gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
+ - if @timeout
+ = render partial: "search/results/timeout"
+ - elsif @search_objects.to_a.empty?
= render partial: "search/results/empty"
- = render_if_exists 'shared/promotions/promote_advanced_search'
-- else
- = render partial: 'search/results_status', locals: { search_service: @search_service }
- = render_if_exists 'shared/promotions/promote_advanced_search'
-
- .results.gl-md-display-flex.gl-mt-3
- - if %w(issues merge_requests).include?(@scope)
- #js-search-sidebar{ class: search_bar_classes }
- .gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
+ - else
- if @scope == 'commits'
%ul.content-list.commit-list
= render partial: "search/results/commit", collection: @search_objects
diff --git a/app/views/search/results/_blob_data.html.haml b/app/views/search/results/_blob_data.html.haml
index 16d640273b..fb2825ad15 100644
--- a/app/views/search/results/_blob_data.html.haml
+++ b/app/views/search/results/_blob_data.html.haml
@@ -5,6 +5,7 @@
= sprite_icon('document')
%strong
= search_blob_title(project, path)
+ = copy_file_path_button(path)
- if blob.data
.file-content.code.term{ data: { qa_selector: 'file_text_content' } }
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link, highlight_line: blob.highlight_line
diff --git a/app/views/search/results/_issuable.html.haml b/app/views/search/results/_issuable.html.haml
index da0adba88d..551f5c048b 100644
--- a/app/views/search/results/_issuable.html.haml
+++ b/app/views/search/results/_issuable.html.haml
@@ -1,14 +1,19 @@
-%div{ class: 'search-result-row gl-pb-3! gl-mt-5 gl-mb-0!' }
- %span.gl-display-flex.gl-align-items-center
- %span.badge.badge-pill.gl-badge.sm{ class: "badge-#{issuable_state_to_badge_class(issuable)}" }= issuable_state_text(issuable)
- = sprite_icon('eye-slash', css_class: 'gl-text-gray-500 gl-ml-2') if issuable.respond_to?(:confidential?) && issuable.confidential?
- = link_to issuable_path(issuable), data: { track_event: 'click_text', track_label: "#{issuable.class.name.downcase}_title", track_property: 'search_result' }, class: 'gl-w-full' do
- %span.term.str-truncated.gl-font-weight-bold.gl-ml-2= issuable.title
- .gl-text-gray-500.gl-my-3
- = issuable_project_reference(issuable)
- ·
- = sprintf(s_('created %{issuable_created} by %{author}'), { issuable_created: time_ago_with_tooltip(issuable.created_at, placement: 'bottom'), author: link_to_member(@project, issuable.author, avatar: false) }).html_safe
- ·
- = sprintf(s_('updated %{time_ago}'), { time_ago: time_ago_with_tooltip(issuable.updated_at, placement: 'bottom') }).html_safe
- .description.term.col-sm-10.gl-px-0
- = highlight_and_truncate_issuable(issuable, @search_term, @search_highlight)
+%div{ class: 'search-result-row gl-display-flex gl-sm-flex-direction-row gl-flex-direction-column gl-align-items-center gl-pb-3! gl-mt-5 gl-mb-0!' }
+ .col-sm-9
+ %span.gl-display-flex.gl-align-items-center
+ %span.badge.badge-pill.gl-badge.sm{ class: "badge-#{issuable_state_to_badge_class(issuable)}" }= issuable_state_text(issuable)
+ = sprite_icon('eye-slash', css_class: 'gl-text-gray-500 gl-ml-2') if issuable.respond_to?(:confidential?) && issuable.confidential?
+ = link_to issuable_path(issuable), data: { track_event: 'click_text', track_label: "#{issuable.class.name.downcase}_title", track_property: 'search_result' }, class: 'gl-w-full' do
+ %span.term.str-truncated.gl-font-weight-bold.gl-ml-2= issuable.title
+ .gl-text-gray-500.gl-my-3
+ = issuable_project_reference(issuable)
+ ·
+ = sprintf(s_('created %{issuable_created} by %{author}'), { issuable_created: time_ago_with_tooltip(issuable.created_at, placement: 'bottom'), author: link_to_member(@project, issuable.author, avatar: false) }).html_safe
+ .description.term.gl-px-0
+ = highlight_and_truncate_issuable(issuable, @search_term, @search_highlight)
+ .col-sm-3.gl-mt-3.gl-sm-mt-0.gl-text-right
+ - if Feature.enabled?(:search_sort_issues_by_popularity) && issuable.respond_to?(:upvotes_count) && issuable.upvotes_count > 0
+ %li.issuable-upvotes.gl-list-style-none.has-tooltip{ title: _('Upvotes') }
+ = sprite_icon('thumb-up', css_class: "gl-vertical-align-middle")
+ = issuable.upvotes_count
+ %span.gl-text-gray-500= sprintf(s_('updated %{time_ago}'), { time_ago: time_ago_with_tooltip(issuable.updated_at, placement: 'bottom') }).html_safe
diff --git a/app/views/search/results/_timeout.html.haml b/app/views/search/results/_timeout.html.haml
new file mode 100644
index 0000000000..740e2bedd5
--- /dev/null
+++ b/app/views/search/results/_timeout.html.haml
@@ -0,0 +1,10 @@
+.gl-display-flex.gl-flex-direction-column.gl-align-items-center
+ %div
+ .svg-content.svg-150
+ = image_tag 'illustrations/search-timeout-md.svg'
+ %div
+ %h4.gl-text-center.gl-font-weight-bold= _('Your search timed out')
+ %p.gl-text-center= _('To resolve this, try to:')
+ %ul
+ %li= html_escape(_('Refine your search criteria (select a %{strong_open}group%{strong_close} and %{strong_open}project%{strong_close} when possible)')) % { strong_open: ''.html_safe, strong_close: ''.html_safe }
+ %li= html_escape(_('Use double quotes for multiple keywords, such as %{code_open}"your search"%{code_close}')) % { code_open: ''.html_safe, code_close: '
'.html_safe }
diff --git a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
index f788bf53a4..35a3835a52 100644
--- a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
+++ b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
@@ -10,5 +10,5 @@
%div
= _('Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work.')
.gl-alert-actions
- = link_to _('Settings'), project_settings_ci_cd_path(project), class: 'alert-link btn gl-button btn-info'
- = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link btn gl-button btn-default gl-ml-2'
+ = link_to _('Settings'), project_settings_ci_cd_path(project), class: 'alert-link btn gl-button btn-confirm'
+ = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link btn gl-button btn-default gl-ml-3'
diff --git a/app/views/shared/_confirm_your_email_alert.html.haml b/app/views/shared/_confirm_your_email_alert.html.haml
new file mode 100644
index 0000000000..b9906a89ce
--- /dev/null
+++ b/app/views/shared/_confirm_your_email_alert.html.haml
@@ -0,0 +1,7 @@
+.js-vue-alert{ 'v-cloak': true,
+ data: { dismissible: 'true',
+ title: _('Please confirm your email address'),
+ primary_button_text: _('Resend confirmation email'),
+ primary_button_link: new_user_confirmation_path,
+ variant: 'warning'} }
+ = (_("To continue, you need to select the link in the confirmation email we sent to verify your email address. If you didn't get our email, select %{strongStart}Resend confirmation email.%{strongEnd}") % { strongStart: '', strongEnd: '' }).html_safe
diff --git a/app/views/shared/_global_alert.html.haml b/app/views/shared/_global_alert.html.haml
index bebc72fe42..ea83f5c165 100644
--- a/app/views/shared/_global_alert.html.haml
+++ b/app/views/shared/_global_alert.html.haml
@@ -2,19 +2,23 @@
- title = local_assigns.fetch(:title, nil)
- variant = local_assigns.fetch(:variant, :info)
+- dismissible = local_assigns.fetch(:dismissible, true)
- alert_class = local_assigns.fetch(:alert_class, nil)
- alert_data = local_assigns.fetch(:alert_data, nil)
- close_button_class = local_assigns.fetch(:close_button_class, nil)
- close_button_data = local_assigns.fetch(:close_button_data, nil)
- icon = icons[variant]
+- alert_root_class = 'gl-alert-layout-limited' if fluid_layout
+- alert_container_class = [container_class, @content_class] unless fluid_layout || local_assigns.fetch(:is_contained, false)
-%div{ role: 'alert', class: ["gl-alert-#{variant}", alert_class], data: alert_data }
- %div{ class: [container_class, @content_class, 'gl-px-0!'] }
- .gl-alert
- = sprite_icon(icon, size: 16, css_class: "gl-alert-icon#{' gl-alert-icon-no-title' if title.nil?}")
- %button.gl-alert-dismiss.js-close{ type: 'button', aria: { label: _('Close') }, class: close_button_class, data: close_button_data }
+%div{ role: 'alert', class: [alert_root_class, 'gl-alert-max-content', 'gl-alert', "gl-alert-#{variant}", alert_class], data: alert_data }
+ .gl-alert-container{ class: alert_container_class }
+ = sprite_icon(icon, size: 16, css_class: "gl-alert-icon#{' gl-alert-icon-no-title' if title.nil?}")
+ - if dismissible
+ %button.btn.gl-dismiss-btn.btn-default.btn-sm.gl-button.btn-default-tertiary.btn-icon.js-close{ type: 'button', aria: { label: _('Dismiss') }, class: close_button_class, data: close_button_data }
= sprite_icon('close', size: 16)
+ .gl-alert-content{ role: 'alert' }
- if title
- .gl-alert-title
+ %h4.gl-alert-title
= title
= yield
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index 7055dc8142..e96372a29d 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -28,8 +28,7 @@
title: _('Please choose a group URL with no special characters.'),
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
%p.validation-error.gl-field-error.field-validation.hide
- = _('Group path is already taken. Suggestions: ')
- %span.gl-path-suggestions
+ = _("Group path is already taken. We've suggested one that is available.")
%p.validation-success.gl-field-success.field-validation.hide= _('Group path is available.')
%p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group URL availability...')
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index 65e0234193..f03314563c 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -8,7 +8,18 @@
= _('Git repository URL')
= f.text_field :import_url, value: import_url.sanitized_url,
autocomplete: 'off', class: 'form-control gl-form-input', placeholder: 'https://gitlab.company.com/group/project.git', required: true
+ = render 'shared/global_alert',
+ variant: :warning,
+ alert_class: 'gl-mt-3 js-import-url-warning hide',
+ dismissible: false,
+ close_button_class: 'js-close-2fa-enabled-success-alert' do
+ .gl-alert-body
+ = s_('Import|A repository URL usually ends in a .git suffix, although this is not required. Double check to make sure your repository URL is correct.')
+ .gl-alert.gl-alert-not-dismissible.gl-alert-warning.gl-mt-3.hide#project_import_url_warning
+ .gl-alert-container
+ = sprite_icon('warning-solid', css_class: 'gl-icon s16 gl-alert-icon gl-alert-icon-no-title')
+ .gl-alert-content{ role: 'alert' }
.row
.form-group.col-md-6
= f.label :import_url_user, class: 'label-bold' do
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index 3817ff8a56..d5f4add279 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,5 +1,5 @@
- if any_projects?(@projects)
- .project-item-select-holder.btn-group.gl-ml-auto.gl-mr-auto.gl-py-3.gl-relative.gl-display-flex.gl-overflow-hidden
+ .project-item-select-holder.btn-group.gl-ml-auto.gl-mr-auto.gl-relative.gl-overflow-hidden{ class: 'gl-display-flex!' }
%a.btn.gl-button.btn-confirm.new-project-item-link.block-truncated.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] }, class: "gl-m-0!" }
= loading_icon(color: 'light')
= project_select_tag :project_path, class: "project-item-select gl-absolute! gl-visibility-hidden", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path], with_shared: local_assigns[:with_shared], include_projects_in_subgroups: local_assigns[:include_projects_in_subgroups] }, with_feature_enabled: local_assigns[:with_feature_enabled]
diff --git a/app/views/shared/_project_limit.html.haml b/app/views/shared/_project_limit.html.haml
index 9110f5a7f3..90612ba623 100644
--- a/app/views/shared/_project_limit.html.haml
+++ b/app/views/shared/_project_limit.html.haml
@@ -1,8 +1,10 @@
- if cookies[:hide_project_limit_message].blank? && !current_user.hide_project_limit && !current_user.can_create_project? && current_user.projects_limit > 0
- .project-limit-message.gl-alert.gl-alert-warning.gl-display-none.gl-sm-display-block
- = _("You won't be able to create new projects because you have reached your project limit.")
-
- .float-right
- = link_to _("Don't show again"), profile_path(user: {hide_project_limit: true}), method: :put, class: 'alert-link'
- |
- = link_to _('Remind later'), '#', class: 'hide-project-limit-message alert-link'
+ = render 'shared/global_alert',
+ variant: :warning,
+ dismissible: false,
+ alert_class: 'project-limit-message' do
+ .gl-alert-body
+ = _("You won't be able to create new projects because you have reached your project limit.")
+ .gl-alert-actions
+ = link_to _('Remind later'), '#', class: 'alert-link hide-project-limit-message btn gl-button btn-confirm'
+ = link_to _("Don't show again"), profile_path(user: {hide_project_limit: true}), method: :put, class: 'alert-link btn gl-button btn-default gl-ml-3'
diff --git a/app/views/shared/_ping_consent.html.haml b/app/views/shared/_service_ping_consent.html.haml
similarity index 60%
rename from app/views/shared/_ping_consent.html.haml
rename to app/views/shared/_service_ping_consent.html.haml
index d0f1e4d722..77597124e5 100644
--- a/app/views/shared/_ping_consent.html.haml
+++ b/app/views/shared/_service_ping_consent.html.haml
@@ -1,5 +1,5 @@
- if session[:ask_for_usage_stats_consent]
- .ping-consent-message.gl-alert.gl-alert-info
+ .service-ping-consent-message.gl-alert.gl-alert-info
= sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
%button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
= sprite_icon('close', css_class: 'gl-icon')
@@ -8,7 +8,7 @@
- settings_link = link_to _('your settings'), metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), class: 'gl-link'
= s_('To help improve GitLab, we would like to periodically %{docs_link}. This can be changed at any time in %{settings_link}.').html_safe % { docs_link: docs_link, settings_link: settings_link }
.gl-alert-actions.gl-mt-3
- - send_usage_data_path = admin_application_settings_path(application_setting: { version_check_enabled: 1, usage_ping_enabled: 1 })
+ - send_service_data_path = admin_application_settings_path(application_setting: { version_check_enabled: 1, usage_ping_enabled: 1 })
- not_now_path = admin_application_settings_path(application_setting: { version_check_enabled: 0, usage_ping_enabled: 0 })
- = link_to _("Send usage data"), send_usage_data_path, 'data-url' => admin_application_settings_path, method: :put, 'data-check-enabled': true, 'data-ping-enabled': true, class: 'js-usage-consent-action alert-link btn gl-button btn-info'
- = link_to _("Don't send usage data"), not_now_path, 'data-url' => admin_application_settings_path, method: :put, 'data-check-enabled': false, 'data-ping-enabled': false, class: 'js-usage-consent-action alert-link btn gl-button btn-default gl-ml-2'
+ = link_to _("Send service data"), send_service_data_path, 'data-url' => admin_application_settings_path, method: :put, 'data-check-enabled': true, 'data-service-ping-enabled': true, class: 'js-service-ping-consent-action alert-link btn gl-button btn-info'
+ = link_to _("Don't send service data"), not_now_path, 'data-url' => admin_application_settings_path, method: :put, 'data-check-enabled': false, 'data-service-ping-enabled': false, class: 'js-service-ping-consent-action alert-link btn gl-button btn-default gl-ml-2'
diff --git a/app/views/shared/_sidebar_toggle_button.html.haml b/app/views/shared/_sidebar_toggle_button.html.haml
index a5a411db8a..b3d6c4c327 100644
--- a/app/views/shared/_sidebar_toggle_button.html.haml
+++ b/app/views/shared/_sidebar_toggle_button.html.haml
@@ -1,7 +1,5 @@
%a.toggle-sidebar-button.js-toggle-sidebar.qa-toggle-sidebar.rspec-toggle-sidebar{ role: "button", type: "button", title: "Toggle sidebar" }
= sprite_icon('chevron-double-lg-left', css_class: 'icon-chevron-double-lg-left')
- - if sidebar_refactor_disabled?
- = sprite_icon('chevron-double-lg-right', css_class: 'icon-chevron-double-lg-right')
%span.collapse-text.gl-ml-3= _("Collapse sidebar")
= button_tag class: 'close-nav-button', type: 'button' do
diff --git a/app/views/shared/access_tokens/_form.html.haml b/app/views/shared/access_tokens/_form.html.haml
index 88c24a2749..6435475a9a 100644
--- a/app/views/shared/access_tokens/_form.html.haml
+++ b/app/views/shared/access_tokens/_form.html.haml
@@ -1,5 +1,9 @@
- title = local_assigns.fetch(:title, _('Add a %{type}') % { type: type })
- prefix = local_assigns.fetch(:prefix, :personal_access_token)
+- help_path = local_assigns.fetch(:help_path)
+- project = local_assigns.fetch(:project, false)
+- access_levels = local_assigns.fetch(:access_levels, false)
+- default_access_level = local_assigns.fetch(:default_access_level, false)
%h5.gl-mt-0
= title
@@ -11,13 +15,16 @@
= form_errors(token)
.row
- .form-group.col-md-6
- = f.label :name, _('Name'), class: 'label-bold'
- = f.text_field :name, class: 'form-control gl-form-input', required: true, data: { qa_selector: 'access_token_name_field' }
+ .form-group.col
+ .row
+ = f.label :name, _('Token name'), class: 'label-bold col-md-12'
+ .col-md-6
+ = f.text_field :name, class: 'form-control gl-form-input', required: true, data: { qa_selector: 'access_token_name_field' }, :'aria-describedby' => 'access_token_help_text'
+ %span.form-text.text-muted.col-md-12#access_token_help_text= _('For example, the application using the token or the purpose of the token.')
.row
.form-group.col-md-6
- = f.label :expires_at, _('Expires at'), class: 'label-bold'
+ = f.label :expires_at, _('Expiration date'), class: 'label-bold'
.input-icon-wrapper
= render_if_exists 'personal_access_tokens/callout_max_personal_access_token_lifetime'
@@ -25,8 +32,20 @@
.js-access-tokens-expires-at
= f.text_field :expires_at, class: 'datepicker gl-datepicker-input form-control gl-form-input', placeholder: 'YYYY-MM-DD', autocomplete: 'off', data: { js_name: 'expiresAt' }
+ - if project
+ .row
+ .form-group.col-md-6
+ = label_tag :access_level, _("Select a role"), class: "label-bold"
+ .select-wrapper
+ = select_tag :"#{prefix}[access_level]", options_for_select(access_levels, default_access_level), class: "form-control project-access-select select-control", data: { qa_selector: 'access_token_access_level' }
+ = sprite_icon('chevron-down', css_class: "gl-icon gl-absolute gl-top-3 gl-right-3 gl-text-gray-200")
+
.form-group
- = f.label :scopes, _('Scopes'), class: 'label-bold'
+ %b{ :'aria-describedby' => 'select_scope_help_text' }
+ = s_('Tokens|Select scopes')
+ %p.text-secondary#select_scope_help_text
+ = s_('Tokens|Scopes set the permission levels granted to the token.')
+ = link_to "Learn more.", help_path, target: '_blank'
= render 'shared/tokens/scopes_form', prefix: prefix, token: token, scopes: scopes
- if prefix == :personal_access_token && Feature.enabled?(:personal_access_tokens_scoped_to_projects, current_user)
diff --git a/app/views/shared/access_tokens/_table.html.haml b/app/views/shared/access_tokens/_table.html.haml
index 9c59d5ae1f..1f08bff985 100644
--- a/app/views/shared/access_tokens/_table.html.haml
+++ b/app/views/shared/access_tokens/_table.html.haml
@@ -1,10 +1,15 @@
- no_active_tokens_message = local_assigns.fetch(:no_active_tokens_message, _('This user has no active %{type}.') % { type: type_plural })
- impersonation = local_assigns.fetch(:impersonation, false)
+- project = local_assigns.fetch(:project, false)
+- personal = !impersonation && !project
%hr
%h5
= _('Active %{type} (%{token_length})') % { type: type_plural, token_length: active_tokens.length }
+- if personal && !personal_access_token_expiration_enforced?
+ %p.profile-settings-content
+ = _("Personal access tokens are not revoked upon expiration.")
- if impersonation
%p.profile-settings-content
= _("To see all the user's personal access tokens you must impersonate them first.")
@@ -14,18 +19,21 @@
%table.table.active-tokens
%thead
%tr
- %th= _('Name')
+ %th= _('Token name')
+ %th= _('Scopes')
%th= s_('AccessTokens|Created')
%th
= _('Last Used')
= link_to sprite_icon('question-o'), help_page_path('user/profile/personal_access_tokens.md', anchor: 'view-the-last-time-a-token-was-used'), target: '_blank'
%th= _('Expires')
- %th= _('Scopes')
+ - if project
+ %th= _('Role')
%th
%tbody
- active_tokens.each do |token|
%tr
%td= token.name
+ %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected')
%td= token.created_at.to_date.to_s(:medium)
%td
- if token.last_used_at?
@@ -42,8 +50,9 @@
= _('In %{time_to_now}') % { time_to_now: distance_of_time_in_words_to_now(token.expires_at) }
- else
%span.token-never-expires-label= _('Never')
- %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected')
- %td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: 'gl-button btn btn-danger btn-sm float-right qa-revoke-button', data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } }
+ - if project
+ %td= project.project_member(token.user).human_access
+ %td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: "gl-button btn btn-danger btn-sm float-right qa-revoke-button #{'btn-danger-secondary' unless token.expires?}", data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } }
- else
.settings-message.text-center
= no_active_tokens_message
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index c1a50cfe71..9ccd5655fb 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -2,6 +2,7 @@
- group = local_assigns.fetch(:group, false)
- @no_breadcrumb_container = true
- @no_container = true
+- @content_wrapper_class = "#{@content_wrapper_class} gl-relative"
- @content_class = "issue-boards-content js-focus-mode-board"
- if board.to_type == "EpicBoard"
- breadcrumb_title _("Epic Boards")
@@ -9,6 +10,9 @@
- breadcrumb_title _("Issue Boards")
= render 'shared/alerts/positioning_disabled'
+= content_for :after_content do
+ #js-right-sidebar-portal
+
- page_title("#{board.name}", _("Boards"))
- add_page_specific_style 'page_bundles/boards'
diff --git a/app/views/shared/deploy_keys/_form.html.haml b/app/views/shared/deploy_keys/_form.html.haml
index 452e54f9cd..bf2514f8b0 100644
--- a/app/views/shared/deploy_keys/_form.html.haml
+++ b/app/views/shared/deploy_keys/_form.html.haml
@@ -13,7 +13,7 @@
= form.label :key, class: 'col-form-label col-sm-2'
.col-sm-10
%p.light
- - link_start = "".html_safe
+ - link_start = "".html_safe
- link_end = ''
= _('Paste a public key here. %{link_start}How do I generate it?%{link_end}').html_safe % { link_start: link_start, link_end: link_end.html_safe }
= form.text_area :key, class: 'form-control gl-form-input thin_area', rows: 5, data: { qa_selector: 'deploy_key_field' }
diff --git a/app/views/shared/deploy_keys/_project_group_form.html.haml b/app/views/shared/deploy_keys/_project_group_form.html.haml
index 0c671b4a1c..8da48a7936 100644
--- a/app/views/shared/deploy_keys/_project_group_form.html.haml
+++ b/app/views/shared/deploy_keys/_project_group_form.html.haml
@@ -9,7 +9,7 @@
.form-group.row
%p.light.gl-mb-0
= _('Paste a public key here.')
- = link_to _('How do I generate it?'), help_page_path("ssh/README")
+ = link_to _('How do I generate it?'), help_page_path("ssh/index")
= f.fields_for :deploy_keys_projects do |deploy_keys_project_form|
.form-group.row
diff --git a/app/views/shared/deploy_tokens/_form.html.haml b/app/views/shared/deploy_tokens/_form.html.haml
index 976776ccc6..5d351bd11f 100644
--- a/app/views/shared/deploy_tokens/_form.html.haml
+++ b/app/views/shared/deploy_tokens/_form.html.haml
@@ -38,7 +38,7 @@
- if packages_registry_enabled?(group_or_project)
%fieldset.form-group.form-check
- = f.check_box :read_package_registry, class: 'form-check-input'
+ = f.check_box :read_package_registry, class: 'form-check-input', data: { qa_selector: 'deploy_token_read_package_registry_checkbox' }
= f.label :read_package_registry, 'read_package_registry', class: 'label-bold form-check-label'
.text-secondary= s_('DeployTokens|Allows read access to the package registry.')
diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml
index 13d9d71d58..9842457a2e 100644
--- a/app/views/shared/empty_states/_issues.html.haml
+++ b/app/views/shared/empty_states/_issues.html.haml
@@ -47,7 +47,7 @@
= link_to _('New issue'), button_path, class: 'gl-button btn btn-confirm', id: 'new_issue_link'
- if show_import_button
- .js-csv-import-export-buttons{ data: { show_import_button: show_import_button.to_s, issuable_type: issuable_type, import_csv_issues_path: import_csv_namespace_project_issues_path, can_edit: can_edit.to_s, project_import_jira_path: project_import_jira_path(@project), max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes), container_class: 'gl-display-inline-flex gl-vertical-align-middle', show_label: 'true' } }
+ .js-csv-import-export-buttons{ data: { show_import_button: 'true', issuable_type: issuable_type, import_csv_issues_path: import_csv_namespace_project_issues_path, can_edit: can_edit.to_s, project_import_jira_path: project_import_jira_path(@project), max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes), container_class: 'gl-display-inline-flex gl-vertical-align-middle', show_label: 'true' } }
%hr
%p.gl-text-center.gl-mb-0
%strong
diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml
index bbbb728d04..3a526a9f30 100644
--- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml
+++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml
@@ -6,21 +6,13 @@
= form_tag [:bulk_update, @project, type], method: :post, class: "bulk-update" do
.block.issuable-sidebar-header
.filter-item.inline.update-issues-btn.float-left
- = button_tag _('Update all'), class: "gl-button btn update-selected-issues btn-confirm", disabled: true
+ = button_tag _('Update all'), class: "gl-button btn js-update-selected-issues btn-confirm", disabled: true
= button_tag _('Cancel'), class: "gl-button btn btn-default js-bulk-update-menu-hide float-right"
- if params[:state] != 'merged'
.block
.title
= _('Status')
- .filter-item
- = dropdown_tag(_("Select status"), options: { toggle_class: "js-issue-status", title: _("Change status"), dropdown_class: "dropdown-menu-status dropdown-menu-selectable", data: { field_name: "update[state_event]", default_label: _("Status") } } ) do
- %ul
- %li
- %a{ href: "#", data: { id: "reopen" } }
- = _('Open')
- %li
- %a{ href: "#", data: { id: "close" } }
- = _('Closed')
+ .js-issue-status
.block
.title
= _('Assignee')
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index e79719d41b..6aa80e6808 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -6,11 +6,16 @@
= form_errors(issuable)
- if @conflict
- .gl-alert.gl-alert-danger.gl-mb-5
- Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
- Please check out
- = link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project, issuable]), target: "_blank", rel: 'noopener noreferrer'
- and make sure your changes will not unintentionally remove theirs
+ = render 'shared/global_alert',
+ variant: :danger,
+ dismissible: false,
+ is_contained: true,
+ alert_class: 'gl-mb-5' do
+ .gl-alert-body
+ Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
+ Please check out
+ = link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project, issuable]), target: "_blank", rel: 'noopener noreferrer'
+ and make sure your changes will not unintentionally remove theirs
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
diff --git a/app/views/shared/issuable/_invite_members_trigger.html.haml b/app/views/shared/issuable/_invite_members_trigger.html.haml
deleted file mode 100644
index 5dd6ec0add..0000000000
--- a/app/views/shared/issuable/_invite_members_trigger.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-- return unless can_import_members?
-
-.js-invite-members-modal{ data: { id: project.id,
- name: project.name,
- is_project: 'true',
- access_levels: ProjectMember.access_level_roles.to_json,
- default_access_level: Gitlab::Access::GUEST,
- help_link: help_page_url('user/permissions') } }
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index c03697a407..737a0ff8c5 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -25,6 +25,8 @@
= check_box_tag checkbox_id, nil, false, class: "check-all-issues left"
- if is_epic_board
#js-board-filtered-search{ data: { full_path: @group&.full_path } }
+ - elsif Feature.enabled?(:issue_boards_filtered_search, board&.resource_parent) && board
+ #js-issue-board-filtered-search
- else
.issues-other-filters.filtered-search-wrapper.d-flex.flex-column.flex-md-row
.filtered-search-box
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 416c788603..c76aa17669 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -10,19 +10,13 @@
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type }
.issuable-sidebar
- .block.issuable-sidebar-header
- - if signed_in
- %span.issuable-header-text.hide-collapsed.float-left
- = _('To Do')
+ .issuable-sidebar-header.gl-py-3
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
= sidebar_gutter_toggle_icon
- if signed_in
- = render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar
+ .js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
= form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
- - if signed_in
- .block.todo.hide-expanded
- = render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar, is_collapsed: true
.block.assignee.qa-assignee-block
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
@@ -34,34 +28,11 @@
= render_if_exists 'shared/issuable/sidebar_item_epic', issuable_sidebar: issuable_sidebar, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type
- if issuable_sidebar[:supports_milestone]
- - milestone = issuable_sidebar[:milestone] || {}
.block.milestone{ :class => ("gl-border-b-0!" if issuable_sidebar[:supports_iterations]), data: { qa_selector: 'milestone_block' } }
- .sidebar-collapsed-icon.has-tooltip{ title: sidebar_milestone_tooltip_label(milestone), data: { container: 'body', html: 'true', placement: 'left', boundary: 'viewport' } }
- = sprite_icon('clock')
- %span.milestone-title.collapse-truncated-title
- - if milestone.present?
- = milestone[:title]
- - else
- = _('None')
- .hide-collapsed.gl-line-height-20.gl-mb-2.gl-text-gray-900{ data: { testid: "milestone_title" } }
- = _('Milestone')
- = loading_icon(css_class: 'gl-vertical-align-text-bottom hidden block-loading')
- - if can_edit_issuable
- = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { qa_selector: "edit_milestone_link", track_label: "right_sidebar", track_property: "milestone", track_event: "click_edit_button", track_value: "" }
- .value.hide-collapsed
- - if milestone.present?
- - milestone_title = milestone[:expired] ? _("%{milestone_name} (Past due)").html_safe % { milestone_name: milestone[:title] } : milestone[:title]
- = link_to milestone_title, milestone[:web_url], class: "bold has-tooltip", title: sidebar_milestone_remaining_days(milestone), data: { container: "body", html: 'true', boundary: 'viewport', qa_selector: 'milestone_link', qa_title: milestone[:title] }
- - else
- %span.no-value
- = _('None')
-
- .selectbox.hide-collapsed
- = f.hidden_field 'milestone_id', value: milestone[:id], id: nil
- = dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable_type}[milestone_id]", project_id: issuable_sidebar[:project_id], issuable_id: issuable_sidebar[:id], ability_name: issuable_type, issue_update: issuable_sidebar[:issuable_json_path], use_id: true, default_no: true, selected: milestone[:title], null_default: true, display: 'static' }})
+ .js-milestone-select{ data: { can_edit: can_edit_issuable.to_s, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid] } }
- if @project.group.present? && issuable_sidebar[:supports_iterations]
- .block{ class: 'gl-pt-0!' }
+ .block{ class: 'gl-pt-0!', data: { qa_selector: 'iteration_container' } }
= render_if_exists 'shared/issuable/iteration_select', can_edit: can_edit_issuable.to_s, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type
- if issuable_sidebar[:supports_time_tracking]
@@ -75,13 +46,13 @@
.js-sidebar-labels{ data: sidebar_labels_data(issuable_sidebar, @project) }
- = render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar
+ = render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar, can_edit: can_edit_issuable.to_s, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid]
- if issuable_sidebar[:supports_severity]
#js-severity
- if issuable_sidebar.dig(:features_available, :health_status)
- .js-sidebar-status-entry-point
+ .js-sidebar-status-entry-point{ data: sidebar_status_data(issuable_sidebar, @project) }
- if issuable_sidebar.has_key?(:confidential)
%script#js-confidential-issue-data{ type: "application/json" }= { is_confidential: issuable_sidebar[:confidential], is_editable: can_edit_issuable }.to_json.html_safe
diff --git a/app/views/shared/issuable/_sidebar_todo.html.haml b/app/views/shared/issuable/_sidebar_todo.html.haml
deleted file mode 100644
index a867421298..0000000000
--- a/app/views/shared/issuable/_sidebar_todo.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-- is_collapsed = local_assigns.fetch(:is_collapsed, false)
-- has_todo = !!issuable_sidebar.dig(:current_user, :todo, :id)
-
-- todo_button_data = issuable_todo_button_data(issuable_sidebar, is_collapsed)
-- button_title = has_todo ? todo_button_data[:mark_text] : todo_button_data[:todo_text]
-- button_icon = has_todo ? todo_button_data[:mark_icon] : todo_button_data[:todo_icon]
-
-%button.issuable-todo-btn.js-issuable-todo{ type: 'button',
- class: (is_collapsed ? 'btn-blank sidebar-collapsed-icon dont-change-state has-tooltip' : 'gl-button btn btn-default issuable-header-btn float-right'),
- title: button_title,
- 'aria-label' => button_title,
- data: todo_button_data }
- %span.issuable-todo-inner.js-issuable-todo-inner<
- = is_collapsed ? button_icon : button_title
- = loading_icon(css_class: is_collapsed ? '' : 'gl-ml-3')
diff --git a/app/views/shared/issuable/_sort_dropdown.html.haml b/app/views/shared/issuable/_sort_dropdown.html.haml
index 9e3caf62d7..caf271e9ee 100644
--- a/app/views/shared/issuable/_sort_dropdown.html.haml
+++ b/app/views/shared/issuable/_sort_dropdown.html.haml
@@ -1,6 +1,7 @@
- sort_value = @sort
- sort_title = issuable_sort_option_title(sort_value)
- viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues'
+- viewing_merge_requests = controller.controller_name == 'merge_requests'
.dropdown.inline.gl-ml-3.issue-sort-dropdown
.btn-group{ role: 'group' }
@@ -17,6 +18,7 @@
= sortable_item(sort_title_due_date, page_filter_path(sort: sort_value_due_date), sort_title) if viewing_issues
= sortable_item(sort_title_popularity, page_filter_path(sort: sort_value_popularity), sort_title)
= sortable_item(sort_title_label_priority, page_filter_path(sort: sort_value_label_priority), sort_title)
+ = sortable_item(sort_title_merged_date, page_filter_path(sort: sort_value_merged_date), sort_title) if viewing_merge_requests
= sortable_item(sort_title_relative_position, page_filter_path(sort: sort_value_relative_position), sort_title) if viewing_issues
= render_if_exists('shared/ee/issuable/sort_dropdown', viewing_issues: viewing_issues, sort_title: sort_title)
= issuable_sort_direction_button(sort_value)
diff --git a/app/views/shared/issue_type/_details_content.html.haml b/app/views/shared/issue_type/_details_content.html.haml
index ceedb5e5c5..0bf002fbbc 100644
--- a/app/views/shared/issue_type/_details_content.html.haml
+++ b/app/views/shared/issue_type/_details_content.html.haml
@@ -20,6 +20,9 @@
#js-related-merge-requests{ data: { endpoint: expose_path(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: issuable.iid)), project_namespace: @project.namespace.path, project_path: @project.path } }
+ - if can?(current_user, :admin_feature_flags_issue_links, @project)
+ = render_if_exists 'projects/issues/related_feature_flags'
+
- if can?(current_user, :download_code, @project)
- add_page_startup_api_call related_branches_path
#related-branches{ data: { url: related_branches_path } }
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
deleted file mode 100644
index 2aac3a94c4..0000000000
--- a/app/views/shared/members/_group.html.haml
+++ /dev/null
@@ -1,50 +0,0 @@
-- group_link = local_assigns[:group_link]
-- group = group_link.shared_with_group
-- can_admin_member = local_assigns[:can_admin_member]
-- group_link_path = local_assigns[:group_link_path]
-- dom_id = "group_member_#{group_link.id}"
-
--# Note this is just for groups. For individual members please see shared/members/_member
-
-%li.member.js-member.group_member.py-2.px-3.d-flex.flex-column.flex-md-row{ id: dom_id, data: { qa_selector: 'group_row' } }
- %span.list-item-name.mb-2.m-md-0
- = group_icon(group, class: "avatar s40 flex-shrink-0 flex-grow-0", alt: '')
- .user-info
- = link_to group.full_name, group_path(group), class: 'member'
- .cgray
- Given access #{time_ago_with_tooltip(group_link.created_at)}
- %span.js-expires-in{ class: ('gl-display-none' unless group_link.expires?) }
- ·
- %span.js-expires-in-text{ class: ('text-warning' if group_link.expires_soon?) }
- - if group_link.expires?
- = _("Expires in %{expires_at}").html_safe % { expires_at: distance_of_time_in_words_to_now(group_link.expires_at) }
- .controls.member-controls.align-items-center
- = form_tag group_link_path, method: :put, remote: true, class: 'js-edit-member-form form-group d-sm-flex' do
- = hidden_field_tag "group_link[group_access]", group_link.group_access
- .member-form-control.dropdown.mr-sm-2.d-sm-inline-block
- %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button",
- disabled: !can_admin_member,
- data: { toggle: "dropdown", field_name: "group_link[group_access]" } }
- %span.dropdown-toggle-text
- = group_link.human_access
- = sprite_icon("chevron-down", css_class: "dropdown-menu-toggle-icon gl-top-3")
- .dropdown-menu.dropdown-select.dropdown-menu-right.dropdown-menu-selectable
- = dropdown_title(_("Change role"))
- .dropdown-content
- %ul
- - Gitlab::Access.options_with_owner.each do |role, role_id|
- %li
- = link_to role, '#',
- class: ("is-active" if group_link.group_access == role_id),
- data: { id: role_id, el_id: dom_id }
- .clearable-input.member-form-control.d-sm-inline-block
- = text_field_tag 'group_link[expires_at]', group_link.expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: _('Expiration date'), id: "member_expires_at_#{group.id}", disabled: !can_admin_member
- = sprite_icon('close', size: 16, css_class: 'clear-icon js-clear-input gl-text-gray-200')
- - if can_admin_member
- = link_to group_link_path,
- method: :delete,
- data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name }, qa_selector: 'delete_group_access_link' },
- class: 'gl-button btn btn-danger m-0 ml-sm-2 align-self-center' do
- %span.d-block.d-sm-none
- = _("Delete")
- = sprite_icon('remove', css_class: 'd-none d-sm-block')
diff --git a/app/views/shared/members/_manage_access_button.html.haml b/app/views/shared/members/_manage_access_button.html.haml
new file mode 100644
index 0000000000..13509a7480
--- /dev/null
+++ b/app/views/shared/members/_manage_access_button.html.haml
@@ -0,0 +1,7 @@
+- path = local_assigns.fetch(:path, nil)
+
+.gl-float-right
+ = link_to path, class: 'btn btn-default btn-sm gl-button' do
+ = sprite_icon('pencil-square', css_class: 'gl-icon gl-button-icon')
+ %span.gl-button-text
+ = _('Manage access')
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index 8f334be042..ba0e5e492f 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -1,5 +1,4 @@
- show_roles = local_assigns.fetch(:show_roles, true)
-- show_controls = local_assigns.fetch(:show_controls, true)
- force_mobile_view = local_assigns.fetch(:force_mobile_view, false)
- member = local_assigns.fetch(:member)
- current_user_is_group_owner = local_assigns.fetch(:current_user_is_group_owner, false)
@@ -7,11 +6,10 @@
- group = local_assigns.fetch(:group)
- user = local_assigns.fetch(:user, member.user)
- source = member.source
-- override = member.try(:override)
-# Note this is just for individual members. For groups please see shared/members/_group
-%li.member.js-member.py-2.px-3.d-flex.flex-column{ class: [dom_class(member), ("is-overridden" if override), ("flex-md-row" unless force_mobile_view)], id: dom_id(member), data: { qa_selector: 'member_row' } }
+%li.member.js-member.py-2.px-3.d-flex.flex-column{ class: [dom_class(member), ("flex-md-row" unless force_mobile_view)], id: dom_id(member), data: { qa_selector: 'member_row' } }
%span.list-item-name.mb-2.m-md-0
- if user
= image_tag avatar_icon_for_user(user, 40), class: "avatar s40 flex-shrink-0 flex-grow-0", alt: ''
@@ -62,70 +60,4 @@
- if show_roles
.controls.member-controls.align-items-center
= render_if_exists 'shared/members/ee/ldap_tag', can_override: member.can_override?
- - if show_controls && member.source == membership_source
-
- - if member.can_resend_invite?
- = link_to sprite_icon('paper-airplane'), polymorphic_path([:resend_invite, member]),
- method: :post,
- class: 'gl-button btn btn-default align-self-center mr-sm-2',
- title: _('Resend invite')
-
- - if user != current_user && member.can_update?
- = form_for member, remote: true, html: { class: "js-edit-member-form form-group #{'d-sm-flex' unless force_mobile_view}" } do |f|
- = f.hidden_field :access_level
- .member-form-control.dropdown{ class: [("mr-sm-2 d-sm-inline-block" unless force_mobile_view)] }
- %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button",
- disabled: member.can_override? && !override,
- data: { toggle: "dropdown", field_name: "#{f.object_name}[access_level]", qa_selector: "access_level_dropdown" } }
- %span.dropdown-toggle-text
- = member.human_access
- = sprite_icon("chevron-down", css_class: "dropdown-menu-toggle-icon gl-top-3")
- .dropdown-menu.dropdown-select.dropdown-menu-right.dropdown-menu-selectable
- = dropdown_title(_("Change role"))
- .dropdown-content
- %ul
- - member.valid_level_roles.each do |role, role_id|
- %li
- = link_to role, '#',
- class: ("is-active" if member.access_level == role_id),
- data: { id: role_id, el_id: dom_id(member), qa_selector: "#{role.downcase}_access_level_link" }
- = render_if_exists 'shared/members/ee/revert_ldap_group_sync_option',
- group: group,
- member: member,
- can_override: member.can_override?
- .clearable-input.member-form-control{ class: [("d-sm-inline-block" unless force_mobile_view)] }
- = f.text_field :expires_at,
- disabled: member.can_override? && !override,
- class: 'form-control js-access-expiration-date js-member-update-control',
- placeholder: _('Expiration date'),
- id: "member_expires_at_#{member.id}",
- data: { el_id: dom_id(member) }
- = sprite_icon('close', size: 16, css_class: 'clear-icon js-clear-input gl-text-gray-200')
- - else
- %span.member-access-text.user-access-role= member.human_access
-
- - if member.can_approve?
- = link_to polymorphic_path([:approve_access_request, member]),
- method: :post,
- class: "btn btn-confirm btn-icon gl-button align-self-center m-0 mb-2 #{'mb-sm-0 ml-sm-2' unless force_mobile_view}",
- title: _('Grant access') do
- %span{ class: ('d-block d-sm-none' unless force_mobile_view) }
- = _('Grant access')
- - unless force_mobile_view
- = sprite_icon('check', css_class: 'd-none d-sm-block')
-
- - if member.can_remove?
- - if current_user == user
- = link_to polymorphic_path([:leave, member.source, :members]), method: :delete, data: { confirm: leave_confirmation_message(member.source) }, class: "btn gl-button btn-svg btn-danger align-self-center m-0 #{'ml-sm-2' unless force_mobile_view}" do
- = sprite_icon('leave', css_class: 'gl-icon')
- = _('Leave')
- - else
- %button{ data: { member_path: member_path(member.member), member_type: member.type, message: remove_member_message(member), is_access_request: member.request?.to_s, qa_selector: 'delete_member_button' },
- class: "js-remove-member-button btn gl-button btn-danger align-self-center m-0 #{'ml-sm-2 btn-icon' unless force_mobile_view}",
- title: remove_member_title(member) }
- %span{ class: ('d-block d-sm-none' unless force_mobile_view) }
- = _("Delete")
- - unless force_mobile_view
- = sprite_icon('remove', css_class: 'd-none d-sm-block gl-icon')
- - else
- %span.member-access-text.user-access-role= member.human_access
+ %span.member-access-text.user-access-role= member.human_access
diff --git a/app/views/shared/members/_requests.html.haml b/app/views/shared/members/_requests.html.haml
index 3aa43ed192..8b0a85656d 100644
--- a/app/views/shared/members/_requests.html.haml
+++ b/app/views/shared/members/_requests.html.haml
@@ -1,20 +1,19 @@
- membership_source = local_assigns.fetch(:membership_source)
- requesters = local_assigns.fetch(:requesters)
-- force_mobile_view = local_assigns.fetch(:force_mobile_view, false)
- group = local_assigns.fetch(:group)
- current_user_is_group_owner = group && group.has_owner?(current_user)
- return if requesters.empty?
-.card.gl-mt-3{ class: ('card-mobile' if force_mobile_view ) }
+.card.gl-mt-3{ data: { testid: 'access-requests' } }
.card-header
= _("Users requesting access to")
%strong= membership_source.name
%span.badge.badge-pill= requesters.size
+ = render 'shared/members/manage_access_button', path: membership_source.is_a?(Project) ? project_project_members_path(@project, tab: 'access_requests') : group_group_members_path(@group, tab: 'access_requests')
%ul.content-list.members-list
= render partial: 'shared/members/member',
collection: requesters, as: :member,
locals: { membership_source: membership_source,
group: group,
- force_mobile_view: force_mobile_view,
current_user_is_group_owner: current_user_is_group_owner }
diff --git a/app/views/shared/members/_search_field.html.haml b/app/views/shared/members/_search_field.html.haml
deleted file mode 100644
index b1e3134f7a..0000000000
--- a/app/views/shared/members/_search_field.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-- name = local_assigns.fetch(:name, :search)
-
-.search-control-wrap.gl-relative
- = search_field_tag name, params[name], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
- %button.user-search-btn.border-left.gl-display-flex.gl-align-items-center.gl-justify-content-center{ type: 'submit', 'aria': { label: _('Submit search') }, data: { testid: 'user-search-submit' } }
- = sprite_icon('search')
diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml
deleted file mode 100644
index 682e3a0433..0000000000
--- a/app/views/shared/members/_sort_dropdown.html.haml
+++ /dev/null
@@ -1,19 +0,0 @@
-.dropdown.inline.qa-user-sort-dropdown{ data: { testid: 'user-sort-dropdown' } }
- = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown', testid: 'dropdown-toggle' })
- %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
- %li.dropdown-header
- = _("Sort by")
- - member_sort_options_hash.each do |value, title|
- %li
- = link_to filter_group_project_member_path(sort: value), class: ("is-active" if @sort == value) do
- = title
- %li.divider
- %li{ data: { testid: 'filter-members-with-inherited-permissions' } }
- = link_to filter_group_project_member_path(with_inherited_permissions: nil), class: ("is-active" unless params[:with_inherited_permissions].present?) do
- = _("Show all members")
- %li{ data: { testid: 'filter-members-with-inherited-permissions' } }
- = link_to filter_group_project_member_path(with_inherited_permissions: 'exclude'), class: ("is-active" if params[:with_inherited_permissions] == 'exclude') do
- = _("Show only direct members")
- %li{ data: { testid: 'filter-members-with-inherited-permissions' } }
- = link_to filter_group_project_member_path(with_inherited_permissions: 'only'), class: ("is-active" if params[:with_inherited_permissions] == 'only') do
- = _("Show only inherited members")
diff --git a/app/views/shared/milestones/_form_dates.html.haml b/app/views/shared/milestones/_form_dates.html.haml
index e0664c1feb..7a41e381a9 100644
--- a/app/views/shared/milestones/_form_dates.html.haml
+++ b/app/views/shared/milestones/_form_dates.html.haml
@@ -1,13 +1,11 @@
-.col-md-6
- .form-group.row
- .col-form-label.col-sm-2
- = f.label :start_date, _('Start Date')
- .col-sm-10
- = f.text_field :start_date, class: "datepicker form-control gl-form-input", data: { qa_selector: "start_date_field" }, placeholder: _('Select start date'), autocomplete: 'off'
- %a.inline.float-right.gl-mt-2.js-clear-start-date{ href: "#" }= _('Clear start date')
- .form-group.row
- .col-form-label.col-sm-2
- = f.label :due_date, _('Due Date')
- .col-sm-10
- = f.text_field :due_date, class: "datepicker form-control gl-form-input", data: { qa_selector: "due_date_field" }, placeholder: _('Select due date'), autocomplete: 'off'
- %a.inline.float-right.gl-mt-2.js-clear-due-date{ href: "#" }= _('Clear due date')
+.form-group.row
+ .col-form-label.col-sm-2
+ = f.label :start_date, _('Start Date')
+ .col-sm-4
+ = f.text_field :start_date, class: "datepicker form-control gl-form-input", data: { qa_selector: "start_date_field" }, placeholder: _('Select start date'), autocomplete: 'off'
+ %a.inline.float-right.gl-mt-2.js-clear-start-date{ href: "#" }= _('Clear start date')
+ .col-form-label.col-sm-2
+ = f.label :due_date, _('Due Date')
+ .col-sm-4
+ = f.text_field :due_date, class: "datepicker form-control gl-form-input", data: { qa_selector: "due_date_field" }, placeholder: _('Select due date'), autocomplete: 'off'
+ %a.inline.float-right.gl-mt-2.js-clear-due-date{ href: "#" }= _('Clear due date')
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index 184904dd7a..12380d4c34 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -25,3 +25,5 @@
= link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, assignee_id: assignee.id, state: 'all' }),
class: 'has-tooltip', title: _("Assigned to %{assignee_name}") % { assignee_name: assignee.name }, data: { container: 'body' } do
- image_tag(avatar_icon_for_user(assignee, 16), class: "avatar s16", alt: '')
+
+ = render_if_exists "shared/milestones/issuable_weight", issuable: issuable
diff --git a/app/views/shared/milestones/_issuables.html.haml b/app/views/shared/milestones/_issuables.html.haml
index 9147e1c50e..460ddd0897 100644
--- a/app/views/shared/milestones/_issuables.html.haml
+++ b/app/views/shared/milestones/_issuables.html.haml
@@ -4,11 +4,15 @@
.card
.card-header{ class: panel_class }
- .title
- = title
- - if show_counter
- .counter
- = number_with_delimiter(issuables.length)
+ .header.gl-mb-2
+ .title
+ = title
+ .issuable-count-weight.gl-ml-3
+ - if show_counter
+ %span.counter
+ = sprite_icon('issues', css_class: 'gl-vertical-align-text-bottom')
+ = number_with_delimiter(issuables.length)
+ = render_if_exists "shared/milestones/issuables_weight", issuables: issuables
- class_prefix = dom_class(issuables).pluralize
%ul{ class: "content-list milestone-#{class_prefix}-list", id: "#{class_prefix}-list-#{id}" }
diff --git a/app/views/shared/milestones/_milestone_complete_alert.html.haml b/app/views/shared/milestones/_milestone_complete_alert.html.haml
new file mode 100644
index 0000000000..1c25fae747
--- /dev/null
+++ b/app/views/shared/milestones/_milestone_complete_alert.html.haml
@@ -0,0 +1,10 @@
+- milestone = local_assigns[:milestone]
+
+- if milestone.complete? && milestone.active?
+ = render 'shared/global_alert',
+ variant: :success,
+ is_contained: true,
+ alert_data: { testid: 'all-issues-closed-alert' },
+ dismissible: false do
+ .gl-alert-body
+ = yield
diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml
index c37fdf0c98..2709a39c47 100644
--- a/app/views/shared/milestones/_top.html.haml
+++ b/app/views/shared/milestones/_top.html.haml
@@ -5,11 +5,8 @@
= render 'shared/milestones/header', milestone: milestone
= render 'shared/milestones/description', milestone: milestone
-
-- if milestone.complete? && milestone.active?
- .gl-alert.gl-alert-success.gl-mt-3
- %span
- = _('All issues for this milestone are closed.')
- = group ? _('You may close the milestone now.') : _('Navigate to the project to close the milestone.')
+= render 'shared/milestones/milestone_complete_alert', milestone: milestone do
+ = _('All issues for this milestone are closed.')
+ = group ? _('You may close the milestone now.') : _('Navigate to the project to close the milestone.')
= render_if_exists 'shared/milestones/burndown', milestone: milestone, project: @project
diff --git a/app/views/shared/namespaces/cascading_settings/_enforcement_checkbox.html.haml b/app/views/shared/namespaces/cascading_settings/_enforcement_checkbox.html.haml
index ab4d8816ec..cfa8735168 100644
--- a/app/views/shared/namespaces/cascading_settings/_enforcement_checkbox.html.haml
+++ b/app/views/shared/namespaces/cascading_settings/_enforcement_checkbox.html.haml
@@ -1,9 +1,10 @@
- attribute = local_assigns.fetch(:attribute, nil)
+- group = local_assigns.fetch(:group, nil)
- form = local_assigns.fetch(:form, nil)
- setting_locked = local_assigns.fetch(:setting_locked, false)
- help_text = local_assigns.fetch(:help_text, s_('CascadingSettings|Subgroups cannot change this setting.'))
-- return unless attribute && group && form && cascading_namespace_settings_enabled?
+- return unless attribute && group && form
- return if setting_locked
- lock_attribute = "lock_#{attribute}"
diff --git a/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml b/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml
index d27b364163..83d602aba2 100644
--- a/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml
+++ b/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml
@@ -1,10 +1,11 @@
- attribute = local_assigns.fetch(:attribute, nil)
+- group = local_assigns.fetch(:group, nil)
- settings_path_helper = local_assigns.fetch(:settings_path_helper, nil)
- form = local_assigns.fetch(:form, nil)
- setting_locked = local_assigns.fetch(:setting_locked, false)
- help_text = local_assigns.fetch(:help_text, nil)
-- return unless attribute && form && settings_path_helper
+- return unless attribute && group && form && settings_path_helper
= form.label attribute, class: 'custom-control-label', aria: { disabled: setting_locked } do
= render 'shared/namespaces/cascading_settings/setting_label_container' do
diff --git a/app/views/shared/namespaces/cascading_settings/_setting_label_fieldset.html.haml b/app/views/shared/namespaces/cascading_settings/_setting_label_fieldset.html.haml
index 4a2ec9f30f..66c760b466 100644
--- a/app/views/shared/namespaces/cascading_settings/_setting_label_fieldset.html.haml
+++ b/app/views/shared/namespaces/cascading_settings/_setting_label_fieldset.html.haml
@@ -1,9 +1,10 @@
- attribute = local_assigns.fetch(:attribute, nil)
+- group = local_assigns.fetch(:group, nil)
- settings_path_helper = local_assigns.fetch(:settings_path_helper, nil)
- setting_locked = local_assigns.fetch(:setting_locked, false)
- help_text = local_assigns.fetch(:help_text, nil)
-- return unless attribute && settings_path_helper
+- return unless attribute && group && settings_path_helper
%legend.h5.gl-border-none.gl-m-0
= render 'shared/namespaces/cascading_settings/setting_label_container' do
diff --git a/app/views/shared/nav/_scope_menu.html.haml b/app/views/shared/nav/_scope_menu.html.haml
index cbee0e0429..1a7089fb57 100644
--- a/app/views/shared/nav/_scope_menu.html.haml
+++ b/app/views/shared/nav/_scope_menu.html.haml
@@ -1,6 +1,6 @@
-- if sidebar_refactor_enabled?
- = nav_link(**scope_menu.active_routes, html_options: scope_menu.nav_link_html_options) do
- = render 'shared/nav/scope_menu_body', scope_menu: scope_menu
-- else
- .context-header
- = render 'shared/nav/scope_menu_body', scope_menu: scope_menu
+= nav_link(**scope_menu.active_routes, html_options: scope_menu.nav_link_html_options) do
+ = link_to scope_menu.link, **scope_menu.container_html_options, data: { qa_selector: 'sidebar_menu_link', qa_menu_item: scope_qa_menu_item(scope_menu.container) } do
+ %span{ class: scope_avatar_classes(scope_menu.container) }
+ = source_icon(scope_menu.container, alt: scope_menu.title, class: ['avatar', 'avatar-tile', 's32'], width: 32, height: 32)
+ %span.sidebar-context-title
+ = scope_menu.title
diff --git a/app/views/shared/nav/_scope_menu_body.html.haml b/app/views/shared/nav/_scope_menu_body.html.haml
deleted file mode 100644
index a94c681e2d..0000000000
--- a/app/views/shared/nav/_scope_menu_body.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-- avatar_size = sidebar_refactor_disabled? ? 40 : 32
-- avatar_size_class = sidebar_refactor_disabled? ? 's40' : 's32'
-
-= link_to scope_menu.link, **scope_menu.container_html_options, data: { qa_selector: 'project_scope_link' } do
- %span{ class: ['avatar-container', 'rect-avatar', 'project-avatar', avatar_size_class] }
- = source_icon(scope_menu.container, alt: scope_menu.title, class: ['avatar', 'avatar-tile', avatar_size_class], width: avatar_size, height: avatar_size)
- %span.sidebar-context-title
- = scope_menu.title
diff --git a/app/views/shared/nav/_sidebar.html.haml b/app/views/shared/nav/_sidebar.html.haml
index 54c3b8a281..915352996d 100644
--- a/app/views/shared/nav/_sidebar.html.haml
+++ b/app/views/shared/nav/_sidebar.html.haml
@@ -1,15 +1,14 @@
%aside.nav-sidebar{ class: ('sidebar-collapsed-desktop' if collapsed_sidebar?), **sidebar_tracking_attributes_by_object(sidebar.container), 'aria-label': sidebar.aria_label }
.nav-sidebar-inner-scroll
- - if sidebar.scope_menu && sidebar_refactor_disabled?
- = render partial: 'shared/nav/scope_menu', object: sidebar.scope_menu
- - elsif sidebar.render_raw_scope_menu_partial
- = render sidebar.render_raw_scope_menu_partial
-
- %ul.sidebar-top-level-items.qa-project-sidebar
- - if sidebar.scope_menu && sidebar_refactor_enabled?
+ %ul.sidebar-top-level-items{ data: { qa_selector: sidebar_qa_selector(sidebar.container) } }
+ - if sidebar.render_raw_scope_menu_partial
+ = render sidebar.render_raw_scope_menu_partial
+ - elsif sidebar.scope_menu
= render partial: 'shared/nav/scope_menu', object: sidebar.scope_menu
+
- if sidebar.renderable_menus.any?
= render partial: 'shared/nav/sidebar_menu', collection: sidebar.renderable_menus
+
- if sidebar.render_raw_menus_partial
= render sidebar.render_raw_menus_partial
diff --git a/app/views/shared/nav/_sidebar_menu.html.haml b/app/views/shared/nav/_sidebar_menu.html.haml
index b80bd515a3..9a04139d2f 100644
--- a/app/views/shared/nav/_sidebar_menu.html.haml
+++ b/app/views/shared/nav/_sidebar_menu.html.haml
@@ -15,12 +15,12 @@
%ul.sidebar-sub-level-items{ class: ('is-fly-out-only' unless sidebar_menu.has_renderable_items?) }
= nav_link(**sidebar_menu.all_active_routes, html_options: { class: 'fly-out-top-item' } ) do
- - if sidebar_refactor_disabled?
- = link_to sidebar_menu.link, class: "'has-sub-items' if sidebar_menu.has_renderable_items?", **sidebar_menu.collapsed_container_html_options do
- = render 'shared/nav/sidebar_menu_collapsed', sidebar_menu: sidebar_menu
- - else
- %span.fly-out-top-item-container
- = render 'shared/nav/sidebar_menu_collapsed', sidebar_menu: sidebar_menu
+ %span.fly-out-top-item-container
+ %strong.fly-out-top-item-name
+ = sidebar_menu.title
+ - if sidebar_menu.has_pill?
+ %span.badge.badge-pill.count.fly-out-badge{ **sidebar_menu.pill_html_options }
+ = number_with_delimiter(sidebar_menu.pill_count)
- if sidebar_menu.has_renderable_items?
%li.divider.fly-out-top-item
diff --git a/app/views/shared/nav/_sidebar_menu_collapsed.html.haml b/app/views/shared/nav/_sidebar_menu_collapsed.html.haml
deleted file mode 100644
index 78567a991d..0000000000
--- a/app/views/shared/nav/_sidebar_menu_collapsed.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%strong.fly-out-top-item-name
- = sidebar_menu.title
-- if sidebar_menu.has_pill?
- %span.badge.badge-pill.count.fly-out-badge{ **sidebar_menu.pill_html_options }
- = number_with_delimiter(sidebar_menu.pill_count)
diff --git a/app/views/shared/snippets/_embed.html.haml b/app/views/shared/snippets/_embed.html.haml
index f698e1a301..b5abd00b8f 100644
--- a/app/views/shared/snippets/_embed.html.haml
+++ b/app/views/shared/snippets/_embed.html.haml
@@ -4,12 +4,12 @@
= external_snippet_icon('doc-text')
%strong.file-title-name
- %a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) }
+ %a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil), target: '_blank', rel: 'noopener noreferrer' }
= blob.name
%small
= number_to_human_size(blob.size)
- %a.gitlab-logo-wrapper{ href: url_for(only_path: false, overwrite_params: nil), title: 'view on gitlab' }
+ %a.gitlab-logo-wrapper{ href: url_for(only_path: false, overwrite_params: nil), title: 'View on GitLab', target: '_blank', rel: 'noopener noreferrer' }
%img.gitlab-logo{ src: image_url('ext_snippet_icons/logo.svg'), alt: "GitLab logo" }
.file-actions.d-none.d-sm-block
diff --git a/app/views/shared/wikis/edit.html.haml b/app/views/shared/wikis/edit.html.haml
index 729646c273..15710f0df4 100644
--- a/app/views/shared/wikis/edit.html.haml
+++ b/app/views/shared/wikis/edit.html.haml
@@ -18,7 +18,7 @@
.nav-controls.pb-md-3.pb-lg-0
- if @page.persisted?
- - if can?(current_user, :admin_wiki, @wiki.container)
+ - if can?(current_user, :create_wiki, @wiki.container)
#delete-wiki-modal-wrapper{ data: { delete_wiki_url: wiki_page_path(@wiki, @page), page_title: @page.human_title } }
= render 'shared/wikis/form', uploads_path: wiki_attachment_upload_url
diff --git a/app/views/users/unsubscribes/show.html.haml b/app/views/users/unsubscribes/show.html.haml
new file mode 100644
index 0000000000..8b3dc69f3a
--- /dev/null
+++ b/app/views/users/unsubscribes/show.html.haml
@@ -0,0 +1,11 @@
+- page_title _("Unsubscribe"), _("Admin Notifications")
+%h3.page-title Unsubscribe from Admin notifications
+
+%hr
+= form_tag unsubscribe_path(Base64.urlsafe_encode64(@email)) do
+ %p
+ Yes, I want to unsubscribe
+ %strong= @email
+ from any further admin emails.
+ .form-actions
+ = submit_tag 'Unsubscribe', class: 'gl-button btn btn-confirm'
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 31c590183d..8d08beb56a 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -247,6 +247,15 @@
:idempotent: true
:tags:
- :exclude_from_kubernetes
+- :name: cronjob:database_partition_management
+ :worker_name: Database::PartitionManagementWorker
+ :feature_category: :database
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: cronjob:environments_auto_stop_cron
:worker_name: Environments::AutoStopCronWorker
:feature_category: :continuous_delivery
@@ -265,9 +274,9 @@
:weight: 1
:idempotent:
:tags: []
-- :name: cronjob:gitlab_usage_ping
- :worker_name: GitlabUsagePingWorker
- :feature_category: :usage_ping
+- :name: cronjob:gitlab_service_ping
+ :worker_name: GitlabServicePingWorker
+ :feature_category: :service_ping
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -1078,6 +1087,15 @@
:weight: 2
:idempotent: true
:tags: []
+- :name: jira_connect:jira_connect_forward_event
+ :worker_name: JiraConnect::ForwardEventWorker
+ :feature_category: :integrations
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: jira_connect:jira_connect_sync_branch
:worker_name: JiraConnect::SyncBranchWorker
:feature_category: :integrations
@@ -1085,7 +1103,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent: true
+ :idempotent:
:tags: []
- :name: jira_connect:jira_connect_sync_builds
:worker_name: JiraConnect::SyncBuildsWorker
@@ -1094,7 +1112,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent: true
+ :idempotent:
:tags:
- :exclude_from_kubernetes
- :name: jira_connect:jira_connect_sync_deployments
@@ -1104,7 +1122,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent: true
+ :idempotent:
:tags:
- :exclude_from_kubernetes
- :name: jira_connect:jira_connect_sync_feature_flags
@@ -1114,7 +1132,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent: true
+ :idempotent:
:tags:
- :exclude_from_kubernetes
- :name: jira_connect:jira_connect_sync_merge_request
@@ -1124,7 +1142,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent: true
+ :idempotent:
:tags: []
- :name: jira_connect:jira_connect_sync_project
:worker_name: JiraConnect::SyncProjectWorker
@@ -1133,7 +1151,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent: true
+ :idempotent:
:tags:
- :exclude_from_kubernetes
- :name: jira_importer:jira_import_advance_stage
@@ -1309,6 +1327,15 @@
:idempotent: true
:tags:
- :exclude_from_kubernetes
+- :name: package_repositories:packages_helm_extraction
+ :worker_name: Packages::Helm::ExtractionWorker
+ :feature_category: :package_registry
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: package_repositories:packages_maven_metadata_sync
:worker_name: Packages::Maven::Metadata::SyncWorker
:feature_category: :package_registry
@@ -1347,6 +1374,15 @@
:weight: 1
:idempotent:
:tags: []
+- :name: pipeline_background:ci_archive_trace
+ :worker_name: Ci::ArchiveTraceWorker
+ :feature_category: :continuous_integration
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: pipeline_background:ci_build_trace_chunk_flush
:worker_name: Ci::BuildTraceChunkFlushWorker
:feature_category: :continuous_integration
@@ -1567,6 +1603,15 @@
:weight: 5
:idempotent:
:tags: []
+- :name: pipeline_processing:ci_build_finished
+ :worker_name: Ci::BuildFinishedWorker
+ :feature_category: :continuous_integration
+ :has_external_dependencies:
+ :urgency: :high
+ :resource_boundary: :cpu
+ :weight: 5
+ :idempotent:
+ :tags: []
- :name: pipeline_processing:ci_build_prepare
:worker_name: Ci::BuildPrepareWorker
:feature_category: :continuous_integration
@@ -1601,7 +1646,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 5
- :idempotent:
+ :idempotent: true
:tags: []
- :name: pipeline_processing:pipeline_process
:worker_name: PipelineProcessWorker
diff --git a/app/workers/archive_trace_worker.rb b/app/workers/archive_trace_worker.rb
index 629526ec17..ecde05f94d 100644
--- a/app/workers/archive_trace_worker.rb
+++ b/app/workers/archive_trace_worker.rb
@@ -1,16 +1,5 @@
# frozen_string_literal: true
-class ArchiveTraceWorker # rubocop:disable Scalability/IdempotentWorker
- include ApplicationWorker
-
- sidekiq_options retry: 3
- include PipelineBackgroundQueue
-
- # rubocop: disable CodeReuse/ActiveRecord
- def perform(job_id)
- Ci::Build.without_archived_trace.find_by(id: job_id).try do |job|
- Ci::ArchiveTraceService.new.execute(job, worker_name: self.class.name)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
+class ArchiveTraceWorker < ::Ci::ArchiveTraceWorker # rubocop:disable Scalability/IdempotentWorker
+ # DEPRECATED: Not triggered since https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64934/
end
diff --git a/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb b/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb
index 5ca9de63fd..10f7cb20df 100644
--- a/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb
+++ b/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb
@@ -1,15 +1,54 @@
# frozen_string_literal: true
module AuthorizedProjectUpdate
- class UserRefreshFromReplicaWorker < ::AuthorizedProjectsWorker
+ class UserRefreshFromReplicaWorker
+ include ApplicationWorker
+
+ sidekiq_options retry: 3
feature_category :authentication_and_authorization
urgency :low
queue_namespace :authorized_project_update
- deduplicate :until_executing, including_scheduled: true
idempotent!
+ deduplicate :until_executing, including_scheduled: true
- # This worker will start reading data from the replica database soon
- # Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/333219
+ def perform(user_id)
+ if Feature.enabled?(:user_refresh_from_replica_worker_uses_replica_db)
+ use_replica_if_available do
+ user = User.find_by_id(user_id)
+
+ if user && project_authorizations_needs_refresh?(user)
+ enqueue_project_authorizations_refresh(user)
+ end
+ end
+ else
+ user = User.find_by_id(user_id)
+ return unless user
+
+ user.refresh_authorized_projects(source: self.class.name)
+ end
+ end
+
+ private
+
+ # We use this approach instead of specifying `data_consistency :delayed` because these jobs
+ # are enqueued in large numbers, and using `data_consistency :delayed`
+ # does not allow us to deduplicate these jobs.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/325291
+ def use_replica_if_available(&block)
+ return yield unless ::Gitlab::Database::LoadBalancing.enable?
+
+ ::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries(&block)
+ end
+
+ def project_authorizations_needs_refresh?(user)
+ AuthorizedProjectUpdate::FindRecordsDueForRefreshService.new(user).needs_refresh?
+ end
+
+ def enqueue_project_authorizations_refresh(user)
+ with_context(user: user) do
+ AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker.perform_async(user.id)
+ end
+ end
end
end
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index a3eaacec8a..0d41f7b943 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -1,61 +1,9 @@
# frozen_string_literal: true
-class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
- include ApplicationWorker
+class BuildFinishedWorker < ::Ci::BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
+ # DEPRECATED: Not triggered since https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64934/
- sidekiq_options retry: 3
- include PipelineQueue
-
- queue_namespace :pipeline_processing
+ # We need to explicitly specify these settings. They aren't inheriting from the parent class.
urgency :high
worker_resource_boundary :cpu
-
- ARCHIVE_TRACES_IN = 2.minutes.freeze
-
- # rubocop: disable CodeReuse/ActiveRecord
- def perform(build_id)
- Ci::Build.find_by(id: build_id).try do |build|
- process_build(build)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- private
-
- # Processes a single CI build that has finished.
- #
- # This logic resides in a separate method so that EE can extend it more
- # easily.
- #
- # @param [Ci::Build] build The build to process.
- def process_build(build)
- # We execute these in sync to reduce IO.
- build.parse_trace_sections!
- build.update_coverage
- Ci::BuildReportResultService.new.execute(build)
-
- # We execute these async as these are independent operations.
- BuildHooksWorker.perform_async(build.id)
- ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
-
- if build.failed?
- ::Ci::MergeRequests::AddTodoWhenBuildFailsWorker.perform_async(build.id)
- end
-
- ##
- # We want to delay sending a build trace to object storage operation to
- # validate that this fixes a race condition between this and flushing live
- # trace chunks and chunks being removed after consolidation and putting
- # them into object storage archive.
- #
- # TODO This is temporary fix we should improve later, after we validate
- # that this is indeed the culprit.
- #
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/267112 for more
- # details.
- #
- ArchiveTraceWorker.perform_in(ARCHIVE_TRACES_IN, build.id)
- end
end
-
-BuildFinishedWorker.prepend_mod_with('BuildFinishedWorker')
diff --git a/app/workers/build_queue_worker.rb b/app/workers/build_queue_worker.rb
index aa3c03f773..4ab08bbd7f 100644
--- a/app/workers/build_queue_worker.rb
+++ b/app/workers/build_queue_worker.rb
@@ -10,7 +10,7 @@ class BuildQueueWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :continuous_integration
urgency :high
worker_resource_boundary :cpu
- data_consistency :sticky, feature_flag: :load_balancing_for_build_queue_worker
+ data_consistency :sticky
# rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
diff --git a/app/workers/bulk_imports/export_request_worker.rb b/app/workers/bulk_imports/export_request_worker.rb
index 24e75ad0f8..d3bb36d830 100644
--- a/app/workers/bulk_imports/export_request_worker.rb
+++ b/app/workers/bulk_imports/export_request_worker.rb
@@ -25,7 +25,7 @@ module BulkImports
def http_client(configuration)
@client ||= Clients::HTTP.new(
- uri: configuration.url,
+ url: configuration.url,
token: configuration.access_token
)
end
diff --git a/app/workers/ci/archive_trace_worker.rb b/app/workers/ci/archive_trace_worker.rb
new file mode 100644
index 0000000000..16288faf37
--- /dev/null
+++ b/app/workers/ci/archive_trace_worker.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Ci
+ class ArchiveTraceWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ sidekiq_options retry: 3
+ include PipelineBackgroundQueue
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(job_id)
+ Ci::Build.without_archived_trace.find_by(id: job_id).try do |job|
+ Ci::ArchiveTraceService.new.execute(job, worker_name: self.class.name)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/workers/ci/archive_traces_cron_worker.rb b/app/workers/ci/archive_traces_cron_worker.rb
index c748bc33ad..5fe3adf870 100644
--- a/app/workers/ci/archive_traces_cron_worker.rb
+++ b/app/workers/ci/archive_traces_cron_worker.rb
@@ -12,7 +12,7 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def perform
# Archive stale live traces which still resides in redis or database
- # This could happen when ArchiveTraceWorker sidekiq jobs were lost by receiving SIGKILL
+ # This could happen when Ci::ArchiveTraceWorker sidekiq jobs were lost by receiving SIGKILL
# More details in https://gitlab.com/gitlab-org/gitlab-foss/issues/36791
Ci::Build.with_stale_live_trace.find_each(batch_size: 100) do |build|
Ci::ArchiveTraceService.new.execute(build, worker_name: self.class.name)
diff --git a/app/workers/ci/build_finished_worker.rb b/app/workers/ci/build_finished_worker.rb
new file mode 100644
index 0000000000..1d6e3b1fa3
--- /dev/null
+++ b/app/workers/ci/build_finished_worker.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module Ci
+ class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ sidekiq_options retry: 3
+ include PipelineQueue
+
+ queue_namespace :pipeline_processing
+ urgency :high
+ worker_resource_boundary :cpu
+
+ ARCHIVE_TRACES_IN = 2.minutes.freeze
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(build_id)
+ Ci::Build.find_by(id: build_id).try do |build|
+ process_build(build)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ # Processes a single CI build that has finished.
+ #
+ # This logic resides in a separate method so that EE can extend it more
+ # easily.
+ #
+ # @param [Ci::Build] build The build to process.
+ def process_build(build)
+ # We execute these in sync to reduce IO.
+ build.parse_trace_sections!
+ build.update_coverage
+ Ci::BuildReportResultService.new.execute(build)
+
+ # We execute these async as these are independent operations.
+ BuildHooksWorker.perform_async(build.id)
+ ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
+
+ if build.failed?
+ ::Ci::MergeRequests::AddTodoWhenBuildFailsWorker.perform_async(build.id)
+ end
+
+ ##
+ # We want to delay sending a build trace to object storage operation to
+ # validate that this fixes a race condition between this and flushing live
+ # trace chunks and chunks being removed after consolidation and putting
+ # them into object storage archive.
+ #
+ # TODO This is temporary fix we should improve later, after we validate
+ # that this is indeed the culprit.
+ #
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/267112 for more
+ # details.
+ #
+ archive_trace_worker_class(build).perform_in(ARCHIVE_TRACES_IN, build.id)
+ end
+
+ def archive_trace_worker_class(build)
+ if Feature.enabled?(:ci_build_finished_worker_namespace_changed, build.project, default_enabled: :yaml)
+ Ci::ArchiveTraceWorker
+ else
+ ::ArchiveTraceWorker
+ end
+ end
+ end
+end
+
+Ci::BuildFinishedWorker.prepend_mod_with('Ci::BuildFinishedWorker')
diff --git a/app/workers/ci/resource_groups/assign_resource_from_resource_group_worker.rb b/app/workers/ci/resource_groups/assign_resource_from_resource_group_worker.rb
index 15ed89fd00..ad0ed3d16f 100644
--- a/app/workers/ci/resource_groups/assign_resource_from_resource_group_worker.rb
+++ b/app/workers/ci/resource_groups/assign_resource_from_resource_group_worker.rb
@@ -2,7 +2,10 @@
module Ci
module ResourceGroups
- class AssignResourceFromResourceGroupWorker # rubocop:disable Scalability/IdempotentWorker
+ # This worker is to assign a resource to a pipeline job from a resource group
+ # and enqueue the job to be executed by a runner.
+ # See https://docs.gitlab.com/ee/ci/yaml/#resource_group for more information.
+ class AssignResourceFromResourceGroupWorker
include ApplicationWorker
sidekiq_options retry: 3
@@ -11,6 +14,13 @@ module Ci
queue_namespace :pipeline_processing
feature_category :continuous_delivery
+ # This worker is idempotent that it produces the same result
+ # as long as the same resource group id is passed as an argument.
+ # Therefore, we can deduplicate the sidekiq jobs until the on-going
+ # assignment process has been finished.
+ idempotent!
+ deduplicate :until_executed
+
def perform(resource_group_id)
::Ci::ResourceGroup.find_by_id(resource_group_id).try do |resource_group|
Ci::ResourceGroups::AssignResourceFromResourceGroupService.new(resource_group.project, nil)
diff --git a/app/workers/clusters/applications/activate_service_worker.rb b/app/workers/clusters/applications/activate_service_worker.rb
index d4d0ae96e0..a7073b78a8 100644
--- a/app/workers/clusters/applications/activate_service_worker.rb
+++ b/app/workers/clusters/applications/activate_service_worker.rb
@@ -15,7 +15,7 @@ module Clusters
return unless cluster
cluster.all_projects.find_each do |project|
- project.find_or_initialize_service(service_name).update!(active: true)
+ project.find_or_initialize_integration(service_name).update!(active: true)
end
end
end
diff --git a/app/workers/clusters/applications/deactivate_service_worker.rb b/app/workers/clusters/applications/deactivate_service_worker.rb
index 935b455a4f..9337af5662 100644
--- a/app/workers/clusters/applications/deactivate_service_worker.rb
+++ b/app/workers/clusters/applications/deactivate_service_worker.rb
@@ -10,18 +10,18 @@ module Clusters
loggable_arguments 1
- def perform(cluster_id, service_name)
+ def perform(cluster_id, integration_name)
cluster = Clusters::Cluster.find_by_id(cluster_id)
- raise cluster_missing_error(service_name) unless cluster
+ raise cluster_missing_error(integration_name) unless cluster
- service = "#{service_name}_service".to_sym
- cluster.all_projects.with_service(service).find_each do |project|
- project.public_send(service).update!(active: false) # rubocop:disable GitlabSecurity/PublicSend
+ integration = ::Project.integration_association_name(integration_name).to_sym
+ cluster.all_projects.with_integration(integration).find_each do |project|
+ project.public_send(integration).update!(active: false) # rubocop:disable GitlabSecurity/PublicSend
end
end
- def cluster_missing_error(service)
- ActiveRecord::RecordNotFound.new("Can't deactivate #{service} services, host cluster not found! Some inconsistent records may be left in database.")
+ def cluster_missing_error(integration_name)
+ ActiveRecord::RecordNotFound.new("Can't deactivate #{integration_name} integrations, host cluster not found! Some inconsistent records may be left in database.")
end
end
end
diff --git a/app/workers/concerns/application_worker.rb b/app/workers/concerns/application_worker.rb
index 3cba1eb31c..e158ae0c29 100644
--- a/app/workers/concerns/application_worker.rb
+++ b/app/workers/concerns/application_worker.rb
@@ -47,11 +47,36 @@ module ApplicationWorker
end
class_methods do
+ extend ::Gitlab::Utils::Override
+
def inherited(subclass)
subclass.set_queue
subclass.after_set_class_attribute { subclass.set_queue }
end
+ override :validate_worker_attributes!
+ def validate_worker_attributes!
+ super
+
+ # Since the delayed data_consistency will use sidekiq built in retry mechanism, it is required that this mechanism
+ # is not disabled.
+ if retry_disabled? && get_data_consistency == :delayed
+ raise ArgumentError, "Retry support cannot be disabled if data_consistency is set to :delayed"
+ end
+ end
+
+ # Checks if sidekiq retry support is disabled
+ def retry_disabled?
+ get_sidekiq_options['retry'] == 0 || get_sidekiq_options['retry'] == false
+ end
+
+ override :sidekiq_options
+ def sidekiq_options(opts = {})
+ super.tap do
+ validate_worker_attributes!
+ end
+ end
+
def perform_async(*args)
# Worker execution for workers with data_consistency set to :delayed or :sticky
# will be delayed to give replication enough time to complete
diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb
index 6ebf7c7c26..1eff53cea0 100644
--- a/app/workers/concerns/gitlab/github_import/object_importer.rb
+++ b/app/workers/concerns/gitlab/github_import/object_importer.rb
@@ -36,14 +36,15 @@ module Gitlab
importer_class.new(object, project, client).execute
- counter.increment
+ Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
+
info(project.id, message: 'importer finished')
rescue StandardError => e
error(project.id, e, hash)
end
- def counter
- @counter ||= Gitlab::Metrics.counter(counter_name, counter_description)
+ def object_type
+ raise NotImplementedError
end
# Returns the representation class to use for the object. This class must
@@ -57,16 +58,6 @@ module Gitlab
raise NotImplementedError
end
- # Returns the name (as a Symbol) of the Prometheus counter.
- def counter_name
- raise NotImplementedError
- end
-
- # Returns the description (as a String) of the Prometheus counter.
- def counter_description
- raise NotImplementedError
- end
-
private
attr_accessor :github_id
diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb
index e62bd8d988..f8b945b889 100644
--- a/app/workers/concerns/waitable_worker.rb
+++ b/app/workers/concerns/waitable_worker.rb
@@ -32,7 +32,9 @@ module WaitableWorker
failed = []
args_list.each do |args|
- new.perform(*args)
+ worker = new
+ Gitlab::AppJsonLogger.info(worker.structured_payload(message: 'running inline'))
+ worker.perform(*args)
rescue StandardError
failed << args
end
diff --git a/app/workers/concerns/worker_attributes.rb b/app/workers/concerns/worker_attributes.rb
index 096be80878..806fce3863 100644
--- a/app/workers/concerns/worker_attributes.rb
+++ b/app/workers/concerns/worker_attributes.rb
@@ -12,6 +12,7 @@ module WorkerAttributes
VALID_URGENCIES = [:high, :low, :throttled].freeze
VALID_DATA_CONSISTENCIES = [:always, :sticky, :delayed].freeze
+ DEFAULT_DATA_CONSISTENCY = :always
NAMESPACE_WEIGHTS = {
auto_devops: 2,
@@ -110,7 +111,7 @@ module WorkerAttributes
end
def get_data_consistency
- class_attributes[:data_consistency] || :always
+ class_attributes[:data_consistency] || DEFAULT_DATA_CONSISTENCY
end
def get_data_consistency_feature_flag_enabled?
diff --git a/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb b/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
index 3027d46b8b..33dda6a8f0 100644
--- a/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
+++ b/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
@@ -49,15 +49,11 @@ module ContainerExpirationPolicies
end
def remaining_work_count
- total_count = cleanup_scheduled_count + cleanup_unfinished_count
+ count = cleanup_scheduled_count
- log_info(
- cleanup_scheduled_count: cleanup_scheduled_count,
- cleanup_unfinished_count: cleanup_unfinished_count,
- cleanup_total_count: total_count
- )
+ return count if count > max_running_jobs
- total_count
+ count + cleanup_unfinished_count
end
private
diff --git a/app/workers/container_expiration_policy_worker.rb b/app/workers/container_expiration_policy_worker.rb
index 8fc139ac87..a35ca5d184 100644
--- a/app/workers/container_expiration_policy_worker.rb
+++ b/app/workers/container_expiration_policy_worker.rb
@@ -17,6 +17,7 @@ class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWo
process_stale_ongoing_cleanups
disable_policies_without_container_repositories
throttling_enabled? ? perform_throttled : perform_unthrottled
+ log_counts
end
private
@@ -28,6 +29,26 @@ class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWo
end
end
+ def log_counts
+ use_replica_if_available do
+ required_count = ContainerRepository.requiring_cleanup.count
+ unfinished_count = ContainerRepository.with_unfinished_cleanup.count
+
+ log_extra_metadata_on_done(:cleanup_required_count, required_count)
+ log_extra_metadata_on_done(:cleanup_unfinished_count, unfinished_count)
+ log_extra_metadata_on_done(:cleanup_total_count, required_count + unfinished_count)
+ end
+ end
+
+ # data_consistency :delayed not used as this is a cron job and those jobs are
+ # not perfomed with a delay
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63635#note_603771207
+ def use_replica_if_available(&blk)
+ return yield unless ::Gitlab::Database::LoadBalancing.enable?
+
+ ::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries(&blk)
+ end
+
def process_stale_ongoing_cleanups
threshold = delete_tags_service_timeout.seconds + 30.minutes
ContainerRepository.with_stale_ongoing_cleanup(threshold.ago)
diff --git a/app/workers/database/partition_management_worker.rb b/app/workers/database/partition_management_worker.rb
new file mode 100644
index 0000000000..c9b1cd6d26
--- /dev/null
+++ b/app/workers/database/partition_management_worker.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Database
+ class PartitionManagementWorker
+ include ApplicationWorker
+
+ sidekiq_options retry: 3
+ include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+
+ feature_category :database
+ idempotent!
+
+ def perform
+ Gitlab::Database::Partitioning::PartitionManager.new.sync_partitions
+ ensure
+ Gitlab::Database::Partitioning::PartitionMonitoring.new.report_metrics
+ end
+ end
+end
diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb
index 9702fac39b..64f73d1fba 100644
--- a/app/workers/expire_pipeline_cache_worker.rb
+++ b/app/workers/expire_pipeline_cache_worker.rb
@@ -10,7 +10,7 @@ class ExpirePipelineCacheWorker
queue_namespace :pipeline_cache
urgency :high
worker_resource_boundary :cpu
- data_consistency :delayed, feature_flag: :load_balancing_for_expire_pipeline_cache_worker
+ data_consistency :delayed
# This worker _should_ be idempotent, but due to us moving this to data_consistency :delayed
# and an ongoing incompatibility between the two switches, we need to disable this.
diff --git a/app/workers/gitlab/github_import/import_diff_note_worker.rb b/app/workers/gitlab/github_import/import_diff_note_worker.rb
index 25fb037569..85b7d6c76b 100644
--- a/app/workers/gitlab/github_import/import_diff_note_worker.rb
+++ b/app/workers/gitlab/github_import/import_diff_note_worker.rb
@@ -13,12 +13,8 @@ module Gitlab
Importer::DiffNoteImporter
end
- def counter_name
- :github_importer_imported_diff_notes
- end
-
- def counter_description
- 'The number of imported GitHub pull request review comments'
+ def object_type
+ :diff_note
end
end
end
diff --git a/app/workers/gitlab/github_import/import_issue_worker.rb b/app/workers/gitlab/github_import/import_issue_worker.rb
index d9c496e3eb..8fdc0219ff 100644
--- a/app/workers/gitlab/github_import/import_issue_worker.rb
+++ b/app/workers/gitlab/github_import/import_issue_worker.rb
@@ -13,12 +13,8 @@ module Gitlab
Importer::IssueAndLabelLinksImporter
end
- def counter_name
- :github_importer_imported_issues
- end
-
- def counter_description
- 'The number of imported GitHub issues'
+ def object_type
+ :issue
end
end
end
diff --git a/app/workers/gitlab/github_import/import_lfs_object_worker.rb b/app/workers/gitlab/github_import/import_lfs_object_worker.rb
index 78f78fdb16..2a95366bac 100644
--- a/app/workers/gitlab/github_import/import_lfs_object_worker.rb
+++ b/app/workers/gitlab/github_import/import_lfs_object_worker.rb
@@ -13,12 +13,8 @@ module Gitlab
Importer::LfsObjectImporter
end
- def counter_name
- :github_importer_imported_lfs_objects
- end
-
- def counter_description
- 'The number of imported GitHub Lfs Objects'
+ def object_type
+ :lfs_object
end
end
end
diff --git a/app/workers/gitlab/github_import/import_note_worker.rb b/app/workers/gitlab/github_import/import_note_worker.rb
index d0f97a15af..2125c95377 100644
--- a/app/workers/gitlab/github_import/import_note_worker.rb
+++ b/app/workers/gitlab/github_import/import_note_worker.rb
@@ -13,12 +13,8 @@ module Gitlab
Importer::NoteImporter
end
- def counter_name
- :github_importer_imported_notes
- end
-
- def counter_description
- 'The number of imported GitHub comments'
+ def object_type
+ :note
end
end
end
diff --git a/app/workers/gitlab/github_import/import_pull_request_merged_by_worker.rb b/app/workers/gitlab/github_import/import_pull_request_merged_by_worker.rb
index a8b79cf9b3..91dab3470d 100644
--- a/app/workers/gitlab/github_import/import_pull_request_merged_by_worker.rb
+++ b/app/workers/gitlab/github_import/import_pull_request_merged_by_worker.rb
@@ -15,12 +15,8 @@ module Gitlab
Importer::PullRequestMergedByImporter
end
- def counter_name
- :github_importer_imported_pull_requests_merged_by
- end
-
- def counter_description
- 'The number of imported GitHub pull requests merged by'
+ def object_type
+ :pull_request_merged_by
end
end
end
diff --git a/app/workers/gitlab/github_import/import_pull_request_review_worker.rb b/app/workers/gitlab/github_import/import_pull_request_review_worker.rb
index 5ee88d5d32..de10fe4058 100644
--- a/app/workers/gitlab/github_import/import_pull_request_review_worker.rb
+++ b/app/workers/gitlab/github_import/import_pull_request_review_worker.rb
@@ -15,12 +15,8 @@ module Gitlab
Importer::PullRequestReviewImporter
end
- def counter_name
- :github_importer_imported_pull_request_reviews
- end
-
- def counter_description
- 'The number of imported GitHub pull request reviews'
+ def object_type
+ :pull_request_review
end
end
end
diff --git a/app/workers/gitlab/github_import/import_pull_request_worker.rb b/app/workers/gitlab/github_import/import_pull_request_worker.rb
index 9560874f24..79938a157d 100644
--- a/app/workers/gitlab/github_import/import_pull_request_worker.rb
+++ b/app/workers/gitlab/github_import/import_pull_request_worker.rb
@@ -13,12 +13,8 @@ module Gitlab
Importer::PullRequestImporter
end
- def counter_name
- :github_importer_imported_pull_requests
- end
-
- def counter_description
- 'The number of imported GitHub pull requests'
+ def object_type
+ :pull_request
end
end
end
diff --git a/app/workers/gitlab/github_import/stage/finish_import_worker.rb b/app/workers/gitlab/github_import/stage/finish_import_worker.rb
index f5980cc248..f909d7e2f3 100644
--- a/app/workers/gitlab/github_import/stage/finish_import_worker.rb
+++ b/app/workers/gitlab/github_import/stage/finish_import_worker.rb
@@ -29,7 +29,8 @@ module Gitlab
info(
project.id,
message: "GitHub project import finished",
- duration_s: duration.round(2)
+ duration_s: duration.round(2),
+ object_counts: ::Gitlab::GithubImport::ObjectCounter.summary(project)
)
end
diff --git a/app/workers/gitlab/import/stuck_import_job.rb b/app/workers/gitlab/import/stuck_import_job.rb
index ac789ce118..57fb3baf2b 100644
--- a/app/workers/gitlab/import/stuck_import_job.rb
+++ b/app/workers/gitlab/import/stuck_import_job.rb
@@ -5,7 +5,7 @@ module Gitlab
module StuckImportJob
extend ActiveSupport::Concern
- IMPORT_JOBS_EXPIRATION = 15.hours.seconds.to_i
+ IMPORT_JOBS_EXPIRATION = 24.hours.seconds.to_i
included do
include ApplicationWorker
diff --git a/app/workers/gitlab_usage_ping_worker.rb b/app/workers/gitlab_service_ping_worker.rb
similarity index 73%
rename from app/workers/gitlab_usage_ping_worker.rb
rename to app/workers/gitlab_service_ping_worker.rb
index 782b089261..a27629eac0 100644
--- a/app/workers/gitlab_usage_ping_worker.rb
+++ b/app/workers/gitlab_service_ping_worker.rb
@@ -1,19 +1,19 @@
# frozen_string_literal: true
-class GitlabUsagePingWorker # rubocop:disable Scalability/IdempotentWorker
- LEASE_KEY = 'gitlab_usage_ping_worker:ping'
+class GitlabServicePingWorker # rubocop:disable Scalability/IdempotentWorker
+ LEASE_KEY = 'gitlab_service_ping_worker:ping'
LEASE_TIMEOUT = 86400
include ApplicationWorker
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
include Gitlab::ExclusiveLeaseHelpers
- feature_category :usage_ping
+ feature_category :service_ping
sidekiq_options retry: 3, dead: false
sidekiq_retry_in { |count| (count + 1) * 8.hours.to_i }
def perform
- # Disable usage ping for GitLab.com
+ # Disable service ping for GitLab.com
# See https://gitlab.com/gitlab-org/gitlab/-/issues/292929 for details
return if Gitlab.com?
@@ -22,7 +22,7 @@ class GitlabUsagePingWorker # rubocop:disable Scalability/IdempotentWorker
# Splay the request over a minute to avoid thundering herd problems.
sleep(rand(0.0..60.0).round(3))
- SubmitUsagePingService.new.execute
+ ServicePing::SubmitService.new.execute
end
end
end
diff --git a/app/workers/jira_connect/forward_event_worker.rb b/app/workers/jira_connect/forward_event_worker.rb
new file mode 100644
index 0000000000..877ab46cfe
--- /dev/null
+++ b/app/workers/jira_connect/forward_event_worker.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module JiraConnect
+ class ForwardEventWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ queue_namespace :jira_connect
+ feature_category :integrations
+ worker_has_external_dependencies!
+
+ def perform(installation_id, base_path, event_path)
+ installation = JiraConnectInstallation.find_by_id(installation_id)
+
+ return if installation&.instance_url.nil?
+
+ proxy_url = installation.instance_url + event_path
+ qsh = Atlassian::Jwt.create_query_string_hash(proxy_url, 'POST', installation.instance_url + base_path)
+ jwt = Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret)
+
+ Gitlab::HTTP.post(proxy_url, headers: { 'Authorization' => "JWT #{jwt}" })
+ ensure
+ installation.destroy if installation
+ end
+ end
+end
diff --git a/app/workers/jira_connect/sync_branch_worker.rb b/app/workers/jira_connect/sync_branch_worker.rb
index 4e8566d86c..2723287b77 100644
--- a/app/workers/jira_connect/sync_branch_worker.rb
+++ b/app/workers/jira_connect/sync_branch_worker.rb
@@ -1,16 +1,17 @@
# frozen_string_literal: true
module JiraConnect
- class SyncBranchWorker
+ class SyncBranchWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
sidekiq_options retry: 3
queue_namespace :jira_connect
feature_category :integrations
+ data_consistency :delayed
loggable_arguments 1, 2
+
worker_has_external_dependencies!
- idempotent!
def perform(project_id, branch_name, commit_shas, update_sequence_id)
project = Project.find_by_id(project_id)
diff --git a/app/workers/jira_connect/sync_builds_worker.rb b/app/workers/jira_connect/sync_builds_worker.rb
index 11a3b59803..4c4daba331 100644
--- a/app/workers/jira_connect/sync_builds_worker.rb
+++ b/app/workers/jira_connect/sync_builds_worker.rb
@@ -1,18 +1,18 @@
# frozen_string_literal: true
module JiraConnect
- class SyncBuildsWorker
+ class SyncBuildsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
sidekiq_options retry: 3
- idempotent!
- worker_has_external_dependencies!
-
queue_namespace :jira_connect
feature_category :integrations
+ data_consistency :delayed
tags :exclude_from_kubernetes
+ worker_has_external_dependencies!
+
def perform(pipeline_id, sequence_id)
pipeline = Ci::Pipeline.find_by_id(pipeline_id)
diff --git a/app/workers/jira_connect/sync_deployments_worker.rb b/app/workers/jira_connect/sync_deployments_worker.rb
index 9f75b1161f..0dc34b5999 100644
--- a/app/workers/jira_connect/sync_deployments_worker.rb
+++ b/app/workers/jira_connect/sync_deployments_worker.rb
@@ -1,18 +1,18 @@
# frozen_string_literal: true
module JiraConnect
- class SyncDeploymentsWorker
+ class SyncDeploymentsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
sidekiq_options retry: 3
- idempotent!
- worker_has_external_dependencies!
-
queue_namespace :jira_connect
feature_category :integrations
+ data_consistency :delayed
tags :exclude_from_kubernetes
+ worker_has_external_dependencies!
+
def perform(deployment_id, sequence_id)
deployment = Deployment.find_by_id(deployment_id)
diff --git a/app/workers/jira_connect/sync_feature_flags_worker.rb b/app/workers/jira_connect/sync_feature_flags_worker.rb
index 0d8d3d3142..c484cabbe6 100644
--- a/app/workers/jira_connect/sync_feature_flags_worker.rb
+++ b/app/workers/jira_connect/sync_feature_flags_worker.rb
@@ -1,18 +1,18 @@
# frozen_string_literal: true
module JiraConnect
- class SyncFeatureFlagsWorker
+ class SyncFeatureFlagsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
sidekiq_options retry: 3
- idempotent!
- worker_has_external_dependencies!
-
queue_namespace :jira_connect
feature_category :integrations
+ data_consistency :delayed
tags :exclude_from_kubernetes
+ worker_has_external_dependencies!
+
def perform(feature_flag_id, sequence_id)
feature_flag = ::Operations::FeatureFlag.find_by_id(feature_flag_id)
diff --git a/app/workers/jira_connect/sync_merge_request_worker.rb b/app/workers/jira_connect/sync_merge_request_worker.rb
index bf31df2271..bb0d24667e 100644
--- a/app/workers/jira_connect/sync_merge_request_worker.rb
+++ b/app/workers/jira_connect/sync_merge_request_worker.rb
@@ -1,14 +1,14 @@
# frozen_string_literal: true
module JiraConnect
- class SyncMergeRequestWorker
+ class SyncMergeRequestWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
sidekiq_options retry: 3
queue_namespace :jira_connect
feature_category :integrations
- idempotent!
+ data_consistency :delayed
worker_has_external_dependencies!
diff --git a/app/workers/jira_connect/sync_project_worker.rb b/app/workers/jira_connect/sync_project_worker.rb
index dfff0c4b3b..317bace89b 100644
--- a/app/workers/jira_connect/sync_project_worker.rb
+++ b/app/workers/jira_connect/sync_project_worker.rb
@@ -1,15 +1,16 @@
# frozen_string_literal: true
module JiraConnect
- class SyncProjectWorker
+ class SyncProjectWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
sidekiq_options retry: 3
queue_namespace :jira_connect
feature_category :integrations
+ data_consistency :delayed
tags :exclude_from_kubernetes
- idempotent!
+
worker_has_external_dependencies!
MERGE_REQUEST_LIMIT = 400
diff --git a/app/workers/merge_request_cleanup_refs_worker.rb b/app/workers/merge_request_cleanup_refs_worker.rb
index 162c6dc2a8..408d070d56 100644
--- a/app/workers/merge_request_cleanup_refs_worker.rb
+++ b/app/workers/merge_request_cleanup_refs_worker.rb
@@ -2,6 +2,8 @@
class MergeRequestCleanupRefsWorker
include ApplicationWorker
+ include LimitedCapacity::Worker
+ include Gitlab::Utils::StrongMemoize
sidekiq_options retry: 3
@@ -9,20 +11,60 @@ class MergeRequestCleanupRefsWorker
tags :exclude_from_kubernetes
idempotent!
- def perform(merge_request_id)
+ # Hard-coded to 4 for now. Will be configurable later on via application settings.
+ # This means, there can only be 4 jobs running at the same time at maximum.
+ MAX_RUNNING_JOBS = 4
+ FAILURE_THRESHOLD = 3
+
+ def perform_work
return unless Feature.enabled?(:merge_request_refs_cleanup, default_enabled: false)
- merge_request = MergeRequest.find_by_id(merge_request_id)
-
unless merge_request
- logger.error("Failed to find merge request with ID: #{merge_request_id}")
+ logger.error('No existing merge request to be cleaned up.')
return
end
- result = ::MergeRequests::CleanupRefsService.new(merge_request).execute
+ log_extra_metadata_on_done(:merge_request_id, merge_request.id)
- return if result[:status] == :success
+ result = MergeRequests::CleanupRefsService.new(merge_request).execute
- logger.error("Failed cleanup refs of merge request (#{merge_request_id}): #{result[:message]}")
+ if result[:status] == :success
+ merge_request_cleanup_schedule.complete!
+ else
+ if merge_request_cleanup_schedule.failed_count < FAILURE_THRESHOLD
+ merge_request_cleanup_schedule.retry!
+ else
+ merge_request_cleanup_schedule.mark_as_failed!
+ end
+
+ log_extra_metadata_on_done(:message, result[:message])
+ end
+
+ log_extra_metadata_on_done(:status, merge_request_cleanup_schedule.status)
+ end
+
+ def remaining_work_count
+ MergeRequest::CleanupSchedule
+ .scheduled_and_unstarted
+ .limit(max_running_jobs)
+ .count
+ end
+
+ def max_running_jobs
+ MAX_RUNNING_JOBS
+ end
+
+ private
+
+ def merge_request
+ strong_memoize(:merge_request) do
+ merge_request_cleanup_schedule&.merge_request
+ end
+ end
+
+ def merge_request_cleanup_schedule
+ strong_memoize(:merge_request_cleanup_schedule) do
+ MergeRequest::CleanupSchedule.start_next
+ end
end
end
diff --git a/app/workers/namespaces/in_product_marketing_emails_worker.rb b/app/workers/namespaces/in_product_marketing_emails_worker.rb
index 7985325d1a..1f46be2955 100644
--- a/app/workers/namespaces/in_product_marketing_emails_worker.rb
+++ b/app/workers/namespaces/in_product_marketing_emails_worker.rb
@@ -14,7 +14,6 @@ module Namespaces
def perform
return if paid_self_managed_instance?
return if setting_disabled?
- return if experiment_inactive?
Namespaces::InProductMarketingEmailsService.send_for_all_tracks_and_intervals
end
@@ -28,10 +27,6 @@ module Namespaces
def setting_disabled?
!Gitlab::CurrentSettings.in_product_marketing_emails_enabled
end
-
- def experiment_inactive?
- Gitlab.com? && !Gitlab::Experimentation.active?(:in_product_marketing_emails)
- end
end
end
diff --git a/app/workers/packages/helm/extraction_worker.rb b/app/workers/packages/helm/extraction_worker.rb
new file mode 100644
index 0000000000..fd4e720da9
--- /dev/null
+++ b/app/workers/packages/helm/extraction_worker.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Packages
+ module Helm
+ class ExtractionWorker
+ include ApplicationWorker
+
+ queue_namespace :package_repositories
+ feature_category :package_registry
+ deduplicate :until_executing
+
+ idempotent!
+
+ def perform(channel, package_file_id)
+ package_file = ::Packages::PackageFile.find_by_id(package_file_id)
+
+ return unless package_file && !package_file.package.default?
+
+ ::Packages::Helm::ProcessFileService.new(channel, package_file).execute
+
+ rescue ::Packages::Helm::ExtractFileMetadataService::ExtractionError,
+ ::Packages::Helm::ProcessFileService::ExtractionError,
+ ::ActiveModel::ValidationError => e
+ Gitlab::ErrorTracking.log_exception(e, project_id: package_file.project_id)
+ package_file.package.update_column(:status, :error)
+ end
+ end
+ end
+end
diff --git a/app/workers/partition_creation_worker.rb b/app/workers/partition_creation_worker.rb
index 2b21741d6c..bb4834ab2d 100644
--- a/app/workers/partition_creation_worker.rb
+++ b/app/workers/partition_creation_worker.rb
@@ -10,8 +10,7 @@ class PartitionCreationWorker
idempotent!
def perform
- Gitlab::Database::Partitioning::PartitionCreator.new.create_partitions
- ensure
- Gitlab::Database::Partitioning::PartitionMonitoring.new.report_metrics
+ # This worker has been removed in favor of Database::PartitionManagementWorker
+ Database::PartitionManagementWorker.new.perform
end
end
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
index 97e6adbbf1..40d138752b 100644
--- a/app/workers/pipeline_hooks_worker.rb
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -8,7 +8,7 @@ class PipelineHooksWorker # rubocop:disable Scalability/IdempotentWorker
queue_namespace :pipeline_hooks
worker_resource_boundary :cpu
- data_consistency :delayed, feature_flag: :load_balancing_for_pipeline_hooks_worker
+ data_consistency :delayed
# rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
index 967be3b3e8..da38d2fc0c 100644
--- a/app/workers/project_service_worker.rb
+++ b/app/workers/project_service_worker.rb
@@ -15,6 +15,6 @@ class ProjectServiceWorker # rubocop:disable Scalability/IdempotentWorker
integration.execute(data)
rescue StandardError => error
integration_class = integration&.class&.name || "Not Found"
- logger.error class: self.class.name, service_class: integration_class, message: error.message
+ Gitlab::ErrorTracking.log_exception(error, integration_class: integration_class)
end
end
diff --git a/app/workers/projects/post_creation_worker.rb b/app/workers/projects/post_creation_worker.rb
index 1970f79729..389e987e81 100644
--- a/app/workers/projects/post_creation_worker.rb
+++ b/app/workers/projects/post_creation_worker.rb
@@ -15,21 +15,21 @@ module Projects
return unless project
- create_prometheus_service(project)
+ create_prometheus_integration(project)
end
private
- def create_prometheus_service(project)
- service = project.find_or_initialize_service(::PrometheusService.to_param)
+ def create_prometheus_integration(project)
+ integration = project.find_or_initialize_integration(::Integrations::Prometheus.to_param)
# If the service has already been inserted in the database, that
# means it came from a template, and there's nothing more to do.
- return if service.persisted?
+ return if integration.persisted?
- return unless service.prometheus_available?
+ return unless integration.prometheus_available?
- service.save!
+ integration.save!
rescue ActiveRecord::RecordInvalid => e
Gitlab::ErrorTracking.track_exception(e, extra: { project_id: project.id })
end
diff --git a/app/workers/prometheus/create_default_alerts_worker.rb b/app/workers/prometheus/create_default_alerts_worker.rb
index 0dba752ced..9d163cd828 100644
--- a/app/workers/prometheus/create_default_alerts_worker.rb
+++ b/app/workers/prometheus/create_default_alerts_worker.rb
@@ -15,7 +15,7 @@ module Prometheus
return unless project
- result = Prometheus::CreateDefaultAlertsService.new(project: project).execute
+ result = ::Prometheus::CreateDefaultAlertsService.new(project: project).execute
log_info(result.message) if result.error?
end
diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb
index a9a8201205..31d68e65b2 100644
--- a/app/workers/repository_check/single_repository_worker.rb
+++ b/app/workers/repository_check/single_repository_worker.rb
@@ -46,7 +46,7 @@ module RepositoryCheck
true
rescue Gitlab::Git::Repository::GitError => e
- Gitlab::RepositoryCheckLogger.error(e.message)
+ Gitlab::RepositoryCheckLogger.error("#{repository.full_path}: #{e.message}")
false
end
diff --git a/app/workers/schedule_merge_request_cleanup_refs_worker.rb b/app/workers/schedule_merge_request_cleanup_refs_worker.rb
index b5ea529887..40a773ca58 100644
--- a/app/workers/schedule_merge_request_cleanup_refs_worker.rb
+++ b/app/workers/schedule_merge_request_cleanup_refs_worker.rb
@@ -10,21 +10,10 @@ class ScheduleMergeRequestCleanupRefsWorker
tags :exclude_from_kubernetes
idempotent!
- # Based on existing data, MergeRequestCleanupRefsWorker can run 3 jobs per
- # second. This means that 180 jobs can be performed but since there are some
- # spikes from time time, it's better to give it some allowance.
- LIMIT = 180
- DELAY = 10.seconds
- BATCH_SIZE = 30
-
def perform
return if Gitlab::Database.read_only?
return unless Feature.enabled?(:merge_request_refs_cleanup, default_enabled: false)
- ids = MergeRequest::CleanupSchedule.scheduled_merge_request_ids(LIMIT).map { |id| [id] }
-
- MergeRequestCleanupRefsWorker.bulk_perform_in(DELAY, ids, batch_size: BATCH_SIZE) # rubocop:disable Scalability/BulkPerformWithContext
-
- log_extra_metadata_on_done(:merge_requests_count, ids.size)
+ MergeRequestCleanupRefsWorker.perform_with_capacity
end
end
diff --git a/babel.config.js b/babel.config.js
index 4dfca8f614..d10de05258 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -19,6 +19,10 @@ const plugins = [
'@babel/plugin-proposal-private-methods',
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/229146
'@babel/plugin-transform-arrow-functions',
+ // See: https://gitlab.com/gitlab-org/gitlab/-/issues/336216
+ '@babel/plugin-proposal-optional-chaining',
+ // See: https://gitlab.com/gitlab-org/gitlab/-/issues/336216
+ '@babel/plugin-proposal-nullish-coalescing-operator',
'lodash',
];
diff --git a/bin/actioncable b/bin/actioncable
deleted file mode 100755
index 14600ec117..0000000000
--- a/bin/actioncable
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/sh
-
-set -e
-
-cd $(dirname $0)/..
-app_root=$(pwd)
-
-puma_pidfile="$app_root/tmp/pids/puma_actioncable.pid"
-puma_config="$app_root/config/puma_actioncable.rb"
-
-spawn_puma()
-{
- exec bundle exec puma --config "${puma_config}" --environment "$RAILS_ENV" "$@"
-}
-
-get_puma_pid()
-{
- pid=$(cat "${puma_pidfile}")
- if [ -z "$pid" ] ; then
- echo "Could not find a PID in $puma_pidfile"
- exit 1
- fi
- echo "${pid}"
-}
-
-start()
-{
- spawn_puma -d
-}
-
-start_foreground()
-{
- spawn_puma
-}
-
-stop()
-{
- get_puma_pid
- kill -INT "$(get_puma_pid)"
-}
-
-reload()
-{
- kill -USR2 "$(get_puma_pid)"
-}
-
-case "$1" in
- start)
- start
- ;;
- start_foreground)
- start_foreground
- ;;
- stop)
- stop
- ;;
- reload)
- reload
- ;;
- *)
- echo "Usage: RAILS_ENV=your_env $0 {start|start_foreground|stop|reload}"
- ;;
-esac
diff --git a/bin/background_jobs_sk b/bin/background_jobs_sk
index 0aab69126b..0e9a5365d4 100755
--- a/bin/background_jobs_sk
+++ b/bin/background_jobs_sk
@@ -24,13 +24,13 @@ restart()
fi
pkill -u $gitlab_user -f 'sidekiq [0-9]'
- start_sidekiq -P $sidekiq_pidfile -d -L $sidekiq_logfile >> $sidekiq_logfile 2>&1
+ start_sidekiq -P $sidekiq_pidfile -d -L $sidekiq_logfile "$@" >> $sidekiq_logfile 2>&1
}
# Starts on foreground but output to the logfile instead stdout.
start_silent()
{
- start_sidekiq >> $sidekiq_logfile 2>&1
+ start_sidekiq "$@" >> $sidekiq_logfile 2>&1
}
start_sidekiq()
@@ -50,17 +50,17 @@ case "$1" in
stop
;;
start)
- restart
+ restart "$@"
;;
start_silent)
warn "Deprecated: Will be removed at 13.0 (see https://gitlab.com/gitlab-org/gitlab/-/issues/196731)."
start_silent
;;
start_foreground)
- start_sidekiq
+ start_sidekiq "$@"
;;
restart)
- restart
+ restart "$@"
;;
*)
echo "Usage: RAILS_ENV=
Elasticsearch for
searching?}
- B1[Check Admin Area > Integrations
to ensure the settings are correct]
+ B1[From the Admin Area, select
Integrations from the left
sidebar to ensure the settings
are correct.]
B2[Perform a search via
the rails console]
B3[If all settings are correct
and it still doesn't show Elasticsearch
doing the searches, escalate
to GitLab support.]
B4[Perform
the same search via the
Elasticsearch API]
@@ -196,7 +196,9 @@ Troubleshooting search result issues is rather straight forward on Elasticsearch
The first step is to confirm GitLab is using Elasticsearch for the search function.
To do this:
-1. Confirm the integration is enabled in **Admin Area > Settings > General**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. On the left sidebar, select **Settings > General**, and then confirm the
+ integration is enabled.
1. Confirm searches use Elasticsearch by accessing the rails console
(`sudo gitlab-rails console`) and running the following commands:
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index 92070a86a0..08755dd328 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -275,7 +275,24 @@ integration active:
p = Project.find_by_sql("SELECT p.id FROM projects p LEFT JOIN services s ON p.id = s.project_id WHERE s.type = 'JiraService' AND s.active = true")
p.each do |project|
- project.jira_service.update_attribute(:password, '