summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/snippets
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 10:34:06 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 10:34:06 +0000
commit859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch)
treed7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /app/assets/javascripts/snippets
parent446d496a6d000c73a304be52587cd9bbc7493136 (diff)
downloadgitlab-ce-859a6fb938bb9ee2a317c46dfa4fcc1af49608f0.tar.gz
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/snippets')
-rw-r--r--app/assets/javascripts/snippets/components/edit.vue81
-rw-r--r--app/assets/javascripts/snippets/components/embed_dropdown.vue2
-rw-r--r--app/assets/javascripts/snippets/components/show.vue14
-rw-r--r--app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue2
-rw-r--r--app/assets/javascripts/snippets/components/snippet_blob_edit.vue8
-rw-r--r--app/assets/javascripts/snippets/components/snippet_blob_view.vue4
-rw-r--r--app/assets/javascripts/snippets/components/snippet_description_edit.vue2
-rw-r--r--app/assets/javascripts/snippets/components/snippet_header.vue6
-rw-r--r--app/assets/javascripts/snippets/components/snippet_visibility_edit.vue2
-rw-r--r--app/assets/javascripts/snippets/index.js2
-rw-r--r--app/assets/javascripts/snippets/mixins/snippets.js8
-rw-r--r--app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql2
-rw-r--r--app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql3
-rw-r--r--app/assets/javascripts/snippets/utils/blob.js4
-rw-r--r--app/assets/javascripts/snippets/utils/error.js15
15 files changed, 121 insertions, 34 deletions
diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue
index ffb5e242973..9f43ac36df7 100644
--- a/app/assets/javascripts/snippets/components/edit.vue
+++ b/app/assets/javascripts/snippets/components/edit.vue
@@ -1,27 +1,28 @@
<script>
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
+import eventHub from '~/blob/components/eventhub';
import { deprecatedCreateFlash as Flash } from '~/flash';
-import { __, sprintf } from '~/locale';
-import TitleField from '~/vue_shared/components/form/title.vue';
import { redirectTo, joinPaths } from '~/lib/utils/url_utility';
-import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
+import { __, sprintf } from '~/locale';
import {
SNIPPET_MARK_EDIT_APP_START,
SNIPPET_MEASURE_BLOBS_CONTENT,
} from '~/performance/constants';
-import eventHub from '~/blob/components/eventhub';
import { performanceMarkAndMeasure } from '~/performance/utils';
+import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
+import TitleField from '~/vue_shared/components/form/title.vue';
-import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql';
-import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql';
-import { getSnippetMixin } from '../mixins/snippets';
import { SNIPPET_CREATE_MUTATION_ERROR, SNIPPET_UPDATE_MUTATION_ERROR } from '../constants';
+import { getSnippetMixin } from '../mixins/snippets';
+import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql';
+import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql';
import { markBlobPerformance } from '../utils/blob';
+import { getErrorMessage } from '../utils/error';
import SnippetBlobActionsEdit from './snippet_blob_actions_edit.vue';
-import SnippetVisibilityEdit from './snippet_visibility_edit.vue';
import SnippetDescriptionEdit from './snippet_description_edit.vue';
+import SnippetVisibilityEdit from './snippet_visibility_edit.vue';
eventHub.$on(SNIPPET_MEASURE_BLOBS_CONTENT, markBlobPerformance);
@@ -32,6 +33,7 @@ export default {
SnippetBlobActionsEdit,
TitleField,
FormFooterActions,
+ CaptchaModal: () => import('~/captcha/captcha_modal.vue'),
GlButton,
GlLoadingIcon,
},
@@ -66,12 +68,25 @@ export default {
description: '',
visibilityLevel: this.selectedLevel,
},
+ captchaResponse: '',
+ needsCaptchaResponse: false,
+ captchaSiteKey: '',
+ spamLogId: '',
};
},
computed: {
hasBlobChanges() {
return this.actions.length > 0;
},
+ hasNoChanges() {
+ return (
+ this.actions.every(
+ (action) => !action.content && !action.filePath && !action.previousPath,
+ ) &&
+ !this.snippet.title &&
+ !this.snippet.description
+ );
+ },
hasValidBlobs() {
return this.actions.every((x) => x.content);
},
@@ -88,6 +103,8 @@ export default {
description: this.snippet.description,
visibilityLevel: this.snippet.visibilityLevel,
blobActions: this.actions,
+ ...(this.spamLogId && { spamLogId: this.spamLogId }),
+ ...(this.captchaResponse && { captchaResponse: this.captchaResponse }),
};
},
saveButtonLabel() {
@@ -116,7 +133,7 @@ export default {
onBeforeUnload(e = {}) {
const returnValue = __('Are you sure you want to lose unsaved changes?');
- if (!this.hasBlobChanges || this.isUpdating) return undefined;
+ if (!this.hasBlobChanges || this.hasNoChanges || this.isUpdating) return undefined;
Object.assign(e, { returnValue });
return returnValue;
@@ -159,6 +176,13 @@ export default {
.then(({ data }) => {
const baseObj = this.newSnippet ? data?.createSnippet : data?.updateSnippet;
+ if (baseObj.needsCaptchaResponse) {
+ // If we need a captcha response, start process for receiving captcha response.
+ // We will resubmit after the response is obtained.
+ this.requestCaptchaResponse(baseObj.captchaSiteKey, baseObj.spamLogId);
+ return;
+ }
+
const errors = baseObj?.errors;
if (errors.length) {
this.flashAPIFailure(errors[0]);
@@ -167,12 +191,44 @@ export default {
}
})
.catch((e) => {
- this.flashAPIFailure(e);
+ // eslint-disable-next-line no-console
+ console.error('[gitlab] unexpected error while updating snippet', e);
+
+ this.flashAPIFailure(getErrorMessage(e));
});
},
updateActions(actions) {
this.actions = actions;
},
+ /**
+ * Start process for getting captcha response from user
+ *
+ * @param captchaSiteKey Stored in data and used to display the captcha.
+ * @param spamLogId Stored in data and included when the form is re-submitted.
+ */
+ requestCaptchaResponse(captchaSiteKey, spamLogId) {
+ this.captchaSiteKey = captchaSiteKey;
+ this.spamLogId = spamLogId;
+ this.needsCaptchaResponse = true;
+ },
+ /**
+ * Handle the captcha response from the user
+ *
+ * @param captchaResponse The captchaResponse value emitted from the modal.
+ */
+ receivedCaptchaResponse(captchaResponse) {
+ this.needsCaptchaResponse = false;
+ this.captchaResponse = captchaResponse;
+
+ if (this.captchaResponse) {
+ // If the user solved the captcha resubmit the form.
+ this.handleFormSubmit();
+ } else {
+ // If the user didn't solve the captcha (e.g. they just closed the modal),
+ // finish the update and allow them to continue editing or manually resubmit the form.
+ this.isUpdating = false;
+ }
+ },
},
};
</script>
@@ -190,6 +246,11 @@ export default {
class="loading-animation prepend-top-20 gl-mb-6"
/>
<template v-else>
+ <captcha-modal
+ :captcha-site-key="captchaSiteKey"
+ :needs-captcha-response="needsCaptchaResponse"
+ @receivedCaptchaResponse="receivedCaptchaResponse"
+ />
<title-field
id="snippet-title"
v-model="snippet.title"
diff --git a/app/assets/javascripts/snippets/components/embed_dropdown.vue b/app/assets/javascripts/snippets/components/embed_dropdown.vue
index a5d2c337d67..f6c9c569b5f 100644
--- a/app/assets/javascripts/snippets/components/embed_dropdown.vue
+++ b/app/assets/javascripts/snippets/components/embed_dropdown.vue
@@ -1,5 +1,4 @@
<script>
-import { escape as esc } from 'lodash';
import {
GlButton,
GlDropdown,
@@ -8,6 +7,7 @@ import {
GlFormInputGroup,
GlTooltipDirective,
} from '@gitlab/ui';
+import { escape as esc } from 'lodash';
import { __ } from '~/locale';
const MSG_EMBED = __('Embed');
diff --git a/app/assets/javascripts/snippets/components/show.vue b/app/assets/javascripts/snippets/components/show.vue
index a3e5535c5fa..46629a569ec 100644
--- a/app/assets/javascripts/snippets/components/show.vue
+++ b/app/assets/javascripts/snippets/components/show.vue
@@ -1,20 +1,20 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
-import EmbedDropdown from './embed_dropdown.vue';
-import SnippetHeader from './snippet_header.vue';
-import SnippetTitle from './snippet_title.vue';
-import SnippetBlob from './snippet_blob_view.vue';
-import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue';
-import { SNIPPET_VISIBILITY_PUBLIC } from '~/snippets/constants';
+import eventHub from '~/blob/components/eventhub';
import {
SNIPPET_MARK_VIEW_APP_START,
SNIPPET_MEASURE_BLOBS_CONTENT,
} from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
-import eventHub from '~/blob/components/eventhub';
+import { SNIPPET_VISIBILITY_PUBLIC } from '~/snippets/constants';
+import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue';
import { getSnippetMixin } from '../mixins/snippets';
import { markBlobPerformance } from '../utils/blob';
+import EmbedDropdown from './embed_dropdown.vue';
+import SnippetBlob from './snippet_blob_view.vue';
+import SnippetHeader from './snippet_header.vue';
+import SnippetTitle from './snippet_title.vue';
eventHub.$on(SNIPPET_MEASURE_BLOBS_CONTENT, markBlobPerformance);
diff --git a/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue
index ff27c90a84d..d221195ddc7 100644
--- a/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue
@@ -2,9 +2,9 @@
import { GlButton } from '@gitlab/ui';
import { cloneDeep } from 'lodash';
import { s__, sprintf } from '~/locale';
-import SnippetBlobEdit from './snippet_blob_edit.vue';
import { SNIPPET_MAX_BLOBS } from '../constants';
import { createBlob, decorateBlob, diffAll } from '../utils/blob';
+import SnippetBlobEdit from './snippet_blob_edit.vue';
export default {
components: {
diff --git a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue
index c8545e334a6..4fb27397039 100644
--- a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue
@@ -1,12 +1,12 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import BlobHeaderEdit from '~/blob/components/blob_edit_header.vue';
-import EditorLite from '~/vue_shared/components/editor_lite.vue';
-import { getBaseURL, joinPaths } from '~/lib/utils/url_utility';
-import axios from '~/lib/utils/axios_utils';
-import { SNIPPET_BLOB_CONTENT_FETCH_ERROR } from '~/snippets/constants';
import { deprecatedCreateFlash as Flash } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import { getBaseURL, joinPaths } from '~/lib/utils/url_utility';
import { sprintf } from '~/locale';
+import { SNIPPET_BLOB_CONTENT_FETCH_ERROR } from '~/snippets/constants';
+import EditorLite from '~/vue_shared/components/editor_lite.vue';
export default {
components: {
diff --git a/app/assets/javascripts/snippets/components/snippet_blob_view.vue b/app/assets/javascripts/snippets/components/snippet_blob_view.vue
index 4326c3c3159..27b3a30b40a 100644
--- a/app/assets/javascripts/snippets/components/snippet_blob_view.vue
+++ b/app/assets/javascripts/snippets/components/snippet_blob_view.vue
@@ -1,8 +1,8 @@
<script>
import GetBlobContent from 'shared_queries/snippet/snippet_blob_content.query.graphql';
-import BlobHeader from '~/blob/components/blob_header.vue';
import BlobContent from '~/blob/components/blob_content.vue';
+import BlobHeader from '~/blob/components/blob_header.vue';
import {
SIMPLE_BLOB_VIEWER,
@@ -31,8 +31,10 @@ export default {
},
result() {
if (this.activeViewerType === RICH_BLOB_VIEWER) {
+ // eslint-disable-next-line vue/no-mutating-props
this.blob.richViewer.renderError = null;
} else {
+ // eslint-disable-next-line vue/no-mutating-props
this.blob.simpleViewer.renderError = null;
}
},
diff --git a/app/assets/javascripts/snippets/components/snippet_description_edit.vue b/app/assets/javascripts/snippets/components/snippet_description_edit.vue
index 5e6caf27bdd..bac423f6838 100644
--- a/app/assets/javascripts/snippets/components/snippet_description_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_description_edit.vue
@@ -1,7 +1,7 @@
<script>
import { GlFormInput } from '@gitlab/ui';
-import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import setupCollapsibleInputs from '~/snippet/collapsible_input';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
export default {
components: {
diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue
index 5ba62908b43..bf19b63650e 100644
--- a/app/assets/javascripts/snippets/components/snippet_header.vue
+++ b/app/assets/javascripts/snippets/components/snippet_header.vue
@@ -11,14 +11,14 @@ import {
GlButton,
GlTooltipDirective,
} from '@gitlab/ui';
-import CanCreatePersonalSnippet from 'shared_queries/snippet/user_permissions.query.graphql';
import CanCreateProjectSnippet from 'shared_queries/snippet/project_permissions.query.graphql';
+import CanCreatePersonalSnippet from 'shared_queries/snippet/user_permissions.query.graphql';
+import { fetchPolicies } from '~/lib/graphql';
+import { joinPaths } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import DeleteSnippetMutation from '../mutations/deleteSnippet.mutation.graphql';
-import { joinPaths } from '~/lib/utils/url_utility';
-import { fetchPolicies } from '~/lib/graphql';
export default {
components: {
diff --git a/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue b/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue
index ee5076835ab..18a7d4ad218 100644
--- a/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue
@@ -1,7 +1,7 @@
<script>
import { GlIcon, GlFormGroup, GlFormRadio, GlFormRadioGroup, GlLink } from '@gitlab/ui';
-import { defaultSnippetVisibilityLevels } from '../utils/blob';
import { SNIPPET_LEVELS_RESTRICTED, SNIPPET_LEVELS_DISABLED } from '~/snippets/constants';
+import { defaultSnippetVisibilityLevels } from '../utils/blob';
export default {
components: {
diff --git a/app/assets/javascripts/snippets/index.js b/app/assets/javascripts/snippets/index.js
index 853ccb0c2fe..789332ce5b7 100644
--- a/app/assets/javascripts/snippets/index.js
+++ b/app/assets/javascripts/snippets/index.js
@@ -1,9 +1,9 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import Translate from '~/vue_shared/translate';
import createDefaultClient from '~/lib/graphql';
import { SNIPPET_LEVELS_MAP, SNIPPET_VISIBILITY_PRIVATE } from '~/snippets/constants';
+import Translate from '~/vue_shared/translate';
Vue.use(VueApollo);
Vue.use(Translate);
diff --git a/app/assets/javascripts/snippets/mixins/snippets.js b/app/assets/javascripts/snippets/mixins/snippets.js
index 89a88958152..7552eae97fc 100644
--- a/app/assets/javascripts/snippets/mixins/snippets.js
+++ b/app/assets/javascripts/snippets/mixins/snippets.js
@@ -11,10 +11,14 @@ export const getSnippetMixin = {
ids: [this.snippetGid],
};
},
- update: (data) => {
+ update(data) {
const res = data.snippets.nodes[0];
+
+ // Set `snippet.blobs` since some child components are coupled to this.
if (res) {
- res.blobs = res.blobs.nodes;
+ // It's possible for us to not get any blobs in a response.
+ // In this case, we should default to current blobs.
+ res.blobs = res.blobs ? res.blobs.nodes : this.blobs;
}
return res;
diff --git a/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql b/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql
index f688868d1b9..64d5d7c30fa 100644
--- a/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql
+++ b/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql
@@ -4,5 +4,7 @@ mutation CreateSnippet($input: CreateSnippetInput!) {
snippet {
webUrl
}
+ needsCaptchaResponse
+ captchaSiteKey
}
}
diff --git a/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql b/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql
index 548725f7357..0a72f71b7c9 100644
--- a/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql
+++ b/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql
@@ -4,5 +4,8 @@ mutation UpdateSnippet($input: UpdateSnippetInput!) {
snippet {
webUrl
}
+ needsCaptchaResponse
+ captchaSiteKey
+ spamLogId
}
}
diff --git a/app/assets/javascripts/snippets/utils/blob.js b/app/assets/javascripts/snippets/utils/blob.js
index a47418323f2..2a3f590a803 100644
--- a/app/assets/javascripts/snippets/utils/blob.js
+++ b/app/assets/javascripts/snippets/utils/blob.js
@@ -1,4 +1,6 @@
import { uniqueId } from 'lodash';
+import { SNIPPET_MARK_BLOBS_CONTENT, SNIPPET_MEASURE_BLOBS_CONTENT } from '~/performance/constants';
+import { performanceMarkAndMeasure } from '~/performance/utils';
import {
SNIPPET_BLOB_ACTION_CREATE,
SNIPPET_BLOB_ACTION_UPDATE,
@@ -7,8 +9,6 @@ import {
SNIPPET_LEVELS_MAP,
SNIPPET_VISIBILITY,
} from '../constants';
-import { performanceMarkAndMeasure } from '~/performance/utils';
-import { SNIPPET_MARK_BLOBS_CONTENT, SNIPPET_MEASURE_BLOBS_CONTENT } from '~/performance/constants';
const createLocalId = () => uniqueId('blob_local_');
diff --git a/app/assets/javascripts/snippets/utils/error.js b/app/assets/javascripts/snippets/utils/error.js
new file mode 100644
index 00000000000..2d5c2a64038
--- /dev/null
+++ b/app/assets/javascripts/snippets/utils/error.js
@@ -0,0 +1,15 @@
+import { isString } from 'lodash';
+import { __ } from '~/locale';
+
+export const UNEXPECTED_ERROR = __('Unexpected error');
+
+export const getErrorMessage = (e) => {
+ if (!e) {
+ return UNEXPECTED_ERROR;
+ }
+ if (isString(e)) {
+ return e;
+ }
+
+ return e.message || e.networkError || UNEXPECTED_ERROR;
+};