Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-07 15:09:00 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-07 15:09:00 +0300
commitba27dbddc7dbc42f2cc8d84e815a9ea19f87a81d (patch)
treee71fba864897fa78be7f0c40ded23d0f719abf84 /app
parent708815aefead73a61473c1a611aea169ab16a358 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/error_tracking/components/error_details.vue28
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_actions.vue24
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_list.vue80
-rw-r--r--app/assets/javascripts/labels/components/promote_label_modal.vue9
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue17
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue73
-rw-r--r--app/assets/javascripts/notes/components/discussion_notes.vue6
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue12
-rw-r--r--app/assets/javascripts/sidebar/components/status/status_dropdown.vue45
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue40
-rw-r--r--app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue15
-rw-r--r--app/assets/stylesheets/page_bundles/error_tracking_details.scss31
-rw-r--r--app/assets/stylesheets/page_bundles/error_tracking_index.scss18
-rw-r--r--app/controllers/admin/users_controller.rb12
-rw-r--r--app/controllers/jira_connect/app_descriptor_controller.rb7
-rw-r--r--app/models/concerns/packages/downloadable.rb15
-rw-r--r--app/models/packages/npm/metadata_cache.rb1
-rw-r--r--app/models/packages/package.rb9
-rw-r--r--app/presenters/ml/candidate_details_presenter.rb5
-rw-r--r--app/services/users/activate_service.rb52
21 files changed, 279 insertions, 222 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue
index 9f054b94de7..182090e64f9 100644
--- a/app/assets/javascripts/error_tracking/components/error_details.vue
+++ b/app/assets/javascripts/error_tracking/components/error_details.vue
@@ -237,7 +237,7 @@ export default {
<template>
<div>
- <div v-if="errorLoading" class="py-3">
+ <div v-if="errorLoading" class="gl-py-5">
<gl-loading-icon size="lg" />
</div>
@@ -258,23 +258,25 @@ export default {
{{ __('No stack trace for this error') }}
</gl-alert>
- <div class="error-details-header d-flex py-2 justify-content-between">
+ <div
+ class="error-details-header gl-border-b gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-py-3 gl-justify-content-space-between"
+ >
<div
v-if="!loadingStacktrace && stacktrace"
- class="error-details-meta my-auto"
+ class="gl-my-auto gl-text-truncate"
data-qa-selector="reported_text"
>
<gl-sprintf :message="__('Reported %{timeAgo} by %{reportedBy}')">
<template #reportedBy>
- <strong class="error-details-meta-culprit">{{ error.culprit }}</strong>
+ <strong>{{ error.culprit }}</strong>
</template>
<template #timeAgo>
<time-ago-tooltip :time="stacktraceData.date_received" />
</template>
</gl-sprintf>
</div>
- <div class="error-details-actions">
- <div class="d-inline-flex bv-d-sm-down-none">
+ <div>
+ <div class="gl-display-none gl-md-display-inline-flex">
<gl-button
:loading="updatingIgnoreStatus"
data-testid="update-ignore-status-btn"
@@ -283,7 +285,7 @@ export default {
{{ ignoreBtnLabel }}
</gl-button>
<gl-button
- class="ml-2"
+ class="gl-ml-3"
category="secondary"
variant="confirm"
:loading="updatingResolveStatus"
@@ -294,7 +296,7 @@ export default {
</gl-button>
<gl-button
v-if="error.gitlabIssuePath"
- class="ml-2"
+ class="gl-ml-3"
data-testid="view_issue_button"
:href="error.gitlabIssuePath"
variant="confirm"
@@ -305,7 +307,7 @@ export default {
ref="sentryIssueForm"
:action="projectIssuesPath"
method="POST"
- class="d-inline-block ml-2"
+ class="gl-display-inline-block gl-ml-3"
>
<gl-form-input class="hidden" name="issue[title]" :value="issueTitle" />
<input name="issue[description]" :value="issueDescription" type="hidden" />
@@ -329,7 +331,7 @@ export default {
</div>
<gl-dropdown
text="Options"
- class="error-details-options d-md-none"
+ class="gl-w-full gl-md-display-none"
right
:disabled="issueUpdateInProgress"
>
@@ -362,7 +364,7 @@ export default {
</div>
<div>
<tooltip-on-truncate :title="error.title" truncate-target="child" placement="top">
- <h2 class="text-truncate">{{ error.title }}</h2>
+ <h2 class="gl-text-truncate">{{ error.title }}</h2>
</tooltip-on-truncate>
<template v-if="error.tags">
<gl-badge v-if="error.tags.level" :variant="errorSeverityVariant" class="gl-mr-3">
@@ -373,12 +375,12 @@ export default {
<error-details-info :error="error" />
- <div v-if="loadingStacktrace" class="py-3">
+ <div v-if="loadingStacktrace" class="gl-py-5">
<gl-loading-icon size="lg" />
</div>
<template v-else-if="showStacktrace">
- <h3 class="my-4">{{ __('Stack trace') }}</h3>
+ <h3 class="gl-my-6">{{ __('Stack trace') }}</h3>
<stacktrace :entries="stacktrace" />
</template>
</div>
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue b/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue
index a5e712f4fc2..35e8e26ecfb 100644
--- a/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue
+++ b/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue
@@ -44,37 +44,45 @@ export default {
<template>
<div>
- <gl-button-group class="gl-flex-direction-column flex-md-row gl-ml-0 ml-md-n4">
+ <gl-button-group class="gl-flex-direction-column gl-md-flex-direction-row gl-ml-n6">
<gl-button
:key="ignoreBtn.status"
:ref="`${ignoreBtn.title.toLowerCase()}Error`"
v-gl-tooltip.hover
- class="gl-display-block gl-mb-4 mb-md-0 gl-w-full"
+ class="gl-display-block gl-mb-4 gl-md-mb-0 gl-w-full"
:title="ignoreBtn.title"
:aria-label="ignoreBtn.title"
@click="$emit('update-issue-status', { errorId: error.id, status: ignoreBtn.status })"
>
- <gl-icon class="gl-display-none d-md-inline gl-m-0" :name="ignoreBtn.icon" :size="12" />
- <span class="d-md-none">{{ ignoreBtn.title }}</span>
+ <gl-icon
+ class="gl-display-none gl-md-display-inline gl-m-0"
+ :name="ignoreBtn.icon"
+ :size="12"
+ />
+ <span class="gl-md-display-none">{{ ignoreBtn.title }}</span>
</gl-button>
<gl-button
:key="resolveBtn.status"
:ref="`${resolveBtn.title.toLowerCase()}Error`"
v-gl-tooltip.hover
- class="gl-display-block gl-mb-4 mb-md-0 gl-w-full"
+ class="gl-display-block gl-mb-4 gl-md-mb-0 gl-w-full"
:title="resolveBtn.title"
:aria-label="resolveBtn.title"
@click="$emit('update-issue-status', { errorId: error.id, status: resolveBtn.status })"
>
- <gl-icon class="gl-display-none d-md-inline gl-m-0" :name="resolveBtn.icon" :size="12" />
- <span class="d-md-none">{{ resolveBtn.title }}</span>
+ <gl-icon
+ class="gl-display-none gl-md-display-inline gl-m-0"
+ :name="resolveBtn.icon"
+ :size="12"
+ />
+ <span class="gl-md-display-none">{{ resolveBtn.title }}</span>
</gl-button>
</gl-button-group>
<gl-button
:href="detailsLink"
category="primary"
variant="confirm"
- class="gl-display-block d-md-none gl-mb-4 mb-md-0"
+ class="gl-display-block gl-md-display-none! gl-mb-4 gl-md-mb-0"
>
{{ __('More details') }}
</gl-button>
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
index 23783bb7bb1..e3784cc8b92 100644
--- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
+++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
@@ -31,8 +31,6 @@ import {
import { I18N_ERROR_TRACKING_LIST } from '../constants';
import ErrorTrackingActions from './error_tracking_actions.vue';
-export const tableDataClass = 'table-col d-flex d-md-table-cell align-items-center';
-
const isValidErrorId = (errorId) => {
return /^[0-9]+$/.test(errorId);
};
@@ -46,30 +44,29 @@ export default {
key: 'error',
label: __('Error'),
thClass: 'w-60p',
- tdClass: `${tableDataClass} px-3 rounded-top`,
},
{
key: 'events',
label: __('Events'),
- thClass: 'text-right',
- tdClass: `${tableDataClass}`,
+ thClass: 'gl-text-right',
+ tdClass: 'gl-text-right',
},
{
key: 'users',
label: __('Users'),
- thClass: 'text-right',
- tdClass: `${tableDataClass}`,
+ thClass: 'gl-text-right',
+ tdClass: 'gl-text-right',
},
{
key: 'lastSeen',
label: __('Last seen'),
- thClass: 'w-15p',
- tdClass: `${tableDataClass}`,
+ thClass: 'gl-w-15p',
+ tdClass: 'gl-text-left',
},
{
key: 'status',
label: '',
- tdClass: `${tableDataClass}`,
+ tdClass: 'gl-text-center',
},
],
statusFilters: {
@@ -275,6 +272,7 @@ export default {
<template>
<div class="error-list">
<div v-if="errorTrackingEnabled">
+ <!-- Enable ET -->
<gl-alert
v-if="showIntegratedDisabledAlert"
variant="danger"
@@ -303,18 +301,20 @@ export default {
</gl-button>
</div>
</gl-alert>
+
+ <!-- Search / Filter Bar -->
<div
- class="row flex-column flex-md-row align-items-md-center m-0 mt-sm-2 p-3 p-sm-3 bg-secondary border"
+ class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-md-align-items-center gl-m-0 gl-p-5 gl-bg-gray-50 gl-border"
>
- <div class="search-box flex-fill mb-1 mb-md-0">
- <div class="filtered-search-box mb-0">
+ <div class="search-box gl-display-flex gl-flex-grow-1 gl-mb-2 gl-md-mb-0">
+ <div class="filtered-search-box gl-mb-0">
<gl-dropdown
:text="__('Recent searches')"
class="filtered-search-history-dropdown-wrapper"
toggle-class="filtered-search-history-dropdown-toggle-button gl-shadow-none! gl-border-r-gray-200! gl-border-1! gl-rounded-0!"
:disabled="loading"
>
- <div v-if="!$options.hasLocalStorage" class="px-3">
+ <div v-if="!$options.hasLocalStorage" class="gl-px-5">
{{ __('This feature requires local storage to be enabled') }}
</div>
<template v-else-if="recentSearches.length > 0">
@@ -329,12 +329,12 @@ export default {
>{{ __('Clear recent searches') }}
</gl-dropdown-item>
</template>
- <div v-else class="px-3">{{ __("You don't have any recent searches") }}</div>
+ <div v-else class="gl-px-5">{{ __("You don't have any recent searches") }}</div>
</gl-dropdown>
<div class="filtered-search-input-container gl-flex-grow-1">
<gl-form-input
v-model="errorSearchQuery"
- class="pl-2 filtered-search"
+ class="gl-pl-3! filtered-search"
:disabled="loading"
:placeholder="__('Search or filter results…')"
autofocus
@@ -346,7 +346,7 @@ export default {
v-if="errorSearchQuery.length > 0"
v-gl-tooltip.hover
:title="__('Clear')"
- class="clear-search text-secondary"
+ class="clear-search gl-text-secondary"
name="clear"
icon="close"
@click="errorSearchQuery = ''"
@@ -357,7 +357,7 @@ export default {
<gl-dropdown
:text="$options.statusFilters[statusFilter]"
- class="status-dropdown mx-md-1 mb-1 mb-md-0"
+ class="status-dropdown gl-md-ml-2 gl-md-mr-2 gl-mb-2 gl-md-mb-0"
:disabled="loading"
right
>
@@ -366,7 +366,7 @@ export default {
:key="status"
@click="filterErrors(status, label)"
>
- <span class="d-flex">
+ <span class="gl-display-flex">
<gl-icon
class="gl-dropdown-item-check-icon"
:class="{ invisible: !isCurrentStatusFilter(status) }"
@@ -383,7 +383,7 @@ export default {
:key="field"
@click="sortErrorsByField(field)"
>
- <span class="d-flex">
+ <span class="gl-display-flex">
<gl-icon
class="gl-dropdown-item-check-icon"
:class="{ invisible: !isCurrentSortField(field) }"
@@ -395,58 +395,65 @@ export default {
</gl-dropdown>
</div>
- <div v-if="loading" class="py-3">
+ <div v-if="loading" class="gl-py-5">
<gl-loading-icon size="lg" />
</div>
+ <!-- Results Table -->
<template v-else>
- <h4 class="d-block d-md-none my-3">{{ __('Open errors') }}</h4>
+ <h4 class="gl-display-block gl-md-display-none! gl-my-5">{{ __('Open errors') }}</h4>
<gl-table
- class="error-list-table mt-3"
+ class="error-list-table gl-mt-5"
:items="errors"
:fields="$options.fields"
:show-empty="true"
fixed
stacked="md"
- tbody-tr-class="table-row mb-4"
+ tbody-tr-class="table-row"
>
+ <!-- table head -->
<template #head(error)>
- <div class="d-none d-md-block">{{ __('Open errors') }}</div>
+ <div class="gl-display-none gl-md-display-block">{{ __('Open errors') }}</div>
</template>
<template #head(events)="data">
- <div class="text-md-right">{{ data.label }}</div>
+ {{ data.label }}
</template>
<template #head(users)="data">
- <div class="text-md-right">{{ data.label }}</div>
+ {{ data.label }}
</template>
+ <!-- table row -->
<template #cell(error)="errors">
- <div class="d-flex flex-column">
- <gl-link class="d-flex mw-100 text-dark" :href="getDetailsLink(errors.item.id)">
- <strong class="text-truncate">{{ errors.item.title.trim() }}</strong>
+ <div class="gl-display-flex gl-flex-direction-column">
+ <gl-link
+ class="gl-display-flex gl-max-w-full gl-text-body"
+ :href="getDetailsLink(errors.item.id)"
+ >
+ <strong class="gl-text-truncate">{{ errors.item.title.trim() }}</strong>
</gl-link>
- <span class="text-secondary text-truncate mw-100">
+ <span class="gl-text-secondary gl-text-truncate gl-max-w-full">
{{ errors.item.culprit }}
</span>
</div>
</template>
+
<template #cell(events)="errors">
- <div class="text-right">{{ errors.item.count }}</div>
+ {{ errors.item.count }}
</template>
<template #cell(users)="errors">
- <div class="text-right">{{ errors.item.userCount }}</div>
+ {{ errors.item.userCount }}
</template>
<template #cell(lastSeen)="errors">
- <div class="text-lg-left text-right">
- <time-ago :time="errors.item.lastSeen" class="text-secondary" />
- </div>
+ <time-ago :time="errors.item.lastSeen" class="gl-text-secondary" />
</template>
+
<template #cell(status)="errors">
<error-tracking-actions :error="errors.item" @update-issue-status="updateErrosStatus" />
</template>
+
<template #empty>
{{ __('No errors to display.') }}
<gl-link class="js-try-again" @click="restartPolling">
@@ -465,6 +472,7 @@ export default {
/>
</template>
</div>
+ <!-- Get Started with ET -->
<div v-else>
<gl-empty-state :title="__('Get started with error tracking')" :svg-path="illustrationPath">
<template #description>
diff --git a/app/assets/javascripts/labels/components/promote_label_modal.vue b/app/assets/javascripts/labels/components/promote_label_modal.vue
index 298cc20ab35..752fda83d6b 100644
--- a/app/assets/javascripts/labels/components/promote_label_modal.vue
+++ b/app/assets/javascripts/labels/components/promote_label_modal.vue
@@ -4,6 +4,7 @@ import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import { s__, __, sprintf } from '~/locale';
+import { stripQuotes } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import eventHub from '../event_hub';
export default {
@@ -52,6 +53,12 @@ export default {
},
);
},
+ cleanedLabelColor() {
+ return stripQuotes(this.labelColor);
+ },
+ cleanedLabelTextColor() {
+ return stripQuotes(this.labelTextColor);
+ },
},
methods: {
onSubmit() {
@@ -97,7 +104,7 @@ export default {
<template #labelTitle>
<span
class="label color-label"
- :style="`background-color: ${labelColor}; color: ${labelTextColor};`"
+ :style="`background-color: ${cleanedLabelColor}; color: ${cleanedLabelTextColor};`"
>
{{ labelTitle }}
</span>
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
index 20c5248052b..747e92b9e85 100644
--- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
+++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
@@ -1,25 +1,11 @@
<script>
-import { GlLink } from '@gitlab/ui';
-
export default {
name: 'CandidateDetailRow',
- components: {
- GlLink,
- },
props: {
label: {
type: String,
required: true,
},
- text: {
- type: [String, Number],
- required: true,
- },
- href: {
- type: String,
- required: false,
- default: '',
- },
sectionLabel: {
type: String,
required: false,
@@ -34,8 +20,7 @@ export default {
<td class="gl-text-secondary gl-font-weight-bold">{{ sectionLabel }}</td>
<td class="gl-font-weight-bold">{{ label }}</td>
<td>
- <gl-link v-if="href" :href="href">{{ text }}</gl-link>
- <template v-else>{{ text }}</template>
+ <slot></slot>
</td>
</tr>
</template>
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
index b7a612a9688..a68fb7d340a 100644
--- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
+++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
@@ -1,4 +1,5 @@
<script>
+import { GlAvatarLabeled, GlLink } from '@gitlab/ui';
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue';
import DetailRow from './components/candidate_detail_row.vue';
@@ -29,6 +30,8 @@ export default {
ModelExperimentsHeader,
DeleteButton,
DetailRow,
+ GlAvatarLabeled,
+ GlLink,
},
props: {
candidate: {
@@ -94,52 +97,51 @@ export default {
<tbody>
<tr class="divider"></tr>
- <detail-row
- :label="$options.i18n.ID_LABEL"
- :section-label="$options.i18n.INFO_LABEL"
- :text="info.iid"
- />
+ <detail-row :label="$options.i18n.ID_LABEL" :section-label="$options.i18n.INFO_LABEL">
+ {{ info.iid }}
+ </detail-row>
- <detail-row :label="$options.i18n.MLFLOW_ID_LABEL" :text="info.eid" />
+ <detail-row :label="$options.i18n.MLFLOW_ID_LABEL">{{ info.eid }}</detail-row>
- <detail-row :label="$options.i18n.STATUS_LABEL" :text="info.status" />
+ <detail-row :label="$options.i18n.STATUS_LABEL">{{ info.status }}</detail-row>
- <detail-row
- :label="$options.i18n.EXPERIMENT_LABEL"
- :text="info.experiment_name"
- :href="info.path_to_experiment"
- />
+ <detail-row :label="$options.i18n.EXPERIMENT_LABEL">
+ <gl-link :href="info.path_to_experiment">
+ {{ info.experiment_name }}
+ </gl-link>
+ </detail-row>
- <detail-row
- v-if="info.path_to_artifact"
- :label="$options.i18n.ARTIFACTS_LABEL"
- :href="info.path_to_artifact"
- :text="$options.i18n.ARTIFACTS_LABEL"
- />
+ <detail-row v-if="info.path_to_artifact" :label="$options.i18n.ARTIFACTS_LABEL">
+ <gl-link :href="info.path_to_artifact">
+ {{ $options.i18n.ARTIFACTS_LABEL }}
+ </gl-link>
+ </detail-row>
<template v-if="ciJob">
<tr class="divider"></tr>
<detail-row
:label="$options.i18n.JOB_LABEL"
- :text="ciJob.name"
- :href="ciJob.path"
:section-label="$options.i18n.CI_SECTION_LABEL"
- />
+ >
+ <gl-link :href="ciJob.path">
+ {{ ciJob.name }}
+ </gl-link>
+ </detail-row>
- <detail-row
- v-if="ciJob.user"
- :label="$options.i18n.CI_USER_LABEL"
- :href="ciJob.user.path"
- :text="ciJob.user.username"
- />
+ <detail-row v-if="ciJob.user" :label="$options.i18n.CI_USER_LABEL">
+ <gl-avatar-labeled label="" :size="24" :src="ciJob.user.avatar">
+ <gl-link :href="ciJob.user.path">
+ {{ ciJob.user.name }}
+ </gl-link>
+ </gl-avatar-labeled>
+ </detail-row>
- <detail-row
- v-if="ciJob.merge_request"
- :label="$options.i18n.CI_MR_LABEL"
- :text="ciJob.merge_request.title"
- :href="ciJob.merge_request.path"
- />
+ <detail-row v-if="ciJob.merge_request" :label="$options.i18n.CI_MR_LABEL">
+ <gl-link :href="ciJob.merge_request.path">
+ !{{ ciJob.merge_request.iid }} {{ ciJob.merge_request.title }}
+ </gl-link>
+ </detail-row>
</template>
<template v-for="{ sectionName, sectionValues } in sections">
@@ -150,8 +152,9 @@ export default {
:key="item.name"
:label="item.name"
:section-label="index === 0 ? sectionName : ''"
- :text="item.value"
- />
+ >
+ {{ item.value }}
+ </detail-row>
</template>
</tbody>
</table>
diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue
index 3e8cddc3174..a3034e550ed 100644
--- a/app/assets/javascripts/notes/components/discussion_notes.vue
+++ b/app/assets/javascripts/notes/components/discussion_notes.vue
@@ -82,6 +82,9 @@ export default {
url: this.discussion.discussion_path,
};
},
+ isDiscussionInternal() {
+ return this.discussion.notes[0]?.internal;
+ },
},
methods: {
...mapActions(['toggleDiscussion', 'setSelectedCommentPositionHover']),
@@ -139,6 +142,7 @@ export default {
:discussion-resolve-path="discussion.resolve_path"
:is-overview-tab="isOverviewTab"
:should-scroll-to-note="shouldScrollToNote"
+ :internal-note="isDiscussionInternal"
@handleDeleteNote="$emit('deleteNote')"
@startReplying="$emit('startReplying')"
>
@@ -171,6 +175,7 @@ export default {
:note="componentData(note)"
:help-page-path="helpPagePath"
:line="line"
+ :internal-note="isDiscussionInternal"
@handleDeleteNote="$emit('deleteNote')"
/>
</template>
@@ -190,6 +195,7 @@ export default {
:discussion-resolve-path="discussion.resolve_path"
:is-overview-tab="isOverviewTab"
:should-scroll-to-note="shouldScrollToNote"
+ :internal-note="isDiscussionInternal"
@handleDeleteNote="$emit('deleteNote')"
>
<template #avatar-badge>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index f5fe2cc1284..2feca0bd7a5 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -163,6 +163,15 @@ export default {
return true;
},
+ isDiscussionInternal() {
+ return this.discussion.notes[0]?.internal;
+ },
+ discussionHolderClass() {
+ return {
+ 'is-replying gl-pt-0!': this.isReplying,
+ 'internal-note': this.isDiscussionInternal,
+ };
+ },
},
created() {
eventHub.$on('startReplying', this.onStartReplying);
@@ -318,8 +327,9 @@ export default {
/>
<li
v-else-if="canShowReplyActions && showReplies"
- :class="{ 'is-replying gl-bg-white! gl-pt-0!': isReplying }"
+ data-testid="reply-wrapper"
class="discussion-reply-holder gl-border-t-0! clearfix"
+ :class="discussionHolderClass"
>
<discussion-actions
v-if="!isReplying && userCanReply"
diff --git a/app/assets/javascripts/sidebar/components/status/status_dropdown.vue b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue
index 7763ec00091..69ec4214712 100644
--- a/app/assets/javascripts/sidebar/components/status/status_dropdown.vue
+++ b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue
@@ -1,39 +1,35 @@
<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { __ } from '~/locale';
import { statusDropdownOptions } from '../../constants';
export default {
components: {
- GlDropdown,
- GlDropdownItem,
+ GlCollapsibleListbox,
},
data() {
return {
status: null,
+ selectedValue: undefined,
};
},
computed: {
dropdownText() {
- return this.status?.text ?? this.$options.i18n.defaultDropdownText;
- },
- selectedValue() {
- return this.status?.value;
+ const selected = this.$options.statusDropdownOptions.find(
+ (option) => option.value === this.selectedValue,
+ );
+ return selected?.text || this.$options.i18n.defaultDropdownText;
},
},
methods: {
- onDropdownItemClick(statusOption) {
- // clear status if the currently checked status is clicked again
- if (this.status?.value === statusOption.value) {
- this.status = null;
- } else {
- this.status = statusOption;
- }
+ handleReset() {
+ this.selectedValue = undefined;
},
},
i18n: {
dropdownTitle: __('Change status'),
defaultDropdownText: __('Select status'),
+ resetText: __('Reset'),
},
statusDropdownOptions,
};
@@ -41,17 +37,14 @@ export default {
<template>
<div>
<input type="hidden" name="update[state_event]" :value="selectedValue" />
- <gl-dropdown :text="dropdownText" :title="$options.i18n.dropdownTitle" class="gl-w-full">
- <gl-dropdown-item
- v-for="statusOption in $options.statusDropdownOptions"
- :key="statusOption.value"
- :is-checked="selectedValue === statusOption.value"
- is-check-item
- :title="statusOption.text"
- @click="onDropdownItemClick(statusOption)"
- >
- {{ statusOption.text }}
- </gl-dropdown-item>
- </gl-dropdown>
+ <gl-collapsible-listbox
+ v-model="selectedValue"
+ block
+ :header-text="$options.i18n.dropdownTitle"
+ :reset-button-label="$options.i18n.resetText"
+ :toggle-text="dropdownText"
+ :items="$options.statusDropdownOptions"
+ @reset="handleReset"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue
index 4c3ba76d12d..bacbe5d46a6 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue
@@ -1,5 +1,5 @@
<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { __ } from '~/locale';
import { subscriptionsDropdownOptions } from '../../constants';
@@ -8,27 +8,27 @@ export default {
i18n: {
defaultDropdownText: __('Select subscription'),
headerText: __('Change subscription'),
+ resetText: __('Reset'),
},
components: {
- GlDropdown,
- GlDropdownItem,
+ GlCollapsibleListbox,
},
data() {
return {
- subscription: undefined,
+ selectedValue: undefined,
};
},
computed: {
dropdownText() {
- return this.subscription?.text ?? this.$options.i18n.defaultDropdownText;
- },
- selectedValue() {
- return this.subscription?.value;
+ const selected = this.$options.subscriptionsDropdownOptions.find(
+ (option) => option.value === this.selectedValue,
+ );
+ return selected?.text || this.$options.i18n.defaultDropdownText;
},
},
methods: {
- handleClick(option) {
- this.subscription = option.value === this.subscription?.value ? undefined : option;
+ handleReset() {
+ this.selectedValue = undefined;
},
},
};
@@ -36,16 +36,14 @@ export default {
<template>
<div>
<input type="hidden" name="update[subscription_event]" :value="selectedValue" />
- <gl-dropdown class="gl-w-full" :header-text="$options.i18n.headerText" :text="dropdownText">
- <gl-dropdown-item
- v-for="subscriptionsOption in $options.subscriptionsDropdownOptions"
- :key="subscriptionsOption.value"
- is-check-item
- :is-checked="selectedValue === subscriptionsOption.value"
- @click="handleClick(subscriptionsOption)"
- >
- {{ subscriptionsOption.text }}
- </gl-dropdown-item>
- </gl-dropdown>
+ <gl-collapsible-listbox
+ v-model="selectedValue"
+ block
+ :header-text="$options.i18n.headerText"
+ :reset-button-label="$options.i18n.resetText"
+ :toggle-text="dropdownText"
+ :items="$options.subscriptionsDropdownOptions"
+ @reset="handleReset"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js
index 1a359533435..2687ea5ccf8 100644
--- a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js
+++ b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js
@@ -24,7 +24,7 @@ export const toggleSuperSidebarCollapsed = (collapsed, saveCookie) => {
findPage().classList.toggle(SIDEBAR_COLLAPSED_CLASS, collapsed);
sidebarState.isPeek = false;
- sidebarState.isPeekable = Boolean(gon.features?.superSidebarPeek) && collapsed;
+ sidebarState.isPeekable = collapsed;
sidebarState.isCollapsed = collapsed;
if (saveCookie && isDesktopBreakpoint()) {
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
index 748d6082abd..57b19620c10 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
@@ -45,18 +45,31 @@ export default {
required: false,
default: false,
},
+ internalNote: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
...mapGetters(['getUserData']),
renderedNote() {
return renderMarkdown(this.note.body);
},
+ internalNoteClass() {
+ return {
+ 'internal-note': this.internalNote,
+ };
+ },
},
};
</script>
<template>
- <timeline-entry-item class="note note-wrapper note-comment being-posted fade-in-half">
+ <timeline-entry-item
+ class="note note-wrapper note-comment being-posted fade-in-half"
+ :class="internalNoteClass"
+ >
<div class="timeline-avatar gl-float-left">
<gl-avatar-link :href="getUserData.path">
<gl-avatar
diff --git a/app/assets/stylesheets/page_bundles/error_tracking_details.scss b/app/assets/stylesheets/page_bundles/error_tracking_details.scss
index a47c5cc9b3e..9b93fa7f6d8 100644
--- a/app/assets/stylesheets/page_bundles/error_tracking_details.scss
+++ b/app/assets/stylesheets/page_bundles/error_tracking_details.scss
@@ -1,36 +1,5 @@
@import 'page_bundles/mixins_and_variables_and_functions';
-.error-details {
- li {
- @include gl-line-height-32;
- }
-
- .btn-outline-info {
- color: var(--blue-500, $blue-500);
- border-color: var(--blue-500, $blue-500);
- }
-
- .error-details-header {
- border-bottom: 1px solid var(--border-color, $border-color);
-
- @include media-breakpoint-down(xs) {
- flex-flow: column;
-
- .error-details-meta-culprit {
- display: flex;
- }
-
- .error-details-options {
- width: 100%;
-
- .dropdown-toggle {
- text-align: center;
- }
- }
- }
- }
-}
-
.stacktrace {
.file-title {
svg {
diff --git a/app/assets/stylesheets/page_bundles/error_tracking_index.scss b/app/assets/stylesheets/page_bundles/error_tracking_index.scss
index 5c49bcc0348..4baab693aed 100644
--- a/app/assets/stylesheets/page_bundles/error_tracking_index.scss
+++ b/app/assets/stylesheets/page_bundles/error_tracking_index.scss
@@ -1,29 +1,13 @@
@import 'page_bundles/mixins_and_variables_and_functions';
.error-list {
- .dropdown {
- min-width: auto;
- }
-
.filtered-search-box .form-control {
min-width: unset;
}
- .sort-control {
- .btn {
- padding-right: 2rem;
- }
-
- .gl-dropdown-caret {
- position: absolute;
- right: 0.5rem;
- top: 0.5rem;
- }
- }
-
@include media-breakpoint-down(sm) {
.error-list-table {
- .table-col {
+ td {
min-height: 68px;
&:last-child {
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 8d2146cddc5..3c96e49499f 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -87,12 +87,14 @@ class Admin::UsersController < Admin::ApplicationController
end
def activate
- if user.blocked?
- return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user must be unblocked to be activated"))
- end
+ activate_service = Users::ActivateService.new(current_user)
+ result = activate_service.execute(user)
- user.activate
- redirect_back_or_admin_user(notice: _("Successfully activated"))
+ if result.success?
+ redirect_back_or_admin_user(notice: _("Successfully activated"))
+ else
+ redirect_back_or_admin_user(alert: result.message)
+ end
end
def deactivate
diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb
index 3c50d54fa10..2c498820a1e 100644
--- a/app/controllers/jira_connect/app_descriptor_controller.rb
+++ b/app/controllers/jira_connect/app_descriptor_controller.rb
@@ -8,7 +8,7 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
skip_before_action :verify_atlassian_jwt!
def show
- render json: {
+ result = {
name: Atlassian::JiraConnect.app_name,
description: 'Integrate commits, branches and merge requests from GitLab into Jira',
key: Atlassian::JiraConnect.app_key,
@@ -36,10 +36,15 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
gdpr: true
}
}
+
+ result[:links][:feedback] = URI.join(HOME_URL, FEEDBACK_URL) if Feature.enabled?(:jira_for_cloud_app_feedback_link)
+
+ render json: result
end
private
+ FEEDBACK_URL = '/gitlab-org/gitlab/-/issues/413652'
HOME_URL = 'https://gitlab.com'
DOC_URL = 'https://docs.gitlab.com/ee/integration/jira/'
diff --git a/app/models/concerns/packages/downloadable.rb b/app/models/concerns/packages/downloadable.rb
new file mode 100644
index 00000000000..011f5ddda9c
--- /dev/null
+++ b/app/models/concerns/packages/downloadable.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Packages
+ module Downloadable
+ extend ActiveSupport::Concern
+
+ def touch_last_downloaded_at
+ ::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
+ update_column(:last_downloaded_at, Time.zone.now)
+ end
+ end
+ end
+end
+
+Packages::Downloadable.prepend_mod
diff --git a/app/models/packages/npm/metadata_cache.rb b/app/models/packages/npm/metadata_cache.rb
index 7a7c66d7a45..02efeda69cb 100644
--- a/app/models/packages/npm/metadata_cache.rb
+++ b/app/models/packages/npm/metadata_cache.rb
@@ -4,6 +4,7 @@ module Packages
module Npm
class MetadataCache < ApplicationRecord
include FileStoreMounter
+ include Packages::Downloadable
belongs_to :project, inverse_of: :npm_metadata_caches
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index f1044f865aa..9888a139760 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -6,6 +6,7 @@ class Packages::Package < ApplicationRecord
include UsageStatistics
include Gitlab::Utils::StrongMemoize
include Packages::Installable
+ include Packages::Downloadable
DISPLAYABLE_STATUSES = [:default, :error].freeze
INSTALLABLE_STATUSES = [:default, :hidden].freeze
@@ -359,12 +360,6 @@ class Packages::Package < ApplicationRecord
name.gsub(/#{Gitlab::Regex::Packages::PYPI_NORMALIZED_NAME_REGEX_STRING}/o, '-').downcase
end
- def touch_last_downloaded_at
- ::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
- update_column(:last_downloaded_at, Time.zone.now)
- end
- end
-
def publish_creation_event
::Gitlab::EventStore.publish(
::Packages::PackageCreatedEvent.new(data: {
@@ -437,5 +432,3 @@ class Packages::Package < ApplicationRecord
end
end
end
-
-Packages::Package.prepend_mod
diff --git a/app/presenters/ml/candidate_details_presenter.rb b/app/presenters/ml/candidate_details_presenter.rb
index 58ec2aee471..7f0bd9d6c11 100644
--- a/app/presenters/ml/candidate_details_presenter.rb
+++ b/app/presenters/ml/candidate_details_presenter.rb
@@ -53,7 +53,9 @@ module Ml
{
user: {
path: user_path(user),
- username: user.username
+ username: user.username,
+ name: user.name,
+ avatar: user.avatar_url
}
}
end
@@ -64,6 +66,7 @@ module Ml
{
merge_request: {
path: project_merge_request_path(mr.project, mr),
+ iid: mr.iid,
title: mr.title
}
}
diff --git a/app/services/users/activate_service.rb b/app/services/users/activate_service.rb
new file mode 100644
index 00000000000..dfc2996bcce
--- /dev/null
+++ b/app/services/users/activate_service.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Users
+ class ActivateService < BaseService
+ def initialize(current_user)
+ @current_user = current_user
+ end
+
+ def execute(user)
+ return error(_('You are not authorized to perform this action'), :forbidden) unless allowed?
+
+ return error(_('Error occurred. A blocked user must be unblocked to be activated'), :forbidden) if user.blocked?
+
+ return success(_('Successfully activated')) if user.active?
+
+ if user.activate
+ after_activate_hook(user)
+ log_event(user)
+ success(_('Successfully activated'))
+ else
+ error(user.errors.full_messages.to_sentence, :unprocessable_entity)
+ end
+ end
+
+ private
+
+ attr_reader :current_user
+
+ def allowed?
+ can?(current_user, :admin_all_resources)
+ end
+
+ def after_activate_hook(user)
+ # overridden by EE module
+ end
+
+ def log_event(user)
+ Gitlab::AppLogger.info(message: 'User activated', user: user.username.to_s, email: user.email.to_s,
+ activated_by: current_user.username.to_s, ip_address: current_user.current_sign_in_ip.to_s)
+ end
+
+ def success(message)
+ ::ServiceResponse.success(message: message)
+ end
+
+ def error(message, reason)
+ ::ServiceResponse.error(message: message, reason: reason)
+ end
+ end
+end
+
+Users::ActivateService.prepend_mod_with('Users::ActivateService') # rubocop: disable Cop/InjectEnterpriseEditionModule