Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-05-22 12:17:43 +00:00
parent 817de15424
commit a075011d5b
111 changed files with 1324 additions and 970 deletions

View File

@ -103,19 +103,6 @@ Layout/ArgumentAlignment:
- 'ee/app/services/external_status_checks/create_service.rb'
- 'ee/app/services/external_status_checks/destroy_service.rb'
- 'ee/app/services/external_status_checks/update_service.rb'
- 'ee/db/geo/post_migrate/20210217020154_add_unique_index_on_container_repository_registry.rb'
- 'ee/db/geo/post_migrate/20210217020156_add_unique_index_on_terraform_state_version_registry.rb'
- 'ee/lib/ee/gitlab/import_sources.rb'
- 'ee/lib/ee/gitlab/middleware/read_only/controller.rb'
- 'ee/lib/ee/gitlab/scim/group/deprovisioning_service.rb'
- 'ee/lib/ee/gitlab/usage_data.rb'
- 'ee/lib/elastic/latest/commit_config.rb'
- 'ee/lib/gitlab/analytics/cycle_analytics/summary/group/stage_summary.rb'
- 'ee/lib/gitlab/auth/smartcard/base.rb'
- 'ee/lib/gitlab/elastic/indexer.rb'
- 'ee/lib/gitlab/geo/git_ssh_proxy.rb'
- 'ee/lib/gitlab/geo/replicator.rb'
- 'ee/lib/gitlab/graphql/aggregations/epics/epic_node.rb'
- 'ee/spec/lib/ee/gitlab/scim/params_parser_spec.rb'
- 'ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb'
- 'ee/spec/lib/ee/gitlab/usage/service_ping_report_spec.rb'

View File

@ -64,19 +64,6 @@ Layout/FirstHashElementIndentation:
- 'ee/spec/graphql/types/vulnerability_response_type_spec.rb'
- 'ee/spec/helpers/billing_plans_helper_spec.rb'
- 'ee/spec/helpers/groups/security_features_helper_spec.rb'
- 'ee/spec/helpers/projects/security/discover_helper_spec.rb'
- 'ee/spec/initializers/fog_google_https_private_urls_spec.rb'
- 'ee/spec/lib/audit/compliance_framework_changes_auditor_spec.rb'
- 'ee/spec/lib/audit/external_status_check_changes_auditor_spec.rb'
- 'ee/spec/lib/audit/project_changes_auditor_spec.rb'
- 'ee/spec/lib/audit/project_ci_cd_setting_changes_auditor_spec.rb'
- 'ee/spec/lib/audit/project_setting_changes_auditor_spec.rb'
- 'ee/spec/lib/ee/api/entities/experiment_spec.rb'
- 'ee/spec/lib/ee/gitlab/auth/ldap/access_levels_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/backfill_project_statistics_storage_size_without_uploads_size_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/parsers/security/common_spec.rb'
- 'ee/spec/lib/gitlab/auth/smartcard/san_extension_spec.rb'
- 'ee/spec/lib/gitlab/ci/parsers/security/dast_spec.rb'
- 'ee/spec/lib/gitlab/ci/reports/coverage_fuzzing/report_spec.rb'
- 'ee/spec/lib/gitlab/geo/log_cursor/lease_spec.rb'
- 'ee/spec/lib/gitlab/graphql/aggregations/epics/lazy_epic_aggregate_spec.rb'

View File

