Commit 6b5d8b17 authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖
Browse files

Add latest changes from gitlab-org/gitlab@master

parent a84995f4
93762b621c011fe570339c1c247d5197c2cfefcc
2106629e3af3e8949b23f20825d6bfee62c10992
......@@ -46,6 +46,9 @@ function getFallbackKey() {
export default class IssuableForm {
constructor(form) {
if (form.length === 0) {
return;
}
this.form = form;
this.toggleWip = this.toggleWip.bind(this);
this.renderWipExplanation = this.renderWipExplanation.bind(this);
......
......@@ -23,6 +23,7 @@ import CsvImportExportButtons from '~/issuable/components/csv_import_export_butt
import IssuableByEmail from '~/issuable/components/issuable_by_email.vue';
import { IssuableStatus } from '~/issues/constants';
import axios from '~/lib/utils/axios_utils';
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { scrollUp } from '~/lib/utils/scroll_utils';
import { getParameterByName, joinPaths } from '~/lib/utils/url_utility';
import {
......@@ -45,6 +46,8 @@ import {
ISSUE_REFERENCE,
MAX_LIST_SIZE,
PAGE_SIZE,
PARAM_FIRST_PAGE_SIZE,
PARAM_LAST_PAGE_SIZE,
PARAM_PAGE_AFTER,
PARAM_PAGE_BEFORE,
PARAM_SORT,
......@@ -390,12 +393,14 @@ export default {
},
urlParams() {
return {
page_after: this.pageParams.afterCursor,
page_before: this.pageParams.beforeCursor,
search: this.searchQuery,
sort: urlSortParams[this.sortKey],
state: this.state,
...this.urlFilterParams,
first_page_size: this.pageParams.firstPageSize,
last_page_size: this.pageParams.lastPageSize,
page_after: this.pageParams.afterCursor,
page_before: this.pageParams.beforeCursor,
};
},
hasCrmParameter() {
......@@ -632,6 +637,8 @@ export default {
this.showBulkEditSidebar = showBulkEditSidebar;
},
updateData(sortValue) {
const firstPageSize = getParameterByName(PARAM_FIRST_PAGE_SIZE);
const lastPageSize = getParameterByName(PARAM_LAST_PAGE_SIZE);
const pageAfter = getParameterByName(PARAM_PAGE_AFTER);
const pageBefore = getParameterByName(PARAM_PAGE_BEFORE);
const state = getParameterByName(PARAM_STATE);
......@@ -660,7 +667,13 @@ export default {
this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
this.filterTokens = isSearchDisabled ? [] : getFilterTokens(window.location.search);
this.pageParams = getInitialPageParams(sortKey, pageAfter, pageBefore);
this.pageParams = getInitialPageParams(
sortKey,
isPositiveInteger(firstPageSize) ? parseInt(firstPageSize, 10) : undefined,
isPositiveInteger(lastPageSize) ? parseInt(lastPageSize, 10) : undefined,
pageAfter,
pageBefore,
);
this.sortKey = sortKey;
this.state = state || IssuableStates.Opened;
},
......
......@@ -57,6 +57,8 @@ export const MAX_LIST_SIZE = 10;
export const PAGE_SIZE = 20;
export const PAGE_SIZE_MANUAL = 100;
export const PARAM_ASSIGNEE_ID = 'assignee_id';
export const PARAM_FIRST_PAGE_SIZE = 'first_page_size';
export const PARAM_LAST_PAGE_SIZE = 'last_page_size';
export const PARAM_PAGE_AFTER = 'page_after';
export const PARAM_PAGE_BEFORE = 'page_before';
export const PARAM_SORT = 'sort';
......
......@@ -46,8 +46,15 @@ import {
WEIGHT_DESC,
} from './constants';
export const getInitialPageParams = (sortKey, afterCursor, beforeCursor) => ({
firstPageSize: sortKey === RELATIVE_POSITION_ASC ? PAGE_SIZE_MANUAL : PAGE_SIZE,
export const getInitialPageParams = (
sortKey,
firstPageSize = sortKey === RELATIVE_POSITION_ASC ? PAGE_SIZE_MANUAL : PAGE_SIZE,
lastPageSize,
afterCursor,
beforeCursor,
) => ({
firstPageSize: lastPageSize ? undefined : firstPageSize,
lastPageSize,
afterCursor,
beforeCursor,
});
......
......@@ -25,6 +25,7 @@ import {
NOT_AVAILABLE_TEXT,
NOT_AVAILABLE_SIZE,
MORE_ACTIONS_TEXT,
COPY_IMAGE_PATH_TITLE,
} from '../../constants/index';
export default {
......@@ -72,6 +73,7 @@ export default {
CONFIGURATION_DETAILS_ROW_TEST,
MISSING_MANIFEST_WARNING_TOOLTIP,
MORE_ACTIONS_TEXT,
COPY_IMAGE_PATH_TITLE,
},
computed: {
formattedSize() {
......@@ -138,7 +140,7 @@ export default {
<clipboard-button
v-if="tag.location"
:title="tag.location"
:title="$options.i18n.COPY_IMAGE_PATH_TITLE"
:text="tag.location"
category="tertiary"
:disabled="disabled"
......
......@@ -14,6 +14,7 @@ import {
IMAGE_FAILED_DELETED_STATUS,
IMAGE_MIGRATING_STATE,
ROOT_IMAGE_TEXT,
COPY_IMAGE_PATH_TITLE,
} from '../../constants/index';
import DeleteButton from '../delete_button.vue';
import CleanupStatus from './cleanup_status.vue';
......@@ -52,6 +53,7 @@ export default {
i18n: {
REMOVE_REPOSITORY_LABEL,
ROW_SCHEDULED_FOR_DELETION,
COPY_IMAGE_PATH_TITLE,
},
computed: {
disabledDelete() {
......@@ -115,7 +117,7 @@ export default {
v-if="item.location"
:disabled="deleting"
:text="item.location"
:title="item.location"
:title="$options.i18n.COPY_IMAGE_PATH_TITLE"
category="tertiary"
/>
</template>
......
......@@ -41,6 +41,8 @@ export const EMPTY_RESULT_MESSAGE = s__(
'ContainerRegistry|To widen your search, change or remove the filters above.',
);
export const COPY_IMAGE_PATH_TITLE = s__('ContainerRegistry|Copy image path');
// Parameters
export const IMAGE_DELETE_SCHEDULED_STATUS = 'DELETE_SCHEDULED';
......
<script>
import { GlButton } from '@gitlab/ui';
import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { EDITOR_APP_STATUS_EMPTY, EDITOR_APP_STATUS_LOADING } from '../../constants';
import FileTreePopover from '../popovers/file_tree_popover.vue';
import BranchSwitcher from './branch_switcher.vue';
......@@ -12,7 +11,6 @@ export default {
FileTreePopover,
GlButton,
},
mixins: [glFeatureFlagMixin()],
props: {
hasUnsavedChanges: {
type: Boolean,
......@@ -43,11 +41,7 @@ export default {
return this.appStatus === EDITOR_APP_STATUS_LOADING;
},
showFileTreeToggle() {
return (
this.glFeatures.pipelineEditorFileTree &&
!this.isNewCiConfigFile &&
this.appStatus !== EDITOR_APP_STATUS_EMPTY
);
return !this.isNewCiConfigFile && this.appStatus !== EDITOR_APP_STATUS_EMPTY;
},
},
methods: {
......
<script>
import { GlModal } from '@gitlab/ui';
import { __ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import CommitSection from './components/commit/commit_section.vue';
import PipelineEditorDrawer from './components/drawer/pipeline_editor_drawer.vue';
import PipelineEditorFileNav from './components/file_nav/pipeline_editor_file_nav.vue';
......@@ -34,7 +33,6 @@ export default {
PipelineEditorHeader,
PipelineEditorTabs,
},
mixins: [glFeatureFlagMixin()],
props: {
ciConfigData: {
type: Object,
......@@ -76,9 +74,6 @@ export default {
includesFiles() {
return this.ciConfigData?.includes || [];
},
isFileTreeVisible() {
return this.showFileTree && this.glFeatures.pipelineEditorFileTree;
},
},
mounted() {
this.showFileTree = JSON.parse(localStorage.getItem(FILE_TREE_DISPLAY_KEY)) || false;
......@@ -140,7 +135,7 @@ export default {
/>
<div class="gl-display-flex gl-w-full gl-sm-flex-direction-column">
<pipeline-editor-file-tree
v-if="isFileTreeVisible"
v-if="showFileTree"
class="gl-flex-shrink-0"
:includes="includesFiles"
/>
......
......@@ -98,10 +98,10 @@ export default {
<template>
<div data-qa-selector="approvals_summary_content">
<strong>{{ approvalLeftMessage }}</strong>
<span class="gl-font-weight-bold">{{ approvalLeftMessage }}</span>
<template v-if="hasApprovers">
<span v-if="approvalLeftMessage">{{ message }}</span>
<strong v-else>{{ message }}</strong>
<span v-else class="gl-font-weight-bold">{{ message }}</span>
<user-avatar-list
class="gl-display-inline-block gl-vertical-align-middle"
:img-size="24"
......
......@@ -234,6 +234,8 @@
.navbar-sub-nav {
display: flex;
align-items: center;
height: 100%;
margin: 0 0 0 6px;
.dropdown-chevron {
......
......@@ -439,7 +439,7 @@ $browser-scrollbar-size: 10px;
/*
* Misc
*/
$header-height: var(--header-height, 40px);
$header-height: var(--header-height, 48px);
$header-zindex: 1000;
$zindex-dropdown-menu: 300;
$suggestion-header-height: 46px;
......
......@@ -755,7 +755,7 @@ input {
padding: 0 16px;
z-index: 1000;
margin-bottom: 0;
min-height: var(--header-height, 40px);
min-height: var(--header-height, 48px);
border: 0;
position: fixed;
top: 0;
......@@ -771,7 +771,7 @@ input {
display: flex;
justify-content: space-between;
position: relative;
min-height: var(--header-height, 40px);
min-height: var(--header-height, 48px);
padding-left: 0;
}
.navbar-gitlab .header-content .title {
......@@ -787,9 +787,6 @@ input {
.navbar-gitlab .header-content .title img {
height: 24px;
}
.navbar-gitlab .header-content .title img + .logo-text {
margin-left: 8px;
}
.navbar-gitlab .header-content .title a {
display: flex;
align-items: center;
......@@ -915,6 +912,8 @@ input {
}
.navbar-sub-nav {
display: flex;
align-items: center;
height: 100%;
margin: 0 0 0 6px;
}
.caret-down,
......@@ -1034,7 +1033,7 @@ input {
left: 0;
z-index: 600;
width: 220px;
top: var(--header-height, 40px);
top: var(--header-height, 48px);
background-color: #303030;
transform: translate3d(0, 0, 0);
}
......@@ -2044,19 +2043,9 @@ body.gl-dark {
.gl-display-none {
display: none;
}
@media (min-width: 992px) {
.gl-lg-display-none\! {
display: none !important;
}
}
.gl-display-flex {
display: flex;
}
@media (min-width: 992px) {
.gl-lg-display-flex {
display: flex;
}
}
@media (min-width: 576px) {
.gl-sm-display-block {
display: block;
......
......@@ -740,7 +740,7 @@ input {
padding: 0 16px;
z-index: 1000;
margin-bottom: 0;
min-height: var(--header-height, 40px);
min-height: var(--header-height, 48px);
border: 0;
position: fixed;
top: 0;
......@@ -756,7 +756,7 @@ input {
display: flex;
justify-content: space-between;
position: relative;
min-height: var(--header-height, 40px);
min-height: var(--header-height, 48px);
padding-left: 0;
}
.navbar-gitlab .header-content .title {
......@@ -772,9 +772,6 @@ input {
.navbar-gitlab .header-content .title img {
height: 24px;
}
.navbar-gitlab .header-content .title img + .logo-text {
margin-left: 8px;
}
.navbar-gitlab .header-content .title a {
display: flex;
align-items: center;
......@@ -900,6 +897,8 @@ input {
}
.navbar-sub-nav {
display: flex;
align-items: center;
height: 100%;
margin: 0 0 0 6px;
}
.caret-down,
......@@ -1019,7 +1018,7 @@ input {
left: 0;
z-index: 600;
width: 220px;
top: var(--header-height, 40px);
top: var(--header-height, 48px);
background-color: #f0f0f0;
transform: translate3d(0, 0, 0);
}
......@@ -1704,19 +1703,9 @@ svg.s16 {
.gl-display-none {
display: none;
}
@media (min-width: 992px) {
.gl-lg-display-none\! {
display: none !important;
}
}
.gl-display-flex {
display: flex;
}
@media (min-width: 992px) {
.gl-lg-display-flex {
display: flex;
}
}
@media (min-width: 576px) {
.gl-sm-display-block {
display: block;
......
......@@ -419,7 +419,7 @@ body.navless {
}
}
.navless-container {
margin-top: var(--header-height, 40px);
margin-top: var(--header-height, 48px);
padding-top: 32px;
}
.btn {
......@@ -506,7 +506,7 @@ label.label-bold {
}
.navbar-empty {
justify-content: center;
height: var(--header-height, 40px);
height: var(--header-height, 48px);
background: #fff;
border-bottom: 1px solid #dbdbdb;
}
......
# frozen_string_literal: true
module Mailgun
class WebhooksController < ApplicationController
respond_to :json
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
before_action :ensure_feature_enabled!
before_action :authenticate_signature!
feature_category :team_planning
WEBHOOK_PROCESSORS = [
::Members::Mailgun::ProcessWebhookService
].freeze
def process_webhook
WEBHOOK_PROCESSORS.each do |processor_class|
processor = processor_class.new(params['event-data'] || {})
processor.execute if processor.should_process?
end
head :ok
end
private
def ensure_feature_enabled!
render_406 unless Gitlab::CurrentSettings.mailgun_events_enabled?
end
def authenticate_signature!
access_denied! unless valid_signature?
end
def valid_signature?
return false if Gitlab::CurrentSettings.mailgun_signing_key.blank?
# per this guide: https://documentation.mailgun.com/en/latest/user_manual.html#webhooks
digest = OpenSSL::Digest.new('SHA256')
data = [params.dig(:signature, :timestamp), params.dig(:signature, :token)].join
hmac_digest = OpenSSL::HMAC.hexdigest(digest, Gitlab::CurrentSettings.mailgun_signing_key, data)
ActiveSupport::SecurityUtils.secure_compare(params.dig(:signature, :signature), hmac_digest)
end
def render_406
# failure to stop retries per https://documentation.mailgun.com/en/latest/user_manual.html#webhooks
head :not_acceptable
end
end
end
# frozen_string_literal: true
module Members
module Mailgun
class PermanentFailuresController < ApplicationController
respond_to :json
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
before_action :ensure_feature_enabled!
before_action :authenticate_signature!
before_action :validate_invite_email!
feature_category :authentication_and_authorization
def create
webhook_processor.execute
head :ok
end
private
def ensure_feature_enabled!
render_406 unless Gitlab::CurrentSettings.mailgun_events_enabled?
end
def authenticate_signature!
access_denied! unless valid_signature?
end
def valid_signature?
return false if Gitlab::CurrentSettings.mailgun_signing_key.blank?
# per this guide: https://documentation.mailgun.com/en/latest/user_manual.html#webhooks
digest = OpenSSL::Digest.new('SHA256')
data = [params.dig(:signature, :timestamp), params.dig(:signature, :token)].join
hmac_digest = OpenSSL::HMAC.hexdigest(digest, Gitlab::CurrentSettings.mailgun_signing_key, data)
ActiveSupport::SecurityUtils.secure_compare(params.dig(:signature, :signature), hmac_digest)
end
def validate_invite_email!
# permanent_failures webhook does not provide a way to filter failures, so we'll get them all on this endpoint
# and we only care about our invite_emails
render_406 unless payload[:tags]&.include?(::Members::Mailgun::INVITE_EMAIL_TAG)
end
def webhook_processor
::Members::Mailgun::ProcessWebhookService.new(payload)
end
def payload
@payload ||= params.permit!['event-data']
end
def render_406
# failure to stop retries per https://documentation.mailgun.com/en/latest/user_manual.html#webhooks
head :not_acceptable
end
end
end
end
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment