diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-12 21:09:34 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-12 21:09:34 +0300 |
commit | 949ed51c6d20ba24e1f508cae9a65b86114bff62 (patch) | |
tree | 9308fe87e6d61b35eb3b2d101c47e89b9054655f /app | |
parent | c0b17cee8be646588b14db49ad6d91b8cc818f97 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
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 |