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-08-15 12:10:30 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-08-15 12:10:30 +0300
commitcd5f4619db234c00d0c01ed63bb92df6c10b0fd3 (patch)
tree09ef12a2231c616d4f747b9d5b83597ae179bb06 /app
parent024f77efd68833bb78540ff9b4c7b4ec4b9dfe39 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/admin/abuse_report/components/report_actions.vue10
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue47
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue78
-rw-r--r--app/assets/javascripts/deploy_keys/components/keys_panel.vue11
-rw-r--r--app/assets/javascripts/nav/components/new_nav_toggle.vue12
-rw-r--r--app/assets/javascripts/service_desk/components/empty_state_without_any_issues.vue7
-rw-r--r--app/assets/javascripts/service_desk/components/info_banner.vue2
-rw-r--r--app/assets/stylesheets/pages/projects.scss13
-rw-r--r--app/controllers/admin/abuse_reports_controller.rb18
-rw-r--r--app/models/concerns/enum_inheritance.rb58
-rw-r--r--app/serializers/admin/abuse_report_details_entity.rb8
-rw-r--r--app/services/admin/abuse_report_update_service.rb91
-rw-r--r--app/services/admin/abuse_reports/moderate_user_service.rb93
-rw-r--r--app/views/admin/application_settings/appearances/_form.html.haml267
-rw-r--r--app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml51
-rw-r--r--app/views/admin/deploy_keys/new.html.haml14
-rw-r--r--app/views/profiles/preferences/show.html.haml2
-rw-r--r--app/views/projects/deploy_keys/edit.html.haml12
-rw-r--r--app/views/projects/issues/service_desk.html.haml2
-rw-r--r--app/views/shared/deploy_keys/_form.html.haml51
-rw-r--r--app/views/shared/deploy_keys/_index.html.haml12
-rw-r--r--app/views/shared/deploy_keys/_project_group_form.html.haml8
22 files changed, 492 insertions, 375 deletions
diff --git a/app/assets/javascripts/admin/abuse_report/components/report_actions.vue b/app/assets/javascripts/admin/abuse_report/components/report_actions.vue
index 57d5d46ceb4..92478e10289 100644
--- a/app/assets/javascripts/admin/abuse_report/components/report_actions.vue
+++ b/app/assets/javascripts/admin/abuse_report/components/report_actions.vue
@@ -95,10 +95,12 @@ export default {
return;
}
- axios
- .put(this.report.updatePath, this.form)
- .then(this.handleResponse)
- .catch(this.handleError);
+ // TODO: In 16.4 use moderateUserPath without falling back to using updatePath
+ // See https://gitlab.com/gitlab-org/modelops/anti-abuse/team-tasks/-/issues/167?work_item_iid=443
+ const { moderateUserPath, updatePath } = this.report;
+ const path = moderateUserPath || updatePath;
+
+ axios.put(path, this.form).then(this.handleResponse).catch(this.handleError);
},
handleResponse({ data }) {
this.toggleActionsDrawer();
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index 4860215d8f2..ec17bbea48f 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -1,5 +1,5 @@
<script>
-import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
+import { GlButton, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { createAlert } from '~/alert';
import { s__ } from '~/locale';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
@@ -14,8 +14,9 @@ export default {
ConfirmModal,
KeysPanel,
NavigationTabs,
- GlLoadingIcon,
+ GlButton,
GlIcon,
+ GlLoadingIcon,
},
props: {
endpoint: {
@@ -42,6 +43,10 @@ export default {
available_project_keys: s__('DeployKeys|Privately accessible deploy keys'),
public_keys: s__('DeployKeys|Publicly accessible deploy keys'),
},
+ i18n: {
+ loading: s__('DeployKeys|Loading deploy keys'),
+ addButton: s__('DeployKeys|Add new key'),
+ },
computed: {
tabs() {
return Object.keys(this.$options.scopes).map((scope) => {
@@ -132,23 +137,41 @@ export default {
</script>
<template>
- <div class="gl-mb-3 deploy-keys">
+ <div class="deploy-keys">
<confirm-modal :visible="confirmModalVisible" @remove="removeKey" @cancel="cancel" />
<gl-loading-icon
v-if="isLoading && !hasKeys"
- :label="s__('DeployKeys|Loading deploy keys')"
- size="lg"
+ :label="$options.i18n.loading"
+ size="sm"
+ class="gl-m-5"
/>
<template v-else-if="hasKeys">
- <div class="top-area scrolling-tabs-container inner-page-scroll-tabs">
- <div class="fade-left">
- <gl-icon name="chevron-lg-left" :size="12" />
- </div>
- <div class="fade-right">
- <gl-icon name="chevron-lg-right" :size="12" />
+ <div class="gl-new-card-header gl-align-items-center gl-pt-0 gl-pb-0 gl-pl-0">
+ <div class="top-area scrolling-tabs-container inner-page-scroll-tabs gl-border-b-0">
+ <div class="fade-left">
+ <gl-icon name="chevron-lg-left" :size="12" />
+ </div>
+ <div class="fade-right">
+ <gl-icon name="chevron-lg-right" :size="12" />
+ </div>
+
+ <navigation-tabs
+ :tabs="tabs"
+ scope="deployKeys"
+ class="gl-rounded-lg"
+ @onChangeTab="onChangeTab"
+ />
</div>
- <navigation-tabs :tabs="tabs" scope="deployKeys" @onChangeTab="onChangeTab" />
+ <div class="gl-new-card-actions">
+ <gl-button
+ size="small"
+ class="js-toggle-button js-toggle-content"
+ data-testid="add-new-deploy-key-button"
+ >
+ {{ $options.i18n.addButton }}
+ </gl-button>
+ </div>
</div>
<keys-panel
:project-id="projectId"
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index 79c45553659..16c745d8cff 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -1,6 +1,6 @@
<!-- eslint-disable vue/multi-word-component-names -->
<script>
-import { GlIcon, GlLink, GlTooltipDirective, GlButton } from '@gitlab/ui';
+import { GlBadge, GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { head, tail } from 'lodash';
import { s__, sprintf } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
@@ -10,9 +10,9 @@ import ActionBtn from './action_btn.vue';
export default {
components: {
ActionBtn,
+ GlBadge,
GlButton,
GlIcon,
- GlLink,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -111,12 +111,21 @@ export default {
</script>
<template>
- <div class="gl-responsive-table-row deploy-key">
+ <div
+ class="gl-responsive-table-row gl-align-items-flex-start deploy-key gl-bg-gray-10 gl-md-pl-5 gl-md-pr-5 gl-border-gray-100!"
+ >
<div class="table-section section-40">
- <div role="rowheader" class="table-mobile-header">{{ s__('DeployKeys|Deploy key') }}</div>
+ <div
+ role="rowheader"
+ class="table-mobile-header gl-align-self-start gl-font-weight-bold gl-text-gray-700"
+ >
+ {{ s__('DeployKeys|Deploy key') }}
+ </div>
<div class="table-mobile-content" data-testid="key-container">
- <strong class="title" data-testid="key-title-content"> {{ deployKey.title }} </strong>
- <dl>
+ <p class="title gl-font-weight-semibold gl-text-gray-700" data-testid="key-title-content">
+ {{ deployKey.title }}
+ </p>
+ <dl class="gl-font-sm gl-mb-0">
<dt>{{ __('SHA256') }}</dt>
<dd class="fingerprint" data-testid="key-sha256-fingerprint-content">
{{ deployKey.fingerprint_sha256 }}
@@ -133,53 +142,62 @@ export default {
</div>
</div>
<div class="table-section section-20 section-wrap">
- <div role="rowheader" class="table-mobile-header">{{ s__('DeployKeys|Project usage') }}</div>
- <div class="table-mobile-content deploy-project-list">
+ <div role="rowheader" class="table-mobile-header gl-font-weight-bold gl-text-gray-700">
+ {{ s__('DeployKeys|Project usage') }}
+ </div>
+ <div class="table-mobile-content deploy-project-list gl-display-flex gl-flex-wrap">
<template v-if="projects.length > 0">
- <gl-link
+ <gl-badge
v-gl-tooltip
:title="projectTooltipTitle(firstProject)"
- class="label deploy-project-label"
+ :icon="firstProject.can_push ? 'lock-open' : 'lock'"
+ class="deploy-project-label gl-mr-2 gl-mb-2 gl-truncate"
>
- <span> {{ firstProject.project.full_name }} </span>
- <gl-icon :name="firstProject.can_push ? 'lock-open' : 'lock'" />
- </gl-link>
- <gl-link
+ <span class="gl-text-truncate">{{ firstProject.project.full_name }}</span>
+ </gl-badge>
+
+ <gl-badge
v-if="isExpandable"
v-gl-tooltip
:title="restProjectsTooltip"
- class="label deploy-project-label"
- @click="toggleExpanded"
+ class="deploy-project-label gl-mr-2 gl-mb-2 gl-truncate"
+ href="#"
+ @click.native="toggleExpanded"
>
- <span>{{ restProjectsLabel }}</span>
- </gl-link>
- <gl-link
+ <span class="gl-text-truncate">{{ restProjectsLabel }}</span>
+ </gl-badge>
+
+ <gl-badge
v-for="deployKeysProject in restProjects"
v-else-if="isExpanded"
:key="deployKeysProject.project.full_path"
v-gl-tooltip
:href="deployKeysProject.project.full_path"
:title="projectTooltipTitle(deployKeysProject)"
- class="label deploy-project-label"
+ :icon="deployKeysProject.can_push ? 'lock-open' : 'lock'"
+ class="deploy-project-label gl-mr-2 gl-mb-2 gl-truncate"
>
- <span> {{ deployKeysProject.project.full_name }} </span>
- <gl-icon :name="deployKeysProject.can_push ? 'lock-open' : 'lock'" />
- </gl-link>
+ <span class="gl-text-truncate">{{ deployKeysProject.project.full_name }}</span>
+ </gl-badge>
</template>
- <span v-else class="text-secondary">{{ __('None') }}</span>
+ <span v-else class="gl-text-secondary">{{ __('None') }}</span>
</div>
</div>
<div class="table-section section-15">
- <div role="rowheader" class="table-mobile-header">{{ __('Created') }}</div>
- <div class="table-mobile-content text-secondary key-created-at">
+ <div role="rowheader" class="table-mobile-header gl-font-weight-bold gl-text-gray-700">
+ {{ __('Created') }}
+ </div>
+ <div class="table-mobile-content gl-text-gray-700 key-created-at">
<span v-gl-tooltip :title="tooltipTitle(deployKey.created_at)">
<gl-icon name="calendar" /> <span>{{ timeFormatted(deployKey.created_at) }}</span>
</span>
</div>
</div>
<div class="table-section section-15">
- <div role="rowheader" class="table-mobile-header">{{ __('Expires') }}</div>
- <div class="table-mobile-content text-secondary key-expires-at">
+ <div role="rowheader" class="table-mobile-header gl-font-weight-bold gl-text-gray-700">
+ {{ __('Expires') }}
+ </div>
+ <div class="table-mobile-content gl-text-gray-700 key-expires-at">
<span
v-if="deployKey.expires_at"
v-gl-tooltip
@@ -214,7 +232,7 @@ export default {
:deploy-key="deployKey"
:title="__('Remove')"
:aria-label="__('Remove')"
- category="primary"
+ category="secondary"
variant="danger"
icon="remove"
type="remove"
@@ -229,7 +247,7 @@ export default {
type="disable"
data-container="body"
icon="cancel"
- category="primary"
+ category="secondary"
variant="danger"
/>
</div>
diff --git a/app/assets/javascripts/deploy_keys/components/keys_panel.vue b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
index e04cbbe72b9..dac63188aa5 100644
--- a/app/assets/javascripts/deploy_keys/components/keys_panel.vue
+++ b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
@@ -28,9 +28,12 @@ export default {
</script>
<template>
- <div class="deploy-keys-panel table-holder">
+ <div class="deploy-keys-panel table-holder gl-bg-white gl-rounded-lg">
<template v-if="keys.length > 0">
- <div role="row" class="gl-responsive-table-row table-row-header">
+ <div
+ role="row"
+ class="gl-responsive-table-row table-row-header gl-font-base gl-font-weight-bold gl-text-gray-900 gl-md-pl-5 gl-md-pr-5 gl-bg-gray-10 gl-border-gray-100!"
+ >
<div role="rowheader" class="table-section section-40">
{{ s__('DeployKeys|Deploy key') }}
</div>
@@ -50,8 +53,8 @@ export default {
:project-id="projectId"
/>
</template>
- <div v-else class="settings-message text-center gl-mt-5">
- {{ s__('DeployKeys|No deploy keys found. Create one with the form above.') }}
+ <div v-else class="gl-new-card-empty gl-bg-gray-10 gl-text-center gl-p-5">
+ {{ s__('DeployKeys|No deploy keys found, start by adding a new one above.') }}
</div>
</div>
</template>
diff --git a/app/assets/javascripts/nav/components/new_nav_toggle.vue b/app/assets/javascripts/nav/components/new_nav_toggle.vue
index 82302ec4602..4e5d6b0ce6c 100644
--- a/app/assets/javascripts/nav/components/new_nav_toggle.vue
+++ b/app/assets/javascripts/nav/components/new_nav_toggle.vue
@@ -68,10 +68,15 @@ export default {
<gl-disclosure-dropdown-item v-if="newNavigation" @action="toggleNav">
<div class="gl-new-dropdown-item-content">
<div
- class="gl-new-dropdown-item-text-wrapper gl-display-flex! gl-justify-content-space-between gl-align-items-center gl-py-2!"
+ class="gl-new-dropdown-item-text-wrapper gl-display-flex! gl-justify-content-space-between gl-align-items-center gl-py-2! gl-gap-3"
>
{{ $options.i18n.toggleMenuItemLabel }}
- <gl-toggle :value="isEnabled" :label="$options.i18n.toggleLabel" label-position="hidden" />
+ <gl-toggle
+ class="gl-flex-grow-0!"
+ :value="isEnabled"
+ :label="$options.i18n.toggleLabel"
+ label-position="hidden"
+ />
</div>
</div>
</gl-disclosure-dropdown-item>
@@ -84,11 +89,12 @@ export default {
</div>
<div
- class="menu-item gl-cursor-pointer gl-display-flex! gl-justify-content-space-between gl-align-items-center"
+ class="menu-item gl-cursor-pointer gl-display-flex! gl-justify-content-space-between gl-align-items-center gl-gap-3"
@click.prevent.stop="toggleNav"
>
{{ $options.i18n.toggleMenuItemLabel }}
<gl-toggle
+ class="gl-flex-grow-0!"
:value="isEnabled"
:label="$options.i18n.toggleLabel"
label-position="hidden"
diff --git a/app/assets/javascripts/service_desk/components/empty_state_without_any_issues.vue b/app/assets/javascripts/service_desk/components/empty_state_without_any_issues.vue
index 0679d31a8b8..9dbed2c2579 100644
--- a/app/assets/javascripts/service_desk/components/empty_state_without_any_issues.vue
+++ b/app/assets/javascripts/service_desk/components/empty_state_without_any_issues.vue
@@ -1,6 +1,5 @@
<script>
import { GlEmptyState, GlLink } from '@gitlab/ui';
-import { helpPagePath } from '~/helpers/help_page_helper';
import {
noIssuesSignedOutButtonText,
infoBannerTitle,
@@ -17,7 +16,6 @@ export default {
infoBannerAdminNote,
learnMore,
},
- serviceDeskHelpPagePath: helpPagePath('user/project/service_desk/index'),
components: {
GlEmptyState,
GlLink,
@@ -29,6 +27,7 @@ export default {
'canAdminIssues',
'isServiceDeskEnabled',
'serviceDeskEmailAddress',
+ 'serviceDeskHelpPath',
],
computed: {
canSeeEmailAddress() {
@@ -50,7 +49,7 @@ export default {
{{ $options.i18n.infoBannerAdminNote }} <br /><code>{{ serviceDeskEmailAddress }}</code>
</p>
<p>{{ $options.i18n.infoBannerUserNote }}</p>
- <gl-link :href="$options.serviceDeskHelpPagePath" target="_blank">
+ <gl-link :href="serviceDeskHelpPath">
{{ $options.i18n.learnMore }}
</gl-link>
</template>
@@ -67,7 +66,7 @@ export default {
>
<template #description>
<p>{{ $options.i18n.infoBannerUserNote }}</p>
- <gl-link :href="$options.serviceDeskHelpPagePath">
+ <gl-link :href="serviceDeskHelpPath">
{{ $options.i18n.learnMore }}
</gl-link>
</template>
diff --git a/app/assets/javascripts/service_desk/components/info_banner.vue b/app/assets/javascripts/service_desk/components/info_banner.vue
index 8aaced839a5..5667ee2f31d 100644
--- a/app/assets/javascripts/service_desk/components/info_banner.vue
+++ b/app/assets/javascripts/service_desk/components/info_banner.vue
@@ -51,7 +51,7 @@ export default {
</p>
<p>
{{ $options.i18n.infoBannerUserNote }}
- <gl-link :href="serviceDeskHelpPath" target="_blank">{{ $options.i18n.learnMore }}</gl-link
+ <gl-link :href="serviceDeskHelpPath">{{ $options.i18n.learnMore }}</gl-link
>.
</p>
<p v-if="canEnableServiceDesk" class="gl-mt-3">
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 71936f37333..b9cae28537d 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -65,7 +65,6 @@
white-space: normal;
}
- .deploy-project-label,
.key-created-at {
svg {
vertical-align: text-top;
@@ -83,18 +82,6 @@
.deploy-project-list {
margin-bottom: -$gl-padding-4;
-
- a.deploy-project-label {
- margin-right: $gl-padding-4;
- margin-bottom: $gl-padding-4;
- color: $gl-text-color-secondary;
- background-color: $gray-50;
- line-height: $gl-btn-line-height;
-
- &:hover {
- color: $blue-600;
- }
- }
}
.vs-public {
diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb
index 6b998c3d494..329c4e4921a 100644
--- a/app/controllers/admin/abuse_reports_controller.rb
+++ b/app/controllers/admin/abuse_reports_controller.rb
@@ -4,7 +4,7 @@ class Admin::AbuseReportsController < Admin::ApplicationController
feature_category :insider_threat
before_action :set_status_param, only: :index, if: -> { Feature.enabled?(:abuse_reports_list) }
- before_action :find_abuse_report, only: [:show, :update, :destroy]
+ before_action :find_abuse_report, only: [:show, :moderate_user, :update, :destroy]
def index
@abuse_reports = AbuseReportsFinder.new(params).execute
@@ -12,8 +12,22 @@ class Admin::AbuseReportsController < Admin::ApplicationController
def show; end
+ # Kept for backwards compatibility.
+ # TODO: See https://gitlab.com/gitlab-org/modelops/anti-abuse/team-tasks/-/issues/167?work_item_iid=443
+ # In 16.4 remove or re-use this endpoint after frontend has migrated to using moderate_user endpoint
def update
- response = Admin::AbuseReportUpdateService.new(@abuse_report, current_user, permitted_params).execute
+ response = Admin::AbuseReports::ModerateUserService.new(@abuse_report, current_user, permitted_params).execute
+
+ if response.success?
+ render json: { message: response.message }
+ else
+ render json: { message: response.message }, status: :unprocessable_entity
+ end
+ end
+
+ def moderate_user
+ response = Admin::AbuseReports::ModerateUserService.new(@abuse_report, current_user, permitted_params).execute
+
if response.success?
render json: { message: response.message }
else
diff --git a/app/models/concerns/enum_inheritance.rb b/app/models/concerns/enum_inheritance.rb
new file mode 100644
index 00000000000..1df1f3d43fd
--- /dev/null
+++ b/app/models/concerns/enum_inheritance.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module EnumInheritance
+ # == STI through Enum
+ #
+ # WARNING: Usage of STI is heavily discouraged: https://docs.gitlab.com/ee/development/database/single_table_inheritance.html
+ #
+ # Active Record allows definition of STI through the <tt>Base.inheritance_column</tt>. However, this stores the class
+ # name as string into the record, which is heavy and unnecessary. EnumInheritance adapts ActiveRecord to use an enum
+ # instead.
+ #
+ # Details:
+ # - Correct class mapping is specified in the <tt>self.sti_type_map<\tt>, which maps the symbol of the type to
+ # a fully classified class as string.
+ # - If the type passed does not have an specified class, then the class will be the base class
+ #
+ # Example
+ # class Animal
+ # include EnumInheritable
+ #
+ # enum animal_type: {
+ # dog: 1,
+ # cat: 2,
+ # bird: 3
+ # }
+ #
+ # def self.inheritance_column_to_class_map = {
+ # dog: 'Animals::Dog',
+ # cat: 'Animals::Cat'
+ # }
+ #
+ # def self.inheritance_column = 'animal_type'
+ # end
+ #
+ # class Animals::Dog < Animal; end
+ # class Animals::Cat < Animal; end
+ extend ActiveSupport::Concern
+
+ included do
+ def self.sti_class_to_enum_map = inheritance_column_to_class_map.invert
+ end
+
+ class_methods do
+ extend ::Gitlab::Utils::Override
+
+ def inheritance_column_to_class_map = {}.freeze
+
+ override :sti_class_for
+ def sti_class_for(type_name)
+ inheritance_column_to_class_map[type_name.to_sym]&.constantize || base_class
+ end
+
+ override :sti_name
+ def sti_name
+ sti_class_to_enum_map[name].to_s
+ end
+ end
+end
diff --git a/app/serializers/admin/abuse_report_details_entity.rb b/app/serializers/admin/abuse_report_details_entity.rb
index f0e84fc44d2..3efb8508e5e 100644
--- a/app/serializers/admin/abuse_report_details_entity.rb
+++ b/app/serializers/admin/abuse_report_details_entity.rb
@@ -79,9 +79,17 @@ module Admin
expose :reported_content, as: :content
expose :reported_from_url, as: :url
expose :screenshot_path, as: :screenshot
+
+ # Kept for backwards compatibility.
+ # TODO: See https://gitlab.com/gitlab-org/modelops/anti-abuse/team-tasks/-/issues/167?work_item_iid=443
+ # In 16.4 remove or re-use this field after frontend has migrated to using moderate_user_path
expose :update_path do |report|
admin_abuse_report_path(report)
end
+
+ expose :moderate_user_path do |report|
+ moderate_user_admin_abuse_report_path(report)
+ end
end
end
end
diff --git a/app/services/admin/abuse_report_update_service.rb b/app/services/admin/abuse_report_update_service.rb
deleted file mode 100644
index 12cf8bf14a8..00000000000
--- a/app/services/admin/abuse_report_update_service.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-# frozen_string_literal: true
-
-module Admin
- class AbuseReportUpdateService < BaseService
- attr_reader :abuse_report, :params, :current_user, :action
-
- def initialize(abuse_report, current_user, params)
- @abuse_report = abuse_report
- @current_user = current_user
- @params = params
- @action = determine_action
- end
-
- def execute
- return ServiceResponse.error(message: 'Admin is required') unless current_user&.can_admin_all_resources?
- return ServiceResponse.error(message: 'Action is required') unless action.present?
-
- result = perform_action
- if result[:status] == :success
- event = close_report_and_record_event
- ServiceResponse.success(message: event.success_message)
- else
- ServiceResponse.error(message: result[:message])
- end
- end
-
- private
-
- def determine_action
- action = params[:user_action]
- if action.in?(ResourceEvents::AbuseReportEvent.actions.keys)
- action.to_sym
- elsif close_report?
- :close_report
- end
- end
-
- def perform_action
- case action
- when :ban_user then ban_user
- when :block_user then block_user
- when :delete_user then delete_user
- when :close_report then close_report
- end
- end
-
- def ban_user
- Users::BanService.new(current_user).execute(abuse_report.user)
- end
-
- def block_user
- Users::BlockService.new(current_user).execute(abuse_report.user)
- end
-
- def delete_user
- abuse_report.user.delete_async(deleted_by: current_user)
- success
- end
-
- def close_report
- return error('Report already closed') if abuse_report.closed?
-
- abuse_report.closed!
- success
- end
-
- def close_report_and_record_event
- event = action
-
- if close_report? && action != :close_report
- close_report
- event = "#{action}_and_close_report"
- end
-
- record_event(event)
- end
-
- def close_report?
- params[:close].to_s == 'true'
- end
-
- def record_event(action)
- reason = params[:reason]
- unless reason.in?(ResourceEvents::AbuseReportEvent.reasons.keys)
- reason = ResourceEvents::AbuseReportEvent.reasons[:other]
- end
-
- abuse_report.events.create(action: action, user: current_user, reason: reason, comment: params[:comment])
- end
- end
-end
diff --git a/app/services/admin/abuse_reports/moderate_user_service.rb b/app/services/admin/abuse_reports/moderate_user_service.rb
new file mode 100644
index 00000000000..da61a4dc8f6
--- /dev/null
+++ b/app/services/admin/abuse_reports/moderate_user_service.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module Admin
+ module AbuseReports
+ class ModerateUserService < BaseService
+ attr_reader :abuse_report, :params, :current_user, :action
+
+ def initialize(abuse_report, current_user, params)
+ @abuse_report = abuse_report
+ @current_user = current_user
+ @params = params
+ @action = determine_action
+ end
+
+ def execute
+ return ServiceResponse.error(message: 'Admin is required') unless current_user&.can_admin_all_resources?
+ return ServiceResponse.error(message: 'Action is required') unless action.present?
+
+ result = perform_action
+ if result[:status] == :success
+ event = close_report_and_record_event
+ ServiceResponse.success(message: event.success_message)
+ else
+ ServiceResponse.error(message: result[:message])
+ end
+ end
+
+ private
+
+ def determine_action
+ action = params[:user_action]
+ if action.in?(ResourceEvents::AbuseReportEvent.actions.keys)
+ action.to_sym
+ elsif close_report?
+ :close_report
+ end
+ end
+
+ def perform_action
+ case action
+ when :ban_user then ban_user
+ when :block_user then block_user
+ when :delete_user then delete_user
+ when :close_report then close_report
+ end
+ end
+
+ def ban_user
+ Users::BanService.new(current_user).execute(abuse_report.user)
+ end
+
+ def block_user
+ Users::BlockService.new(current_user).execute(abuse_report.user)
+ end
+
+ def delete_user
+ abuse_report.user.delete_async(deleted_by: current_user)
+ success
+ end
+
+ def close_report
+ return error('Report already closed') if abuse_report.closed?
+
+ abuse_report.closed!
+ success
+ end
+
+ def close_report_and_record_event
+ event = action
+
+ if close_report? && action != :close_report
+ close_report
+ event = "#{action}_and_close_report"
+ end
+
+ record_event(event)
+ end
+
+ def close_report?
+ params[:close].to_s == 'true'
+ end
+
+ def record_event(action)
+ reason = params[:reason]
+ unless reason.in?(ResourceEvents::AbuseReportEvent.reasons.keys)
+ reason = ResourceEvents::AbuseReportEvent.reasons[:other]
+ end
+
+ abuse_report.events.create(action: action, user: current_user, reason: reason, comment: params[:comment])
+ end
+ end
+ end
+end
diff --git a/app/views/admin/application_settings/appearances/_form.html.haml b/app/views/admin/application_settings/appearances/_form.html.haml
index fb5c320268e..672af002e5e 100644
--- a/app/views/admin/application_settings/appearances/_form.html.haml
+++ b/app/views/admin/application_settings/appearances/_form.html.haml
@@ -3,146 +3,143 @@
= gitlab_ui_form_for @appearance, url: admin_application_settings_appearances_path, html: { class: 'gl-mt-3' } do |f|
= form_errors(@appearance)
- .row
- .col-lg-4
- %h4.gl-mt-0= _('Navigation bar')
-
- .col-lg-8
- .form-group
- = f.label :header_logo, _('Header logo'), class: 'col-form-label gl-pt-0'
- %p
- - if @appearance.header_logo?
- = image_tag @appearance.header_logo_path, class: 'appearance-light-logo-preview'
- - if @appearance.persisted?
- %br
- = render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, method: :delete, href: header_logos_admin_application_settings_appearances_path, button_options: { data: { confirm: _("Header logo will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove header logo') } }) do
- = _('Remove header logo')
- %hr
- = f.hidden_field :header_logo_cache
- = f.file_field :header_logo, class: "", accept: 'image/*'
- .form-text.text-muted
- = _('Maximum file size is 1MB. Pages are optimized for a 24px tall header logo')
- %hr
- .row
- .col-lg-4
- %h4.gl-mt-0 Favicon
-
- .col-lg-8
- .form-group
- = f.label :favicon, _('Favicon'), class: 'col-form-label gl-pt-0'
- %p
- - if @appearance.favicon?
- = image_tag @appearance.favicon_path, class: 'appearance-light-logo-preview'
- - if @appearance.persisted?
- %br
- = render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, method: :delete, href: favicon_admin_application_settings_appearances_path, button_options: { data: { confirm: _("Favicon will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove favicon') } }) do
- = _('Remove favicon')
- %hr
- = f.hidden_field :favicon_cache
- = f.file_field :favicon, class: '', accept: 'image/*'
- .form-text.text-muted
- = _("Maximum file size is 1 MB. Image size must be 32 x 32 pixels. Allowed image formats are %{favicon_extension_allowlist}.") % { favicon_extension_allowlist: favicon_extension_allowlist }
+ .settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0= _('Navigation bar')
+
+ .form-group
+ = f.label :header_logo, _('Header logo'), class: 'col-form-label gl-pt-0'
+ %p
+ - if @appearance.header_logo?
+ = image_tag @appearance.header_logo_path, class: 'appearance-light-logo-preview'
+ - if @appearance.persisted?
+ %br
+ = render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, method: :delete, href: header_logos_admin_application_settings_appearances_path, button_options: { data: { confirm: _("Header logo will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove header logo') } }) do
+ = _('Remove header logo')
+ %hr
+ = f.hidden_field :header_logo_cache
+ = f.file_field :header_logo, class: "", accept: 'image/*'
+ .form-text.text-muted
+ = _('Maximum file size is 1MB. Pages are optimized for a 24px tall header logo')
+
+ .settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0 Favicon
+
+ .form-group
+ = f.label :favicon, _('Favicon'), class: 'col-form-label gl-pt-0'
+ %p
+ - if @appearance.favicon?
+ = image_tag @appearance.favicon_path, class: 'appearance-light-logo-preview'
+ - if @appearance.persisted?
%br
- = _("Images with incorrect dimensions are not resized automatically, and may result in unexpected behavior.")
+ = render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, method: :delete, href: favicon_admin_application_settings_appearances_path, button_options: { data: { confirm: _("Favicon will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove favicon') } }) do
+ = _('Remove favicon')
+ %hr
+ = f.hidden_field :favicon_cache
+ = f.file_field :favicon, class: '', accept: 'image/*'
+ .form-text.text-muted
+ = _("Maximum file size is 1 MB. Image size must be 32 x 32 pixels. Allowed image formats are %{favicon_extension_allowlist}.") % { favicon_extension_allowlist: favicon_extension_allowlist }
+ %br
+ = _("Images with incorrect dimensions are not resized automatically, and may result in unexpected behavior.")
= render partial: 'admin/application_settings/appearances/system_header_footer_form', locals: { form: f }
- %hr
- .row
- .col-lg-4
- %h4.gl-mt-0= _('Sign in/Sign up pages')
-
- .col-lg-8
- .form-group
- = f.label :title, class: 'col-form-label'
- = f.text_field :title, class: "form-control gl-form-input"
- .form-group
- = f.label :description, class: 'col-form-label'
- = f.text_area :description, class: "form-control gl-form-input", rows: 10
+ .settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0= _('Sign in/Sign up pages')
+
+ .form-group
+ = f.label :title, class: 'col-form-label'
+ = f.text_field :title, class: "form-control gl-form-input"
+ .form-group
+ = f.label :description, class: 'col-form-label'
+ = f.text_area :description, class: "form-control gl-form-input", rows: 10
+ .form-text.text-muted
+ = parsed_with_gfm
+ .form-group
+ = f.label :logo, class: 'col-form-label gl-pt-0'
+ %p
+ - if @appearance.logo?
+ = image_tag @appearance.logo_path, class: 'appearance-logo-preview'
+ - if @appearance.persisted?
+ %br
+ = render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, method: :delete, href: logo_admin_application_settings_appearances_path, button_options: { data: { confirm: _("Logo will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove logo') } }) do
+ = _('Remove logo')
+ %hr
+ = f.hidden_field :logo_cache
+ = f.file_field :logo, class: "", accept: 'image/*'
+ .form-text.text-muted
+ = _('Maximum file size is 1 MB. Pages are optimized for a 128x128 px logo.')
+
+ .settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0= _('Progressive Web App (PWA)')
+
+ .form-group
+ = f.label _("Name"), class: 'col-form-label'
+ = f.text_field :pwa_name, class: "form-control gl-form-input"
+ .form-group
+ = f.label _("Short name"), class: 'col-form-label'
+ = f.text_field :pwa_short_name, class: "form-control gl-form-input"
+ .form-group
+ = f.label _("Description"), class: 'col-form-label'
+ = f.text_area :pwa_description, class: "form-control gl-form-input", rows: 10
+ .form-text.text-muted
+ = parsed_with_gfm
+ .form-group
+ = f.label :pwa_icon, class: 'col-form-label gl-pt-0'
+ %p
+ - if @appearance.pwa_icon?
+ = image_tag @appearance.pwa_icon_path, class: 'appearance-pwa-icon-preview'
+ - if @appearance.persisted?
+ %br
+ = render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, method: :delete, href: pwa_icon_admin_application_settings_appearances_path, button_options: { data: { confirm: _("Icon will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove icon') } }) do
+ = _('Remove icon')
+ %hr
+ = f.hidden_field :pwa_icon_cache
+ = f.file_field :pwa_icon, class: "", accept: 'image/*'
+ .form-text.text-muted
+ = _('Maximum file size is 1MB.')
+
+ .settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0= _('New project pages')
+
+ .form-group
+ = f.label :new_project_guidelines, class: 'col-form-label'
+ %p
+ = f.text_area :new_project_guidelines, class: "form-control gl-form-input", rows: 10
.form-text.text-muted
= parsed_with_gfm
- .form-group
- = f.label :logo, class: 'col-form-label gl-pt-0'
- %p
- - if @appearance.logo?
- = image_tag @appearance.logo_path, class: 'appearance-logo-preview'
- - if @appearance.persisted?
- %br
- = render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, method: :delete, href: logo_admin_application_settings_appearances_path, button_options: { data: { confirm: _("Logo will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove logo') } }) do
- = _('Remove logo')
- %hr
- = f.hidden_field :logo_cache
- = f.file_field :logo, class: "", accept: 'image/*'
- .form-text.text-muted
- = _('Maximum file size is 1 MB. Pages are optimized for a 128x128 px logo.')
-
- %hr
- .row
- .col-lg-4
- %h4.gl-mt-0= _('Progressive Web App (PWA)')
-
- .col-lg-8
- .form-group
- = f.label _("Name"), class: 'col-form-label'
- = f.text_field :pwa_name, class: "form-control gl-form-input"
- .form-group
- = f.label _("Short name"), class: 'col-form-label'
- = f.text_field :pwa_short_name, class: "form-control gl-form-input"
- .form-group
- = f.label _("Description"), class: 'col-form-label'
- = f.text_area :pwa_description, class: "form-control gl-form-input", rows: 10
+
+ .settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0= _('Profile image guideline')
+
+ .form-group
+ = f.label :profile_image_guidelines, class: 'col-form-label'
+ %p
+ = f.text_area :profile_image_guidelines, class: "form-control gl-form-input", rows: 10
.form-text.text-muted
= parsed_with_gfm
- .form-group
- = f.label :pwa_icon, class: 'col-form-label gl-pt-0'
- %p
- - if @appearance.pwa_icon?
- = image_tag @appearance.pwa_icon_path, class: 'appearance-pwa-icon-preview'
- - if @appearance.persisted?
- %br
- = render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, method: :delete, href: pwa_icon_admin_application_settings_appearances_path, button_options: { data: { confirm: _("Icon will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove icon') } }) do
- = _('Remove icon')
- %hr
- = f.hidden_field :pwa_icon_cache
- = f.file_field :pwa_icon, class: "", accept: 'image/*'
- .form-text.text-muted
- = _('Maximum file size is 1MB.')
-
- %hr
- .row
- .col-lg-4
- %h4.gl-mt-0= _('New project pages')
-
- .col-lg-8
- .form-group
- = f.label :new_project_guidelines, class: 'col-form-label'
- %p
- = f.text_area :new_project_guidelines, class: "form-control gl-form-input", rows: 10
- .form-text.text-muted
- = parsed_with_gfm
-
- %hr
- .row
- .col-lg-4
- %h4.gl-mt-0= _('Profile image guideline')
-
- .col-lg-8
- .form-group
- = f.label :profile_image_guidelines, class: 'col-form-label'
- %p
- = f.text_area :profile_image_guidelines, class: "form-control gl-form-input", rows: 10
- .form-text.text-muted
- = parsed_with_gfm
-
- .gl-mt-3.gl-mb-3
- = f.submit _('Update appearance settings'), pajamas_button: true
- - if @appearance.persisted? || @appearance.updated_at
- .mt-4
- - if @appearance.persisted?
- Preview last save:
- = link_to _('Sign-in page'), preview_sign_in_admin_application_settings_appearances_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
- = link_to _('New project page'), new_project_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
-
- - if @appearance.updated_at
- %span.float-right
- Last edit #{time_ago_with_tooltip(@appearance.updated_at)}
+
+ - if @appearance.persisted? || @appearance.updated_at
+ .settings-section
+ - if @appearance.persisted?
+ Preview last save:
+ = link_to _('Sign-in page'), preview_sign_in_admin_application_settings_appearances_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('New project page'), new_project_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
+
+ - if @appearance.updated_at
+ %span.float-right
+ Last edit #{time_ago_with_tooltip(@appearance.updated_at)}
+
+ .settings-sticky-footer
+ = f.submit _('Update appearance settings'), pajamas_button: true
diff --git a/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml b/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
index 2ca037db532..61df5f5fd0d 100644
--- a/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
+++ b/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
@@ -1,30 +1,29 @@
- form = local_assigns.fetch(:form)
-%hr
-.row
- .col-lg-4
- %h4.gl-mt-0
- = _('System header and footer')
+.settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0
+ = _('System header and footer')
- .col-lg-8
- .form-group
- = form.label :header_message, _('Header message'), class: 'col-form-label label-bold'
- = form.text_area :header_message, placeholder: _('State your message to activate'), class: "form-control gl-form-input js-autosize"
- .form-group
- = form.label :footer_message, _('Footer message'), class: 'col-form-label label-bold'
- = form.text_area :footer_message, placeholder: _('State your message to activate'), class: "form-control gl-form-input js-autosize"
- .form-group
- = form.gitlab_ui_checkbox_component :email_header_and_footer_enabled,
- _('Enable header and footer in emails'),
- help_text: _('Add header and footer to emails. Please note that color settings will only be applied within the application interface'),
- label_options: { class: 'gl-font-weight-bold!' }
+ .form-group
+ = form.label :header_message, _('Header message'), class: 'col-form-label label-bold'
+ = form.text_area :header_message, placeholder: _('State your message to activate'), class: "form-control gl-form-input js-autosize"
+ .form-group
+ = form.label :footer_message, _('Footer message'), class: 'col-form-label label-bold'
+ = form.text_area :footer_message, placeholder: _('State your message to activate'), class: "form-control gl-form-input js-autosize"
+ .form-group
+ = form.gitlab_ui_checkbox_component :email_header_and_footer_enabled,
+ _('Enable header and footer in emails'),
+ help_text: _('Add header and footer to emails. Please note that color settings will only be applied within the application interface'),
+ label_options: { class: 'gl-font-weight-bold!' }
- .form-group.js-toggle-colors-container
- = render Pajamas::ButtonComponent.new(variant: :link, button_options: { class: 'js-toggle-colors-link' }) do
- = _('Customize colors')
- .form-group.js-toggle-colors-container.hide
- = form.label :message_background_color, _('Background Color'), class: 'col-form-label label-bold'
- = form.color_field :message_background_color, class: "form-control gl-form-input"
- .form-group.js-toggle-colors-container.hide
- = form.label :message_font_color, _('Font Color'), class: 'col-form-label label-bold'
- = form.color_field :message_font_color, class: "form-control gl-form-input"
+ .form-group.js-toggle-colors-container
+ = render Pajamas::ButtonComponent.new(variant: :link, button_options: { class: 'js-toggle-colors-link' }) do
+ = _('Customize colors')
+ .form-group.js-toggle-colors-container.hide
+ = form.label :message_background_color, _('Background Color'), class: 'col-form-label label-bold'
+ = form.color_field :message_background_color, class: "form-control gl-form-input"
+ .form-group.js-toggle-colors-container.hide
+ = form.label :message_font_color, _('Font Color'), class: 'col-form-label label-bold'
+ = form.color_field :message_font_color, class: "form-control gl-form-input"
diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml
index a03d6cb5a94..3d73b255a5e 100644
--- a/app/views/admin/deploy_keys/new.html.haml
+++ b/app/views/admin/deploy_keys/new.html.haml
@@ -1,11 +1,9 @@
- page_title _('New Deploy Key')
%h1.page-title.gl-font-size-h-display= _('New public deploy key')
-%hr
-%div
- = gitlab_ui_form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f|
- = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
- .form-actions
- = f.submit 'Create', data: { qa_selector: "add_deploy_key_button" }, pajamas_button: true
- = render Pajamas::ButtonComponent.new(href: admin_deploy_keys_path) do
- = _('Cancel')
+= gitlab_ui_form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f|
+ = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
+ .gl-display-flex.gl-mt-6.gl-gap-3
+ = f.submit 'Create', data: { qa_selector: "add_deploy_key_button" }, pajamas_button: true
+ = render Pajamas::ButtonComponent.new(href: admin_deploy_keys_path) do
+ = _('Cancel')
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index e7ea139eb71..397ba7ae700 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -40,7 +40,7 @@
%p.gl-text-secondary
= s_('Preferences|Customize the appearance of the syntax.')
= succeed '.' do
- = link_to _('Learn more'), help_page_path('user/profile/preferences', anchor: 'syntax-highlighting-theme'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more'), help_page_path('user/profile/preferences', anchor: 'change-the-syntax-highlighting-theme'), target: '_blank', rel: 'noopener noreferrer'
.syntax-theme.row
- Gitlab::ColorSchemes.each do |scheme|
%label.col-6.col-sm-4.col-md-3.col-lg-auto.gl-mb-5
diff --git a/app/views/projects/deploy_keys/edit.html.haml b/app/views/projects/deploy_keys/edit.html.haml
index 997443d5fa9..0044ff4dc24 100644
--- a/app/views/projects/deploy_keys/edit.html.haml
+++ b/app/views/projects/deploy_keys/edit.html.haml
@@ -1,10 +1,8 @@
- page_title _('Edit Deploy Key')
%h1.page-title.gl-font-size-h-display= _('Edit Deploy Key')
-%hr
-%div
- = gitlab_ui_form_for [@project, @deploy_key], include_id: false, html: { class: 'js-requires-input' } do |f|
- = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
- .form-actions
- = f.submit _('Save changes'), pajamas_button: true
- = link_button_to _('Cancel'), project_settings_repository_path(@project)
+= gitlab_ui_form_for [@project, @deploy_key], include_id: false, html: { class: 'js-requires-input' } do |f|
+ = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
+ .gl-display-flex.gl-mt-6.gl-gap-3
+ = f.submit _('Save changes'), pajamas_button: true
+ = link_button_to _('Cancel'), project_settings_repository_path(@project, anchor: 'js-deploy-keys-settings')
diff --git a/app/views/projects/issues/service_desk.html.haml b/app/views/projects/issues/service_desk.html.haml
index 9793f21e4a9..2d17719a8c2 100644
--- a/app/views/projects/issues/service_desk.html.haml
+++ b/app/views/projects/issues/service_desk.html.haml
@@ -15,7 +15,7 @@
can_edit_project_settings: can?(current_user, :admin_project, @project).to_s,
service_desk_callout_svg_path: image_path('service_desk_callout.svg'),
service_desk_settings_path: edit_project_path(@project, anchor: 'js-service-desk'),
- service_desk_help_path: help_page_path('user/project/service_desk'),
+ service_desk_help_path: help_page_path('user/project/service_desk/index'),
is_service_desk_supported: Gitlab::ServiceDesk.supported?.to_s,
is_service_desk_enabled: @project.service_desk_enabled?.to_s } }
- else
diff --git a/app/views/shared/deploy_keys/_form.html.haml b/app/views/shared/deploy_keys/_form.html.haml
index 280362a12a9..bbaf5bf9627 100644
--- a/app/views/shared/deploy_keys/_form.html.haml
+++ b/app/views/shared/deploy_keys/_form.html.haml
@@ -5,37 +5,34 @@
= form_errors(deploy_key)
.form-group
- = form.label :title, class: 'col-form-label col-sm-2'
- .col-sm-10= form.text_field :title, class: 'form-control gl-form-input', data: { testid: 'deploy-key-title-field' }, readonly: ('readonly' unless can?(current_user, :update_deploy_key, deploy_key))
+ = form.label :title
+ = form.text_field :title, class: 'form-control gl-form-input', data: { testid: 'deploy-key-title-field' }, readonly: ('readonly' unless can?(current_user, :update_deploy_key, deploy_key))
-.form-group
- - if deploy_key.new_record?
- = form.label :key, class: 'col-form-label col-sm-2'
- .col-sm-10
- %p.light
- - link_start = "<a href='#{help_page_path('user/ssh')}' target='_blank' rel='noreferrer noopener'>".html_safe
- - link_end = '</a>'
- = _('Paste a public key here. %{link_start}How do I generate it?%{link_end}').html_safe % { link_start: link_start, link_end: link_end.html_safe }
- = form.text_area :key, class: 'form-control gl-form-input thin_area', rows: 5, data: { testid: 'deploy-key-field' }
- - else
- - if deploy_key.fingerprint_sha256.present?
- = form.label :fingerprint, _('Fingerprint (SHA256)'), class: 'col-form-label col-sm-2'
- .col-sm-10
- = form.text_field :fingerprint_sha256, class: 'form-control gl-form-input', readonly: 'readonly'
- - if deploy_key.fingerprint.present?
- = form.label :fingerprint, _('Fingerprint (MD5)'), class: 'col-form-label col-sm-2'
- .col-sm-10
- = form.text_field :fingerprint, class: 'form-control gl-form-input', readonly: 'readonly'
+- if deploy_key.new_record?
+ .form-group
+ = form.label :key
+
+ %p.gl-text-secondary
+ - link_start = "<a href='#{help_page_path('user/ssh')}' target='_blank' rel='noreferrer noopener'>".html_safe
+ - link_end = '</a>'
+ = _('Paste a public key here. %{link_start}How do I generate it?%{link_end}').html_safe % { link_start: link_start, link_end: link_end.html_safe }
+ = form.text_area :key, class: 'form-control gl-form-input thin_area', rows: 5, data: { testid: 'deploy-key-field' }
+- else
+ - if deploy_key.fingerprint_sha256.present?
+ .form-group
+ = form.label :fingerprint, _('Fingerprint (SHA256)')
+ = form.text_field :fingerprint_sha256, class: 'form-control gl-form-input', readonly: 'readonly'
+ - if deploy_key.fingerprint.present?
+ .form-group
+ = form.label :fingerprint, _('Fingerprint (MD5)')
+ = form.text_field :fingerprint, class: 'form-control gl-form-input', readonly: 'readonly'
.form-group
- .col-sm-10
- = form.label :expires_at, _('Expiration date (optional)'), class: 'label-bold'
- = form.text_field :expires_at, class: 'form-control gl-form-input', readonly: 'readonly'
+ = form.label :expires_at, _('Expiration date (optional)'), class: 'label-bold'
+ = form.text_field :expires_at, class: 'form-control gl-form-input', readonly: 'readonly'
- if deploy_keys_project.present?
= form.fields_for :deploy_keys_projects, deploy_keys_project do |deploy_keys_project_form|
.form-group
- .col-form-label.col-sm-2
- .col-sm-10
- = deploy_keys_project_form.gitlab_ui_checkbox_component :can_push, _('Grant write permissions to this key'),
- help_text: _('Allow this key to push to this repository')
+ = deploy_keys_project_form.gitlab_ui_checkbox_component :can_push, _('Grant write permissions to this key'),
+ help_text: _('Allow this key to push to this repository')
diff --git a/app/views/shared/deploy_keys/_index.html.haml b/app/views/shared/deploy_keys/_index.html.haml
index 32720d0353b..3f81cbbb9b1 100644
--- a/app/views/shared/deploy_keys/_index.html.haml
+++ b/app/views/shared/deploy_keys/_index.html.haml
@@ -4,11 +4,13 @@
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Deploy keys')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
- %p
+ %p.gl-text-secondary.gl-mb-0
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/deploy_keys/index') }
= _("Add deploy keys to grant read/write access to this repository. %{link_start}What are deploy keys?%{link_end}").html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
.settings-content
- %h5.gl-mt-0
- = render @deploy_keys.form_partial_path
- %hr
- #js-deploy-keys{ data: { endpoint: project_deploy_keys_path(@project), project_id: @project.id } }
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
+ - c.with_body do
+ .gl-new-card-add-form.gl-m-3.gl-display-none.js-toggle-content
+ = render @deploy_keys.form_partial_path
+
+ #js-deploy-keys{ data: { endpoint: project_deploy_keys_path(@project), project_id: @project.id } }
diff --git a/app/views/shared/deploy_keys/_project_group_form.html.haml b/app/views/shared/deploy_keys/_project_group_form.html.haml
index 2f90345ed12..c633088b26a 100644
--- a/app/views/shared/deploy_keys/_project_group_form.html.haml
+++ b/app/views/shared/deploy_keys/_project_group_form.html.haml
@@ -1,5 +1,9 @@
= gitlab_ui_form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f|
= form_errors(@deploy_keys.new_key)
+
+ .form-group.row
+ %h4.gl-my-0= s_('DeployKeys|Add new deploy key')
+
.form-group.row
= f.label :title, class: "label-bold"
= f.text_field :title, class: 'form-control gl-form-input', required: true, data: { testid: 'deploy-key-title-field' }
@@ -20,5 +24,7 @@
= f.gitlab_ui_datepicker :expires_at, data: { testid: 'deploy-key-expires-at-field' }, value: f.object.expires_at
%p.form-text.text-muted= ssh_key_expires_field_description
- .form-group.row
+ .form-group.row.gl-mb-0
= f.submit _("Add key"), data: { testid: "add-deploy-key-button"}, pajamas_button: true
+ = render Pajamas::ButtonComponent.new(button_options: { type: 'reset', class: 'gl-ml-3 js-toggle-button' }) do
+ = _('Cancel')