diff options
author | Phil Hughes <me@iamphill.com> | 2018-04-27 17:58:18 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-04-27 17:58:18 +0100 |
commit | cb4a91e267da3d0d5adc6faf43ccb70ab3e686d6 (patch) | |
tree | a7f8534eae39da9f9ebb7d1f4795e347dcf7c271 /app | |
parent | f1b206a56b7add6142a8d2cec7a2df49633e1d60 (diff) | |
parent | 6a651eb721dc421297c5f52c937d25faa139bc13 (diff) | |
download | gitlab-ce-cb4a91e267da3d0d5adc6faf43ccb70ab3e686d6.tar.gz |
Merge branch '44846-improve-web-ide-left-panel-and-modes' into ide-activity-bar-review-mode
Diffstat (limited to 'app')
53 files changed, 368 insertions, 315 deletions
diff --git a/app/assets/javascripts/ide/components/activity_bar.vue b/app/assets/javascripts/ide/components/activity_bar.vue index 0465de54977..561ffde885a 100644 --- a/app/assets/javascripts/ide/components/activity_bar.vue +++ b/app/assets/javascripts/ide/components/activity_bar.vue @@ -1,7 +1,7 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; -import { ActivityBarViews } from '../constants'; +import { activityBarViews } from '../constants'; export default { components: { @@ -17,7 +17,7 @@ export default { methods: { ...mapActions(['updateActivityBarView']), }, - ActivityBarViews, + activityBarViews, }; </script> @@ -41,9 +41,10 @@ export default { type="button" class="ide-sidebar-link js-ide-edit-mode" :class="{ - active: currentActivityView === $options.ActivityBarViews.edit + active: currentActivityView === $options.activityBarViews.edit }" - @click.prevent="updateActivityBarView($options.ActivityBarViews.edit)" + @click.prevent="updateActivityBarView($options.activityBarViews.edit)" + :aria-label="s__('IDE|Edit mode')" > <icon name="code" @@ -69,9 +70,10 @@ export default { type="button" class="ide-sidebar-link js-ide-commit-mode" :class="{ - active: currentActivityView === $options.ActivityBarViews.commit + active: currentActivityView === $options.activityBarViews.commit }" - @click.prevent="updateActivityBarView($options.ActivityBarViews.commit)" + @click.prevent="updateActivityBarView($options.activityBarViews.commit)" + :aria-label="s__('IDE|Commit mode')" > <icon name="commit" diff --git a/app/assets/javascripts/ide/components/changed_file_icon.vue b/app/assets/javascripts/ide/components/changed_file_icon.vue index 1fc11c84639..fdbc14a4b8f 100644 --- a/app/assets/javascripts/ide/components/changed_file_icon.vue +++ b/app/assets/javascripts/ide/components/changed_file_icon.vue @@ -26,11 +26,18 @@ export default { required: false, default: false, }, + forceModifiedIcon: { + type: Boolean, + required: false, + default: false, + }, }, computed: { changedIcon() { const suffix = this.file.staged && !this.showStagedIcon ? '-solid' : ''; - return this.file.tempFile ? `file-addition${suffix}` : `file-modified${suffix}`; + return this.file.tempFile && !this.forceModifiedIcon + ? `file-addition${suffix}` + : `file-modified${suffix}`; }, stagedIcon() { return `${this.changedIcon}-solid`; diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index 7a5057b1e8e..77b35078e56 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -17,12 +17,6 @@ export default { RepoEditor, FindFile, }, - props: { - emptyStateSvgPath: { - type: String, - required: true, - }, - }, computed: { ...mapState([ 'changedFiles', @@ -30,6 +24,7 @@ export default { 'viewer', 'currentMergeRequestId', 'fileFindVisible', + 'emptyStateSvgPath', ]), ...mapGetters(['activeFile', 'hasChanges']), }, diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue index 769e9b79cad..b1b5c0d4a28 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/index.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -1,49 +1,54 @@ <script> - import { mapActions } from 'vuex'; - import icon from '~/vue_shared/components/icon.vue'; - import newModal from './modal.vue'; - import upload from './upload.vue'; +import { mapActions } from 'vuex'; +import icon from '~/vue_shared/components/icon.vue'; +import newModal from './modal.vue'; +import upload from './upload.vue'; - export default { - components: { - icon, - newModal, - upload, +export default { + components: { + icon, + newModal, + upload, + }, + props: { + branch: { + type: String, + required: true, }, - props: { - branch: { - type: String, - required: true, - }, - path: { - type: String, - required: true, - }, + path: { + type: String, + required: true, }, - data() { - return { - openModal: false, - modalType: '', - dropdownOpen: false, - }; + }, + data() { + return { + openModal: false, + modalType: '', + dropdownOpen: false, + }; + }, + watch: { + dropdownOpen() { + this.$nextTick(() => { + this.$refs.dropdownMenu.scrollIntoView(); + }); }, - methods: { - ...mapActions([ - 'createTempEntry', - ]), - createNewItem(type) { - this.modalType = type; - this.openModal = true; - this.dropdownOpen = false; - }, - hideModal() { - this.openModal = false; - }, - openDropdown() { - this.dropdownOpen = !this.dropdownOpen; - }, + }, + methods: { + ...mapActions(['createTempEntry']), + createNewItem(type) { + this.modalType = type; + this.openModal = true; + this.dropdownOpen = false; }, - }; + hideModal() { + this.openModal = false; + }, + openDropdown() { + this.dropdownOpen = !this.dropdownOpen; + }, + }, +}; </script> <template> @@ -71,7 +76,10 @@ css-classes="pull-left" /> </button> - <ul class="dropdown-menu dropdown-menu-right"> + <ul + class="dropdown-menu dropdown-menu-right" + ref="dropdownMenu" + > <li> <a href="#" diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index 4b5a50785b6..a95a0225950 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -40,13 +40,6 @@ export default { return __('Create file'); }, - formLabelName() { - if (this.type === 'tree') { - return __('Directory name'); - } - - return __('File name'); - }, }, mounted() { this.$refs.fieldName.focus(); @@ -82,8 +75,8 @@ export default { @submit.prevent="createEntryInStore" > <fieldset class="form-group append-bottom-0"> - <label class="label-light col-sm-3"> - {{ formLabelName }} + <label class="label-light col-sm-3 ide-new-modal-label"> + {{ __('Name') }} </label> <div class="col-sm-9"> <input diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue index 1a7ed991bf0..4f102cfc026 100644 --- a/app/assets/javascripts/ide/components/repo_file.vue +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -100,7 +100,7 @@ export default { :file="file" /> </span> - <span class="pull-right"> + <span class="pull-right ide-file-icon-holder"> <mr-file-icon v-if="file.mrChange" /> @@ -109,7 +109,8 @@ export default { :file="file" :show-tooltip="true" :show-staged-icon="true" - class="prepend-top-5 pull-right" + :force-modified-icon="true" + class="pull-right" /> </span> <new-dropdown diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue index 99f67862636..b3221deada0 100644 --- a/app/assets/javascripts/ide/components/repo_tab.vue +++ b/app/assets/javascripts/ide/components/repo_tab.vue @@ -100,6 +100,7 @@ export default { <changed-file-icon v-else :file="tab" + :force-modified-icon="true" /> </button> </li> diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js index d8a49894a7e..16110b2f5a4 100644 --- a/app/assets/javascripts/ide/constants.js +++ b/app/assets/javascripts/ide/constants.js @@ -7,7 +7,7 @@ export const FILE_FINDER_EMPTY_ROW_HEIGHT = 33; export const MAX_TITLE_LENGTH = 50; export const MAX_BODY_LENGTH = 72; -export const ActivityBarViews = { +export const activityBarViews = { edit: 'ide-tree', commit: 'commit-section', review: 'ide-review', diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js index bbe3398e416..e5c47d09886 100644 --- a/app/assets/javascripts/ide/index.js +++ b/app/assets/javascripts/ide/index.js @@ -22,11 +22,7 @@ function initIde(el) { }); }, render(createElement) { - return createElement('ide', { - props: { - emptyStateSvgPath: el.dataset.emptyStateSvgPath, - }, - }); + return createElement('ide'); }, }); } diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index ab3fb1422b8..9900eabb358 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -74,7 +74,7 @@ export const createTempEntry = ( } worker.addEventListener('message', ({ data }) => { - const { file } = data; + const { file, parentPath } = data; worker.terminate(); @@ -90,6 +90,10 @@ export const createTempEntry = ( dispatch('setFileActive', file.path); } + if (parentPath && !state.entries[parentPath].opened) { + commit(types.TOGGLE_TREE_OPEN, parentPath); + } + resolve(file); }); @@ -146,6 +150,14 @@ export const setCurrentBranchId = ({ commit }, currentBranchId) => { commit(types.SET_CURRENT_BRANCH, currentBranchId); }; +export const updateTempFlagForEntry = ({ commit, dispatch, state }, { file, tempFile }) => { + commit(types.UPDATE_TEMP_FLAG, { path: file.path, tempFile }); + + if (file.parentPath) { + dispatch('updateTempFlagForEntry', { file: state.entries[file.parentPath], tempFile }); + } +}; + export const toggleFileFinder = ({ commit }, fileFindVisible) => commit(types.TOGGLE_FILE_FINDER, fileFindVisible); diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index 8bdf59dac26..c851bbd0b59 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -62,7 +62,7 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive const file = state.entries[path]; commit(types.TOGGLE_LOADING, { entry: file }); return service - .getFileData(file.url) + .getFileData(`${gon.relative_url_root ? gon.relative_url_root : ''}${file.url}`) .then(res => { const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']); setPageTitle(pageTitle); diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js index 119debaf5f3..349ff68f1e3 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/actions.js +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -110,6 +110,17 @@ export const updateFilesAfterCommit = ( { root: true }, ); + commit( + rootTypes.TOGGLE_FILE_CHANGED, + { + file, + changed: false, + }, + { root: true }, + ); + + dispatch('updateTempFlagForEntry', { file, tempFile: false }, { root: true }); + eventHub.$emit(`editor.update.model.content.${file.key}`, { content: file.content, changed: !!changedFile, diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index 0f331f9a578..aa96e7f519f 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -61,4 +61,5 @@ export const ADD_PENDING_TAB = 'ADD_PENDING_TAB'; export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB'; export const UPDATE_ACTIVITY_BAR_VIEW = 'UPDATE_ACTIVITY_BAR_VIEW'; +export const UPDATE_TEMP_FLAG = 'UPDATE_TEMP_FLAG'; export const TOGGLE_FILE_FINDER = 'TOGGLE_FILE_FINDER'; diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index 6590d7c146a..27c84ec695f 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -4,6 +4,7 @@ import mergeRequestMutation from './mutations/merge_request'; import fileMutations from './mutations/file'; import treeMutations from './mutations/tree'; import branchMutations from './mutations/branch'; +import { sortTree } from './utils'; export default { [types.SET_INITIAL_DATA](state, data) { @@ -73,7 +74,7 @@ export default { f => foundEntry.tree.find(e => e.path === f.path) === undefined, ); Object.assign(foundEntry, { - tree: foundEntry.tree.concat(tree), + tree: sortTree(foundEntry.tree.concat(tree)), }); } @@ -86,10 +87,16 @@ export default { if (!foundEntry) { Object.assign(state.trees[`${projectId}/${branchId}`], { - tree: state.trees[`${projectId}/${branchId}`].tree.concat(data.treeList), + tree: sortTree(state.trees[`${projectId}/${branchId}`].tree.concat(data.treeList)), }); } }, + [types.UPDATE_TEMP_FLAG](state, { path, tempFile }) { + Object.assign(state.entries[path], { + tempFile, + changed: tempFile, + }); + }, [types.UPDATE_VIEWER](state, viewer) { Object.assign(state, { viewer, diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index 9a0412cb282..908df02f964 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -1,4 +1,4 @@ -import { ActivityBarViews } from '../constants'; +import { activityBarViews } from '../constants'; export default () => ({ currentProjectId: '', @@ -20,6 +20,6 @@ export default () => ({ entries: {}, viewer: 'editor', delayViewerUpdated: false, - currentActivityView: ActivityBarViews.edit, + currentActivityView: activityBarViews.edit, fileFindVisible: false, }); diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 86612d845e0..59185f8f0ad 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -33,6 +33,7 @@ export const dataStructure = () => ({ raw: '', content: '', parentTreeUrl: '', + parentPath: '', renderError: false, base64: false, editorRow: 1, @@ -65,6 +66,7 @@ export const decorateData = entity => { previewMode, file_lock, html, + parentPath = '', } = entity; return { @@ -81,6 +83,7 @@ export const decorateData = entity => { opened, active, parentTreeUrl, + parentPath, changed, renderError, content, @@ -121,8 +124,8 @@ const sortTreesByTypeAndName = (a, b) => { } else if (a.type === 'blob' && b.type === 'tree') { return 1; } - if (a.name.toLowerCase() < b.name.toLowerCase()) return -1; - if (a.name.toLowerCase() > b.name.toLowerCase()) return 1; + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; return 0; }; diff --git a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js index a1673276900..d249b05f47c 100644 --- a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js +++ b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js @@ -6,6 +6,7 @@ self.addEventListener('message', e => { const treeList = []; let file; + let parentPath; const entries = data.reduce((acc, path) => { const pathSplit = path.split('/'); const blobName = pathSplit.pop().trim(); @@ -17,6 +18,8 @@ self.addEventListener('message', e => { const foundEntry = acc[folderPath]; if (!foundEntry) { + parentPath = parentFolder ? parentFolder.path : null; + const tree = decorateData({ projectId, branchId, @@ -29,6 +32,7 @@ self.addEventListener('message', e => { tempFile, changed: tempFile, opened: tempFile, + parentPath, }); Object.assign(acc, { @@ -52,6 +56,8 @@ self.addEventListener('message', e => { if (blobName !== '') { const fileFolder = acc[pathSplit.join('/')]; + parentPath = fileFolder ? fileFolder.path : null; + file = decorateData({ projectId, branchId, @@ -66,6 +72,7 @@ self.addEventListener('message', e => { content, base64, previewMode: viewerInformationForPath(blobName), + parentPath, }); Object.assign(acc, { @@ -86,5 +93,6 @@ self.addEventListener('message', e => { entries, treeList: sortTree(treeList), file, + parentPath, }); }); diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 7e9a50a885d..f8b3d3061f0 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -12,7 +12,8 @@ import ModalStore from './boards/stores/modal_store'; export default class MilestoneSelect { constructor(currentProject, els, options = {}) { if (currentProject !== null) { - this.currentProject = typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject; + this.currentProject = + typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject; } this.init(els, options); @@ -26,7 +27,10 @@ export default class MilestoneSelect { } $els.each((i, dropdown) => { - let milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault; + let milestoneLinkNoneTemplate, + milestoneLinkTemplate, + selectedMilestone, + selectedMilestoneDefault; const $dropdown = $(dropdown); const projectId = $dropdown.data('projectId'); const milestonesUrl = $dropdown.data('milestones'); @@ -46,45 +50,47 @@ export default class MilestoneSelect { const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); const $value = $block.find('.value'); const $loading = $block.find('.block-loading').fadeOut(); - selectedMilestoneDefault = (showAny ? '' : null); - selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault); + selectedMilestoneDefault = showAny ? '' : null; + selectedMilestoneDefault = showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault; selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault; if (issueUpdateURL) { - milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); + milestoneLinkTemplate = _.template( + '<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>', + ); milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; } return $dropdown.glDropdown({ showMenuAbove: showMenuAbove, - data: (term, callback) => axios.get(milestonesUrl) - .then(({ data }) => { + data: (term, callback) => + axios.get(milestonesUrl).then(({ data }) => { const extraOptions = []; if (showAny) { extraOptions.push({ - id: 0, - name: '', - title: 'Any Milestone' + id: null, + name: null, + title: 'Any Milestone', }); } if (showNo) { extraOptions.push({ id: -1, name: 'No Milestone', - title: 'No Milestone' + title: 'No Milestone', }); } if (showUpcoming) { extraOptions.push({ id: -2, name: '#upcoming', - title: 'Upcoming' + title: 'Upcoming', }); } if (showStarted) { extraOptions.push({ id: -3, name: '#started', - title: 'Started' + title: 'Started', }); } if (extraOptions.length) { @@ -106,7 +112,7 @@ export default class MilestoneSelect { `, filterable: true, search: { - fields: ['title'] + fields: ['title'], }, selectable: true, toggleLabel: (selected, el, e) => { @@ -119,7 +125,7 @@ export default class MilestoneSelect { defaultLabel: defaultLabel, fieldName: $dropdown.data('fieldName'), text: milestone => _.escape(milestone.title), - id: (milestone) => { + id: milestone => { if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { return milestone.name; } else { @@ -131,7 +137,7 @@ export default class MilestoneSelect { // display:block overrides the hide-collapse rule return $value.css('display', ''); }, - opened: (e) => { + opened: e => { const $el = $(e.currentTarget); if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) { selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; @@ -140,7 +146,7 @@ export default class MilestoneSelect { $(`[data-milestone-id="${_.escape(selectedMilestone)}"] > a`, $el).addClass('is-active'); }, vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: (clickEvent) => { + clicked: clickEvent => { const { $el, e } = clickEvent; let selected = clickEvent.selectedObj; @@ -155,11 +161,14 @@ export default class MilestoneSelect { const page = $('body').attr('data-page'); const isIssueIndex = page === 'projects:issues:index'; - const isMRIndex = (page === page && page === 'projects:merge_requests:index'); - const isSelecting = (selected.name !== selectedMilestone); + const isMRIndex = page === page && page === 'projects:merge_requests:index'; + const isSelecting = selected.name !== selectedMilestone; selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault; - if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { + if ( + $dropdown.hasClass('js-filter-bulk-update') || + $dropdown.hasClass('js-issuable-form-dropdown') + ) { e.preventDefault(); return; } @@ -177,10 +186,13 @@ export default class MilestoneSelect { return $dropdown.closest('form').submit(); } else if ($dropdown.hasClass('js-issue-board-sidebar')) { if (selected.id !== -1 && isSelecting) { - gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ - id: selected.id, - title: selected.name - })); + gl.issueBoards.boardStoreIssueSet( + 'milestone', + new ListMilestone({ + id: selected.id, + title: selected.name, + }), + ); } else { gl.issueBoards.boardStoreIssueDelete('milestone'); } @@ -188,7 +200,8 @@ export default class MilestoneSelect { $dropdown.trigger('loading.gl.dropdown'); $loading.removeClass('hidden').fadeIn(); - gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) + gl.issueBoards.BoardsStore.detail.issue + .update($dropdown.attr('data-issue-update')) .then(() => { $dropdown.trigger('loaded.gl.dropdown'); $loading.fadeOut(); @@ -203,7 +216,8 @@ export default class MilestoneSelect { data[abilityName].milestone_id = selected != null ? selected : null; $loading.removeClass('hidden').fadeIn(); $dropdown.trigger('loading.gl.dropdown'); - return axios.put(issueUpdateURL, data) + return axios + .put(issueUpdateURL, data) .then(({ data }) => { $dropdown.trigger('loaded.gl.dropdown'); $loading.fadeOut(); @@ -215,7 +229,10 @@ export default class MilestoneSelect { data.milestone.name = data.milestone.title; $value.html(milestoneLinkTemplate(data.milestone)); return $sidebarCollapsedValue - .attr('data-original-title', `${data.milestone.name}<br />${data.milestone.remaining}`) + .attr( + 'data-original-title', + `${data.milestone.name}<br />${data.milestone.remaining}`, + ) .find('span') .text(data.milestone.title); } else { @@ -230,7 +247,7 @@ export default class MilestoneSelect { $loading.fadeOut(); }); } - } + }, }); }); } diff --git a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue index 0e3ac636661..9ce176744ba 100644 --- a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue +++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue @@ -52,16 +52,15 @@ text() { const keepContributionsText = s__(`AdminArea| You are about to permanently delete the user %{username}. - This will delete all of the issues, merge requests, and groups linked to them. + Issues, merge requests, and groups linked to them will be transferred to a system-wide "Ghost-user". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered.`); const deleteContributionsText = s__(`AdminArea| You are about to permanently delete the user %{username}. - Issues, merge requests, and groups linked to them will be transferred to a system-wide "Ghost-user". + This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered.`); - return sprintf(this.deleteContributions ? deleteContributionsText : keepContributionsText, { username: `<strong>${_.escape(this.username)}</strong>`, diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js index cbc2d80ee18..50d042fef29 100644 --- a/app/assets/javascripts/pages/users/activity_calendar.js +++ b/app/assets/javascripts/pages/users/activity_calendar.js @@ -188,11 +188,11 @@ export default class ActivityCalendar { }, { text: 'W', - y: 29 + this.dayYPos(2), + y: 29 + this.dayYPos(3), }, { text: 'F', - y: 29 + this.dayYPos(3), + y: 29 + this.dayYPos(5), }, ]; this.svg diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue index 2fd1715ee79..8ffaa52d9e8 100644 --- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue +++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue @@ -5,7 +5,6 @@ import PerformanceBarService from '../services/performance_bar_service'; import detailedMetric from './detailed_metric.vue'; import requestSelector from './request_selector.vue'; import simpleMetric from './simple_metric.vue'; -import upstreamPerformanceBar from './upstream_performance_bar.vue'; import Flash from '../../flash'; @@ -14,7 +13,6 @@ export default { detailedMetric, requestSelector, simpleMetric, - upstreamPerformanceBar, }, props: { store: { @@ -128,9 +126,6 @@ export default { {{ currentRequest.details.host.hostname }} </span> </div> - <upstream-performance-bar - v-if="initialRequest && currentRequest.details" - /> <detailed-metric v-for="metric in $options.detailedMetrics" :key="metric.metric" diff --git a/app/assets/javascripts/performance_bar/components/upstream_performance_bar.vue b/app/assets/javascripts/performance_bar/components/upstream_performance_bar.vue deleted file mode 100644 index 2b5915f381f..00000000000 --- a/app/assets/javascripts/performance_bar/components/upstream_performance_bar.vue +++ /dev/null @@ -1,20 +0,0 @@ -<script> -export default { - mounted() { - const upstreamPerformanceBar = document - .getElementById('peek-view-performance-bar') - .cloneNode(true); - - upstreamPerformanceBar.classList.remove('hidden'); - - this.$refs.wrapper.appendChild(upstreamPerformanceBar); - }, -}; -</script> -<template> - <div - id="peek-view-performance-bar-vue" - class="view" - ref="wrapper" - ></div> -</template> diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js index a0ddf36a672..4a98aed7679 100644 --- a/app/assets/javascripts/performance_bar/index.js +++ b/app/assets/javascripts/performance_bar/index.js @@ -1,5 +1,3 @@ -import 'vendor/peek.performance_bar'; - import Vue from 'vue'; import performanceBarApp from './components/performance_bar_app.vue'; import PerformanceBarStore from './stores/performance_bar_store'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue index 602b68ea572..7d366c495f0 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue @@ -1,66 +1,70 @@ <script> - import { n__ } from '~/locale'; - import statusIcon from '../mr_widget_status_icon.vue'; - import eventHub from '../../event_hub'; +import { n__ } from '~/locale'; +import statusIcon from '../mr_widget_status_icon.vue'; +import eventHub from '../../event_hub'; - export default { - name: 'MRWidgetFailedToMerge', +export default { + name: 'MRWidgetFailedToMerge', - components: { - statusIcon, - }, + components: { + statusIcon, + }, - props: { - mr: { - type: Object, - required: true, - default: () => ({}), - }, + props: { + mr: { + type: Object, + required: true, + default: () => ({}), }, + }, - data() { - return { - timer: 10, - isRefreshing: false, - }; - }, + data() { + return { + timer: 10, + isRefreshing: false, + intervalId: null, + }; + }, - computed: { - timerText() { - return n__( - 'Refreshing in a second to show the updated status...', - 'Refreshing in %d seconds to show the updated status...', - this.timer, - ); - }, + computed: { + timerText() { + return n__( + 'Refreshing in a second to show the updated status...', + 'Refreshing in %d seconds to show the updated status...', + this.timer, + ); }, + }, - mounted() { - setInterval(() => { - this.updateTimer(); - }, 1000); - }, + mounted() { + this.intervalId = setInterval(this.updateTimer, 1000); + }, - created() { - eventHub.$emit('DisablePolling'); - }, + created() { + eventHub.$emit('DisablePolling'); + }, - methods: { - refresh() { - this.isRefreshing = true; - eventHub.$emit('MRWidgetUpdateRequested'); - eventHub.$emit('EnablePolling'); - }, - updateTimer() { - this.timer = this.timer - 1; + beforeDestroy() { + if (this.intervalId) { + clearInterval(this.intervalId); + } + }, - if (this.timer === 0) { - this.refresh(); - } - }, + methods: { + refresh() { + this.isRefreshing = true; + eventHub.$emit('MRWidgetUpdateRequested'); + eventHub.$emit('EnablePolling'); }, + updateTimer() { + this.timer = this.timer - 1; - }; + if (this.timer === 0) { + this.refresh(); + } + }, + }, +}; </script> <template> <div class="mr-widget-body media"> diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue index ee1c3498748..be2755452e2 100644 --- a/app/assets/javascripts/vue_shared/components/file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/file_icon.vue @@ -1,9 +1,9 @@ <script> - import getIconForFile from './file_icon/file_icon_map'; - import loadingIcon from '../../vue_shared/components/loading_icon.vue'; - import icon from '../../vue_shared/components/icon.vue'; +import getIconForFile from './file_icon/file_icon_map'; +import loadingIcon from '../../vue_shared/components/loading_icon.vue'; +import icon from '../../vue_shared/components/icon.vue'; - /* This is a re-usable vue component for rendering a svg sprite +/* This is a re-usable vue component for rendering a svg sprite icon Sample configuration: @@ -15,60 +15,60 @@ /> */ - export default { - components: { - loadingIcon, - icon, +export default { + components: { + loadingIcon, + icon, + }, + props: { + fileName: { + type: String, + required: true, }, - props: { - fileName: { - type: String, - required: true, - }, - folder: { - type: Boolean, - required: false, - default: false, - }, + folder: { + type: Boolean, + required: false, + default: false, + }, - opened: { - type: Boolean, - required: false, - default: false, - }, + opened: { + type: Boolean, + required: false, + default: false, + }, - loading: { - type: Boolean, - required: false, - default: false, - }, + loading: { + type: Boolean, + required: false, + default: false, + }, - size: { - type: Number, - required: false, - default: 16, - }, + size: { + type: Number, + required: false, + default: 16, + }, - cssClasses: { - type: String, - required: false, - default: '', - }, + cssClasses: { + type: String, + required: false, + default: '', + }, + }, + computed: { + spriteHref() { + const iconName = getIconForFile(this.fileName) || 'file'; + return `${gon.sprite_file_icons}#${iconName}`; + }, + folderIconName() { + return this.opened ? 'folder-open' : 'folder'; }, - computed: { - spriteHref() { - const iconName = getIconForFile(this.fileName) || 'file'; - return `${gon.sprite_file_icons}#${iconName}`; - }, - folderIconName() { - return this.opened ? 'folder-open' : 'folder'; - }, - iconSizeClass() { - return this.size ? `s${this.size}` : ''; - }, + iconSizeClass() { + return this.size ? `s${this.size}` : ''; }, - }; + }, +}; </script> <template> <span> @@ -82,6 +82,7 @@ v-if="!loading && folder" :name="folderIconName" :size="size" + css-classes="folder-icon" /> <loading-icon v-if="loading" diff --git a/app/assets/javascripts/vue_shared/models/label.js b/app/assets/javascripts/vue_shared/models/label.js index 70b9efe0c68..d29c7fe973a 100644 --- a/app/assets/javascripts/vue_shared/models/label.js +++ b/app/assets/javascripts/vue_shared/models/label.js @@ -1,4 +1,4 @@ -class ListLabel { +export default class ListLabel { constructor(obj) { this.id = obj.id; this.title = obj.title; diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss index 17c31d6b184..66dbe403385 100644 --- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss +++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss @@ -241,8 +241,6 @@ } .scrolling-tabs-container { - position: relative; - .merge-request-tabs-container & { overflow: hidden; } @@ -272,8 +270,6 @@ } .inner-page-scroll-tabs { - position: relative; - .fade-right { @include fade(left, $white-light); right: 0; diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 2f3a80daa90..3fa7a260017 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -19,6 +19,7 @@ .fork-svg { margin-right: 4px; + vertical-align: bottom; } } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index e9384d41e00..1aca3c5cf1a 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -70,7 +70,7 @@ } .branch-info .commit-icon { - margin-right: 3px; + margin-right: 8px; svg { top: 3px; diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index b0852adb459..d81236c5883 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -314,6 +314,10 @@ display: inline-flex; vertical-align: top; + &:hover .color-label { + text-decoration: underline; + } + .label { vertical-align: inherit; font-size: $label-font-size; diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 4657b37b9a3..79f32033af4 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -68,6 +68,11 @@ } } + .ide-file-icon-holder { + display: flex; + align-items: center; + } + .ide-file-changed-icon { margin-left: auto; @@ -78,7 +83,7 @@ .ide-new-btn { display: none; - margin-bottom: -4px; + margin-right: -8px; } &:hover, @@ -90,10 +95,8 @@ } } - &.folder { - svg { - fill: $gl-text-color-secondary; - } + .folder-icon { + fill: $gl-text-color-secondary; } } @@ -111,7 +114,8 @@ .file-col-commit-message { display: flex; overflow: visible; - padding: 6px 10px; + align-items: center; + padding: 6px 12px; } .multi-file-loading-container { @@ -430,6 +434,12 @@ } } + .projects-sidebar { + display: flex; + flex-direction: column; + flex: 1; + } + .multi-file-commit-panel-inner { display: flex; flex: 1; @@ -946,3 +956,7 @@ .ide-review-sub-header { color: $gl-text-color-secondary; } + +.ide-new-modal-label { + line-height: 34px; +} diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss index 45ae94abaff..06ef58531d7 100644 --- a/app/assets/stylesheets/performance_bar.scss +++ b/app/assets/stylesheets/performance_bar.scss @@ -1,5 +1,4 @@ @import 'framework/variables'; -@import 'peek/views/performance_bar'; @import 'peek/views/rblineprof'; #js-peek { diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index fef29789832..e7a36e20050 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -17,7 +17,7 @@ module BlobHelper end def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) - "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" + "#{ide_path}/project#{url_for([project, "edit", "blob", id: [ref, path], script_name: "/"])}" end def edit_blob_button(project = @project, ref = @ref, path = @path, options = {}) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 98894b86551..e594a1d0ba3 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -63,7 +63,7 @@ module CommitsHelper # Returns a link formatted as a commit branch link def commit_branch_link(url, text) link_to(url, class: 'label label-gray ref-name branch-link') do - sprite_icon('fork', size: 16, css_class: 'fork-svg') + "#{text}" + sprite_icon('fork', size: 12, css_class: 'fork-svg') + "#{text}" end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index a64b2acdd77..801e624e1de 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -400,7 +400,8 @@ module ProjectsHelper exports_path = File.join(Settings.shared['path'], 'tmp/project_exports') filtered_message = message.strip.gsub(exports_path, "[REPO EXPORT PATH]") - filtered_message.gsub(project.repository_storage_path.chomp('/'), "[REPOS PATH]") + disk_path = Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path + filtered_message.gsub(disk_path.chomp('/'), "[REPOS PATH]") end def project_child_container_class(view_path) diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb index f05e606995d..f66bdd529f1 100644 --- a/app/models/concerns/storage/legacy_namespace.rb +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -45,25 +45,25 @@ module Storage # Hooks - # Save the storage paths before the projects are destroyed to use them on after destroy + # Save the storages before the projects are destroyed to use them on after destroy def prepare_for_destroy - old_repository_storage_paths + old_repository_storages end private def move_repositories - # Move the namespace directory in all storage paths used by member projects - repository_storage_paths.each do |repository_storage_path| + # Move the namespace directory in all storages used by member projects + repository_storages.each do |repository_storage| # Ensure old directory exists before moving it - gitlab_shell.add_namespace(repository_storage_path, full_path_was) + gitlab_shell.add_namespace(repository_storage, full_path_was) # Ensure new directory exists before moving it (if there's a parent) - gitlab_shell.add_namespace(repository_storage_path, parent.full_path) if parent + gitlab_shell.add_namespace(repository_storage, parent.full_path) if parent - unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path) + unless gitlab_shell.mv_namespace(repository_storage, full_path_was, full_path) - Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}" + Rails.logger.error "Exception moving path #{repository_storage} from #{full_path_was} to #{full_path}" # if we cannot move namespace directory we should rollback # db changes in order to prevent out of sync between db and fs @@ -72,33 +72,33 @@ module Storage end end - def old_repository_storage_paths - @old_repository_storage_paths ||= repository_storage_paths + def old_repository_storages + @old_repository_storage_paths ||= repository_storages end - def repository_storage_paths + def repository_storages # We need to get the storage paths for all the projects, even the ones that are # pending delete. Unscoping also get rids of the default order, which causes # problems with SELECT DISTINCT. Project.unscoped do - all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path) + all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage) end end def rm_dir # Remove the namespace directory in all storages paths used by member projects - old_repository_storage_paths.each do |repository_storage_path| + old_repository_storages.each do |repository_storage| # Move namespace directory into trash. # We will remove it later async new_path = "#{full_path}+#{id}+deleted" - if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path) + if gitlab_shell.mv_namespace(repository_storage, full_path, new_path) Gitlab::AppLogger.info %Q(Namespace directory "#{full_path}" moved to "#{new_path}") # Remove namespace directroy async with delay so # GitLab has time to remove all projects first run_after_commit do - GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path) + GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage, new_path) end end end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index c1c27ccf3e5..06aa67c600f 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -197,10 +197,6 @@ class MergeRequestDiff < ActiveRecord::Base CompareService.new(project, head_commit_sha).execute(project, sha, straight: true) end - def commits_count - super || merge_request_diff_commits.size - end - private def create_merge_request_diff_files(diffs) diff --git a/app/models/project.rb b/app/models/project.rb index 172980b0ad4..d4e9e51c7be 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -518,10 +518,6 @@ class Project < ActiveRecord::Base repository.empty? end - def repository_storage_path - Gitlab.config.repositories.storages[repository_storage]&.legacy_disk_path - end - def team @team ||= ProjectTeam.new(self) end @@ -1105,7 +1101,7 @@ class Project < ActiveRecord::Base # Check if repository already exists on disk def check_repository_path_availability return true if skip_disk_validation - return false unless repository_storage_path + return false unless repository_storage expires_full_path_cache # we need to clear cache to validate renames correctly @@ -1910,14 +1906,14 @@ class Project < ActiveRecord::Base def check_repository_absence! return if skip_disk_validation - if repository_storage_path.blank? || repository_with_same_path_already_exists? + if repository_storage.blank? || repository_with_same_path_already_exists? errors.add(:base, 'There is already a repository with that name on disk') throw :abort end end def repository_with_same_path_already_exists? - gitlab_shell.exists?(repository_storage_path, "#{disk_path}.git") + gitlab_shell.exists?(repository_storage, "#{disk_path}.git") end # set last_activity_at to the same as created_at diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index b7e38ceb502..f799a0b4227 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -21,7 +21,7 @@ class ProjectWiki end delegate :empty?, to: :pages - delegate :repository_storage_path, :hashed_storage?, to: :project + delegate :repository_storage, :hashed_storage?, to: :project def path @project.path + '.wiki' diff --git a/app/models/repository.rb b/app/models/repository.rb index 5bdaa7f0720..6831305fb93 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -84,9 +84,14 @@ class Repository # Return absolute path to repository def path_to_repo - @path_to_repo ||= File.expand_path( - File.join(repository_storage_path, disk_path + '.git') - ) + @path_to_repo ||= + begin + storage = Gitlab.config.repositories.storages[@project.repository_storage] + + File.expand_path( + File.join(storage.legacy_disk_path, disk_path + '.git') + ) + end end def inspect @@ -915,10 +920,6 @@ class Repository raw_repository.fetch_ref(source_repository.raw_repository, source_ref: source_ref, target_ref: target_ref) end - def repository_storage_path - @project.repository_storage_path - end - def rebase(user, merge_request) raw.rebase(user, merge_request.id, branch: merge_request.source_branch, branch_sha: merge_request.source_branch_sha, diff --git a/app/models/storage/hashed_project.rb b/app/models/storage/hashed_project.rb index fae1b64961a..26b4b78ac64 100644 --- a/app/models/storage/hashed_project.rb +++ b/app/models/storage/hashed_project.rb @@ -1,7 +1,7 @@ module Storage class HashedProject attr_accessor :project - delegate :gitlab_shell, :repository_storage_path, to: :project + delegate :gitlab_shell, :repository_storage, to: :project ROOT_PATH_PREFIX = '@hashed'.freeze @@ -24,7 +24,7 @@ module Storage end def ensure_storage_path_exists - gitlab_shell.add_namespace(repository_storage_path, base_dir) + gitlab_shell.add_namespace(repository_storage, base_dir) end def rename_repo diff --git a/app/models/storage/legacy_project.rb b/app/models/storage/legacy_project.rb index 9d9e5e1d352..27cb388c702 100644 --- a/app/models/storage/legacy_project.rb +++ b/app/models/storage/legacy_project.rb @@ -1,7 +1,7 @@ module Storage class LegacyProject attr_accessor :project - delegate :namespace, :gitlab_shell, :repository_storage_path, to: :project + delegate :namespace, :gitlab_shell, :repository_storage, to: :project def initialize(project) @project = project @@ -24,18 +24,18 @@ module Storage def ensure_storage_path_exists return unless namespace - gitlab_shell.add_namespace(repository_storage_path, base_dir) + gitlab_shell.add_namespace(repository_storage, base_dir) end def rename_repo new_full_path = project.build_full_path - if gitlab_shell.mv_repository(repository_storage_path, project.full_path_was, new_full_path) + if gitlab_shell.mv_repository(repository_storage, project.full_path_was, new_full_path) # If repository moved successfully we need to send update instructions to users. # However we cannot allow rollback since we moved repository # So we basically we mute exceptions in next actions begin - gitlab_shell.mv_repository(repository_storage_path, "#{project.full_path_was}.wiki", "#{new_full_path}.wiki") + gitlab_shell.mv_repository(repository_storage, "#{project.full_path_was}.wiki", "#{new_full_path}.wiki") return true rescue => e Rails.logger.error "Exception renaming #{project.full_path_was} -> #{new_full_path}: #{e}" diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 44e869851ca..71c93660b4b 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -91,7 +91,7 @@ module Projects project.run_after_commit do # self is now project - GitlabShellWorker.perform_in(5.minutes, :remove_repository, self.repository_storage_path, new_path) + GitlabShellWorker.perform_in(5.minutes, :remove_repository, self.repository_storage, new_path) end else false @@ -100,9 +100,9 @@ module Projects def mv_repository(from_path, to_path) # There is a possibility project does not have repository or wiki - return true unless gitlab_shell.exists?(project.repository_storage_path, from_path + '.git') + return true unless gitlab_shell.exists?(project.repository_storage, from_path + '.git') - gitlab_shell.mv_repository(project.repository_storage_path, from_path, to_path) + gitlab_shell.mv_repository(project.repository_storage, from_path, to_path) end def attempt_rollback(project, message) diff --git a/app/services/projects/hashed_storage/migrate_repository_service.rb b/app/services/projects/hashed_storage/migrate_repository_service.rb index 67178de75de..68c1af2396b 100644 --- a/app/services/projects/hashed_storage/migrate_repository_service.rb +++ b/app/services/projects/hashed_storage/migrate_repository_service.rb @@ -47,8 +47,8 @@ module Projects private def move_repository(from_name, to_name) - from_exists = gitlab_shell.exists?(project.repository_storage_path, "#{from_name}.git") - to_exists = gitlab_shell.exists?(project.repository_storage_path, "#{to_name}.git") + from_exists = gitlab_shell.exists?(project.repository_storage, "#{from_name}.git") + to_exists = gitlab_shell.exists?(project.repository_storage, "#{to_name}.git") # If we don't find the repository on either original or target we should log that as it could be an issue if the # project was not originally empty. @@ -60,7 +60,7 @@ module Projects return true end - gitlab_shell.mv_repository(project.repository_storage_path, from_name, to_name) + gitlab_shell.mv_repository(project.repository_storage, from_name, to_name) end def rollback_folder_move diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 5a23f0f0a62..61acdd58021 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -127,7 +127,7 @@ module Projects end def move_repo_folder(from_name, to_name) - gitlab_shell.mv_repository(project.repository_storage_path, from_name, to_name) + gitlab_shell.mv_repository(project.repository_storage, from_name, to_name) end def execute_system_hooks diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml index 2ff4221efbd..badf3dd74b3 100644 --- a/app/views/admin/users/_user.html.haml +++ b/app/views/admin/users/_user.html.haml @@ -43,7 +43,7 @@ delete_user_url: admin_user_path(user), block_user_url: block_admin_user_path(user), username: user.name, - delete_contributions: 'false' }, type: 'button' } + delete_contributions: false }, type: 'button' } = s_('AdminUsers|Delete user') %li @@ -52,5 +52,5 @@ delete_user_url: admin_user_path(user, hard_delete: true), block_user_url: block_admin_user_path(user), username: user.name, - delete_contributions: 'true' }, type: 'button' } + delete_contributions: true }, type: 'button' } = s_('AdminUsers|Delete user and contributions') diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index ec3be869797..814ccdae8f3 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -183,7 +183,7 @@ delete_user_url: admin_user_path(@user), block_user_url: block_admin_user_path(@user), username: @user.name, - delete_contributions: 'false' }, type: 'button' } + delete_contributions: false }, type: 'button' } = s_('AdminUsers|Delete user') - else - if @user.solo_owned_groups.present? @@ -215,7 +215,7 @@ delete_user_url: admin_user_path(@user, hard_delete: true), block_user_url: block_admin_user_path(@user), username: @user.name, - delete_contributions: 'true' }, type: 'button' } + delete_contributions: true }, type: 'button' } = s_('AdminUsers|Delete user and contributions') - else %p diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml index 440623b34f5..571eb28f195 100644 --- a/app/views/ci/variables/_variable_row.html.haml +++ b/app/views/ci/variables/_variable_row.html.haml @@ -17,14 +17,14 @@ .ci-variable-row-body %input.js-ci-variable-input-id{ type: "hidden", name: id_input_name, value: id } %input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name } - %input.js-ci-variable-input-key.ci-variable-body-item.form-control{ type: "text", + %input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control{ type: "text", name: key_input_name, value: key, placeholder: s_('CiVariables|Input variable key') } .ci-variable-body-item - .form-control.js-secret-value-placeholder{ class: ('hide' unless id) } + .form-control.js-secret-value-placeholder.qa-ci-variable-input-value{ class: ('hide' unless id) } = '*' * 20 - %textarea.js-ci-variable-input-value.js-secret-value.form-control{ class: ('hide' if id), + %textarea.js-ci-variable-input-value.js-secret-value.qa-ci-variable-input-value.form-control{ class: ('hide' if id), rows: 1, name: value_input_name, placeholder: s_('CiVariables|Input variable value') } diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 86cd0759a2c..3375e01b3a1 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,4 +1,6 @@ - breadcrumb_title "General Settings" +- @content_class = "limit-container-width" unless fluid_layout + .panel.panel-default.prepend-top-default .panel-heading Group settings diff --git a/app/views/peek/_bar.html.haml b/app/views/peek/_bar.html.haml index b4d86e1601c..a911449672b 100644 --- a/app/views/peek/_bar.html.haml +++ b/app/views/peek/_bar.html.haml @@ -5,8 +5,3 @@ peek_url: peek_routes.results_url, profile_url: url_for(params.merge(lineprofiler: 'true')) }, class: Peek.env } - -#peek-view-performance-bar.hidden - = render_server_response_time - %span#serverstats - %ul.performance-bar diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index 475c6ba4d3d..a603b1024eb 100644..100755 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -12,7 +12,7 @@ - if @namespaces.present? .fork-thumbnail-container.js-fork-content %h5.prepend-top-0.append-bottom-0.prepend-left-default.append-right-default - Click to fork the project + = _("Select a namespace to fork the project") - @namespaces.each do |namespace| = render 'fork_button', namespace: namespace - else diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index cb21f90696f..403d22c79f8 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -32,6 +32,13 @@ required: true, title: 'You can choose a descriptive name different from the path.' +- if @group.persisted? + .form-group.group-name-holder + = f.label :id, class: 'control-label' do + = _("Group ID") + .col-sm-10 + = f.text_field :id, class: 'form-control', readonly: true + .form-group.group-description-holder = f.label :description, class: 'control-label' .col-sm-10 diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index 51fad4faf36..08b1c3a7d7a 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -13,7 +13,9 @@ class RepositoryForkWorker # See https://gitlab.com/gitlab-org/gitaly/issues/1110 if args.empty? source_project = target_project.forked_from_project - return target_project.mark_import_as_failed('Source project cannot be found.') unless source_project + unless source_project + return target_project.mark_import_as_failed('Source project cannot be found.') + end fork_repository(target_project, source_project.repository_storage, source_project.disk_path) else |