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
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 12:08:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 12:08:42 +0300
commitb76ae638462ab0f673e5915986070518dd3f9ad3 (patch)
treebdab0533383b52873be0ec0eb4d3c66598ff8b91 /app/assets/javascripts/repository
parent434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff)
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'app/assets/javascripts/repository')
-rw-r--r--app/assets/javascripts/repository/components/blob_button_group.vue29
-rw-r--r--app/assets/javascripts/repository/components/blob_content_viewer.vue34
-rw-r--r--app/assets/javascripts/repository/components/blob_edit.vue22
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue3
-rw-r--r--app/assets/javascripts/repository/components/delete_blob_modal.vue99
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue1
-rw-r--r--app/assets/javascripts/repository/constants.js3
-rw-r--r--app/assets/javascripts/repository/mutations/lock_path.mutation.graphql13
-rw-r--r--app/assets/javascripts/repository/queries/blob_info.query.graphql11
9 files changed, 190 insertions, 25 deletions
diff --git a/app/assets/javascripts/repository/components/blob_button_group.vue b/app/assets/javascripts/repository/components/blob_button_group.vue
index 273825b996a..4e7ca7b17e4 100644
--- a/app/assets/javascripts/repository/components/blob_button_group.vue
+++ b/app/assets/javascripts/repository/components/blob_button_group.vue
@@ -2,6 +2,7 @@
import { GlButtonGroup, GlButton, GlModalDirective } from '@gitlab/ui';
import { uniqueId } from 'lodash';
import { sprintf, __ } from '~/locale';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import getRefMixin from '../mixins/get_ref';
import DeleteBlobModal from './delete_blob_modal.vue';
import UploadBlobModal from './upload_blob_modal.vue';
@@ -17,11 +18,12 @@ export default {
GlButton,
UploadBlobModal,
DeleteBlobModal,
+ LockButton: () => import('ee_component/repository/components/lock_button.vue'),
},
directives: {
GlModal: GlModalDirective,
},
- mixins: [getRefMixin],
+ mixins: [getRefMixin, glFeatureFlagMixin()],
inject: {
targetBranch: {
default: '',
@@ -55,6 +57,18 @@ export default {
type: Boolean,
required: true,
},
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ isLocked: {
+ type: Boolean,
+ required: true,
+ },
+ canLock: {
+ type: Boolean,
+ required: true,
+ },
},
computed: {
replaceModalId() {
@@ -76,10 +90,19 @@ export default {
<template>
<div class="gl-mr-3">
<gl-button-group>
- <gl-button v-gl-modal="replaceModalId">
+ <lock-button
+ v-if="glFeatures.fileLocks"
+ :name="name"
+ :path="path"
+ :project-path="projectPath"
+ :is-locked="isLocked"
+ :can-lock="canLock"
+ data-testid="lock"
+ />
+ <gl-button v-gl-modal="replaceModalId" data-testid="replace">
{{ $options.i18n.replace }}
</gl-button>
- <gl-button v-gl-modal="deleteModalId">
+ <gl-button v-gl-modal="deleteModalId" data-testid="delete">
{{ $options.i18n.delete }}
</gl-button>
</gl-button-group>
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue
index 09ac60c94c7..665b0698cc0 100644
--- a/app/assets/javascripts/repository/components/blob_content_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue
@@ -8,6 +8,7 @@ import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
+import getRefMixin from '../mixins/get_ref';
import blobInfoQuery from '../queries/blob_info.query.graphql';
import BlobButtonGroup from './blob_button_group.vue';
import BlobEdit from './blob_edit.vue';
@@ -21,6 +22,12 @@ export default {
BlobContent,
GlLoadingIcon,
},
+ mixins: [getRefMixin],
+ inject: {
+ originalBranch: {
+ default: '',
+ },
+ },
apollo: {
project: {
query: blobInfoQuery,
@@ -28,6 +35,7 @@ export default {
return {
projectPath: this.projectPath,
filePath: this.path,
+ ref: this.originalBranch || this.ref,
};
},
result() {
@@ -67,6 +75,10 @@ export default {
project: {
userPermissions: {
pushCode: false,
+ downloadCode: false,
+ },
+ pathLocks: {
+ nodes: [],
},
repository: {
empty: true,
@@ -87,9 +99,6 @@ export default {
externalStorageUrl: '',
replacePath: '',
deletePath: '',
- canLock: false,
- isLocked: false,
- lockLink: '',
forkPath: '',
simpleViewer: {},
richViewer: null,
@@ -108,8 +117,11 @@ export default {
isLoading() {
return this.$apollo.queries.project.loading || this.isLoadingLegacyViewer;
},
+ isBinaryFileType() {
+ return this.isBinary || this.viewer.fileType === 'download';
+ },
blobInfo() {
- const nodes = this.project?.repository?.blobs?.nodes;
+ const nodes = this.project?.repository?.blobs?.nodes || [];
return nodes[0] || {};
},
@@ -131,6 +143,14 @@ export default {
const { fileType } = this.viewer;
return viewerProps(fileType, this.blobInfo);
},
+ canLock() {
+ const { pushCode, downloadCode } = this.project.userPermissions;
+
+ return pushCode && downloadCode;
+ },
+ isLocked() {
+ return this.project.pathLocks.nodes.some((node) => node.path === this.path);
+ },
},
methods: {
loadLegacyViewer() {
@@ -161,13 +181,14 @@ export default {
<blob-header
:blob="blobInfo"
:hide-viewer-switcher="!hasRichViewer || isBinary"
+ :is-binary="isBinaryFileType"
:active-viewer-type="viewer.type"
:has-render-error="hasRenderError"
@viewer-changed="switchViewer"
>
<template #actions>
<blob-edit
- v-if="!isBinary"
+ :show-edit-button="!isBinary"
:edit-path="blobInfo.editBlobPath"
:web-ide-path="blobInfo.ideEditPath"
/>
@@ -179,6 +200,9 @@ export default {
:delete-path="blobInfo.webPath"
:can-push-code="project.userPermissions.pushCode"
:empty-repo="project.repository.empty"
+ :project-path="projectPath"
+ :is-locked="isLocked"
+ :can-lock="canLock"
/>
</template>
</blob-header>
diff --git a/app/assets/javascripts/repository/components/blob_edit.vue b/app/assets/javascripts/repository/components/blob_edit.vue
index 3d97ebe89e4..30ed4cd57f1 100644
--- a/app/assets/javascripts/repository/components/blob_edit.vue
+++ b/app/assets/javascripts/repository/components/blob_edit.vue
@@ -15,6 +15,10 @@ export default {
},
mixins: [glFeatureFlagsMixin()],
props: {
+ showEditButton: {
+ type: Boolean,
+ required: true,
+ },
editPath: {
type: String,
required: true,
@@ -30,17 +34,31 @@ export default {
<template>
<web-ide-link
v-if="glFeatures.consolidatedEditButton"
+ :show-edit-button="showEditButton"
class="gl-mr-3"
:edit-url="editPath"
:web-ide-url="webIdePath"
:is-blob="true"
/>
<div v-else>
- <gl-button class="gl-mr-2" category="primary" variant="confirm" :href="editPath">
+ <gl-button
+ v-if="showEditButton"
+ class="gl-mr-2"
+ category="primary"
+ variant="confirm"
+ :href="editPath"
+ data-testid="edit"
+ >
{{ $options.i18n.edit }}
</gl-button>
- <gl-button class="gl-mr-3" category="primary" variant="confirm" :href="webIdePath">
+ <gl-button
+ class="gl-mr-3"
+ category="primary"
+ variant="confirm"
+ :href="webIdePath"
+ data-testid="web-ide"
+ >
{{ $options.i18n.webIde }}
</gl-button>
</div>
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
index 0b8408643ac..db84e2b5912 100644
--- a/app/assets/javascripts/repository/components/breadcrumbs.vue
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -247,7 +247,8 @@ export default {
return items;
},
renderAddToTreeDropdown() {
- return this.canCollaborate || this.canCreateMrFromFork;
+ const isBlobPath = this.$route.name === 'blobPath' || this.$route.name === 'blobPathDecoded';
+ return !isBlobPath && (this.canCollaborate || this.canCreateMrFromFork);
},
},
methods: {
diff --git a/app/assets/javascripts/repository/components/delete_blob_modal.vue b/app/assets/javascripts/repository/components/delete_blob_modal.vue
index 6599d99d7bd..a307b7c0b8a 100644
--- a/app/assets/javascripts/repository/components/delete_blob_modal.vue
+++ b/app/assets/javascripts/repository/components/delete_blob_modal.vue
@@ -1,14 +1,24 @@
<script>
-import { GlModal, GlFormGroup, GlFormInput, GlFormTextarea, GlToggle } from '@gitlab/ui';
+import { GlModal, GlFormGroup, GlFormInput, GlFormTextarea, GlToggle, GlForm } from '@gitlab/ui';
import csrf from '~/lib/utils/csrf';
import { __ } from '~/locale';
+import validation from '~/vue_shared/directives/validation';
import {
SECONDARY_OPTIONS_TEXT,
COMMIT_LABEL,
TARGET_BRANCH_LABEL,
TOGGLE_CREATE_MR_LABEL,
+ COMMIT_MESSAGE_SUBJECT_MAX_LENGTH,
+ COMMIT_MESSAGE_BODY_MAX_LENGTH,
} from '../constants';
+const initFormField = ({ value, required = true, skipValidation = false }) => ({
+ value,
+ required,
+ state: skipValidation ? true : null,
+ feedback: null,
+});
+
export default {
csrf,
components: {
@@ -17,6 +27,7 @@ export default {
GlFormInput,
GlFormTextarea,
GlToggle,
+ GlForm,
},
i18n: {
PRIMARY_OPTIONS_TEXT: __('Delete file'),
@@ -24,6 +35,12 @@ export default {
COMMIT_LABEL,
TARGET_BRANCH_LABEL,
TOGGLE_CREATE_MR_LABEL,
+ COMMIT_MESSAGE_HINT: __(
+ 'Try to keep the first line under 52 characters and the others under 72.',
+ ),
+ },
+ directives: {
+ validation: validation(),
},
props: {
modalId: {
@@ -60,12 +77,20 @@ export default {
},
},
data() {
+ const form = {
+ state: false,
+ showValidation: false,
+ fields: {
+ // fields key must match case of form name for validation directive to work
+ commit_message: initFormField({ value: this.commitMessage }),
+ branch_name: initFormField({ value: this.targetBranch }),
+ },
+ };
return {
loading: false,
- commit: this.commitMessage,
- target: this.targetBranch,
createNewMr: true,
error: '',
+ form,
};
},
computed: {
@@ -76,7 +101,7 @@ export default {
{
variant: 'danger',
loading: this.loading,
- disabled: !this.formCompleted || this.loading,
+ disabled: this.loading || !this.form.state,
},
],
};
@@ -91,18 +116,44 @@ export default {
],
};
},
+ /* eslint-disable dot-notation */
showCreateNewMrToggle() {
- return this.canPushCode && this.target !== this.originalBranch;
+ return this.canPushCode && this.form.fields['branch_name'].value !== this.originalBranch;
},
formCompleted() {
- return this.commit && this.target;
+ return this.form.fields['commit_message'].value && this.form.fields['branch_name'].value;
},
+ showHint() {
+ const splitCommitMessageByLineBreak = this.form.fields['commit_message'].value
+ .trim()
+ .split('\n');
+ const [firstLine, ...otherLines] = splitCommitMessageByLineBreak;
+
+ const hasFirstLineExceedMaxLength = firstLine.length > COMMIT_MESSAGE_SUBJECT_MAX_LENGTH;
+
+ const hasOtherLineExceedMaxLength =
+ Boolean(otherLines.length) &&
+ otherLines.some((text) => text.length > COMMIT_MESSAGE_BODY_MAX_LENGTH);
+
+ return (
+ !this.form.fields['commit_message'].feedback &&
+ (hasFirstLineExceedMaxLength || hasOtherLineExceedMaxLength)
+ );
+ },
+ /* eslint-enable dot-notation */
},
methods: {
submitForm(e) {
e.preventDefault(); // Prevent modal from closing
+ this.form.showValidation = true;
+
+ if (!this.form.state) {
+ return;
+ }
+
this.loading = true;
- this.$refs.form.submit();
+ this.form.showValidation = false;
+ this.$refs.form.$el.submit();
},
},
};
@@ -110,13 +161,15 @@ export default {
<template>
<gl-modal
+ v-bind="$attrs"
+ data-testid="modal-delete"
:modal-id="modalId"
:title="modalTitle"
:action-primary="primaryOptions"
:action-cancel="cancelOptions"
@primary="submitForm"
>
- <form ref="form" :action="deletePath" method="post">
+ <gl-form ref="form" novalidate :action="deletePath" method="post">
<input type="hidden" name="_method" value="delete" />
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
<template v-if="emptyRepo">
@@ -129,15 +182,37 @@ export default {
<!-- Once "push to branch" permission is made available, will need to add to conditional
Follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/335462 -->
<input v-if="createNewMr" type="hidden" name="create_merge_request" value="1" />
- <gl-form-group :label="$options.i18n.COMMIT_LABEL" label-for="commit_message">
- <gl-form-textarea v-model="commit" name="commit_message" :disabled="loading" />
+ <gl-form-group
+ :label="$options.i18n.COMMIT_LABEL"
+ label-for="commit_message"
+ :invalid-feedback="form.fields['commit_message'].feedback"
+ >
+ <gl-form-textarea
+ v-model="form.fields['commit_message'].value"
+ v-validation:[form.showValidation]
+ name="commit_message"
+ :state="form.fields['commit_message'].state"
+ :disabled="loading"
+ required
+ />
+ <p v-if="showHint" class="form-text gl-text-gray-600" data-testid="hint">
+ {{ $options.i18n.COMMIT_MESSAGE_HINT }}
+ </p>
</gl-form-group>
<gl-form-group
v-if="canPushCode"
:label="$options.i18n.TARGET_BRANCH_LABEL"
label-for="branch_name"
+ :invalid-feedback="form.fields['branch_name'].feedback"
>
- <gl-form-input v-model="target" :disabled="loading" name="branch_name" />
+ <gl-form-input
+ v-model="form.fields['branch_name'].value"
+ v-validation:[form.showValidation]
+ :state="form.fields['branch_name'].state"
+ :disabled="loading"
+ name="branch_name"
+ required
+ />
</gl-form-group>
<gl-toggle
v-if="showCreateNewMrToggle"
@@ -146,6 +221,6 @@ export default {
:label="$options.i18n.TOGGLE_CREATE_MR_LABEL"
/>
</template>
- </form>
+ </gl-form>
</gl-modal>
</template>
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index 82c18d13a6a..fa358a75cc1 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -170,6 +170,7 @@ export default {
this.apolloQuery(blobInfoQuery, {
projectPath: this.projectPath,
filePath: this.path,
+ ref: this.ref,
});
},
apolloQuery(query, variables) {
diff --git a/app/assets/javascripts/repository/constants.js b/app/assets/javascripts/repository/constants.js
index 2d2faa8d9f3..b536bcb1875 100644
--- a/app/assets/javascripts/repository/constants.js
+++ b/app/assets/javascripts/repository/constants.js
@@ -8,3 +8,6 @@ export const SECONDARY_OPTIONS_TEXT = __('Cancel');
export const COMMIT_LABEL = __('Commit message');
export const TARGET_BRANCH_LABEL = __('Target branch');
export const TOGGLE_CREATE_MR_LABEL = __('Start a new merge request with these changes');
+
+export const COMMIT_MESSAGE_SUBJECT_MAX_LENGTH = 52;
+export const COMMIT_MESSAGE_BODY_MAX_LENGTH = 72;
diff --git a/app/assets/javascripts/repository/mutations/lock_path.mutation.graphql b/app/assets/javascripts/repository/mutations/lock_path.mutation.graphql
new file mode 100644
index 00000000000..eaebc4ddf17
--- /dev/null
+++ b/app/assets/javascripts/repository/mutations/lock_path.mutation.graphql
@@ -0,0 +1,13 @@
+mutation toggleLock($projectPath: ID!, $filePath: String!, $lock: Boolean!) {
+ projectSetLocked(input: { projectPath: $projectPath, filePath: $filePath, lock: $lock }) {
+ project {
+ id
+ pathLocks {
+ nodes {
+ path
+ }
+ }
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/repository/queries/blob_info.query.graphql b/app/assets/javascripts/repository/queries/blob_info.query.graphql
index a8f263941e2..45f07f7dc58 100644
--- a/app/assets/javascripts/repository/queries/blob_info.query.graphql
+++ b/app/assets/javascripts/repository/queries/blob_info.query.graphql
@@ -1,11 +1,18 @@
-query getBlobInfo($projectPath: ID!, $filePath: String!) {
+query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
project(fullPath: $projectPath) {
+ id
userPermissions {
pushCode
+ downloadCode
+ }
+ pathLocks {
+ nodes {
+ path
+ }
}
repository {
empty
- blobs(paths: [$filePath]) {
+ blobs(paths: [$filePath], ref: $ref) {
nodes {
webPath
name