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>2022-02-03 21:17:34 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-03 21:17:34 +0300
commit0aa20f3dac8e19cc10b62e08a5c84df105a648c2 (patch)
tree3347e2aa05c399d70f82775fa58ffd4b531ba282 /app
parent67daaf4021a180166ad063e3a75ea777e96586a6 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/members/components/avatars/user_avatar.vue14
-rw-r--r--app/assets/javascripts/repository/components/blob_content_viewer.vue88
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue20
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue14
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/index.js31
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue14
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue14
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue9
-rw-r--r--app/assets/javascripts/repository/constants.js51
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer.vue14
-rw-r--r--app/models/commit.rb4
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/merge_request.rb35
-rw-r--r--app/serializers/member_user_entity.rb1
-rw-r--r--app/services/issuable/common_system_notes_service.rb2
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml4
-rw-r--r--app/views/shared/web_hooks/_hook.html.haml2
17 files changed, 160 insertions, 159 deletions
diff --git a/app/assets/javascripts/members/components/avatars/user_avatar.vue b/app/assets/javascripts/members/components/avatars/user_avatar.vue
index 9687eacb036..ec59f0f681c 100644
--- a/app/assets/javascripts/members/components/avatars/user_avatar.vue
+++ b/app/assets/javascripts/members/components/avatars/user_avatar.vue
@@ -8,10 +8,14 @@ import {
import { generateBadges } from 'ee_else_ce/members/utils';
import { glEmojiTag } from '~/emoji';
import { __ } from '~/locale';
+import { isUserBusy } from '~/set_status_modal/utils';
import { AVATAR_SIZE } from '../../constants';
export default {
name: 'UserAvatar',
+ i18n: {
+ busy: __('Busy'),
+ },
avatarSize: AVATAR_SIZE,
orphanedUserLabel: __('Orphaned member'),
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
@@ -46,7 +50,10 @@ export default {
}).filter((badge) => badge.show);
},
statusEmoji() {
- return this.user?.status?.emoji;
+ return this.user?.showStatus && this.user?.status?.emoji;
+ },
+ isUserBusy() {
+ return isUserBusy(this.user?.availability || '');
},
},
methods: {
@@ -73,6 +80,11 @@ export default {
:entity-id="user.id"
>
<template #meta>
+ <div v-if="isUserBusy" class="gl-p-1">
+ <span class="gl-text-gray-500 gl-font-sm gl-font-weight-normal"
+ >({{ $options.i18n.busy }})</span
+ >
+ </div>
<div v-if="statusEmoji" class="gl-p-1">
<span
v-safe-html:[$options.safeHtmlConfig]="glEmojiTag(statusEmoji)"
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue
index ca89e549181..a37367afa3b 100644
--- a/app/assets/javascripts/repository/components/blob_content_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue
@@ -9,12 +9,14 @@ import axios from '~/lib/utils/axios_utils';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import getRefMixin from '../mixins/get_ref';
import blobInfoQuery from '../queries/blob_info.query.graphql';
+import { DEFAULT_BLOB_INFO, TEXT_FILE_TYPE, LFS_STORAGE } from '../constants';
import BlobButtonGroup from './blob_button_group.vue';
import BlobEdit from './blob_edit.vue';
import ForkSuggestion from './fork_suggestion.vue';
-import { loadViewer, viewerProps } from './blob_viewers';
+import { loadViewer } from './blob_viewers';
export default {
i18n: {
@@ -29,7 +31,7 @@ export default {
GlButton,
ForkSuggestion,
},
- mixins: [getRefMixin],
+ mixins: [getRefMixin, glFeatureFlagMixin()],
inject: {
originalBranch: {
default: '',
@@ -78,52 +80,7 @@ export default {
isBinary: false,
isLoadingLegacyViewer: false,
activeViewerType: SIMPLE_BLOB_VIEWER,
- project: {
- userPermissions: {
- pushCode: false,
- downloadCode: false,
- createMergeRequestIn: false,
- forkProject: false,
- },
- pathLocks: {
- nodes: [],
- },
- repository: {
- empty: true,
- blobs: {
- nodes: [
- {
- name: '',
- size: '',
- rawTextBlob: '',
- type: '',
- fileType: '',
- tooLarge: false,
- path: '',
- editBlobPath: '',
- ideEditPath: '',
- forkAndEditPath: '',
- ideForkAndEditPath: '',
- storedExternally: false,
- externalStorage: '',
- environmentFormattedExternalUrl: '',
- environmentExternalUrlForRouteMap: '',
- canModifyBlob: false,
- canCurrentUserPushToBranch: false,
- archived: false,
- rawPath: '',
- externalStorageUrl: '',
- replacePath: '',
- pipelineEditorPath: '',
- deletePath: '',
- simpleViewer: {},
- richViewer: null,
- webPath: '',
- },
- ],
- },
- },
- },
+ project: DEFAULT_BLOB_INFO,
};
},
computed: {
@@ -134,7 +91,7 @@ export default {
return this.$apollo.queries.project.loading;
},
isBinaryFileType() {
- return this.isBinary || this.blobInfo.simpleViewer?.fileType !== 'text';
+ return this.isBinary || this.blobInfo.simpleViewer?.fileType !== TEXT_FILE_TYPE;
},
blobInfo() {
const nodes = this.project?.repository?.blobs?.nodes || [];
@@ -153,11 +110,16 @@ export default {
},
blobViewer() {
const { fileType } = this.viewer;
- return loadViewer(fileType, this.isUsingLfs);
+ return this.shouldLoadLegacyViewer ? null : loadViewer(fileType, this.isUsingLfs);
},
- viewerProps() {
- const { fileType } = this.viewer;
- return viewerProps(fileType, this.blobInfo);
+ shouldLoadLegacyViewer() {
+ return this.viewer.fileType === TEXT_FILE_TYPE && !this.glFeatures.highlightJs;
+ },
+ legacyViewerLoaded() {
+ return (
+ (this.activeViewerType === SIMPLE_BLOB_VIEWER && this.legacySimpleViewer) ||
+ (this.activeViewerType === RICH_BLOB_VIEWER && this.legacyRichViewer)
+ );
},
canLock() {
const { pushCode, downloadCode } = this.project.userPermissions;
@@ -186,20 +148,22 @@ export default {
: this.blobInfo.forkAndEditPath;
},
isUsingLfs() {
- return this.blobInfo.storedExternally && this.blobInfo.externalStorage === 'lfs';
+ return this.blobInfo.storedExternally && this.blobInfo.externalStorage === LFS_STORAGE;
},
},
methods: {
- loadLegacyViewer(type) {
- if (this.legacyViewerLoaded(type)) {
+ loadLegacyViewer() {
+ if (this.legacyViewerLoaded) {
return;
}
+ const type = this.activeViewerType;
+
this.isLoadingLegacyViewer = true;
axios
.get(`${this.blobInfo.webPath}?format=json&viewer=${type}`)
.then(({ data: { html, binary } }) => {
- if (type === 'simple') {
+ if (type === SIMPLE_BLOB_VIEWER) {
this.legacySimpleViewer = html;
} else {
this.legacyRichViewer = html;
@@ -210,12 +174,6 @@ export default {
})
.catch(() => this.displayError());
},
- legacyViewerLoaded(type) {
- return (
- (type === SIMPLE_BLOB_VIEWER && this.legacySimpleViewer) ||
- (type === RICH_BLOB_VIEWER && this.legacyRichViewer)
- );
- },
displayError() {
createFlash({ message: __('An error occurred while loading the file. Please try again.') });
},
@@ -223,7 +181,7 @@ export default {
this.activeViewerType = newViewer || SIMPLE_BLOB_VIEWER;
if (!this.blobViewer) {
- this.loadLegacyViewer(this.activeViewerType);
+ this.loadLegacyViewer();
}
},
editBlob(target) {
@@ -309,7 +267,7 @@ export default {
:hide-line-numbers="true"
:loading="isLoadingLegacyViewer"
/>
- <component :is="blobViewer" v-else v-bind="viewerProps" class="blob-viewer" />
+ <component :is="blobViewer" v-else :blob="blobInfo" class="blob-viewer" />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue
index 48fa33eb558..f7b318c64d9 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue
@@ -9,19 +9,17 @@ export default {
GlLink,
},
props: {
- fileName: {
- type: String,
+ blob: {
+ type: Object,
required: true,
},
- filePath: {
- type: String,
- required: true,
- },
- fileSize: {
- type: Number,
- required: false,
- default: 0,
- },
+ },
+ data() {
+ return {
+ fileName: this.blob.name,
+ filePath: this.blob.rawPath,
+ fileSize: this.blob.rawSize || 0,
+ };
},
computed: {
downloadFileSize() {
diff --git a/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue
index 83d36209bb3..5027f7877aa 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue
@@ -1,15 +1,17 @@
<script>
export default {
props: {
- url: {
- type: String,
- required: true,
- },
- alt: {
- type: String,
+ blob: {
+ type: Object,
required: true,
},
},
+ data() {
+ return {
+ url: this.blob.rawPath,
+ alt: this.blob.name,
+ };
+ },
};
</script>
<template>
diff --git a/app/assets/javascripts/repository/components/blob_viewers/index.js b/app/assets/javascripts/repository/components/blob_viewers/index.js
index 6e4e36579d3..daa11802cc5 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/index.js
+++ b/app/assets/javascripts/repository/components/blob_viewers/index.js
@@ -17,34 +17,3 @@ export const loadViewer = (type, isUsingLfs) => {
return viewer;
};
-
-export const viewerProps = (type, blob) => {
- const props = {
- text: {
- content: blob.rawTextBlob,
- autoDetect: true, // We'll eventually disable autoDetect and pass the language explicitly to reduce the footprint (https://gitlab.com/gitlab-org/gitlab/-/issues/348145)
- },
- download: {
- fileName: blob.name,
- filePath: blob.rawPath,
- fileSize: blob.rawSize,
- },
- image: {
- url: blob.rawPath,
- alt: blob.name,
- },
- video: {
- url: blob.rawPath,
- },
- pdf: {
- url: blob.rawPath,
- fileSize: blob.rawSize,
- },
- lfs: {
- fileName: blob.name,
- filePath: blob.rawPath,
- },
- };
-
- return props[type] || props[blob.externalStorage];
-};
diff --git a/app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue
index 1596c5c91b1..6dc7e10662e 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue
@@ -13,15 +13,17 @@ export default {
GlSprintf,
},
props: {
- fileName: {
- type: String,
- required: true,
- },
- filePath: {
- type: String,
+ blob: {
+ type: Object,
required: true,
},
},
+ data() {
+ return {
+ fileName: this.blob.name,
+ filePath: this.blob.rawPath,
+ };
+ },
};
</script>
diff --git a/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue
index 803a357df52..c3df5984426 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue
@@ -11,17 +11,17 @@ export default {
tooLargeButtonText: __('Download PDF'),
},
props: {
- url: {
- type: String,
- required: true,
- },
- fileSize: {
- type: Number,
+ blob: {
+ type: Object,
required: true,
},
},
data() {
- return { totalPages: 0 };
+ return {
+ url: this.blob.rawPath,
+ fileSize: this.blob.rawSize,
+ totalPages: 0,
+ };
},
computed: {
tooLargeToDisplay() {
diff --git a/app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue
index dec0c4802ca..260b831f4d1 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue
@@ -1,11 +1,16 @@
<script>
export default {
props: {
- url: {
- type: String,
+ blob: {
+ type: Object,
required: true,
},
},
+ data() {
+ return {
+ url: this.blob.rawPath,
+ };
+ },
};
</script>
<template>
diff --git a/app/assets/javascripts/repository/constants.js b/app/assets/javascripts/repository/constants.js
index d01757d6141..e206d9bfbd2 100644
--- a/app/assets/javascripts/repository/constants.js
+++ b/app/assets/javascripts/repository/constants.js
@@ -25,3 +25,54 @@ export const PDF_MAX_FILE_SIZE = 10000000; // 10 MB
export const PDF_MAX_PAGE_LIMIT = 50;
export const ROW_APPEAR_DELAY = 150;
+
+export const DEFAULT_BLOB_INFO = {
+ userPermissions: {
+ pushCode: false,
+ downloadCode: false,
+ createMergeRequestIn: false,
+ forkProject: false,
+ },
+ pathLocks: {
+ nodes: [],
+ },
+ repository: {
+ empty: true,
+ blobs: {
+ nodes: [
+ {
+ name: '',
+ size: '',
+ rawTextBlob: '',
+ type: '',
+ fileType: '',
+ tooLarge: false,
+ path: '',
+ editBlobPath: '',
+ ideEditPath: '',
+ forkAndEditPath: '',
+ ideForkAndEditPath: '',
+ storedExternally: false,
+ externalStorage: '',
+ environmentFormattedExternalUrl: '',
+ environmentExternalUrlForRouteMap: '',
+ canModifyBlob: false,
+ canCurrentUserPushToBranch: false,
+ archived: false,
+ rawPath: '',
+ externalStorageUrl: '',
+ replacePath: '',
+ pipelineEditorPath: '',
+ deletePath: '',
+ simpleViewer: {},
+ richViewer: null,
+ webPath: '',
+ },
+ ],
+ },
+ },
+};
+
+export const TEXT_FILE_TYPE = 'text';
+
+export const LFS_STORAGE = 'lfs';
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer.vue
index 0aa2589637f..f6580a716ee 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer.vue
@@ -4,6 +4,7 @@ import LineNumbers from '~/vue_shared/components/line_numbers.vue';
import { sanitize } from '~/lib/dompurify';
const LINE_SELECT_CLASS_NAME = 'hll';
+const PLAIN_TEXT_LANGUAGE = 'plaintext';
export default {
components: {
@@ -13,24 +14,21 @@ export default {
SafeHtml: GlSafeHtmlDirective,
},
props: {
- content: {
- type: String,
+ blob: {
+ type: Object,
required: true,
},
- language: {
- type: String,
- required: false,
- default: 'plaintext',
- },
autoDetect: {
type: Boolean,
required: false,
- default: false,
+ default: true, // We'll eventually disable autoDetect and pass the language explicitly to reduce the footprint (https://gitlab.com/gitlab-org/gitlab/-/issues/348145)
},
},
data() {
return {
languageDefinition: null,
+ content: this.blob.rawTextBlob,
+ language: this.blob.language || PLAIN_TEXT_LANGUAGE,
hljs: null,
};
},
diff --git a/app/models/commit.rb b/app/models/commit.rb
index f0c5f3c2d12..5293bfcf1ab 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -513,9 +513,7 @@ class Commit
# We don't want to do anything for `Commit` model, so this is empty.
end
- # WIP is deprecated in favor of Draft. Currently both options are supported
- # https://gitlab.com/gitlab-org/gitlab/-/issues/227426
- DRAFT_REGEX = /\A\s*#{Regexp.union(Gitlab::Regex.merge_request_wip, Gitlab::Regex.merge_request_draft)}|(fixup!|squash!)\s/.freeze
+ DRAFT_REGEX = /\A\s*#{Gitlab::Regex.merge_request_draft}|(fixup!|squash!)\s/.freeze
def work_in_progress?
!!(title =~ DRAFT_REGEX)
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index f4780a1cfc0..0138c0ad20f 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -580,7 +580,7 @@ module Issuable
##
# Overridden in MergeRequest
#
- def wipless_title_changed(old_title)
+ def draftless_title_changed(old_title)
old_title != title
end
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index fd77e530af0..ffa010cf9fc 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -563,16 +563,22 @@ class MergeRequest < ApplicationRecord
DRAFT_REGEX = /\A*#{Gitlab::Regex.merge_request_draft}+\s*/i.freeze
- def self.work_in_progress?(title)
+ def self.draft?(title)
!!(title =~ DRAFT_REGEX)
end
- def self.wipless_title(title)
+ def self.draftless_title(title)
title.sub(DRAFT_REGEX, "")
end
- def self.wip_title(title)
- work_in_progress?(title) ? title : "Draft: #{title}"
+ def self.draft_title(title)
+ draft?(title) ? title : "Draft: #{title}"
+ end
+
+ class << self
+ alias_method :work_in_progress?, :draft?
+ alias_method :wipless_title, :draftless_title
+ alias_method :wip_title, :draft_title
end
def self.participant_includes
@@ -585,9 +591,10 @@ class MergeRequest < ApplicationRecord
# Verifies if title has changed not taking into account Draft prefix
# for merge requests.
- def wipless_title_changed(old_title)
- self.class.wipless_title(old_title) != self.wipless_title
+ def draftless_title_changed(old_title)
+ self.class.draftless_title(old_title) != self.draftless_title
end
+ alias_method :wipless_title_changed, :draftless_title_changed
def hook_attrs
Gitlab::HookData::MergeRequestBuilder.new(self).build
@@ -1086,18 +1093,20 @@ class MergeRequest < ApplicationRecord
@closed_event ||= target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: :closed).last
end
- def work_in_progress?
- self.class.work_in_progress?(title)
+ def draft?
+ self.class.draft?(title)
end
- alias_method :draft?, :work_in_progress?
+ alias_method :work_in_progress?, :draft?
- def wipless_title
- self.class.wipless_title(self.title)
+ def draftless_title
+ self.class.draftless_title(self.title)
end
+ alias_method :wipless_title, :draftless_title
- def wip_title
- self.class.wip_title(self.title)
+ def draft_title
+ self.class.draft_title(self.title)
end
+ alias_method :wip_title, :draft_title
def mergeable?(skip_ci_check: false, skip_discussions_check: false)
return false unless mergeable_state?(skip_ci_check: skip_ci_check,
diff --git a/app/serializers/member_user_entity.rb b/app/serializers/member_user_entity.rb
index 01920fc95bb..fde3282ad25 100644
--- a/app/serializers/member_user_entity.rb
+++ b/app/serializers/member_user_entity.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
class MemberUserEntity < UserEntity
- unexpose :show_status
unexpose :path
unexpose :state
unexpose :status_tooltip_html
diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb
index 38050708fc5..9ee54c7ba0f 100644
--- a/app/services/issuable/common_system_notes_service.rb
+++ b/app/services/issuable/common_system_notes_service.rb
@@ -71,7 +71,7 @@ module Issuable
def create_title_change_note(old_title)
create_draft_note(old_title)
- if issuable.wipless_title_changed(old_title)
+ if issuable.draftless_title_changed(old_title)
SystemNoteService.change_title(issuable, issuable.project, current_user, old_title)
end
end
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 20b86e0e7cd..d663034b9d8 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -156,13 +156,13 @@
= sprite_icon('slight-frown')
%span.nav-item-name
= _('Abuse Reports')
- %span.badge.badge-pill.count= number_with_delimiter(AbuseReport.count(:all))
+ = gl_badge_tag number_with_delimiter(AbuseReport.count(:all)), variant: :info, size: :sm
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_abuse_reports_path do
%strong.fly-out-top-item-name
= _('Abuse Reports')
- %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(AbuseReport.count(:all))
+ = gl_badge_tag number_with_delimiter(AbuseReport.count(:all)), variant: :info, size: :sm
= render_if_exists 'layouts/nav/sidebar/licenses_link'
diff --git a/app/views/shared/web_hooks/_hook.html.haml b/app/views/shared/web_hooks/_hook.html.haml
index c5a03ef4dc1..529ef47a2cf 100644
--- a/app/views/shared/web_hooks/_hook.html.haml
+++ b/app/views/shared/web_hooks/_hook.html.haml
@@ -22,4 +22,4 @@
.col-md-4.col-lg-5.text-right-md.gl-mt-2
%span>= render 'shared/web_hooks/test_button', hook: hook, button_class: 'btn-sm btn-default gl-mr-3'
%span>= link_to _('Edit'), edit_hook_path(hook), class: 'btn gl-button btn-default btn-sm gl-mr-3'
- = link_to _('Delete'), destroy_hook_path(hook), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn gl-button btn-secondary btn-danger-secondary btn-sm'
+ = link_to _('Delete'), destroy_hook_path(hook), aria: { label: s_('Webhooks|Delete webhook') }, data: { confirm_btn_variant: "danger", confirm: s_('Webhooks|Are you sure you want to delete this webhook?') }, method: :delete, class: 'btn gl-button btn-secondary btn-danger-secondary btn-sm'