Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-11-17 18:10:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-11-17 18:10:28 +0300
commita331169e6e84f93fd9b841b56465ac113b6d03f9 (patch)
tree1e823b05f484f55a5f7663b9aa580b64f6c6e0fd /app
parentc609c898ff7f42c3d72a1c6d619e1ff786f5e18a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/clusters_list/components/clusters_view_all.vue1
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor.vue6
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor_alert.vue33
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor_error.vue31
-rw-r--r--app/assets/javascripts/content_editor/components/editor_state_observer.vue2
-rw-r--r--app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue22
-rw-r--r--app/assets/javascripts/content_editor/components/wrappers/table_cell_body.vue4
-rw-r--r--app/assets/javascripts/content_editor/components/wrappers/table_cell_header.vue4
-rw-r--r--app/assets/javascripts/content_editor/extensions/table.js43
-rw-r--r--app/assets/javascripts/content_editor/extensions/table_cell.js3
-rw-r--r--app/assets/javascripts/content_editor/extensions/table_header.js3
-rw-r--r--app/assets/javascripts/content_editor/services/feature_flags.js3
-rw-r--r--app/assets/javascripts/content_editor/services/serialization_helpers.js16
-rw-r--r--app/assets/javascripts/content_editor/services/upload_helpers.js10
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue55
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue5
-rw-r--r--app/controllers/projects/wikis_controller.rb4
-rw-r--r--app/models/ci/pipeline.rb9
-rw-r--r--app/models/project.rb1
19 files changed, 136 insertions, 119 deletions
diff --git a/app/assets/javascripts/clusters_list/components/clusters_view_all.vue b/app/assets/javascripts/clusters_list/components/clusters_view_all.vue
index 48a1944fde3..285876e57d8 100644
--- a/app/assets/javascripts/clusters_list/components/clusters_view_all.vue
+++ b/app/assets/javascripts/clusters_list/components/clusters_view_all.vue
@@ -205,6 +205,7 @@ export default {
></gl-link
><gl-button
category="secondary"
+ data-qa-selector="connect_existing_cluster_button"
variant="confirm"
class="gl-ml-4"
:href="addClusterPath"
diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue
index 02ab34447ca..a8405fe37c7 100644
--- a/app/assets/javascripts/content_editor/components/content_editor.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor.vue
@@ -3,7 +3,7 @@ import { GlLoadingIcon } from '@gitlab/ui';
import { EditorContent as TiptapEditorContent } from '@tiptap/vue-2';
import { LOADING_CONTENT_EVENT, LOADING_SUCCESS_EVENT, LOADING_ERROR_EVENT } from '../constants';
import { createContentEditor } from '../services/create_content_editor';
-import ContentEditorError from './content_editor_error.vue';
+import ContentEditorAlert from './content_editor_alert.vue';
import ContentEditorProvider from './content_editor_provider.vue';
import EditorStateObserver from './editor_state_observer.vue';
import FormattingBubbleMenu from './formatting_bubble_menu.vue';
@@ -12,7 +12,7 @@ import TopToolbar from './top_toolbar.vue';
export default {
components: {
GlLoadingIcon,
- ContentEditorError,
+ ContentEditorAlert,
ContentEditorProvider,
TiptapEditorContent,
TopToolbar,
@@ -92,7 +92,7 @@ export default {
<content-editor-provider :content-editor="contentEditor">
<div>
<editor-state-observer @docUpdate="notifyChange" @focus="focus" @blur="blur" />
- <content-editor-error />
+ <content-editor-alert />
<div
data-testid="content-editor"
data-qa-selector="content_editor_container"
diff --git a/app/assets/javascripts/content_editor/components/content_editor_alert.vue b/app/assets/javascripts/content_editor/components/content_editor_alert.vue
new file mode 100644
index 00000000000..c6737da1d77
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/content_editor_alert.vue
@@ -0,0 +1,33 @@
+<script>
+import { GlAlert } from '@gitlab/ui';
+import EditorStateObserver from './editor_state_observer.vue';
+
+export default {
+ components: {
+ GlAlert,
+ EditorStateObserver,
+ },
+ data() {
+ return {
+ message: null,
+ variant: 'danger',
+ };
+ },
+ methods: {
+ displayAlert({ message, variant }) {
+ this.message = message;
+ this.variant = variant;
+ },
+ dismissAlert() {
+ this.message = null;
+ },
+ },
+};
+</script>
+<template>
+ <editor-state-observer @alert="displayAlert">
+ <gl-alert v-if="message" class="gl-mb-6" :variant="variant" @dismiss="dismissAlert">
+ {{ message }}
+ </gl-alert>
+ </editor-state-observer>
+</template>
diff --git a/app/assets/javascripts/content_editor/components/content_editor_error.vue b/app/assets/javascripts/content_editor/components/content_editor_error.vue
deleted file mode 100644
index 031ea92a7e9..00000000000
--- a/app/assets/javascripts/content_editor/components/content_editor_error.vue
+++ /dev/null
@@ -1,31 +0,0 @@
-<script>
-import { GlAlert } from '@gitlab/ui';
-import EditorStateObserver from './editor_state_observer.vue';
-
-export default {
- components: {
- GlAlert,
- EditorStateObserver,
- },
- data() {
- return {
- error: null,
- };
- },
- methods: {
- displayError({ error }) {
- this.error = error;
- },
- dismissError() {
- this.error = null;
- },
- },
-};
-</script>
-<template>
- <editor-state-observer @error="displayError">
- <gl-alert v-if="error" class="gl-mb-6" variant="danger" @dismiss="dismissError">
- {{ error }}
- </gl-alert>
- </editor-state-observer>
-</template>
diff --git a/app/assets/javascripts/content_editor/components/editor_state_observer.vue b/app/assets/javascripts/content_editor/components/editor_state_observer.vue
index 2eeb0719096..0604047a953 100644
--- a/app/assets/javascripts/content_editor/components/editor_state_observer.vue
+++ b/app/assets/javascripts/content_editor/components/editor_state_observer.vue
@@ -7,7 +7,7 @@ export const tiptapToComponentMap = {
transaction: 'transaction',
focus: 'focus',
blur: 'blur',
- error: 'error',
+ alert: 'alert',
};
const getComponentEventName = (tiptapEventName) => tiptapToComponentMap[tiptapEventName];
diff --git a/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue b/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue
index c44e8145982..41c083111c5 100644
--- a/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue
+++ b/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue
@@ -26,8 +26,8 @@ export default {
type: Object,
required: true,
},
- getPos: {
- type: Function,
+ node: {
+ type: Object,
required: true,
},
},
@@ -61,7 +61,17 @@ export default {
const { state } = this.editor;
const { $cursor } = state.selection;
- this.displayActionsDropdown = $cursor?.pos - $cursor?.parentOffset - 1 === this.getPos();
+ if (!$cursor) return;
+
+ this.displayActionsDropdown = false;
+
+ for (let level = 0; level < $cursor.depth; level += 1) {
+ if ($cursor.node(level) === this.node) {
+ this.displayActionsDropdown = true;
+ break;
+ }
+ }
+
if (this.displayActionsDropdown) {
this.selectedRect = getSelectedRect(state);
}
@@ -99,7 +109,11 @@ export default {
:as="cellType"
@click="hideDropdown"
>
- <span v-if="displayActionsDropdown" class="gl-absolute gl-right-0 gl-top-0">
+ <span
+ v-if="displayActionsDropdown"
+ contenteditable="false"
+ class="gl-absolute gl-right-0 gl-top-0"
+ >
<gl-dropdown
ref="dropdown"
dropup
diff --git a/app/assets/javascripts/content_editor/components/wrappers/table_cell_body.vue b/app/assets/javascripts/content_editor/components/wrappers/table_cell_body.vue
index 6b4343dd5b8..47cd837d060 100644
--- a/app/assets/javascripts/content_editor/components/wrappers/table_cell_body.vue
+++ b/app/assets/javascripts/content_editor/components/wrappers/table_cell_body.vue
@@ -11,8 +11,8 @@ export default {
type: Object,
required: true,
},
- getPos: {
- type: Function,
+ node: {
+ type: Object,
required: true,
},
},
diff --git a/app/assets/javascripts/content_editor/components/wrappers/table_cell_header.vue b/app/assets/javascripts/content_editor/components/wrappers/table_cell_header.vue
index 5f9889374f6..150f78bc84f 100644
--- a/app/assets/javascripts/content_editor/components/wrappers/table_cell_header.vue
+++ b/app/assets/javascripts/content_editor/components/wrappers/table_cell_header.vue
@@ -11,8 +11,8 @@ export default {
type: Object,
required: true,
},
- getPos: {
- type: Function,
+ node: {
+ type: Object,
required: true,
},
},
diff --git a/app/assets/javascripts/content_editor/extensions/table.js b/app/assets/javascripts/content_editor/extensions/table.js
index 0f0477cba2e..004bb8b815c 100644
--- a/app/assets/javascripts/content_editor/extensions/table.js
+++ b/app/assets/javascripts/content_editor/extensions/table.js
@@ -1 +1,42 @@
-export { Table as default } from '@tiptap/extension-table';
+import { Table } from '@tiptap/extension-table';
+import { debounce } from 'lodash';
+import { __ } from '~/locale';
+import { getMarkdownSource } from '../services/markdown_sourcemap';
+import { shouldRenderHTMLTable } from '../services/serialization_helpers';
+
+let alertShown = false;
+const onUpdate = debounce((editor) => {
+ if (alertShown) return;
+
+ editor.state.doc.descendants((node) => {
+ if (node.type.name === 'table' && node.attrs.isMarkdown && shouldRenderHTMLTable(node)) {
+ editor.emit('alert', {
+ message: __(
+ 'The content editor may change the markdown formatting style of the document, which may not match your original markdown style.',
+ ),
+ variant: 'warning',
+ });
+
+ alertShown = true;
+
+ return false;
+ }
+
+ return true;
+ });
+}, 1000);
+
+export default Table.extend({
+ addAttributes() {
+ return {
+ isMarkdown: {
+ default: null,
+ parseHTML: (element) => Boolean(getMarkdownSource(element)),
+ },
+ };
+ },
+
+ onUpdate({ editor }) {
+ onUpdate(editor);
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/table_cell.js b/app/assets/javascripts/content_editor/extensions/table_cell.js
index befc33e669f..9f437ce066c 100644
--- a/app/assets/javascripts/content_editor/extensions/table_cell.js
+++ b/app/assets/javascripts/content_editor/extensions/table_cell.js
@@ -1,10 +1,9 @@
import { TableCell } from '@tiptap/extension-table-cell';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import TableCellBodyWrapper from '../components/wrappers/table_cell_body.vue';
-import { isBlockTablesFeatureEnabled } from '../services/feature_flags';
export default TableCell.extend({
- content: isBlockTablesFeatureEnabled() ? 'block+' : 'inline*',
+ content: 'block+',
addNodeView() {
return VueNodeViewRenderer(TableCellBodyWrapper);
diff --git a/app/assets/javascripts/content_editor/extensions/table_header.js b/app/assets/javascripts/content_editor/extensions/table_header.js
index 829b06fc14b..045fd03199b 100644
--- a/app/assets/javascripts/content_editor/extensions/table_header.js
+++ b/app/assets/javascripts/content_editor/extensions/table_header.js
@@ -1,10 +1,9 @@
import { TableHeader } from '@tiptap/extension-table-header';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import TableCellHeaderWrapper from '../components/wrappers/table_cell_header.vue';
-import { isBlockTablesFeatureEnabled } from '../services/feature_flags';
export default TableHeader.extend({
- content: isBlockTablesFeatureEnabled() ? 'block+' : 'inline*',
+ content: 'block+',
addNodeView() {
return VueNodeViewRenderer(TableCellHeaderWrapper);
},
diff --git a/app/assets/javascripts/content_editor/services/feature_flags.js b/app/assets/javascripts/content_editor/services/feature_flags.js
deleted file mode 100644
index 5f7a4595938..00000000000
--- a/app/assets/javascripts/content_editor/services/feature_flags.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function isBlockTablesFeatureEnabled() {
- return gon.features?.contentEditorBlockTables;
-}
diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js
index b2327555b45..ed5910fca18 100644
--- a/app/assets/javascripts/content_editor/services/serialization_helpers.js
+++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js
@@ -1,5 +1,4 @@
import { uniq } from 'lodash';
-import { isBlockTablesFeatureEnabled } from './feature_flags';
const defaultAttrs = {
td: { colspan: 1, rowspan: 1, colwidth: null },
@@ -75,7 +74,7 @@ function getChildren(node) {
return children;
}
-function shouldRenderHTMLTable(table) {
+export function shouldRenderHTMLTable(table) {
const { rows, cells } = getRowsAndCells(table);
const cellChildCount = Math.max(...cells.map((cell) => cell.childCount));
@@ -282,11 +281,6 @@ export function renderOrderedList(state, node) {
}
export function renderTableCell(state, node) {
- if (!isBlockTablesFeatureEnabled()) {
- state.renderInline(node);
- return;
- }
-
if (!isInBlockTable(node) || containsParagraphWithOnlyText(node)) {
state.renderInline(node.child(0));
} else {
@@ -303,9 +297,7 @@ export function renderTableRow(state, node) {
}
export function renderTable(state, node) {
- if (isBlockTablesFeatureEnabled()) {
- setIsInBlockTable(node, shouldRenderHTMLTable(node));
- }
+ setIsInBlockTable(node, shouldRenderHTMLTable(node));
if (isInBlockTable(node)) renderTagOpen(state, 'table');
@@ -317,9 +309,7 @@ export function renderTable(state, node) {
state.closeBlock(node);
state.flushClose();
- if (isBlockTablesFeatureEnabled()) {
- unsetIsInBlockTable(node);
- }
+ unsetIsInBlockTable(node);
}
export function renderHardBreak(state, node, parent, index) {
diff --git a/app/assets/javascripts/content_editor/services/upload_helpers.js b/app/assets/javascripts/content_editor/services/upload_helpers.js
index 8ac3f719309..f5bf2742748 100644
--- a/app/assets/javascripts/content_editor/services/upload_helpers.js
+++ b/app/assets/javascripts/content_editor/services/upload_helpers.js
@@ -72,8 +72,9 @@ const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown }) => {
);
} catch (e) {
editor.commands.deleteRange({ from: position, to: position + 1 });
- editor.emit('error', {
- error: __('An error occurred while uploading the image. Please try again.'),
+ editor.emit('alert', {
+ message: __('An error occurred while uploading the image. Please try again.'),
+ variant: 'danger',
});
}
};
@@ -102,8 +103,9 @@ const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown }) =
);
} catch (e) {
editor.commands.deleteRange({ from, to: from + 1 });
- editor.emit('error', {
- error: __('An error occurred while uploading the file. Please try again.'),
+ editor.emit('alert', {
+ message: __('An error occurred while uploading the file. Please try again.'),
+ variant: 'danger',
});
}
};
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue
index 6728f2875a4..7d0cea67099 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue
@@ -1,20 +1,15 @@
<script>
import {
- GlAlert,
GlDropdown,
GlDropdownItem,
GlDropdownSectionHeader,
- GlLoadingIcon,
GlTooltipDirective,
} from '@gitlab/ui';
-import axios from '~/lib/utils/axios_utils';
-import { __, s__ } from '~/locale';
+import { __ } from '~/locale';
export const i18n = {
artifacts: __('Artifacts'),
artifactSectionHeader: __('Download artifacts'),
- artifactsFetchErrorMessage: s__('Pipelines|Could not load artifacts.'),
- noArtifacts: s__('Pipelines|No artifacts available'),
};
export default {
@@ -23,11 +18,9 @@ export default {
GlTooltip: GlTooltipDirective,
},
components: {
- GlAlert,
GlDropdown,
GlDropdownItem,
GlDropdownSectionHeader,
- GlLoadingIcon,
},
inject: {
artifactsEndpoint: {
@@ -42,39 +35,22 @@ export default {
type: Number,
required: true,
},
+ artifacts: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
- data() {
- return {
- artifacts: [],
- hasError: false,
- isLoading: false,
- };
- },
- methods: {
- fetchArtifacts() {
- this.isLoading = true;
- // Replace the placeholder with the ID of the pipeline we are viewing
- const endpoint = this.artifactsEndpoint.replace(
- this.artifactsEndpointPlaceholder,
- this.pipelineId,
- );
- return axios
- .get(endpoint)
- .then(({ data }) => {
- this.artifacts = data.artifacts;
- })
- .catch(() => {
- this.hasError = true;
- })
- .finally(() => {
- this.isLoading = false;
- });
+ computed: {
+ shouldShowDropdown() {
+ return this.artifacts?.length;
},
},
};
</script>
<template>
<gl-dropdown
+ v-if="shouldShowDropdown"
v-gl-tooltip
class="build-artifacts js-pipeline-dropdown-download"
:title="$options.i18n.artifacts"
@@ -84,22 +60,11 @@ export default {
right
lazy
text-sr-only
- @show.once="fetchArtifacts"
>
<gl-dropdown-section-header>{{
$options.i18n.artifactSectionHeader
}}</gl-dropdown-section-header>
- <gl-alert v-if="hasError" variant="danger" :dismissible="false">
- {{ $options.i18n.artifactsFetchErrorMessage }}
- </gl-alert>
-
- <gl-loading-icon v-else-if="isLoading" size="sm" />
-
- <gl-alert v-else-if="!artifacts.length" variant="info" :dismissible="false">
- {{ $options.i18n.noArtifacts }}
- </gl-alert>
-
<gl-dropdown-item
v-for="(artifact, i) in artifacts"
:key="i"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index 9bb955c534f..f7c952f9ef6 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -101,6 +101,9 @@ export default {
? this.pipeline.details.status
: {};
},
+ artifacts() {
+ return this.pipeline?.details?.artifacts;
+ },
hasStages() {
return this.pipeline?.details?.stages?.length > 0;
},
@@ -285,7 +288,7 @@ export default {
/>
</span>
<linked-pipelines-mini-list v-if="triggered.length" :triggered="triggered" />
- <pipeline-artifacts :pipeline-id="pipeline.id" class="gl-ml-3" />
+ <pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" />
</span>
</div>
</div>
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 158a8cc262d..02dfaf4c193 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -7,9 +7,5 @@ class Projects::WikisController < Projects::ApplicationController
alias_method :container, :project
- before_action do
- push_frontend_feature_flag(:content_editor_block_tables, @project, default_enabled: :yaml)
- end
-
feature_category :wiki
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 9cc382f0c95..a29aa756e38 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -652,8 +652,15 @@ module Ci
end
def batch_lookup_report_artifact_for_file_type(file_type)
+ batch_lookup_report_artifact_for_file_types([file_type])
+ end
+
+ def batch_lookup_report_artifact_for_file_types(file_types)
+ file_types_to_search = []
+ file_types.each { |file_type| file_types_to_search.append(*::Ci::JobArtifact.associated_file_types_for(file_type.to_s)) }
+
latest_report_artifacts
- .values_at(*::Ci::JobArtifact.associated_file_types_for(file_type.to_s))
+ .values_at(*file_types_to_search.uniq)
.flatten
.compact
.last
diff --git a/app/models/project.rb b/app/models/project.rb
index 604158d1a6e..2288850553c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -494,6 +494,7 @@ class Project < ApplicationRecord
validates :variables, nested_attributes_duplicates: { scope: :environment_scope }
validates :bfg_object_map, file_size: { maximum: :max_attachment_size }
validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true }
+ validates :suggestion_commit_message, length: { maximum: 255 }
# Scopes
scope :pending_delete, -> { where(pending_delete: true) }