summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/pikaday.vue48
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue49
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue148
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue37
-rw-r--r--app/assets/javascripts/vue_shared/components/svg_gradient.vue34
-rw-r--r--app/assets/stylesheets/framework/buttons.scss23
-rw-r--r--app/assets/stylesheets/framework/header.scss43
-rw-r--r--app/assets/stylesheets/page_bundles/milestone.scss6
-rw-r--r--app/assets/stylesheets/pages/issuable.scss8
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss84
-rw-r--r--app/assets/stylesheets/startup/startup-general.scss80
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/finders/autocomplete/users_finder.rb8
-rw-r--r--app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb2
-rw-r--r--app/models/member.rb2
-rw-r--r--app/models/user.rb47
-rw-r--r--app/models/users_star_project.rb2
-rw-r--r--app/services/ci/retry_build_service.rb4
-rw-r--r--app/services/merge_requests/rebase_service.rb4
-rw-r--r--app/services/merge_requests/squash_service.rb4
-rw-r--r--app/views/layouts/header/_default.html.haml14
22 files changed, 119 insertions, 532 deletions
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 25468d4a697..4664bb56958 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -1,4 +1,4 @@
-import Store from 'ee_else_ce/sidebar/stores/sidebar_store';
+import Store from '~/sidebar/stores/sidebar_store';
import createFlash from '~/flash';
import { __, sprintf } from '~/locale';
import toast from '~/vue_shared/plugins/global_toast';
diff --git a/app/assets/javascripts/vue_shared/components/pikaday.vue b/app/assets/javascripts/vue_shared/components/pikaday.vue
deleted file mode 100644
index 3c0ac32e512..00000000000
--- a/app/assets/javascripts/vue_shared/components/pikaday.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<script>
-import { GlDatepicker } from '@gitlab/ui';
-import { pikadayToString } from '~/lib/utils/datetime_utility';
-
-export default {
- name: 'DatePicker',
- components: {
- GlDatepicker,
- },
- props: {
- selectedDate: {
- type: Date,
- required: false,
- default: null,
- },
- minDate: {
- type: Date,
- required: false,
- default: null,
- },
- maxDate: {
- type: Date,
- required: false,
- default: null,
- },
- },
- methods: {
- selected(date) {
- this.$emit('newDateSelected', pikadayToString(date));
- },
- toggled() {
- this.$emit('hidePicker');
- },
- },
-};
-</script>
-
-<template>
- <gl-datepicker
- :value="selectedDate"
- :min-date="minDate"
- :max-date="maxDate"
- start-opened
- @close="toggled"
- @click="toggled"
- @input="selected"
- />
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
deleted file mode 100644
index 460a10e08ed..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-
-export default {
- name: 'CollapsedCalendarIcon',
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- components: {
- GlIcon,
- },
- props: {
- containerClass: {
- type: String,
- required: false,
- default: '',
- },
- text: {
- type: String,
- required: false,
- default: '',
- },
- showIcon: {
- type: Boolean,
- required: false,
- default: true,
- },
- tooltipText: {
- type: String,
- required: false,
- default: '',
- },
- },
- methods: {
- click() {
- this.$emit('click');
- },
- },
-};
-</script>
-
-<template>
- <div v-gl-tooltip.left.viewport="tooltipText" :class="containerClass" @click="click">
- <gl-icon v-if="showIcon" name="calendar" />
- <slot>
- <span> {{ text }} </span>
- </slot>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
deleted file mode 100644
index 4531fafbf72..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
+++ /dev/null
@@ -1,148 +0,0 @@
-<script>
-import { GlLoadingIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
-import { dateInWords } from '../../../lib/utils/datetime_utility';
-import datePicker from '../pikaday.vue';
-import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
-import toggleSidebar from './toggle_sidebar.vue';
-
-export default {
- name: 'SidebarDatePicker',
- components: {
- datePicker,
- toggleSidebar,
- collapsedCalendarIcon,
- GlLoadingIcon,
- },
- props: {
- blockClass: {
- type: String,
- required: false,
- default: '',
- },
- collapsed: {
- type: Boolean,
- required: false,
- default: true,
- },
- showToggleSidebar: {
- type: Boolean,
- required: false,
- default: false,
- },
- isLoading: {
- type: Boolean,
- required: false,
- default: false,
- },
- editable: {
- type: Boolean,
- required: false,
- default: false,
- },
- label: {
- type: String,
- required: false,
- default: __('Date picker'),
- },
- selectedDate: {
- type: Date,
- required: false,
- default: null,
- },
- minDate: {
- type: Date,
- required: false,
- default: null,
- },
- maxDate: {
- type: Date,
- required: false,
- default: null,
- },
- },
- data() {
- return {
- editing: false,
- };
- },
- computed: {
- selectedAndEditable() {
- return this.selectedDate && this.editable;
- },
- selectedDateWords() {
- return dateInWords(this.selectedDate, true);
- },
- collapsedText() {
- return this.selectedDateWords ? this.selectedDateWords : __('None');
- },
- },
- methods: {
- stopEditing() {
- this.editing = false;
- },
- toggleDatePicker() {
- this.editing = !this.editing;
- },
- newDateSelected(date = null) {
- this.date = date;
- this.editing = false;
- this.$emit('saveDate', date);
- },
- toggleSidebar() {
- this.$emit('toggleCollapse');
- },
- },
-};
-</script>
-
-<template>
- <div :class="blockClass" class="block">
- <div class="issuable-sidebar-header">
- <toggle-sidebar :collapsed="collapsed" @toggle="toggleSidebar" />
- </div>
- <collapsed-calendar-icon :text="collapsedText" class="sidebar-collapsed-icon" />
- <div class="title">
- {{ label }}
- <gl-loading-icon v-if="isLoading" size="sm" :inline="true" />
- <div class="float-right">
- <button
- v-if="editable && !editing"
- type="button"
- class="btn-blank btn-link btn-primary-hover-link btn-sidebar-action"
- @click="toggleDatePicker"
- >
- {{ __('Edit') }}
- </button>
- <toggle-sidebar v-if="showToggleSidebar" :collapsed="collapsed" @toggle="toggleSidebar" />
- </div>
- </div>
- <div class="value">
- <date-picker
- v-if="editing"
- :selected-date="selectedDate"
- :min-date="minDate"
- :max-date="maxDate"
- :label="label"
- @newDateSelected="newDateSelected"
- @hidePicker="stopEditing"
- />
- <span v-else class="value-content">
- <template v-if="selectedDate">
- <strong>{{ selectedDateWords }}</strong>
- <span v-if="selectedAndEditable" class="no-value">
- -
- <button
- type="button"
- class="btn-blank btn-link btn-secondary-hover-link"
- @click="newDateSelected(null)"
- >
- {{ __('remove') }}
- </button>
- </span>
- </template>
- <span v-else class="no-value">{{ __('None') }}</span>
- </span>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue b/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue
deleted file mode 100644
index 17904f20341..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownForm, GlDropdownDivider } from '@gitlab/ui';
-
-export default {
- components: {
- GlDropdownForm,
- GlDropdown,
- GlDropdownDivider,
- },
- props: {
- headerText: {
- type: String,
- required: true,
- },
- text: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <gl-dropdown class="show" :text="text" @toggle="$emit('toggle')">
- <template #header>
- <p class="gl-font-weight-bold gl-text-center gl-mt-2 gl-mb-4">{{ headerText }}</p>
- <gl-dropdown-divider />
- <slot name="search"></slot>
- </template>
- <gl-dropdown-form>
- <slot name="items"></slot>
- </gl-dropdown-form>
- <template #footer>
- <slot name="footer"></slot>
- </template>
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/svg_gradient.vue b/app/assets/javascripts/vue_shared/components/svg_gradient.vue
deleted file mode 100644
index 5ce45d492f9..00000000000
--- a/app/assets/javascripts/vue_shared/components/svg_gradient.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<script>
-export default {
- props: {
- colors: {
- type: Array,
- required: true,
- validator(value) {
- return value.length === 2;
- },
- },
- opacity: {
- type: Array,
- required: true,
- validator(value) {
- return value.length === 2;
- },
- },
- identifierName: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-<template>
- <svg height="0" width="0">
- <defs>
- <linearGradient :id="identifierName">
- <stop :stop-color="colors[0]" :stop-opacity="opacity[0]" offset="0%" />
- <stop :stop-color="colors[1]" :stop-opacity="opacity[1]" offset="100%" />
- </linearGradient>
- </defs>
- </svg>
-</template>
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 1d7457e3c56..9cebd4f49a4 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -366,29 +366,6 @@
background-color: transparent;
border-color: transparent;
}
-
- &.btn-secondary-hover-link,
- &.btn-default-hover-link {
- color: $gl-text-color-secondary;
-
- &:hover,
- &:active,
- &:focus {
- color: $blue-600;
- text-decoration: none;
- }
- }
-
- &.btn-primary-hover-link {
- color: inherit;
-
- &:hover,
- &:active,
- &:focus {
- color: $blue-600;
- text-decoration: none;
- }
- }
}
// The .btn-svg class is available for legacy icon buttons to
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 89d59587cba..1004383cfd3 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -415,49 +415,6 @@ $top-nav-hover-bg: var(--indigo-900-alpha-008, $indigo-900-alpha-008) !important
}
}
-.title-container,
-.navbar-nav {
- .badge.badge-pill:not(.gl-badge) {
- position: inherit;
- font-weight: $gl-font-weight-normal;
- margin-left: -6px;
- font-size: 11px;
- color: var(--gray-950, $white);
- padding: 0 5px;
- line-height: 12px;
- border-radius: 7px;
- box-shadow: 0 1px 0 rgba($gl-header-color, 0.2);
-
- &.green-badge {
- background-color: var(--green-400, $green-400);
- }
-
- &.merge-requests-count {
- background-color: var(--orange-400, $orange-400);
- }
-
- &.todos-count {
- background-color: var(--blue-400, $blue-400);
- }
- }
-
- .canary-badge {
- .badge {
- font-size: $gl-font-size-small;
- line-height: $gl-line-height;
- padding: 0 $grid-size;
- }
-
- &:hover {
- text-decoration: none;
-
- .badge {
- text-decoration: none;
- }
- }
- }
-}
-
@include media-breakpoint-down(xs) {
.navbar-gitlab .container-fluid {
font-size: 18px;
diff --git a/app/assets/stylesheets/page_bundles/milestone.scss b/app/assets/stylesheets/page_bundles/milestone.scss
index 08d9d24d246..989219552a6 100644
--- a/app/assets/stylesheets/page_bundles/milestone.scss
+++ b/app/assets/stylesheets/page_bundles/milestone.scss
@@ -42,12 +42,6 @@ $status-box-line-height: 26px;
}
.milestone-content {
- .issues-count {
- margin-right: 17px;
- float: right;
- width: 105px;
- }
-
.issuable-row {
span {
a {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index ad05cbd1f0e..d2a3d17e1bb 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -274,16 +274,10 @@
font-weight: $gl-font-weight-normal;
}
- .no-value,
- .btn-default-hover-link,
- .btn-secondary-hover-link {
+ .no-value {
color: $gl-text-color-secondary;
}
- .btn-secondary-hover-link:hover {
- color: $blue-600;
- }
-
.sidebar-collapsed-icon {
display: none;
}
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index f14298c2cf7..e4a1a6dc016 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -7,12 +7,8 @@ body.gl-dark {
--gray-100: #404040;
--gray-600: #bfbfbf;
--gray-900: #fafafa;
- --gray-950: #fff;
--green-100: #0d532a;
- --green-400: #108548;
--green-700: #91d4a8;
- --blue-400: #1f75cb;
- --orange-400: #ab6100;
--indigo-900-alpha-008: rgba(235, 235, 250, 0.08);
--gl-text-color: #fafafa;
--border-color: #4f4f4f;
@@ -314,10 +310,18 @@ h1 {
padding-left: 0.6em;
border-radius: 10rem;
}
+.badge-success {
+ color: #fff;
+ background-color: #2da160;
+}
.badge-info {
color: #fff;
background-color: #428fdc;
}
+.badge-warning {
+ color: #fff;
+ background-color: #c17d10;
+}
.bg-transparent {
background-color: transparent !important;
}
@@ -394,6 +398,34 @@ a.gl-badge.badge-info:active {
0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
outline: none;
}
+.gl-badge.badge-success {
+ background-color: #0d532a;
+ color: #91d4a8;
+}
+a.gl-badge.badge-success.active,
+a.gl-badge.badge-success:active {
+ color: #ecf4ee;
+ background-color: #24663b;
+}
+a.gl-badge.badge-success:active {
+ box-shadow: inset 0 0 0 1px rgba(51, 51, 51, 0.8),
+ 0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
+ outline: none;
+}
+.gl-badge.badge-warning {
+ background-color: #703800;
+ color: #e9be74;
+}
+a.gl-badge.badge-warning.active,
+a.gl-badge.badge-warning:active {
+ color: #fdf1dd;
+ background-color: #8f4700;
+}
+a.gl-badge.badge-warning:active {
+ box-shadow: inset 0 0 0 1px rgba(51, 51, 51, 0.8),
+ 0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
+ outline: none;
+}
.gl-button .gl-badge {
top: 0;
}
@@ -920,36 +952,6 @@ input {
line-height: 18px;
margin: 4px 0 4px 2px;
}
-.title-container .badge.badge-pill:not(.gl-badge),
-.navbar-nav .badge.badge-pill:not(.gl-badge) {
- position: inherit;
- font-weight: 400;
- margin-left: -6px;
- font-size: 11px;
- color: var(--gray-950, #333);
- padding: 0 5px;
- line-height: 12px;
- border-radius: 7px;
- box-shadow: 0 1px 0 rgba(76, 78, 84, 0.2);
-}
-.title-container .badge.badge-pill:not(.gl-badge).green-badge,
-.navbar-nav .badge.badge-pill:not(.gl-badge).green-badge {
- background-color: var(--green-400, #108548);
-}
-.title-container .badge.badge-pill:not(.gl-badge).merge-requests-count,
-.navbar-nav .badge.badge-pill:not(.gl-badge).merge-requests-count {
- background-color: var(--orange-400, #ab6100);
-}
-.title-container .badge.badge-pill:not(.gl-badge).todos-count,
-.navbar-nav .badge.badge-pill:not(.gl-badge).todos-count {
- background-color: var(--blue-400, #1f75cb);
-}
-.title-container .canary-badge .badge,
-.navbar-nav .canary-badge .badge {
- font-size: 12px;
- line-height: 16px;
- padding: 0 0.5rem;
-}
@media (max-width: 575.98px) {
.navbar-gitlab .container-fluid {
font-size: 18px;
@@ -2022,18 +2024,9 @@ body.gl-dark {
white-space: nowrap;
width: 1px;
}
-.gl-bg-green-500 {
- background-color: #2da160;
-}
.gl-border-none\! {
border-style: none !important;
}
-.gl-rounded-pill {
- border-radius: 0.75rem;
-}
-.gl-text-white {
- color: #333;
-}
.gl-display-none {
display: none;
}
@@ -2055,9 +2048,8 @@ body.gl-dark {
.gl-pr-2 {
padding-right: 0.25rem;
}
-.gl-py-1 {
- padding-top: 0.125rem;
- padding-bottom: 0.125rem;
+.gl-ml-n2 {
+ margin-left: -0.25rem;
}
.gl-ml-3 {
margin-left: 0.5rem;
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 7eced0f3b07..fb4ca0dc736 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -295,10 +295,18 @@ h1 {
padding-left: 0.6em;
border-radius: 10rem;
}
+.badge-success {
+ color: #fff;
+ background-color: #108548;
+}
.badge-info {
color: #fff;
background-color: #1f75cb;
}
+.badge-warning {
+ color: #fff;
+ background-color: #ab6100;
+}
.bg-transparent {
background-color: transparent !important;
}
@@ -375,6 +383,34 @@ a.gl-badge.badge-info:active {
0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
outline: none;
}
+.gl-badge.badge-success {
+ background-color: #c3e6cd;
+ color: #24663b;
+}
+a.gl-badge.badge-success.active,
+a.gl-badge.badge-success:active {
+ color: #0a4020;
+ background-color: #91d4a8;
+}
+a.gl-badge.badge-success:active {
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.8),
+ 0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
+ outline: none;
+}
+.gl-badge.badge-warning {
+ background-color: #f5d9a8;
+ color: #8f4700;
+}
+a.gl-badge.badge-warning.active,
+a.gl-badge.badge-warning:active {
+ color: #5c2900;
+ background-color: #e9be74;
+}
+a.gl-badge.badge-warning:active {
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.8),
+ 0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
+ outline: none;
+}
.gl-button .gl-badge {
top: 0;
}
@@ -901,36 +937,6 @@ input {
line-height: 18px;
margin: 4px 0 4px 2px;
}
-.title-container .badge.badge-pill:not(.gl-badge),
-.navbar-nav .badge.badge-pill:not(.gl-badge) {
- position: inherit;
- font-weight: 400;
- margin-left: -6px;
- font-size: 11px;
- color: var(--gray-950, #fff);
- padding: 0 5px;
- line-height: 12px;
- border-radius: 7px;
- box-shadow: 0 1px 0 rgba(76, 78, 84, 0.2);
-}
-.title-container .badge.badge-pill:not(.gl-badge).green-badge,
-.navbar-nav .badge.badge-pill:not(.gl-badge).green-badge {
- background-color: var(--green-400, #2da160);
-}
-.title-container .badge.badge-pill:not(.gl-badge).merge-requests-count,
-.navbar-nav .badge.badge-pill:not(.gl-badge).merge-requests-count {
- background-color: var(--orange-400, #c17d10);
-}
-.title-container .badge.badge-pill:not(.gl-badge).todos-count,
-.navbar-nav .badge.badge-pill:not(.gl-badge).todos-count {
- background-color: var(--blue-400, #428fdc);
-}
-.title-container .canary-badge .badge,
-.navbar-nav .canary-badge .badge {
- font-size: 12px;
- line-height: 16px;
- padding: 0 0.5rem;
-}
@media (max-width: 575.98px) {
.navbar-gitlab .container-fluid {
font-size: 18px;
@@ -1691,18 +1697,9 @@ svg.s16 {
white-space: nowrap;
width: 1px;
}
-.gl-bg-green-500 {
- background-color: #108548;
-}
.gl-border-none\! {
border-style: none !important;
}
-.gl-rounded-pill {
- border-radius: 0.75rem;
-}
-.gl-text-white {
- color: #fff;
-}
.gl-display-none {
display: none;
}
@@ -1724,9 +1721,8 @@ svg.s16 {
.gl-pr-2 {
padding-right: 0.25rem;
}
-.gl-py-1 {
- padding-top: 0.125rem;
- padding-bottom: 0.125rem;
+.gl-ml-n2 {
+ margin-left: -0.25rem;
}
.gl-ml-3 {
margin-left: 0.5rem;
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index b40e2affcee..cd3f65cf044 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -16,7 +16,7 @@ class Admin::UsersController < Admin::ApplicationController
return redirect_to admin_cohorts_path if params[:tab] == 'cohorts'
@users = User.filter_items(params[:filter]).order_name_asc
- @users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
+ @users = @users.search(params[:search_query], with_private_emails: true) if params[:search_query].present?
@users = users_with_included_associations(@users)
@users = @users.sort_by_attribute(@sort = params[:sort])
@users = @users.page(params[:page])
diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb
index a9fffd3f411..33d9a8a3dbc 100644
--- a/app/finders/autocomplete/users_finder.rb
+++ b/app/finders/autocomplete/users_finder.rb
@@ -62,7 +62,7 @@ module Autocomplete
find_users
.active
.reorder_by_name
- .optionally_search(search)
+ .optionally_search(search, use_minimum_char_limit: use_minimum_char_limit)
.where_not_in(skip_users)
.limit_to_todo_authors(
user: current_user,
@@ -99,6 +99,12 @@ module Autocomplete
ActiveRecord::Associations::Preloader.new.preload(items, :status)
end
# rubocop: enable CodeReuse/ActiveRecord
+
+ def use_minimum_char_limit
+ return if project.blank? && group.blank? # We return nil so that we use the default defined in the User model
+
+ false
+ end
end
end
diff --git a/app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb b/app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb
index c4f91d0c15c..b1db355aa40 100644
--- a/app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb
+++ b/app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb
@@ -8,7 +8,7 @@ module Mutations
ADMIN_MESSAGE = 'You must be an admin to use this mutation'
- ::Gitlab::ApplicationContext::KNOWN_KEYS.each do |key|
+ ::Gitlab::ApplicationContext.known_keys.each do |key|
argument key,
GraphQL::Types::String,
required: false,
diff --git a/app/models/member.rb b/app/models/member.rb
index 1c1b603b4c7..b46a497dd69 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -204,7 +204,7 @@ class Member < ApplicationRecord
class << self
def search(query)
- joins(:user).merge(User.search(query))
+ joins(:user).merge(User.search(query, use_minimum_char_limit: false))
end
def search_invite_email(query)
diff --git a/app/models/user.rb b/app/models/user.rb
index 65061305c02..313c1726429 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -668,7 +668,8 @@ class User < ApplicationRecord
sanitized_order_sql = Arel.sql(sanitize_sql_array([order, query: query]))
- scope = options[:with_private_emails] ? search_with_secondary_emails(query) : search_with_public_emails(query)
+ scope = options[:with_private_emails] ? with_primary_or_secondary_email(query) : with_public_email(query)
+ scope = scope.or(search_by_name_or_username(query, use_minimum_char_limit: options[:use_minimum_char_limit]))
scope.reorder(sanitized_order_sql, :name)
end
@@ -685,50 +686,32 @@ class User < ApplicationRecord
reorder(:name)
end
- def search_with_public_emails(query)
- return none if query.blank?
-
- query = query.downcase
+ # searches user by given pattern
+ # it compares name and username fields with given pattern
+ # This method uses ILIKE on PostgreSQL.
+ def search_by_name_or_username(query, use_minimum_char_limit: nil)
+ use_minimum_char_limit = user_search_minimum_char_limit if use_minimum_char_limit.nil?
where(
- fuzzy_arel_match(:name, query, use_minimum_char_limit: user_search_minimum_char_limit)
- .or(fuzzy_arel_match(:username, query, use_minimum_char_limit: user_search_minimum_char_limit))
- .or(arel_table[:public_email].eq(query))
+ fuzzy_arel_match(:name, query, use_minimum_char_limit: use_minimum_char_limit)
+ .or(fuzzy_arel_match(:username, query, use_minimum_char_limit: use_minimum_char_limit))
)
end
- def search_without_secondary_emails(query)
- return none if query.blank?
-
- query = query.downcase
-
- where(
- fuzzy_arel_match(:name, query, lower_exact_match: true)
- .or(fuzzy_arel_match(:username, query, lower_exact_match: true))
- .or(arel_table[:email].eq(query))
- )
+ def with_public_email(email_address)
+ where(public_email: email_address)
end
- # searches user by given pattern
- # it compares name, email, username fields and user's secondary emails with given pattern
- # This method uses ILIKE on PostgreSQL.
-
- def search_with_secondary_emails(query)
- return none if query.blank?
-
- query = query.downcase
-
+ def with_primary_or_secondary_email(email_address)
email_table = Email.arel_table
matched_by_email_user_id = email_table
.project(email_table[:user_id])
- .where(email_table[:email].eq(query))
+ .where(email_table[:email].eq(email_address))
.take(1) # at most 1 record as there is a unique constraint
where(
- fuzzy_arel_match(:name, query, use_minimum_char_limit: user_search_minimum_char_limit)
- .or(fuzzy_arel_match(:username, query, use_minimum_char_limit: user_search_minimum_char_limit))
- .or(arel_table[:email].eq(query))
- .or(arel_table[:id].eq(matched_by_email_user_id))
+ arel_table[:email].eq(email_address)
+ .or(arel_table[:id].eq(matched_by_email_user_id))
)
end
diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb
index c633e2d8b3d..1549c099a64 100644
--- a/app/models/users_star_project.rb
+++ b/app/models/users_star_project.rb
@@ -32,7 +32,7 @@ class UsersStarProject < ApplicationRecord
end
def search(query)
- joins(:user).merge(User.search(query))
+ joins(:user).merge(User.search(query, use_minimum_char_limit: false))
end
end
end
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 2371b3615ee..71588f714e8 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -70,7 +70,9 @@ module Ci
def check_assignable_runners!(build); end
def clone_build(build)
- project.builds.new(build_attributes(build))
+ project.builds.new(build_attributes(build)).tap do |new_build|
+ yield(new_build) if block_given?
+ end
end
def build_attributes(build)
diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb
index d1f45b4b49c..1c4e1784b34 100644
--- a/app/services/merge_requests/rebase_service.rb
+++ b/app/services/merge_requests/rebase_service.rb
@@ -2,7 +2,7 @@
module MergeRequests
class RebaseService < MergeRequests::BaseService
- REBASE_ERROR = 'Rebase failed. Please rebase locally'
+ REBASE_ERROR = 'Rebase failed: Rebase locally, resolve all conflicts, then push the branch.'
attr_reader :merge_request, :rebase_error
@@ -35,7 +35,7 @@ module MergeRequests
def set_rebase_error(exception)
@rebase_error =
if exception.is_a?(Gitlab::Git::PreReceiveError)
- "Something went wrong during the rebase pre-receive hook: #{exception.message}."
+ "The rebase pre-receive hook failed: #{exception.message}."
else
REBASE_ERROR
end
diff --git a/app/services/merge_requests/squash_service.rb b/app/services/merge_requests/squash_service.rb
index 69b9740c2a5..f04682bf08a 100644
--- a/app/services/merge_requests/squash_service.rb
+++ b/app/services/merge_requests/squash_service.rb
@@ -9,9 +9,9 @@ module MergeRequests
return success(squash_sha: merge_request.diff_head_sha)
end
- return error(s_('MergeRequests|This project does not allow squashing commits when merge requests are accepted.')) if squash_forbidden?
+ return error(s_("MergeRequests|Squashing not allowed: This project doesn't allow you to squash commits when merging.")) if squash_forbidden?
- squash! || error(s_('MergeRequests|Failed to squash. Should be done manually.'))
+ squash! || error(s_('MergeRequests|Squashing failed: Squash the commits locally, resolve any conflicts, then push the branch.'))
end
private
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 246a31f86c9..0a3cdb5b5cd 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -16,7 +16,7 @@
= logo_text
- if Gitlab.com_and_canary?
= link_to Gitlab::Saas.canary_toggle_com_url, class: 'canary-badge bg-transparent', data: { qa_selector: 'canary_badge_link' }, target: :_blank, rel: 'noopener noreferrer' do
- %span.gl-badge.gl-bg-green-500.gl-text-white.gl-rounded-pill.gl-font-weight-bold.gl-py-1
+ = gl_badge_tag({ variant: :success }) do
= _('Next')
- if current_user
@@ -64,7 +64,7 @@
container: 'body' } do
= sprite_icon('issues')
- issues_count = assigned_issuables_count(:issues)
- %span.badge.badge-pill.issues-count.green-badge{ class: ('hidden' if issues_count == 0) }
+ = gl_badge_tag({ size: :sm, variant: :success }, { class: "gl-ml-n2 #{(' gl-display-none' if issues_count == 0)}", "aria-label": n_("%d assigned issue", "%d assigned issues", issues_count) % issues_count }) do
= number_with_delimiter(issues_count)
- if header_link?(:merge_requests)
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter dropdown" }) do
@@ -77,7 +77,7 @@
track_property: 'navigation',
container: 'body' } do
= sprite_icon('git-merge')
- %span.badge.badge-pill.merge-requests-count.js-merge-requests-count{ class: ('hidden' if user_merge_requests_counts[:total] == 0) }
+ = gl_badge_tag({ size: :sm, variant: :warning }, { class: "js-merge-requests-count gl-ml-n2#{(' gl-display-none' if user_merge_requests_counts[:total] == 0)}", "aria-label": n_("%d merge request", "%d merge requests", user_merge_requests_counts[:total]) % user_merge_requests_counts[:total] }) do
= number_with_delimiter(user_merge_requests_counts[:total])
= sprite_icon('chevron-down', css_class: 'caret-down gl-mx-0!')
.dropdown-menu.dropdown-menu-right
@@ -87,12 +87,12 @@
%li
= link_to assigned_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center js-prefetch-document' do
= _('Assigned to you')
- %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-assigned-mr-count{ class: "" }
+ = gl_badge_tag({ variant: :neutral, size: :sm }, { class: "js-assigned-mr-count gl-ml-auto" }) do
= user_merge_requests_counts[:assigned]
%li
= link_to reviewer_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center js-prefetch-document' do
= _('Review requests for you')
- %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-reviewer-mr-count{ class: "" }
+ = gl_badge_tag({ variant: :neutral, size: :sm }, { class: "js-reviewer-mr-count gl-ml-auto" }) do
= user_merge_requests_counts[:review_requested]
- if header_link?(:todos)
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
@@ -103,7 +103,9 @@
track_property: 'navigation',
container: 'body' } do
= sprite_icon('todo-done')
- %span.badge.badge-pill.todos-count.js-todos-count{ class: ('hidden' if todos_pending_count == 0) }
+ -# The todos' counter badge's visibility is being toggled by adding or removing the .hidden class in Js.
+ -# We'll eventually migrate to .gl-display-none: https://gitlab.com/gitlab-org/gitlab/-/issues/351792.
+ = gl_badge_tag({ size: :sm, variant: :info }, { class: "js-todos-count gl-ml-n2#{(' hidden' if todos_pending_count == 0)}", "aria-label": _("Todos count") }) do
= todos_count_format(todos_pending_count)
%li.nav-item.header-help.dropdown.d-none.d-md-block{ **tracking_attrs('main_navigation', 'click_question_mark_link', 'navigation') }
= link_to help_path, class: 'header-help-dropdown-toggle gl-relative', data: { toggle: "dropdown" } do