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-12 21:09:34 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-12 21:09:34 +0300
commit949ed51c6d20ba24e1f508cae9a65b86114bff62 (patch)
tree9308fe87e6d61b35eb3b2d101c47e89b9054655f /app
parentc0b17cee8be646588b14db49ad6d91b8cc818f97 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/graphql_shared/possible_types.json4
-rw-r--r--app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue18
-rw-r--r--app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue18
-rw-r--r--app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue27
-rw-r--r--app/assets/javascripts/merge_request_tabs.js1
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue1
-rw-r--r--app/assets/javascripts/notes/mixins/discussion_navigation.js2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue44
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue7
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue125
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/models/concerns/spammable.rb22
-rw-r--r--app/models/issue.rb8
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/models/snippet.rb9
-rw-r--r--app/presenters/packages/nuget/service_index_presenter.rb21
-rw-r--r--app/services/merge_requests/create_service.rb1
-rw-r--r--app/services/merge_requests/update_service.rb5
-rw-r--r--app/views/profiles/preferences/show.html.haml2
-rw-r--r--app/views/projects/diffs/_text_file.html.haml53
20 files changed, 240 insertions, 133 deletions
diff --git a/app/assets/javascripts/graphql_shared/possible_types.json b/app/assets/javascripts/graphql_shared/possible_types.json
index cf3a0003cd1..7651bbba71c 100644
--- a/app/assets/javascripts/graphql_shared/possible_types.json
+++ b/app/assets/javascripts/graphql_shared/possible_types.json
@@ -3,6 +3,10 @@
"AlertManagementHttpIntegration",
"AlertManagementPrometheusIntegration"
],
+ "BaseHeaderInterface": [
+ "AuditEventStreamingHeader",
+ "AuditEventsStreamingInstanceHeader"
+ ],
"CiVariable": [
"CiGroupVariable",
"CiInstanceVariable",
diff --git a/app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue b/app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue
index 15606ad567c..23d6edae415 100644
--- a/app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue
+++ b/app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue
@@ -1,5 +1,5 @@
<script>
-import { GlDropdownItem, GlModalDirective } from '@gitlab/ui';
+import { GlDisclosureDropdownItem, GlModalDirective } from '@gitlab/ui';
import { LEAVE_MODAL_ID } from '../../constants';
import LeaveModal from '../modals/leave_modal.vue';
@@ -7,7 +7,7 @@ export default {
name: 'LeaveGroupDropdownItem',
modalId: LEAVE_MODAL_ID,
components: {
- GlDropdownItem,
+ GlDisclosureDropdownItem,
LeaveModal,
},
directives: {
@@ -27,10 +27,12 @@ export default {
</script>
<template>
- <gl-dropdown-item v-gl-modal="$options.modalId">
- <span class="gl-text-red-500">
- <slot></slot>
- </span>
- <leave-modal :member="member" :permissions="permissions" />
- </gl-dropdown-item>
+ <gl-disclosure-dropdown-item v-gl-modal="$options.modalId">
+ <template #list-item>
+ <span class="gl-text-red-500">
+ <slot></slot>
+ </span>
+ <leave-modal :member="member" :permissions="permissions" />
+ </template>
+ </gl-disclosure-dropdown-item>
</template>
diff --git a/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue b/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue
index f224aaa31f7..627b47a1e81 100644
--- a/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue
+++ b/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue
@@ -1,10 +1,10 @@
<script>
-import { GlDropdownItem } from '@gitlab/ui';
+import { GlDisclosureDropdownItem } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
export default {
name: 'RemoveMemberDropdownItem',
- components: { GlDropdownItem },
+ components: { GlDisclosureDropdownItem },
inject: ['namespace'],
props: {
memberId: {
@@ -75,12 +75,14 @@ export default {
</script>
<template>
- <gl-dropdown-item
+ <gl-disclosure-dropdown-item
data-qa-selector="delete_member_dropdown_item"
- @click="showRemoveMemberModal(modalData)"
+ @action="showRemoveMemberModal(modalData)"
>
- <span class="gl-text-red-500">
- <slot></slot>
- </span>
- </gl-dropdown-item>
+ <template #list-item>
+ <span class="gl-text-red-500">
+ <slot></slot>
+ </span>
+ </template>
+ </gl-disclosure-dropdown-item>
</template>
diff --git a/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue b/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue
index c82ebadea6e..25dc4831b11 100644
--- a/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue
+++ b/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue
@@ -1,5 +1,5 @@
<script>
-import { GlDropdown, GlTooltipDirective } from '@gitlab/ui';
+import { GlDisclosureDropdown, GlTooltipDirective } from '@gitlab/ui';
import { sprintf } from '~/locale';
import { parseUserDeletionObstacles } from '~/vue_shared/components/user_deletion_obstacles/utils';
import {
@@ -14,7 +14,7 @@ export default {
name: 'UserActionDropdown',
i18n: I18N,
components: {
- GlDropdown,
+ GlDisclosureDropdown,
DisableTwoFactorDropdownItem: () =>
import(
'ee_component/members/components/action_dropdowns/disable_two_factor_dropdown_item.vue'
@@ -99,15 +99,15 @@ export default {
</script>
<template>
- <gl-dropdown
+ <gl-disclosure-dropdown
v-if="showDropdown"
v-gl-tooltip="$options.i18n.actions"
- :text="$options.i18n.actions"
+ :toggle-text="$options.i18n.actions"
text-sr-only
icon="ellipsis_v"
category="tertiary"
no-caret
- right
+ placement="right"
data-testid="user-action-dropdown"
data-qa-selector="user_action_dropdown"
>
@@ -131,15 +131,16 @@ export default {
:user-deletion-obstacles="userDeletionObstaclesUserData"
:modal-message="modalRemoveUser"
:prevent-removal="permissions.canRemoveBlockedByLastOwner"
- >{{ $options.i18n.removeMember }}</remove-member-dropdown-item
>
+ {{ $options.i18n.removeMember }}
+ </remove-member-dropdown-item>
</template>
- <ldap-override-dropdown-item v-else-if="showLdapOverride" :member="member">{{
- $options.i18n.editPermissions
- }}</ldap-override-dropdown-item>
- <ban-member-dropdown-item v-if="showBan" :member="member">{{
- $options.i18n.banMember
- }}</ban-member-dropdown-item>
- </gl-dropdown>
+ <ldap-override-dropdown-item v-else-if="showLdapOverride" :member="member">
+ {{ $options.i18n.editPermissions }}
+ </ldap-override-dropdown-item>
+ <ban-member-dropdown-item v-if="showBan" :member="member">
+ {{ $options.i18n.banMember }}
+ </ban-member-dropdown-item>
+ </gl-disclosure-dropdown>
</template>
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index cef224d83e2..8307d0a9eed 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -97,6 +97,7 @@ function mountPipelines() {
targetProjectFullPath: mrWidgetData?.target_project_full_path || '',
fullPath: pipelineTableViewEl.dataset.fullPath,
manualActionsLimit: 50,
+ withFailedJobsDetails: true,
},
render(createElement) {
return createElement('commit-pipelines-table', {
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index b80262622eb..865b3d1a057 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -293,6 +293,7 @@ export default {
<div class="timeline-content">
<div
:data-discussion-id="discussion.id"
+ :data-discussion-resolvable="discussion.resolvable"
:data-discussion-resolved="discussion.resolved"
class="discussion js-discussion-container"
data-qa-selector="discussion_content"
diff --git a/app/assets/javascripts/notes/mixins/discussion_navigation.js b/app/assets/javascripts/notes/mixins/discussion_navigation.js
index 90de7db8c1b..8e69f1ddc88 100644
--- a/app/assets/javascripts/notes/mixins/discussion_navigation.js
+++ b/app/assets/javascripts/notes/mixins/discussion_navigation.js
@@ -9,7 +9,7 @@ function getAllDiscussionElements() {
const containerEl = isOverviewPage() ? '.tab-pane.notes' : '.diffs';
return Array.from(
document.querySelectorAll(
- `${containerEl} div[data-discussion-id]:not([data-discussion-resolved])`,
+ `${containerEl} div[data-discussion-id][data-discussion-resolvable]:not([data-discussion-resolved])`,
),
);
}
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue b/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue
new file mode 100644
index 00000000000..09a75a7c8e5
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue
@@ -0,0 +1,44 @@
+<script>
+import { GlIcon, GlLink, GlPopover, GlSprintf } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
+
+export default {
+ components: {
+ GlIcon,
+ GlLink,
+ GlPopover,
+ GlSprintf,
+ },
+ props: {
+ pipelinePath: {
+ required: true,
+ type: String,
+ },
+ },
+
+ i18n: {
+ additionalInfoPopover: s__(
+ 'Pipelines|You will see a maximum of 100 jobs in this list. To view all failed jobs, %{linkStart}go to the details page%{linkEnd} of this pipeline.',
+ ),
+ additionalInfoTitle: __('Limitation on this view'),
+ showFailedJobs: __('Show failed jobs'),
+ },
+};
+</script>
+<template>
+ <div class="gl-border-none!">
+ <gl-icon name="chevron-right" />
+ {{ $options.i18n.showFailedJobs }}
+ <gl-icon id="target" name="information-o" />
+ <gl-popover target="target" placement="top">
+ <template #title> {{ $options.i18n.additionalInfoTitle }} </template>
+ <slot>
+ <gl-sprintf :message="$options.i18n.additionalInfoPopover">
+ <template #link="{ content }">
+ <gl-link class="gl-font-sm" :href="pipelinePath"> {{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </slot>
+ </gl-popover>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
index a804236d5a1..ff1a01d5037 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
@@ -32,6 +32,11 @@ export default {
type: String,
required: true,
},
+ refClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
mergeRequestRef() {
@@ -183,6 +188,7 @@ export default {
v-if="mergeRequestRef"
:href="mergeRequestRef.path"
class="gl-font-sm gl-font-monospace gl-text-gray-700! gl-hover-text-gray-900!"
+ :class="refClass"
data-testid="merge-request-ref"
@click="trackClick('click_mr_ref')"
>{{ mergeRequestRef.iid }}</gl-link
@@ -191,6 +197,7 @@ export default {
v-else
:href="refUrl"
class="gl-font-sm gl-font-monospace gl-text-gray-700! gl-hover-text-gray-900!"
+ :class="refClass"
data-testid="commit-ref-name"
@click="trackClick('click_commit_name')"
>{{ commitRef.name }}</gl-link
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
index b2da0df17c0..0c25fb45094 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
@@ -2,8 +2,10 @@
import { GlTableLite, GlTooltipDirective } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { keepLatestDownstreamPipelines } from '~/pipelines/components/parsing_utils';
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
+import PipelineFailedJobsWidget from '~/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue';
import eventHub from '../../event_hub';
import { TRACKING_CATEGORIES } from '../../constants';
import PipelineOperations from './pipeline_operations.vue';
@@ -12,7 +14,6 @@ import PipelineTriggerer from './pipeline_triggerer.vue';
import PipelineUrl from './pipeline_url.vue';
import PipelinesStatusBadge from './pipelines_status_badge.vue';
-const DEFAULT_TD_CLASS = 'gl-p-5!';
const HIDE_TD_ON_MOBILE = 'gl-display-none! gl-lg-display-table-cell!';
const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!';
@@ -20,6 +21,7 @@ const DEFAULT_TH_CLASSES =
export default {
components: {
GlTableLite,
+ PipelineFailedJobsWidget,
PipelineMiniGraph,
PipelineOperations,
PipelinesStatusBadge,
@@ -27,51 +29,15 @@ export default {
PipelineTriggerer,
PipelineUrl,
},
- tableFields: [
- {
- key: 'status',
- label: s__('Pipeline|Status'),
- thClass: DEFAULT_TH_CLASSES,
- columnClass: 'gl-w-15p',
- tdClass: DEFAULT_TD_CLASS,
- thAttr: { 'data-testid': 'status-th' },
- },
- {
- key: 'pipeline',
- label: __('Pipeline'),
- thClass: DEFAULT_TH_CLASSES,
- tdClass: `${DEFAULT_TD_CLASS}`,
- columnClass: 'gl-w-30p',
- thAttr: { 'data-testid': 'pipeline-th' },
- },
- {
- key: 'triggerer',
- label: s__('Pipeline|Triggerer'),
- thClass: DEFAULT_TH_CLASSES,
- tdClass: `${DEFAULT_TD_CLASS} ${HIDE_TD_ON_MOBILE}`,
- columnClass: 'gl-w-10p',
- thAttr: { 'data-testid': 'triggerer-th' },
- },
- {
- key: 'stages',
- label: s__('Pipeline|Stages'),
- thClass: DEFAULT_TH_CLASSES,
- tdClass: DEFAULT_TD_CLASS,
- columnClass: 'gl-w-quarter',
- thAttr: { 'data-testid': 'stages-th' },
- },
- {
- key: 'actions',
- thClass: DEFAULT_TH_CLASSES,
- tdClass: DEFAULT_TD_CLASS,
- columnClass: 'gl-w-15p',
- thAttr: { 'data-testid': 'actions-th' },
- },
- ],
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [Tracking.mixin()],
+ mixins: [Tracking.mixin(), glFeatureFlagMixin()],
+ inject: {
+ withFailedJobsDetails: {
+ default: false,
+ },
+ },
props: {
pipelines: {
type: Array,
@@ -104,6 +70,63 @@ export default {
cancelingPipeline: null,
};
},
+ computed: {
+ tableFields() {
+ return [
+ {
+ key: 'status',
+ label: s__('Pipeline|Status'),
+ thClass: DEFAULT_TH_CLASSES,
+ columnClass: 'gl-w-15p',
+ tdClass: this.tdClasses,
+ thAttr: { 'data-testid': 'status-th' },
+ },
+ {
+ key: 'pipeline',
+ label: __('Pipeline'),
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: `${this.tdClasses}`,
+ columnClass: 'gl-w-30p',
+ thAttr: { 'data-testid': 'pipeline-th' },
+ },
+ {
+ key: 'triggerer',
+ label: s__('Pipeline|Triggerer'),
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: `${this.tdClasses} ${HIDE_TD_ON_MOBILE}`,
+ columnClass: 'gl-w-10p',
+ thAttr: { 'data-testid': 'triggerer-th' },
+ },
+ {
+ key: 'stages',
+ label: s__('Pipeline|Stages'),
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: this.tdClasses,
+ columnClass: 'gl-w-quarter',
+ thAttr: { 'data-testid': 'stages-th' },
+ },
+ {
+ key: 'actions',
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: this.tdClasses,
+ columnClass: 'gl-w-15p',
+ thAttr: { 'data-testid': 'actions-th' },
+ },
+ ];
+ },
+ tdClasses() {
+ return this.withFailedJobsDetails ? 'gl-pb-0! gl-border-none!' : 'pl-p-5!';
+ },
+ pipelinesWithDetails() {
+ if (this.withFailedJobsDetails) {
+ return this.pipelines.map((p) => {
+ return { ...p, _showDetails: true };
+ });
+ }
+
+ return this.pipelines;
+ },
+ },
watch: {
pipelines() {
this.cancelingPipeline = null;
@@ -120,11 +143,17 @@ export default {
const downstream = pipeline.triggered;
return keepLatestDownstreamPipelines(downstream);
},
+ hasFailedJobs(pipeline) {
+ return pipeline?.failed_builds?.length > 0 || false;
+ },
setModalData(data) {
this.pipelineId = data.pipeline.id;
this.pipeline = data.pipeline;
this.endpoint = data.endpoint;
},
+ showFailedJobsWidget(item) {
+ return this.glFeatures.ciJobFailuresInMr && this.hasFailedJobs(item);
+ },
onSubmit() {
eventHub.$emit('postAction', this.endpoint);
this.cancelingPipeline = this.pipelineId;
@@ -142,9 +171,8 @@ export default {
<template>
<div class="ci-table">
<gl-table-lite
- :fields="$options.tableFields"
- :items="pipelines"
- tbody-tr-class="commit"
+ :fields="tableFields"
+ :items="pipelinesWithDetails"
:tbody-tr-attr="$options.TBODY_TR_ATTR"
stacked="lg"
fixed
@@ -167,6 +195,7 @@ export default {
:pipeline="item"
:pipeline-schedule-url="pipelineScheduleUrl"
:pipeline-key="pipelineKeyOption.value"
+ ref-color="gl-text-black-normal"
/>
</template>
@@ -188,6 +217,10 @@ export default {
<template #cell(actions)="{ item }">
<pipeline-operations :pipeline="item" :canceling-pipeline="cancelingPipeline" />
</template>
+
+ <template #row-details="{ item }">
+ <pipeline-failed-jobs-widget v-if="showFailedJobsWidget(item)" :pipeline-path="item.path" />
+ </template>
</gl-table-lite>
<pipeline-stop-modal :pipeline="pipeline" @submit="onSubmit" />
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 123a8c70444..6558dc10994 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -53,6 +53,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:mr_activity_filters, current_user)
push_frontend_feature_flag(:review_apps_redeploy_mr_widget, project)
push_frontend_feature_flag(:comment_on_files, current_user)
+ push_frontend_feature_flag(:ci_job_failures_in_mr, project)
end
around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :diffs, :discussions]
diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb
index 8eb790fde0d..36a2242b5b5 100644
--- a/app/models/concerns/spammable.rb
+++ b/app/models/concerns/spammable.rb
@@ -94,12 +94,22 @@ module Spammable
end
def unrecoverable_spam_error!
- self.errors.add(:base, _("Your %{spammable_entity_type} has been recognized as spam and has been discarded.") \
+ self.errors.add(:base, _("Your %{spammable_entity_type} has been recognized as spam. "\
+ "Please, change the content to proceed.") \
% { spammable_entity_type: spammable_entity_type })
end
def spammable_entity_type
- self.class.name.underscore
+ case self
+ when Issue
+ _('issue')
+ when MergeRequest
+ _('merge request')
+ when Snippet
+ _('snippet')
+ else
+ self.class.model_name.human.downcase
+ end
end
def spam_title
@@ -127,8 +137,12 @@ module Spammable
end
# Override in Spammable if further checks are necessary
- def check_for_spam?(user:)
- true
+ def check_for_spam?(*)
+ spammable_attribute_changed?
+ end
+
+ def spammable_attribute_changed?
+ (changed & self.class.spammable_attrs.to_h.keys).any?
end
def check_for_spam(action:, user:)
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 5e3b441c959..044afc1ca64 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -856,11 +856,9 @@ class Issue < ApplicationRecord
end
def spammable_attribute_changed?
- title_changed? ||
- description_changed? ||
- # NOTE: We need to check them for spam when issues are made non-confidential, because spam
- # may have been added while they were confidential and thus not being checked for spam.
- confidential_changed?(from: true, to: false)
+ # NOTE: We need to check them for spam when issues are made non-confidential, because spam
+ # may have been added while they were confidential and thus not being checked for spam.
+ super || confidential_changed?(from: true, to: false)
end
def ensure_metrics!
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index d6e9be11d36..2ce45b90330 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -23,6 +23,7 @@ class MergeRequest < ApplicationRecord
include Approvable
include IdInOrdered
include Todoable
+ include Spammable
extend ::Gitlab::Utils::Override
@@ -154,6 +155,9 @@ class MergeRequest < ApplicationRecord
# Flag to skip triggering mergeRequestMergeStatusUpdated GraphQL subscription.
attr_accessor :skip_merge_status_trigger
+ attr_spammable :title, spam_title: true
+ attr_spammable :description, spam_description: true
+
participant :reviewers
# Keep states definition to be evaluated before the state_machine block to
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 899f08b718a..d4f8c1b3b0b 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -261,19 +261,14 @@ class Snippet < ApplicationRecord
notes.includes(:author)
end
- def check_for_spam?(user:)
- visibility_level_changed?(to: Snippet::PUBLIC) ||
- (public? && (title_changed? || description_changed?))
+ def check_for_spam?(*)
+ visibility_level_changed?(to: Snippet::PUBLIC) || (public? && spammable_attribute_changed?)
end
def supports_recaptcha?
true
end
- def spammable_entity_type
- 'snippet'
- end
-
def to_ability_name
'snippet'
end
diff --git a/app/presenters/packages/nuget/service_index_presenter.rb b/app/presenters/packages/nuget/service_index_presenter.rb
index 033a1845c1c..b262735508c 100644
--- a/app/presenters/packages/nuget/service_index_presenter.rb
+++ b/app/presenters/packages/nuget/service_index_presenter.rb
@@ -35,12 +35,13 @@ module Packages
end
def resources
- available_services.map { |service| build_service(service) }
- .flatten
+ available_services.flat_map { |service| build_service(service) }
end
private
+ attr_reader :project_or_group
+
def available_services
case scope
when :group
@@ -77,13 +78,13 @@ module Packages
end
def scope
- return :project if @project_or_group.is_a?(::Project)
- return :group if @project_or_group.is_a?(::Group)
+ return :project if project_or_group.is_a?(::Project)
+ return :group if project_or_group.is_a?(::Group)
end
def download_service_url
params = {
- id: @project_or_group.id,
+ id: project_or_group.id,
package_name: nil,
package_version: nil,
package_filename: nil
@@ -97,7 +98,7 @@ module Packages
def metadata_service_url
params = {
- id: @project_or_group.id,
+ id: project_or_group.id,
package_name: nil,
package_version: nil
}
@@ -119,18 +120,18 @@ module Packages
def search_service_url
case scope
when :group
- api_v4_groups___packages_nuget_query_path(id: @project_or_group.id)
+ api_v4_groups___packages_nuget_query_path(id: project_or_group.id)
when :project
- api_v4_projects_packages_nuget_query_path(id: @project_or_group.id)
+ api_v4_projects_packages_nuget_query_path(id: project_or_group.id)
end
end
def publish_service_url
- api_v4_projects_packages_nuget_path(id: @project_or_group.id)
+ api_v4_projects_packages_nuget_path(id: project_or_group.id)
end
def symbol_service_url
- api_v4_projects_packages_nuget_symbolpackage_path(id: @project_or_group.id)
+ api_v4_projects_packages_nuget_symbolpackage_path(id: project_or_group.id)
end
end
end
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index 39e1594d215..a042e570132 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -41,6 +41,7 @@ module MergeRequests
# timeout, we do this before we attempt to save the merge request.
merge_request.skip_ensure_merge_request_diff = true
+ Spam::SpamActionService.new(spammable: merge_request, user: current_user, action: :create).execute
end
def set_projects!
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index aaed01403cb..123420d9af3 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -209,6 +209,11 @@ module MergeRequests
old_branch, new_branch)
end
+ override :before_update
+ def before_update(merge_request, skip_spam_check: false)
+ Spam::SpamActionService.new(spammable: merge_request, user: current_user, action: :update).execute unless skip_spam_check
+ end
+
override :handle_quick_actions
def handle_quick_actions(merge_request)
super
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 4930ef71e7d..a085840ee84 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -171,6 +171,6 @@
= f.gitlab_ui_checkbox_component :enabled_following,
s_('Preferences|Enable follow users')
= render_if_exists 'profiles/preferences/code_suggestions_settings', form: f
-
+ = render_if_exists 'profiles/preferences/zoekt_settings', form: f
#js-profile-preferences-app{ data: data_attributes }
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index 9b64afa8c60..08aeb3d4b07 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -4,40 +4,33 @@
%a.show-suppressed-diff.cursor-pointer.js-show-suppressed-diff= _("Changes suppressed. Click to show.")
%table.text-file.diff-wrap-lines.code.code-commit.js-syntax-highlight.commit-diff{ data: diff_view_data, class: too_big ? 'hide' : '' }
- - if Feature.enabled?(:inline_haml_diff_line_rendering, @project)
- - diff_file.highlighted_diff_lines.each do |line|
- - line_code = diff_file.line_code(line)
+ - diff_file.highlighted_diff_lines.each do |line|
+ - line_code = diff_file.line_code(line)
- %tr.line_holder{ class: line.type, id: line_code }
- - case line.type
- - when 'match'
- = diff_match_line line.old_pos, line.new_pos, text: line.text
- - when 'old-nomappinginraw', 'new-nomappinginraw', 'unchanged-nomappinginraw'
- = diff_nomappinginraw_line line, %w[old_line diff-line-num], %w[new_line diff-line-num], %w[line_content]
- - when 'old-nonewline', 'new-nonewline'
- %td.old_line.diff-line-num
- %td.new_line.diff-line-num
- %td.line_content.match= line.text
- - else
- %td.old_line.diff-line-num{ class: "#{line.type} js-avatar-container", data: { linenumber: line.old_pos } }
- = add_diff_note_button(line_code, diff_file.position(line), line.type)
- %a{ href: "##{line_code}", data: { linenumber: diff_link_number(line.type, "new", line.old_pos) } }
+ %tr.line_holder{ class: line.type, id: line_code }
+ - case line.type
+ - when 'match'
+ = diff_match_line line.old_pos, line.new_pos, text: line.text
+ - when 'old-nomappinginraw', 'new-nomappinginraw', 'unchanged-nomappinginraw'
+ = diff_nomappinginraw_line line, %w[old_line diff-line-num], %w[new_line diff-line-num], %w[line_content]
+ - when 'old-nonewline', 'new-nonewline'
+ %td.old_line.diff-line-num
+ %td.new_line.diff-line-num
+ %td.line_content.match= line.text
+ - else
+ %td.old_line.diff-line-num{ class: "#{line.type} js-avatar-container", data: { linenumber: line.old_pos } }
+ = add_diff_note_button(line_code, diff_file.position(line), line.type)
+ %a{ href: "##{line_code}", data: { linenumber: diff_link_number(line.type, "new", line.old_pos) } }
- %td.new_line.diff-line-num{ class: line.type, data: { linenumber: line.new_pos } }
- %a{ href: "##{line_code}", data: { linenumber: diff_link_number(line.type, "old", line.new_pos) } }
+ %td.new_line.diff-line-num{ class: line.type, data: { linenumber: line.new_pos } }
+ %a{ href: "##{line_code}", data: { linenumber: diff_link_number(line.type, "old", line.new_pos) } }
- %td.line_content{ class: line.type }<
- = diff_line_content(line.rich_text)
+ %td.line_content{ class: line.type }<
+ = diff_line_content(line.rich_text)
- - if line.discussable? && @grouped_diff_discussions.present? && @grouped_diff_discussions[line_code]
- - line_discussions = @grouped_diff_discussions[line_code]
- = render "discussions/diff_discussion", discussions: line_discussions, expanded: line_discussions.any?(&:expanded?)
-
- - else
- = render partial: "projects/diffs/line",
- collection: diff_file.highlighted_diff_lines,
- as: :line,
- locals: { diff_file: diff_file, discussions: @grouped_diff_discussions }
+ - if line.discussable? && @grouped_diff_discussions.present? && @grouped_diff_discussions[line_code]
+ - line_discussions = @grouped_diff_discussions[line_code]
+ = render "discussions/diff_discussion", discussions: line_discussions, expanded: line_discussions.any?(&:expanded?)
- if !diff_file.new_file? && !diff_file.deleted_file? && diff_file.highlighted_diff_lines.any?
- last_line = diff_file.highlighted_diff_lines.last