diff options
Diffstat (limited to 'app')
272 files changed, 1556 insertions, 1323 deletions
diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue index 8512bf9dd7b..ca1662313c2 100644 --- a/app/assets/javascripts/badges/components/badge.vue +++ b/app/assets/javascripts/badges/components/badge.vue @@ -1,7 +1,7 @@ <script> import Icon from '~/vue_shared/components/icon.vue'; import Tooltip from '~/vue_shared/directives/tooltip'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { name: 'Badge', diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue index 47e6e618219..0eff922d93c 100644 --- a/app/assets/javascripts/badges/components/badge_form.vue +++ b/app/assets/javascripts/badges/components/badge_form.vue @@ -4,7 +4,7 @@ import { mapActions, mapState } from 'vuex'; import createFlash from '~/flash'; import { s__, sprintf } from '~/locale'; import LoadingButton from '~/vue_shared/components/loading_button.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import createEmptyBadge from '../empty_badge'; import Badge from './badge.vue'; diff --git a/app/assets/javascripts/badges/components/badge_list.vue b/app/assets/javascripts/badges/components/badge_list.vue index ab518820378..5b64ea1080c 100644 --- a/app/assets/javascripts/badges/components/badge_list.vue +++ b/app/assets/javascripts/badges/components/badge_list.vue @@ -1,6 +1,6 @@ <script> import { mapState } from 'vuex'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import BadgeListRow from './badge_list_row.vue'; import { GROUP_BADGE } from '../constants'; diff --git a/app/assets/javascripts/badges/components/badge_list_row.vue b/app/assets/javascripts/badges/components/badge_list_row.vue index f28eff18f03..d01465232a7 100644 --- a/app/assets/javascripts/badges/components/badge_list_row.vue +++ b/app/assets/javascripts/badges/components/badge_list_row.vue @@ -2,7 +2,7 @@ import { mapActions, mapState } from 'vuex'; import { s__ } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import { PROJECT_BADGE } from '../constants'; import Badge from './badge.vue'; diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue index 5e28fc396ab..a04b828e277 100644 --- a/app/assets/javascripts/boards/components/board_list.vue +++ b/app/assets/javascripts/boards/components/board_list.vue @@ -1,6 +1,6 @@ <script> import Sortable from 'sortablejs'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import boardNewIssue from './board_new_issue.vue'; import boardCard from './board_card.vue'; import eventHub from '../eventhub'; diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index ee3dc38bca6..2a96d1443e1 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -1,6 +1,6 @@ <script> import $ from 'jquery'; -import { GlButton } from '@gitlab-org/gitlab-ui'; +import { GlButton } from '@gitlab/ui'; import eventHub from '../eventhub'; import ProjectSelect from './project_select.vue'; import ListIssue from '../models/issue'; diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue index 2315a48a306..48c46ae32f1 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import { sprintf, __ } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; @@ -231,7 +231,7 @@ export default { > <span class="js-assignee-tooltip"> <span class="bold d-block">Assignee</span> - {{ assignee.name }} + {{ assignee.name }} <span class="text-white-50">@{{ assignee.username }}</span> </span> </user-avatar-link> diff --git a/app/assets/javascripts/boards/components/issue_due_date.vue b/app/assets/javascripts/boards/components/issue_due_date.vue index 025ef7e9743..d6c87132843 100644 --- a/app/assets/javascripts/boards/components/issue_due_date.vue +++ b/app/assets/javascripts/boards/components/issue_due_date.vue @@ -1,6 +1,6 @@ <script> import dateFormat from 'dateformat'; -import { GlTooltip } from '@gitlab-org/gitlab-ui'; +import { GlTooltip } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import { __ } from '~/locale'; import { getDayDifference, getTimeago, dateInWords } from '~/lib/utils/datetime_utility'; diff --git a/app/assets/javascripts/boards/components/issue_time_estimate.vue b/app/assets/javascripts/boards/components/issue_time_estimate.vue index efc7daf7812..a9803c8ab5d 100644 --- a/app/assets/javascripts/boards/components/issue_time_estimate.vue +++ b/app/assets/javascripts/boards/components/issue_time_estimate.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltip } from '@gitlab-org/gitlab-ui'; +import { GlTooltip } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility'; diff --git a/app/assets/javascripts/boards/components/modal/index.vue b/app/assets/javascripts/boards/components/modal/index.vue index fdd1346d4c7..fc0e3e46b88 100644 --- a/app/assets/javascripts/boards/components/modal/index.vue +++ b/app/assets/javascripts/boards/components/modal/index.vue @@ -6,7 +6,7 @@ import ModalList from './list.vue'; import ModalFooter from './footer.vue'; import EmptyState from './empty_state.vue'; import ModalStore from '../../stores/modal_store'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/boards/components/modal/lists_dropdown.vue b/app/assets/javascripts/boards/components/modal/lists_dropdown.vue index 20665f903d5..c502e32d169 100644 --- a/app/assets/javascripts/boards/components/modal/lists_dropdown.vue +++ b/app/assets/javascripts/boards/components/modal/lists_dropdown.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import ModalStore from '../../stores/modal_store'; import boardsStore from '../../stores/boards_store'; diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue index 503417644fa..83e6e237757 100644 --- a/app/assets/javascripts/boards/components/project_select.vue +++ b/app/assets/javascripts/boards/components/project_select.vue @@ -2,7 +2,7 @@ import $ from 'jquery'; import _ from 'underscore'; import Icon from '~/vue_shared/components/icon.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import eventHub from '../eventhub'; import Api from '../../api'; diff --git a/app/assets/javascripts/deploy_keys/components/action_btn.vue b/app/assets/javascripts/deploy_keys/components/action_btn.vue index ea74fd27ff6..4acd21376bf 100644 --- a/app/assets/javascripts/deploy_keys/components/action_btn.vue +++ b/app/assets/javascripts/deploy_keys/components/action_btn.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import eventHub from '../eventhub'; export default { diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index 631a9673b3e..1d2ac59e20a 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -6,7 +6,7 @@ import eventHub from '../eventhub'; import DeployKeysService from '../service'; import DeployKeysStore from '../store'; import KeysPanel from './keys_panel.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js index c0c21416275..8542a6e718a 100644 --- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js +++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js @@ -112,7 +112,7 @@ const JumpToDiscussion = Vue.extend({ if (!hasDiscussionsToJumpTo) { // If there are no discussions to jump to on the current page, - // switch to the notes tab and jump to the first disucssion there. + // switch to the notes tab and jump to the first discussion there. window.mrTabs.activateTab('show'); activeTab = 'show'; jumpToFirstDiscussion = true; diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 7c60fb3da42..881febedb7c 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -3,7 +3,7 @@ import { mapState, mapGetters, mapActions } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; import { __ } from '~/locale'; import createFlash from '~/flash'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import eventHub from '../../notes/event_hub'; import CompareVersions from './compare_versions.vue'; import DiffFile from './diff_file.vue'; @@ -94,7 +94,7 @@ export default { return __('Show latest version'); }, canCurrentUserFork() { - return this.currentUser.canFork === true && this.currentUser.canCreateMergeRequest; + return this.currentUser.can_fork === true && this.currentUser.can_create_merge_request; }, showCompareVersions() { return this.mergeRequestDiffs && this.mergeRequestDiff; @@ -128,6 +128,7 @@ export default { eventHub.$once('fetchedNotesData', this.setDiscussions); }, methods: { + ...mapActions(['startTaskList']), ...mapActions('diffs', [ 'setBaseConfig', 'fetchDiffFiles', @@ -157,7 +158,13 @@ export default { if (this.isNotesFetched && !this.assignedDiscussions && !this.isLoading) { this.assignedDiscussions = true; - requestIdleCallback(() => this.assignDiscussionsToDiff(), { timeout: 1000 }); + requestIdleCallback( + () => + this.assignDiscussionsToDiff() + .then(this.$nextTick) + .then(this.startTaskList), + { timeout: 1000 }, + ); } }, adjustView() { diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue index 23d0bad2ecb..aa72aca1478 100644 --- a/app/assets/javascripts/diffs/components/commit_item.vue +++ b/app/assets/javascripts/diffs/components/commit_item.vue @@ -40,15 +40,17 @@ export default { }, computed: { authorName() { - return (this.commit.author && this.commit.author.name) || this.commit.authorName; + return (this.commit.author && this.commit.author.name) || this.commit.author_name; }, authorUrl() { return ( - (this.commit.author && this.commit.author.webUrl) || `mailto:${this.commit.authorEmail}` + (this.commit.author && this.commit.author.web_url) || `mailto:${this.commit.author_email}` ); }, authorAvatar() { - return (this.commit.author && this.commit.author.avatarUrl) || this.commit.authorGravatarUrl; + return ( + (this.commit.author && this.commit.author.avatar_url) || this.commit.author_gravatar_url + ); }, }, }; @@ -66,18 +68,18 @@ export default { <div class="commit-detail flex-list"> <div class="commit-content qa-commit-content"> <a - :href="commit.commitUrl" + :href="commit.commit_url" class="commit-row-message item-title" - v-html="commit.titleHtml" + v-html="commit.title_html" ></a> <span class="commit-row-message d-block d-sm-none"> · - {{ commit.shortId }} + {{ commit.short_id }} </span> <button - v-if="commit.descriptionHtml" + v-if="commit.description_html" class="text-expander js-toggle-button" type="button" :aria-label="__('Toggle commit description')" @@ -95,29 +97,29 @@ export default { ></a> {{ s__('CommitWidget|authored') }} <time-ago-tooltip - :time="commit.authoredDate" + :time="commit.authored_date" /> </div> <pre - v-if="commit.descriptionHtml" + v-if="commit.description_html" class="commit-row-description js-toggle-content append-bottom-8" - v-html="commit.descriptionHtml" + v-html="commit.description_html" ></pre> </div> <div class="commit-actions flex-row d-none d-sm-flex"> <div - v-if="commit.signatureHtml" - v-html="commit.signatureHtml" + v-if="commit.signature_html" + v-html="commit.signature_html" ></div> <commit-pipeline-status - v-if="commit.pipelineStatusPath" - :endpoint="commit.pipelineStatusPath" + v-if="commit.pipeline_status_path" + :endpoint="commit.pipeline_status_path" /> <div class="commit-sha-group"> <div class="label label-monospace" - v-text="commit.shortId" + v-text="commit.short_id" ></div> <clipboard-button :text="commit.id" diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue index a5b87dfc2d9..f50cb94a18f 100644 --- a/app/assets/javascripts/diffs/components/compare_versions.vue +++ b/app/assets/javascripts/diffs/components/compare_versions.vue @@ -1,6 +1,6 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; -import Tooltip from '@gitlab-org/gitlab-ui/dist/directives/tooltip'; +import Tooltip from '@gitlab/ui/dist/directives/tooltip'; import { __ } from '~/locale'; import { getParameterValues, mergeUrlParams } from '~/lib/utils/url_utility'; import Icon from '~/vue_shared/components/icon.vue'; diff --git a/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue b/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue index f4b333f3700..112206e4ad6 100644 --- a/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue +++ b/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue @@ -56,16 +56,16 @@ export default { methods: { commitsText(version) { return n__( - `${version.commitsCount} commit,`, - `${version.commitsCount} commits,`, - version.commitsCount, + `${version.commits_count} commit,`, + `${version.commits_count} commits,`, + version.commits_count, ); }, href(version) { if (this.showCommitCount) { - return version.versionPath; + return version.version_path; } - return version.comparePath; + return version.compare_path; }, versionName(version) { if (this.isLatest(version)) { @@ -74,7 +74,7 @@ export default { if (this.targetBranch && (this.isBase(version) || !version)) { return this.targetBranch.branchName; } - return `version ${version.versionIndex}`; + return `version ${version.version_index}`; }, isActive(version) { if (!version) { @@ -84,11 +84,11 @@ export default { if (this.targetBranch) { return ( (this.isBase(version) && !this.startVersion) || - (this.startVersion && this.startVersion.versionIndex === version.versionIndex) + (this.startVersion && this.startVersion.version_index === version.version_index) ); } - return version.versionIndex === this.mergeRequestVersion.versionIndex; + return version.version_index === this.mergeRequestVersion.version_index; }, isBase(version) { if (!version || !this.targetBranch) { @@ -98,7 +98,7 @@ export default { }, isLatest(version) { return ( - this.mergeRequestVersion && version.versionIndex === this.targetVersions[0].versionIndex + this.mergeRequestVersion && version.version_index === this.targetVersions[0].version_index ); }, }, @@ -142,7 +142,7 @@ export default { </div> <div> <small class="commit-sha"> - {{ version.truncatedCommitSha }} + {{ version.truncated_commit_sha }} </small> </div> <div> @@ -151,8 +151,8 @@ export default { {{ commitsText(version) }} </template> <time-ago - v-if="version.createdAt" - :time="version.createdAt" + v-if="version.created_at" + :time="version.created_at" class="js-timeago js-timeago-render" /> </small> diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue index 547742a5ff4..5e5fda5fba6 100644 --- a/app/assets/javascripts/diffs/components/diff_content.vue +++ b/app/assets/javascripts/diffs/components/diff_content.vue @@ -39,7 +39,7 @@ export default { return this.diffFile.viewer.name === 'text'; }, diffFileCommentForm() { - return this.getCommentFormForDiffFile(this.diffFile.fileHash); + return this.getCommentFormForDiffFile(this.diffFile.file_hash); }, showNotesContainer() { return this.diffFile.discussions.length || this.diffFileCommentForm; @@ -73,28 +73,28 @@ export default { <inline-diff-view v-if="isInlineView" :diff-file="diffFile" - :diff-lines="diffFile.highlightedDiffLines || []" + :diff-lines="diffFile.highlighted_diff_lines || []" /> <parallel-diff-view v-if="isParallelView" :diff-file="diffFile" - :diff-lines="diffFile.parallelDiffLines || []" + :diff-lines="diffFile.parallel_diff_lines || []" /> </template> <diff-viewer v-else :diff-mode="diffMode" - :new-path="diffFile.newPath" - :new-sha="diffFile.diffRefs.headSha" - :old-path="diffFile.oldPath" - :old-sha="diffFile.diffRefs.baseSha" - :file-hash="diffFile.fileHash" + :new-path="diffFile.new_path" + :new-sha="diffFile.diff_refs.head_sha" + :old-path="diffFile.old_path" + :old-sha="diffFile.diff_refs.base_sha" + :file-hash="diffFile.file_hash" :project-path="projectPath" > <image-diff-overlay slot="image-overlay" :discussions="diffFile.discussions" - :file-hash="diffFile.fileHash" + :file-hash="diffFile.file_hash" :can-comment="getNoteableData.current_user.can_create_note" /> <div @@ -115,7 +115,7 @@ export default { :save-button-title="__('Comment')" class="diff-comment-form new-note discussion-form discussion-form-container" @handleFormUpdate="handleSaveNote" - @cancelForm="closeDiffFileCommentForm(diffFile.fileHash)" + @cancelForm="closeDiffFileCommentForm(diffFile.file_hash)" /> </div> </diff-viewer> diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index e76c7afd863..5da0bfb5bfe 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -3,7 +3,7 @@ import { mapActions, mapGetters, mapState } from 'vuex'; import _ from 'underscore'; import { __, sprintf } from '~/locale'; import createFlash from '~/flash'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import DiffFileHeader from './diff_file_header.vue'; import DiffContent from './diff_content.vue'; @@ -32,6 +32,7 @@ export default { computed: { ...mapState('diffs', ['currentDiffFileId']), ...mapGetters(['isNotesFetched']), + ...mapGetters('diffs', ['getDiffFileDiscussions']), isCollapsed() { return this.file.collapsed || false; }, @@ -39,7 +40,7 @@ export default { return sprintf( __('You can %{linkStart}view the blob%{linkEnd} instead.'), { - linkStart: `<a href="${_.escape(this.file.viewPath)}">`, + linkStart: `<a href="${_.escape(this.file.view_path)}">`, linkEnd: '</a>', }, false, @@ -48,21 +49,34 @@ export default { showExpandMessage() { return ( this.isCollapsed || - (!this.file.highlightedDiffLines && + (!this.file.highlighted_diff_lines && !this.isLoadingCollapsedDiff && - !this.file.tooLarge && + !this.file.too_large && this.file.text) ); }, showLoadingIcon() { return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed); }, + hasDiffLines() { + return ( + this.file.highlighted_diff_lines && + this.file.parallel_diff_lines && + this.file.parallel_diff_lines.length > 0 + ); + }, + }, + watch: { + 'file.collapsed': function fileCollapsedWatch(newVal, oldVal) { + if (!newVal && oldVal && !this.hasDiffLines) { + this.handleLoadCollapsedDiff(); + } + }, }, methods: { ...mapActions('diffs', ['loadCollapsedDiff', 'assignDiscussionsToDiff']), handleToggle() { - const { highlightedDiffLines, parallelDiffLines } = this.file; - if (!highlightedDiffLines && parallelDiffLines !== undefined && !parallelDiffLines.length) { + if (!this.hasDiffLines) { this.handleLoadCollapsedDiff(); } else { this.file.collapsed = !this.file.collapsed; @@ -81,7 +95,7 @@ export default { .then(() => { requestIdleCallback( () => { - this.assignDiscussionsToDiff(); + this.assignDiscussionsToDiff(this.getDiffFileDiscussions(this.file)); }, { timeout: 1000 }, ); @@ -103,9 +117,9 @@ export default { <template> <div - :id="file.fileHash" + :id="file.file_hash" :class="{ - 'is-active': currentDiffFileId === file.fileHash + 'is-active': currentDiffFileId === file.file_hash }" class="diff-file file-holder" > @@ -129,7 +143,7 @@ export default { make your changes there, and submit a merge request. </span> <a - :href="file.forkPath" + :href="file.fork_path" class="js-fork-suggestion-button btn btn-grouped btn-inverted btn-success" > Fork @@ -145,7 +159,7 @@ export default { <diff-content v-if="!isCollapsed && file.renderIt" - :class="{ hidden: isCollapsed || file.tooLarge }" + :class="{ hidden: isCollapsed || file.too_large }" :diff-file="file" /> <gl-loading-icon @@ -166,7 +180,7 @@ export default { </a> </div> <div - v-if="file.tooLarge" + v-if="file.too_large" class="nothing-here-block diff-collapsed js-too-large-diff" > {{ __('This source diff could not be displayed because it is too large.') }} diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index dcf1057eb84..af03cec6582 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -68,32 +68,32 @@ export default { }, titleLink() { if (this.diffFile.submodule) { - return this.diffFile.submoduleTreeUrl || this.diffFile.submoduleLink; + return this.diffFile.submodule_tree_url || this.diffFile.submodule_link; } return this.discussionPath; }, filePath() { if (this.diffFile.submodule) { - return `${this.diffFile.filePath} @ ${truncateSha(this.diffFile.blob.id)}`; + return `${this.diffFile.file_path} @ ${truncateSha(this.diffFile.blob.id)}`; } - if (this.diffFile.deletedFile) { - return sprintf(__('%{filePath} deleted'), { filePath: this.diffFile.filePath }, false); + if (this.diffFile.deleted_file) { + return sprintf(__('%{filePath} deleted'), { filePath: this.diffFile.file_path }, false); } - return this.diffFile.filePath; + return this.diffFile.file_path; }, titleTag() { - return this.diffFile.fileHash ? 'a' : 'span'; + return this.diffFile.file_hash ? 'a' : 'span'; }, isUsingLfs() { - return this.diffFile.storedExternally && this.diffFile.externalStorage === 'lfs'; + return this.diffFile.stored_externally && this.diffFile.external_storage === 'lfs'; }, collapseIcon() { return this.expanded ? 'chevron-down' : 'chevron-right'; }, viewFileButtonText() { - const truncatedContentSha = _.escape(truncateSha(this.diffFile.contentSha)); + const truncatedContentSha = _.escape(truncateSha(this.diffFile.content_sha)); return sprintf( s__('MergeRequests|View file @ %{commitId}'), { @@ -103,7 +103,7 @@ export default { ); }, viewReplacedFileButtonText() { - const truncatedBaseSha = _.escape(truncateSha(this.diffFile.diffRefs.baseSha)); + const truncatedBaseSha = _.escape(truncateSha(this.diffFile.diff_refs.base_sha)); return sprintf( s__('MergeRequests|View replaced file @ %{commitId}'), { @@ -113,7 +113,7 @@ export default { ); }, gfmCopyText() { - return `\`${this.diffFile.filePath}\``; + return `\`${this.diffFile.file_path}\``; }, }, methods: { @@ -164,21 +164,21 @@ export default { aria-hidden="true" css-classes="js-file-icon append-right-5" /> - <span v-if="diffFile.renamedFile"> + <span v-if="diffFile.renamed_file"> <strong v-tooltip - :title="diffFile.oldPath" + :title="diffFile.old_path" class="file-title-name" data-container="body" - v-html="diffFile.oldPathHtml" + v-html="diffFile.old_path_html" ></strong> → <strong v-tooltip - :title="diffFile.newPath" + :title="diffFile.new_path" class="file-title-name" data-container="body" - v-html="diffFile.newPathHtml" + v-html="diffFile.new_path_html" ></strong> </span> @@ -195,16 +195,16 @@ export default { <clipboard-button :title="__('Copy file path to clipboard')" - :text="diffFile.filePath" + :text="diffFile.file_path" :gfm="gfmCopyText" css-class="btn-default btn-transparent btn-clipboard" /> <small - v-if="diffFile.modeChanged" + v-if="diffFile.mode_changed" ref="fileMode" > - {{ diffFile.aMode }} → {{ diffFile.bMode }} + {{ diffFile.a_mode }} → {{ diffFile.b_mode }} </small> <span @@ -220,7 +220,7 @@ export default { class="file-actions d-none d-sm-block" > <template - v-if="diffFile.blob && diffFile.blob.readableText" + v-if="diffFile.blob && diffFile.blob.readable_text" > <button :disabled="!diffHasDiscussions(diffFile)" @@ -234,33 +234,33 @@ export default { </button> <edit-button - v-if="!diffFile.deletedFile" + v-if="!diffFile.deleted_file" :can-current-user-fork="canCurrentUserFork" - :edit-path="diffFile.editPath" - :can-modify-blob="diffFile.canModifyBlob" + :edit-path="diffFile.edit_path" + :can-modify-blob="diffFile.can_modify_blob" @showForkMessage="showForkMessage" /> </template> <a - v-if="diffFile.replacedViewPath" - :href="diffFile.replacedViewPath" + v-if="diffFile.replaced_view_path" + :href="diffFile.replaced_view_path" class="btn view-file js-view-file" v-html="viewReplacedFileButtonText" > </a> <a - :href="diffFile.viewPath" + :href="diffFile.view_path" class="btn view-file js-view-file" v-html="viewFileButtonText" > </a> <a - v-if="diffFile.externalUrl" + v-if="diffFile.external_url" v-tooltip - :href="diffFile.externalUrl" - :title="`View on ${diffFile.formattedExternalUrl}`" + :href="diffFile.external_url" + :title="`View on ${diffFile.formatted_external_url}`" target="_blank" rel="noopener noreferrer" class="btn btn-file-option" diff --git a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue index 254bc235691..8f8c2a31c71 100644 --- a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue +++ b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue @@ -3,7 +3,7 @@ import { mapActions } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; import { pluralize, truncate } from '~/lib/utils/text_utility'; import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants'; export default { diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue index f4a9be19496..8f037eeefc4 100644 --- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue +++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue @@ -73,7 +73,7 @@ export default { }), ...mapGetters(['isLoggedIn']), lineHref() { - return `#${this.line.lineCode || ''}`; + return `#${this.line.line_code || ''}`; }, shouldShowCommentButton() { return ( @@ -99,7 +99,7 @@ export default { methods: { ...mapActions('diffs', ['loadMoreLines', 'showCommentForm']), handleCommentButton() { - this.showCommentForm({ lineCode: this.line.lineCode }); + this.showCommentForm({ lineCode: this.line.line_code }); }, handleLoadMoreLines() { if (this.isRequesting) { @@ -108,8 +108,8 @@ export default { this.isRequesting = true; const endpoint = this.contextLinesPath; - const oldLineNumber = this.line.metaData.oldPos || 0; - const newLineNumber = this.line.metaData.newPos || 0; + const oldLineNumber = this.line.meta_data.old_pos || 0; + const newLineNumber = this.line.meta_data.new_pos || 0; const offset = newLineNumber - oldLineNumber; const bottom = this.isBottom; const { fileHash } = this; @@ -125,12 +125,12 @@ export default { to = lineNumber + UNFOLD_COUNT; } else { const diffFile = utils.findDiffFile(this.diffFiles, this.fileHash); - const indexForInline = utils.findIndexInInlineLines(diffFile.highlightedDiffLines, { + const indexForInline = utils.findIndexInInlineLines(diffFile.highlighted_diff_lines, { oldLineNumber, newLineNumber, }); - const prevLine = diffFile.highlightedDiffLines[indexForInline - 2]; - const prevLineNumber = (prevLine && prevLine.newLine) || 0; + const prevLine = diffFile.highlighted_diff_lines[indexForInline - 2]; + const prevLineNumber = (prevLine && prevLine.new_line) || 0; if (since <= prevLineNumber + 1) { since = prevLineNumber + 1; diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue index bb9bb821de3..07f38172575 100644 --- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue +++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue @@ -53,7 +53,7 @@ export default { this.noteableData.diff_head_sha, DIFF_NOTE_TYPE, this.noteableData.source_project_id, - this.line.lineCode, + this.line.line_code, ]; this.initAutoSave(this.noteableData, keys); @@ -72,7 +72,7 @@ export default { } this.cancelCommentForm({ - lineCode: this.line.lineCode, + lineCode: this.line.line_code, }); this.$nextTick(() => { this.resetAutoSave(); @@ -94,7 +94,7 @@ export default { <note-form ref="noteForm" :is-editing="true" - :line-code="line.lineCode" + :line-code="line.line_code" save-button-title="Comment" class="diff-comment-form" @cancelForm="handleCancelCommentForm" diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue index 5d9a0b123fe..0a893a57f07 100644 --- a/app/assets/javascripts/diffs/components/diff_table_cell.vue +++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue @@ -96,9 +96,7 @@ export default { }; }, lineNumber() { - const { lineType } = this; - - return lineType === OLD_LINE_TYPE ? this.line.oldLine : this.line.newLine; + return this.lineType === OLD_LINE_TYPE ? this.line.old_line : this.line.new_line; }, }, }; diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue index 46a51859da5..b9e14c53d2c 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue @@ -48,7 +48,7 @@ export default { :discussions="line.discussions" /> <diff-line-note-form - v-if="diffLineCommentForms[line.lineCode]" + v-if="diffLineCommentForms[line.line_code]" :diff-file-hash="diffFileHash" :line="line" :note-target-line="line" diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue index 542acd3d930..1f4088066d1 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue @@ -52,9 +52,7 @@ export default { }; }, inlineRowId() { - const { lineCode, oldLine, newLine } = this.line; - - return lineCode || `${this.fileHash}_${oldLine}_${newLine}`; + return this.line.line_code || `${this.fileHash}_${this.line.old_line}_${this.line.new_line}`; }, }, created() { @@ -107,7 +105,7 @@ export default { <td :class="line.type" class="line_content" - v-html="line.richText" + v-html="line.rich_text" > </td> </tr> diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue index fbf9e77ac07..79efac89e98 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_view.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue @@ -43,16 +43,16 @@ export default { v-for="(line, index) in diffLines" > <inline-diff-table-row - :key="line.lineCode" - :file-hash="diffFile.fileHash" - :context-lines-path="diffFile.contextLinesPath" + :key="line.line_code" + :file-hash="diffFile.file_hash" + :context-lines-path="diffFile.context_lines_path" :line="line" :is-bottom="index + 1 === diffLinesLength" /> <inline-diff-comment-row v-if="shouldRenderInlineCommentRow(line)" :key="index" - :diff-file-hash="diffFile.fileHash" + :diff-file-hash="diffFile.file_hash" :line="line" :line-index="index" /> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue index 3b71c0a1fd4..00c2df4dac1 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue @@ -27,10 +27,10 @@ export default { diffLineCommentForms: state => state.diffs.diffLineCommentForms, }), leftLineCode() { - return this.line.left && this.line.left.lineCode; + return this.line.left && this.line.left.line_code; }, rightLineCode() { - return this.line.right && this.line.right.lineCode; + return this.line.right && this.line.right.line_code; }, hasExpandedDiscussionOnLeft() { return this.line.left && this.line.left.discussions diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue index fcc3b3e9117..2d87db12fd6 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -120,11 +120,11 @@ export default { class="diff-line-num old_line" /> <td - :id="line.left.lineCode" + :id="line.left.line_code" :class="parallelViewLeftLineType" class="line_content parallel left-side" @mousedown.native="handleParallelLineMouseDown" - v-html="line.left.richText" + v-html="line.left.rich_text" > </td> </template> @@ -146,11 +146,11 @@ export default { class="diff-line-num new_line" /> <td - :id="line.right.lineCode" + :id="line.right.line_code" :class="line.right.type" class="line_content parallel right-side" @mousedown.native="handleParallelLineMouseDown" - v-html="line.right.richText" + v-html="line.right.rich_text" > </td> </template> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index 3452f0d2b00..6942f9b53e0 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -46,8 +46,8 @@ export default { > <parallel-diff-table-row :key="index" - :file-hash="diffFile.fileHash" - :context-lines-path="diffFile.contextLinesPath" + :file-hash="diffFile.file_hash" + :context-lines-path="diffFile.context_lines_path" :line="line" :is-bottom="index + 1 === diffLinesLength" /> @@ -55,7 +55,7 @@ export default { v-if="shouldRenderParallelCommentRow(line)" :key="`dcr-${index}`" :line="line" - :diff-file-hash="diffFile.fileHash" + :diff-file-hash="diffFile.file_hash" :line-index="index" /> </template> diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue index 91052b303a6..1f82eeae6cb 100644 --- a/app/assets/javascripts/diffs/components/tree_list.vue +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -1,6 +1,6 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import { convertPermissionToBoolean } from '~/lib/utils/common_utils'; import Icon from '~/vue_shared/components/icon.vue'; import FileRow from '~/vue_shared/components/file_row.vue'; @@ -35,7 +35,7 @@ export default { if (search === '') return this.renderTreeList ? this.tree : this.allBlobs; - return this.allBlobs.filter(f => f.name.toLowerCase().indexOf(search) >= 0); + return this.allBlobs.filter(f => f.path.toLowerCase().indexOf(search) >= 0); }, rowDisplayTextKey() { if (this.renderTreeList && this.search.trim() === '') { diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js index aae89109c27..06ef4207d85 100644 --- a/app/assets/javascripts/diffs/index.js +++ b/app/assets/javascripts/diffs/index.js @@ -1,6 +1,5 @@ import Vue from 'vue'; import { mapState } from 'vuex'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import diffsApp from './components/app.vue'; export default function initDiffsApp(store) { @@ -17,9 +16,7 @@ export default function initDiffsApp(store) { return { endpoint: dataset.endpoint, projectPath: dataset.projectPath, - currentUser: convertObjectPropsToCamelCase(JSON.parse(dataset.currentUserData), { - deep: true, - }), + currentUser: JSON.parse(dataset.currentUserData) || {}, }; }, computed: { diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index d3e9c7c88f0..6354d3ce1e6 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -50,8 +50,8 @@ export const assignDiscussionsToDiff = ( }; export const removeDiscussionsFromDiff = ({ commit }, removeDiscussion) => { - const { fileHash, line_code, id } = removeDiscussion; - commit(types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, { fileHash, lineCode: line_code, id }); + const { file_hash, line_code, id } = removeDiscussion; + commit(types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, { fileHash: file_hash, lineCode: line_code, id }); }; export const startRenderDiffsQueue = ({ state, commit }) => { @@ -167,7 +167,7 @@ export const expandAllFiles = ({ commit }) => { export const toggleFileDiscussions = ({ getters, dispatch }, diff) => { const discussions = getters.getDiffFileDiscussions(diff); const shouldCloseAll = getters.diffHasAllExpandedDiscussions(diff); - const shouldExpandAll = getters.diffHasAllCollpasedDiscussions(diff); + const shouldExpandAll = getters.diffHasAllCollapsedDiscussions(diff); discussions.forEach(discussion => { const data = { discussionId: discussion.id }; @@ -189,7 +189,8 @@ export const saveDiffDiscussion = ({ dispatch }, { note, formData }) => { return dispatch('saveNote', postData, { root: true }) .then(result => dispatch('updateDiscussion', result.discussion, { root: true })) .then(discussion => dispatch('assignDiscussionsToDiff', [discussion])) - .then(() => dispatch('closeDiffFileCommentForm', formData.diffFile.fileHash)) + .then(() => dispatch('closeDiffFileCommentForm', formData.diffFile.file_hash)) + .then(() => dispatch('startTaskList', null, { root: true })) .catch(() => createFlash(s__('MergeRequests|Saving the comment failed'))); }; diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js index bf490f9d78a..6a87b712b48 100644 --- a/app/assets/javascripts/diffs/store/getters.js +++ b/app/assets/javascripts/diffs/store/getters.js @@ -1,4 +1,3 @@ -import _ from 'underscore'; import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '../constants'; export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW_TYPE; @@ -24,11 +23,11 @@ export const diffHasAllExpandedDiscussions = (state, getters) => diff => { }; /** - * Checks if the diff has all discussions collpased + * Checks if the diff has all discussions collapsed * @param {Object} diff * @returns {Boolean} */ -export const diffHasAllCollpasedDiscussions = (state, getters) => diff => { +export const diffHasAllCollapsedDiscussions = (state, getters) => diff => { const discussions = getters.getDiffFileDiscussions(diff); return ( @@ -68,8 +67,7 @@ export const diffHasDiscussions = (state, getters) => diff => */ export const getDiffFileDiscussions = (state, getters, rootState, rootGetters) => diff => rootGetters.discussions.filter( - discussion => - discussion.diff_discussion && _.isEqual(discussion.diff_file.file_hash, diff.fileHash), + discussion => discussion.diff_discussion && discussion.diff_file.file_hash === diff.file_hash, ) || []; export const shouldRenderParallelCommentRow = state => line => { @@ -90,14 +88,14 @@ export const shouldRenderParallelCommentRow = state => line => { return true; } - const hasCommentFormOnLeft = line.left && state.diffLineCommentForms[line.left.lineCode]; - const hasCommentFormOnRight = line.right && state.diffLineCommentForms[line.right.lineCode]; + const hasCommentFormOnLeft = line.left && state.diffLineCommentForms[line.left.line_code]; + const hasCommentFormOnRight = line.right && state.diffLineCommentForms[line.right.line_code]; return hasCommentFormOnLeft || hasCommentFormOnRight; }; export const shouldRenderInlineCommentRow = state => line => { - if (state.diffLineCommentForms[line.lineCode]) return true; + if (state.diffLineCommentForms[line.line_code]) return true; if (!line.discussions || line.discussions.length === 0) { return false; @@ -108,7 +106,7 @@ export const shouldRenderInlineCommentRow = state => line => { // prevent babel-plugin-rewire from generating an invalid default during karma∂ tests export const getDiffFileByHash = state => fileHash => - state.diffFiles.find(file => file.fileHash === fileHash); + state.diffFiles.find(file => file.file_hash === fileHash); export const allBlobs = state => Object.values(state.treeEntries).filter(f => f.type === 'blob'); diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index a7eea2c1449..2133cfe4825 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -23,12 +23,11 @@ export default { }, [types.SET_DIFF_DATA](state, data) { - const diffData = convertObjectPropsToCamelCase(data, { deep: true }); - prepareDiffData(diffData); - const { tree, treeEntries } = generateTreeList(diffData.diffFiles); + prepareDiffData(data); + const { tree, treeEntries } = generateTreeList(data.diff_files); Object.assign(state, { - ...diffData, + ...convertObjectPropsToCamelCase(data), tree: sortTree(tree), treeEntries, }); @@ -42,7 +41,7 @@ export default { [types.SET_MERGE_REQUEST_DIFFS](state, mergeRequestDiffs) { Object.assign(state, { - mergeRequestDiffs: convertObjectPropsToCamelCase(mergeRequestDiffs, { deep: true }), + mergeRequestDiffs, }); }, @@ -62,13 +61,18 @@ export default { const { lineNumbers, contextLines, fileHash } = options; const { bottom } = options.params; const diffFile = findDiffFile(state.diffFiles, fileHash); - const { highlightedDiffLines, parallelDiffLines } = diffFile; removeMatchLine(diffFile, lineNumbers, bottom); - const lines = addLineReferences(contextLines, lineNumbers, bottom); + + const lines = addLineReferences(contextLines, lineNumbers, bottom).map(line => ({ + ...line, + line_code: line.line_code || `${fileHash}_${line.old_line}_${line.new_line}`, + discussions: line.discussions || [], + })); + addContextLines({ - inlineLines: highlightedDiffLines, - parallelLines: parallelDiffLines, + inlineLines: diffFile.highlighted_diff_lines, + parallelLines: diffFile.parallel_diff_lines, contextLines: lines, bottom, lineNumbers, @@ -76,10 +80,9 @@ export default { }, [types.ADD_COLLAPSED_DIFFS](state, { file, data }) { - const normalizedData = convertObjectPropsToCamelCase(data, { deep: true }); - prepareDiffData(normalizedData); - const [newFileData] = normalizedData.diffFiles.filter(f => f.fileHash === file.fileHash); - const selectedFile = state.diffFiles.find(f => f.fileHash === file.fileHash); + prepareDiffData(data); + const [newFileData] = data.diff_files.filter(f => f.file_hash === file.file_hash); + const selectedFile = state.diffFiles.find(f => f.file_hash === file.file_hash); Object.assign(selectedFile, { ...newFileData }); }, @@ -95,20 +98,20 @@ export default { const discussionLineCode = discussion.line_code; const fileHash = discussion.diff_file.file_hash; - const lineCheck = ({ lineCode }) => - lineCode === discussionLineCode && + const lineCheck = line => + line.line_code === discussionLineCode && isDiscussionApplicableToLine({ discussion, - diffPosition: diffPositionByLineCode[lineCode], + diffPosition: diffPositionByLineCode[line.line_code], latestDiff, }); state.diffFiles = state.diffFiles.map(diffFile => { - if (diffFile.fileHash === fileHash) { + if (diffFile.file_hash === fileHash) { const file = { ...diffFile }; - if (file.highlightedDiffLines) { - file.highlightedDiffLines = file.highlightedDiffLines.map(line => { + if (file.highlighted_diff_lines) { + file.highlighted_diff_lines = file.highlighted_diff_lines.map(line => { if (lineCheck(line)) { return { ...line, @@ -120,8 +123,8 @@ export default { }); } - if (file.parallelDiffLines) { - file.parallelDiffLines = file.parallelDiffLines.map(line => { + if (file.parallel_diff_lines) { + file.parallel_diff_lines = file.parallel_diff_lines.map(line => { const left = line.left && lineCheck(line.left); const right = line.right && lineCheck(line.right); @@ -142,7 +145,7 @@ export default { }); } - if (!file.parallelDiffLines || !file.highlightedDiffLines) { + if (!file.parallel_diff_lines || !file.highlighted_diff_lines) { file.discussions = file.discussions.concat(discussion); } @@ -154,16 +157,16 @@ export default { }, [types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, lineCode, id }) { - const selectedFile = state.diffFiles.find(f => f.fileHash === fileHash); + const selectedFile = state.diffFiles.find(f => f.file_hash === fileHash); if (selectedFile) { - if (selectedFile.parallelDiffLines) { - const targetLine = selectedFile.parallelDiffLines.find( + if (selectedFile.parallel_diff_lines) { + const targetLine = selectedFile.parallel_diff_lines.find( line => - (line.left && line.left.lineCode === lineCode) || - (line.right && line.right.lineCode === lineCode), + (line.left && line.left.line_code === lineCode) || + (line.right && line.right.line_code === lineCode), ); if (targetLine) { - const side = targetLine.left && targetLine.left.lineCode === lineCode ? 'left' : 'right'; + const side = targetLine.left && targetLine.left.line_code === lineCode ? 'left' : 'right'; Object.assign(targetLine[side], { discussions: [], @@ -171,9 +174,9 @@ export default { } } - if (selectedFile.highlightedDiffLines) { - const targetInlineLine = selectedFile.highlightedDiffLines.find( - line => line.lineCode === lineCode, + if (selectedFile.highlighted_diff_lines) { + const targetInlineLine = selectedFile.highlighted_diff_lines.find( + line => line.line_code === lineCode, ); if (targetInlineLine) { diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js index a935b9b1ffa..d9d3c0f2ca2 100644 --- a/app/assets/javascripts/diffs/store/utils.js +++ b/app/assets/javascripts/diffs/store/utils.js @@ -1,5 +1,4 @@ import _ from 'underscore'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { diffModes } from '~/ide/constants'; import { LINE_POSITION_LEFT, @@ -15,7 +14,7 @@ import { } from '../constants'; export function findDiffFile(files, hash) { - return files.filter(file => file.fileHash === hash)[0]; + return files.filter(file => file.file_hash === hash)[0]; } export const getReversePosition = linePosition => { @@ -39,14 +38,14 @@ export function getFormData(params) { } = params; const position = JSON.stringify({ - base_sha: diffFile.diffRefs.baseSha, - start_sha: diffFile.diffRefs.startSha, - head_sha: diffFile.diffRefs.headSha, - old_path: diffFile.oldPath, - new_path: diffFile.newPath, + base_sha: diffFile.diff_refs.base_sha, + start_sha: diffFile.diff_refs.start_sha, + head_sha: diffFile.diff_refs.head_sha, + old_path: diffFile.old_path, + new_path: diffFile.new_path, position_type: positionType || TEXT_DIFF_POSITION_TYPE, - old_line: noteTargetLine ? noteTargetLine.oldLine : null, - new_line: noteTargetLine ? noteTargetLine.newLine : null, + old_line: noteTargetLine ? noteTargetLine.old_line : null, + new_line: noteTargetLine ? noteTargetLine.new_line : null, x: params.x, y: params.y, width: params.width, @@ -56,7 +55,7 @@ export function getFormData(params) { const postData = { view: diffViewType, line_type: linePosition === LINE_POSITION_RIGHT ? NEW_LINE_TYPE : OLD_LINE_TYPE, - merge_request_diff_head_sha: diffFile.diffRefs.headSha, + merge_request_diff_head_sha: diffFile.diff_refs.head_sha, in_reply_to_discussion_id: '', note_project_id: '', target_type: noteableData.targetType, @@ -69,10 +68,10 @@ export function getFormData(params) { noteable_id: noteableData.id, commit_id: '', type: - diffFile.diffRefs.startSha && diffFile.diffRefs.headSha + diffFile.diff_refs.start_sha && diffFile.diff_refs.head_sha ? DIFF_NOTE_TYPE : LEGACY_DIFF_NOTE_TYPE, - line_code: noteTargetLine ? noteTargetLine.lineCode : null, + line_code: noteTargetLine ? noteTargetLine.line_code : null, }, }; @@ -93,7 +92,7 @@ export const findIndexInInlineLines = (lines, lineNumbers) => { return _.findIndex( lines, - line => line.oldLine === oldLineNumber && line.newLine === newLineNumber, + line => line.old_line === oldLineNumber && line.new_line === newLineNumber, ); }; @@ -105,18 +104,18 @@ export const findIndexInParallelLines = (lines, lineNumbers) => { line => line.left && line.right && - line.left.oldLine === oldLineNumber && - line.right.newLine === newLineNumber, + line.left.old_line === oldLineNumber && + line.right.new_line === newLineNumber, ); }; export function removeMatchLine(diffFile, lineNumbers, bottom) { - const indexForInline = findIndexInInlineLines(diffFile.highlightedDiffLines, lineNumbers); - const indexForParallel = findIndexInParallelLines(diffFile.parallelDiffLines, lineNumbers); + const indexForInline = findIndexInInlineLines(diffFile.highlighted_diff_lines, lineNumbers); + const indexForParallel = findIndexInParallelLines(diffFile.parallel_diff_lines, lineNumbers); const factor = bottom ? 1 : -1; - diffFile.highlightedDiffLines.splice(indexForInline + factor, 1); - diffFile.parallelDiffLines.splice(indexForParallel + factor, 1); + diffFile.highlighted_diff_lines.splice(indexForInline + factor, 1); + diffFile.parallel_diff_lines.splice(indexForParallel + factor, 1); } export function addLineReferences(lines, lineNumbers, bottom) { @@ -125,18 +124,16 @@ export function addLineReferences(lines, lineNumbers, bottom) { let matchLineIndex = -1; const linesWithNumbers = lines.map((l, index) => { - const line = convertObjectPropsToCamelCase(l); - - if (line.type === MATCH_LINE_TYPE) { + if (l.type === MATCH_LINE_TYPE) { matchLineIndex = index; } else { - Object.assign(line, { - oldLine: bottom ? oldLineNumber + index + 1 : oldLineNumber + index - lineCount, - newLine: bottom ? newLineNumber + index + 1 : newLineNumber + index - lineCount, + Object.assign(l, { + old_line: bottom ? oldLineNumber + index + 1 : oldLineNumber + index - lineCount, + new_line: bottom ? newLineNumber + index + 1 : newLineNumber + index - lineCount, }); } - return line; + return l; }); if (matchLineIndex > -1) { @@ -146,9 +143,9 @@ export function addLineReferences(lines, lineNumbers, bottom) { : linesWithNumbers[matchLineIndex + 1]; Object.assign(line, { - metaData: { - oldPos: targetLine.oldLine, - newPos: targetLine.newLine, + meta_data: { + old_pos: targetLine.old_line, + new_pos: targetLine.new_line, }, }); } @@ -187,11 +184,11 @@ export function trimFirstCharOfLineContent(line = {}) { const parsedLine = Object.assign({}, line); - if (line.richText) { - const firstChar = parsedLine.richText.charAt(0); + if (line.rich_text) { + const firstChar = parsedLine.rich_text.charAt(0); if (firstChar === ' ' || firstChar === '+' || firstChar === '-') { - parsedLine.richText = line.richText.substring(1); + parsedLine.rich_text = line.rich_text.substring(1); } } @@ -201,15 +198,15 @@ export function trimFirstCharOfLineContent(line = {}) { // This prepares and optimizes the incoming diff data from the server // by setting up incremental rendering and removing unneeded data export function prepareDiffData(diffData) { - const filesLength = diffData.diffFiles.length; + const filesLength = diffData.diff_files.length; let showingLines = 0; for (let i = 0; i < filesLength; i += 1) { - const file = diffData.diffFiles[i]; + const file = diffData.diff_files[i]; - if (file.parallelDiffLines) { - const linesLength = file.parallelDiffLines.length; + if (file.parallel_diff_lines) { + const linesLength = file.parallel_diff_lines.length; for (let u = 0; u < linesLength; u += 1) { - const line = file.parallelDiffLines[u]; + const line = file.parallel_diff_lines[u]; if (line.left) { line.left = trimFirstCharOfLineContent(line.left); } @@ -219,13 +216,13 @@ export function prepareDiffData(diffData) { } } - if (file.highlightedDiffLines) { - const linesLength = file.highlightedDiffLines.length; + if (file.highlighted_diff_lines) { + const linesLength = file.highlighted_diff_lines.length; for (let u = 0; u < linesLength; u += 1) { - const line = file.highlightedDiffLines[u]; + const line = file.highlighted_diff_lines[u]; Object.assign(line, { ...trimFirstCharOfLineContent(line) }); } - showingLines += file.parallelDiffLines.length; + showingLines += file.parallel_diff_lines.length; } Object.assign(file, { @@ -238,26 +235,21 @@ export function prepareDiffData(diffData) { export function getDiffPositionByLineCode(diffFiles) { return diffFiles.reduce((acc, diffFile) => { - const { baseSha, headSha, startSha } = diffFile.diffRefs; - const { newPath, oldPath } = diffFile; - // We can only use highlightedDiffLines to create the map of diff lines because // highlightedDiffLines will also include every parallel diff line in it. - if (diffFile.highlightedDiffLines) { - diffFile.highlightedDiffLines.forEach(line => { - const { lineCode, oldLine, newLine } = line; - - if (lineCode) { - acc[lineCode] = { - baseSha, - headSha, - startSha, - newPath, - oldPath, - oldLine, - newLine, - lineCode, - positionType: 'text', + if (diffFile.highlighted_diff_lines) { + diffFile.highlighted_diff_lines.forEach(line => { + if (line.line_code) { + acc[line.line_code] = { + base_sha: diffFile.diff_refs.base_sha, + head_sha: diffFile.diff_refs.head_sha, + start_sha: diffFile.diff_refs.start_sha, + new_path: diffFile.new_path, + old_path: diffFile.old_path, + old_line: line.old_line, + new_line: line.new_line, + line_code: line.line_code, + position_type: 'text', }; } }); @@ -270,30 +262,30 @@ export function getDiffPositionByLineCode(diffFiles) { // This method will check whether the discussion is still applicable // to the diff line in question regarding different versions of the MR export function isDiscussionApplicableToLine({ discussion, diffPosition, latestDiff }) { - const { lineCode, ...diffPositionCopy } = diffPosition; + const { line_code, ...diffPositionCopy } = diffPosition; if (discussion.original_position && discussion.position) { - const originalRefs = convertObjectPropsToCamelCase(discussion.original_position); - const refs = convertObjectPropsToCamelCase(discussion.position); + const originalRefs = discussion.original_position; + const refs = discussion.position; return _.isEqual(refs, diffPositionCopy) || _.isEqual(originalRefs, diffPositionCopy); } - return latestDiff && discussion.active && lineCode === discussion.line_code; + // eslint-disable-next-line + return latestDiff && discussion.active && line_code === discussion.line_code; } export const generateTreeList = files => files.reduce( (acc, file) => { - const { fileHash, addedLines, removedLines, newFile, deletedFile, newPath } = file; - const split = newPath.split('/'); + const split = file.new_path.split('/'); split.forEach((name, i) => { const parent = acc.treeEntries[split.slice(0, i).join('/')]; const path = `${parent ? `${parent.path}/` : ''}${name}`; if (!acc.treeEntries[path]) { - const type = path === newPath ? 'blob' : 'tree'; + const type = path === file.new_path ? 'blob' : 'tree'; acc.treeEntries[path] = { key: path, path, @@ -307,11 +299,11 @@ export const generateTreeList = files => if (type === 'blob') { Object.assign(entry, { changed: true, - tempFile: newFile, - deleted: deletedFile, - fileHash, - addedLines, - removedLines, + tempFile: file.new_file, + deleted: file.deleted_file, + fileHash: file.file_hash, + addedLines: file.added_lines, + removedLines: file.removed_lines, }); } else { Object.assign(entry, { @@ -329,6 +321,6 @@ export const generateTreeList = files => ); export const getDiffMode = diffFile => { - const diffModeKey = Object.keys(diffModes).find(key => diffFile[`${key}File`]); + const diffModeKey = Object.keys(diffModes).find(key => diffFile[`${key}_file`]); return diffModes[diffModeKey] || diffModes.replaced; }; diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue index a48f5fcb7d6..6c0b5c1f427 100644 --- a/app/assets/javascripts/environments/components/container.vue +++ b/app/assets/javascripts/environments/components/container.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import tablePagination from '../../vue_shared/components/table_pagination.vue'; import environmentTable from '../components/environments_table.vue'; diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue index 03c3ad0401f..b849772a82e 100644 --- a/app/assets/javascripts/environments/components/environment_actions.vue +++ b/app/assets/javascripts/environments/components/environment_actions.vue @@ -4,7 +4,7 @@ import { formatTime } from '~/lib/utils/datetime_utility'; import Icon from '~/vue_shared/components/icon.vue'; import eventHub from '../event_hub'; import tooltip from '../../vue_shared/directives/tooltip'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { directives: { diff --git a/app/assets/javascripts/environments/components/environment_external_url.vue b/app/assets/javascripts/environments/components/environment_external_url.vue index 1e8a892c0b8..af537cfb991 100644 --- a/app/assets/javascripts/environments/components/environment_external_url.vue +++ b/app/assets/javascripts/environments/components/environment_external_url.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import { s__ } from '~/locale'; diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index 50b0e9747ee..34d94490533 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -1,7 +1,7 @@ <script> import Timeago from 'timeago.js'; import _ from 'underscore'; -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import { humanize } from '~/lib/utils/text_utility'; import Icon from '~/vue_shared/components/icon.vue'; diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue index 7c723fa8979..ae4f07a71cd 100644 --- a/app/assets/javascripts/environments/components/environment_monitoring.vue +++ b/app/assets/javascripts/environments/components/environment_monitoring.vue @@ -2,7 +2,7 @@ /** * Renders the Monitoring (Metrics) link in environments table. */ -import { GlButton, GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlButton, GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; export default { diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue index 298469e6482..46c77dca871 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.vue +++ b/app/assets/javascripts/environments/components/environment_rollback.vue @@ -6,7 +6,7 @@ * Makes a post request when the button is clicked. */ import { s__ } from '~/locale'; -import { GlTooltipDirective, GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import eventHub from '../event_hub'; @@ -63,7 +63,7 @@ export default { > <icon v-if="isLastDeployment" - name="repeat" + name="repeat" /> <icon v-else diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue index 327c96a93e9..efecc128368 100644 --- a/app/assets/javascripts/environments/components/environment_stop.vue +++ b/app/assets/javascripts/environments/components/environment_stop.vue @@ -5,7 +5,7 @@ */ import $ from 'jquery'; -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import { s__ } from '~/locale'; import eventHub from '../event_hub'; diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.vue b/app/assets/javascripts/environments/components/environment_terminal_button.vue index b8b909f350c..83727caad16 100644 --- a/app/assets/javascripts/environments/components/environment_terminal_button.vue +++ b/app/assets/javascripts/environments/components/environment_terminal_button.vue @@ -3,7 +3,7 @@ * Renders a terminal button to open a web terminal. * Used in environments table. */ -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; export default { diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue index c03d4f29ff9..4eae4eec394 100644 --- a/app/assets/javascripts/environments/components/environments_table.vue +++ b/app/assets/javascripts/environments/components/environments_table.vue @@ -2,7 +2,7 @@ /** * Render environments table. */ -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import environmentItem from './environment_item.vue'; export default { diff --git a/app/assets/javascripts/environments/components/stop_environment_modal.vue b/app/assets/javascripts/environments/components/stop_environment_modal.vue index 6397f6caf1b..2ebc7b5b951 100644 --- a/app/assets/javascripts/environments/components/stop_environment_modal.vue +++ b/app/assets/javascripts/environments/components/stop_environment_modal.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import GlModal from '~/vue_shared/components/gl_modal.vue'; import { s__, sprintf } from '~/locale'; import LoadingButton from '~/vue_shared/components/loading_button.vue'; diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index 3233f5c4f71..aad5647c045 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -1,5 +1,5 @@ /* Developer beware! Do not add logic to showButton or hideButton - * that will force a reflow. Doing so will create a signficant performance + * that will force a reflow. Doing so will create a significant performance * bottleneck for pages with large diffs. For a comprehensive list of what * causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a */ diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js index af7936a92fb..d9a4d06b549 100644 --- a/app/assets/javascripts/filtered_search/dropdown_emoji.js +++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js @@ -69,10 +69,13 @@ export default class DropdownEmoji extends FilteredSearchDropdown { // Replace empty gl-emoji tag to real content const dropdownItems = [...this.dropdown.querySelectorAll('.filter-dropdown-item')]; dropdownItems.forEach(dropdownItem => { - const name = dropdownItem.querySelector('.js-data-value').innerText; - const emojiTag = this.glEmojiTag(name); - const emojiElement = dropdownItem.querySelector('gl-emoji'); - emojiElement.outerHTML = emojiTag; + const valueElement = dropdownItem.querySelector('.js-data-value'); + if (valueElement !== null) { + const name = valueElement.innerText; + const emojiTag = this.glEmojiTag(name); + const emojiElement = dropdownItem.querySelector('gl-emoji'); + emojiElement.outerHTML = emojiTag; + } }); } diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js index c23d4c484a5..89dcff74d0e 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -135,10 +135,6 @@ export default class FilteredSearchVisualTokens { } static updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) { - if (tokenValue === 'none') { - return Promise.resolve(); - } - const username = tokenValue.replace(/^@/, ''); return ( UsersCache.retrieve(username) @@ -184,7 +180,12 @@ export default class FilteredSearchVisualTokens { const tokenValueElement = tokenValueContainer.querySelector('.value'); tokenValueElement.innerText = tokenValue; + if (tokenValue === 'none' || tokenValue === 'any') { + return; + } + const tokenType = tokenName.toLowerCase(); + if (tokenType === 'label') { FilteredSearchVisualTokens.updateLabelTokenColor(tokenValueContainer, tokenValue); } else if (tokenType === 'author' || tokenType === 'assignee') { diff --git a/app/assets/javascripts/frequent_items/components/app.vue b/app/assets/javascripts/frequent_items/components/app.vue index 159c0bdc992..1ed8254bc58 100644 --- a/app/assets/javascripts/frequent_items/components/app.vue +++ b/app/assets/javascripts/frequent_items/components/app.vue @@ -1,7 +1,7 @@ <script> import { mapState, mapActions, mapGetters } from 'vuex'; import AccessorUtilities from '~/lib/utils/accessor'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import eventHub from '../event_hub'; import store from '../store/'; import { FREQUENT_ITEMS, STORAGE_KEY } from '../constants'; diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 00b3d283570..6f8b73564d0 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -151,10 +151,16 @@ class GfmAutoComplete { // Team Members $input.atwho({ at: '@', + alias: 'users', displayTpl(value) { let tmpl = GfmAutoComplete.Loading.template; - if (value.username != null) { - tmpl = GfmAutoComplete.Members.template; + const { avatarTag, username, title } = value; + if (username != null) { + tmpl = GfmAutoComplete.Members.templateFunction({ + avatarTag, + username, + title, + }); } return tmpl; }, @@ -565,8 +571,9 @@ GfmAutoComplete.Emoji = { }; // Team Members GfmAutoComplete.Members = { - // eslint-disable-next-line no-template-curly-in-string - template: '<li>${avatarTag} ${username} <small>${title}</small></li>', + templateFunction({ avatarTag, username, title }) { + return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small></li>`; + }, }; GfmAutoComplete.Labels = { template: diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue index 2a4a39436e7..29dc2d6a8a3 100644 --- a/app/assets/javascripts/groups/components/app.vue +++ b/app/assets/javascripts/groups/components/app.vue @@ -8,7 +8,7 @@ import { HIDDEN_CLASS } from '~/lib/utils/constants'; import { getParameterByName } from '~/lib/utils/common_utils'; import { mergeUrlParams } from '~/lib/utils/url_utility'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import eventHub from '../event_hub'; import { COMMON_STR, CONTENT_LIST_CLASS } from '../constants'; import groupsComponent from './groups.vue'; diff --git a/app/assets/javascripts/ide/components/branches/search_list.vue b/app/assets/javascripts/ide/components/branches/search_list.vue index 358f1153de2..600e1063f89 100644 --- a/app/assets/javascripts/ide/components/branches/search_list.vue +++ b/app/assets/javascripts/ide/components/branches/search_list.vue @@ -2,7 +2,7 @@ import { mapActions, mapState } from 'vuex'; import _ from 'underscore'; import Icon from '~/vue_shared/components/icon.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import Item from './item.vue'; export default { diff --git a/app/assets/javascripts/ide/components/error_message.vue b/app/assets/javascripts/ide/components/error_message.vue index 2d9bd99e82a..11fc15871ac 100644 --- a/app/assets/javascripts/ide/components/error_message.vue +++ b/app/assets/javascripts/ide/components/error_message.vue @@ -1,6 +1,6 @@ <script> import { mapActions } from 'vuex'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/ide/components/file_templates/dropdown.vue b/app/assets/javascripts/ide/components/file_templates/dropdown.vue index 891f7d48b4c..81d8d0b5132 100644 --- a/app/assets/javascripts/ide/components/file_templates/dropdown.vue +++ b/app/assets/javascripts/ide/components/file_templates/dropdown.vue @@ -2,7 +2,7 @@ import $ from 'jquery'; import { mapActions, mapState } from 'vuex'; import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue index 364ab9426e0..6b4849eb6f9 100644 --- a/app/assets/javascripts/ide/components/ide_side_bar.vue +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -1,6 +1,6 @@ <script> import { mapState, mapGetters } from 'vuex'; -import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab/ui'; import IdeTree from './ide_tree.vue'; import ResizablePanel from './resizable_panel.vue'; import ActivityBar from './activity_bar.vue'; diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index d2ff55a4ee3..12ed7f86b3d 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -1,7 +1,7 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; -import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab/ui'; import FileRow from '~/vue_shared/components/file_row.vue'; import NavDropdown from './nav_dropdown.vue'; import FileRowExtra from './file_row_extra.vue'; diff --git a/app/assets/javascripts/ide/components/jobs/list.vue b/app/assets/javascripts/ide/components/jobs/list.vue index 57da8b4e2cb..e3626f60899 100644 --- a/app/assets/javascripts/ide/components/jobs/list.vue +++ b/app/assets/javascripts/ide/components/jobs/list.vue @@ -1,6 +1,6 @@ <script> import { mapActions } from 'vuex'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import Stage from './stage.vue'; export default { diff --git a/app/assets/javascripts/ide/components/jobs/stage.vue b/app/assets/javascripts/ide/components/jobs/stage.vue index 5644759d2f9..e4fc78afaf2 100644 --- a/app/assets/javascripts/ide/components/jobs/stage.vue +++ b/app/assets/javascripts/ide/components/jobs/stage.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import tooltip from '../../../vue_shared/directives/tooltip'; import Icon from '../../../vue_shared/components/icon.vue'; import CiIcon from '../../../vue_shared/components/ci_icon.vue'; diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue index e4000f588bd..4df29590b85 100644 --- a/app/assets/javascripts/ide/components/merge_requests/list.vue +++ b/app/assets/javascripts/ide/components/merge_requests/list.vue @@ -3,7 +3,7 @@ import { mapActions, mapState } from 'vuex'; import _ from 'underscore'; import { __ } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import Item from './item.vue'; import TokenedInput from '../shared/tokened_input.vue'; diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue index 16aec1decd6..36cfcac4186 100644 --- a/app/assets/javascripts/ide/components/pipelines/list.vue +++ b/app/assets/javascripts/ide/components/pipelines/list.vue @@ -1,7 +1,7 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import _ from 'underscore'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import { sprintf, __ } from '../../../locale'; import Icon from '../../../vue_shared/components/icon.vue'; import CiIcon from '../../../vue_shared/components/ci_icon.vue'; diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue index 0bd56ff6e9b..afc0cfca7d8 100644 --- a/app/assets/javascripts/ide/components/preview/clientside.vue +++ b/app/assets/javascripts/ide/components/preview/clientside.vue @@ -3,7 +3,7 @@ import { mapActions, mapGetters, mapState } from 'vuex'; import _ from 'underscore'; import { Manager } from 'smooshpack'; import { listen } from 'codesandbox-api'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import Navigator from './navigator.vue'; import { packageJsonPath } from '../../constants'; import { createPathWithExt } from '../../utils'; diff --git a/app/assets/javascripts/ide/components/preview/navigator.vue b/app/assets/javascripts/ide/components/preview/navigator.vue index af8959186f9..cc6fc595b74 100644 --- a/app/assets/javascripts/ide/components/preview/navigator.vue +++ b/app/assets/javascripts/ide/components/preview/navigator.vue @@ -1,7 +1,7 @@ <script> import { listen } from 'codesandbox-api'; import Icon from '~/vue_shared/components/icon.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js index 8fa86995ef0..ea2525a2f0e 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js @@ -28,7 +28,7 @@ export const receiveLatestPipelineError = ({ commit, dispatch }, err) => { dispatch( 'setErrorMessage', { - text: __('An error occured whilst fetching the latest pipline.'), + text: __('An error occured whilst fetching the latest pipeline.'), action: () => dispatch('forcePipelineRequest').then(() => dispatch('setErrorMessage', null, { root: true }), diff --git a/app/assets/javascripts/init_legacy_filters.js b/app/assets/javascripts/init_legacy_filters.js deleted file mode 100644 index b6ff97d1279..00000000000 --- a/app/assets/javascripts/init_legacy_filters.js +++ /dev/null @@ -1,14 +0,0 @@ -/* eslint-disable no-new */ -import LabelsSelect from './labels_select'; -import subscriptionSelect from './subscription_select'; -import UsersSelect from './users_select'; -import issueStatusSelect from './issue_status_select'; -import MilestoneSelect from './milestone_select'; - -export default () => { - new UsersSelect(); - new LabelsSelect(); - new MilestoneSelect(); - issueStatusSelect(); - subscriptionSelect(); -}; diff --git a/app/assets/javascripts/jobs/components/artifacts_block.vue b/app/assets/javascripts/jobs/components/artifacts_block.vue index 93c89411b4a..932675c0fc6 100644 --- a/app/assets/javascripts/jobs/components/artifacts_block.vue +++ b/app/assets/javascripts/jobs/components/artifacts_block.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import timeagoMixin from '~/vue_shared/mixins/timeago'; diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/commit_block.vue index 06fe23fedce..9373dbebc8a 100644 --- a/app/assets/javascripts/jobs/components/commit_block.vue +++ b/app/assets/javascripts/jobs/components/commit_block.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; export default { diff --git a/app/assets/javascripts/jobs/components/empty_state.vue b/app/assets/javascripts/jobs/components/empty_state.vue index be7425c2d25..afc4d931a68 100644 --- a/app/assets/javascripts/jobs/components/empty_state.vue +++ b/app/assets/javascripts/jobs/components/empty_state.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/jobs/components/erased_block.vue b/app/assets/javascripts/jobs/components/erased_block.vue index d80e905c68e..712f564b065 100644 --- a/app/assets/javascripts/jobs/components/erased_block.vue +++ b/app/assets/javascripts/jobs/components/erased_block.vue @@ -1,6 +1,6 @@ <script> import _ from 'underscore'; -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; export default { diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue index 90216b04e92..ecb809ca4de 100644 --- a/app/assets/javascripts/jobs/components/job_app.vue +++ b/app/assets/javascripts/jobs/components/job_app.vue @@ -1,7 +1,7 @@ <script> import _ from 'underscore'; import { mapGetters, mapState, mapActions } from 'vuex'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import { isScrolledToBottom } from '~/lib/utils/scroll_utils'; import { polyfillSticky } from '~/lib/utils/sticky'; import bp from '~/breakpoints'; diff --git a/app/assets/javascripts/jobs/components/job_container_item.vue b/app/assets/javascripts/jobs/components/job_container_item.vue index 3ddcfd11dca..80cbed422a0 100644 --- a/app/assets/javascripts/jobs/components/job_container_item.vue +++ b/app/assets/javascripts/jobs/components/job_container_item.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; import tooltip from '~/vue_shared/directives/tooltip'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import Icon from '~/vue_shared/components/icon.vue'; diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job_log_controllers.vue index 8b506b124ec..5e0495bb231 100644 --- a/app/assets/javascripts/jobs/components/job_log_controllers.vue +++ b/app/assets/javascripts/jobs/components/job_log_controllers.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltipDirective, GlLink, GlButton } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui'; import { polyfillSticky } from '~/lib/utils/sticky'; import Icon from '~/vue_shared/components/icon.vue'; import { numberToHumanSize } from '~/lib/utils/number_utils'; diff --git a/app/assets/javascripts/jobs/components/sidebar.vue b/app/assets/javascripts/jobs/components/sidebar.vue index f7b7b8f10f7..21f0a1435d7 100644 --- a/app/assets/javascripts/jobs/components/sidebar.vue +++ b/app/assets/javascripts/jobs/components/sidebar.vue @@ -1,7 +1,7 @@ <script> import _ from 'underscore'; import { mapActions, mapState } from 'vuex'; -import { GlLink, GlButton } from '@gitlab-org/gitlab-ui'; +import { GlLink, GlButton } from '@gitlab/ui'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import { timeIntervalInWords } from '~/lib/utils/datetime_utility'; import Icon from '~/vue_shared/components/icon.vue'; diff --git a/app/assets/javascripts/jobs/components/sidebar_detail_row.vue b/app/assets/javascripts/jobs/components/sidebar_detail_row.vue index cfedb38e17a..d143e9f586c 100644 --- a/app/assets/javascripts/jobs/components/sidebar_detail_row.vue +++ b/app/assets/javascripts/jobs/components/sidebar_detail_row.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; export default { name: 'SidebarDetailRow', diff --git a/app/assets/javascripts/jobs/components/stuck_block.vue b/app/assets/javascripts/jobs/components/stuck_block.vue index ca4bf471363..2b2ebe4c3f7 100644 --- a/app/assets/javascripts/jobs/components/stuck_block.vue +++ b/app/assets/javascripts/jobs/components/stuck_block.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; /** * Renders Stuck Runners block for job's view. */ diff --git a/app/assets/javascripts/jobs/components/trigger_block.vue b/app/assets/javascripts/jobs/components/trigger_block.vue index 1e62c05b4d1..4d18f76b7ea 100644 --- a/app/assets/javascripts/jobs/components/trigger_block.vue +++ b/app/assets/javascripts/jobs/components/trigger_block.vue @@ -1,5 +1,5 @@ <script> -import { GlButton } from '@gitlab-org/gitlab-ui'; +import { GlButton } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js index 54ed217572a..8045f6dc3ff 100644 --- a/app/assets/javascripts/jobs/store/actions.js +++ b/app/assets/javascripts/jobs/store/actions.js @@ -80,8 +80,8 @@ export const fetchJob = ({ state, dispatch }) => { export const receiveJobSuccess = ({ commit }, data = {}) => { commit(types.RECEIVE_JOB_SUCCESS, data); - if (data.favicon) { - setFaviconOverlay(data.favicon); + if (data.status && data.status.favicon) { + setFaviconOverlay(data.status.favicon); } else { resetFavicon(); } diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js index d440b2c9ef1..35e92b0b5d9 100644 --- a/app/assets/javascripts/jobs/store/getters.js +++ b/app/assets/javascripts/jobs/store/getters.js @@ -42,7 +42,7 @@ export const emptyStateIllustration = state => (state.job && state.job.status && state.job.status.illustration) || {}; export const emptyStateAction = state => - (state.job && state.job.status && state.job.status.action) || {}; + (state.job && state.job.status && state.job.status.action) || null; export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceComplete; diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index c52cfb806a2..3618c6af7e2 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -39,7 +39,7 @@ function blockTagText(text, textArea, blockTag, selected) { } } -function moveCursor({ textArea, tag, wrapped, removedLastNewLine, select }) { +function moveCursor({ textArea, tag, positionBetweenTags, removedLastNewLine, select }) { var pos; if (!textArea.setSelectionRange) { return; @@ -51,7 +51,7 @@ function moveCursor({ textArea, tag, wrapped, removedLastNewLine, select }) { return textArea.setSelectionRange(startPosition, endPosition); } if (textArea.selectionStart === textArea.selectionEnd) { - if (wrapped) { + if (positionBetweenTags) { pos = textArea.selectionStart - tag.length; } else { pos = textArea.selectionStart; @@ -67,7 +67,6 @@ function moveCursor({ textArea, tag, wrapped, removedLastNewLine, select }) { export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wrap, select }) { var textToInsert, - inserted, selectedSplit, startChar, removedLastNewLine, @@ -155,7 +154,7 @@ export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wr return moveCursor({ textArea, tag: tag.replace(textPlaceholder, selected), - wrap, + positionBetweenTags: wrap && selected.length === 0, removedLastNewLine, select, }); @@ -171,10 +170,6 @@ function updateText({ textArea, tag, blockTag, wrap, select }) { return insertMarkdownText({ textArea, text, tag, blockTag, selected, wrap, select }); } -function replaceRange(s, start, end, substitute) { - return s.substring(0, start) + substitute + s.substring(end); -} - export function addMarkdownListeners(form) { return $('.js-md', form) .off('click') diff --git a/app/assets/javascripts/notebook/cells/output/index.vue b/app/assets/javascripts/notebook/cells/output/index.vue index d9f8604ed10..bd0bcc0d819 100644 --- a/app/assets/javascripts/notebook/cells/output/index.vue +++ b/app/assets/javascripts/notebook/cells/output/index.vue @@ -22,7 +22,7 @@ export default { }, output: { type: Object, - requred: true, + required: true, default: () => ({}), }, }, diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 90fe339e3de..dfb53c986fc 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -16,7 +16,7 @@ import 'vendor/jquery.atwho'; import AjaxCache from '~/lib/utils/ajax_cache'; import Vue from 'vue'; import syntaxHighlight from '~/syntax_highlight'; -import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab/ui'; import axios from './lib/utils/axios_utils'; import { getLocationHash } from './lib/utils/url_utility'; import Flash from './flash'; diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 754c6e79ee4..10e80883c00 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -6,7 +6,6 @@ import Autosize from 'autosize'; import { __, sprintf } from '~/locale'; import Flash from '../../flash'; import Autosave from '../../autosave'; -import TaskList from '../../task_list'; import { capitalizeFirstCharacter, convertToCamelCase, @@ -146,7 +145,6 @@ export default { }); this.initAutoSave(); - this.initTaskList(); }, methods: { ...mapActions([ @@ -298,13 +296,6 @@ Please check your network connection and try again.`; ]); } }, - initTaskList() { - return new TaskList({ - dataType: 'note', - fieldName: 'note', - selector: '.notes', - }); - }, resizeTextarea() { this.$nextTick(() => { Autosize.update(this.$refs.textarea); diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index b209f736c3f..30fcb895369 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -1,10 +1,9 @@ <script> import { mapState, mapActions } from 'vuex'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue'; -import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab/ui'; import { trimFirstCharOfLineContent, getDiffMode } from '~/diffs/store/utils'; export default { @@ -34,7 +33,9 @@ export default { return getDiffMode(this.diffFile); }, hasTruncatedDiffLines() { - return this.discussion.truncatedDiffLines && this.discussion.truncatedDiffLines.length !== 0; + return ( + this.discussion.truncated_diff_lines && this.discussion.truncated_diff_lines.length !== 0 + ); }, isDiscussionsExpanded() { return true; // TODO: @fatihacet - Fix this. @@ -50,19 +51,17 @@ export default { return text ? 'text-file' : 'js-image-file'; }, diffFile() { - return convertObjectPropsToCamelCase(this.discussion.diffFile, { deep: true }); + return this.discussion.diff_file; }, imageDiffHtml() { - return this.discussion.imageDiffHtml; + return this.discussion.image_diff_html; }, userColorScheme() { return window.gon.user_color_scheme; }, normalizedDiffLines() { - if (this.discussion.truncatedDiffLines) { - return this.discussion.truncatedDiffLines.map(line => - trimFirstCharOfLineContent(convertObjectPropsToCamelCase(line)), - ); + if (this.discussion.truncated_diff_lines) { + return this.discussion.truncated_diff_lines.map(line => trimFirstCharOfLineContent(line)); } return []; @@ -97,7 +96,7 @@ export default { class="diff-file file-holder" > <diff-file-header - :discussion-path="discussion.discussionPath" + :discussion-path="discussion.discussion_path" :diff-file="diffFile" :can-current-user-fork="false" :discussions-expanded="isDiscussionsExpanded" @@ -111,15 +110,15 @@ export default { <table> <tr v-for="line in normalizedDiffLines" - :key="line.lineCode" + :key="line.line_code" class="line_holder" > - <td class="diff-line-num old_line">{{ line.oldLine }}</td> - <td class="diff-line-num new_line">{{ line.newLine }}</td> + <td class="diff-line-num old_line">{{ line.old_line }}</td> + <td class="diff-line-num new_line">{{ line.new_line }}</td> <td :class="line.type" class="line_content" - v-html="line.richText" + v-html="line.rich_text" > </td> </tr> @@ -165,17 +164,17 @@ export default { > <diff-viewer :diff-mode="diffMode" - :new-path="diffFile.newPath" - :new-sha="diffFile.diffRefs.headSha" - :old-path="diffFile.oldPath" - :old-sha="diffFile.diffRefs.baseSha" - :file-hash="diffFile.fileHash" + :new-path="diffFile.new_path" + :new-sha="diffFile.diff_refs.head_sha" + :old-path="diffFile.old_path" + :old-sha="diffFile.diff_refs.base_sha" + :file-hash="diffFile.file_hash" :project-path="projectPath" > <image-diff-overlay slot="image-overlay" :discussions="discussion" - :file-hash="diffFile.fileHash" + :file-hash="diffFile.file_hash" :show-comment-icon="true" :should-toggle-discussion="false" badge-class="image-comment-badge" diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue index f7a61fbfcd4..0f0d7f4661a 100644 --- a/app/assets/javascripts/notes/components/note_actions.vue +++ b/app/assets/javascripts/notes/components/note_actions.vue @@ -2,7 +2,7 @@ import { mapGetters } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; import tooltip from '~/vue_shared/directives/tooltip'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { name: 'NoteActions', diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index cf4c35de42c..9375627359c 100644 --- a/app/assets/javascripts/notes/components/note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -4,7 +4,6 @@ import noteEditedText from './note_edited_text.vue'; import noteAwardsList from './note_awards_list.vue'; import noteAttachment from './note_attachment.vue'; import noteForm from './note_form.vue'; -import TaskList from '../../task_list'; import autosave from '../mixins/autosave'; export default { @@ -37,14 +36,12 @@ export default { }, mounted() { this.renderGFM(); - this.initTaskList(); if (this.isEditing) { this.initAutoSave(this.note); } }, updated() { - this.initTaskList(); this.renderGFM(); if (this.isEditing) { @@ -59,15 +56,6 @@ export default { renderGFM() { $(this.$refs['note-body']).renderGFM(); }, - initTaskList() { - if (this.canEdit) { - this.taskList = new TaskList({ - dataType: 'note', - fieldName: 'note', - selector: '.notes', - }); - } - }, handleFormUpdate(note, parentElement, callback) { this.$emit('handleFormUpdate', note, parentElement, callback); }, diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index c1dfa036678..7740967ccd5 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -1,6 +1,5 @@ <script> import { mapActions, mapGetters } from 'vuex'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { truncateSha } from '~/lib/utils/text_utility'; import { s__ } from '~/locale'; import systemNote from '~/vue_shared/components/notes/system_note.vue'; @@ -88,17 +87,16 @@ export default { transformedDiscussion() { return { ...this.discussion.notes[0], - truncatedDiffLines: this.discussion.truncated_diff_lines || [], - truncatedDiffLinesPath: this.discussion.truncated_diff_lines_path, - diffFile: this.discussion.diff_file, - diffDiscussion: this.discussion.diff_discussion, - imageDiffHtml: this.discussion.image_diff_html, + truncated_diff_lines: this.discussion.truncated_diff_lines || [], + truncated_diff_lines_path: this.discussion.truncated_diff_lines_path, + diff_file: this.discussion.diff_file, + diff_discussion: this.discussion.diff_discussion, active: this.discussion.active, - discussionPath: this.discussion.discussion_path, + discussion_path: this.discussion.discussion_path, resolved: this.discussion.resolved, - resolvedBy: this.discussion.resolved_by, - resolvedByPush: this.discussion.resolved_by_push, - resolvedAt: this.discussion.resolved_at, + resolved_by: this.discussion.resolved_by, + resolved_by_push: this.discussion.resolved_by_push, + resolved_at: this.discussion.resolved_at, }; }, author() { @@ -138,7 +136,7 @@ export default { return null; }, resolvedText() { - return this.transformedDiscussion.resolvedByPush ? 'Automatically resolved' : 'Resolved'; + return this.transformedDiscussion.resolved_by_push ? 'Automatically resolved' : 'Resolved'; }, hasMultipleUnresolvedDiscussions() { return this.unresolvedDiscussions.length > 1; @@ -150,12 +148,14 @@ export default { ); }, shouldRenderDiffs() { - const { diffDiscussion, diffFile } = this.transformedDiscussion; - - return diffDiscussion && diffFile && this.renderDiffFile; + return ( + this.transformedDiscussion.diff_discussion && + this.transformedDiscussion.diff_file && + this.renderDiffFile + ); }, shouldGroupReplies() { - return !this.shouldRenderDiffs && !this.transformedDiscussion.diffDiscussion; + return !this.shouldRenderDiffs && !this.transformedDiscussion.diff_discussion; }, shouldRenderHeader() { return this.shouldRenderDiffs; @@ -165,7 +165,7 @@ export default { }, wrapperComponentProps() { if (this.shouldRenderDiffs) { - return { discussion: convertObjectPropsToCamelCase(this.discussion) }; + return { discussion: this.discussion }; } return {}; @@ -184,8 +184,8 @@ export default { }, shouldShowDiscussions() { const isExpanded = this.discussion.expanded; - const { diffDiscussion, resolved } = this.transformedDiscussion; - const isResolvedNonDiffDiscussion = !diffDiscussion && resolved; + const { resolved } = this.transformedDiscussion; + const isResolvedNonDiffDiscussion = !this.transformedDiscussion.diff_discussion && resolved; return isExpanded || this.alwaysExpanded || isResolvedNonDiffDiscussion; }, @@ -333,9 +333,9 @@ Please check your network connection and try again.`; :expanded="discussion.expanded" @toggleHandler="toggleDiscussionHandler" > - <template v-if="transformedDiscussion.diffDiscussion"> + <template v-if="transformedDiscussion.diff_discussion"> started a discussion on - <a :href="transformedDiscussion.discussionPath"> + <a :href="transformedDiscussion.discussion_path"> <template v-if="transformedDiscussion.active"> the diff </template> @@ -356,8 +356,8 @@ Please check your network connection and try again.`; </note-header> <note-edited-text v-if="transformedDiscussion.resolved" - :edited-at="transformedDiscussion.resolvedAt" - :edited-by="transformedDiscussion.resolvedBy" + :edited-at="transformedDiscussion.resolved_at" + :edited-by="transformedDiscussion.resolved_by" :action-text="resolvedText" class-name="discussion-headline-light js-discussion-headline" /> diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index e302a89ee95..9ab91e2abe5 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -46,6 +46,7 @@ export default { 'is-requesting being-posted': this.isRequesting, 'disabled-content': this.isDeleting, target: this.isTarget, + 'is-editable': this.note.current_user.can_edit, }; }, canResolve() { diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index e555279a6ac..69ddfd751e0 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -122,6 +122,7 @@ export default { setTargetNoteHash: 'setTargetNoteHash', toggleDiscussion: 'toggleDiscussion', setNotesFetchedState: 'setNotesFetchedState', + startTaskList: 'startTaskList', }), getComponentName(discussion) { if (discussion.isSkeletonNote) { @@ -157,6 +158,7 @@ export default { this.isFetching = false; }) .then(() => this.$nextTick()) + .then(() => this.startTaskList()) .then(() => this.checkLocationHash()) .catch(() => { this.setLoadingState(false); diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index 88739ffb083..5b2f0540020 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -1,6 +1,8 @@ +import Vue from 'vue'; import $ from 'jquery'; import axios from '~/lib/utils/axios_utils'; import Visibility from 'visibilityjs'; +import TaskList from '../../task_list'; import Flash from '../../flash'; import Poll from '../../lib/utils/poll'; import * as types from './mutation_types'; @@ -58,12 +60,13 @@ export const deleteNote = ({ commit, dispatch }, note) => dispatch('updateMergeRequestWidget'); }); -export const updateNote = ({ commit }, { endpoint, note }) => +export const updateNote = ({ commit, dispatch }, { endpoint, note }) => service .updateNote(endpoint, note) .then(res => res.json()) .then(res => { commit(types.UPDATE_NOTE, res); + dispatch('startTaskList'); }); export const replyToDiscussion = ({ commit }, { endpoint, data }) => @@ -85,6 +88,7 @@ export const createNewNote = ({ commit, dispatch }, { endpoint, data }) => commit(types.ADD_NEW_NOTE, res); dispatch('updateMergeRequestWidget'); + dispatch('startTaskList'); } return res; }); @@ -260,6 +264,8 @@ const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => { commit(types.ADD_NEW_NOTE, note); } }); + + dispatch('startTaskList'); } commit(types.SET_LAST_FETCHED_AT, resp.last_fetched_at); @@ -335,7 +341,7 @@ export const scrollToNoteIfNeeded = (context, el) => { }; export const fetchDiscussionDiffLines = ({ commit }, discussion) => - axios.get(discussion.truncatedDiffLinesPath).then(({ data }) => { + axios.get(discussion.truncated_diff_lines_path).then(({ data }) => { commit(types.SET_DISCUSSION_DIFF_LINES, { discussionId: discussion.id, diffLines: data.truncated_diff_lines, @@ -368,5 +374,16 @@ export const setCommentsDisabled = ({ commit }, data) => { commit(types.DISABLE_COMMENTS, data); }; +export const startTaskList = ({ dispatch }) => + Vue.nextTick( + () => + new TaskList({ + dataType: 'note', + fieldName: 'note', + selector: '.notes .is-editable', + onSuccess: () => dispatch('startTaskList'), + }), + ); + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index c8d9e196103..f6054e0be87 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -102,7 +102,7 @@ export default { discussionsData.forEach(discussion => { if (discussion.diff_file) { Object.assign(discussion, { - fileHash: discussion.diff_file.file_hash, + file_hash: discussion.diff_file.file_hash, truncated_diff_lines: discussion.truncated_diff_lines || [], }); } @@ -195,7 +195,7 @@ export default { const selectedDiscussion = state.discussions.find(disc => disc.id === note.id); note.expanded = true; // override expand flag to prevent collapse if (note.diff_file) { - Object.assign(note, { fileHash: note.diff_file.file_hash }); + Object.assign(note, { file_hash: note.diff_file.file_hash }); } Object.assign(selectedDiscussion, { ...note }); }, diff --git a/app/assets/javascripts/pages/dashboard/issues/index.js b/app/assets/javascripts/pages/dashboard/issues/index.js index c4901dd1cb6..9055738f86e 100644 --- a/app/assets/javascripts/pages/dashboard/issues/index.js +++ b/app/assets/javascripts/pages/dashboard/issues/index.js @@ -1,7 +1,13 @@ import projectSelect from '~/project_select'; -import initLegacyFilters from '~/init_legacy_filters'; +import initFilteredSearch from '~/pages/search/init_filtered_search'; +import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; +import { FILTERED_SEARCH } from '~/pages/constants'; document.addEventListener('DOMContentLoaded', () => { + initFilteredSearch({ + page: FILTERED_SEARCH.ISSUES, + filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, + }); + projectSelect(); - initLegacyFilters(); }); diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js index c4901dd1cb6..260484726f3 100644 --- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js +++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js @@ -1,7 +1,15 @@ import projectSelect from '~/project_select'; -import initLegacyFilters from '~/init_legacy_filters'; +import initFilteredSearch from '~/pages/search/init_filtered_search'; +import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; +import { FILTERED_SEARCH } from '~/pages/constants'; document.addEventListener('DOMContentLoaded', () => { + IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests(); + + initFilteredSearch({ + page: FILTERED_SEARCH.MERGE_REQUESTS, + filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, + }); + projectSelect(); - initLegacyFilters(); }); diff --git a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue index f970a5ebb64..cbe85eaa590 100644 --- a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue +++ b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue @@ -1,7 +1,7 @@ <script> import _ from 'underscore'; import { s__, sprintf } from '~/locale'; -import { GlModal, GlModalDirective } from '@gitlab-org/gitlab-ui'; +import { GlModal, GlModalDirective } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/pages/users/user_overview_block.js b/app/assets/javascripts/pages/users/user_overview_block.js index 2ed177be558..eec2b5ca8e5 100644 --- a/app/assets/javascripts/pages/users/user_overview_block.js +++ b/app/assets/javascripts/pages/users/user_overview_block.js @@ -10,6 +10,7 @@ export default class UserOverviewBlock { limit: DEFAULT_LIMIT, ...options.requestParams, }; + this.postRenderCallback = options.postRenderCallback; this.loadData(); } @@ -43,5 +44,9 @@ export default class UserOverviewBlock { } loadingEl.classList.add('hide'); + + if (this.postRenderCallback) { + this.postRenderCallback.call(this); + } } } diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index 04bcb16f036..aa537d4a43e 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -2,7 +2,8 @@ import $ from 'jquery'; import axios from '~/lib/utils/axios_utils'; import Activities from '~/activities'; import { localTimeAgo } from '~/lib/utils/datetime_utility'; -import { __, sprintf } from '~/locale'; +import AjaxCache from '~/lib/utils/ajax_cache'; +import { __ } from '~/locale'; import flash from '~/flash'; import ActivityCalendar from './activity_calendar'; import UserOverviewBlock from './user_overview_block'; @@ -62,23 +63,20 @@ import UserOverviewBlock from './user_overview_block'; * </div> */ -const CALENDAR_TEMPLATES = { - activity: ` - <div class="clearfix calendar"> - <div class="js-contrib-calendar"></div> - <div class="calendar-hint bottom-right"></div> - </div> - `, - overview: ` - <div class="clearfix calendar"> - <div class="calendar-hint"></div> - <div class="js-contrib-calendar prepend-top-20"></div> - </div> - `, -}; +const CALENDAR_TEMPLATE = ` + <div class="clearfix calendar"> + <div class="js-contrib-calendar"></div> + <div class="calendar-hint bottom-right"></div> + </div> +`; const CALENDAR_PERIOD_6_MONTHS = 6; const CALENDAR_PERIOD_12_MONTHS = 12; +/* computation based on + * width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group); + * (see activity_calendar.js) + */ +const OVERVIEW_CALENDAR_BREAKPOINT = 918; export default class UserTabs { constructor({ defaultAction, action, parentEl }) { @@ -105,6 +103,12 @@ export default class UserTabs { .off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)) .on('click', '.gl-pagination a', event => this.changeProjectsPage(event)); + + window.addEventListener('resize', () => this.onResize()); + } + + onResize() { + this.loadActivityCalendar(); } changeProjectsPage(e) { @@ -167,8 +171,6 @@ export default class UserTabs { return; } - this.loadActivityCalendar('activity'); - // eslint-disable-next-line no-new new Activities('#activity'); @@ -180,10 +182,10 @@ export default class UserTabs { return; } - this.loadActivityCalendar('overview'); + this.loadActivityCalendar(); UserTabs.renderMostRecentBlocks('#js-overview .activities-block', { - requestParams: { limit: 5 }, + requestParams: { limit: 10 }, }); UserTabs.renderMostRecentBlocks('#js-overview .projects-block', { requestParams: { limit: 10, skip_pagination: true }, @@ -198,52 +200,39 @@ export default class UserTabs { container, url: $(`${container} .overview-content-list`).data('href'), ...options, + postRenderCallback: () => localTimeAgo($('.js-timeago', container)), }); } - loadActivityCalendar(action) { - const monthsAgo = action === 'overview' ? CALENDAR_PERIOD_6_MONTHS : CALENDAR_PERIOD_12_MONTHS; + loadActivityCalendar() { const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar'); const calendarPath = $calendarWrap.data('calendarPath'); + + AjaxCache.retrieve(calendarPath) + .then(data => UserTabs.renderActivityCalendar(data, $calendarWrap)) + .catch(() => flash(__('There was an error loading users activity calendar.'))); + } + + static renderActivityCalendar(data, $calendarWrap) { + const monthsAgo = UserTabs.getVisibleCalendarPeriod($calendarWrap); const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); const utcOffset = $calendarWrap.data('utcOffset'); - let utcFormatted = 'UTC'; - if (utcOffset !== 0) { - utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${utcOffset / 3600}`; - } + const calendarHint = __('Issues, merge requests, pushes and comments.'); - axios - .get(calendarPath) - .then(({ data }) => { - $calendarWrap.html(CALENDAR_TEMPLATES[action]); - - let calendarHint = ''; - - if (action === 'activity') { - calendarHint = sprintf( - __( - 'Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})', - ), - { utcFormatted }, - ); - } else if (action === 'overview') { - calendarHint = __('Issues, merge requests, pushes and comments.'); - } - - $calendarWrap.find('.calendar-hint').text(calendarHint); - - // eslint-disable-next-line no-new - new ActivityCalendar( - '.tab-pane.active .js-contrib-calendar', - '.tab-pane.active .user-calendar-activities', - data, - calendarActivitiesPath, - utcOffset, - 0, - monthsAgo, - ); - }) - .catch(() => flash(__('There was an error loading users activity calendar.'))); + $calendarWrap.html(CALENDAR_TEMPLATE); + + $calendarWrap.find('.calendar-hint').text(calendarHint); + + // eslint-disable-next-line no-new + new ActivityCalendar( + '.tab-pane.active .js-contrib-calendar', + '.tab-pane.active .user-calendar-activities', + data, + calendarActivitiesPath, + utcOffset, + 0, + monthsAgo, + ); } toggleLoading(status) { @@ -267,4 +256,11 @@ export default class UserTabs { getCurrentAction() { return this.$parentEl.find('.nav-links a.active').data('action'); } + + static getVisibleCalendarPeriod($calendarWrap) { + const width = $calendarWrap.width(); + return width < OVERVIEW_CALENDAR_BREAKPOINT + ? CALENDAR_PERIOD_6_MONTHS + : CALENDAR_PERIOD_12_MONTHS; + } } diff --git a/app/assets/javascripts/pipelines/components/empty_state.vue b/app/assets/javascripts/pipelines/components/empty_state.vue index 8a0259ed5a5..f756c651684 100644 --- a/app/assets/javascripts/pipelines/components/empty_state.vue +++ b/app/assets/javascripts/pipelines/components/empty_state.vue @@ -1,5 +1,5 @@ <script> -import { GlButton } from '@gitlab-org/gitlab-ui'; +import { GlButton } from '@gitlab/ui'; export default { name: 'PipelinesEmptyState', diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue index b82e28a0735..3a39dfe181d 100644 --- a/app/assets/javascripts/pipelines/components/graph/action_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue @@ -1,10 +1,9 @@ <script> -import $ from 'jquery'; +import { GlTooltipDirective, GlButton } from '@gitlab/ui'; import axios from '~/lib/utils/axios_utils'; import { dasherize } from '~/lib/utils/text_utility'; import { __ } from '~/locale'; import createFlash from '~/flash'; -import tooltip from '~/vue_shared/directives/tooltip'; import Icon from '~/vue_shared/components/icon.vue'; /** @@ -20,23 +19,20 @@ import Icon from '~/vue_shared/components/icon.vue'; export default { components: { Icon, + GlButton, }, - directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, - props: { tooltipText: { type: String, required: true, }, - link: { type: String, required: true, }, - actionIcon: { type: String, required: true, @@ -47,7 +43,6 @@ export default { isDisabled: false, }; }, - computed: { cssClass() { const actionIconDash = dasherize(this.actionIcon); @@ -62,8 +57,7 @@ export default { * */ onClickAction() { - $(this.$el).tooltip('hide'); - + this.$root.$emit('bv::hide::tooltip', `js-ci-action-${this.link}`); this.isDisabled = true; axios @@ -82,18 +76,16 @@ export default { }; </script> <template> - <button - v-tooltip + <gl-button + :id="`js-ci-action-${link}`" + v-gl-tooltip="{ boundary: 'viewport' }" :title="tooltipText" :class="cssClass" :disabled="isDisabled" - type="button" class="js-ci-action btn btn-blank btn-transparent ci-action-icon-container ci-action-icon-wrapper" - data-container="body" - data-boundary="viewport" @click="onClickAction" > <icon :name="actionIcon"/> - </button> + </gl-button> </template> diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue index 4de8b3401e8..6c9a11c3829 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue @@ -1,6 +1,6 @@ <script> import _ from 'underscore'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import StageColumnComponent from './stage_column_component.vue'; export default { diff --git a/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue index 34bada533df..2670ea29db6 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue @@ -1,8 +1,8 @@ <script> import $ from 'jquery'; +import { GlTooltipDirective } from '@gitlab/ui'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import JobItem from './job_item.vue'; -import tooltip from '../../../vue_shared/directives/tooltip'; /** * Renders the dropdown for the pipeline graph. @@ -12,32 +12,27 @@ import tooltip from '../../../vue_shared/directives/tooltip'; */ export default { directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, - components: { JobItem, CiIcon, }, - props: { group: { type: Object, required: true, }, }, - computed: { tooltipText() { const { name, status } = this.group; return `${name} - ${status.label}`; }, }, - mounted() { this.stopDropdownClickPropagation(); }, - methods: { /** * When the user right clicks or cmd/ctrl + click in the group name or the action icon @@ -65,12 +60,10 @@ export default { <template> <div class="ci-job-dropdown-container dropdown dropright"> <button - v-tooltip + v-gl-tooltip.hover="{ boundary: 'viewport' }" :title="tooltipText" type="button" data-toggle="dropdown" - data-container="body" - data-boundary="viewport" data-display="static" class="dropdown-menu-toggle build-content" > diff --git a/app/assets/javascripts/pipelines/components/graph/job_item.vue b/app/assets/javascripts/pipelines/components/graph/job_item.vue index 7cdde8a53b3..e6abf32decc 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_item.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_item.vue @@ -1,7 +1,7 @@ <script> import ActionComponent from './action_component.vue'; import JobNameComponent from './job_name_component.vue'; -import tooltip from '../../../vue_shared/directives/tooltip'; +import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import { sprintf } from '~/locale'; import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin'; @@ -34,9 +34,10 @@ export default { components: { ActionComponent, JobNameComponent, + GlLink, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, mixins: [delayedJobMixin], props: { @@ -55,7 +56,6 @@ export default { default: Infinity, }, }, - computed: { status() { return this.job && this.job.status ? this.job.status : {}; @@ -88,7 +88,6 @@ export default { tooltipBoundary() { return this.dropdownLength < 5 ? 'viewport' : null; }, - /** * Verifies if the provided job has an action path * @@ -98,7 +97,6 @@ export default { return this.job.status && this.job.status.action && this.job.status.action.path; }, }, - methods: { pipelineActionRequestComplete() { this.$emit('pipelineActionRequestComplete'); @@ -108,30 +106,26 @@ export default { </script> <template> <div class="ci-job-component"> - <a + <gl-link v-if="status.has_details" - v-tooltip + v-gl-tooltip="{ boundary: tooltipBoundary }" :href="status.details_path" :title="tooltipText" :class="cssClassJobName" - :data-boundary="tooltipBoundary" - data-container="body" class="js-pipeline-graph-job-link" > - <job-name-component :name="job.name" :status="job.status" /> - </a> + </gl-link> <div v-else - v-tooltip + v-gl-tooltip :title="tooltipText" :class="cssClassJobName" class="js-job-component-tooltip non-details-job-component" - data-container="body" > <job-name-component diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue index efbab51d200..d5f931943d5 100644 --- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue @@ -13,34 +13,28 @@ export default { type: String, required: true, }, - groups: { type: Array, required: true, }, - isFirstColumn: { type: Boolean, required: false, default: false, }, - stageConnectorClass: { type: String, required: false, default: '', }, }, - methods: { groupId(group) { return `ci-badge-${_.escape(group.name)}`; }, - buildConnnectorClass(index) { return index === 0 && !this.isFirstColumn ? 'left-connector' : ''; }, - pipelineActionRequestComplete() { this.$emit('refreshPipelineGraph'); }, @@ -50,7 +44,8 @@ export default { <template> <li :class="stageConnectorClass" - class="stage-column"> + class="stage-column" + > <div class="stage-name"> {{ title }} </div> @@ -78,7 +73,6 @@ export default { :group="group" @pipelineActionRequestComplete="pipelineActionRequestComplete" /> - </li> </ul> </div> diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue index 8f004b491c8..ac7fa4b195e 100644 --- a/app/assets/javascripts/pipelines/components/header_component.vue +++ b/app/assets/javascripts/pipelines/components/header_component.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import ciHeader from '../../vue_shared/components/header_ci_component.vue'; import eventHub from '../event_hub'; diff --git a/app/assets/javascripts/pipelines/components/nav_controls.vue b/app/assets/javascripts/pipelines/components/nav_controls.vue index 0911acbb131..5104fe36b42 100644 --- a/app/assets/javascripts/pipelines/components/nav_controls.vue +++ b/app/assets/javascripts/pipelines/components/nav_controls.vue @@ -1,5 +1,5 @@ <script> -import { GlLink, GlButton } from '@gitlab-org/gitlab-ui'; +import { GlLink, GlButton } from '@gitlab/ui'; import LoadingButton from '../../vue_shared/components/loading_button.vue'; export default { diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue index be4b37f3c8c..249f7b9f368 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue @@ -1,5 +1,5 @@ <script> -import { GlLink, GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlLink, GlTooltipDirective } from '@gitlab/ui'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import popover from '~/vue_shared/directives/popover'; diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue index 811495c45a9..112c1559f25 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue @@ -1,5 +1,5 @@ <script> -import { GlButton, GlTooltipDirective, GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlButton, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui'; import { s__, sprintf } from '~/locale'; import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; import eventHub from '../event_hub'; diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue index 2abb24b87b6..d7d9eb00faa 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue @@ -1,5 +1,5 @@ <script> -import { GlLink, GlButton, GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlLink, GlButton, GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; export default { diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index 026d533d10f..fd674a8d447 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -308,7 +308,8 @@ export default { <div v-for="(stage, index) in pipeline.details.stages" :key="index" - class="stage-container dropdown js-mini-pipeline-graph"> + class="stage-container dropdown js-mini-pipeline-graph" + > <pipeline-stage :type="$options.pipelinesTable" :stage="stage" diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 3df8f7a6da6..1d62472671a 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -13,14 +13,13 @@ */ import $ from 'jquery'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui'; import { __ } from '../../locale'; import Flash from '../../flash'; import axios from '../../lib/utils/axios_utils'; import eventHub from '../event_hub'; import Icon from '../../vue_shared/components/icon.vue'; import JobItem from './graph/job_item.vue'; -import tooltip from '../../vue_shared/directives/tooltip'; import { PIPELINES_TABLE } from '../constants'; export default { @@ -31,7 +30,7 @@ export default { }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { @@ -159,11 +158,10 @@ export default { <button id="stageDropdown" ref="dropdown" - v-tooltip + v-gl-tooltip.hover :class="triggerButtonClass" :title="stage.title" class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button" - data-placement="top" data-toggle="dropdown" data-display="static" type="button" diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js index 41bc5dcce5c..32bfa47e5f2 100644 --- a/app/assets/javascripts/pipelines/mixins/pipelines.js +++ b/app/assets/javascripts/pipelines/mixins/pipelines.js @@ -1,5 +1,5 @@ import Visibility from 'visibilityjs'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import { __ } from '../../locale'; import Flash from '../../flash'; import Poll from '../../lib/utils/poll'; diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js index f5dae5ad808..5a3407693e5 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js @@ -2,7 +2,7 @@ import _ from 'underscore'; import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue'; import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue'; import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import store from '../store'; diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue index 9a729ca9b91..7400b685c7e 100644 --- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue +++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue @@ -5,7 +5,7 @@ import Poll from '~/lib/utils/poll'; import Flash from '~/flash'; import { s__, sprintf } from '~/locale'; import tooltip from '~/vue_shared/directives/tooltip'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import CommitPipelineService from '../services/commit_pipeline_service'; export default { diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue index 0a906f40f5a..6f94f5d6d2a 100644 --- a/app/assets/javascripts/registry/components/app.vue +++ b/app/assets/javascripts/registry/components/app.vue @@ -1,6 +1,6 @@ <script> import { mapGetters, mapActions } from 'vuex'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import Flash from '../../flash'; import store from '../stores'; import collapsibleContainer from './collapsible_container.vue'; diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue index be9816a55c4..d85de973740 100644 --- a/app/assets/javascripts/registry/components/collapsible_container.vue +++ b/app/assets/javascripts/registry/components/collapsible_container.vue @@ -1,6 +1,6 @@ <script> import { mapActions } from 'vuex'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import Flash from '../../flash'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import tooltip from '../../vue_shared/directives/tooltip'; diff --git a/app/assets/javascripts/reports/components/summary_row.vue b/app/assets/javascripts/reports/components/summary_row.vue index a44ba833b63..7e73ccb6c28 100644 --- a/app/assets/javascripts/reports/components/summary_row.vue +++ b/app/assets/javascripts/reports/components/summary_row.vue @@ -1,7 +1,7 @@ <script> import CiIcon from '~/vue_shared/components/ci_icon.vue'; import Popover from '~/vue_shared/components/help_popover.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; /** * Renders the summary row for each report diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 17def77b2d7..0a4583b5861 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -253,7 +253,6 @@ export class SearchAutocomplete { } getCategoryContents() { - const userId = gon.current_user_id; const userName = gon.current_username; const { projectOptions, groupOptions, dashboardOptions } = gl; @@ -279,21 +278,21 @@ export class SearchAutocomplete { const issueItems = [ { text: s__('SearchAutocomplete|Issues assigned to me'), - url: `${issuesPath}/?assignee_id=${userId}`, + url: `${issuesPath}/?assignee_username=${userName}`, }, { text: s__("SearchAutocomplete|Issues I've created"), - url: `${issuesPath}/?author_id=${userId}`, + url: `${issuesPath}/?author_username=${userName}`, }, ]; const mergeRequestItems = [ { text: s__('SearchAutocomplete|Merge requests assigned to me'), - url: `${mrPath}/?assignee_id=${userId}`, + url: `${mrPath}/?assignee_username=${userName}`, }, { text: s__("SearchAutocomplete|Merge requests I've created"), - url: `${mrPath}/?author_id=${userId}`, + url: `${mrPath}/?author_username=${userName}`, }, ]; diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue index 4d461baf74d..8a48eea5c89 100644 --- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue +++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue @@ -5,7 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue'; import GfmAutoComplete from '~/gfm_auto_complete'; import { __, s__ } from '~/locale'; import Api from '~/api'; -import { GlModal, GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlModal, GlTooltipDirective } from '@gitlab/ui'; import eventHub from './event_hub'; import EmojiMenuInModal from './emoji_menu_in_modal'; diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue index fe73f6a0cef..43d2f754bac 100644 --- a/app/assets/javascripts/sidebar/components/participants/participants.vue +++ b/app/assets/javascripts/sidebar/components/participants/participants.vue @@ -2,7 +2,7 @@ import { __, n__, sprintf } from '~/locale'; import tooltip from '~/vue_shared/directives/tooltip'; import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { directives: { diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue index b145e5dc5e2..87780411c26 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue @@ -1,7 +1,7 @@ <script> import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility'; import tooltip from '../../../vue_shared/directives/tooltip'; -import { GlProgressBar } from '@gitlab-org/gitlab-ui'; +import { GlProgressBar } from '@gitlab/ui'; export default { name: 'TimeTrackingComparisonPane', diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue index 913a616d9f1..7edef35461a 100644 --- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue +++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue @@ -1,7 +1,7 @@ <script> import { __ } from '~/locale'; import tooltip from '~/vue_shared/directives/tooltip'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index 2a8380f5f2b..fe741dc60cb 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -33,6 +33,10 @@ export default { type: Object, required: true, }, + showMetrics: { + type: Boolean, + required: true, + }, }, deployedTextMap: { running: __('Deploying to'), @@ -40,10 +44,8 @@ export default { failed: __('Failed to deploy to'), }, data() { - const features = window.gon.features || {}; return { isStopping: false, - enableCiEnvironmentsStatusChanges: features.ciEnvironmentsStatusChanges, }; }, computed: { @@ -74,10 +76,10 @@ export default { : ''; }, shouldRenderDropdown() { - return ( - this.enableCiEnvironmentsStatusChanges && - (this.deployment.changes && this.deployment.changes.length > 0) - ); + return this.deployment.changes && this.deployment.changes.length > 0; + }, + showMemoryUsage() { + return this.hasMetrics && this.showMetrics; }, }, methods: { @@ -141,7 +143,7 @@ export default { {{ deployTimeago }} </span> <memory-usage - v-if="hasMetrics" + v-if="showMemoryUsage" :metrics-url="deployment.metrics_url" :metrics-monitoring-url="deployment.metrics_monitoring_url" /> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue index b3340290ed3..060361a6516 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import ciIcon from '../../vue_shared/components/ci_icon.vue'; export default { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue index 4bfbdcf1404..409fc2f2db4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import eventHub from '../../event_hub'; import statusIcon from '../mr_widget_status_icon.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue index 7e33021e4b4..fb9ca897844 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue @@ -6,7 +6,7 @@ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue'; import statusIcon from '../mr_widget_status_icon.vue'; import eventHub from '../../event_hub'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { name: 'MRWidgetMerged', diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue index 0e714cc2aa1..5a7f6cc36fa 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import simplePoll from '../../../lib/utils/simple_poll'; import eventHub from '../../event_hub'; import statusIcon from '../mr_widget_status_icon.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 063d1e15544..3b840540657 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -312,6 +312,7 @@ export default { :key="`pre-merge-deploy-${deployment.id}`" class="js-pre-merge-deploy" :deployment="deployment" + :show-metrics="false" /> <div class="mr-section-container"> <grouped-test-reports-app @@ -366,6 +367,7 @@ export default { v-for="postMergeDeployment in mr.postMergeDeployments" :key="`post-merge-deploy-${postMergeDeployment.id}`" :deployment="postMergeDeployment" + :show-metrics="true" class="js-post-deployment" /> </template> diff --git a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue index 766fc211bf5..c5fbaf87b00 100644 --- a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import { pluralize } from '~/lib/utils/text_utility'; import { __, sprintf } from '~/locale'; diff --git a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue index 6780254827f..b0962684430 100644 --- a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue +++ b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import CiIcon from './ci_icon.vue'; /** * Renders CI Badge link with CI icon and status text based on diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue index 6b90a1f540e..671b4909839 100644 --- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue +++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue @@ -12,7 +12,7 @@ * css-class="btn-transparent" * /> */ -import { GlButton, GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlButton, GlTooltipDirective } from '@gitlab/ui'; import Icon from '../components/icon.vue'; export default { diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index b1139f34e41..420bd25b496 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import UserAvatarLink from './user_avatar/user_avatar_link.vue'; import Icon from '../../vue_shared/components/icon.vue'; diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue index c78b96695cf..97bdd9915c5 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; import Icon from '../../icon.vue'; import { numberToHumanSize } from '../../../../lib/utils/number_utils'; diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue index 419987d2c50..a084cfdf612 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue @@ -2,7 +2,7 @@ import axios from '~/lib/utils/axios_utils'; import { __ } from '~/locale'; import $ from 'jquery'; -import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab/ui'; const { CancelToken } = axios; let axiosSource; diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue index e68a2aa73fa..d7f24c1afc5 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue @@ -44,8 +44,11 @@ export default { isNew() { return this.diffMode === diffModes.new; }, + isRenamed() { + return this.diffMode === diffModes.renamed; + }, imagePath() { - return this.isNew ? this.newPath : this.oldPath; + return this.isNew || this.isRenamed ? this.newPath : this.oldPath; }, }, methods: { @@ -114,7 +117,7 @@ export default { }]" > <slot - v-if="isNew" + v-if="isNew || isRenamed" slot="image-overlay" name="image-overlay" > diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue index 0e194eaaed5..0f4effda79f 100644 --- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue +++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue @@ -1,6 +1,6 @@ <script> import { __ } from '~/locale'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue index 03818be6a69..545be568ad3 100644 --- a/app/assets/javascripts/vue_shared/components/file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/file_icon.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import getIconForFile from './file_icon/file_icon_map'; import icon from '../../vue_shared/components/icon.vue'; diff --git a/app/assets/javascripts/vue_shared/components/gl_countdown.vue b/app/assets/javascripts/vue_shared/components/gl_countdown.vue index a35986b2d03..97f7998f461 100644 --- a/app/assets/javascripts/vue_shared/components/gl_countdown.vue +++ b/app/assets/javascripts/vue_shared/components/gl_countdown.vue @@ -1,6 +1,6 @@ <script> import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility'; -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; /** * Counts down to a given end date. diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index aee88cae48d..1a91a8b81e3 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -1,8 +1,9 @@ <script> +import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui'; import CiIconBadge from './ci_badge_link.vue'; import TimeagoTooltip from './time_ago_tooltip.vue'; -import tooltip from '../directives/tooltip'; import UserAvatarImage from './user_avatar/user_avatar_image.vue'; +import LoadingButton from '~/vue_shared/components/loading_button.vue'; /** * Renders header component for job and pipeline page based on UI mockups @@ -16,9 +17,12 @@ export default { CiIconBadge, TimeagoTooltip, UserAvatarImage, + GlLink, + GlButton, + LoadingButton, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { status: { @@ -98,8 +102,8 @@ export default { by <template v-if="user"> - <a - v-tooltip + <gl-link + v-gl-tooltip :href="user.path" :title="user.email" class="js-user-link commit-committer-link" @@ -113,7 +117,7 @@ export default { /> {{ user.name }} - </a> + </gl-link> <span v-if="user.status_tooltip_html" v-html="user.status_tooltip_html"></span> @@ -127,16 +131,16 @@ export default { <template v-for="(action, i) in actions" > - <a + <gl-link v-if="action.type === 'link'" :key="i" :href="action.path" :class="action.cssClass" > {{ action.label }} - </a> + </gl-link> - <a + <gl-link v-else-if="action.type === 'ujs-link'" :key="i" :href="action.path" @@ -145,31 +149,24 @@ export default { rel="nofollow" > {{ action.label }} - </a> + </gl-link> - <button + <loading-button v-else-if="action.type === 'button'" :key="i" + :loading="action.isLoading" :disabled="action.isLoading" :class="action.cssClass" - type="button" + container-class="d-inline" + :label="action.label" @click="onClickAction(action)" - > - {{ action.label }} - <i - v-show="action.isLoading" - class="fa fa-spin fa-spinner" - aria-hidden="true" - > - </i> - </button> + /> </template> </section> - <button + <gl-button v-if="hasSidebarButton" id="toggleSidebar" - type="button" - class="btn btn-default d-block d-sm-none + class="d-block d-sm-none sidebar-toggle-btn js-sidebar-build-toggle js-sidebar-build-toggle-header" @click="onClickSidebarButton" > @@ -179,6 +176,6 @@ sidebar-toggle-btn js-sidebar-build-toggle js-sidebar-build-toggle-header" aria-labelledby="toggleSidebar" > </i> - </button> + </gl-button> </header> </template> diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue index 69d7e5c46f5..9bae8a32a8c 100644 --- a/app/assets/javascripts/vue_shared/components/loading_button.vue +++ b/app/assets/javascripts/vue_shared/components/loading_button.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; /* eslint-disable vue/require-default-prop */ /* This is a re-usable vue component for rendering a button that will probably be sending off ajax requests and need diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 27e3f314dd3..ca9e57a9b00 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -1,6 +1,6 @@ <script> import $ from 'jquery'; -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import ToolbarButton from './toolbar_button.vue'; import Icon from '../icon.vue'; diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue index b0a93794013..3cb48023002 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; export default { components: { diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue index 91d0bbfc21c..13af4b627de 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue @@ -1,5 +1,5 @@ <script> -import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlTooltipDirective } from '@gitlab/ui'; import Icon from '../icon.vue'; export default { diff --git a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue index 8cb72afcdc0..2dcd161b4fb 100644 --- a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue @@ -1,5 +1,5 @@ <script> -import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab/ui'; export default { name: 'SkeletonNote', diff --git a/app/assets/javascripts/vue_shared/components/pagination_links.vue b/app/assets/javascripts/vue_shared/components/pagination_links.vue index 89dcf049f6e..0b44f8578cb 100644 --- a/app/assets/javascripts/vue_shared/components/pagination_links.vue +++ b/app/assets/javascripts/vue_shared/components/pagination_links.vue @@ -1,5 +1,5 @@ <script> -import { GlPagination } from '@gitlab-org/gitlab-ui'; +import { GlPagination } from '@gitlab/ui'; import { s__ } from '../../locale'; export default { diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue index 5b12bb6b59e..6aa880603b9 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import datePicker from '../pikaday.vue'; import toggleSidebar from './toggle_sidebar.vue'; import collapsedCalendarIcon from './collapsed_calendar_icon.vue'; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue index e50d612ce36..98b8b6460fe 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue @@ -4,7 +4,7 @@ import { __ } from '~/locale'; import LabelsSelect from '~/labels_select'; import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue'; -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import DropdownTitle from './dropdown_title.vue'; import DropdownValue from './dropdown_value.vue'; import DropdownValueCollapsed from './dropdown_value_collapsed.vue'; diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue index e7cb5cfac12..5d1c92c3b3d 100644 --- a/app/assets/javascripts/vue_shared/components/toggle_button.vue +++ b/app/assets/javascripts/vue_shared/components/toggle_button.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab-org/gitlab-ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import { s__ } from '../../locale'; import icon from './icon.vue'; diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue index 4cfb1ded0a9..c78d98ccd9e 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue @@ -15,7 +15,7 @@ */ -import { GlTooltip } from '@gitlab-org/gitlab-ui'; +import { GlTooltip } from '@gitlab/ui'; import defaultAvatarUrl from 'images/no_avatar.png'; import { placeholderImage } from '../../../lazy_loader'; diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue index 351a639c6e8..6dd519ea56d 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue @@ -17,7 +17,7 @@ */ -import { GlLink, GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { GlLink, GlTooltipDirective } from '@gitlab/ui'; import userAvatarImage from './user_avatar_image.vue'; export default { diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 6bdcb20210b..037a5adfb7e 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -415,7 +415,6 @@ span.idiff { } .preview-container { - height: 100%; overflow: auto; .file-container { diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 6d998fa1e07..3c7bf0b0e46 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -842,11 +842,15 @@ background-repeat: repeat; } - .diff-file-discussions + .discussion-form::before { - width: auto; - margin-left: -16px; - margin-right: -16px; - margin-bottom: 16px; + .diff-file-discussions + .discussion-form { + padding: $gl-padding; + + &::before { + width: auto; + margin-left: -$gl-padding; + margin-right: -$gl-padding; + margin-bottom: $gl-padding; + } } .notes { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 00b06aea898..38851de6401 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -47,12 +47,6 @@ @extend .fixed-width-container; } } - - .diffs { - .mr-version-controls { - @extend .fixed-width-container; - } - } } .issuable-details { @@ -733,6 +727,10 @@ .issuable-main-info { flex: 1 auto; margin-right: 10px; + + .issue-weight-icon { + vertical-align: sub; + } } .issuable-meta { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 855d73a9939..97b3f696139 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -176,8 +176,10 @@ background-color: $white-light; } -.discussion-form-container { - padding: $gl-padding-top $gl-padding $gl-padding; +table { + .discussion-form-container { + padding: $gl-padding-top $gl-padding $gl-padding; + } } .discussion-notes .disabled-comment { @@ -239,7 +241,7 @@ .discussion-reply-holder { background-color: $white-light; padding: 10px 16px; - border-radius: 0 0 $border-radius-default $border-radius-default; + border-radius: 0 0 3px 3px; &.is-replying { padding-bottom: $gl-padding; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index c57c1eee350..085ff27e6ef 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -1,6 +1,6 @@ $system-note-icon-size: 32px; $system-note-svg-size: 16px; -$note-form-margin-left: 70px; +$note-form-margin-left: 72px; @mixin vertical-line($left) { &::before { @@ -13,12 +13,32 @@ $note-form-margin-left: 70px; } } +@mixin outline-comment() { + margin: $gl-padding; + border: 1px solid $border-color; + border-radius: $border-radius-default; +} + .note-wrapper { padding: $gl-padding; + + &.outlined { + @include outline-comment(); + } +} + +.main-notes-list { + @include vertical-line(36px); } -.issuable-discussion { - .notes.timeline > .timeline-entry { +.notes { + display: block; + list-style: none; + margin: 0; + padding: 0; + position: relative; + + &.timeline > .timeline-entry { border: 1px solid $border-color; border-radius: $border-radius-default; margin: $gl-padding 0; @@ -51,18 +71,6 @@ $note-form-margin-left: 70px; border-top: 1px solid $border-color; } } -} - -.main-notes-list { - @include vertical-line(39px); -} - -.notes { - display: block; - list-style: none; - margin: 0; - padding: 0; - position: relative; > .note-discussion { .card { @@ -71,10 +79,6 @@ $note-form-margin-left: 70px; li.note { border-bottom: 1px solid $border-color; - - &:first-child { - border-radius: $border-radius-default $border-radius-default 0 0; - } } } @@ -268,7 +272,7 @@ $note-form-margin-left: 70px; } .system-note { - padding: 6px $gl-padding-24; + padding: 6px 21px; margin: $gl-padding-24 0; background-color: transparent; @@ -387,6 +391,7 @@ $note-form-margin-left: 70px; line-height: 42px; padding: 0 $gl-padding; border-top: 1px solid $border-color; + border-radius: 0; &:hover { background-color: $gray-light; @@ -402,6 +407,24 @@ $note-form-margin-left: 70px; } } +.tab-pane.notes { + .diff-file .notes .system-note { + margin: 0; + } +} + +.tab-pane.diffs { + .system-note { + padding: 0 $gl-padding; + margin-left: 20px; + } + + .notes > .note-discussion li.note.system-note { + border-bottom: 0; + padding: 0 $gl-padding; + } +} + .diff-file { .is-over { .add-diff-note { @@ -410,18 +433,12 @@ $note-form-margin-left: 70px; } .discussion-notes { - &:not(:first-child) { - border-top: 1px solid $white-normal; - margin-top: 20px; - } - &:not(:last-child) { - border-bottom: 1px solid $white-normal; - margin-bottom: 20px; + margin-bottom: 0; } .system-note { - margin: 0; + background-color: $white-light; padding: $gl-padding; } } @@ -479,9 +496,12 @@ $note-form-margin-left: 70px; } .note-wrapper { - margin: $gl-padding; - border: 1px solid $border-color; - border-radius: $border-radius-default; + @include outline-comment(); + + &.system-note { + border: 0; + margin-left: 20px; + } } .discussion-reply-holder { @@ -491,6 +511,20 @@ $note-form-margin-left: 70px; } } +.commit-diff { + .notes { + @include vertical-line(52px); + } + + .notes_content { + background-color: $white-light; + } + + .discussion-reply-holder { + border-top: 1px solid $border-color; + } +} + .discussion-header, .note-header-info { a { @@ -897,3 +931,23 @@ $note-form-margin-left: 70px; } } } + +//This needs to be deleted when Snippet/Commit comments are convered to Vue +// See https://gitlab.com/gitlab-org/gitlab-ce/issues/53918#note_117038785 +.unstyled-comments { + + .discussion-header { + padding: $gl-padding; + border-bottom: 1px solid $border-color; + } + + .note-wrapper.outlined { + margin: 0; + border: 0; + border-radius: 0; + } + + .discussion-form-container { + padding: $gl-padding; + } +} diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb index 7701f2e645b..fc877142418 100644 --- a/app/controllers/admin/background_jobs_controller.rb +++ b/app/controllers/admin/background_jobs_controller.rb @@ -1,9 +1,4 @@ # frozen_string_literal: true class Admin::BackgroundJobsController < Admin::ApplicationController - def show - ps_output, _ = Gitlab::Popen.popen(%W(ps ww -U #{Gitlab.config.gitlab.user} -o pid,pcpu,pmem,stat,start,command)) - @sidekiq_processes = ps_output.split("\n").grep(/sidekiq \d+\.\d+\.\d+/) - @concurrency = Sidekiq.options[:concurrency] - end end diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb index f5825ecb19a..706bcc1e549 100644 --- a/app/controllers/admin/impersonation_tokens_controller.rb +++ b/app/controllers/admin/impersonation_tokens_controller.rb @@ -11,6 +11,7 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController @impersonation_token = finder.build(impersonation_token_params) if @impersonation_token.save + PersonalAccessToken.redis_store!(current_user.id, @impersonation_token.token) redirect_to admin_user_impersonation_tokens_path, notice: "A new impersonation token has been created." else set_index_vars @@ -53,6 +54,8 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController @impersonation_token ||= finder.build @inactive_impersonation_tokens = finder(state: 'inactive').execute @active_impersonation_tokens = finder(state: 'active').execute.order(:expires_at) + + @new_impersonation_token = PersonalAccessToken.redis_getdel(current_user.id) end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7f4aa8244ac..b839da7770d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -181,11 +181,11 @@ class ApplicationController < ActionController::Base Ability.allowed?(object, action, subject) end - def access_denied!(message = nil) + def access_denied!(message = nil, status = nil) # If we display a custom access denied message to the user, we don't want to # hide existence of the resource, rather tell them they cannot access it using # the provided message - status = message.present? ? :forbidden : :not_found + status ||= message.present? ? :forbidden : :not_found respond_to do |format| format.any { head status } diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 5217b4be928..34a8c50fcbd 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -81,36 +81,36 @@ module IssuableCollections end def issuable_finder_for(finder_class) - finder_class.new(current_user, filter_params) + finder_class.new(current_user, finder_options) end # rubocop:disable Gitlab/ModuleWithInstanceVariables - # rubocop: disable CodeReuse/ActiveRecord - def filter_params - set_sort_order_from_cookie - set_default_state + def finder_options + params[:state] = default_state if params[:state].blank? - # Skip irrelevant Rails routing params - @filter_params = params.dup.except(:controller, :action, :namespace_id) - @filter_params[:sort] ||= default_sort_order + options = { + scope: params[:scope], + state: params[:state], + sort: set_sort_order_from_cookie || default_sort_order + } - @sort = @filter_params[:sort] + # Used by view to highlight active option + @sort = options[:sort] if @project - @filter_params[:project_id] = @project.id + options[:project_id] = @project.id elsif @group - @filter_params[:group_id] = @group.id - @filter_params[:include_subgroups] = true - @filter_params[:use_cte_for_search] = true + options[:group_id] = @group.id + options[:include_subgroups] = true + options[:use_cte_for_search] = true end - @filter_params.permit(finder_type.valid_params) + params.permit(finder_type.valid_params).merge(options) end - # rubocop: enable CodeReuse/ActiveRecord # rubocop:enable Gitlab/ModuleWithInstanceVariables - def set_default_state - params[:state] = 'opened' if params[:state].blank? + def default_state + 'opened' end def set_sort_order_from_cookie @@ -121,7 +121,7 @@ module IssuableCollections sort_value = update_cookie_value(sort_param) set_secure_cookie(remember_sorting_key, sort_value) - params[:sort] = sort_value + sort_value end def remember_sorting_key diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index 285f2c3a8a0..ed10f32512e 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -19,7 +19,7 @@ module MergeRequestsAction (MergeRequestsFinder if action_name == 'merge_requests') end - def filter_params + def finder_options super.merge(non_archived: true) end end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index c032fb2efb5..4ce9be44403 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -4,13 +4,6 @@ class DashboardController < Dashboard::ApplicationController include IssuesAction include MergeRequestsAction - FILTER_PARAMS = [ - :author_id, - :assignee_id, - :milestone_title, - :label_name - ].freeze - before_action :event_filter, only: :activity before_action :projects, only: [:issues, :merge_requests] before_action :set_show_full_reference, only: [:issues, :merge_requests] @@ -51,10 +44,13 @@ class DashboardController < Dashboard::ApplicationController end def check_filters_presence! - @no_filters_set = FILTER_PARAMS.none? { |k| params.key?(k) } + @no_filters_set = finder_type.scalar_params.none? { |k| params.key?(k) } return unless @no_filters_set + # Call to set selected `state` and `sort` options in view + finder_options + respond_to do |format| format.html { render } format.atom { head :bad_request } diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index c02ec407262..2a6fe3b9c97 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -122,7 +122,7 @@ class Projects::BlobController < Projects::ApplicationController @lines.map! do |line| # These are marked as context lines but are loaded from blobs. # We also have context lines loaded from diffs in other places. - diff_line = Gitlab::Diff::Line.new(line, 'context', nil, nil, nil) + diff_line = Gitlab::Diff::Line.new(line, nil, nil, nil, nil) diff_line.rich_text = line diff_line end diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 92ef10a9ef5..0a593bd35b6 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -46,7 +46,9 @@ class Projects::DeployKeysController < Projects::ApplicationController end def enable - Projects::EnableDeployKeyService.new(@project, current_user, params).execute + key = Projects::EnableDeployKeyService.new(@project, current_user, params).execute + + return render_404 unless key respond_to do |format| format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') } @@ -54,19 +56,16 @@ class Projects::DeployKeysController < Projects::ApplicationController end end - # rubocop: disable CodeReuse/ActiveRecord def disable - deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]) - return render_404 unless deploy_key_project + deploy_key_project = Projects::DisableDeployKeyService.new(@project, current_user, params).execute - deploy_key_project.destroy! + return render_404 unless deploy_key_project respond_to do |format| format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') } format.json { head :ok } end end - # rubocop: enable CodeReuse/ActiveRecord protected diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 5307cd0720a..b3d77335c2a 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -22,6 +22,12 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic def render_diffs @environment = @merge_request.environments_for(current_user).last + notes_grouped_by_path = renderable_notes.group_by { |note| note.position.file_path } + + @diffs.diff_files.each do |diff_file| + notes = notes_grouped_by_path.fetch(diff_file.file_path, []) + notes.each { |note| diff_file.unfold_diff_lines(note.position) } + end @diffs.write_cache @@ -108,4 +114,10 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic @grouped_diff_discussions = @merge_request.grouped_diff_discussions(@compare.diff_refs) @notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes), @merge_request) end + + def renderable_notes + define_diff_comment_vars unless @notes + + @notes + end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 4bdb857b2d9..23d16fed7b9 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -14,9 +14,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action :set_issuables_index, only: [:index] before_action :authenticate_user!, only: [:assign_related_issues] before_action :check_user_can_push_to_source_branch!, only: [:rebase] - before_action do - push_frontend_feature_flag(:ci_environments_status_changes) - end def index @merge_requests = @issuables diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index ebf70f25bda..7b6657e1196 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -45,9 +45,9 @@ class RootController < Dashboard::ProjectsController when 'todos' redirect_to(dashboard_todos_path) when 'issues' - redirect_to(issues_dashboard_path(assignee_id: current_user.id)) + redirect_to(issues_dashboard_path(assignee_username: current_user.username)) when 'merge_requests' - redirect_to(merge_requests_dashboard_path(assignee_id: current_user.id)) + redirect_to(merge_requests_dashboard_path(assignee_username: current_user.username)) end end diff --git a/app/finders/events_finder.rb b/app/finders/events_finder.rb index 2e82bda8730..8df01f1dad9 100644 --- a/app/finders/events_finder.rb +++ b/app/finders/events_finder.rb @@ -58,7 +58,7 @@ class EventsFinder def by_target_type(events) return events unless Event::TARGET_TYPES[params[:target_type]] - events.where(target_type: Event::TARGET_TYPES[params[:target_type]]) + events.where(target_type: Event::TARGET_TYPES[params[:target_type]].name) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb index c96979619fd..a9ce5be13f3 100644 --- a/app/finders/group_descendants_finder.rb +++ b/app/finders/group_descendants_finder.rb @@ -178,7 +178,7 @@ class GroupDescendantsFinder end def sort - params.fetch(:sort, 'id_asc') + params.fetch(:sort, 'created_desc') end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 93bef592c65..fdc630cbf72 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -14,7 +14,9 @@ # project_id: integer # milestone_title: string # author_id: integer +# author_username: string # assignee_id: integer or 'None' or 'Any' +# assignee_username: string # search: string # label_name: string # sort: string @@ -49,25 +51,15 @@ class IssuableFinder assignee_username author_id author_username - authorized_only - group_id - iids label_name milestone_title my_reaction_emoji - non_archived - project_id - scope search - sort - state - include_subgroups - use_cte_for_search ] end def self.array_params - @array_params ||= { label_name: [], iids: [], assignee_username: [] } + @array_params ||= { label_name: [], assignee_username: [] } end def self.valid_params diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4f91e3e4117..74042f0bae8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -173,17 +173,7 @@ module ApplicationHelper without = options.delete(:without) add_label = options.delete(:label) - exist_opts = { - state: params[:state], - scope: params[:scope], - milestone_title: params[:milestone_title], - assignee_id: params[:assignee_id], - author_id: params[:author_id], - search: params[:search], - label_name: params[:label_name] - } - - options = exist_opts.merge(options) + options = request.query_parameters.merge(options) if without.present? without.each do |key| diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index c158cf20dd6..44f85e9c0f8 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -24,6 +24,23 @@ module AuthHelper Gitlab::Auth::OAuth::Provider.label_for(name) end + def form_based_provider_priority + ['crowd', /^ldap/, 'kerberos'] + end + + def form_based_provider_with_highest_priority + @form_based_provider_with_highest_priority ||= begin + form_based_provider_priority.each do |provider_regexp| + highest_priority = form_based_providers.find { |provider| provider.match?(provider_regexp) } + break highest_priority unless highest_priority.nil? + end + end + end + + def form_based_auth_provider_has_active_class?(provider) + form_based_provider_with_highest_priority == provider + end + def form_based_provider?(name) [LDAP_PROVIDER, 'crowd'].any? { |pattern| pattern === name.to_s } end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index 463f4145bdd..d90ef8903a7 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -2,11 +2,11 @@ module DashboardHelper def assigned_issues_dashboard_path - issues_dashboard_path(assignee_id: current_user.id) + issues_dashboard_path(assignee_username: current_user.username) end def assigned_mrs_dashboard_path - merge_requests_dashboard_path(assignee_id: current_user.id) + merge_requests_dashboard_path(assignee_username: current_user.username) end def dashboard_nav_links diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb index 3d0eb3d0d51..49171df1433 100644 --- a/app/helpers/import_helper.rb +++ b/app/helpers/import_helper.rb @@ -83,7 +83,7 @@ module ImportHelper private def github_project_url(full_path) - URI.join(github_root_url, full_path).to_s + Gitlab::Utils.append_path(github_root_url, full_path) end def github_root_url @@ -95,6 +95,6 @@ module ImportHelper end def gitea_project_url(full_path) - URI.join(@gitea_host_url, full_path).to_s + Gitlab::Utils.append_path(@gitea_host_url, full_path) end end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 761f42f2f0f..a7fe8c3d59c 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -19,10 +19,7 @@ module NavHelper end def page_gutter_class - if current_path?('merge_requests#show') || - current_path?('projects/merge_requests/conflicts#show') || - current_path?('issues#show') || - current_path?('milestones#show') + if page_has_markdown? if cookies[:collapsed_gutter] == 'true' %w[page-gutter right-sidebar-collapsed] @@ -50,6 +47,17 @@ module NavHelper class_names end + def show_separator? + Gitlab::Sherlock.enabled? || can?(current_user, :read_instance_statistics) + end + + def page_has_markdown? + current_path?('merge_requests#show') || + current_path?('projects/merge_requests/conflicts#show') || + current_path?('issues#show') || + current_path?('milestones#show') + end + private def get_header_links diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index 42f9a1213e9..df318de740a 100644 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -7,7 +7,7 @@ module ProfilesHelper [ [s_("Profiles|Use a private email - %{email}").html_safe % { email: private_email }, Gitlab::PrivateCommitEmail::TOKEN], - verified_emails + *verified_emails ] end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 4f9e1322b56..80cc568820a 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -163,15 +163,26 @@ module SearchHelper if @project.present? opts[:data]['project-id'] = @project.id opts[:data]['base-endpoint'] = project_path(@project) - else - # Group context + elsif @group.present? opts[:data]['group-id'] = @group.id opts[:data]['base-endpoint'] = group_canonical_path(@group) + else + opts[:data]['base-endpoint'] = root_dashboard_path end opts end + def search_history_storage_prefix + if @project.present? + @project.full_path + elsif @group.present? + @group.full_path + else + 'dashboard' + end + end + # Sanitize a HTML field for search display. Most tags are stripped out and the # maximum length is set to 200 characters. def search_md_sanitize(object, field) diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 00000000000..71fbba5b328 --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 889f8ce27a6..d60861dc95f 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -98,7 +98,7 @@ module Ci scope :matches_tag_ids, -> (tag_ids) do matcher = ::ActsAsTaggableOn::Tagging - .where(taggable_type: CommitStatus) + .where(taggable_type: CommitStatus.name) .where(context: 'tags') .where('taggable_id = ci_builds.id') .where.not(tag_id: tag_ids).select('1') @@ -108,7 +108,7 @@ module Ci scope :with_any_tags, -> do matcher = ::ActsAsTaggableOn::Tagging - .where(taggable_type: CommitStatus) + .where(taggable_type: CommitStatus.name) .where(context: 'tags') .where('taggable_id = ci_builds.id').select('1') @@ -464,7 +464,9 @@ module Ci end def repo_url - auth = "gitlab-ci-token:#{ensure_token!}@" + return unless token + + auth = "gitlab-ci-token:#{token}@" project.http_url_to_repo.sub(%r{^https?://}) do |prefix| prefix + auth end @@ -725,7 +727,7 @@ module Ci trace = trace.dup Gitlab::Ci::MaskSecret.mask!(trace, project.runners_token) if project - Gitlab::Ci::MaskSecret.mask!(trace, token) + Gitlab::Ci::MaskSecret.mask!(trace, token) if token trace end @@ -814,12 +816,12 @@ module Ci .concat(pipeline.persisted_variables) .append(key: 'CI_JOB_ID', value: id.to_s) .append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self)) - .append(key: 'CI_JOB_TOKEN', value: token, public: false) + .append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false) .append(key: 'CI_BUILD_ID', value: id.to_s) - .append(key: 'CI_BUILD_TOKEN', value: token, public: false) + .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false) .append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER) - .append(key: 'CI_REGISTRY_PASSWORD', value: token, public: false) - .append(key: 'CI_REPOSITORY_URL', value: repo_url, public: false) + .append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false) + .append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false) .concat(deploy_token_variables) end end @@ -831,9 +833,9 @@ module Ci variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(',')) variables.append(key: 'CI_SERVER_NAME', value: 'GitLab') variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION) - variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: gitlab_version_info.major.to_s) - variables.append(key: 'CI_SERVER_VERSION_MINOR', value: gitlab_version_info.minor.to_s) - variables.append(key: 'CI_SERVER_VERSION_PATCH', value: gitlab_version_info.patch.to_s) + variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s) + variables.append(key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s) + variables.append(key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s) variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision) variables.append(key: 'CI_JOB_NAME', value: name) variables.append(key: 'CI_JOB_STAGE', value: stage) @@ -850,10 +852,6 @@ module Ci end end - def gitlab_version_info - @gitlab_version_info ||= Gitlab::VersionInfo.parse(Gitlab::VERSION) - end - def legacy_variables Gitlab::Ci::Variables::Collection.new.tap do |variables| variables.append(key: 'CI_BUILD_REF', value: sha) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 56010e899a4..9512ba42f67 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -58,15 +58,9 @@ module Ci after_create :keep_around_commits, unless: :importing? - enum_with_nil source: { - unknown: nil, - push: 1, - web: 2, - trigger: 3, - schedule: 4, - api: 5, - external: 6 - } + # We use `Ci::PipelineEnums.sources` here so that EE can more easily extend + # this `Hash` with new values. + enum_with_nil source: ::Ci::PipelineEnums.sources enum_with_nil config_source: { unknown_source: nil, @@ -74,10 +68,9 @@ module Ci auto_devops_source: 2 } - enum failure_reason: { - unknown_failure: 0, - config_error: 1 - } + # We use `Ci::PipelineEnums.failure_reasons` here so that EE can more easily + # extend this `Hash` with new values. + enum failure_reason: ::Ci::PipelineEnums.failure_reasons state_machine :status, initial: :created do event :enqueue do diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb new file mode 100644 index 00000000000..8d8d16e2ec1 --- /dev/null +++ b/app/models/ci/pipeline_enums.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Ci + module PipelineEnums + # Returns the `Hash` to use for creating the `failure_reason` enum for + # `Ci::Pipeline`. + def self.failure_reasons + { + unknown_failure: 0, + config_error: 1 + } + end + + # Returns the `Hash` to use for creating the `sources` enum for + # `Ci::Pipeline`. + def self.sources + { + unknown: nil, + push: 1, + web: 2, + trigger: 3, + schedule: 4, + api: 5, + external: 6 + } + end + end +end diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb index a79a97576d1..c66d5ce54db 100644 --- a/app/models/clusters/applications/knative.rb +++ b/app/models/clusters/applications/knative.rb @@ -41,6 +41,10 @@ module Clusters ) end + def client + cluster.platform_kubernetes.kubeclient.knative_client + end + private def install_script diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb index 93bdf9c223d..0e74cce29b7 100644 --- a/app/models/clusters/concerns/application_status.rb +++ b/app/models/clusters/concerns/application_status.rb @@ -64,6 +64,13 @@ module Clusters status_reason = transition.args.first app_status.status_reason = status_reason if status_reason end + + before_transition any => [:installed, :updated] do |app_status, _| + # When installing any application we are also performing an update + # of tiller (see Gitlab::Kubernetes::Helm::ClientCommand) so + # therefore we need to reflect that in the database. + app_status.cluster.application_helm.update!(version: Gitlab::Kubernetes::Helm::HELM_VERSION) + end end end diff --git a/app/models/clusters/kubernetes_namespace.rb b/app/models/clusters/kubernetes_namespace.rb index ac7f9193b87..34f5e38ff79 100644 --- a/app/models/clusters/kubernetes_namespace.rb +++ b/app/models/clusters/kubernetes_namespace.rb @@ -11,9 +11,13 @@ module Clusters belongs_to :project, class_name: '::Project' has_one :platform_kubernetes, through: :cluster + before_validation :set_defaults + validates :namespace, presence: true validates :namespace, uniqueness: { scope: :cluster_id } + validates :service_account_name, presence: true + delegate :ca_pem, to: :platform_kubernetes, allow_nil: true delegate :api_url, to: :platform_kubernetes, allow_nil: true @@ -22,42 +26,49 @@ module Clusters key: Settings.attr_encrypted_db_key_base_truncated, algorithm: 'aes-256-cbc' + scope :has_service_account_token, -> { where.not(encrypted_service_account_token: nil) } + def token_name "#{namespace}-token" end - def configure_predefined_credentials - self.namespace = kubernetes_or_project_namespace - self.service_account_name = default_service_account_name - end - def predefined_variables config = YAML.dump(kubeconfig) Gitlab::Ci::Variables::Collection.new.tap do |variables| variables - .append(key: 'KUBE_SERVICE_ACCOUNT', value: service_account_name) - .append(key: 'KUBE_NAMESPACE', value: namespace) - .append(key: 'KUBE_TOKEN', value: service_account_token, public: false) + .append(key: 'KUBE_SERVICE_ACCOUNT', value: service_account_name.to_s) + .append(key: 'KUBE_NAMESPACE', value: namespace.to_s) + .append(key: 'KUBE_TOKEN', value: service_account_token.to_s, public: false) .append(key: 'KUBECONFIG', value: config, public: false, file: true) end end - private - - def kubernetes_or_project_namespace - platform_kubernetes&.namespace.presence || project_namespace + def set_defaults + self.namespace ||= default_platform_kubernetes_namespace + self.namespace ||= default_project_namespace + self.service_account_name ||= default_service_account_name end + private + def default_service_account_name + return unless namespace + "#{namespace}-service-account" end - def project_namespace - Gitlab::NamespaceSanitizer.sanitize(project_slug) + def default_platform_kubernetes_namespace + platform_kubernetes&.namespace.presence + end + + def default_project_namespace + Gitlab::NamespaceSanitizer.sanitize(project_slug) if project_slug end def project_slug + return unless project + "#{project.path}-#{project.id}".downcase end diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb index ea02ae6c9d8..3c5d7756eec 100644 --- a/app/models/clusters/platforms/kubernetes.rb +++ b/app/models/clusters/platforms/kubernetes.rb @@ -83,7 +83,7 @@ module Clusters .append(key: 'KUBE_CA_PEM_FILE', value: ca_pem, file: true) end - if kubernetes_namespace = cluster.kubernetes_namespaces.find_by(project: project) + if kubernetes_namespace = cluster.kubernetes_namespaces.has_service_account_token.find_by(project: project) variables.concat(kubernetes_namespace.predefined_variables) else # From 11.5, every Clusters::Project should have at least one @@ -173,9 +173,7 @@ module Clusters kubeclient = build_kube_client! kubeclient.get_pods(namespace: actual_namespace).as_json - rescue Kubeclient::HttpError => err - raise err unless err.error_code == 404 - + rescue Kubeclient::ResourceNotFoundError [] end diff --git a/app/models/commit.rb b/app/models/commit.rb index 9dd0cbacd9e..546fcc54a15 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -230,24 +230,13 @@ class Commit def lazy_author BatchLoader.for(author_email.downcase).batch do |emails, loader| - # A Hash that maps user Emails to the corresponding User objects. The - # Emails at this point are the _primary_ Emails of the Users. - users_for_emails = User - .by_any_email(emails) - .each_with_object({}) { |user, hash| hash[user.email] = user } - - users_for_ids = users_for_emails - .values - .each_with_object({}) { |user, hash| hash[user.id] = user } - - # Some commits may have used an alternative Email address. In this case we - # need to query the "emails" table to map those addresses to User objects. - Email - .where(email: emails - users_for_emails.keys) - .pluck(:email, :user_id) - .each { |(email, id)| users_for_emails[email] = users_for_ids[id] } - - users_for_emails.each { |email, user| loader.call(email, user) } + users = User.by_any_email(emails).includes(:emails) + + emails.each do |email| + user = users.find { |u| u.any_email?(email) } + + loader.call(email, user) + end end end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 755f8bd4d06..0f50bd39131 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -42,18 +42,9 @@ class CommitStatus < ActiveRecord::Base scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) } scope :after_stage, -> (index) { where('stage_idx > ?', index) } - enum_with_nil failure_reason: { - unknown_failure: nil, - script_failure: 1, - api_failure: 2, - stuck_or_timeout_failure: 3, - runner_system_failure: 4, - missing_dependency_failure: 5, - runner_unsupported: 6, - stale_schedule: 7, - job_execution_timeout: 8, - archived_failure: 9 - } + # We use `CommitStatusEnums.failure_reasons` here so that EE can more easily + # extend this `Hash` with new values. + enum_with_nil failure_reason: ::CommitStatusEnums.failure_reasons ## # We still create some CommitStatuses outside of CreatePipelineService. diff --git a/app/models/commit_status_enums.rb b/app/models/commit_status_enums.rb new file mode 100644 index 00000000000..152105d9429 --- /dev/null +++ b/app/models/commit_status_enums.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module CommitStatusEnums + # Returns the Hash to use for creating the `failure_reason` enum for + # `CommitStatus`. + def self.failure_reasons + { + unknown_failure: nil, + script_failure: 1, + api_failure: 2, + stuck_or_timeout_failure: 3, + runner_system_failure: 4, + missing_dependency_failure: 5, + runner_unsupported: 6, + stale_schedule: 7, + job_execution_timeout: 8, + archived_failure: 9 + } + end +end diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb index 0d5311a9985..b42236c1fa2 100644 --- a/app/models/concerns/avatarable.rb +++ b/app/models/concerns/avatarable.rb @@ -86,7 +86,7 @@ module Avatarable params[:model].upload_paths(params[:identifier]) end - Upload.where(uploader: AvatarUploader, path: paths).find_each do |upload| + Upload.where(uploader: AvatarUploader.name, path: paths).find_each do |upload| model = model_class.instantiate('id' => upload.model_id) loader.call({ model: model, identifier: File.basename(upload.path) }, upload) diff --git a/app/models/concerns/deployable.rb b/app/models/concerns/deployable.rb index f4f1989f0a9..bc12b06b5af 100644 --- a/app/models/concerns/deployable.rb +++ b/app/models/concerns/deployable.rb @@ -13,17 +13,18 @@ module Deployable name: expanded_environment_name ) - environment.deployments.create!( + # If we failed to persist envirionment record by validation error, such as name with invalid character, + # the job will fall back to a non-environment job. + return unless environment.persisted? + + create_deployment!( project_id: environment.project_id, environment: environment, ref: ref, tag: tag, sha: sha, user: user, - deployable: self, - on_stop: on_stop).tap do |_| - self.reload # Reload relationships - end + on_stop: on_stop) end end end diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 298d0d42d90..0d88b34fb48 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -97,9 +97,9 @@ module Mentionable # Allows heavy processing to be skipped def matches_cross_reference_regex? reference_pattern = if !project || project.default_issues_tracker? - ReferenceRegexes::DEFAULT_PATTERN + ReferenceRegexes.default_pattern else - ReferenceRegexes::EXTERNAL_PATTERN + ReferenceRegexes.external_pattern end self.class.mentionable_attrs.any? do |attr, _| diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb index fe8fbb71184..b8fb3f71925 100644 --- a/app/models/concerns/mentionable/reference_regexes.rb +++ b/app/models/concerns/mentionable/reference_regexes.rb @@ -2,6 +2,8 @@ module Mentionable module ReferenceRegexes + extend Gitlab::Utils::StrongMemoize + def self.reference_pattern(link_patterns, issue_pattern) Regexp.union(link_patterns, issue_pattern, @@ -15,16 +17,20 @@ module Mentionable ] end - DEFAULT_PATTERN = begin - issue_pattern = Issue.reference_pattern - link_patterns = Regexp.union([Issue, Commit, MergeRequest, Epic].map(&:link_reference_pattern).compact) - reference_pattern(link_patterns, issue_pattern) + def self.default_pattern + strong_memoize(:default_pattern) do + issue_pattern = Issue.reference_pattern + link_patterns = Regexp.union([Issue, Commit, MergeRequest, Epic].map(&:link_reference_pattern).compact) + reference_pattern(link_patterns, issue_pattern) + end end - EXTERNAL_PATTERN = begin - issue_pattern = IssueTrackerService.reference_pattern - link_patterns = URI.regexp(%w(http https)) - reference_pattern(link_patterns, issue_pattern) + def self.external_pattern + strong_memoize(:external_pattern) do + issue_pattern = IssueTrackerService.reference_pattern + link_patterns = URI.regexp(%w(http https)) + reference_pattern(link_patterns, issue_pattern) + end end end end diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 83434276995..811e623b7f7 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -160,18 +160,18 @@ class Deployment < ActiveRecord::Base end def has_metrics? - prometheus_adapter&.can_query? + prometheus_adapter&.can_query? && success? end def metrics - return {} unless has_metrics? && success? + return {} unless has_metrics? metrics = prometheus_adapter.query(:deployment, self) metrics&.merge(deployment_time: finished_at.to_i) || {} end def additional_metrics - return {} unless has_metrics? && success? + return {} unless has_metrics? metrics = prometheus_adapter.query(:additional_metrics_deployment, self) metrics&.merge(deployment_time: finished_at.to_i) || {} diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 5f59e4832db..c32008aa9c7 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -66,6 +66,10 @@ class DiffNote < Note self.original_position.diff_refs == diff_refs end + def discussion_first_note? + self == discussion.first_note + end + private def enqueue_diff_file_creation_job @@ -78,26 +82,33 @@ class DiffNote < Note end def should_create_diff_file? - on_text? && note_diff_file.nil? && self == discussion.first_note + on_text? && note_diff_file.nil? && discussion_first_note? end def fetch_diff_file - if note_diff_file - diff = Gitlab::Git::Diff.new(note_diff_file.to_hash) - Gitlab::Diff::File.new(diff, - repository: project.repository, - diff_refs: original_position.diff_refs) - elsif created_at_diff?(noteable.diff_refs) - # We're able to use the already persisted diffs (Postgres) if we're - # presenting a "current version" of the MR discussion diff. - # So no need to make an extra Gitaly diff request for it. - # As an extra benefit, the returned `diff_file` already - # has `highlighted_diff_lines` data set from Redis on - # `Diff::FileCollection::MergeRequestDiff`. - noteable.diffs(original_position.diff_options).diff_files.first - else - original_position.diff_file(self.project.repository) - end + file = + if note_diff_file + diff = Gitlab::Git::Diff.new(note_diff_file.to_hash) + Gitlab::Diff::File.new(diff, + repository: project.repository, + diff_refs: original_position.diff_refs) + elsif created_at_diff?(noteable.diff_refs) + # We're able to use the already persisted diffs (Postgres) if we're + # presenting a "current version" of the MR discussion diff. + # So no need to make an extra Gitaly diff request for it. + # As an extra benefit, the returned `diff_file` already + # has `highlighted_diff_lines` data set from Redis on + # `Diff::FileCollection::MergeRequestDiff`. + noteable.diffs(original_position.diff_options).diff_files.first + else + original_position.diff_file(self.project.repository) + end + + # Since persisted diff files already have its content "unfolded" + # there's no need to make it pass through the unfolding process. + file&.unfold_diff_lines(position) unless note_diff_file + + file end def supported? diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index 7d9f6d89d44..8f305dd7c22 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -5,8 +5,8 @@ class ServiceHook < WebHook validates :service, presence: true # rubocop: disable CodeReuse/ServiceClass - def execute(data) - WebHookService.new(self, data, 'service_hook').execute + def execute(data, hook_name = 'service_hook') + WebHookService.new(self, data, hook_name).execute end # rubocop: enable CodeReuse/ServiceClass end diff --git a/app/models/identity.rb b/app/models/identity.rb index f5a13dbd6f2..d63dd432426 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,18 +1,14 @@ # frozen_string_literal: true class Identity < ActiveRecord::Base - def self.uniqueness_scope - :provider - end - include Sortable include CaseSensitivity belongs_to :user validates :provider, presence: true - validates :extern_uid, allow_blank: true, uniqueness: { scope: uniqueness_scope, case_sensitive: false } - validates :user_id, uniqueness: { scope: uniqueness_scope } + validates :extern_uid, allow_blank: true, uniqueness: { scope: UniquenessScopes.scopes, case_sensitive: false } + validates :user_id, uniqueness: { scope: UniquenessScopes.scopes } before_save :ensure_normalized_extern_uid, if: :extern_uid_changed? after_destroy :clear_user_synced_attributes, if: :user_synced_attributes_metadata_from_provider? diff --git a/app/models/identity/uniqueness_scopes.rb b/app/models/identity/uniqueness_scopes.rb new file mode 100644 index 00000000000..674b735903f --- /dev/null +++ b/app/models/identity/uniqueness_scopes.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Identity < ActiveRecord::Base + # This module and method are defined in a separate file to allow EE to + # redefine the `scopes` method before it is used in the `Identity` model. + module UniquenessScopes + def self.scopes + [:provider] + end + end +end diff --git a/app/models/member.rb b/app/models/member.rb index 0696ea46c8b..bc8ac14d148 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -152,11 +152,13 @@ class Member < ActiveRecord::Base return member unless can_update_member?(current_user, member) - member.attributes = { - created_by: member.created_by || current_user, - access_level: access_level, - expires_at: expires_at - } + set_member_attributes( + member, + access_level, + current_user: current_user, + expires_at: expires_at, + ldap: ldap + ) if member.request? ::Members::ApproveAccessRequestService.new( @@ -175,6 +177,18 @@ class Member < ActiveRecord::Base # rubocop: enable CodeReuse/ServiceClass end + # Populates the attributes of a member. + # + # This logic resides in a separate method so that EE can extend this logic, + # without having to patch the `add_user` method directly. + def set_member_attributes(member, access_level, current_user: nil, expires_at: nil, ldap: false) + member.attributes = { + created_by: member.created_by || current_user, + access_level: access_level, + expires_at: expires_at + } + end + def add_users(source, users, access_level, current_user: nil, expires_at: nil) return [] unless users.present? diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index df5678ec2f1..92add079a02 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -966,7 +966,6 @@ class MergeRequest < ActiveRecord::Base def mergeable_ci_state? return true unless project.only_allow_merge_if_pipeline_succeeds? - return true unless head_pipeline actual_head_pipeline&.success? || actual_head_pipeline&.skipped? end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 74583af1a29..6f1beede6f9 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -142,7 +142,7 @@ class MergeRequestDiff < ActiveRecord::Base end def commits_by_shas(shas) - return [] unless shas.present? + return MergeRequestDiffCommit.none unless shas.present? merge_request_diff_commits.where(sha: shas) end diff --git a/app/models/project.rb b/app/models/project.rb index d87fc1e4b86..4d1917b9ab2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1903,10 +1903,6 @@ class Project < ActiveRecord::Base false end - def issue_board_milestone_available?(user = nil) - feature_available?(:issue_board_milestone, user) - end - def full_path_was File.join(namespace.full_path, previous_changes['path'].first) end @@ -1968,7 +1964,7 @@ class Project < ActiveRecord::Base end def migrate_to_hashed_storage! - return if hashed_storage?(:repository) + return unless storage_upgradable? update!(repository_read_only: true) diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index d121d088ff6..a252052200a 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -86,14 +86,16 @@ class BambooService < CiService end def read_build_page(response) - if response.code != 200 || response.dig('results', 'results', 'size') == '0' - # If actual build link can't be determined, send user to build summary page. - URI.join("#{bamboo_url}/", "browse/#{build_key}").to_s - else - # If actual build link is available, go to build result page. - result_key = response.dig('results', 'results', 'result', get_build_result_index, 'planResultKey', 'key') - URI.join("#{bamboo_url}/", "browse/#{result_key}").to_s - end + key = + if response.code != 200 || response.dig('results', 'results', 'size') == '0' + # If actual build link can't be determined, send user to build summary page. + build_key + else + # If actual build link is available, go to build result page. + response.dig('results', 'results', 'result', get_build_result_index, 'planResultKey', 'key') + end + + build_url("browse/#{key}") end def read_commit_status(response) @@ -117,7 +119,7 @@ class BambooService < CiService end def build_url(path) - URI.join("#{bamboo_url}/", path).to_s + Gitlab::Utils.append_path(bamboo_url, path) end def get_path(path, query_params = {}) diff --git a/app/models/project_services/chat_message/push_message.rb b/app/models/project_services/chat_message/push_message.rb index 82be33a12a1..5dd0414b7e6 100644 --- a/app/models/project_services/chat_message/push_message.rb +++ b/app/models/project_services/chat_message/push_message.rb @@ -26,16 +26,8 @@ module ChatMessage end def activity - action = if new_branch? - "created" - elsif removed_branch? - "removed" - else - "pushed to" - end - { - title: "#{user_combined_name} #{action} #{ref_type}", + title: humanized_action(short: true), subtitle: "in #{project_link}", text: compare_link, image: user_avatar @@ -44,32 +36,21 @@ module ChatMessage private + def humanized_action(short: false) + action, ref_link, target_link = compose_action_details + text = [user_combined_name, action, ref_type, ref_link] + text << target_link unless short + text.join(' ') + end + def message - if new_branch? - new_branch_message - elsif removed_branch? - removed_branch_message - else - push_message - end + humanized_action end def format(string) Slack::Notifier::LinkFormatter.format(string) end - def new_branch_message - "#{user_combined_name} pushed new #{ref_type} #{branch_link} to #{project_link}" - end - - def removed_branch_message - "#{user_combined_name} removed #{ref_type} #{ref} from #{project_link}" - end - - def push_message - "#{user_combined_name} pushed to #{ref_type} #{branch_link} of #{project_link} (#{compare_link})" - end - def commit_messages commits.map { |commit| compose_commit_message(commit) }.join("\n\n") end @@ -115,6 +96,16 @@ module ChatMessage "[Compare changes](#{compare_url})" end + def compose_action_details + if new_branch? + ['pushed new', branch_link, "to #{project_link}"] + elsif removed_branch? + ['removed', ref, "from #{project_link}"] + else + ['pushed to', branch_link, "of #{project_link} (#{compare_link})"] + end + end + def attachment_color '#345' end diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 158ae0bf255..5ccc2f019cb 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -39,11 +39,9 @@ class DroneCiService < CiService end def commit_status_path(sha, ref) - url = [drone_url, - "gitlab/#{project.full_path}/commits/#{sha}", - "?branch=#{URI.encode(ref.to_s)}&access_token=#{token}"] - - URI.join(*url).to_s + Gitlab::Utils.append_path( + drone_url, + "gitlab/#{project.full_path}/commits/#{sha}?branch=#{URI.encode(ref.to_s)}&access_token=#{token}") end def commit_status(sha, ref) @@ -74,11 +72,9 @@ class DroneCiService < CiService end def build_page(sha, ref) - url = [drone_url, - "gitlab/#{project.full_path}/redirect/commits/#{sha}", - "?branch=#{URI.encode(ref.to_s)}"] - - URI.join(*url).to_s + Gitlab::Utils.append_path( + drone_url, + "gitlab/#{project.full_path}/redirect/commits/#{sha}?branch=#{URI.encode(ref.to_s)}") end def title diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index a399982e5ec..f54497fc6d8 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -9,7 +9,7 @@ class IssueTrackerService < Service # Override this method on services that uses different patterns # This pattern does not support cross-project references # The other code assumes that this pattern is a superset of all - # overridden patterns. See ReferenceRegexes::EXTERNAL_PATTERN + # overridden patterns. See ReferenceRegexes.external_pattern def self.reference_pattern(only_long: false) if only_long /(\b[A-Z][A-Z0-9_]*-)(?<issue>\d+)/ diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 5a38f48c542..9066a0b7f1d 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -54,7 +54,7 @@ class JiraService < IssueTrackerService { username: self.username, password: self.password, - site: URI.join(url, '/').to_s, + site: URI.join(url, '/').to_s, # Intended to find the root context_path: url.path.chomp('/'), auth_type: :basic, read_timeout: 120, diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 3459ded7ccf..c52a531e5fe 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -203,9 +203,7 @@ class KubernetesService < DeploymentService kubeclient = build_kube_client! kubeclient.get_pods(namespace: actual_namespace).as_json - rescue Kubeclient::HttpError => err - raise err unless err.error_code == 404 - + rescue Kubeclient::ResourceNotFoundError [] end diff --git a/app/models/project_services/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb index 6883976f0c8..d8bba58dcbf 100644 --- a/app/models/project_services/mock_ci_service.rb +++ b/app/models/project_services/mock_ci_service.rb @@ -34,10 +34,9 @@ class MockCiService < CiService # http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c # def build_page(sha, ref) - url = [mock_service_url, - "#{project.namespace.path}/#{project.path}/status/#{sha}"] - - URI.join(*url).to_s + Gitlab::Utils.append_path( + mock_service_url, + "#{project.namespace.path}/#{project.path}/status/#{sha}") end # Return string with build status or :error symbol @@ -61,10 +60,9 @@ class MockCiService < CiService end def commit_status_path(sha) - url = [mock_service_url, - "#{project.namespace.path}/#{project.path}/status/#{sha}.json"] - - URI.join(*url).to_s + Gitlab::Utils.append_path( + mock_service_url, + "#{project.namespace.path}/#{project.path}/status/#{sha}.json") end def read_commit_status(response) diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index eeeff5e802a..b8e17087db5 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -132,7 +132,7 @@ class TeamcityService < CiService end def build_url(path) - URI.join("#{teamcity_url}/", path).to_s + Gitlab::Utils.append_path(teamcity_url, path) end def get_path(path) diff --git a/app/models/repository.rb b/app/models/repository.rb index 6e179f61a7b..fff6d4be275 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1049,11 +1049,19 @@ class Repository end def cache - @cache ||= Gitlab::RepositoryCache.new(self) + @cache ||= if is_wiki + Gitlab::RepositoryCache.new(self, extra_namespace: 'wiki') + else + Gitlab::RepositoryCache.new(self) + end end def request_store_cache - @request_store_cache ||= Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore) + @request_store_cache ||= if is_wiki + Gitlab::RepositoryCache.new(self, extra_namespace: 'wiki', backend: Gitlab::SafeRequestStore) + else + Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore) + end end def tags_sorted_by_committed_date diff --git a/app/models/shard.rb b/app/models/shard.rb index 2fa22bd040c..2e75bc91df0 100644 --- a/app/models/shard.rb +++ b/app/models/shard.rb @@ -9,13 +9,12 @@ class Shard < ActiveRecord::Base # The GitLab config does not change for the lifecycle of the process in_config = Gitlab.config.repositories.storages.keys.map(&:to_s) + in_db = all.pluck(:name) - transaction do - in_db = all.pluck(:name) - missing = in_config - in_db - - missing.map { |name| by_name(name) } - end + # This may race with other processes creating shards at the same time, but + # `by_name` will handle that correctly + missing = in_config - in_db + missing.map { |name| by_name(name) } end def self.by_name(name) diff --git a/app/models/user.rb b/app/models/user.rb index a400058e87e..01eba7e0426 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -349,20 +349,28 @@ class User < ActiveRecord::Base def find_by_any_email(email, confirmed: false) return unless email - downcased = email.downcase - - find_by_private_commit_email(downcased) || by_any_email(downcased, confirmed: confirmed).take + by_any_email(email, confirmed: confirmed).take end - # Returns a relation containing all the users for the given Email address - def by_any_email(email, confirmed: false) - users = where(email: email) - users = users.confirmed if confirmed + # Returns a relation containing all the users for the given email addresses + # + # @param emails [String, Array<String>] email addresses to check + # @param confirmed [Boolean] Only return users where the email is confirmed + def by_any_email(emails, confirmed: false) + emails = Array(emails).map(&:downcase) + + from_users = where(email: emails) + from_users = from_users.confirmed if confirmed - emails = joins(:emails).where(emails: { email: email }) - emails = emails.confirmed if confirmed + from_emails = joins(:emails).where(emails: { email: emails }) + from_emails = from_emails.confirmed if confirmed - from_union([users, emails]) + items = [from_users, from_emails] + + user_ids = Gitlab::PrivateCommitEmail.user_ids_for_emails(emails) + items << where(id: user_ids) if user_ids.present? + + from_union(items) end def find_by_private_commit_email(email) @@ -1031,6 +1039,7 @@ class User < ActiveRecord::Base def all_emails all_emails = [] all_emails << email unless temp_oauth_email? + all_emails << private_commit_email all_emails.concat(emails.map(&:email)) all_emails end @@ -1043,16 +1052,24 @@ class User < ActiveRecord::Base verified_emails end + def any_email?(check_email) + downcased = check_email.downcase + + # handle the outdated private commit email case + return true if persisted? && + id == Gitlab::PrivateCommitEmail.user_id_for_email(downcased) + + all_emails.include?(check_email.downcase) + end + def verified_email?(check_email) downcased = check_email.downcase - if email == downcased - primary_email_verified? - else - user_id = Gitlab::PrivateCommitEmail.user_id_for_email(downcased) + # handle the outdated private commit email case + return true if persisted? && + id == Gitlab::PrivateCommitEmail.user_id_for_email(downcased) - user_id == id || emails.confirmed.where(email: downcased).exists? - end + verified_emails.include?(check_email.downcase) end def hook_attrs diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb index 1cd05cf3aac..76e7bc06b4e 100644 --- a/app/models/user_callout.rb +++ b/app/models/user_callout.rb @@ -3,11 +3,9 @@ class UserCallout < ActiveRecord::Base belongs_to :user - enum feature_name: { - gke_cluster_integration: 1, - gcp_signup_offer: 2, - cluster_security_warning: 3 - } + # We use `UserCalloutEnums.feature_names` here so that EE can more easily + # extend this `Hash` with new values. + enum feature_name: ::UserCalloutEnums.feature_names validates :user, presence: true validates :feature_name, diff --git a/app/models/user_callout_enums.rb b/app/models/user_callout_enums.rb new file mode 100644 index 00000000000..b9373ae6166 --- /dev/null +++ b/app/models/user_callout_enums.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module UserCalloutEnums + # Returns the `Hash` to use for the `feature_name` enum in the `UserCallout` + # model. + # + # This method is separate from the `UserCallout` model so that it can be + # extended by EE. + def self.feature_names + { + gke_cluster_integration: 1, + gcp_signup_offer: 2, + cluster_security_warning: 3 + } + end +end diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb index f9623587957..e42d78f47c5 100644 --- a/app/policies/ci/pipeline_policy.rb +++ b/app/policies/ci/pipeline_policy.rb @@ -16,6 +16,10 @@ module Ci enable :update_pipeline end + rule { can?(:owner_access) }.policy do + enable :destroy_pipeline + end + def ref_protected?(user, project, tag, ref) access = ::Gitlab::UserAccess.new(user, project: project) diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb index 93a38f92073..57daf04efc6 100644 --- a/app/presenters/ci/pipeline_presenter.rb +++ b/app/presenters/ci/pipeline_presenter.rb @@ -4,9 +4,11 @@ module Ci class PipelinePresenter < Gitlab::View::Presenter::Delegated include Gitlab::Utils::StrongMemoize - FAILURE_REASONS = { - config_error: 'CI/CD YAML configuration error!' - }.freeze + # We use a class method here instead of a constant, allowing EE to redefine + # the returned `Hash` more easily. + def self.failure_reasons + { config_error: 'CI/CD YAML configuration error!' } + end presents :pipeline @@ -21,7 +23,7 @@ module Ci def failure_reason return unless pipeline.failure_reason? - FAILURE_REASONS[pipeline.failure_reason.to_sym] || + self.class.failure_reasons[pipeline.failure_reason.to_sym] || pipeline.failure_reason end diff --git a/app/serializers/environment_status_entity.rb b/app/serializers/environment_status_entity.rb index f87cc894d2f..f6321b9e520 100644 --- a/app/serializers/environment_status_entity.rb +++ b/app/serializers/environment_status_entity.rb @@ -11,7 +11,7 @@ class EnvironmentStatusEntity < Grape::Entity project_environment_path(es.project, es.environment) end - expose :metrics_url, if: ->(*) { can_read_environment? && environment.has_metrics? } do |es| + expose :metrics_url, if: ->(*) { can_read_environment? && deployment.has_metrics? } do |es| metrics_project_environment_deployment_path(es.project, es.environment, es.deployment) end @@ -37,7 +37,7 @@ class EnvironmentStatusEntity < Grape::Entity es.deployment.try(:formatted_deployment_time) end - expose :changes, if: ->(*) { Feature.enabled?(:ci_environments_status_changes, project) } + expose :changes private @@ -45,6 +45,10 @@ class EnvironmentStatusEntity < Grape::Entity object.environment end + def deployment + object.deployment + end + def project object.environment.project end diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb new file mode 100644 index 00000000000..13f892aabb8 --- /dev/null +++ b/app/services/ci/destroy_pipeline_service.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Ci + class DestroyPipelineService < BaseService + def execute(pipeline) + raise Gitlab::Access::AccessDeniedError unless can?(current_user, :destroy_pipeline, pipeline) + + AuditEventService.new(current_user, pipeline).security_event + + pipeline.destroy! + end + end +end diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb index 19dc0478591..ca0f7b30053 100644 --- a/app/services/clusters/applications/check_installation_progress_service.rb +++ b/app/services/clusters/applications/check_installation_progress_service.rb @@ -15,8 +15,9 @@ module Clusters check_timeout end rescue Kubeclient::HttpError => e - Rails.logger.error "Kubernetes error: #{e.class.name} #{e.message}" - app.make_errored!("Kubernetes error") unless app.errored? + Rails.logger.error("Kubernetes error: #{e.error_code} #{e.message}") + Gitlab::Sentry.track_acceptable_exception(e, extra: { scope: 'kubernetes', app_id: app.id }) + app.make_errored!("Kubernetes error: #{e.error_code}") unless app.errored? end private @@ -53,7 +54,7 @@ module Clusters def remove_installation_pod helm_api.delete_pod!(install_command.pod_name) rescue => e - Rails.logger.error "Kubernetes error: #{e.class.name} #{e.message}" + Rails.logger.error("Kubernetes error: #{e.class.name} #{e.message}") # no-op end diff --git a/app/services/clusters/applications/install_service.rb b/app/services/clusters/applications/install_service.rb index 5a24d78e712..f4385748c43 100644 --- a/app/services/clusters/applications/install_service.rb +++ b/app/services/clusters/applications/install_service.rb @@ -13,10 +13,12 @@ module Clusters ClusterWaitForAppInstallationWorker.perform_in( ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id) rescue Kubeclient::HttpError => e - Rails.logger.error "Kubernetes error: #{e.class.name} #{e.message}" - app.make_errored!("Kubernetes error.") + Rails.logger.error("Kubernetes error: #{e.error_code} #{e.message}") + Gitlab::Sentry.track_acceptable_exception(e, extra: { scope: 'kubernetes', app_id: app.id }) + app.make_errored!("Kubernetes error: #{e.error_code}") rescue StandardError => e Rails.logger.error "Can't start installation process: #{e.class.name} #{e.message}" + Gitlab::Sentry.track_acceptable_exception(e, extra: { scope: 'kubernetes', app_id: app.id }) app.make_errored!("Can't start installation process.") end end diff --git a/app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb b/app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb index 2b607681082..b31426556f6 100644 --- a/app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb +++ b/app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb @@ -23,7 +23,7 @@ module Clusters attr_reader :cluster, :kubernetes_namespace, :platform def configure_kubernetes_namespace - kubernetes_namespace.configure_predefined_credentials + kubernetes_namespace.set_defaults end def create_project_service_account diff --git a/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb b/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb index 277cc4b788d..4ad04ab801e 100644 --- a/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb +++ b/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb @@ -21,10 +21,7 @@ module Clusters def get_secret kubeclient.get_secret(service_account_token_name, namespace).as_json - rescue Kubeclient::HttpError => err - raise err unless err.error_code == 404 - - nil + rescue Kubeclient::ResourceNotFoundError end end end diff --git a/app/services/issuable/clone/attributes_rewriter.rb b/app/services/issuable/clone/attributes_rewriter.rb new file mode 100644 index 00000000000..0300cc0d8d3 --- /dev/null +++ b/app/services/issuable/clone/attributes_rewriter.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Issuable + module Clone + class AttributesRewriter < ::Issuable::Clone::BaseService + def initialize(current_user, original_entity, new_entity) + @current_user = current_user + @original_entity = original_entity + @new_entity = new_entity + end + + def execute + new_entity.update(milestone: cloneable_milestone, labels: cloneable_labels) + copy_resource_label_events + end + + private + + def cloneable_milestone + title = original_entity.milestone&.title + return unless title + + params = { title: title, project_ids: new_entity.project&.id, group_ids: group&.id } + + milestones = MilestonesFinder.new(params).execute + milestones.first + end + + def cloneable_labels + params = { + project_id: new_entity.project&.id, + group_id: group&.id, + title: original_entity.labels.select(:title), + include_ancestor_groups: true + } + + params[:only_group_labels] = true if new_parent.is_a?(Group) + + LabelsFinder.new(current_user, params).execute + end + + def copy_resource_label_events + original_entity.resource_label_events.find_in_batches do |batch| + events = batch.map do |event| + entity_key = new_entity.is_a?(Issue) ? 'issue_id' : 'epic_id' + # rubocop: disable CodeReuse/ActiveRecord + event.attributes + .except('id', 'reference', 'reference_html') + .merge(entity_key => new_entity.id, 'action' => ResourceLabelEvent.actions[event.action]) + # rubocop: enable CodeReuse/ActiveRecord + end + + Gitlab::Database.bulk_insert(ResourceLabelEvent.table_name, events) + end + end + + def entity_key + new_entity.class.name.parameterize('_').foreign_key + end + end + end +end diff --git a/app/services/issuable/clone/base_service.rb b/app/services/issuable/clone/base_service.rb new file mode 100644 index 00000000000..42dd9c666f5 --- /dev/null +++ b/app/services/issuable/clone/base_service.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module Issuable + module Clone + class BaseService < IssuableBaseService + attr_reader :original_entity, :new_entity + + alias_method :old_project, :project + + def execute(original_entity, new_project = nil) + @original_entity = original_entity + + # Using transaction because of a high resources footprint + # on rewriting notes (unfolding references) + # + ActiveRecord::Base.transaction do + @new_entity = create_new_entity + + update_new_entity + update_old_entity + create_notes + end + end + + private + + def update_new_entity + rewriters = [ContentRewriter, AttributesRewriter] + + rewriters.each do |rewriter| + rewriter.new(current_user, original_entity, new_entity).execute + end + end + + def update_old_entity + close_issue + end + + def create_notes + add_note_from + add_note_to + end + + def close_issue + close_service = Issues::CloseService.new(old_project, current_user) + close_service.execute(original_entity, notifications: false, system_note: false) + end + + def new_parent + new_entity.project ? new_entity.project : new_entity.group + end + + def group + if new_entity.project&.group && current_user.can?(:read_group, new_entity.project.group) + new_entity.project.group + end + end + end + end +end diff --git a/app/services/issuable/clone/content_rewriter.rb b/app/services/issuable/clone/content_rewriter.rb new file mode 100644 index 00000000000..e1e0b75085d --- /dev/null +++ b/app/services/issuable/clone/content_rewriter.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module Issuable + module Clone + class ContentRewriter < ::Issuable::Clone::BaseService + def initialize(current_user, original_entity, new_entity) + @current_user = current_user + @original_entity = original_entity + @new_entity = new_entity + @project = original_entity.project + end + + def execute + rewrite_description + rewrite_award_emoji(original_entity, new_entity) + rewrite_notes + end + + private + + def rewrite_description + new_entity.update(description: rewrite_content(original_entity.description)) + end + + def rewrite_notes + original_entity.notes_with_associations.find_each do |note| + new_note = note.dup + new_params = { + project: new_entity.project, noteable: new_entity, + note: rewrite_content(new_note.note), + created_at: note.created_at, + updated_at: note.updated_at + } + + if note.system_note_metadata + new_params[:system_note_metadata] = note.system_note_metadata.dup + end + + new_note.update(new_params) + + rewrite_award_emoji(note, new_note) + end + end + + def rewrite_content(content) + return unless content + + rewriters = [Gitlab::Gfm::ReferenceRewriter, Gitlab::Gfm::UploadsRewriter] + + rewriters.inject(content) do |text, klass| + rewriter = klass.new(text, old_project, current_user) + rewriter.rewrite(new_parent) + end + end + + def rewrite_award_emoji(old_awardable, new_awardable) + old_awardable.award_emoji.each do |award| + new_award = award.dup + new_award.awardable = new_awardable + new_award.save + end + end + end + end +end diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index d2bdba1e627..41b6a96b005 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -1,165 +1,66 @@ # frozen_string_literal: true module Issues - class MoveService < Issues::BaseService + class MoveService < Issuable::Clone::BaseService MoveError = Class.new(StandardError) - def execute(issue, new_project) - @old_issue = issue - @old_project = @project - @new_project = new_project + def execute(issue, target_project) + @target_project = target_project - unless issue.can_move?(current_user, new_project) + unless issue.can_move?(current_user, @target_project) raise MoveError, 'Cannot move issue due to insufficient permissions!' end - if @project == new_project + if @project == @target_project raise MoveError, 'Cannot move issue to project it originates from!' end - # Using transaction because of a high resources footprint - # on rewriting notes (unfolding references) - # - ActiveRecord::Base.transaction do - @new_issue = create_new_issue - - update_new_issue - update_old_issue - end + super notify_participants - @new_issue + new_entity end private - def update_new_issue - rewrite_notes - copy_resource_label_events - rewrite_issue_award_emoji - add_note_moved_from - end + def update_old_entity + super - def update_old_issue - add_note_moved_to - close_issue mark_as_moved end - def create_new_issue - new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids, - milestone_id: cloneable_milestone_id, - project: @new_project, author: @old_issue.author, - description: rewrite_content(@old_issue.description), - assignee_ids: @old_issue.assignee_ids } - - new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params) - CreateService.new(@new_project, @current_user, new_params).execute - end - - # rubocop: disable CodeReuse/ActiveRecord - def cloneable_label_ids - params = { - project_id: @new_project.id, - title: @old_issue.labels.pluck(:title), - include_ancestor_groups: true - } + def create_new_entity + new_params = { + id: nil, + iid: nil, + project: @target_project, + author: original_entity.author, + assignee_ids: original_entity.assignee_ids + } - LabelsFinder.new(current_user, params).execute.pluck(:id) + new_params = original_entity.serializable_hash.symbolize_keys.merge(new_params) + CreateService.new(@target_project, @current_user, new_params).execute end - # rubocop: enable CodeReuse/ActiveRecord - - def cloneable_milestone_id - title = @old_issue.milestone&.title - return unless title - - if @new_project.group && can?(current_user, :read_group, @new_project.group) - group_id = @new_project.group.id - end - - params = - { title: title, project_ids: @new_project.id, group_ids: group_id } - milestones = MilestonesFinder.new(params).execute - milestones.first&.id - end - - def rewrite_notes - @old_issue.notes_with_associations.find_each do |note| - new_note = note.dup - new_params = { project: @new_project, noteable: @new_issue, - note: rewrite_content(new_note.note), - created_at: note.created_at, - updated_at: note.updated_at } - - new_note.update(new_params) - - rewrite_award_emoji(note, new_note) - end - end - - # rubocop: disable CodeReuse/ActiveRecord - def copy_resource_label_events - @old_issue.resource_label_events.find_in_batches do |batch| - events = batch.map do |event| - event.attributes - .except('id', 'reference', 'reference_html') - .merge('issue_id' => @new_issue.id, 'action' => ResourceLabelEvent.actions[event.action]) - end - - Gitlab::Database.bulk_insert(ResourceLabelEvent.table_name, events) - end - end - # rubocop: enable CodeReuse/ActiveRecord - - def rewrite_issue_award_emoji - rewrite_award_emoji(@old_issue, @new_issue) - end - - def rewrite_award_emoji(old_awardable, new_awardable) - old_awardable.award_emoji.each do |award| - new_award = award.dup - new_award.awardable = new_awardable - new_award.save - end - end - - def rewrite_content(content) - return unless content - - rewriters = [Gitlab::Gfm::ReferenceRewriter, - Gitlab::Gfm::UploadsRewriter] - - rewriters.inject(content) do |text, klass| - rewriter = klass.new(text, @old_project, @current_user) - rewriter.rewrite(@new_project) - end + def mark_as_moved + original_entity.update(moved_to: new_entity) end - def close_issue - close_service = CloseService.new(@old_project, @current_user) - close_service.execute(@old_issue, notifications: false, system_note: false) + def notify_participants + notification_service.async.issue_moved(original_entity, new_entity, @current_user) end - def add_note_moved_from - SystemNoteService.noteable_moved(@new_issue, @new_project, - @old_issue, @current_user, + def add_note_from + SystemNoteService.noteable_moved(new_entity, @target_project, + original_entity, current_user, direction: :from) end - def add_note_moved_to - SystemNoteService.noteable_moved(@old_issue, @old_project, - @new_issue, @current_user, + def add_note_to + SystemNoteService.noteable_moved(original_entity, old_project, + new_entity, current_user, direction: :to) end - - def mark_as_moved - @old_issue.update(moved_to: @new_issue) - end - - def notify_participants - notification_service.async.issue_moved(@old_issue, @new_issue, @current_user) - end end end diff --git a/app/services/merge_requests/reload_diffs_service.rb b/app/services/merge_requests/reload_diffs_service.rb index b47d8f3f63a..c64b2e99b52 100644 --- a/app/services/merge_requests/reload_diffs_service.rb +++ b/app/services/merge_requests/reload_diffs_service.rb @@ -29,10 +29,6 @@ module MergeRequests # rubocop: disable CodeReuse/ActiveRecord def clear_cache(new_diff) - # Executing the iteration we cache highlighted diffs for each diff file of - # MergeRequestDiff. - cacheable_collection(new_diff).write_cache - # Remove cache for all diffs on this MR. Do not use the association on the # model, as that will interfere with other actions happening when # reloading the diff. diff --git a/app/services/notes/base_service.rb b/app/services/notes/base_service.rb new file mode 100644 index 00000000000..c1260837c12 --- /dev/null +++ b/app/services/notes/base_service.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Notes + class BaseService < ::BaseService + def clear_noteable_diffs_cache(note) + if note.is_a?(DiffNote) && + note.discussion_first_note? && + note.position.unfolded_diff?(project.repository) + note.noteable.diffs.clear_cache + end + end + end +end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 049e6c5a871..e03789e3ca9 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Notes - class CreateService < ::BaseService + class CreateService < ::Notes::BaseService def execute merge_request_diff_head_sha = params.delete(:merge_request_diff_head_sha) @@ -35,6 +35,7 @@ module Notes if !only_commands && note.save todo_service.new_note(note, current_user) + clear_noteable_diffs_cache(note) end if command_params.present? diff --git a/app/services/notes/destroy_service.rb b/app/services/notes/destroy_service.rb index 64e9accd97f..fa0c2c5c86b 100644 --- a/app/services/notes/destroy_service.rb +++ b/app/services/notes/destroy_service.rb @@ -1,11 +1,13 @@ # frozen_string_literal: true module Notes - class DestroyService < BaseService + class DestroyService < ::Notes::BaseService def execute(note) TodoService.new.destroy_target(note) do |note| note.destroy end + + clear_noteable_diffs_cache(note) end end end diff --git a/app/services/projects/disable_deploy_key_service.rb b/app/services/projects/disable_deploy_key_service.rb new file mode 100644 index 00000000000..e483c0708c4 --- /dev/null +++ b/app/services/projects/disable_deploy_key_service.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Projects + class DisableDeployKeyService < BaseService + def execute + # rubocop: disable CodeReuse/ActiveRecord + deploy_key_project = project.deploy_keys_projects.find_by(deploy_key_id: params[:id]) + # rubocop: enable CodeReuse/ActiveRecord + + deploy_key_project&.destroy! + end + end +end diff --git a/app/services/projects/enable_deploy_key_service.rb b/app/services/projects/enable_deploy_key_service.rb index 102088e9557..38219cacee9 100644 --- a/app/services/projects/enable_deploy_key_service.rb +++ b/app/services/projects/enable_deploy_key_service.rb @@ -2,9 +2,10 @@ module Projects class EnableDeployKeyService < BaseService - # rubocop: disable CodeReuse/ActiveRecord def execute - key = accessible_keys.find_by(id: params[:key_id] || params[:id]) + key_id = params[:key_id] || params[:id] + key = find_accessible_key(key_id) + return unless key unless project.deploy_keys.include?(key) @@ -13,12 +14,15 @@ module Projects key end - # rubocop: enable CodeReuse/ActiveRecord private - def accessible_keys - current_user.accessible_deploy_keys + def find_accessible_key(key_id) + if current_user.admin? + DeployKey.find_by_id(key_id) + else + current_user.accessible_deploy_keys.find_by_id(key_id) + end end end end diff --git a/app/services/todos/destroy/entity_leave_service.rb b/app/services/todos/destroy/entity_leave_service.rb index e8d1bcdd142..ebfb20132d0 100644 --- a/app/services/todos/destroy/entity_leave_service.rb +++ b/app/services/todos/destroy/entity_leave_service.rb @@ -45,7 +45,7 @@ module Todos # rubocop: disable CodeReuse/ActiveRecord def remove_confidential_issue_todos Todo.where( - target_id: confidential_issues.select(:id), target_type: Issue, user_id: user.id + target_id: confidential_issues.select(:id), target_type: Issue.name, user_id: user.id ).delete_all end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/services/todos/destroy/private_features_service.rb b/app/services/todos/destroy/private_features_service.rb index a8c3fe0ef5a..bd49519d694 100644 --- a/app/services/todos/destroy/private_features_service.rb +++ b/app/services/todos/destroy/private_features_service.rb @@ -14,9 +14,9 @@ module Todos def execute ProjectFeature.where(project_id: project_ids).each do |project_features| target_types = [] - target_types << Issue if private?(project_features.issues_access_level) - target_types << MergeRequest if private?(project_features.merge_requests_access_level) - target_types << Commit if private?(project_features.repository_access_level) + target_types << Issue.name if private?(project_features.issues_access_level) + target_types << MergeRequest.name if private?(project_features.merge_requests_access_level) + target_types << Commit.name if private?(project_features.repository_access_level) next if target_types.empty? diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb index de6ff92d1da..3f503f3da28 100644 --- a/app/services/users/build_service.rb +++ b/app/services/users/build_service.rb @@ -28,7 +28,7 @@ module Users identity_attrs = params.slice(:extern_uid, :provider) - if identity_attrs.any? + unless identity_attrs.empty? user.identities.build(identity_attrs) end @@ -95,10 +95,6 @@ module Users if params[:reset_password] user_params.merge!(force_random_password: true, password_expires_at: nil) end - - if user_default_internal_regex_enabled? && !user_params.key?(:external) - user_params[:external] = user_external? - end else allowed_signup_params = signup_params allowed_signup_params << :skip_confirmation if skip_authorization @@ -109,6 +105,10 @@ module Users end end + if user_default_internal_regex_enabled? && !user_params.key?(:external) + user_params[:external] = user_external? + end + user_params end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index ffc1e5f75ca..e90599f2505 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -149,9 +149,9 @@ class FileUploader < GitlabUploader # return a new uploader with a file copy on another project def self.copy_to(uploader, to_project) - moved = uploader.dup.tap do |u| - u.model = to_project - end + moved = self.new(to_project) + moved.object_store = uploader.object_store + moved.filename = uploader.filename moved.copy_file(uploader.file) moved diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml index adb496495d1..0d42094fc89 100644 --- a/app/views/admin/application_settings/_ci_cd.html.haml +++ b/app/views/admin/application_settings/_ci_cd.html.haml @@ -42,12 +42,12 @@ <code>4 mins 2 sec</code>, <code>2h42min</code>. = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration') .form-group - = f.label :archive_builds_in_human_readable, 'Archive builds in', class: 'label-bold' + = f.label :archive_builds_in_human_readable, 'Archive jobs', class: 'label-bold' = f.text_field :archive_builds_in_human_readable, class: 'form-control', placeholder: 'never' .form-text.text-muted - Set the duration when build gonna be considered old. Archived builds cannot be retried. - Make it empty to never expire builds. It has to be larger than 1 day. - The default unit is in seconds, but you can define an alternative. For example: - <code>4 mins 2 sec</code>, <code>2h42min</code>. + Set the duration for which the jobs will be considered as old and expired. + Once that time passes, the jobs will be archived and no longer able to be + retried. Make it empty to never expire jobs. It has to be no less than 1 day, + for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>. = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index faa5854bb40..a0a00ac5d96 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -6,40 +6,5 @@ %p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing %hr - - .card - .card-header Sidekiq running processes - .card-body - - if @sidekiq_processes.empty? - %h4.cred - %i.fa.fa-exclamation-triangle - There are no running sidekiq processes. Please restart GitLab - - else - .table-holder - %table.table - %thead - %th USER - %th PID - %th CPU - %th MEM - %th STATE - %th START - %th COMMAND - %tbody - - @sidekiq_processes.each do |process| - %tr - %td= gitlab_config.user - - parse_sidekiq_ps(process).each do |value| - %td= value - .clearfix - %p - %i.fa.fa-exclamation-circle - If '[#{@concurrency} of #{@concurrency} busy]' is shown, restart GitLab with 'sudo service gitlab reload'. - %p - %i.fa.fa-exclamation-circle - If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{gitlab_config.user} -f sidekiq) and restart GitLab. - - - .card %iframe{ src: sidekiq_path, width: '100%', height: 970, style: "border: 0" } diff --git a/app/views/admin/impersonation_tokens/index.html.haml b/app/views/admin/impersonation_tokens/index.html.haml index 9e490713ef3..8e869fb4b71 100644 --- a/app/views/admin/impersonation_tokens/index.html.haml +++ b/app/views/admin/impersonation_tokens/index.html.haml @@ -5,6 +5,11 @@ .row.prepend-top-default .col-lg-12 + - if @new_impersonation_token + = render "shared/personal_access_tokens_created_container", new_token_value: @new_impersonation_token, + container_title: 'Your New Impersonation Token', + clipboard_button_title: 'Copy impersonation token to clipboard' + = render "shared/personal_access_tokens_form", path: admin_user_impersonation_tokens_path, impersonation: true, token: @impersonation_token, scopes: @scopes = render "shared/personal_access_tokens_table", impersonation: true, active_tokens: @active_impersonation_tokens, inactive_tokens: @inactive_impersonation_tokens diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index 832ba877558..fdd5c19d562 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -1,6 +1,6 @@ - @hide_top_links = true - page_title _("Issues") -- @breadcrumb_link = issues_dashboard_path(assignee_id: current_user.id) +- @breadcrumb_link = issues_dashboard_path(assignee_username: current_user.username) = content_for :meta_tags do = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues") @@ -16,7 +16,7 @@ .nav-controls = render 'shared/issuable/feed_buttons' -= render 'shared/issuable/filter', type: :issues += render 'shared/issuable/search_bar', type: :issues - if current_user && @no_filters_set = render 'shared/dashboard/no_filter_selected' diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml index fba8d1cf667..77cfa1271df 100644 --- a/app/views/dashboard/merge_requests.html.haml +++ b/app/views/dashboard/merge_requests.html.haml @@ -1,6 +1,6 @@ - @hide_top_links = true - page_title _("Merge Requests") -- @breadcrumb_link = merge_requests_dashboard_path(assignee_id: current_user.id) +- @breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username) .page-title-holder %h1.page-title= _('Merge Requests') @@ -12,7 +12,7 @@ .top-area = render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set -= render 'shared/issuable/filter', type: :merge_requests += render 'shared/issuable/search_bar', type: :merge_requests - if current_user && @no_filters_set = render 'shared/dashboard/no_filter_selected' diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index 5ddb3ece1cb..ec968e435cd 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -1,10 +1,10 @@ - if form_based_providers.any? - if crowd_enabled? - .login-box.tab-pane.active{ id: "crowd", role: 'tabpanel' } + .login-box.tab-pane{ id: "crowd", role: 'tabpanel', class: active_when(form_based_auth_provider_has_active_class?(:crowd)) } .login-body = render 'devise/sessions/new_crowd' - @ldap_servers.each_with_index do |server, i| - .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && !crowd_enabled?) } + .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && form_based_auth_provider_has_active_class?(:ldapmain)) } .login-body = render 'devise/sessions/new_ldap', server: server - if password_authentication_enabled_for_web? @@ -12,6 +12,8 @@ .login-body = render 'devise/sessions/new_base' + = render_if_exists 'devise/sessions/new_smartcard' + - elsif password_authentication_enabled_for_web? .login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' } .login-body diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml index 7dced0942f5..aee05b6c81c 100644 --- a/app/views/devise/shared/_tabs_ldap.html.haml +++ b/app/views/devise/shared/_tabs_ldap.html.haml @@ -1,10 +1,13 @@ %ul.nav-links.new-session-tabs.nav-tabs.nav{ class: ('custom-provider-tabs' if form_based_providers.any?) } - if crowd_enabled? %li.nav-item - = link_to "Crowd", "#crowd", class: 'nav-link active', 'data-toggle' => 'tab' + = link_to "Crowd", "#crowd", class: "nav-link #{active_when(form_based_auth_provider_has_active_class?(:crowd))}", 'data-toggle' => 'tab' - @ldap_servers.each_with_index do |server, i| %li.nav-item - = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && !crowd_enabled?)} qa-ldap-tab", 'data-toggle' => 'tab' + = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && form_based_auth_provider_has_active_class?(:ldapmain))} qa-ldap-tab", 'data-toggle' => 'tab' + + = render_if_exists 'devise/shared/tab_smartcard' + - if password_authentication_enabled_for_web? %li.nav-item = link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab' diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml index 1765251c93d..10187129a33 100644 --- a/app/views/discussions/_discussion.html.haml +++ b/app/views/discussions/_discussion.html.haml @@ -1,12 +1,12 @@ - expanded = discussion.expanded? -%li.note.note-discussion.timeline-entry +%li.note.note-discussion.timeline-entry.unstyled-comments .timeline-entry-inner - .timeline-icon - = link_to user_path(discussion.author) do - = image_tag avatar_icon_for_user(discussion.author), class: "avatar s40" .timeline-content .discussion.js-toggle-container{ data: { discussion_id: discussion.id } } .discussion-header + .timeline-icon + = link_to user_path(discussion.author) do + = image_tag avatar_icon_for_user(discussion.author), class: "avatar s40" .discussion-actions %button.note-action-button.discussion-toggle-button.js-toggle-button{ type: "button", class: ("js-toggle-lazy-diff" unless expanded) } - if expanded diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index 82693ec832e..69914fccc48 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -7,10 +7,10 @@ .event-title.d-flex.flex-wrap = inline_event_icon(event) %span.event-type.d-inline-block.append-right-4.pushed #{event.action_name} #{event.ref_type} - %span + %span.append-right-4 - commits_link = project_commits_path(project, event.ref_name) - should_link = event.tag? ? project.repository.tag_exists?(event.ref_name) : project.repository.branch_exists?(event.ref_name) - = link_to_if should_link, event.ref_name, commits_link, class: 'ref-name append-right-4' + = link_to_if should_link, event.ref_name, commits_link, class: 'ref-name' = render "events/event_scope", event: event diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 8f8b6b454d9..ea5f2b166b4 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -64,7 +64,7 @@ = link_to '#', class: 'dashboard-shortcuts-web-ide', title: _('Web IDE') do = _('Web IDE') - - if Gitlab::Sherlock.enabled? || can?(current_user, :read_instance_statistics) + - if show_separator? %li.line-separator.d-none.d-sm-block = render_if_exists 'dashboard/operations/nav_link' - if can?(current_user, :read_instance_statistics) diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index c10d4ea1a4d..c1e1eaff942 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -14,17 +14,7 @@ .col-lg-8 - if @new_personal_access_token - .created-personal-access-token-container - %h5.prepend-top-0 - Your New Personal Access Token - .form-group - .input-group - = text_field_tag 'created-personal-access-token', @new_personal_access_token, readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-personal-access-token-help-block" - %span.input-group-append - = clipboard_button(text: @new_personal_access_token, title: "Copy personal access token to clipboard", placement: "left", class: "input-group-text btn-default btn-clipboard") - %span#created-personal-access-token-help-block.form-text.text-muted.text-danger Make sure you save it - you won't be able to access it again. - - %hr + = render "shared/personal_access_tokens_created_container", new_token_value: @new_personal_access_token = render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml index e8a5e63e59e..bc9f6c71fa8 100644 --- a/app/views/projects/diffs/_text_file.html.haml +++ b/app/views/projects/diffs/_text_file.html.haml @@ -3,7 +3,7 @@ .suppressed-container %a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show. -%table.text-file.diff-wrap-lines.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' } +%table.text-file.diff-wrap-lines.code.js-syntax-highlight.commit-diff{ data: diff_view_data, class: too_big ? 'hide' : '' } = render partial: "projects/diffs/line", collection: diff_file.highlighted_diff_lines, as: :line, diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 3aff5538813..de768696fe9 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -165,7 +165,7 @@ .input-group .input-group-prepend .input-group-text - #{URI.join(root_url, @project.namespace.full_path)}/ + #{Gitlab::Utils.append_path(root_url, @project.namespace.full_path)}/ = f.text_field :path, class: 'form-control' %ul %li Be careful. Renaming a project's repository can have unintended side effects. diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml index b3a82d1ef41..82567f88ccc 100644 --- a/app/views/projects/environments/_external_url.html.haml +++ b/app/views/projects/environments/_external_url.html.haml @@ -1,4 +1,4 @@ - if environment.external_url && can?(current_user, :read_environment, environment) = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip qa-view-deployment', title: s_('Environments|Open live environment') do = sprite_icon('external-link') - View deployment + = _("View deployment") diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml index f942b936037..cbd5c54cecc 100644 --- a/app/views/projects/environments/_form.html.haml +++ b/app/views/projects/environments/_form.html.haml @@ -1,22 +1,21 @@ .row.prepend-top-default.append-bottom-default .col-lg-3 %h4.prepend-top-0 - Environments + = _("Environments") %p - Environments allow you to track deployments of your application - = succeed "." do - = link_to "Read more about environments", help_page_path("ci/environments") + - link_to_read_more = link_to(_("Read more about environments"), help_page_path("ci/environments")) + = _("Environments allow you to track deployments of your application %{link_to_read_more}.").html_safe % { link_to_read_more: link_to_read_more } = form_for [@project.namespace.becomes(Namespace), @project, @environment], html: { class: 'col-lg-9' } do |f| = form_errors(@environment) .form-group - = f.label :name, 'Name', class: 'label-bold' + = f.label :name, _('Name'), class: 'label-bold' = f.text_field :name, required: true, class: 'form-control' .form-group - = f.label :external_url, 'External URL', class: 'label-bold' + = f.label :external_url, _('External URL'), class: 'label-bold' = f.url_field :external_url, class: 'form-control' .form-actions - = f.submit 'Save', class: 'btn btn-success' - = link_to 'Cancel', project_environments_path(@project), class: 'btn btn-cancel' + = f.submit _('Save'), class: 'btn btn-save' + = link_to _('Cancel'), project_environments_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/environments/_metrics_button.html.haml b/app/views/projects/environments/_metrics_button.html.haml index a4b27575095..c4f19ea79e7 100644 --- a/app/views/projects/environments/_metrics_button.html.haml +++ b/app/views/projects/environments/_metrics_button.html.haml @@ -2,6 +2,6 @@ - return unless can?(current_user, :read_environment, environment) -= link_to environment_metrics_path(environment), title: 'See metrics', class: 'btn metrics-button' do += link_to environment_metrics_path(environment), title: _('See metrics'), class: 'btn metrics-button' do = sprite_icon('chart') - Monitoring + = _("Monitoring") diff --git a/app/views/projects/environments/edit.html.haml b/app/views/projects/environments/edit.html.haml index d6ff3f729b4..d581bd3aeab 100644 --- a/app/views/projects/environments/edit.html.haml +++ b/app/views/projects/environments/edit.html.haml @@ -1,8 +1,8 @@ - @no_container = true -- page_title "Edit", @environment.name, "Environments" +- page_title _("Edit"), @environment.name, _("Environments") %div{ class: container_class } %h3.page-title - Edit environment + = _('Edit environment') %hr = render 'form' diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index 1ac7dab6775..b7e1cf85cb7 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -1,5 +1,5 @@ - @no_container = true -- page_title "Environments" +- page_title _("Environments") #environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json), "folder-name" => @folder, diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 7ebe617766f..6c0ad34c486 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -1,6 +1,6 @@ - @no_container = true -- page_title "Environments" -- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) +- page_title _("Environments") +- add_to_breadcrumbs(_("Pipelines"), project_pipelines_path(@project)) #environments-list-view{ data: { environments_data: environments_list_data, "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 4222963a754..7b847a85686 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -1,5 +1,5 @@ - @no_container = true -- page_title "Metrics for environment", @environment.name +- page_title _("Metrics for environment"), @environment.name .prometheus-container{ class: container_class } #prometheus-graphs{ data: metrics_data(@project, @environment) } diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml index 62b08e85e22..c1067fdff78 100644 --- a/app/views/projects/environments/new.html.haml +++ b/app/views/projects/environments/new.html.haml @@ -1,9 +1,9 @@ - @no_container = true -- breadcrumb_title "Environments" -- page_title 'New Environment' +- breadcrumb_title _("Environments") +- page_title _("New Environment") %div{ class: container_class } %h3.page-title - New environment + = _("New environment") %hr = render 'form' diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index 8c5b6e089ea..d59b2d4fb01 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -1,7 +1,7 @@ - @no_container = true -- add_to_breadcrumbs "Environments", project_environments_path(@project) +- add_to_breadcrumbs _("Environments"), project_environments_path(@project) - breadcrumb_title @environment.name -- page_title "Environments" +- page_title _("Environments") %div{ class: container_class } - if can?(current_user, :stop_environment, @environment) @@ -10,7 +10,7 @@ .modal-content .modal-header %h4.modal-title.d-flex.mw-100 - Stopping + = s_("Environments|Stopping") %span.has-tooltip.text-truncate.ml-1.mr-1.flex-fill{ title: @environment.name, data: { container: '#stop-environment-modal' } } = @environment.name ? @@ -40,7 +40,7 @@ = render 'projects/environments/external_url', environment: @environment = render 'projects/environments/metrics_button', environment: @environment - if can?(current_user, :update_environment, @environment) - = link_to 'Edit', edit_project_environment_path(@project, @environment), class: 'btn' + = link_to _('Edit'), edit_project_environment_path(@project, @environment), class: 'btn' - if can?(current_user, :stop_environment, @environment) = button_tag class: 'btn btn-danger', type: 'button', data: { toggle: 'modal', target: '#stop-environment-modal' } do @@ -52,21 +52,19 @@ .empty-state .text-content %h4.state-title - You don't have any deployments right now. + = _("You don't have any deployments right now.") %p.blank-state-text - Define environments in the deploy stage(s) in - %code .gitlab-ci.yml - to track deployments here. + = _("Define environments in the deploy stage(s) in <code>.gitlab-ci.yml</code> to track deployments here.").html_safe .text-center = link_to _("Read more"), help_page_path("ci/environments"), class: "btn btn-success" - else .table-holder .ci-table.environments{ role: 'grid' } .gl-responsive-table-row.table-row-header{ role: 'row' } - .table-section.section-10{ role: 'columnheader' } ID - .table-section.section-30{ role: 'columnheader' } Commit - .table-section.section-25{ role: 'columnheader' } Job - .table-section.section-15{ role: 'columnheader' } Created + .table-section.section-10{ role: 'columnheader' }= _('ID') + .table-section.section-30{ role: 'columnheader' }= _('Commit') + .table-section.section-25{ role: 'columnheader' }= _('Job') + .table-section.section-15{ role: 'columnheader' }= _('Created') = render @deployments diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml index e40d631a1a1..e837d3d56ac 100644 --- a/app/views/projects/environments/terminal.html.haml +++ b/app/views/projects/environments/terminal.html.haml @@ -1,5 +1,5 @@ - @no_container = true -- page_title "Terminal for environment", @environment.name +- page_title _("Terminal for environment"), @environment.name - content_for :page_specific_javascripts do = stylesheet_link_tag "xterm.css" @@ -9,7 +9,7 @@ .row .col-sm-6 %h3.page-title - Terminal for environment + = _("Terminal for environment") = @environment.name .col-sm-6 diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml index 1bf42ded97a..3cd83feb842 100644 --- a/app/views/projects/merge_requests/_mr_title.html.haml +++ b/app/views/projects/merge_requests/_mr_title.html.haml @@ -37,6 +37,6 @@ = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request' - if can_update_merge_request - = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-sm-none d-md-block btn btn-grouped js-issuable-edit" + = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-sm-none d-md-block btn btn-grouped js-issuable-edit qa-edit-button" = render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request, can_reopen: can_update_merge_request diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 71f34c0d85b..21ea188d7b3 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -23,28 +23,29 @@ %li.inline = link_to edit_label_path(label), class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do = sprite_icon('pencil') - %li.inline - .dropdown - %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' }, aria_label: _('Label actions dropdown') } - = sprite_icon('ellipsis_v') - .dropdown-menu.dropdown-open-left - %ul - - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) - %li - %button.js-promote-project-label-button.btn.btn-transparent.btn-action{ disabled: true, type: 'button', - data: { url: promote_project_label_path(label.project, label), - label_title: label.title, - label_color: label.color, - label_text_color: label.text_color, - group_name: label.project.group.name, - target: '#promote-label-modal', - container: 'body', - toggle: 'modal' } } - = _('Promote to group label') - - if can?(current_user, :admin_label, label) - %li - %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } } - %button.text-danger.remove-row{ type: 'button' }= _('Delete') + - if can?(current_user, :admin_label, label) + %li.inline + .dropdown + %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' }, aria_label: _('Label actions dropdown') } + = sprite_icon('ellipsis_v') + .dropdown-menu.dropdown-open-left + %ul + - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) + %li + %button.js-promote-project-label-button.btn.btn-transparent.btn-action{ disabled: true, type: 'button', + data: { url: promote_project_label_path(label.project, label), + label_title: label.title, + label_color: label.color, + label_text_color: label.text_color, + group_name: label.project.group.name, + target: '#promote-label-modal', + container: 'body', + toggle: 'modal' } } + = _('Promote to group label') + - if can?(current_user, :admin_label, label) + %li + %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } } + %button.text-danger.remove-row{ type: 'button' }= _('Delete') - if current_user %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) diff --git a/app/views/shared/_labels_row.html.haml b/app/views/shared/_labels_row.html.haml deleted file mode 100644 index 21b37a7c9ae..00000000000 --- a/app/views/shared/_labels_row.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -- labels.each do |label| - %span.label-row.btn-group{ role: "group", aria: { label: label.name }, style: "color: #{text_color_for_bg(label.color)}" } - = link_to_label(label, subject: @project, css_class: 'btn btn-transparent') - %button.btn.btn-transparent.label-remove.js-label-filter-remove{ type: "button", style: "background-color: #{label.color};", data: { label: label.title } } - = icon("times") diff --git a/app/views/shared/_personal_access_tokens_created_container.html.haml b/app/views/shared/_personal_access_tokens_created_container.html.haml new file mode 100644 index 00000000000..3150d39b84a --- /dev/null +++ b/app/views/shared/_personal_access_tokens_created_container.html.haml @@ -0,0 +1,14 @@ +- container_title = local_assigns.fetch(:container_title, 'Your New Personal Access Token') +- clipboard_button_title = local_assigns.fetch(:clipboard_button_title, 'Copy personal access token to clipboard') + +.created-personal-access-token-container + %h5.prepend-top-0 + = container_title + .form-group + .input-group + = text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-token-help-block" + %span.input-group-append + = clipboard_button(text: new_token_value, title: clipboard_button_title, placement: "left", class: "input-group-text btn-default btn-clipboard") + %span#created-token-help-block.form-text.text-muted.text-danger Make sure you save it - you won't be able to access it again. + +%hr diff --git a/app/views/shared/_personal_access_tokens_table.html.haml b/app/views/shared/_personal_access_tokens_table.html.haml index cadac1cc99d..2efd03d4867 100644 --- a/app/views/shared/_personal_access_tokens_table.html.haml +++ b/app/views/shared/_personal_access_tokens_table.html.haml @@ -15,8 +15,6 @@ %th Created %th Expires %th Scopes - - if impersonation - %th Token %th %tbody - active_tokens.each do |token| @@ -30,10 +28,6 @@ - else %span.token-never-expires-label Never %td= token.scopes.present? ? token.scopes.join(", ") : "<no scopes selected>" - - if impersonation - %td.token-token-container - = text_field_tag 'impersonation-token-token', token.token, readonly: true, class: "form-control" - = clipboard_button(text: token.token) - path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token) %td= link_to "Revoke", path, method: :put, class: "btn btn-danger float-right", data: { confirm: "Are you sure you want to revoke this #{type} Token? This action cannot be undone." } - else diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml deleted file mode 100644 index c7037335866..00000000000 --- a/app/views/shared/issuable/_filter.html.haml +++ /dev/null @@ -1,32 +0,0 @@ -.issues-filters - .issues-details-filters.row-content-block.second-block - = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search]), method: :get, class: 'filter-form js-filter-form' do - - if params[:search].present? - = hidden_field_tag :search, params[:search] - .issues-other-filters - .filter-item.inline - - if params[:author_id].present? - = hidden_field_tag(:author_id, params[:author_id]) - = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit", - placeholder: "Search authors", data: { any_user: "Any Author", first_user: current_user&.username, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:author_id], field_name: "author_id", default_label: "Author" } }) - - .filter-item.inline - - if params[:assignee_id].present? - = hidden_field_tag(:assignee_id, params[:assignee_id]) - = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", - placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user&.username, null_user: true, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } }) - - .filter-item.inline.milestone-filter - = render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, show_started: true - - .filter-item.inline.labels-filter - = render "shared/issuable/label_dropdown", selected: selected_labels, use_id: false, selected_toggle: params[:label_name], data_options: { field_name: "label_name[]" } - - - unless @no_filters_set - .float-right - = render 'shared/sort_dropdown' - - - has_labels = @labels && @labels.any? - .row-content-block.second-block.filtered-labels{ class: ("hidden" unless has_labels) } - - if has_labels - = render 'shared/labels_row', labels: @labels diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 95f32bd0180..824bbe3524b 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -1,7 +1,6 @@ - type = local_assigns.fetch(:type) - board = local_assigns.fetch(:board, nil) - block_css_class = type != :boards_modal ? 'row-content-block second-block' : '' -- full_path = @project.present? ? @project.full_path : @group.full_path - user_can_admin_list = board && can?(current_user, :admin_list, board.parent) - show_sorting_dropdown = local_assigns.fetch(:show_sorting_dropdown, true) @@ -10,7 +9,7 @@ - if type == :boards #js-multiple-boards-switcher.inline.boards-switcher{ "v-cloak" => true } = render_if_exists "shared/boards/switcher", board: board - = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search]), method: :get, class: 'filter-form js-filter-form' do + = form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form' do - if params[:search].present? = hidden_field_tag :search, params[:search] - if @can_bulk_update @@ -25,7 +24,7 @@ dropdown_class: "filtered-search-history-dropdown", content_class: "filtered-search-history-dropdown-content", title: "Recent searches" }) do - .js-filtered-search-history-dropdown{ data: { full_path: full_path } } + .js-filtered-search-history-dropdown{ data: { full_path: search_history_storage_prefix } } .filtered-search-box-input-container.droplab-dropdown .scroll-container %ul.tokens-container.list-unstyled diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index 84adbd444c5..bc918430823 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -5,7 +5,7 @@ - note_editable = can?(current_user, :admin_note, note) - note_counter = local_assigns.fetch(:note_counter, 0) -%li.timeline-entry{ id: dom_id(note), +%li.timeline-entry.note-wrapper.outlined{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: { author_id: note.author.id, editable: note_editable, diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml index ec1e10bb0c1..4c4050c6054 100644 --- a/app/views/shared/notes/_notes_with_form.html.haml +++ b/app/views/shared/notes/_notes_with_form.html.haml @@ -8,7 +8,7 @@ - if can_create_note? .notes.notes-form.timeline - .timeline-entry + .timeline-entry.note-form .timeline-entry-inner .flash-container.timeline-content diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml index 1c788b9a737..979f6862de3 100644 --- a/app/views/u2f/_authenticate.html.haml +++ b/app/views/u2f/_authenticate.html.haml @@ -1,18 +1,18 @@ #js-authenticate-u2f -%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' } Sign in via 2FA code +%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' }= _("Sign in via 2FA code") -# haml-lint:disable InlineJavaScript %script#js-authenticate-u2f-in-progress{ type: "text/template" } - %p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now. + %p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.") %script#js-authenticate-u2f-error{ type: "text/template" } %div - %p <%= error_message %> (error code: <%= error_code %>) - %a.btn.btn-block.btn-warning#js-u2f-try-again Try again? + %p <%= error_message %> (#{_("error code:")} <%= error_code %>) + %a.btn.btn-block.btn-warning#js-u2f-try-again= _("Try again?") %script#js-authenticate-u2f-authenticated{ type: "text/template" } %div - %p We heard back from your U2F device. You have been authenticated. + %p= _("We heard back from your U2F device. You have been authenticated.") = form_tag(new_user_session_path, method: :post, id: 'js-login-u2f-form') do |f| - resource_params = params[resource_name].presence || params = hidden_field_tag 'user[remember_me]', resource_params.fetch(:remember_me, 0) diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index 39d4d82a77d..f6724f72307 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -2,39 +2,39 @@ -# haml-lint:disable InlineJavaScript %script#js-register-u2f-not-supported{ type: "text/template" } - %p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer). + %p= _("Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).") %script#js-register-u2f-setup{ type: "text/template" } - if current_user.two_factor_otp_enabled? .row.append-bottom-10 .col-md-4 - %button#js-setup-u2f-device.btn.btn-info.btn-block Set up new U2F device + %button#js-setup-u2f-device.btn.btn-info.btn-block= _("Set up new U2F device") .col-md-8 - %p Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left. + %p= _("Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left.") - else .row.append-bottom-10 .col-md-4 - %button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true } Set up new U2F device + %button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true }= _("Set up new U2F device") .col-md-8 - %p.text-warning You need to register a two-factor authentication app before you can set up a U2F device. + %p.text-warning= _("You need to register a two-factor authentication app before you can set up a U2F device.") %script#js-register-u2f-in-progress{ type: "text/template" } - %p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now. + %p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.") %script#js-register-u2f-error{ type: "text/template" } %div %p - %span <%= error_message %> (error code: <%= error_code %>) - %a.btn.btn-warning#js-u2f-try-again Try again? + %span <%= error_message %> (#{_("error code:")} <%= error_code %>) + %a.btn.btn-warning#js-u2f-try-again= _("Try again?") %script#js-register-u2f-registered{ type: "text/template" } .row.append-bottom-10 .col-md-12 - %p Your device was successfully set up! Give it a name and register it with the GitLab server. + %p= _("Your device was successfully set up! Give it a name and register it with the GitLab server.") = form_tag(create_u2f_profile_two_factor_auth_path, method: :post) do .row.append-bottom-10 .col-md-3 - = text_field_tag 'u2f_registration[name]', nil, class: 'form-control', placeholder: "Pick a name" + = text_field_tag 'u2f_registration[name]', nil, class: 'form-control', placeholder: _("Pick a name") .col-md-3 = hidden_field_tag 'u2f_registration[device_response]', nil, class: 'form-control', required: true, id: "js-device-response" - = submit_tag "Register U2F device", class: "btn btn-success" + = submit_tag _("Register U2F device"), class: "btn btn-success" diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml index cf525f2bb2d..b5bc1180290 100644 --- a/app/views/users/_overview.html.haml +++ b/app/views/users/_overview.html.haml @@ -1,32 +1,30 @@ .row + .col-12 + .calendar-block.prepend-top-default.append-bottom-default + .user-calendar.d-none.d-sm-block{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } } + %h4.center.light + = spinner nil, true + .user-calendar-activities.d-none.d-sm-block +.row .col-md-12.col-lg-6 - .calendar-block - .content-block.hide-bottom-border - %h4 - = s_('UserProfile|Activity') - .user-calendar.d-none.d-sm-block.text-left{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } } - %h4.center.light - %i.fa.fa-spinner.fa-spin - .user-calendar-activities.d-none.d-sm-block - - if can?(current_user, :read_cross_project) .activities-block - .border-bottom.prepend-top-16 - %h5 - = s_('UserProfile|Recent contributions') + .prepend-top-16 + .d-flex.align-items-center.border-bottom + %h4.flex-grow + = s_('UserProfile|Activity') + = link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all" .overview-content-list{ data: { href: user_path } } .center.light.loading - %i.fa.fa-spinner.fa-spin - .prepend-top-10 - = link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all" + = spinner nil, true .col-md-12.col-lg-6 .projects-block - .border-bottom.prepend-top-16 - %h4 - = s_('UserProfile|Personal projects') + .prepend-top-16 + .d-flex.align-items-center.border-bottom + %h4.flex-grow + = s_('UserProfile|Personal projects') + = link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all" .overview-content-list{ data: { href: user_projects_path } } .center.light.loading - %i.fa.fa-spinner.fa-spin - .prepend-top-10 - = link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all" + = spinner nil, true diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index d6c8420b744..d11476738e4 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -124,12 +124,6 @@ - if profile_tab?(:activity) #activity.tab-pane - .row-content-block.calendar-block.white.second-block.d-none.d-sm-block - .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } } - %h4.center.light - %i.fa.fa-spinner.fa-spin - .user-calendar-activities - - if can?(current_user, :read_cross_project) %h4.prepend-top-20 = s_('UserProfile|Most Recent Activity') diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb index 51cbbe8882e..61d866b1f02 100644 --- a/app/workers/build_finished_worker.rb +++ b/app/workers/build_finished_worker.rb @@ -13,7 +13,7 @@ class BuildFinishedWorker BuildTraceSectionsWorker.new.perform(build.id) BuildCoverageWorker.new.perform(build.id) - # We execute that async as this are two indepentent operations that can be executed after TraceSections and Coverage + # We execute that async as this are two independent operations that can be executed after TraceSections and Coverage BuildHooksWorker.perform_async(build.id) ArchiveTraceWorker.perform_async(build.id) end diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb index 7b167c95c29..29a7f8e691a 100644 --- a/app/workers/process_commit_worker.rb +++ b/app/workers/process_commit_worker.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Worker for processing individiual commit messages pushed to a repository. +# Worker for processing individual commit messages pushed to a repository. # # Jobs for this worker are scheduled for every commit that is being pushed. As a # result of this the workload of this worker should be kept to a bare minimum. |