@ -136,7 +136,7 @@ $(document).on('markdown-preview:show', (e, $form) => {
if (!$previewButton.parents('.js-vue-markdown-field').length) {
$previewButton.val('edit');
$previewButton.children('span.gl-button-text').text(__('Continue editing'));
$previewButton.addClass('gl-shadow-none! gl-bg-transparent!');
$previewButton.addClass('!gl-shadow-none gl-bg-transparent!');
}
// toggle content
@ -196,7 +196,7 @@ $(document).on('click', previewButtonSelector, function (e) {
$(document).on('mousedown', previewButtonSelector, function (e) {
e.preventDefault();
const $form = $(this).closest('form');
$form.find(previewButtonSelector).removeClass('gl-shadow-none! gl-bg-transparent!');
$form.find(previewButtonSelector).removeClass('!gl-shadow-none gl-bg-transparent!');
});
$(document).on('mouseenter', previewButtonSelector, function (e) {

View File

@ -1,12 +1,5 @@
<script>
import {
GlLabel,
GlTooltip,
GlTooltipDirective,
GlIcon,
GlLoadingIcon,
GlSprintf,
} from '@gitlab/ui';
import { GlLabel, GlTooltipDirective, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { sortBy } from 'lodash';
import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner';
import { isScopedLabel, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
@ -24,7 +17,6 @@ import IssueTimeEstimate from './issue_time_estimate.vue';
export default {
components: {
GlTooltip,
GlLabel,
GlLoadingIcon,
GlIcon,
@ -34,11 +26,12 @@ export default {
IssueCardWeight: () => import('ee_component/boards/components/issue_card_weight.vue'),
IssueIteration: () => import('ee_component/boards/components/issue_iteration.vue'),
IssuableBlockedIcon,
GlSprintf,
WorkItemTypeIcon,
IssueMilestone,
IssueHealthStatus: () =>
import('ee_component/related_items_tree/components/issue_health_status.vue'),
EpicCountables: () =>
import('ee_else_ce/vue_shared/components/epic_countables/epic_countables.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
@ -142,9 +135,6 @@ export default {
shouldRenderEpicCountables() {
return this.isEpicBoard && this.hasChildren;
},
shouldRenderEpicProgress() {
return this.totalWeight > 0;
},
showLabelFooter() {
return this.isShowingLabels && this.item.labels.find(this.showLabel);
},
@ -164,19 +154,17 @@ export default {
}
return __('Blocked issue');
},
descendantCounts() {
return this.item.descendantCounts;
},
descendantWeightSum() {
return this.item.descendantWeightSum;
},
totalEpicsCount() {
return this.item.descendantCounts.openedEpics + this.item.descendantCounts.closedEpics;
return this.descendantCounts.openedEpics + this.descendantCounts.closedEpics;
},
totalIssuesCount() {
return this.item.descendantCounts.openedIssues + this.item.descendantCounts.closedIssues;
},
totalWeight() {
return (
this.item.descendantWeightSum.openedIssues + this.item.descendantWeightSum.closedIssues
);
},
totalProgress() {
return Math.round((this.item.descendantWeightSum.closedIssues / this.totalWeight) * 100);
return this.descendantCounts.openedIssues + this.descendantCounts.closedIssues;
},
showReferencePath() {
return this.isGroupBoard && this.itemReferencePath;
@ -321,83 +309,16 @@ export default {
</span>
{{ itemId }}
</span>
<span v-if="shouldRenderEpicCountables" data-testid="epic-countables">
<gl-tooltip :target="() => $refs.countBadge">
<p v-if="allowSubEpics" class="gl-font-weight-bold gl-m-0">
{{ __('Epics') }} &#8226;
<span class="gl-font-weight-normal">
<gl-sprintf :message="__('%{openedEpics} open, %{closedEpics} closed')">
<template #openedEpics>{{ item.descendantCounts.openedEpics }}</template>
<template #closedEpics>{{ item.descendantCounts.closedEpics }}</template>
</gl-sprintf>
</span>
</p>
<p class="gl-font-weight-bold gl-m-0">
{{ __('Issues') }} &#8226;
<span class="gl-font-weight-normal">
<gl-sprintf :message="__('%{openedIssues} open, %{closedIssues} closed')">
<template #openedIssues>{{ item.descendantCounts.openedIssues }}</template>
<template #closedIssues>{{ item.descendantCounts.closedIssues }}</template>
</gl-sprintf>
</span>
</p>
<p class="gl-font-weight-bold gl-m-0">
{{ __('Total weight') }} &#8226;
<span class="gl-font-weight-normal" data-testid="epic-countables-total-weight">
{{ totalWeight }}
</span>
</p>
</gl-tooltip>
<gl-tooltip
v-if="shouldRenderEpicProgress"
:target="() => $refs.progressBadge"
data-testid="epic-progress-tooltip"
>
<p class="gl-font-weight-bold gl-m-0">
{{ __('Progress') }} &#8226;
<span class="gl-font-weight-normal" data-testid="epic-progress-tooltip-content">
<gl-sprintf
:message="__('%{completedWeight} of %{totalWeight} weight completed')"
>
<template #completedWeight>{{
item.descendantWeightSum.closedIssues
}}</template>
<template #totalWeight>{{ totalWeight }}</template>
</gl-sprintf>
</span>
</p>
</gl-tooltip>
<span
ref="countBadge"
class="board-card-info gl-mr-0 gl-pr-0 gl-pl-3 gl-text-secondary gl-cursor-help"
>
<span v-if="allowSubEpics" class="gl-mr-3">
<gl-icon name="epic" />
{{ totalEpicsCount }}
</span>
<span class="gl-mr-3" data-testid="epic-countables-counts-issues">
<gl-icon name="issues" />
{{ totalIssuesCount }}
</span>
<span class="gl-mr-3" data-testid="epic-countables-weight-issues">
<gl-icon name="weight" />
{{ totalWeight }}
</span>
</span>
<span
v-if="shouldRenderEpicProgress"
ref="progressBadge"
class="board-card-info gl-pl-0 gl-text-secondary gl-cursor-help"
>
<span class="gl-mr-3" data-testid="epic-progress">
<gl-icon name="progress" />
{{ totalProgress }}%
</span>
</span>
</span>
<epic-countables
v-if="shouldRenderEpicCountables"
:allow-sub-epics="allowSubEpics"
:opened-epics-count="descendantCounts.openedEpics"
:closed-epics-count="descendantCounts.closedEpics"
:opened-issues-count="descendantCounts.openedIssues"
:closed-issues-count="descendantCounts.closedIssues"
:opened-issues-weight="descendantWeightSum.openedIssues"
:closed-issues-weight="descendantWeightSum.closedIssues"
/>
<span v-if="!isEpicBoard">
<issue-card-weight
v-if="validIssueWeight(item)"

View File

@ -90,7 +90,7 @@ export default {
: ['gl-border-l-0!', ...this.$options.styles.flatLeftBorder];
},
buttonShadowClass() {
return this.isExpandBtnFocus ? '' : 'gl-shadow-none!';
return this.isExpandBtnFocus ? '' : '!gl-shadow-none';
},
buttonId() {
return `js-linked-pipeline-${this.pipeline.id}`;

View File

@ -75,7 +75,7 @@ export default {
this.editor
.chain()
.focus()
.updateAttributes(this.node.type, {
.updateAttributes(this.node.type.name, {
width: this.$refs.image.width,
height: this.$refs.image.height,
})
@ -103,6 +103,8 @@ export default {
></span>
<img
ref="image"
draggable="true"
data-drag-handle
:src="node.attrs.src"
:alt="node.attrs.alt"
:title="node.attrs.title"

View File

@ -11,28 +11,10 @@ export default {
GlLink,
},
props: {
getPos: {
type: Function,
required: true,
},
editor: {
type: Object,
required: true,
},
node: {
type: Object,
required: true,
},
selected: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
dragData: {},
};
},
computed: {
isStaleUploadedMedia() {
@ -53,9 +35,17 @@ export default {
:src="node.attrs.src"
controls="true"
data-setup="{}"
draggable="true"
data-drag-handle=""
:data-title="node.attrs.title || node.attrs.alt"
/>
<gl-link :href="node.attrs.src" class="with-attachment-icon" target="_blank">
<gl-link
:href="node.attrs.src"
class="with-attachment-icon"
target="_blank"
draggable="true"
data-drag-handle=""
>
{{ node.attrs.title || node.attrs.alt }}
</gl-link>
</node-view-wrapper>

View File

@ -7,6 +7,8 @@ const resolveImageEl = (element) =>
element.nodeName === 'IMG' ? element : element.querySelector('img');
export default Image.extend({
draggable: true,
addOptions() {
return {
...this.parent?.(),

View File

@ -1,6 +1,8 @@
import ListItem from '@tiptap/extension-list-item';
export default ListItem.extend({
draggable: true,
addOptions() {
return {
...this.parent?.(),

View File

@ -2,6 +2,8 @@ import { TaskItem } from '@tiptap/extension-task-item';
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
export default TaskItem.extend({
draggable: true,
addOptions() {
return {
...this.parent?.(),

View File

@ -324,7 +324,7 @@ export default {
<gl-dropdown
:text="__('Recent searches')"
class="filtered-search-history-dropdown-wrapper"
toggle-class="filtered-search-history-dropdown-toggle-button gl-shadow-none! gl-border-r-gray-200! gl-border-1! gl-rounded-0!"
toggle-class="filtered-search-history-dropdown-toggle-button !gl-shadow-none gl-border-r-gray-200! gl-border-1! gl-rounded-0!"
:disabled="loading"
>
<div v-if="!$options.hasLocalStorage" class="gl-px-5">

View File

@ -90,7 +90,7 @@ export default {
:class="{
'disabled-content': !filesLength,
}"
class="gl-shadow-none!"
class="!gl-shadow-none"
category="tertiary"
icon="remove-all"
data-placement="bottom"

View File

@ -307,7 +307,7 @@ export default {
</ul>
</gl-collapse>
<gl-button
class="gl-text-decoration-none! gl-shadow-none! gl-mt-3"
class="gl-text-decoration-none! !gl-shadow-none gl-mt-3"
data-testid="accordion-button"
variant="link"
@click="toggleErrorExpansion"

View File

@ -438,7 +438,7 @@ export default {
</ul>
</gl-collapse>
<gl-button
class="gl-text-decoration-none! gl-shadow-none! gl-mt-3"
class="gl-text-decoration-none! !gl-shadow-none gl-mt-3"
data-testid="accordion-button"
variant="link"
@click="toggleErrorExpansion"

View File

@ -3,7 +3,8 @@ import { GlTabs, GlTab, GlBadge, GlButton } from '@gitlab/ui';
// eslint-disable-next-line no-restricted-imports
import { mapState } from 'vuex';
import { queryToObject } from '~/lib/utils/url_utility';
import { MEMBER_TYPES, TABS, ACTIVE_TAB_QUERY_PARAM_NAME } from 'ee_else_ce/members/constants';
import { MEMBER_TYPES, ACTIVE_TAB_QUERY_PARAM_NAME } from 'ee_else_ce/members/constants';
import { TABS } from 'ee_else_ce/members/tabs_metadata';
import MembersApp from './app.vue';
const countComputed = (state, namespace) => state[namespace]?.pagination?.totalItems || 0;

View File

@ -3,8 +3,6 @@ import { GlFilteredSearchToken } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import PlaceholdersTabApp from './components/placeholders/app.vue';
// Overridden in EE
export const EE_GROUPS_APP_OPTIONS = {};
export const EE_PROJECTS_APP_OPTIONS = {};
@ -184,38 +182,6 @@ export const TAB_QUERY_PARAM_VALUES = Object.freeze({
placeholder: 'placeholders',
});
// Overridden in EE
export const TABS = [
{
namespace: MEMBER_TYPES.user,
title: __('Members'),
},
{
namespace: MEMBER_TYPES.group,
title: __('Groups'),
attrs: { 'data-testid': 'groups-list-tab' },
queryParamValue: TAB_QUERY_PARAM_VALUES.group,
},
{
namespace: MEMBER_TYPES.invite,
title: s__('Members|Pending invitations'),
requiredPermissions: ['canManageMembers'],
queryParamValue: TAB_QUERY_PARAM_VALUES.invite,
},
{
namespace: MEMBER_TYPES.accessRequest,
title: __('Access requests'),
requiredPermissions: ['canManageAccessRequests'],
queryParamValue: TAB_QUERY_PARAM_VALUES.accessRequest,
},
{
namespace: MEMBER_TYPES.placeholder,
title: s__('UserMapping|Placeholders'),
queryParamValue: TAB_QUERY_PARAM_VALUES.placeholder,
component: PlaceholdersTabApp,
},
];
/**
* This user state value comes from the User model
* see the state machine in app/models/user.rb

View File

@ -5,7 +5,7 @@ import Vuex from 'vuex';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { parseDataAttributes } from '~/members/utils';
import { TABS } from 'ee_else_ce/members/constants';
import { TABS } from 'ee_else_ce/members/tabs_metadata';
import MembersTabs from './components/members_tabs.vue';
import membersStore from './store';

View File

@ -0,0 +1,35 @@
import { __, s__ } from '~/locale';
import PlaceholdersTabApp from './components/placeholders/app.vue';
import { MEMBER_TYPES, TAB_QUERY_PARAM_VALUES } from './constants';
// Overridden in EE
export const TABS = [
{
namespace: MEMBER_TYPES.user,
title: __('Members'),
},
{
namespace: MEMBER_TYPES.group,
title: __('Groups'),
attrs: { 'data-testid': 'groups-list-tab' },
queryParamValue: TAB_QUERY_PARAM_VALUES.group,
},
{
namespace: MEMBER_TYPES.invite,
title: s__('Members|Pending invitations'),
requiredPermissions: ['canManageMembers'],
queryParamValue: TAB_QUERY_PARAM_VALUES.invite,
},
{
namespace: MEMBER_TYPES.accessRequest,
title: __('Access requests'),
requiredPermissions: ['canManageAccessRequests'],
queryParamValue: TAB_QUERY_PARAM_VALUES.accessRequest,
},
{
namespace: MEMBER_TYPES.placeholder,
title: s__('UserMapping|Placeholders'),
queryParamValue: TAB_QUERY_PARAM_VALUES.placeholder,
component: PlaceholdersTabApp,
},
];

View File

@ -0,0 +1,33 @@
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { n__ } from '~/locale';
export default {
components: {
GlBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
mergeRequest: {
type: Object,
required: true,
},
},
computed: {
hasApprovers() {
return this.mergeRequest.approvedBy.nodes.length;
},
tooltipTitle() {
return n__('%d approver', '%d approvers', this.mergeRequest.approvedBy.nodes.length);
},
},
};
</script>
<template>
<gl-badge v-if="hasApprovers" v-gl-tooltip="tooltipTitle" icon="approval" variant="success">
{{ __('Approved') }}
</gl-badge>
</template>

View File

@ -1,5 +1,99 @@
import MergeRequest from './merge_request.vue';
const mockMergeRequest = {
reference: '!123456',
titleHtml: 'Merge request title',
webUrl: '#',
author: {
name: 'John Smith',
webUrl: 'https://gitlab.com/root',
},
milestone: {
title: '17.0',
},
labels: {
nodes: [
{
id: 'gid://gitlab/GroupLabel/992791',
color: '#428BCA',
title: 'Deliverable',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/3103452',
color: '#E44D2A',
title: 'devops::create',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/2975007',
color: '#F0AD4E',
title: 'feature::enhancement',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/3412464',
color: '#3cb371',
title: 'frontend',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/16934793',
color: '#A8D695',
title: 'group::code review',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/14918378',
color: '#F0AD4E',
title: 'section::dev',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/10230929',
color: '#009966',
title: 'type::feature',
description: 'Label description',
},
],
},
assignees: {
nodes: [
{
id: 'gid://gitlab/User/1',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
],
},
reviewers: {
nodes: [
{
id: 'gid://gitlab/User/1',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
{
id: 'gid://gitlab/User/2',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
],
},
userDiscussionsCount: 5,
createdAt: '2024-04-22T10:13:09Z',
updatedAt: '2024-04-19T14:34:42Z',
};
const Template = (_, { argTypes }) => {
return {
components: { MergeRequest },
@ -15,106 +109,25 @@ export default {
export const Default = Template.bind({});
Default.args = {
mergeRequest: mockMergeRequest,
};
export const WithApprovalNeeded = Template.bind({});
WithApprovalNeeded.args = {
mergeRequest: {
reference: '!123456',
titleHtml: 'Merge request title',
webUrl: '#',
author: {
name: 'John Smith',
webUrl: 'https://gitlab.com/root',
},
milestone: {
title: '17.0',
},
labels: {
...mockMergeRequest,
approved: false,
approvalsRequired: 4,
approvalsLeft: 2,
approvedBy: {
nodes: [
{
id: 'gid://gitlab/GroupLabel/992791',
color: '#428BCA',
title: 'Deliverable',
description: 'Label description',
id: 1,
},
{
id: 'gid://gitlab/GroupLabel/3103452',
color: '#E44D2A',
title: 'devops::create',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/2975007',
color: '#F0AD4E',
title: 'feature::enhancement',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/3412464',
color: '#3cb371',
title: 'frontend',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/16934793',
color: '#A8D695',
title: 'group::code review',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/14918378',
color: '#F0AD4E',
title: 'section::dev',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/10230929',
color: '#009966',
title: 'type::feature',
description: 'Label description',
id: 2,
},
],
},
assignees: {
nodes: [
{
id: 'gid://gitlab/User/1',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
],
},
reviewers: {
nodes: [
{
id: 'gid://gitlab/User/1',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
{
id: 'gid://gitlab/User/2',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
],
},
headPipeline: {
id: 'gid://gitlab/Ci::Pipeline/1',
detailedStatus: {
id: 'success-1',
icon: 'status_success',
text: 'Passed',
detailsPath: '/',
},
},
userDiscussionsCount: 5,
createdAt: '2024-04-22T10:13:09Z',
updatedAt: '2024-04-19T14:34:42Z',
},
};

View File

@ -1,5 +1,6 @@
<script>
import { GlLink, GlSprintf, GlIcon, GlLabel, GlTooltipDirective } from '@gitlab/ui';
import ApprovalCount from 'ee_component/merge_request_dashboard/components/approval_count.vue';
import { __, sprintf } from '~/locale';
import isShowingLabelsQuery from '~/graphql_shared/client/is_showing_labels.query.graphql';
import SafeHtml from '~/vue_shared/directives/safe_html';
@ -23,6 +24,7 @@ export default {
CiIcon,
TimeAgoTooltip,
UserAvatarLink,
ApprovalCount,
},
directives: {
SafeHtml,
@ -115,6 +117,12 @@ export default {
:class="{ 'gl-mr-2': index !== mergeRequest.reviewers.nodes.length - 1 }"
/>
</li>
<li
v-if="mergeRequest.approvalsRequired || mergeRequest.approved"
class="gl-ml-4 gl-display-flex gl-align-self-center"
>
<approval-count :merge-request="mergeRequest" />
</li>
<li
v-if="mergeRequest.userDiscussionsCount"
v-gl-tooltip="__('Comments')"

View File

@ -2,6 +2,7 @@
#import "~/graphql_shared/fragments/label.fragment.graphql"
#import "~/graphql_shared/fragments/milestone.fragment.graphql"
#import "~/graphql_shared/fragments/ci_icon.fragment.graphql"
#import "ee_else_ce/merge_request_dashboard/queries/merge_request_approval.fragment.graphql"
fragment MergeRequestDashboardFragment on MergeRequest {
id
@ -38,4 +39,5 @@ fragment MergeRequestDashboardFragment on MergeRequest {
userDiscussionsCount
createdAt
updatedAt
...MergeRequestApprovalFragment
}

View File

@ -0,0 +1,8 @@
fragment MergeRequestApprovalFragment on MergeRequest {
approved
approvedBy {
nodes {
id
}
}
}

View File

@ -23,7 +23,7 @@ export default {
<template>
<gl-disclosure-dropdown
placement="right"
placement="bottom-end"
category="tertiary"
:aria-label="__('More actions')"
icon="ellipsis_v"

View File

@ -98,7 +98,7 @@ export default {
modal-id="fork-sync-conflicts-modal"
:text="instructionsStep1"
:title="$options.i18n.copyToClipboard"
class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0"
class="!gl-shadow-none gl-bg-transparent! gl-flex-shrink-0"
/>
</div>
<p>
@ -112,7 +112,7 @@ export default {
modal-id="fork-sync-conflicts-modal"
:text="instructionsStep2"
:title="$options.i18n.copyToClipboard"
class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0"
class="!gl-shadow-none gl-bg-transparent! gl-flex-shrink-0"
/>
</div>
<p>
@ -127,7 +127,7 @@ export default {
modal-id="fork-sync-conflicts-modal"
:text="$options.instructionsStep3"
:title="$options.i18n.copyToClipboard"
class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0 gl-ml-3"
class="!gl-shadow-none gl-bg-transparent! gl-flex-shrink-0 gl-ml-3"
/>
</div>
<template #modal-footer>

View File

@ -118,7 +118,7 @@ export default {
regexButtonHighlightClass() {
return {
'gl-bg-blue-50!': this.regexButtonState,
'gl-shadow-none!': !this.regexButtonState,
'!gl-shadow-none': !this.regexButtonState,
};
},
},

View File

@ -234,7 +234,7 @@ export default {
category="secondary"
data-testid="subscribe-button"
:title="notificationTooltip"
class="sidebar-collapsed-icon sidebar-collapsed-container gl-rounded-0! gl-shadow-none!"
class="sidebar-collapsed-icon sidebar-collapsed-container gl-rounded-0! !gl-shadow-none"
@click="toggleSubscribed"
>
<gl-icon :name="notificationIcon" :size="16" :class="{ 'gl-fill-blue-500': subscribed }" />

View File

@ -217,7 +217,7 @@ export default {
:title="tootltipTitle"
category="tertiary"
type="reset"
class="sidebar-collapsed-icon sidebar-collapsed-container gl-rounded-0! gl-shadow-none!"
class="sidebar-collapsed-icon sidebar-collapsed-container gl-rounded-0! !gl-shadow-none"
@click.stop.prevent="toggleTodo"
>
<gl-icon :class="{ 'todo-undone': hasTodo }" :name="collapsedButtonIcon" />

View File

@ -24,7 +24,7 @@ export default {
<template>
<gl-disclosure-dropdown
:items="items"
placement="center"
placement="bottom"
@shown="$emit('shown')"
@hidden="$emit('hidden')"
>

View File

@ -12,12 +12,12 @@ export default {
ITEM_LOADING: {
id: 'loading',
text: 'loading',
extraAttrs: { disabled: true, class: 'gl-shadow-none!' },
extraAttrs: { disabled: true, class: '!gl-shadow-none' },
},
ITEM_EMPTY: {
id: 'empty',
text: s__('Organization|No organizations available to switch to.'),
extraAttrs: { disabled: true, class: 'gl-shadow-none! gl-text-secondary' },
extraAttrs: { disabled: true, class: '!gl-shadow-none gl-text-secondary' },
},
i18n: {
currentOrganization: s__('Organization|Current organization'),
@ -135,7 +135,7 @@ export default {
<gl-disclosure-dropdown
:items="items"
class="gl-display-block"
placement="center"
placement="bottom"
@shown="onShown"
>
<template #toggle>

View File

@ -208,7 +208,7 @@ export default {
right
:data-testid="`terraform-state-actions-${state.name}`"
:disabled="loading"
toggle-class="gl-px-3! gl-shadow-none!"
toggle-class="gl-px-3! !gl-shadow-none"
>
<template #button-content>
<gl-icon class="gl-mr-0" name="ellipsis_v" />

View File

@ -136,7 +136,7 @@ export default {
<clipboard-button
:text="mergeInfo1"
:title="$options.i18n.copyCommands"
class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0"
class="!gl-shadow-none gl-bg-transparent! gl-flex-shrink-0"
/>
</div>
<p v-if="reviewingDocsPath">
@ -181,7 +181,7 @@ export default {
<clipboard-button
:text="mergeInfo2"
:title="$options.i18n.copyCommands"
class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0"
class="!gl-shadow-none gl-bg-transparent! gl-flex-shrink-0"
/>
</div>
</gl-modal>

View File

@ -7,15 +7,17 @@ export default {
components: { HoverBadge },
i18n: {
badgeLabel: s__('BetaBadge|Beta'),
popoverTitle: s__("BetaBadge|What's Beta?"),
popoverTitle: s__("BetaBadge|What's a beta?"),
descriptionParagraph: s__(
"BetaBadge|A Beta feature is not production-ready, but is unlikely to change drastically before it's released. We encourage users to try Beta features and provide feedback.",
"BetaBadge|A beta feature is not yet production-ready, but is ready for testing and unlikely to change significantly before it's released.",
),
listIntroduction: s__('BetaBadge|A Beta feature:'),
listItemStability: s__('BetaBadge|May be unstable.'),
listItemDataLoss: s__('BetaBadge|Should not cause data loss.'),
listItemReasonableEffort: s__('BetaBadge|Is supported by a commercially reasonable effort.'),
listItemNearCompletion: s__('BetaBadge|Is complete or near completion.'),
listIntroduction: s__('BetaBadge|Beta features:'),
listItemStability: s__('BetaBadge|Have a low risk of data loss, but might still be unstable.'),
listItemReasonableEffort: s__(
'BetaBadge|Are supported on a commercially-reasonable effort basis.',
),
listItemNearCompletion: s__('BetaBadge|Have a near complete user experience.'),
listItemTestAgreement: s__('BetaBadge|Are subject to the GitLab Testing Agreement.'),
},
props: {
size: {
@ -48,9 +50,9 @@ export default {
<ul class="gl-pl-4">
<li>{{ $options.i18n.listItemStability }}</li>
<li>{{ $options.i18n.listItemDataLoss }}</li>
<li>{{ $options.i18n.listItemReasonableEffort }}</li>
<li>{{ $options.i18n.listItemNearCompletion }}</li>
<li>{{ $options.i18n.listItemTestAgreement }}</li>
</ul>
</hover-badge>
</template>

View File

@ -7,15 +7,15 @@ export default {
components: { HoverBadge },
i18n: {
badgeLabel: s__('ExperimentBadge|Experiment'),
popoverTitle: s__("ExperimentBadge|What's an Experiment?"),
popoverTitle: s__("ExperimentBadge|What's an experiment?"),
descriptionParagraph: s__(
"ExperimentBadge|An Experiment is a feature that's in the process of being developed. It's not production-ready. We encourage users to try Experimental features and provide feedback.",
'ExperimentBadge|An experiment is not yet production-ready, but is released for initial testing and feedback during development.',
),
listIntroduction: s__('ExperimentBadge|An Experiment:'),
listItemStability: s__('ExperimentBadge|May be unstable.'),
listItemDataLoss: s__('ExperimentBadge|Can cause data loss.'),
listItemNoSupport: s__('ExperimentBadge|Has no support and might not be documented.'),
listItemCanBeRemoved: s__('ExperimentBadge|Can be removed at any time.'),
listIntroduction: s__('ExperimentBadge|Experiments:'),
listItemStability: s__('ExperimentBadge|Might be unstable or cause data loss.'),
listItemNoSupport: s__('ExperimentBadge|Are not supported and might not be documented.'),
listItemCanBeRemoved: s__('ExperimentBadge|Could be changed or removed at any time.'),
listItemTestAgreement: s__('ExperimentBadge|Are subject to the GitLab Testing Agreement.'),
},
props: {
size: {
@ -35,9 +35,9 @@ export default {
<ul class="gl-pl-4">
<li>{{ $options.i18n.listItemStability }}</li>
<li>{{ $options.i18n.listItemDataLoss }}</li>
<li>{{ $options.i18n.listItemNoSupport }}</li>
<li>{{ $options.i18n.listItemCanBeRemoved }}</li>
<li>{{ $options.i18n.listItemTestAgreement }}</li>
</ul>
</hover-badge>
</template>

View File

@ -158,13 +158,13 @@ export default {
>
<a
v-if="showBlameLink"
class="gl-select-none gl-shadow-none! file-line-blame gl-mx-n2"
class="gl-select-none !gl-shadow-none file-line-blame gl-mx-n2"
:href="`${blamePath}#L${line}`"
></a>
<a
:id="`L${line}`"
:key="line"
class="gl-select-none gl-shadow-none! file-line-num"
class="gl-select-none !gl-shadow-none file-line-num"
:href="`#L${line}`"
:data-line-number="line"
@click="scrollToLine(`#LC${line}`)"

View File

@ -0,0 +1,7 @@
<script>
export default {};
</script>
<template>
<!-- This is intentionally left blank -->
<div></div>
</template>

View File

@ -221,7 +221,7 @@ export default {
<div class="gl-w-full gl-display-flex gl-flex-direction-row gl-justify-content-space-between">
<div class="gl-display-flex gl-flex-direction-row gl-align-items-center gl-w-full">
<gl-button
class="collapsible-card-btn gl-display-flex gl-text-decoration-none gl-reset-color! gl-hover-text-blue-800! gl-shadow-none!"
class="collapsible-card-btn gl-display-flex gl-text-decoration-none gl-reset-color! gl-hover-text-blue-800! !gl-shadow-none"
:aria-label="filename"
variant="link"
category="tertiary"

View File

@ -38,12 +38,12 @@ export default {
class="gl-p-0! gl-absolute gl-z-3 diff-line-num gl-border-r gl-display-flex line-links line-numbers"
>
<a
class="gl-select-none gl-shadow-none! file-line-blame gl-mx-n2 gl-flex-grow-1"
class="gl-select-none !gl-shadow-none file-line-blame gl-mx-n2 gl-flex-grow-1"
:href="`${blamePath}${pageSearchString}#L${number}`"
></a>
<a
:id="`L${number}`"
class="gl-select-none gl-shadow-none! file-line-num"
class="gl-select-none !gl-shadow-none file-line-num"
:href="`#L${number}`"
:data-line-number="number"
>

View File

@ -92,13 +92,13 @@ export default {
class="gl-p-0! gl-z-3 diff-line-num gl-border-r gl-display-flex line-links line-numbers"
>
<a
class="gl-select-none gl-shadow-none! file-line-blame"
class="gl-select-none !gl-shadow-none file-line-blame"
data-event-tracking="click_chunk_blame_on_blob_page"
:href="`${blamePath}${pageSearchString}#L${calculateLineNumber(index)}`"
></a>
<a
:id="`L${calculateLineNumber(index)}`"
class="gl-select-none gl-shadow-none! file-line-num"
class="gl-select-none !gl-shadow-none file-line-num"
:href="`#L${calculateLineNumber(index)}`"
:data-line-number="calculateLineNumber(index)"
>

View File

@ -82,7 +82,7 @@ export default {
v-gl-tooltip.hover.left
category="tertiary"
size="small"
class="gl-float-right gutter-toggle toggle-right-sidebar-button js-toggle-right-sidebar-button gl-shadow-none!"
class="gl-float-right gutter-toggle toggle-right-sidebar-button js-toggle-right-sidebar-button !gl-shadow-none"
:class="collapsedToggleClass"
data-testid="toggle-right-sidebar-button"
:icon="toggleIcon"

View File

@ -182,7 +182,7 @@ export default {
return data.workspace?.workItemTypes?.nodes;
},
skip() {
return !this.canUpdate;
return !this.canUpdate || this.workItemType !== WORK_ITEM_TYPE_VALUE_KEY_RESULT;
},
},
},

View File

@ -9,7 +9,6 @@ import toast from '~/vue_shared/plugins/global_toast';
import { isLoggedIn } from '~/lib/utils/common_utils';
import updateWorkItemNotificationsMutation from '../graphql/update_work_item_notifications.mutation.graphql';
import projectWorkItemTypesQuery from '../graphql/project_work_item_types.query.graphql';
const ICON_ON = 'notifications';
const ICON_OFF = 'notifications-off';
@ -58,22 +57,6 @@ export default {
emailsDisabled: false,
};
},
apollo: {
workItemTypes: {
query: projectWorkItemTypesQuery,
variables() {
return {
fullPath: this.fullPath,
};
},
update(data) {
return data.workspace?.workItemTypes?.nodes;
},
skip() {
return !this.canUpdate;
},
},
},
computed: {
notificationTooltip() {
return this.subscribedToNotifications

View File

@ -1,6 +1,6 @@
#import "./work_item.fragment.graphql"
query groupNamespaceLevel($fullPath: ID!, $iid: String!) {
query groupWorkItem($fullPath: ID!, $iid: String!) {
workspace: namespace(fullPath: $fullPath) {
id
workItem(iid: $iid) {

View File

@ -1,6 +1,6 @@
#import "./work_item.fragment.graphql"
query projectNamespaceLevel($fullPath: ID!, $iid: String!) {
query projectWorkItem($fullPath: ID!, $iid: String!) {
workspace: namespace(fullPath: $fullPath) {
id
workItem(iid: $iid) {

View File

@ -62,3 +62,15 @@ div[style*='margin: 16px 0'] {
}
}
ul.users-list {
list-style: none;
padding: 0px;
display: block;
margin-top: 0px;
}
ul.users-list li {
display: inline-block;
padding-right: 12px;
padding-top: 8px;
}

View File

@ -202,6 +202,18 @@ ul.wiki-pages-list.content-list {
}
}
.wiki-templates-list {
li {
padding-left: $gl-spacing-scale-4 !important;
padding-right: $gl-spacing-scale-4 !important;
li {
padding-left: $gl-spacing-scale-2 !important;
padding-right: 0 !important;
}
}
}
.drawio-editor {
position: fixed;
top: 0;

View File

@ -99,6 +99,7 @@ module WikiActions
end
def templates
@wiki_entries_count = templates_list.total_count
@wiki_entries = WikiDirectory.group_pages(templates_list, templates: true)
render 'shared/wikis/templates'

View File

@ -2,6 +2,16 @@
module Emails
module MergeRequests
extend ActiveSupport::Concern
included do
override_layout_lookup_table.merge!({
merge_when_pipeline_succeeds_email: 'mailer',
approved_merge_request_email: 'mailer',
unapproved_merge_request_email: 'mailer'
})
end
def new_merge_request_email(recipient_id, merge_request_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id, present: true)

View File

@ -10,8 +10,11 @@ module Emails
VERIFICATION_EMAIL_TIMEOUT = 7
included do
layout 'service_desk',
only: [:service_desk_thank_you_email, :service_desk_new_note_email, :service_desk_new_participant_email]
override_layout_lookup_table.merge!({
service_desk_thank_you_email: 'service_desk',
service_desk_new_note_email: 'service_desk',
service_desk_new_participant_email: 'service_desk'
})
end
def service_desk_thank_you_email(issue_id)

View File

@ -7,6 +7,8 @@ class Notify < ApplicationMailer
include ReminderEmailsHelper
include IssuablesHelper
mattr_accessor :override_layout_lookup_table, default: {}
include Emails::Shared
include Emails::Issues
include Emails::MergeRequests
@ -40,6 +42,8 @@ class Notify < ApplicationMailer
helper InProductMarketingHelper
helper RegistrationsHelper
layout :determine_layout
def test_email(recipient_email, subject, body)
mail_with_locale(
to: recipient_email,
@ -70,6 +74,10 @@ class Notify < ApplicationMailer
private
def determine_layout
override_layout_lookup_table[action_name&.to_sym]
end
# Return an email address that displays the name of the sender.
# Override sender_email if you want to hard replace the sender address (e.g. custom email for Service Desk)
def sender(sender_id, send_from_user_email: false, sender_name: nil, sender_email: nil)

View File

@ -23,6 +23,8 @@ class NamespaceSetting < ApplicationRecord
validates :enabled_git_access_protocol, inclusion: { in: enabled_git_access_protocols.keys }
validates :default_branch_protection_defaults, json_schema: { filename: 'default_branch_protection_defaults' }
validates :default_branch_protection_defaults, bytesize: { maximum: -> { DEFAULT_BRANCH_PROTECTIONS_DEFAULT_MAX_SIZE } }
validates :remove_dormant_members, inclusion: { in: [false] }, if: :subgroup?
validates :remove_dormant_members_period, numericality: { only_integer: true, greater_than_or_equal_to: 90 }
validate :allow_mfa_for_group
validate :allow_resource_access_token_creation_for_group
@ -112,14 +114,18 @@ class NamespaceSetting < ApplicationRecord
self.default_branch_name = default_branch_name.presence
end
def subgroup?
!!namespace&.subgroup?
end
def allow_mfa_for_group
if namespace&.subgroup? && allow_mfa_for_subgroups == false
if subgroup? && allow_mfa_for_subgroups == false
errors.add(:allow_mfa_for_subgroups, _('is not allowed since the group is not top-level group.'))
end
end
def allow_resource_access_token_creation_for_group
if namespace&.subgroup? && !resource_access_token_creation_allowed
if subgroup? && !resource_access_token_creation_allowed
errors.add(:resource_access_token_creation_allowed, _('is not allowed since the group is not top-level group.'))
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
module RemoteMirrors # rubocop:disable Gitlab/BoundedContexts -- https://gitlab.com/gitlab-org/gitlab/-/issues/462816
class UpdateService < BaseService
def execute(remote_mirror)
return ServiceResponse.error(message: _('Access Denied')) unless allowed?
return ServiceResponse.error(message: _('Remote mirror is missing')) unless remote_mirror
return ServiceResponse.error(message: _('Project mismatch')) unless remote_mirror.project == project
if remote_mirror.update(allowed_attributes)
ServiceResponse.success(payload: { remote_mirror: remote_mirror })
else
ServiceResponse.error(message: remote_mirror.errors)
end
end
private
def allowed_attributes
RemoteMirrors::Attributes.new(params).allowed
end
def allowed?
Ability.allowed?(current_user, :admin_remote_mirror, project)
end
end
end

View File

@ -15,6 +15,12 @@
= stylesheet_link_tag 'mailer.css'
%body
= yield :preview_text
-# Test stub for RSpec testing of Notify and associated modules
-# Sometimes we override the choosen layout via `determine_layout` in our Notify mailer
-# but ActionMailer doesn't support testing layout in pure Ruby so this HTML comment is
-# used for testing the determined layout
- if Rails.env.test?
/ determine_layout returned template mailer
%table#body{ border: "0", cellpadding: "0", cellspacing: "0" }
%tbody
%tr.line

View File

@ -3,6 +3,6 @@
%td
%img.footer-logo{ alt: "GitLab", src: image_url('mailers/gitlab_logo_black_text.png') }
%div
= notification_reason_text(show_manage_notifications_link: true, show_help_link: true, format: :html)
= notification_reason_text(show_manage_notifications_link: true, show_help_link: true, unsubscribe_url: @unsubscribe_url, format: :html)
= render 'layouts/mailer'

View File

@ -21,6 +21,12 @@
= universal_stylesheet_link_tag 'notify'
= yield :head
%body
-# Test stub for RSpec testing of Notify and associated modules
-# Sometimes we override the choosen layout via `determine_layout` in our Notify mailer
-# but ActionMailer doesn't support testing layout in pure Ruby so this HTML comment is
-# used for testing the determined layout
- if Rails.env.test?
/ determine_layout returned template notify
.content
= html_header_message
= yield

View File

@ -16,6 +16,12 @@
= yield :head
%body
= html_header_message
-# Test stub for RSpec testing of Notify and associated modules
-# Sometimes we override the choosen layout via `determine_layout` in our Notify mailer
-# but ActionMailer doesn't support testing layout in pure Ruby so this HTML comment is
-# used for testing the determined layout
- if Rails.env.test?
/ determine_layout returned template service_desk
.content
= yield
.footer{ style: "margin-top: 10px;" }

View File

@ -1,151 +1,75 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
%html{ lang: I18n.locale }
%head
%meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
%meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
%title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; }
/* iOS BLUE LINKS */
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* ANDROID MARGIN HACK */
body { margin:0 !important; }
div[style*="margin: 16px 0"] { margin:0 !important; }
@media only screen and (max-width: 639px) {
body, #body {
min-width: 320px !important;
}
table.wrapper {
width: 100% !important;
min-width: 320px !important;
}
table.wrapper > tbody > tr > td {
border-left: 0 !important;
border-right: 0 !important;
border-radius: 0 !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
}
ul.users-list {
list-style: none;
padding: 0px;
display: block;
margin-top: 0px;
}
ul.users-list li {
display: inline-block;
padding-right: 12px;
padding-top: 8px;
}
%body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
%tr.success
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
%tr.line
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }
%tr.header
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
= header_logo
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
%span
- if @merge_request.respond_to? :approvals_required
= s_('Notify|Merge request was approved (%{approvals}/%{required_approvals})') % { approvals: @merge_request.approvals.count, required_approvals: @merge_request.approvals_required }
- else
= s_('Notify|Merge request was approved')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.4;text-align:center;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
%tbody
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
= s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was approved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @approved_by)
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }= _("Project")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
= namespace_name
\/
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Branch")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
%tbody
%tr.success
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
%span
- if @merge_request.respond_to? :approvals_required
= s_('Notify|Merge request was approved (%{approvals}/%{required_approvals})') % { approvals: @merge_request.approvals.count, required_approvals: @merge_request.approvals_required }
- else
= s_('Notify|Merge request was approved')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.4;text-align:center;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
%tbody
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
= s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was approved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @approved_by)
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }= _("Project")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
= namespace_name
\/
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Branch")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Author")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon_for_user(@merge_request.author, 24, only_path: false), style: "display:block;border-radius:12px;vertical-align:bottom;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" }
= @merge_request.author.name
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Author")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon_for_user(@merge_request.author, 24, only_path: false), style: "display:block;border-radius:12px;vertical-align:bottom;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" }
= @merge_request.author.name
- if @merge_request.assignees.any?
= render 'users_list', users: @merge_request.assignees, user_label: assignees_label(@merge_request, include_value: false)
- if @merge_request.assignees.any?
= render 'users_list', users: @merge_request.assignees, user_label: assignees_label(@merge_request, include_value: false)
- if @merge_request.reviewers.any?
= render 'users_list', users: @merge_request.reviewers, user_label: reviewers_label(@merge_request, include_value: false)
- if Gitlab.ee?
-# EE-specific start
= render 'layouts/mailer/additional_text'
-# EE-specific end
%tr.footer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%img{ alt: "GitLab", src: image_url('mailers/gitlab_logo_black_text.png'), style: "display:block;margin:0 auto 1em;", width: "90" }/
%div
= notification_reason_text(show_manage_notifications_link: true, show_help_link: true, format: :html)
- if @merge_request.reviewers.any?
= render 'users_list', users: @merge_request.reviewers, user_label: reviewers_label(@merge_request, include_value: false)
- if Gitlab.ee?
-# EE-specific start
= render 'layouts/mailer/additional_text'
-# EE-specific end
- if @target_url
= email_action @target_url

View File

@ -1,151 +1,75 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
%html{ lang: I18n.locale }
%head
%meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }
%meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }
%title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; }
/* iOS BLUE LINKS */
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* ANDROID MARGIN HACK */
body { margin:0 !important; }
div[style*="margin: 16px 0"] { margin:0 !important; }
@media only screen and (max-width: 639px) {
body, #body {
min-width: 320px !important;
}
table.wrapper {
width: 100% !important;
min-width: 320px !important;
}
table.wrapper > tbody > tr > td {
border-left: 0 !important;
border-right: 0 !important;
border-radius: 0 !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
}
ul.users-list {
list-style: none;
padding: 0px;
display: block;
margin-top: 0px;
}
ul.users-list li {
display: inline-block;
padding-right: 12px;
padding-top: 8px;
}
%body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
%tr.success
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
%tr.line
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }
%tr.header
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
= header_logo
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
%tbody
%tr.success
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
%span= _('Merge request was set to auto-merge')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.4;text-align:center;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
%tbody
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
%span{ style: "font-weight: 600;color:#333333;" }= _('Merge request')
%a{ href: merge_request_url(@merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none" }= @merge_request.to_reference
%span= _('was set to auto-merge by')
%img.avatar{ height: "24", src: avatar_icon_for_user(@mwps_set_by, 24, only_path: false), style: "border-radius:12px;margin-left:3px;vertical-align:bottom;", width: "24", alt: "Avatar" }
%a.muted{ href: user_url(@mwps_set_by), style: "color:#333333;text-decoration:none;" }
= @mwps_set_by.name
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }= _('Project')
-# haml-lint:disable NoPlainNodes
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
= namespace_name
\/
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _('Branch')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _('Author')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon_for_user(@merge_request.author, 24, only_path: false), style: "display:block;border-radius:12px;margin:0;", width: "24", alt: "Avatar" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" }
= @merge_request.author.name
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
%span= _('Merge request was set to auto-merge')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.4;text-align:center;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
%tbody
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
%span{ style: "font-weight: 600;color:#333333;" }= _('Merge request')
%a{ href: merge_request_url(@merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none" }= @merge_request.to_reference
%span= _('was set to auto-merge by')
%img.avatar{ height: "24", src: avatar_icon_for_user(@mwps_set_by, 24, only_path: false), style: "border-radius:12px;margin-left:3px;vertical-align:bottom;", width: "24", alt: "Avatar" }
%a.muted{ href: user_url(@mwps_set_by), style: "color:#333333;text-decoration:none;" }
= @mwps_set_by.name
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }= _('Project')
-# haml-lint:disable NoPlainNodes
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
= namespace_name
\/
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _('Branch')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _('Author')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon_for_user(@merge_request.author, 24, only_path: false), style: "display:block;border-radius:12px;margin:0;", width: "24", alt: "Avatar" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" }
= @merge_request.author.name
- if @merge_request.assignees.any?
= render 'users_list', users: @merge_request.assignees, user_label: assignees_label(@merge_request, include_value: false)
- if @merge_request.assignees.any?
= render 'users_list', users: @merge_request.assignees, user_label: assignees_label(@merge_request, include_value: false)
- if @merge_request.reviewers.any?
= render 'users_list', users: @merge_request.reviewers, user_label: reviewers_label(@merge_request, include_value: false)
- if @merge_request.reviewers.any?
= render 'users_list', users: @merge_request.reviewers, user_label: reviewers_label(@merge_request, include_value: false)
= render_if_exists 'layouts/mailer/additional_text'
%tr.footer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%img{ alt: "GitLab", src: image_url('mailers/gitlab_logo_black_text.png'), style: "display:block;margin:0 auto 1em;", width: "90" }
%div
= notification_reason_text(show_manage_notifications_link: true, show_help_link: true, format: :html)
= render_if_exists 'layouts/mailer/additional_text'
- if @target_url
= email_action @target_url

View File

@ -1,154 +1,79 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
%html{ lang: I18n.locale }
%head
%meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
%meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
%title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; }
/* iOS BLUE LINKS */
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* ANDROID MARGIN HACK */
body { margin:0 !important; }
div[style*="margin: 16px 0"] { margin:0 !important; }
@media only screen and (max-width: 639px) {
body, #body {
min-width: 320px !important;
}
table.wrapper {
width: 100% !important;
min-width: 320px !important;
}
table.wrapper > tbody > tr > td {
border-left: 0 !important;
border-right: 0 !important;
border-radius: 0 !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
}
ul.users-list {
list-style: none;
padding: 0px;
display: block;
margin-top: 0px;
}
ul.users-list li {
display: inline-block;
padding-right: 12px;
padding-top: 8px;
}
%body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
%tr.success
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#FC6D26;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
%tr.line
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }
%tr.header
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
= header_logo
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
%img{ alt: "✗", height: "13", src: image_url('mailers/approval/icon-x-orange-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- if @merge_request.respond_to? :approvals_required
%span
= s_('Notify|Merge request was unapproved (%{approvals_count}/%{approvals_required})') % {approvals_count: @merge_request.approvals.count, approvals_required: @merge_request.approvals_required}
- else
%span
= s_('Notify|Merge request was unapproved')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.4;text-align:center;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
%tbody
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
= s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was unapproved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @unapproved_by)
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
= _('Project')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
= namespace_name
\/
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
= _('Branch')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
%tbody
%tr.success
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#FC6D26;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
%img{ alt: "✗", height: "13", src: image_url('mailers/approval/icon-x-orange-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- if @merge_request.respond_to? :approvals_required
%span
= s_('Notify|Merge request was unapproved (%{approvals_count}/%{approvals_required})') % {approvals_count: @merge_request.approvals.count, approvals_required: @merge_request.approvals_required}
- else
%span
= s_('Notify|Merge request was unapproved')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.4;text-align:center;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
%tbody
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
= s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was unapproved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @unapproved_by)
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
= _('Project')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
= namespace_name
\/
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
= _('Branch')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
= _('Author')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon_for_user(@merge_request.author, 24), style: "display:block;border-radius:12px;vertical-align:bottom;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" }
= @merge_request.author.name
- if @merge_request.assignees.any?
= render 'users_list', users: @merge_request.assignees, user_label: assignees_label(@merge_request, include_value: false)
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
= _('Author')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon_for_user(@merge_request.author, 24), style: "display:block;border-radius:12px;vertical-align:bottom;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" }
= @merge_request.author.name
- if @merge_request.reviewers.any?
= render 'users_list', users: @merge_request.reviewers, user_label: reviewers_label(@merge_request, include_value: false)
- if Gitlab.ee?
-# EE-specific start
= render 'layouts/mailer/additional_text'
-# EE-specific end
- if @merge_request.assignees.any?
= render 'users_list', users: @merge_request.assignees, user_label: assignees_label(@merge_request, include_value: false)
%tr.footer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%img{ alt: "GitLab", src: image_url('mailers/gitlab_logo_black_text.png'), style: "display:block;margin:0 auto 1em;", width: "90" }/
%div
= notification_reason_text(show_manage_notifications_link: true, show_help_link: true, format: :html)
- if @merge_request.reviewers.any?
= render 'users_list', users: @merge_request.reviewers, user_label: reviewers_label(@merge_request, include_value: false)
- if Gitlab.ee?
-# EE-specific start
= render 'layouts/mailer/additional_text'
-# EE-specific end
- if @target_url
= email_action @target_url

View File

@ -13,7 +13,7 @@
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { auto_collapse: true, always_show_toggle: true, signed: { in: signed_in }, issuable_type: issuable_type }, class: "#{sidebar_gutter_collapsed_class(is_merge_request)} #{'right-sidebar-merge-requests' if is_merge_request}", 'aria-live' => 'polite', 'aria-label': issuable_type }
.issuable-sidebar{ class: "#{'is-merge-request' if is_merge_request}" }
.issuable-sidebar-header{ class: "#{'gl-pb-2! gl-md-display-flex gl-justify-content-end gl-lg-display-none!' if is_merge_request}" }
= render Pajamas::ButtonComponent.new(button_options: { class: "gutter-toggle gl-float-right js-sidebar-toggle has-tooltip gl-shadow-none! gl-display-block #{'gl-mt-2' if notifications_todos_buttons_enabled?}" , type: 'button', 'aria-label' => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }) do
= render Pajamas::ButtonComponent.new(button_options: { class: "gutter-toggle gl-float-right js-sidebar-toggle has-tooltip !gl-shadow-none gl-display-block #{'gl-mt-2' if notifications_todos_buttons_enabled?}" , type: 'button', 'aria-label' => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }) do
= sidebar_gutter_toggle_icon
- if signed_in
- if !is_merge_request

View File

@ -1,11 +1,10 @@
%li{ class: 'gl-display-flex!' }
%div
= link_to wiki_page.human_title, wiki_page_path(@wiki, wiki_page), data: { testid: 'wiki-page-link', qa_page_name: wiki_page.slug }
%small.gl-pr-2 (#{wiki_page.format})
%li{ class: '!gl-px-5' }
.gl-flex.gl-items-center.gl-justify-between.gl-gap-5
.gl-flex.gl-items-baseline.gl-gap-5.gl-w-full
.gl-flex.gl-items-baseline.gl-grow.gl-gap-2
= link_to wiki_page.human_title, wiki_page_path(@wiki, wiki_page), data: { testid: 'wiki-page-link', qa_page_name: wiki_page.slug }
= render Pajamas::BadgeComponent.new(wiki_page.format, variant: 'muted', class: 'gl-ml-2')
- if wiki_page.last_version
%small.gl-text-secondary= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
- if can?(current_user, :create_wiki, @wiki) && wiki_page.template?
= render Pajamas::ButtonComponent.new(category: :secondary, icon: 'pencil', href: wiki_page_path(@wiki, wiki_page, action: :edit), button_options: { title: s_('Edit template') })
.gl-flex-grow-1.gl-text-right
- if wiki_page.last_version
%small
= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
= render Pajamas::ButtonComponent.new(category: :tertiary, icon: 'pencil', size: 'small', href: wiki_page_path(@wiki, wiki_page, action: :edit), button_options: { title: s_('Edit template'), class: 'has-tooltip' })

View File

@ -7,6 +7,6 @@
= render Pajamas::ButtonComponent.new(icon: 'plus', size: :small, href: "#{wiki_path}/{new_page_title}", button_options: { class: 'wiki-list-create-child-button gl-bg-transparent! gl-hover-bg-gray-50! gl-focus-bg-gray-50! gl-absolute gl-top-1/2 -gl-translate-y-1/2 gl-cursor-pointer gl-right-2' })
= link_to wiki_path, data: { testid: 'wiki-dir-page-link', qa_page_name: wiki_directory.title } do
= wiki_directory.title
%ul{ class: '!gl-pl-7' }
%ul{ class: '!gl-pl-5' }
- wiki_directory.entries.each do |entry|
= render partial: entry.to_partial_path, object: entry, locals: { context: context }

View File

@ -1,4 +1,4 @@
- breadcrumb_title(@page.template? ? s_("Wiki|New Template") : s_("Wiki|New Page")) unless @page.persisted?
- breadcrumb_title(@page.template? ? s_("Wiki|New template") : s_("Wiki|New page")) unless @page.persisted?
- wiki_page_title @page, @page.persisted? ? _('Edit') : _('New')
- add_page_specific_style 'page_bundles/wiki'
- @gfm_form = true
@ -15,9 +15,9 @@
= link_to_wiki_page @page
%span.gl-text-secondary
&middot;
= @page.template? ? s_("Wiki|Edit Template") : s_("Wiki|Edit Page")
= @page.template? ? s_("Wiki|Edit template") : s_("Wiki|Edit page")
- else
= @page.template? ? s_("Wiki|New Template") : s_("Wiki|New Page")
= @page.template? ? s_("Wiki|New template") : s_("Wiki|New page")
= render 'shared/wikis/form', uploads_path: wiki_attachment_upload_url

View File

@ -3,23 +3,34 @@
- page_title s_("Wiki|Templates"), _("Wiki")
- add_page_specific_style 'page_bundles/wiki'
.wiki-page-header.top-area.flex-column.flex-lg-row
%h1.page-title.gl-font-size-h-display.gl-flex-grow-1
= s_("Wiki|Wiki Templates")
.wiki-page-header.gl-flex.gl-items-center{ class: '!gl-mt-5' }
%h1.gl-heading-1.gl-grow{ class: '!gl-mb-0' }
= s_("Wiki|Wiki templates")
.nav-controls.pb-md-3.pb-lg-0
= link_button_to wiki_page_path(@wiki, "#{Wiki::TEMPLATES_DIR}/#{SecureRandom.uuid}", random_title: true), variant: :confirm do
= s_("Wiki|New template")
.dropdown.inline.wiki-sort-dropdown
.btn-group{ role: 'group' }
= wiki_sort_controls(@wiki, params[:direction], action: :templates)
%ul.wiki-pages-list.content-list
- if @templates_list.empty?
%li.no-wiki-pages
= s_("Wiki|No templates found")
- @wiki_entries.each do |entry|
= render partial: entry.to_partial_path, object: entry, locals: { context: 'pages' }
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper.gl-flex-col
%h3.gl-new-card-title
= s_("Wiki|Templates")
.gl-new-card-count
= sprite_icon('template', css_class: 'gl-mr-2')
= @wiki_entries_count
= paginate @templates_list, theme: 'gitlab'
.gl-new-card-actions
= render Pajamas::ButtonComponent.new(href: wiki_page_path(@wiki, "#{Wiki::TEMPLATES_DIR}/#{SecureRandom.uuid}", random_title: true), size: :small) do
= s_("Wiki|New template")
- c.with_body do
%ul.wiki-pages-list.content-list.wiki-templates-list
- if @templates_list.empty?
%li.no-wiki-pages{ class: '!gl-px-5' }
= s_("Wiki|No templates found")
- @wiki_entries.each do |entry|
= render partial: entry.to_partial_path, object: entry, locals: { context: 'pages' }
.gl-pt-3
= paginate @templates_list, theme: 'gitlab'

View File

@ -2,7 +2,7 @@
name: vulnerability_report_advanced_filtering
feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/3429
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140984
rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/17353
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/437128
milestone: '16.9'
group: group::threat insights
type: beta

View File

@ -0,0 +1,9 @@
---
name: use_remote_service_update_service
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/455517
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149707
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/461556
milestone: '17.1'
group: group::source code
type: gitlab_com_derisk
default_enabled: false

View File

@ -107,6 +107,15 @@ module.exports = {
9: '.9',
10: '1',
},
// TODO: Backport to GitLab UI
zIndex: {
0: '0',
1: '1',
2: '2',
3: '3',
200: '200',
9999: '9999',
},
// These extends probably should be moved to GitLab UI:
extend: {
// TODO: Backport to GitLab UI. This should be part of the default colors config.
@ -164,13 +173,6 @@ module.exports = {
limited: '1006px',
'1/2': '50%',
},
zIndex: {
1: '1',
2: '2',
3: '3',
200: '200',
9999: '9999',
},
transitionProperty: {
stroke: 'stroke',
'stroke-opacity': 'stroke-opacity',

View File

@ -4,8 +4,8 @@ classes:
- Elastic::ReindexingSlice
feature_categories:
- global_search
description: TODO
description: Used to track status of Zero downtime reindexing tasks in Elasticsearch or OpenSearch
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55681
milestone: '13.12'
gitlab_schema: gitlab_main
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442660
gitlab_schema: gitlab_main_cell
exempt_from_sharding: true # data is specific to each cell's Elasticsearch cluster, no customer data

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddRemoveDormantMembersToNamespaceSettings < Gitlab::Database::Migration[2.2]
milestone '17.1'
def change
add_column :namespace_settings, :remove_dormant_members, :boolean, default: false, null: false
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddRemoveDormantMembersPeriodToNamespaceSettings < Gitlab::Database::Migration[2.2]
milestone '17.1'
def change
add_column :namespace_settings, :remove_dormant_members_period, :integer, default: 90, null: false
end
end

View File

@ -0,0 +1 @@
16e65c1c310ed5cb4b4f4c2f0bc83bb180b0688d476ecfd84bea681f6b115588

View File

@ -0,0 +1 @@
d4fc6dd79a6e7b535037551858d261db1e7470380e9f2d9d154af25e0c44b71d

View File

@ -12138,6 +12138,8 @@ CREATE TABLE namespace_settings (
lock_duo_features_enabled boolean DEFAULT false NOT NULL,
disable_personal_access_tokens boolean DEFAULT false NOT NULL,
enable_auto_assign_gitlab_duo_pro_seats boolean DEFAULT false NOT NULL,
remove_dormant_members boolean DEFAULT false NOT NULL,
remove_dormant_members_period integer DEFAULT 90 NOT NULL,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT namespace_settings_unique_project_download_limit_alertlist_size CHECK ((cardinality(unique_project_download_limit_alertlist) <= 100)),
CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100))

View File

@ -203,6 +203,57 @@ Gitlab::Llm::AiGateway::Client.new(User.first).stream(prompt: [{role: "user", co
If you can't fetch the response, check `graphql_json.log`, `sidekiq_json.log`, `llm.log` or `modelgateway_debug.log` if it contains error information.
#### Optional: Test with OIDC authentication
In production environment, AI Gateway verifies that the JWT sent by client request is signed by the authentic OIDC provider.
To test this authentication and authorization flow with scopes/unit-primitives, you can take the following optional step.
Apply the following config to AI Gateway:
```shell
# <AI-Gateway-root>/.env
AIGW_AUTH__BYPASS_EXTERNAL=false
AIGW_GITLAB_URL=<your-gdk-url> # e.g. http://gdk.test:3000/ ... This instance behaves as a OIDC provider.
```
Apply the following config to GDK:
```shell
# <GDK-root>/env.runit
GITLAB_SIMULATE_SAAS=1
```
and `gdk update`.
You can test a request from Rails console:
```shell
# Open a terminal and login to Rails console:
gdk rails console
```
Try requests from GitLab-Rails to AI Gateway. Example:
```ruby
# Optionally, you should allow the local requests since your AI Gateway is running in localhost.
Gitlab::CurrentSettings.update(allow_local_requests_from_web_hooks_and_services: true)
Gitlab::Llm::VertexAi::Client.new(User.first, unit_primitive: 'explain_vulnerability').chat(content: "Hi, how are you?")
Gitlab::Llm::VertexAi::Client.new(User.first, unit_primitive: 'explain_vulnerability').messages_chat(content: [{"author": "user", "content": "Hi, how are you?"}])
Gitlab::Llm::VertexAi::Client.new(User.first, unit_primitive: 'explain_vulnerability').code_completion(content: {"prefix": "print('Hello", "suffix": ""})
```
Here is the underlying process happening per request:
1. GitLab-Rails generates a new JWT with a given scope (e.g. `duo_chat`).
1. GitLab-Rails requets to AI Gateway with the JWT (bearer token in `Authorization` HTTP header).
1. AI Gateway decodes the JWT with the JWKS issued by the GitLab-Rails. If it's successfuly, the request is authenticated.
1. AI Gateway verifies if the `scopes` claim in the JWT satisfies the target endpoint's scope requirement.
Required scope varies per endpoint (e.g. `/v1/chat/agent` requires `duo_chat`, `/v2/code/suggestions` requires `code_suggestions`).
NOTE:
If you want to test as self-managed GitLab instance, you need to set up Customer Dot as described in the above section.
### SaaS-only features
These features do not use the AI Gateway and instead reach out to the LLM provider directly because they are not yet following the [architecture blueprint](../../architecture/blueprints/ai_gateway/index.md). [We are planning on](https://gitlab.com/groups/gitlab-org/-/epics/13024) moving these features to our self managed offering, so any features developed under this setup will be migrated over time.
@ -238,7 +289,7 @@ GITLAB_SIMULATE_SAAS=1 RAILS_ENV=development bundle exec rake 'gitlab:duo:setup[
1. Go to the group with the Ultimate license.
1. Select **Settings > General**.
1. Expand the **Permissions and group features** section.
1. Enable **Experiment & Beta features**.
1. Enable **Use experiment and beta features**.
1. Enable the specific feature flag for the feature you want to test.
1. You can use Rake task `rake gitlab:duo:enable_feature_flags` to enable all feature flags that are assigned to group AI Framework.
1. Setup [AI Gateway](#test-ai-features-with-ai-gateway-locally).

View File

@ -157,7 +157,7 @@ To turn on GitLab Duo experiment and beta features for a top-level group:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > General**.
1. Expand **Permissions and group features**.
1. Under **Experiment and Beta features**, select the **Use Experiment and Beta features** checkbox.
1. Under **Experiment and beta features**, select the **Use experiment and beta features** checkbox.
1. Select **Save changes**.
This setting [cascades to all projects](../user/project/merge_requests/approvals/settings.md#cascade-settings-from-the-instance-or-top-level-group)

View File

@ -153,6 +153,7 @@ When transferring groups, note:
- If the immediate parent group's visibility is lower than the group's current visibility, visibility levels for subgroups and projects change to match the new parent group's visibility.
- Only explicit group membership is transferred, not inherited membership. If the group's owners have only inherited membership, this leaves the group without an owner. In this case, the user transferring the group becomes the group's owner.
- Transfers fail if the group is a top-level group and [npm packages](../packages/npm_registry/index.md) following the [naming convention](../packages/npm_registry/index.md#naming-convention) exist in any of the projects in the group, or in any of its subgroups.
- `container_registry` images in the archived projects must be deleted before the transfer. For more information, see the [troubleshooting section](troubleshooting.md#missing-or-insufficient-permission-delete-button-disabled).
- Existing packages that use a group-level endpoint (Maven, NuGet, PyPI, Composer, and Debian) need to be updated per the package's steps for setting up the group level endpoint.
- Existing package names need to be updated if the package uses an instance level endpoint ([Maven](../packages/maven_repository/index.md#naming-convention), [npm](../packages/npm_registry/index.md#naming-convention), [Conan](../packages/conan_repository/index.md#package-recipe-naming-convention-for-instance-remotes)) and the group was moved to another root level namespace.
- Top-level groups that have a subscription on GitLab.com cannot be transferred. To make the transfer possible, the top-level group's subscription must be removed first. Then the top-level group can be transferred as a subgroup to another top-level group.

View File

@ -99,3 +99,11 @@ This error typically occurs when the user you're trying to remove is part of an
- Recommended. Remove the user directly from the invited group, if you have access to the group.
The feature request to **Update `billable_members` endpoint to include invited group** is currently being worked on. For more information, see [issue 386583](https://gitlab.com/gitlab-org/gitlab/-/issues/386583)
## Missing or insufficient permission, delete button disabled
This error typically occurs when a user attempts to remove the `container_registry` images from the archived projects during group transfer. To solve this error:
1. Unarchive the project.
1. Delete the `container_registry` images.
1. Archive the project.

View File

@ -148,7 +148,7 @@ These group-level settings are available for top-level groups and cascade to all
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > General**.
1. Expand the **Permissions and group features** section.
1. Check **Use Experiment and Beta features** checkbox.
1. Check **Use experiment and beta features** checkbox.
1. Select **Save changes**.
### Project-level settings

View File

@ -356,31 +356,6 @@ You can disable following and being followed by other users.
NOTE:
When this feature is being disabled, all current followed/following connections are deleted.
## Exact code search
### Enable exact code search
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** GitLab.com, Self-managed
**Status:** Beta
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105049) as a [beta](../../policy/experiment-beta-support.md#beta) in GitLab 15.9 [with flags](../../administration/feature_flags.md) named `index_code_with_zoekt` and `search_code_with_zoekt`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/388519) in GitLab 16.6.
> - Feature flags `index_code_with_zoekt` and `search_code_with_zoekt` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148378) in GitLab 17.1.
WARNING:
This feature is in [beta](../../policy/experiment-beta-support.md#beta) and subject to change without notice.
For more information, see [epic 9404](https://gitlab.com/groups/gitlab-org/-/epics/9404).
To enable exact code search in GitLab:
1. On the left sidebar, select your avatar.
1. Select **Edit profile**.
1. Select **Preferences**.
1. Clear the **Enable advanced code search** checkbox.
1. Select **Save changes**.
## View a user's activity
GitLab tracks [user contribution activity](contributions_calendar.md).

View File

@ -309,6 +309,32 @@ To customize the time format:
1. Under **Time format**, select either the **System**, **12-hour**, or **24-hour** option.
1. Select **Save changes**.
## Enable exact code search
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** GitLab.com, Self-managed
**Status:** Beta
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105049) as a [beta](../../policy/experiment-beta-support.md#beta) in GitLab 15.9 [with flags](../../administration/feature_flags.md) named `index_code_with_zoekt` and `search_code_with_zoekt`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/388519) in GitLab 16.6.
> - Feature flags `index_code_with_zoekt` and `search_code_with_zoekt` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148378) in GitLab 17.1.
WARNING:
This feature is in [beta](../../policy/experiment-beta-support.md#beta) and subject to change without notice.
For more information, see [epic 9404](https://gitlab.com/groups/gitlab-org/-/epics/9404).
You can use [exact code search](../search/exact_code_search.md) instead of
[advanced search](../search/advanced_search.md) to search for code in GitLab.
To enable exact code search:
1. On the left sidebar, select your avatar.
1. Select **Preferences**.
1. Go to the **Exact code search** section.
1. Select the **Enable exact code search** checkbox.
1. Select **Save changes**.
## User identities in CI job JSON web tokens
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/387537) in GitLab 16.0.

View File

@ -77,7 +77,7 @@ Users with at least the Developer role can create new wiki pages:
1. On the left sidebar, select **Search or go to** and find your project or group.
1. Select **Plan > Wiki**.
1. Select **New page** on this page, or any other wiki page.
1. Select **Wiki actions {ellipsis_v} > New page** on this page, or any other wiki page.
1. Select a content format.
1. Add a title for your new page. Page titles use
[special characters](#special-characters-in-page-titles) for subdirectories and formatting,
@ -96,7 +96,7 @@ locally:
1. On the left sidebar, select **Search or go to** and find your project or group.
1. Select **Plan > Wiki**.
1. On the right sidebar, select **Clone repository**.
1. Select **Wiki actions {ellipsis_v} > Clone repository**.
1. Follow the on-screen instructions.
Files you add to your wiki locally must use one of the following
@ -212,7 +212,7 @@ Prerequisites:
1. On the left sidebar, select **Search or go to** and find your project or group.
1. Select **Plan > Wiki**.
1. On the right sidebar, select **Templates**.
1. Select **Wiki actions {ellipsis_v} > Templates**.
1. Select **New Template**.
1. Enter template title, format and content, as if creating a regular wiki page.

View File

@ -19,6 +19,10 @@ WARNING:
This feature is in [beta](../../policy/experiment-beta-support.md#beta) and subject to change without notice.
For more information, see [epic 9404](https://gitlab.com/groups/gitlab-org/-/epics/9404).
Prerequisites:
- You must [enable exact code search](../profile/preferences.md#enable-exact-code-search) in user preferences.
With exact code search, you can use regular expression and exact match modes
to search for code in all GitLab or in a specific project.

View File

@ -145,20 +145,32 @@ module API
put ':id/remote_mirrors/:mirror_id' do
mirror = find_remote_mirror
mirror_params = declared_params(include_missing: false)
mirror_params[:id] = mirror_params.delete(:mirror_id)
if Feature.enabled?(:use_remote_service_update_service, user_project)
service = ::RemoteMirrors::UpdateService.new(
user_project,
current_user,
declared_params(include_missing: false)
)
verify_mirror_branches_setting(mirror_params)
update_params = { remote_mirrors_attributes: mirror_params }
result = service.execute(mirror)
result = ::Projects::UpdateService
.new(user_project, current_user, update_params)
.execute
render_api_error!(result.message, 400) if result.error?
if result[:status] == :success
present mirror.reset, with: Entities::RemoteMirror
present result.payload[:remote_mirror], with: Entities::RemoteMirror
else
render_api_error!(result[:message], 400)
mirror_params = declared_params(include_missing: false)
mirror_params[:id] = mirror_params.delete(:mirror_id)
verify_mirror_branches_setting(mirror_params)
update_params = { remote_mirrors_attributes: mirror_params }
result = ::Projects::UpdateService
.new(user_project, current_user, update_params)
.execute
render_api_error!(result[:message], 400) unless result[:status] == :success
present mirror.reset, with: Entities::RemoteMirror
end
end

View File

@ -2102,10 +2102,10 @@ msgstr ""
msgid "AIPoweredSM|Enable %{link_start}AI-powered features%{link_end} for this instance."
msgstr ""
msgid "AIPoweredSM|Enable Experiment and Beta AI-powered features"
msgid "AIPoweredSM|Enable GitLab Duo features"
msgstr ""
msgid "AIPoweredSM|Enable GitLab Duo features"
msgid "AIPoweredSM|Enable experiment and beta AI-powered features"
msgstr ""
msgid "AIPoweredSM|Enforce Duo features setting for all subgroups"
@ -7685,6 +7685,9 @@ msgstr ""
msgid "Autosave|Note"
msgstr ""
msgid "Availability"
msgstr ""
msgid "Available"
msgstr ""
@ -8000,34 +8003,28 @@ msgstr ""
msgid "Beta"
msgstr ""
msgid "BetaBadge|A Beta feature is not production-ready, but is unlikely to change drastically before it's released. We encourage users to try Beta features and provide feedback."
msgid "BetaBadge|A beta feature is not yet production-ready, but is ready for testing and unlikely to change significantly before it's released."
msgstr ""
msgid "BetaBadge|A Beta feature:"
msgid "BetaBadge|Are subject to the GitLab Testing Agreement."
msgstr ""
msgid "BetaBadge|A Beta feature: "
msgid "BetaBadge|Are supported on a commercially-reasonable effort basis."
msgstr ""
msgid "BetaBadge|Beta"
msgstr ""
msgid "BetaBadge|Is complete or near completion."
msgid "BetaBadge|Beta features:"
msgstr ""
msgid "BetaBadge|Is supported by a commercially reasonable effort."
msgid "BetaBadge|Have a low risk of data loss, but might still be unstable."
msgstr ""
msgid "BetaBadge|May be unstable."
msgid "BetaBadge|Have a near complete user experience."
msgstr ""
msgid "BetaBadge|Should not cause data loss."
msgstr ""
msgid "BetaBadge|What's Beta?"
msgstr ""
msgid "BetaBadge|What's a Beta?"
msgid "BetaBadge|What's a beta?"
msgstr ""
msgid "BeyondIdentityService|API Token. User must have access to `git-commit-signing` endpoint."
@ -21285,28 +21282,28 @@ msgstr ""
msgid "Experiment features' settings not allowed."
msgstr ""
msgid "ExperimentBadge|An Experiment is a feature that's in the process of being developed. It's not production-ready. We encourage users to try Experimental features and provide feedback."
msgid "ExperimentBadge|An experiment is not yet production-ready, but is released for initial testing and feedback during development."
msgstr ""
msgid "ExperimentBadge|An Experiment:"
msgid "ExperimentBadge|Are not supported and might not be documented."
msgstr ""
msgid "ExperimentBadge|Can be removed at any time."
msgid "ExperimentBadge|Are subject to the GitLab Testing Agreement."
msgstr ""
msgid "ExperimentBadge|Can cause data loss."
msgid "ExperimentBadge|Could be changed or removed at any time."
msgstr ""
msgid "ExperimentBadge|Experiment"
msgstr ""
msgid "ExperimentBadge|Has no support and might not be documented."
msgid "ExperimentBadge|Experiments:"
msgstr ""
msgid "ExperimentBadge|May be unstable."
msgid "ExperimentBadge|Might be unstable or cause data loss."
msgstr ""
msgid "ExperimentBadge|What's an Experiment?"
msgid "ExperimentBadge|What's an experiment?"
msgstr ""
msgid "Experiments"
@ -25081,7 +25078,7 @@ msgstr ""
msgid "GroupSelect|Select a group"
msgstr ""
msgid "GroupSettings| %{link_start}What do Experiment and Beta mean?%{link_end}"
msgid "GroupSettings| %{link_start}What do experiment and beta mean?%{link_end}"
msgstr ""
msgid "GroupSettings|After the instance reaches the user cap, any user who is added or requests access must be approved by an administrator. Leave empty for an unlimited user cap. If you change the user cap to unlimited, you must re-enable %{project_sharing_docs_link_start}project sharing%{link_end} and %{group_sharing_docs_link_start}group sharing%{link_end}."
@ -25192,7 +25189,7 @@ msgstr ""
msgid "GroupSettings|Experiment"
msgstr ""
msgid "GroupSettings|Experiment and Beta features"
msgid "GroupSettings|Experiment and beta features"
msgstr ""
msgid "GroupSettings|Export group"
@ -25315,7 +25312,7 @@ msgstr ""
msgid "GroupSettings|Use Duo features"
msgstr ""
msgid "GroupSettings|Use Experiment and Beta features"
msgid "GroupSettings|Use experiment and beta features"
msgstr ""
msgid "GroupSettings|Users can create %{link_start_project}project access tokens%{link_end} and %{link_start_group}group access tokens%{link_end} in this group"
@ -39025,7 +39022,7 @@ msgstr ""
msgid "Preferences|Enable Gitpod integration"
msgstr ""
msgid "Preferences|Enable Zoekt code search"
msgid "Preferences|Enable exact code search"
msgstr ""
msgid "Preferences|Enable extension marketplace"
@ -39043,6 +39040,9 @@ msgstr ""
msgid "Preferences|Enable keyboard shortcuts"
msgstr ""
msgid "Preferences|Exact code search"
msgstr ""
msgid "Preferences|Failed to save preferences."
msgstr ""
@ -39115,7 +39115,7 @@ msgstr ""
msgid "Preferences|Turns on or off the ability to follow or be followed by other users."
msgstr ""
msgid "Preferences|Turns on or off the preference to search with Zoekt instead of Elasticsearch."
msgid "Preferences|Use exact code search instead of advanced search to search for code in GitLab."
msgstr ""
msgid "Preferences|Use relative times"
@ -40255,6 +40255,9 @@ msgstr ""
msgid "Project milestone"
msgstr ""
msgid "Project mismatch"
msgstr ""
msgid "Project must have default branch"
msgstr ""
@ -42947,6 +42950,9 @@ msgstr ""
msgid "Remote"
msgstr ""
msgid "Remote mirror is missing"
msgstr ""
msgid "Remote object has no absolute path."
msgstr ""
@ -58665,16 +58671,10 @@ msgstr ""
msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Edit Page"
msgid "Wiki|Edit page"
msgstr ""
msgid "Wiki|Edit Template"
msgstr ""
msgid "Wiki|New Page"
msgstr ""
msgid "Wiki|New Template"
msgid "Wiki|Edit template"
msgstr ""
msgid "Wiki|New page"
@ -58713,10 +58713,10 @@ msgstr ""
msgid "Wiki|Wiki Pages"
msgstr ""
msgid "Wiki|Wiki Templates"
msgid "Wiki|Wiki actions"
msgstr ""
msgid "Wiki|Wiki actions"
msgid "Wiki|Wiki templates"
msgstr ""
msgid "Will be created"
@ -59250,6 +59250,12 @@ msgstr ""
msgid "Workspaces|Agents connect workspaces to your Kubernetes cluster. To create a workspace with an allowed agent, group members must have at least the Developer role."
msgstr ""
msgid "Workspaces|All agents"
msgstr ""
msgid "Workspaces|Allowed Agents"
msgstr ""
msgid "Workspaces|Cancel"
msgstr ""
@ -59352,7 +59358,10 @@ msgstr ""
msgid "Workspaces|The branch, tag, or commit hash GitLab uses to create your workspace."
msgstr ""
msgid "Workspaces|This group has no available agents. Select the All agents tab and allow at least one agent."
msgid "Workspaces|This group has no agents. Start by creating an agent."
msgstr ""
msgid "Workspaces|This group has no available agents. Select the %{strongStart}All agents%{strongEnd} tab and allow at least one agent."
msgstr ""
msgid "Workspaces|To create a workspace, add a devfile to this project. A devfile is a configuration file for your workspace."
@ -60697,6 +60706,9 @@ msgstr ""
msgid "added a Zoom call to this issue"
msgstr ""
msgid "agents"
msgstr ""
msgid "ago"
msgstr ""
@ -60932,6 +60944,9 @@ msgstr ""
msgid "cannot be enabled until a valid credit card is on file"
msgstr ""
msgid "cannot be enabled until identity verification is completed"
msgstr ""
msgid "cannot be used because it belongs to a compromised private key. Stop using this key and generate a new one."
msgstr ""

View File

@ -38,7 +38,7 @@ module QA
element 'approve-access-request-button'
end
base.view 'app/assets/javascripts/members/constants.js' do
base.view 'app/assets/javascripts/members/tabs_metadata.js' do
element 'groups-list-tab'
end

View File

@ -28,7 +28,7 @@ RSpec.describe 'new navigation for everyone callout', :js, feature_category: :na
end
context 'when user dismisses callout' do
it 'hides callout' do
it 'hides callout', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/454405' do
expect(page).to have_content callout_title
page.within(find('[data-feature-id="new_nav_for_everyone_callout"]')) do

View File

@ -388,7 +388,7 @@ describe('Linked pipeline', () => {
`(
'applies the class on $activateEventName and removes it on $deactivateEventName',
async ({ activateEventName, deactivateEventName }) => {
const shadowClass = 'gl-shadow-none!';
const shadowClass = '!gl-shadow-none';
expect(findExpandButton().classes()).toContain(shadowClass);

View File

@ -1,6 +1,14 @@
import { NodeViewWrapper } from '@tiptap/vue-2';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ImageWrapper from '~/content_editor/components/wrappers/image.vue';
import { createTestEditor, mockChainedCommands } from '../../test_utils';
import '~/content_editor/services/upload_helpers';
jest.mock('~/content_editor/services/upload_helpers', () => ({
uploadingStates: {
image12: true,
},
}));
describe('content/components/wrappers/image_spec', () => {
let wrapper;
@ -22,7 +30,7 @@ describe('content/components/wrappers/image_spec', () => {
it('renders an image with the given attributes', () => {
createWrapper({
type: 'image',
type: { name: 'image' },
attrs: { src: 'image.png', alt: 'My Image', width: 200, height: 200 },
});
@ -34,8 +42,17 @@ describe('content/components/wrappers/image_spec', () => {
});
});
it('marks the image as draggable', () => {
createWrapper({ type: { name: 'image' }, attrs: { src: 'image.png', alt: 'My Image' } });
expect(findImage().attributes()).toMatchObject({
draggable: 'true',
'data-drag-handle': '',
});
});
it('sets width and height to auto if not provided', () => {
createWrapper({ type: 'image', attrs: { src: 'image.png', alt: 'My Image' } });
createWrapper({ type: { name: 'image' }, attrs: { src: 'image.png', alt: 'My Image' } });
expect(findImage().attributes()).toMatchObject({
src: 'image.png',
@ -45,8 +62,26 @@ describe('content/components/wrappers/image_spec', () => {
});
});
it('hides the wrapper component if it is a stale upload', () => {
createWrapper({
type: { name: 'image' },
attrs: { src: 'image.png', alt: 'My Image', uploading: 'image12' },
});
expect(wrapper.findComponent(NodeViewWrapper).attributes('style')).toBe('display: none;');
});
it('does not hide the wrapper component if the upload is not stale', () => {
createWrapper({
type: { name: 'image' },
attrs: { src: 'image.png', alt: 'My Image', uploading: 'image13' },
});
expect(wrapper.findComponent(NodeViewWrapper).attributes('style')).toBeUndefined();
});
it('renders corner resize handles', () => {
createWrapper({ type: 'image', attrs: { src: 'image.png', alt: 'My Image' } });
createWrapper({ type: { name: 'image' }, attrs: { src: 'image.png', alt: 'My Image' } });
expect(findHandle('nw').exists()).toBe(true);
expect(findHandle('ne').exists()).toBe(true);
@ -68,7 +103,7 @@ describe('content/components/wrappers/image_spec', () => {
beforeEach(() => {
createWrapper({
type: 'image',
type: { name: 'image' },
attrs: { src: 'image.png', alt: 'My Image', width: 400, height: 100 },
});

View File

@ -0,0 +1,85 @@
import { GlLink } from '@gitlab/ui';
import { NodeViewWrapper } from '@tiptap/vue-2';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import PlayableWrapper from '~/content_editor/components/wrappers/playable.vue';
jest.mock('~/content_editor/services/upload_helpers', () => ({
uploadingStates: {
audio12: true,
video12: true,
},
}));
describe('content/components/wrappers/playable_spec', () => {
let wrapper;
const createWrapper = (node = {}) => {
wrapper = shallowMountExtended(PlayableWrapper, {
propsData: {
node,
},
});
};
const findMedia = (type) => wrapper.find(`[as="${type}"]`);
describe.each`
type | src | alt | title
${'video'} | ${'video.mp4'} | ${'My Video'} | ${'My Video 1'}
${'audio'} | ${'audio.mp3'} | ${'My Audio'} | ${'My Audio 1'}
`('for mediaType=$type', ({ type, src, alt, title }) => {
beforeEach(() => {
createWrapper({ type: { name: type }, attrs: { src, alt, title } });
});
it(`renders a ${type} element with the given attributes`, () => {
expect(findMedia(type).attributes()).toMatchObject({ src, 'data-title': title });
});
it('renders alt as title if title is not provided', () => {
createWrapper({ type: { name: type }, attrs: { src, alt } });
expect(findMedia(type).attributes('data-title')).toEqual(alt);
});
it(`marks the ${type} element as draggable`, () => {
expect(findMedia(type).attributes()).toMatchObject({
draggable: 'true',
'data-drag-handle': '',
});
});
it(`renders a gl-link with the link to the ${type}`, () => {
expect(wrapper.findComponent(GlLink).attributes()).toMatchObject({
class: 'with-attachment-icon',
href: src,
target: '_blank',
});
});
it('marks the gl-link as draggable', () => {
expect(wrapper.findComponent(GlLink).attributes()).toMatchObject({
draggable: 'true',
'data-drag-handle': '',
});
});
it('hides the wrapper component if it is a stale upload', () => {
createWrapper({
type: { name: type },
attrs: { src, alt, uploading: `${type}12` },
});
expect(wrapper.findComponent(NodeViewWrapper).attributes('style')).toBe('display: none;');
});
it('does not hide the wrapper component if the upload is not stale', () => {
createWrapper({
type: { name: type },
attrs: { src, alt, uploading: `${type}13` },
});
expect(wrapper.findComponent(NodeViewWrapper).attributes('style')).toBeUndefined();
});
});
});

View File

@ -20,6 +20,10 @@ describe('content_editor/extensions/image', () => {
}));
});
it('sets the draggable option to true', () => {
expect(Image.config.draggable).toBe(true);
});
it('adds data-canonical-src attribute when rendering to HTML', () => {
const initialDoc = doc(
p(

View File

@ -0,0 +1,7 @@
import ListItem from '~/content_editor/extensions/list_item';
describe('content_editor/extensions/list_item', () => {
it('sets the draggable option to true', () => {
expect(ListItem.config.draggable).toBe(true);
});
});

View File

@ -0,0 +1,7 @@
import Playable from '~/content_editor/extensions/playable';
describe('content_editor/extensions/playable', () => {
it('sets the draggable option to true', () => {
expect(Playable.config.draggable).toBe(true);
});
});

View File

@ -23,6 +23,10 @@ describe('content_editor/extensions/task_item', () => {
}));
});
it('sets the draggable option to true', () => {
expect(TaskItem.config.draggable).toBe(true);
});
it('renders a regular task item for non-inapplicable items', () => {
const initialDoc = doc(taskList(taskItem(p('foo'))));

View File

@ -0,0 +1,48 @@
import { shallowMount } from '@vue/test-utils';
import { GlBadge } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import ApprovalCount from '~/merge_request_dashboard/components/approval_count.vue';
let wrapper;
function createComponent(propsData = {}) {
wrapper = shallowMount(ApprovalCount, {
propsData,
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
},
});
}
const findBadge = () => wrapper.findComponent(GlBadge);
const findTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
describe('Merge request dashboard approval count FOSS component', () => {
it('does not render badge when merge request is not approved', () => {
createComponent({
mergeRequest: { approvedBy: { nodes: [] } },
});
expect(findBadge().exists()).toBe(false);
});
it('renders badge when merge request is approved', () => {
createComponent({
mergeRequest: { approvedBy: { nodes: ['approved'] } },
});
expect(findBadge().exists()).toBe(true);
});
it.each`
approvers | tooltipTitle
${[1]} | ${'1 approver'}
${[1, 2]} | ${'2 approvers'}
`('renders badge with correct tooltip title', ({ approvers, tooltipTitle }) => {
createComponent({
mergeRequest: { approvedBy: { nodes: approvers } },
});
expect(findTooltip().value).toBe(tooltipTitle);
});
});

View File

@ -26,6 +26,12 @@ export function createMockMergeRequest(mergeRequest = {}) {
userDiscussionsCount: 0,
createdAt: '',
updatedAt: '',
approved: false,
approvalsRequired: 0,
approvalsLeft: null,
approvedBy: {
nodes: [],
},
__typename: 'MergeRequest',
...mergeRequest,
};

View File

@ -13,27 +13,27 @@ exports[`Beta badge component renders the badge 1`] = `
class="gl-popover"
>
<p>
A Beta feature is not production-ready, but is unlikely to change drastically before it's released. We encourage users to try Beta features and provide feedback.
A beta feature is not yet production-ready, but is ready for testing and unlikely to change significantly before it's released.
</p>
<p
class="gl-mb-0"
>
A Beta feature:
Beta features:
</p>
<ul
class="gl-pl-4"
>
<li>
May be unstable.
Have a low risk of data loss, but might still be unstable.
</li>
<li>
Should not cause data loss.
Are supported on a commercially-reasonable effort basis.
</li>
<li>
Is supported by a commercially reasonable effort.
Have a near complete user experience.
</li>
<li>
Is complete or near completion.
Are subject to the GitLab Testing Agreement.
</li>
</ul>
</div>

View File

@ -13,27 +13,27 @@ exports[`Experiment badge component renders the badge 1`] = `
class="gl-popover"
>
<p>
An Experiment is a feature that's in the process of being developed. It's not production-ready. We encourage users to try Experimental features and provide feedback.
An experiment is not yet production-ready, but is released for initial testing and feedback during development.
</p>
<p
class="gl-mb-0"
>
An Experiment:
Experiments:
</p>
<ul
class="gl-pl-4"
>
<li>
May be unstable.
Might be unstable or cause data loss.
</li>
<li>
Can cause data loss.
Are not supported and might not be documented.
</li>
<li>
Has no support and might not be documented.
Could be changed or removed at any time.
</li>
<li>
Can be removed at any time.
Are subject to the GitLab Testing Agreement.
</li>
</ul>
</div>

View File

@ -12,11 +12,11 @@ exports[`Blob Simple Viewer component rendering matches the snapshot 1`] = `
class="diff-line-num gl-display-flex line-links"
>
<a
class="file-line-blame gl-mx-n2 gl-select-none gl-shadow-none!"
class="!gl-shadow-none file-line-blame gl-mx-n2 gl-select-none"
href="/blame/foo-bar#L1"
/>
<a
class="file-line-num gl-select-none gl-shadow-none!"
class="!gl-shadow-none file-line-num gl-select-none"
data-line-number="1"
href="#L1"
id="reference-0"
@ -28,11 +28,11 @@ exports[`Blob Simple Viewer component rendering matches the snapshot 1`] = `
class="diff-line-num gl-display-flex line-links"
>
<a
class="file-line-blame gl-mx-n2 gl-select-none gl-shadow-none!"
class="!gl-shadow-none file-line-blame gl-mx-n2 gl-select-none"
href="/blame/foo-bar#L2"
/>
<a
class="file-line-num gl-select-none gl-shadow-none!"
class="!gl-shadow-none file-line-num gl-select-none"
data-line-number="2"
href="#L2"
id="reference-1"
@ -44,11 +44,11 @@ exports[`Blob Simple Viewer component rendering matches the snapshot 1`] = `
class="diff-line-num gl-display-flex line-links"
>
<a
class="file-line-blame gl-mx-n2 gl-select-none gl-shadow-none!"
class="!gl-shadow-none file-line-blame gl-mx-n2 gl-select-none"
href="/blame/foo-bar#L3"
/>
<a
class="file-line-num gl-select-none gl-shadow-none!"
class="!gl-shadow-none file-line-num gl-select-none"
data-line-number="3"
href="#L3"
id="reference-2"

View File

@ -6,12 +6,12 @@ exports[`Chunk component rendering isHighlighted is true renders line numbers 1`
data-testid="line-numbers"
>
<a
class="file-line-blame gl-select-none gl-shadow-none!"
class="!gl-shadow-none file-line-blame gl-select-none"
data-event-tracking="click_chunk_blame_on_blob_page"
href="some/blame/path.js#L71"
/>
<a
class="file-line-num gl-select-none gl-shadow-none!"
class="!gl-shadow-none file-line-num gl-select-none"
data-line-number="71"
href="#L71"
id="reference-0"

View File

@ -42,6 +42,10 @@ RSpec.describe Emails::MergeRequests do
is_expected.to have_html_part_content(reviewer.name)
end
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template notify')
end
end
describe '#merge_request_unmergeable_email' do
@ -73,6 +77,10 @@ RSpec.describe Emails::MergeRequests do
is_expected.to have_html_part_content(reviewer.name)
end
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template notify')
end
end
describe '#closed_merge_request_email' do
@ -103,6 +111,10 @@ RSpec.describe Emails::MergeRequests do
expect(subject.text_part).to have_content(reviewer.name)
end
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template notify')
end
end
describe '#merged_merge_request_email' do
@ -135,6 +147,10 @@ RSpec.describe Emails::MergeRequests do
expect(subject.text_part).to have_content(reviewer.name)
end
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template notify')
end
end
describe '#merge_request_status_email' do
@ -167,6 +183,10 @@ RSpec.describe Emails::MergeRequests do
expect(subject.text_part).to have_content(reviewer.name)
end
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template notify')
end
end
describe "#merge_when_pipeline_succeeds_email" do
@ -185,6 +205,10 @@ RSpec.describe Emails::MergeRequests do
is_expected.to have_html_part_content(reviewer.name)
end
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template mailer')
end
end
describe '#approved_merge_request_email' do
@ -200,6 +224,10 @@ RSpec.describe Emails::MergeRequests do
is_expected.to have_html_part_content(reviewer.name)
end
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template mailer')
end
end
describe '#unapproved_merge_request_email' do
@ -215,6 +243,10 @@ RSpec.describe Emails::MergeRequests do
is_expected.to have_html_part_content(reviewer.name)
end
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template mailer')
end
end
describe "#resolved_all_discussions_email" do
@ -223,6 +255,10 @@ RSpec.describe Emails::MergeRequests do
it "includes the name of the resolver" do
expect(subject).to have_body_text current_user_sanitized
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template notify')
end
end
describe '#merge_requests_csv_email' do
@ -255,6 +291,10 @@ RSpec.describe Emails::MergeRequests do
it { expect(subject).to have_content('attachment has been truncated to avoid exceeding the maximum allowed attachment size of 15 MiB.') }
end
it "uses the correct layout template" do
is_expected.to have_html_part_content('determine_layout returned template mailer')
end
end
def expect_sender(user)

Some files were not shown because too many files have changed in this diff Show More