diff options
author | Lin Jen-Shin <godfat@godfat.org> | 2017-07-17 17:38:37 +0300 |
---|---|---|
committer | Lin Jen-Shin <godfat@godfat.org> | 2017-07-17 17:38:37 +0300 |
commit | 65e722ee977a3fcd44fb272aa716dfa679385759 (patch) | |
tree | 3fee24a0e09670909df47163fc8d97fb2cc6380d /app | |
parent | 550ccf443059412a26adfcba15fbe9d05d39a5f9 (diff) | |
parent | 05329d4a364a5c55f2de9546871de1909b6be3f5 (diff) |
Merge remote-tracking branch 'upstream/master' into 30634-protected-pipeline
* upstream/master: (638 commits)
Simplify background migrations stealing code
Expire cached user IDs that can see the performance after 5 minutes
Promote visibility level helpers from Group to Namespace
Fix off-by-one error in background migration retries
Recover from all exceptions when stealing bg migration
Fix label creation from new list for subgroup projects
move click handler to button. when on the icon it wasn't triggered in firefox
Fix incorrect AWS ELB metrics.
Fix wrong link to docs in docs styleguide
Update issue-related docs
Refactor groups docs
Add subgroups limitations to Pages docs
Update Google launcher details
Split docs on IP whitelist for monitoring access
Update health check docs
Bump fog-core to 1.44.3 and fog providers' plugins to latest
Introduce have_gitlab_http_status
Remove Repository#search_files
Update Pipeline's badge count in Merge Request and Commits view to match real-time content
Fixes the user order being overriden in the autocomplete controller
...
Diffstat (limited to 'app')
648 files changed, 5912 insertions, 4331 deletions
diff --git a/app/assets/images/new_nav.png b/app/assets/images/new_nav.png Binary files differindex 8879d26d341..f98ca15d787 100644 --- a/app/assets/images/new_nav.png +++ b/app/assets/images/new_nav.png diff --git a/app/assets/javascripts/blob/notebook/index.js b/app/assets/javascripts/blob/notebook/index.js index 36fe8a7184f..27312d718b0 100644 --- a/app/assets/javascripts/blob/notebook/index.js +++ b/app/assets/javascripts/blob/notebook/index.js @@ -51,8 +51,9 @@ export default () => { methods: { loadFile() { this.$http.get(el.dataset.endpoint) + .then(response => response.json()) .then((res) => { - this.json = res.json(); + this.json = res; this.loading = false; }) .catch((e) => { diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js index b94009ee76b..88b054b76e6 100644 --- a/app/assets/javascripts/boards/boards_bundle.js +++ b/app/assets/javascripts/boards/boards_bundle.js @@ -81,8 +81,9 @@ $(() => { mounted () { Store.disabled = this.disabled; gl.boardService.all() + .then(response => response.json()) .then((resp) => { - resp.json().forEach((board) => { + resp.forEach((board) => { const list = Store.addList(board, this.defaultAvatar); if (list.type === 'closed') { @@ -97,7 +98,8 @@ $(() => { Store.addBlankState(); this.loading = false; - }).catch(() => new Flash('An error occurred. Please try again.')); + }) + .catch(() => new Flash('An error occurred. Please try again.')); }, methods: { updateTokens() { diff --git a/app/assets/javascripts/boards/components/board_blank_state.js b/app/assets/javascripts/boards/components/board_blank_state.js index 870e115bd1a..e7f16899362 100644 --- a/app/assets/javascripts/boards/components/board_blank_state.js +++ b/app/assets/javascripts/boards/components/board_blank_state.js @@ -64,8 +64,9 @@ export default { // Save the labels gl.boardService.generateDefaultLists() - .then((resp) => { - resp.json().forEach((listObj) => { + .then(resp => resp.json()) + .then((data) => { + data.forEach((listObj) => { const list = Store.findList('title', listObj.title); list.id = listObj.id; diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js index 6356c266ee2..1d36519c75c 100644 --- a/app/assets/javascripts/boards/components/modal/index.js +++ b/app/assets/javascripts/boards/components/modal/index.js @@ -88,9 +88,9 @@ gl.issueBoards.IssuesModal = Vue.extend({ return gl.boardService.getBacklog(queryData(this.filter.path, { page: this.page, per: this.perPage, - })).then((res) => { - const data = res.json(); - + })) + .then(resp => resp.json()) + .then((data) => { if (clearIssues) { this.issues = []; } diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index b4b09b3876e..08f7c5ddcd2 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -40,9 +40,8 @@ class List { save () { return gl.boardService.createList(this.label.id) - .then((resp) => { - const data = resp.json(); - + .then(resp => resp.json()) + .then((data) => { this.id = data.id; this.type = data.list_type; this.position = data.position; @@ -91,8 +90,8 @@ class List { } return gl.boardService.getIssuesForList(this.id, data) - .then((resp) => { - const data = resp.json(); + .then(resp => resp.json()) + .then((data) => { this.loading = false; this.issuesSize = data.size; @@ -109,8 +108,8 @@ class List { this.issuesSize += 1; return gl.boardService.newIssue(this.id, issue) - .then((resp) => { - const data = resp.json(); + .then(resp => resp.json()) + .then((data) => { issue.id = data.iid; if (this.issuesSize > 1) { diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js index db9bced2f89..3742507b236 100644 --- a/app/assets/javascripts/boards/services/board_service.js +++ b/app/assets/javascripts/boards/services/board_service.js @@ -23,11 +23,6 @@ class BoardService { url: bulkUpdatePath, }, }); - - Vue.http.interceptors.push((request, next) => { - request.headers['X-CSRF-Token'] = $.rails.csrfToken(); - next(); - }); } all () { diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 60103155ce0..1dfa064acfd 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -13,25 +13,21 @@ window.Build = (function () { this.options = options || $('.js-build-options').data(); this.pageUrl = this.options.pageUrl; - this.buildUrl = this.options.buildUrl; this.buildStatus = this.options.buildStatus; this.state = this.options.logState; this.buildStage = this.options.buildStage; this.$document = $(document); this.logBytes = 0; - this.scrollOffsetPadding = 30; this.hasBeenScrolled = false; this.updateDropdown = this.updateDropdown.bind(this); this.getBuildTrace = this.getBuildTrace.bind(this); - this.scrollToBottom = this.scrollToBottom.bind(this); - this.$body = $('body'); this.$buildTrace = $('#build-trace'); this.$buildRefreshAnimation = $('.js-build-refresh'); this.$truncatedInfo = $('.js-truncated-info'); this.$buildTraceOutput = $('.js-build-output'); - this.$scrollContainer = $('.js-scroll-container'); + this.$topBar = $('.js-top-bar'); // Scroll controllers this.$scrollTopBtn = $('.js-scroll-up'); @@ -63,13 +59,22 @@ window.Build = (function () { .off('click') .on('click', this.scrollToBottom.bind(this)); - const scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100); + this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100); - this.$scrollContainer + $(window) .off('scroll') .on('scroll', () => { - this.hasBeenScrolled = true; - scrollThrottled(); + const contentHeight = this.$buildTraceOutput.prop('scrollHeight'); + if (contentHeight > this.windowSize) { + // means the user did not scroll, the content was updated. + this.windowSize = contentHeight; + } else { + // User scrolled + this.hasBeenScrolled = true; + this.toggleScrollAnimation(false); + } + + this.scrollThrottled(); }); $(window) @@ -77,59 +82,73 @@ window.Build = (function () { .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100)); this.updateArtifactRemoveDate(); + this.initAffixTopArea(); - // eslint-disable-next-line - this.getBuildTrace() - .then(() => this.toggleScroll()) - .then(() => { - if (!this.hasBeenScrolled) { - this.scrollToBottom(); - } - }) - .then(() => this.verifyTopPosition()); + this.getBuildTrace(); } + Build.prototype.initAffixTopArea = function () { + /** + If the browser does not support position sticky, it returns the position as static. + If the browser does support sticky, then we allow the browser to handle it, if not + then we default back to Bootstraps affix + **/ + if (this.$topBar.css('position') !== 'static') return; + + const offsetTop = this.$buildTrace.offset().top; + + this.$topBar.affix({ + offset: { + top: offsetTop, + }, + }); + }; + Build.prototype.canScroll = function () { - return (this.$scrollContainer.prop('scrollHeight') - this.scrollOffsetPadding) > this.$scrollContainer.height(); + return document.body.scrollHeight > window.innerHeight; }; - /** - * | | Up | Down | - * |--------------------------|----------|----------| - * | on scroll bottom | active | disabled | - * | on scroll top | disabled | active | - * | no scroll | disabled | disabled | - * | on.('scroll') is on top | disabled | active | - * | on('scroll) is on bottom | active | disabled | - * - */ Build.prototype.toggleScroll = function () { - const currentPosition = this.$scrollContainer.scrollTop(); - const bottomScroll = currentPosition + this.$scrollContainer.innerHeight(); + const currentPosition = document.body.scrollTop; + const windowHeight = window.innerHeight; if (this.canScroll()) { - if (currentPosition === 0) { + if (currentPosition > 0 && + (document.body.scrollHeight - currentPosition !== windowHeight)) { + // User is in the middle of the log + + this.toggleDisableButton(this.$scrollTopBtn, false); + this.toggleDisableButton(this.$scrollBottomBtn, false); + } else if (currentPosition === 0) { + // User is at Top of Build Log + this.toggleDisableButton(this.$scrollTopBtn, true); this.toggleDisableButton(this.$scrollBottomBtn, false); - } else if (bottomScroll === this.$scrollContainer.prop('scrollHeight')) { + } else if (document.body.scrollHeight - currentPosition === windowHeight) { + // User is at the bottom of the build log. + this.toggleDisableButton(this.$scrollTopBtn, false); this.toggleDisableButton(this.$scrollBottomBtn, true); - } else { - this.toggleDisableButton(this.$scrollTopBtn, false); - this.toggleDisableButton(this.$scrollBottomBtn, false); } + } else { + this.toggleDisableButton(this.$scrollTopBtn, true); + this.toggleDisableButton(this.$scrollBottomBtn, true); } }; - Build.prototype.scrollToTop = function () { + Build.prototype.scrollDown = function () { + document.body.scrollTop = document.body.scrollHeight; + }; + + Build.prototype.scrollToBottom = function () { + this.scrollDown(); this.hasBeenScrolled = true; - this.$scrollContainer.scrollTop(0); this.toggleScroll(); }; - Build.prototype.scrollToBottom = function () { + Build.prototype.scrollToTop = function () { + document.body.scrollTop = 0; this.hasBeenScrolled = true; - this.$scrollContainer.scrollTop(this.$scrollContainer.prop('scrollHeight')); this.toggleScroll(); }; @@ -142,47 +161,6 @@ window.Build = (function () { this.$scrollBottomBtn.toggleClass('animate', toggle); }; - /** - * Build trace top position depends on the space ocupied by the elments rendered before - */ - Build.prototype.verifyTopPosition = function () { - const $buildPage = $('.build-page'); - - const $flashError = $('.alert-wrapper'); - const $header = $('.build-header', $buildPage); - const $runnersStuck = $('.js-build-stuck', $buildPage); - const $startsEnvironment = $('.js-environment-container', $buildPage); - const $erased = $('.js-build-erased', $buildPage); - const prependTopDefault = 20; - - // header + navigation + margin - let topPostion = 168; - - if ($header.length) { - topPostion += $header.outerHeight(); - } - - if ($runnersStuck.length) { - topPostion += $runnersStuck.outerHeight(); - } - - if ($startsEnvironment.length) { - topPostion += $startsEnvironment.outerHeight() + prependTopDefault; - } - - if ($erased.length) { - topPostion += $erased.outerHeight() + prependTopDefault; - } - - if ($flashError.length) { - topPostion += $flashError.outerHeight() + prependTopDefault; - } - - this.$buildTrace.css({ - top: topPostion, - }); - }; - Build.prototype.initSidebar = function () { this.$sidebar = $('.js-build-sidebar'); this.$sidebar.niceScroll(); @@ -200,6 +178,8 @@ window.Build = (function () { this.state = log.state; } + this.windowSize = this.$buildTraceOutput.prop('scrollHeight'); + if (log.append) { this.$buildTraceOutput.append(log.html); this.logBytes += log.size; @@ -227,14 +207,7 @@ window.Build = (function () { } Build.timeout = setTimeout(() => { - //eslint-disable-next-line - this.getBuildTrace() - .then(() => { - if (!this.hasBeenScrolled) { - this.scrollToBottom(); - } - }) - .then(() => this.verifyTopPosition()); + this.getBuildTrace(); }, 4000); } else { this.$buildRefreshAnimation.remove(); @@ -247,7 +220,13 @@ window.Build = (function () { }) .fail(() => { this.$buildRefreshAnimation.remove(); - }); + }) + .then(() => { + if (!this.hasBeenScrolled) { + this.scrollDown(); + } + }) + .then(() => this.toggleScroll()); }; Build.prototype.shouldHideSidebarForViewport = function () { @@ -259,14 +238,11 @@ window.Build = (function () { const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined; const $toggleButton = $('.js-sidebar-build-toggle-header'); - this.$buildTrace - .toggleClass('sidebar-expanded', shouldShow) - .toggleClass('sidebar-collapsed', shouldHide); this.$sidebar .toggleClass('right-sidebar-expanded', shouldShow) .toggleClass('right-sidebar-collapsed', shouldHide); - $('.js-build-page') + this.$topBar .toggleClass('sidebar-expanded', shouldShow) .toggleClass('sidebar-collapsed', shouldHide); @@ -279,17 +255,10 @@ window.Build = (function () { Build.prototype.sidebarOnResize = function () { this.toggleSidebar(this.shouldHideSidebarForViewport()); - - this.verifyTopPosition(); - - if (this.canScroll()) { - this.toggleScroll(); - } }; Build.prototype.sidebarOnClick = function () { if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); - this.verifyTopPosition(); }; Build.prototype.updateArtifactRemoveDate = function () { diff --git a/app/assets/javascripts/close_reopen_report_toggle.js b/app/assets/javascripts/close_reopen_report_toggle.js new file mode 100644 index 00000000000..882d20671cc --- /dev/null +++ b/app/assets/javascripts/close_reopen_report_toggle.js @@ -0,0 +1,97 @@ +import DropLab from './droplab/drop_lab'; +import ISetter from './droplab/plugins/input_setter'; + +// Todo: Remove this when fixing issue in input_setter plugin +const InputSetter = Object.assign({}, ISetter); + +class CloseReopenReportToggle { + constructor(opts = {}) { + this.dropdownTrigger = opts.dropdownTrigger; + this.dropdownList = opts.dropdownList; + this.button = opts.button; + } + + initDroplab() { + this.reopenItem = this.dropdownList.querySelector('.reopen-item'); + this.closeItem = this.dropdownList.querySelector('.close-item'); + + this.droplab = new DropLab(); + + const config = this.setConfig(); + + this.droplab.init(this.dropdownTrigger, this.dropdownList, [InputSetter], config); + } + + updateButton(isClosed) { + this.toggleButtonType(isClosed); + + this.button.blur(); + } + + toggleButtonType(isClosed) { + const [showItem, hideItem] = this.getButtonTypes(isClosed); + + showItem.classList.remove('hidden'); + showItem.classList.add('droplab-item-selected'); + + hideItem.classList.add('hidden'); + hideItem.classList.remove('droplab-item-selected'); + + showItem.click(); + } + + getButtonTypes(isClosed) { + return isClosed ? [this.reopenItem, this.closeItem] : [this.closeItem, this.reopenItem]; + } + + setDisable(shouldDisable = true) { + if (shouldDisable) { + this.button.setAttribute('disabled', 'true'); + this.dropdownTrigger.setAttribute('disabled', 'true'); + } else { + this.button.removeAttribute('disabled'); + this.dropdownTrigger.removeAttribute('disabled'); + } + } + + setConfig() { + const config = { + InputSetter: [ + { + input: this.button, + valueAttribute: 'data-text', + inputAttribute: 'data-value', + }, + { + input: this.button, + valueAttribute: 'data-text', + inputAttribute: 'title', + }, + { + input: this.button, + valueAttribute: 'data-button-class', + inputAttribute: 'class', + }, + { + input: this.dropdownTrigger, + valueAttribute: 'data-toggle-class', + inputAttribute: 'class', + }, + { + input: this.button, + valueAttribute: 'data-url', + inputAttribute: 'href', + }, + { + input: this.button, + valueAttribute: 'data-method', + inputAttribute: 'data-method', + }, + ], + }; + + return config; + } +} + +export default CloseReopenReportToggle; diff --git a/app/assets/javascripts/comment_type_toggle.js b/app/assets/javascripts/comment_type_toggle.js index df0ba86198c..c74184949df 100644 --- a/app/assets/javascripts/comment_type_toggle.js +++ b/app/assets/javascripts/comment_type_toggle.js @@ -1,5 +1,8 @@ import DropLab from './droplab/drop_lab'; -import InputSetter from './droplab/plugins/input_setter'; +import ISetter from './droplab/plugins/input_setter'; + +// Todo: Remove this when fixing issue in input_setter plugin +const InputSetter = Object.assign({}, ISetter); class CommentTypeToggle { constructor(opts = {}) { diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js index 2c38440a2af..687f09882a7 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js +++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js @@ -18,13 +18,26 @@ window.gl.CommitPipelinesTable = CommitPipelinesTable; document.addEventListener('DOMContentLoaded', () => { const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); - if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) { - const table = new CommitPipelinesTable({ - propsData: { - endpoint: pipelineTableViewEl.dataset.endpoint, - helpPagePath: pipelineTableViewEl.dataset.helpPagePath, - }, - }).$mount(); - pipelineTableViewEl.appendChild(table.$el); + if (pipelineTableViewEl) { + // Update MR and Commits tabs + pipelineTableViewEl.addEventListener('update-pipelines-count', (event) => { + if (event.detail.pipelines && + event.detail.pipelines.count && + event.detail.pipelines.count.all) { + const badge = document.querySelector('.js-pipelines-mr-count'); + + badge.textContent = event.detail.pipelines.count.all; + } + }); + + if (pipelineTableViewEl.dataset.disableInitialization === undefined) { + const table = new CommitPipelinesTable({ + propsData: { + endpoint: pipelineTableViewEl.dataset.endpoint, + helpPagePath: pipelineTableViewEl.dataset.helpPagePath, + }, + }).$mount(); + pipelineTableViewEl.appendChild(table.$el); + } } }); diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue index 3c77f14d533..dd751ec97a8 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue @@ -51,11 +51,22 @@ }, methods: { successCallback(resp) { - const response = resp.json(); + return resp.json().then((response) => { + // depending of the endpoint the response can either bring a `pipelines` key or not. + const pipelines = response.pipelines || response; + this.setCommonData(pipelines); - // depending of the endpoint the response can either bring a `pipelines` key or not. - const pipelines = response.pipelines || response; - this.setCommonData(pipelines); + const updatePipelinesEvent = new CustomEvent('update-pipelines-count', { + detail: { + pipelines: response, + }, + }); + + // notifiy to update the count in tabs + if (this.$el.parentElement) { + this.$el.parentElement.dispatchEvent(updatePipelinesEvent); + } + }); }, }, }; diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index 1be9df19c81..6a008112203 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -2,6 +2,7 @@ import './lib/utils/url_utility'; import FilesCommentButton from './files_comment_button'; +import SingleFileDiff from './single_file_diff'; const UNFOLD_COUNT = 20; let isBound = false; @@ -10,7 +11,11 @@ class Diff { constructor() { const $diffFile = $('.files .diff-file'); - $diffFile.singleFileDiff(); + $diffFile.each((index, file) => { + if (!$.data(file, 'singleFileDiff')) { + $.data(file, 'singleFileDiff', new SingleFileDiff(file)); + } + }); FilesCommentButton.init($diffFile); diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js b/app/assets/javascripts/diff_notes/components/resolve_btn.js index 9d51fb53eb2..efb6ced9f46 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_btn.js +++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js @@ -1,4 +1,4 @@ -/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */ +/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, max-len */ /* global CommentsStore */ /* global ResolveService */ /* global Flash */ @@ -64,8 +64,6 @@ const ResolveBtn = Vue.extend({ }); }, resolve: function () { - const errorFlashMsg = 'An error occurred when trying to resolve a comment. Please try again.'; - if (!this.canResolve) return; let promise; @@ -79,24 +77,20 @@ const ResolveBtn = Vue.extend({ .resolve(this.noteId); } - promise.then((response) => { - this.loading = false; + promise + .then(resp => resp.json()) + .then((data) => { + this.loading = false; - if (response.status === 200) { - const data = response.json(); const resolved_by = data ? data.resolved_by : null; CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by); this.discussion.updateHeadline(data); gl.mrWidget.checkStatus(); - } else { - new Flash(errorFlashMsg); - } - this.updateTooltip(); - }).catch(() => { - new Flash(errorFlashMsg); - }); + this.updateTooltip(); + }) + .catch(() => new Flash('An error occurred when trying to resolve a comment. Please try again.')); } }, mounted: function () { diff --git a/app/assets/javascripts/diff_notes/services/resolve.js b/app/assets/javascripts/diff_notes/services/resolve.js index 807ab11d292..2f063f6fe1f 100644 --- a/app/assets/javascripts/diff_notes/services/resolve.js +++ b/app/assets/javascripts/diff_notes/services/resolve.js @@ -1,4 +1,3 @@ -/* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */ /* global Flash */ /* global CommentsStore */ @@ -32,27 +31,22 @@ class ResolveServiceClass { promise = this.resolveAll(mergeRequestId, discussionId); } - promise.then((response) => { - discussion.loading = false; - - if (response.status === 200) { - const data = response.json(); - const resolved_by = data ? data.resolved_by : null; + promise + .then(resp => resp.json()) + .then((data) => { + discussion.loading = false; + const resolvedBy = data ? data.resolved_by : null; if (isResolved) { discussion.unResolveAllNotes(); } else { - discussion.resolveAllNotes(resolved_by); + discussion.resolveAllNotes(resolvedBy); } gl.mrWidget.checkStatus(); discussion.updateHeadline(data); - } else { - throw new Error('An error occurred when trying to resolve discussion.'); - } - }).catch(() => { - new Flash('An error occurred when trying to resolve a discussion. Please try again.'); - }); + }) + .catch(() => new Flash('An error occurred when trying to resolve a discussion. Please try again.')); } resolveAll(mergeRequestId, discussionId) { @@ -62,7 +56,7 @@ class ResolveServiceClass { return this.discussionResource.save({ mergeRequestId, - discussionId + discussionId, }, {}); } @@ -73,7 +67,7 @@ class ResolveServiceClass { return this.discussionResource.delete({ mergeRequestId, - discussionId + discussionId, }, {}); } } diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 4247540de22..ae19592ecbe 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -1,17 +1,13 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ -/* global UsernameValidator */ -/* global ActiveTabMemoizer */ /* global ShortcutsNavigation */ /* global IssuableIndex */ /* global ShortcutsIssuable */ -/* global ZenMode */ /* global Milestone */ /* global IssuableForm */ /* global LabelsSelect */ /* global MilestoneSelect */ /* global Commit */ /* global NotificationsForm */ -/* global TreeView */ /* global NotificationsDropdown */ /* global GroupAvatar */ /* global LineHighlighter */ @@ -25,7 +21,6 @@ /* global ProjectAvatar */ /* global CompareAutocomplete */ /* global ProjectNew */ -/* global Star */ /* global ProjectShow */ /* global Labels */ /* global Shortcuts */ @@ -54,8 +49,19 @@ import UsersSelect from './users_select'; import RefSelectDropdown from './ref_select_dropdown'; import GfmAutoComplete from './gfm_auto_complete'; import ShortcutsBlob from './shortcuts_blob'; +import SigninTabsMemoizer from './signin_tabs_memoizer'; +import Star from './star'; +import Todos from './todos'; +import TreeView from './tree'; +import UsagePing from './usage_ping'; +import UsernameValidator from './username_validator'; +import VersionCheckImage from './version_check_image'; +import Wikis from './wikis'; +import ZenMode from './zen_mode'; import initSettingsPanels from './settings_panels'; import initExperimentalFlags from './experimental_flags'; +import OAuthRememberMe from './oauth_remember_me'; +import PerformanceBar from './performance_bar'; (function() { var Dispatcher; @@ -126,7 +132,8 @@ import initExperimentalFlags from './experimental_flags'; break; case 'sessions:new': new UsernameValidator(); - new ActiveTabMemoizer(); + new SigninTabsMemoizer(); + new OAuthRememberMe({ container: $(".omniauth-container") }).bindEvents(); break; case 'projects:boards:show': case 'projects:boards:index': @@ -161,7 +168,7 @@ import initExperimentalFlags from './experimental_flags'; new UsersSelect(); break; case 'dashboard:todos:index': - new gl.Todos(); + new Todos(); break; case 'dashboard:projects:index': case 'dashboard:projects:starred': @@ -315,7 +322,7 @@ import initExperimentalFlags from './experimental_flags'; new gl.Members(); new UsersSelect(); break; - case 'projects:settings:members:show': + case 'projects:project_members:index': new gl.MemberExpirationDate('.js-access-expiration-date-groups'); new GroupsSelect(); new gl.MemberExpirationDate(); @@ -377,7 +384,7 @@ import initExperimentalFlags from './experimental_flags'; new BlobViewer(); break; case 'help:index': - gl.VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); + VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); break; case 'search:show': new Search(); @@ -393,6 +400,7 @@ import initExperimentalFlags from './experimental_flags'; initSettingsPanels(); break; case 'projects:settings:ci_cd:show': + case 'groups:settings:ci_cd:show': new gl.ProjectVariables(); break; case 'ci:lints:create': @@ -429,7 +437,7 @@ import initExperimentalFlags from './experimental_flags'; new Admin(); switch (path[1]) { case 'cohorts': - new gl.UsagePing(); + new UsagePing(); break; case 'groups': new UsersSelect(); @@ -481,7 +489,7 @@ import initExperimentalFlags from './experimental_flags'; new NotificationsDropdown(); break; case 'wikis': - new gl.Wikis(); + new Wikis(); shortcut_handler = new ShortcutsWiki(); new ZenMode(); new gl.GLForm($('.wiki-form'), true); @@ -513,6 +521,10 @@ import initExperimentalFlags from './experimental_flags'; if (!shortcut_handler) { new Shortcuts(); } + + if (document.querySelector('#peek')) { + new PerformanceBar({ container: '#peek' }); + } }; Dispatcher.prototype.initSearch = function() { diff --git a/app/assets/javascripts/environments/components/environment.vue b/app/assets/javascripts/environments/components/environment.vue index 8120ef182d4..91ed8c8467f 100644 --- a/app/assets/javascripts/environments/components/environment.vue +++ b/app/assets/javascripts/environments/components/environment.vue @@ -32,7 +32,6 @@ export default { state: store.state, visibility: 'available', isLoading: false, - isLoadingFolderContent: false, cssContainerClass: environmentsData.cssClass, endpoint: environmentsData.environmentsDataEndpoint, canCreateDeployment: environmentsData.canCreateDeployment, @@ -86,9 +85,6 @@ export default { errorCallback: this.errorCallback, notificationCallback: (isMakingRequest) => { this.isMakingRequest = isMakingRequest; - - // We need to verify if any folder is open to also fecth it - this.openFolders = this.store.getOpenFolders(); }, }); @@ -119,7 +115,7 @@ export default { this.store.toggleFolder(folder); if (!folder.isOpen) { - this.fetchChildEnvironments(folder, folderUrl); + this.fetchChildEnvironments(folder, folderUrl, true); } }, @@ -147,19 +143,17 @@ export default { .catch(this.errorCallback); }, - fetchChildEnvironments(folder, folderUrl) { - this.isLoadingFolderContent = true; + fetchChildEnvironments(folder, folderUrl, showLoader = false) { + this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader); this.service.getFolderContent(folderUrl) .then(resp => resp.json()) - .then((response) => { - this.store.setfolderContent(folder, response.environments); - this.isLoadingFolderContent = false; - }) + .then(response => this.store.setfolderContent(folder, response.environments)) + .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false)) .catch(() => { - this.isLoadingFolderContent = false; // eslint-disable-next-line no-new new Flash('An error occurred while fetching the environments.'); + this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false); }); }, @@ -176,13 +170,13 @@ export default { successCallback(resp) { this.saveData(resp); - // If folders are open while polling we need to open them again - if (this.openFolders.length) { - this.openFolders.map((folder) => { + // We need to verify if any folder is open to also update it + const openFolders = this.store.getOpenFolders(); + if (openFolders.length) { + openFolders.forEach((folder) => { // TODO - Move this to the backend const folderUrl = `${window.location.pathname}/folders/${folder.folderName}`; - this.store.updateFolder(folder, 'isOpen', true); return this.fetchChildEnvironments(folder, folderUrl); }); } @@ -267,7 +261,7 @@ export default { :environments="state.environments" :can-create-deployment="canCreateDeploymentParsed" :can-read-environment="canReadEnvironmentParsed" - :is-loading-folder-content="isLoadingFolderContent" /> + /> </div> <table-pagination diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index b25113e0fc6..d8b1b2f1b92 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -498,9 +498,9 @@ export default { <div class="table-section section-15 hidden-xs hidden-sm" role="gridcell"> <a v-if="shouldRenderBuildName" - class="build-link" + class="build-link flex-truncate-parent" :href="buildPath"> - {{buildName}} + <span class="flex-truncate-child">{{buildName}}</span> </a> </div> diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue index b1fd9db650b..175cc8f1f72 100644 --- a/app/assets/javascripts/environments/components/environments_table.vue +++ b/app/assets/javascripts/environments/components/environments_table.vue @@ -29,12 +29,6 @@ export default { required: false, default: false, }, - - isLoadingFolderContent: { - type: Boolean, - required: false, - default: false, - }, }, methods: { @@ -74,7 +68,7 @@ export default { /> <template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0"> - <div v-if="isLoadingFolderContent"> + <div v-if="model.isLoadingFolderContent"> <loading-icon size="2" /> </div> diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js index 25b24fbd6dc..8f4066e3a6e 100644 --- a/app/assets/javascripts/environments/mixins/environments_mixin.js +++ b/app/assets/javascripts/environments/mixins/environments_mixin.js @@ -1,17 +1,15 @@ export default { methods: { saveData(resp) { - const response = { - headers: resp.headers, - body: resp.json(), - }; + const headers = resp.headers; + return resp.json().then((response) => { + this.isLoading = false; - this.isLoading = false; - - this.store.storeAvailableCount(response.body.available_count); - this.store.storeStoppedCount(response.body.stopped_count); - this.store.storeEnvironments(response.body.environments); - this.store.setPagination(response.headers); + this.store.storeAvailableCount(response.available_count); + this.store.storeStoppedCount(response.stopped_count); + this.store.storeEnvironments(response.environments); + this.store.setPagination(headers); + }); }, }, }; diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js index a5773dd7e4f..038c149be2d 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js +++ b/app/assets/javascripts/environments/stores/environments_store.js @@ -35,14 +35,18 @@ export default class EnvironmentsStore { */ storeEnvironments(environments = []) { const filteredEnvironments = environments.map((env) => { + const oldEnvironmentState = this.state.environments + .find(element => element.id === env.latest.id) || {}; + let filtered = {}; if (env.size > 1) { filtered = Object.assign({}, env, { isFolder: true, + isLoadingFolderContent: oldEnvironmentState.isLoading || false, folderName: env.name, - isOpen: false, - children: [], + isOpen: oldEnvironmentState.isOpen || false, + children: oldEnvironmentState.children || [], }); } @@ -98,7 +102,7 @@ export default class EnvironmentsStore { * @return {Array} */ toggleFolder(folder) { - return this.updateFolder(folder, 'isOpen', !folder.isOpen); + return this.updateEnvironmentProp(folder, 'isOpen', !folder.isOpen); } /** @@ -125,23 +129,23 @@ export default class EnvironmentsStore { return updated; }); - return this.updateFolder(folder, 'children', updatedEnvironments); + return this.updateEnvironmentProp(folder, 'children', updatedEnvironments); } /** - * Given a folder a prop and a new value updates the correct folder. + * Given a environment, a prop and a new value updates the correct environment. * - * @param {Object} folder + * @param {Object} environment * @param {String} prop * @param {String|Boolean|Object|Array} newValue * @return {Array} */ - updateFolder(folder, prop, newValue) { + updateEnvironmentProp(environment, prop, newValue) { const environments = this.state.environments; const updatedEnvironments = environments.map((env) => { const updateEnv = Object.assign({}, env); - if (env.isFolder && env.id === folder.id) { + if (env.id === environment.id) { updateEnv[prop] = newValue; } @@ -149,8 +153,6 @@ export default class EnvironmentsStore { }); this.state.environments = updatedEnvironments; - - return updatedEnvironments; } getOpenFolders() { diff --git a/app/assets/javascripts/experimental_flags.js b/app/assets/javascripts/experimental_flags.js index dbd3843cef7..6ee65ca72f9 100644 --- a/app/assets/javascripts/experimental_flags.js +++ b/app/assets/javascripts/experimental_flags.js @@ -7,5 +7,8 @@ export default () => { Cookies.set(el.name, el.value, { expires: 365 * 10, }); + + document.body.scrollTop = 0; + window.location.reload(); }); }; diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 2c56b718212..6cb9cfe1382 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -30,6 +30,7 @@ class GfmAutoComplete { this.input.each((i, input) => { const $input = $(input); $input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input)); + $input.on('change.atwho', () => input.dispatchEvent(new Event('input'))); // This triggers at.js again // Needed for quick actions with suffixes (ex: /label ~) $input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup')); diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js index ff601db2aa6..00e1bd94c9c 100644 --- a/app/assets/javascripts/groups/index.js +++ b/app/assets/javascripts/groups/index.js @@ -99,8 +99,10 @@ document.addEventListener('DOMContentLoaded', () => { page: currentPath, }, document.title, currentPath); - this.updateGroups(response.json()); - this.updatePagination(response.headers); + return response.json().then((data) => { + this.updateGroups(data); + this.updatePagination(response.headers); + }); }) .catch(this.handleErrorResponse); }, @@ -114,18 +116,19 @@ document.addEventListener('DOMContentLoaded', () => { }, leaveGroup(group, collection) { this.service.leaveGroup(group.leavePath) + .then(resp => resp.json()) .then((response) => { $.scrollTo(0); this.store.removeGroup(group, collection); // eslint-disable-next-line no-new - new Flash(response.json().notice, 'notice'); + new Flash(response.notice, 'notice'); }) - .catch((response) => { + .catch((error) => { let message = 'An error occurred. Please try again.'; - if (response.status === 403) { + if (error.status === 403) { message = 'Failed to leave the group. Please make sure you are not the only owner'; } diff --git a/app/assets/javascripts/helpers/issuables_helper.js b/app/assets/javascripts/helpers/issuables_helper.js new file mode 100644 index 00000000000..52d0f7e43fc --- /dev/null +++ b/app/assets/javascripts/helpers/issuables_helper.js @@ -0,0 +1,27 @@ +import CloseReopenReportToggle from '../close_reopen_report_toggle'; + +function initCloseReopenReport() { + const container = document.querySelector('.js-issuable-close-dropdown'); + + if (!container) return undefined; + + const dropdownTrigger = container.querySelector('.js-issuable-close-toggle'); + const dropdownList = container.querySelector('.js-issuable-close-menu'); + const button = container.querySelector('.js-issuable-close-button'); + + const closeReopenReportToggle = new CloseReopenReportToggle({ + dropdownTrigger, + dropdownList, + button, + }); + + closeReopenReportToggle.initDroplab(); + + return closeReopenReportToggle; +} + +const IssuablesHelper = { + initCloseReopenReport, +}; + +export default IssuablesHelper; diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 92f6f0d4117..9ac1325fc95 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -1,12 +1,12 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, max-len */ /* global GitLab */ -/* global ZenMode */ /* global Autosave */ /* global dateFormat */ /* global Pikaday */ import UsersSelect from './users_select'; import GfmAutoComplete from './gfm_auto_complete'; +import ZenMode from './zen_mode'; (function() { this.IssuableForm = (function() { diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 0860e237ce1..2bee4fb045a 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -4,13 +4,14 @@ import 'vendor/jquery.waitforimages'; import '~/lib/utils/text_utility'; import './flash'; -import './task_list'; +import TaskList from './task_list'; import CreateMergeRequestDropdown from './create_merge_request_dropdown'; +import IssuablesHelper from './helpers/issuables_helper'; class Issue { constructor() { if ($('a.btn-close').length) { - this.taskList = new gl.TaskList({ + this.taskList = new TaskList({ dataType: 'issue', fieldName: 'description', selector: '.detail-page-description', @@ -28,6 +29,11 @@ class Issue { Issue.initMergeRequests(); Issue.initRelatedBranches(); + this.closeButtons = $('a.btn-close'); + this.reopenButtons = $('a.btn-reopen'); + + this.initCloseReopenReport(); + if (Issue.createMrDropdownWrap) { this.createMergeRequestDropdown = new CreateMergeRequestDropdown(Issue.createMrDropdownWrap); } @@ -35,13 +41,8 @@ class Issue { initIssueBtnEventListeners() { const issueFailMessage = 'Unable to update this issue at this time.'; - const closeButtons = $('a.btn-close'); - const isClosedBadge = $('div.status-box-closed'); - const isOpenBadge = $('div.status-box-open'); - const projectIssuesCounter = $('.issue_counter'); - const reopenButtons = $('a.btn-reopen'); - return closeButtons.add(reopenButtons).on('click', (e) => { + return $(document).on('click', 'a.btn-close, a.btn-reopen', (e) => { var $button, shouldSubmit, url; e.preventDefault(); e.stopImmediatePropagation(); @@ -50,7 +51,9 @@ class Issue { if (shouldSubmit) { Issue.submitNoteForm($button.closest('form')); } - $button.prop('disabled', true); + + this.disableCloseReopenButton($button); + url = $button.attr('href'); return $.ajax({ type: 'PUT', @@ -58,15 +61,19 @@ class Issue { }) .fail(() => new Flash(issueFailMessage)) .done((data) => { + const isClosedBadge = $('div.status-box-closed'); + const isOpenBadge = $('div.status-box-open'); + const projectIssuesCounter = $('.issue_counter'); + if ('id' in data) { $(document).trigger('issuable:change'); const isClosed = $button.hasClass('btn-close'); - closeButtons.toggleClass('hidden', isClosed); - reopenButtons.toggleClass('hidden', !isClosed); isClosedBadge.toggleClass('hidden', !isClosed); isOpenBadge.toggleClass('hidden', isClosed); + this.toggleCloseReopenButton(isClosed); + let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, '')); numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1; projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues)); @@ -83,12 +90,34 @@ class Issue { } else { new Flash(issueFailMessage); } - - $button.prop('disabled', false); + }) + .then(() => { + this.disableCloseReopenButton($button, false); }); }); } + initCloseReopenReport() { + this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport(); + + if (this.closeButtons) this.closeButtons = this.closeButtons.not('.issuable-close-button'); + if (this.reopenButtons) this.reopenButtons = this.reopenButtons.not('.issuable-close-button'); + } + + disableCloseReopenButton($button, shouldDisable) { + if (this.closeReopenReportToggle) { + this.closeReopenReportToggle.setDisable(shouldDisable); + } else { + $button.prop('disabled', shouldDisable); + } + } + + toggleCloseReopenButton(isClosed) { + if (this.closeReopenReportToggle) this.closeReopenReportToggle.updateButton(isClosed); + this.closeButtons.toggleClass('hidden', isClosed); + this.reopenButtons.toggleClass('hidden', !isClosed); + } + static submitNoteForm(form) { var noteText; noteText = form.find("textarea.js-note-text").val(); diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 3d5fb7f441c..efae112923d 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -202,10 +202,7 @@ export default { this.poll = new Poll({ resource: this.service, method: 'getData', - successCallback: (res) => { - const data = res.json(); - this.store.updateState(data); - }, + successCallback: res => res.json().then(data => this.store.updateState(data)), errorCallback(err) { throw new Error(err); }, diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index 43db66c8e08..48bad8f1e68 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -1,5 +1,6 @@ <script> import animateMixin from '../mixins/animate'; + import TaskList from '../../task_list'; export default { mixins: [animateMixin], @@ -46,7 +47,7 @@ if (this.canUpdate) { // eslint-disable-next-line no-new - new gl.TaskList({ + new TaskList({ dataType: 'issue', fieldName: 'description', selector: '.detail-page-description', diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js index 939d17129de..f92e669414a 100644 --- a/app/assets/javascripts/jobs/job_details_bundle.js +++ b/app/assets/javascripts/jobs/job_details_bundle.js @@ -26,14 +26,6 @@ document.addEventListener('DOMContentLoaded', () => { mounted() { this.mediator.initBuildClass(); }, - updated() { - // Wait for flash message to be appended - Vue.nextTick(() => { - if (this.mediator.build) { - this.mediator.build.verifyTopPosition(); - } - }); - }, render(createElement) { return createElement('job-header', { props: { diff --git a/app/assets/javascripts/jobs/job_details_mediator.js b/app/assets/javascripts/jobs/job_details_mediator.js index 063c52fac74..cc014b815c4 100644 --- a/app/assets/javascripts/jobs/job_details_mediator.js +++ b/app/assets/javascripts/jobs/job_details_mediator.js @@ -54,9 +54,8 @@ export default class JobMediator { } successCallback(response) { - const data = response.json(); this.state.isLoading = false; - this.store.storeJob(data); + return response.json().then(data => this.store.storeJob(data)); } errorCallback() { diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index fe752d95b90..26c67fb721c 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -143,26 +143,12 @@ import './render_math'; import './right_sidebar'; import './search'; import './search_autocomplete'; -import './signin_tabs_memoizer'; -import './single_file_diff'; import './smart_interval'; import './snippets_list'; import './star'; import './subscription'; import './subscription_select'; import './syntax_highlight'; -import './task_list'; -import './todos'; -import './tree'; -import './usage_ping'; -import './user'; -import './user_tabs'; -import './username_validator'; -import './users_select'; -import './version_check_image'; -import './visibility_select'; -import './wikis'; -import './zen_mode'; // eslint-disable-next-line global-require, import/no-commonjs if (process.env.NODE_ENV !== 'production') require('./test_utils/'); diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index f93feeec1c2..0db2abe507d 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -2,8 +2,9 @@ /* global MergeRequestTabs */ import 'vendor/jquery.waitforimages'; -import './task_list'; +import TaskList from './task_list'; import './merge_request_tabs'; +import IssuablesHelper from './helpers/issuables_helper'; (function() { this.MergeRequest = (function() { @@ -21,11 +22,14 @@ import './merge_request_tabs'; return _this.showAllCommits(); }; })(this)); + this.initTabs(); this.initMRBtnListeners(); this.initCommitMessageListeners(); + this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport(); + if ($("a.btn-close").length) { - this.taskList = new gl.TaskList({ + this.taskList = new TaskList({ dataType: 'merge_request', fieldName: 'description', selector: '.detail-page-description', @@ -64,11 +68,15 @@ import './merge_request_tabs'; if (shouldSubmit && $this.data('submitted')) { return; } + + if (this.closeReopenReportToggle) this.closeReopenReportToggle.setDisable(); + if (shouldSubmit) { if ($this.hasClass('btn-comment-and-close') || $this.hasClass('btn-comment-and-reopen')) { e.preventDefault(); e.stopImmediatePropagation(); - return _this.submitNoteForm($this.closest('form'), $this); + + _this.submitNoteForm($this.closest('form'), $this); } } }); diff --git a/app/assets/javascripts/monitoring/components/monitoring_column.vue b/app/assets/javascripts/monitoring/components/monitoring_column.vue index e933634643b..c376baea79c 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_column.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_column.vue @@ -35,7 +35,7 @@ data() { return { - graphHeight: 500, + graphHeight: 450, graphWidth: 600, graphHeightOffset: 120, xScale: {}, @@ -88,7 +88,9 @@ }, paddingBottomRootSvg() { - return (Math.ceil(this.graphHeight * 100) / this.graphWidth) || 0; + return { + paddingBottom: `${(Math.ceil(this.graphHeight * 100) / this.graphWidth) || 0}%`, + }; }, }, @@ -103,9 +105,9 @@ this.measurements = measurements.small; } this.data = query.result[0].values; - this.unitOfDisplay = query.unit || 'N/A'; + this.unitOfDisplay = query.unit || ''; this.yAxisLabel = this.columnData.y_label || 'Values'; - this.legendTitle = query.legend || 'Average'; + this.legendTitle = query.label || 'Average'; this.graphWidth = this.$refs.baseSvg.clientWidth - this.margin.left - this.margin.right; this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom; @@ -157,12 +159,12 @@ const xAxis = d3.svg.axis() .scale(axisXScale) - .ticks(measurements.ticks) + .ticks(measurements.xTicks) .orient('bottom'); const yAxis = d3.svg.axis() .scale(this.yScale) - .ticks(measurements.ticks) + .ticks(measurements.yTicks) .orient('left'); d3.select(this.$refs.baseSvg).select('.x-axis').call(xAxis); @@ -170,8 +172,12 @@ const width = this.graphWidth; d3.select(this.$refs.baseSvg).select('.y-axis').call(yAxis) .selectAll('.tick') - .each(function createTickLines() { - d3.select(this).select('line').attr('x2', width); + .each(function createTickLines(d, i) { + if (i > 0) { + d3.select(this).select('line') + .attr('x2', width) + .attr('class', 'axis-tick'); + } // Avoid adding the class to the first tick, to prevent coloring }); // This will select all of the ticks once they're rendered this.xScale = d3.time.scale() @@ -198,7 +204,7 @@ watch: { updateAspectRatio() { if (this.updateAspectRatio) { - this.graphHeight = 500; + this.graphHeight = 450; this.graphWidth = 600; this.measurements = measurements.large; this.draw(); @@ -213,17 +219,17 @@ }; </script> <template> - <div + <div :class="classType"> - <h5 - class="text-center"> + <h5 + class="text-center graph-title"> {{columnData.title}} </h5> - <div - class="prometheus-svg-container"> - <svg + <div + class="prometheus-svg-container" + :style="paddingBottomRootSvg"> + <svg :viewBox="outterViewBox" - :style="{ 'padding-bottom': paddingBottomRootSvg }" ref="baseSvg"> <g class="x-axis" @@ -233,7 +239,7 @@ class="y-axis" transform="translate(70, 20)"> </g> - <monitoring-legends + <monitoring-legends :graph-width="graphWidth" :graph-height="graphHeight" :margin="margin" @@ -243,7 +249,7 @@ :y-axis-label="yAxisLabel" :metric-usage="metricUsage" /> - <svg + <svg class="graph-data" :viewBox="innerViewBox" ref="graphData"> @@ -261,7 +267,7 @@ stroke-width="2" transform="translate(-5, 20)"> </path> - <rect + <rect class="prometheus-graph-overlay" :width="(graphWidth - 70)" :height="(graphHeight - 100)" @@ -275,7 +281,7 @@ :graph-height="graphHeight" :graph-height-offset="graphHeightOffset" /> - <monitoring-flag + <monitoring-flag v-if="showFlag" :current-x-coordinate="currentXCoordinate" :current-y-coordinate="currentYCoordinate" diff --git a/app/assets/javascripts/monitoring/components/monitoring_flag.vue b/app/assets/javascripts/monitoring/components/monitoring_flag.vue index 180a771415b..5a0e50fcab3 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_flag.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_flag.vue @@ -87,14 +87,14 @@ </rect> <text class="text-metric text-metric-bold" - x="8" + x="16" y="35" transform="translate(-5, 20)"> {{formatTime}} </text> <text - class="text-metric-date" - x="8" + class="text-metric" + x="16" y="15" transform="translate(-5, 20)"> {{formatDate}} diff --git a/app/assets/javascripts/monitoring/components/monitoring_legends.vue b/app/assets/javascripts/monitoring/components/monitoring_legends.vue index b30ed3cc889..922a5e1bf0e 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_legends.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_legends.vue @@ -109,13 +109,13 @@ </text> <rect class="rect-axis-text" - :x="xPosition + 50" + :x="xPosition + 60" :y="graphHeight - 80" - width="50" + width="35" height="50"> </rect> <text - class="label-axis-text" + class="label-axis-text x-label-text" :x="xPosition + 60" :y="yPosition" dy=".35em"> @@ -131,13 +131,13 @@ <text class="text-metric-title" x="50" - :y="graphHeight - 40"> + :y="graphHeight - 25"> {{legendTitle}} </text> <text class="text-metric-usage" x="50" - :y="graphHeight - 25"> + :y="graphHeight - 10"> {{metricUsage}} </text> </g> diff --git a/app/assets/javascripts/monitoring/utils/measurements.js b/app/assets/javascripts/monitoring/utils/measurements.js index a60d2522f49..62cd19c86e1 100644 --- a/app/assets/javascripts/monitoring/utils/measurements.js +++ b/app/assets/javascripts/monitoring/utils/measurements.js @@ -8,14 +8,14 @@ export default { }, legends: { width: 15, - height: 30, + height: 25, }, backgroundLegend: { width: 30, height: 50, }, axisLabelLineOffset: -20, - legendOffset: 52, + legendOffset: 35, }, large: { // This covers both md and lg screen sizes margin: { @@ -26,14 +26,15 @@ export default { }, legends: { width: 20, - height: 35, + height: 30, }, backgroundLegend: { width: 30, height: 150, }, axisLabelLineOffset: 20, - legendOffset: 55, + legendOffset: 38, }, - ticks: 3, + xTicks: 8, + yTicks: 3, }; diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 555b8c8a65c..b2c503d1656 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -21,7 +21,7 @@ import CommentTypeToggle from './comment_type_toggle'; import loadAwardsHandler from './awards_handler'; import './autosave'; import './dropzone_input'; -import './task_list'; +import TaskList from './task_list'; window.autosize = autosize; window.Dropzone = Dropzone; @@ -71,7 +71,7 @@ export default class Notes { this.addBinding(); this.setPollingInterval(); this.setupMainTargetNoteForm(); - this.taskList = new gl.TaskList({ + this.taskList = new TaskList({ dataType: 'note', fieldName: 'note', selector: '.notes' @@ -1270,7 +1270,7 @@ export default class Notes { <div class="timeline-entry-inner"> <div class="timeline-icon"> <a href="/${currentUsername}"> - <img class="avatar s40" src="${currentUserAvatar}"> + <img class="avatar s40" src="${currentUserAvatar}" /> </a> </div> <div class="timeline-content ${discussionClass}"> diff --git a/app/assets/javascripts/oauth_remember_me.js b/app/assets/javascripts/oauth_remember_me.js new file mode 100644 index 00000000000..ffc2dd6bbca --- /dev/null +++ b/app/assets/javascripts/oauth_remember_me.js @@ -0,0 +1,32 @@ +/** + * OAuth-based login buttons have a separate "remember me" checkbox. + * + * Toggling this checkbox adds/removes a `remember_me` parameter to the + * login buttons' href, which is passed on to the omniauth callback. + **/ + +export default class OAuthRememberMe { + constructor(opts = {}) { + this.container = opts.container || ''; + this.loginLinkSelector = '.oauth-login'; + } + + bindEvents() { + $('#remember_me', this.container).on('click', this.toggleRememberMe); + } + + // eslint-disable-next-line class-methods-use-this + toggleRememberMe(event) { + const rememberMe = $(event.target).is(':checked'); + + $('.oauth-login', this.container).each((i, element) => { + const href = $(element).attr('href'); + + if (rememberMe) { + $(element).attr('href', `${href}?remember_me=1`); + } else { + $(element).attr('href', href.replace('?remember_me=1', '')); + } + }); + } +} diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js deleted file mode 100644 index de1a99fa3bd..00000000000 --- a/app/assets/javascripts/peek.js +++ /dev/null @@ -1,16 +0,0 @@ -import 'vendor/peek'; -import 'vendor/peek.performance_bar'; - -$(document).on('click', '#peek-show-queries', (e) => { - e.preventDefault(); - $('.peek-rblineprof-modal').hide(); - const $modal = $('#modal-peek-pg-queries'); - if ($modal.length) { - $modal.modal('toggle'); - } -}); - -$(document).on('click', '.js-lineprof-file', (e) => { - e.preventDefault(); - $(e.target).parents('.peek-rblineprof-file').find('.data').toggle(); -}); diff --git a/app/assets/javascripts/performance_bar.js b/app/assets/javascripts/performance_bar.js new file mode 100644 index 00000000000..9bbdf7f513c --- /dev/null +++ b/app/assets/javascripts/performance_bar.js @@ -0,0 +1,62 @@ +import 'vendor/peek'; +import 'vendor/peek.performance_bar'; + +export default class PerformanceBar { + constructor(opts) { + if (!PerformanceBar.singleton) { + this.init(opts); + PerformanceBar.singleton = this; + } + return PerformanceBar.singleton; + } + + init(opts) { + const $container = $(opts.container); + this.$sqlProfileLink = $container.find('.js-toggle-modal-peek-sql'); + this.$sqlProfileModal = $container.find('#modal-peek-pg-queries'); + this.$lineProfileLink = $container.find('.js-toggle-modal-peek-line-profile'); + this.$lineProfileModal = $('#modal-peek-line-profile'); + this.initEventListeners(); + this.showModalOnLoad(); + } + + initEventListeners() { + this.$sqlProfileLink.on('click', () => this.handleSQLProfileLink()); + this.$lineProfileLink.on('click', e => this.handleLineProfileLink(e)); + $(document).on('click', '.js-lineprof-file', PerformanceBar.toggleLineProfileFile); + } + + showModalOnLoad() { + // When a lineprofiler query-string param is present, we show the line + // profiler modal upon page load + if (/lineprofiler/.test(window.location.search)) { + PerformanceBar.toggleModal(this.$lineProfileModal); + } + } + + handleSQLProfileLink() { + PerformanceBar.toggleModal(this.$sqlProfileModal); + } + + handleLineProfileLink(e) { + const lineProfilerParameter = gl.utils.getParameterValues('lineprofiler'); + const lineProfilerParameterRegex = new RegExp(`lineprofiler=${lineProfilerParameter[0]}`); + const shouldToggleModal = lineProfilerParameter.length > 0 && + lineProfilerParameterRegex.test(e.currentTarget.href); + + if (shouldToggleModal) { + e.preventDefault(); + PerformanceBar.toggleModal(this.$lineProfileModal); + } + } + + static toggleModal($modal) { + if ($modal.length) { + $modal.modal('toggle'); + } + } + + static toggleLineProfileFile(e) { + $(e.currentTarget).parents('.peek-rblineprof-file').find('.data').toggle(); + } +} diff --git a/app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js b/app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js index b424e7f205d..50c725aa3d5 100644 --- a/app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js +++ b/app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js @@ -3,6 +3,7 @@ import Translate from '../vue_shared/translate'; import intervalPatternInput from './components/interval_pattern_input.vue'; import TimezoneDropdown from './components/timezone_dropdown'; import TargetBranchDropdown from './components/target_branch_dropdown'; +import { setupPipelineVariableList } from './setup_pipeline_variable_list'; Vue.use(Translate); @@ -39,4 +40,6 @@ document.addEventListener('DOMContentLoaded', () => { gl.timezoneDropdown = new TimezoneDropdown(); gl.targetBranchDropdown = new TargetBranchDropdown(); gl.pipelineScheduleFieldErrors = new gl.GlFieldErrors(formElement); + + setupPipelineVariableList($('.js-pipeline-variable-list')); }); diff --git a/app/assets/javascripts/pipeline_schedules/setup_pipeline_variable_list.js b/app/assets/javascripts/pipeline_schedules/setup_pipeline_variable_list.js new file mode 100644 index 00000000000..644efd10509 --- /dev/null +++ b/app/assets/javascripts/pipeline_schedules/setup_pipeline_variable_list.js @@ -0,0 +1,71 @@ +function insertRow($row) { + const $rowClone = $row.clone(); + $rowClone.removeAttr('data-is-persisted'); + $rowClone.find('input, textarea').val(''); + $row.after($rowClone); +} + +function removeRow($row) { + const isPersisted = gl.utils.convertPermissionToBoolean($row.attr('data-is-persisted')); + + if (isPersisted) { + $row.hide(); + $row + .find('.js-destroy-input') + .val(1); + } else { + $row.remove(); + } +} + +function checkIfRowTouched($row) { + return $row.find('.js-user-input').toArray().some(el => $(el).val().length > 0); +} + +function setupPipelineVariableList(parent = document) { + const $parent = $(parent); + + $parent.on('click', '.js-row-remove-button', (e) => { + const $row = $(e.currentTarget).closest('.js-row'); + removeRow($row); + + e.preventDefault(); + }); + + // Remove any empty rows except the last r + $parent.on('blur', '.js-user-input', (e) => { + const $row = $(e.currentTarget).closest('.js-row'); + + const isTouched = checkIfRowTouched($row); + if ($row.is(':not(:last-child)') && !isTouched) { + removeRow($row); + } + }); + + // Always make sure there is an empty last row + $parent.on('input', '.js-user-input', () => { + const $lastRow = $parent.find('.js-row').last(); + + const isTouched = checkIfRowTouched($lastRow); + if (isTouched) { + insertRow($lastRow); + } + }); + + // Clear out the empty last row so it + // doesn't get submitted and throw validation errors + $parent.closest('form').on('submit', () => { + const $lastRow = $parent.find('.js-row').last(); + + const isTouched = checkIfRowTouched($lastRow); + if (!isTouched) { + $lastRow.find('input, textarea').attr('name', ''); + } + }); +} + +export { + setupPipelineVariableList, + insertRow, + removeRow, +}; diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue index 01ae07aad65..5df317a76bf 100644 --- a/app/assets/javascripts/pipelines/components/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines.vue @@ -129,14 +129,11 @@ }, successCallback(resp) { - const response = { - headers: resp.headers, - body: resp.json(), - }; - - this.store.storeCount(response.body.count); - this.store.storePagination(response.headers); - this.setCommonData(response.body.pipelines); + return resp.json().then((response) => { + this.store.storeCount(response.count); + this.store.storePagination(resp.headers); + this.setCommonData(response.pipelines); + }); }, }, }; diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 87b2725a045..a4a27247406 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -73,8 +73,9 @@ export default { fetchJobs() { this.$http.get(this.stage.dropdown_path) - .then((response) => { - this.dropdownContent = response.json().html; + .then(response => response.json()) + .then((data) => { + this.dropdownContent = data.html; this.isLoading = false; }) .catch(() => { diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js b/app/assets/javascripts/pipelines/pipeline_details_mediatior.js index 82537ea06f5..385e7430a7d 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js +++ b/app/assets/javascripts/pipelines/pipeline_details_mediatior.js @@ -40,10 +40,10 @@ export default class pipelinesMediator { } successCallback(response) { - const data = response.json(); - - this.state.isLoading = false; - this.store.storePipeline(data); + return response.json().then((data) => { + this.state.isLoading = false; + this.store.storePipeline(data); + }); } errorCallback() { diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index c0f757269cb..fd89a1a85c3 100644 --- a/app/assets/javascripts/project_new.js +++ b/app/assets/javascripts/project_new.js @@ -1,5 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, one-var, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, max-len */ +import VisibilitySelect from './visibility_select'; + function highlightChanges($elm) { $elm.addClass('highlight-changes'); setTimeout(() => $elm.removeClass('highlight-changes'), 10); @@ -30,7 +32,7 @@ function highlightChanges($elm) { ProjectNew.prototype.initVisibilitySelect = function() { const visibilityContainer = document.querySelector('.js-visibility-select'); if (!visibilityContainer) return; - const visibilitySelect = new gl.VisibilitySelect(visibilityContainer); + const visibilitySelect = new VisibilitySelect(visibilityContainer); visibilitySelect.init(); const $visibilitySelect = $(visibilityContainer).find('select'); diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js index a4a7f3fa944..e3daa8cf949 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/shortcuts.js @@ -1,6 +1,5 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, prefer-arrow-callback, consistent-return, object-shorthand, no-unused-vars, one-var, one-var-declaration-per-line, no-else-return, comma-dangle, max-len */ /* global Mousetrap */ -/* global findFileURL */ import Cookies from 'js-cookie'; import findAndFollowLink from './shortcuts_dashboard_navigation'; @@ -20,6 +19,7 @@ import findAndFollowLink from './shortcuts_dashboard_navigation'; const $globalDropdownMenu = $('.global-dropdown-menu'); const $globalDropdownToggle = $('.global-dropdown-toggle'); + const findFileURL = document.body.dataset.findFile; $('.global-dropdown').on('hide.bs.dropdown', () => { $globalDropdownMenu.removeClass('shortcuts'); @@ -62,7 +62,7 @@ import findAndFollowLink from './shortcuts_dashboard_navigation'; if (Cookies.get(performanceBarCookieName) === 'true') { Cookies.remove(performanceBarCookieName, { path: '/' }); } else { - Cookies.set(performanceBarCookieName, true, { path: '/' }); + Cookies.set(performanceBarCookieName, 'true', { path: '/' }); } gl.utils.refreshCurrentPage(); }; diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js index 5ccfb4ee9c1..721e92221cf 100644 --- a/app/assets/javascripts/sidebar/sidebar_mediator.js +++ b/app/assets/javascripts/sidebar/sidebar_mediator.js @@ -28,8 +28,8 @@ export default class SidebarMediator { fetch() { this.service.get() - .then((response) => { - const data = response.json(); + .then(response => response.json()) + .then((data) => { this.store.setAssigneeData(data); this.store.setTimeTrackingData(data); }) diff --git a/app/assets/javascripts/signin_tabs_memoizer.js b/app/assets/javascripts/signin_tabs_memoizer.js index 2587facc582..20255398047 100644 --- a/app/assets/javascripts/signin_tabs_memoizer.js +++ b/app/assets/javascripts/signin_tabs_memoizer.js @@ -2,56 +2,52 @@ /* eslint no-new: "off" */ import AccessorUtilities from './lib/utils/accessor'; -((global) => { - /** - * Memorize the last selected tab after reloading a page. - * Does that setting the current selected tab in the localStorage - */ - class ActiveTabMemoizer { - constructor({ currentTabKey = 'current_signin_tab', tabSelector = 'ul.nav-tabs' } = {}) { - this.currentTabKey = currentTabKey; - this.tabSelector = tabSelector; - this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); - - this.bootstrap(); - } - - bootstrap() { - const tabs = document.querySelectorAll(this.tabSelector); - if (tabs.length > 0) { - tabs[0].addEventListener('click', (e) => { - if (e.target && e.target.nodeName === 'A') { - const anchorName = e.target.getAttribute('href'); - this.saveData(anchorName); - } - }); - } - - this.showTab(); - } +/** + * Memorize the last selected tab after reloading a page. + * Does that setting the current selected tab in the localStorage + */ +export default class SigninTabsMemoizer { + constructor({ currentTabKey = 'current_signin_tab', tabSelector = 'ul.nav-tabs' } = {}) { + this.currentTabKey = currentTabKey; + this.tabSelector = tabSelector; + this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); + + this.bootstrap(); + } - showTab() { - const anchorName = this.readData(); - if (anchorName) { - const tab = document.querySelector(`${this.tabSelector} a[href="${anchorName}"]`); - if (tab) { - tab.click(); + bootstrap() { + const tabs = document.querySelectorAll(this.tabSelector); + if (tabs.length > 0) { + tabs[0].addEventListener('click', (e) => { + if (e.target && e.target.nodeName === 'A') { + const anchorName = e.target.getAttribute('href'); + this.saveData(anchorName); } - } + }); } - saveData(val) { - if (!this.isLocalStorageAvailable) return undefined; + this.showTab(); + } - return window.localStorage.setItem(this.currentTabKey, val); + showTab() { + const anchorName = this.readData(); + if (anchorName) { + const tab = document.querySelector(`${this.tabSelector} a[href="${anchorName}"]`); + if (tab) { + tab.click(); + } } + } - readData() { - if (!this.isLocalStorageAvailable) return null; + saveData(val) { + if (!this.isLocalStorageAvailable) return undefined; - return window.localStorage.getItem(this.currentTabKey); - } + return window.localStorage.setItem(this.currentTabKey, val); } - global.ActiveTabMemoizer = ActiveTabMemoizer; -})(window); + readData() { + if (!this.isLocalStorageAvailable) return null; + + return window.localStorage.getItem(this.currentTabKey); + } +} diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 9316a2af0b7..4505a79a2df 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -2,99 +2,82 @@ import FilesCommentButton from './files_comment_button'; -(function() { - window.SingleFileDiff = (function() { - var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER; +const WRAPPER = '<div class="diff-content"></div>'; +const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'; +const ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'; +const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>'; - WRAPPER = '<div class="diff-content"></div>'; - - LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'; - - ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'; +export default class SingleFileDiff { + constructor(file) { + this.file = file; + this.toggleDiff = this.toggleDiff.bind(this); + this.content = $('.diff-content', this.file); + this.$toggleIcon = $('.diff-toggle-caret', this.file); + this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path'); + this.isOpen = !this.diffForPath; + if (this.diffForPath) { + this.collapsedContent = this.content; + this.loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide(); + this.content = null; + this.collapsedContent.after(this.loadingContent); + this.$toggleIcon.addClass('fa-caret-right'); + } else { + this.collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide(); + this.content.after(this.collapsedContent); + this.$toggleIcon.addClass('fa-caret-down'); + } - COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>'; + $('.js-file-title, .click-to-expand', this.file).on('click', (function (e) { + this.toggleDiff($(e.target)); + }).bind(this)); + } - function SingleFileDiff(file) { - this.file = file; - this.toggleDiff = this.toggleDiff.bind(this); - this.content = $('.diff-content', this.file); - this.$toggleIcon = $('.diff-toggle-caret', this.file); - this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path'); - this.isOpen = !this.diffForPath; - if (this.diffForPath) { - this.collapsedContent = this.content; - this.loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide(); - this.content = null; - this.collapsedContent.after(this.loadingContent); - this.$toggleIcon.addClass('fa-caret-right'); - } else { - this.collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide(); - this.content.after(this.collapsedContent); - this.$toggleIcon.addClass('fa-caret-down'); + toggleDiff($target, cb) { + if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return; + this.isOpen = !this.isOpen; + if (!this.isOpen && !this.hasError) { + this.content.hide(); + this.$toggleIcon.addClass('fa-caret-right').removeClass('fa-caret-down'); + this.collapsedContent.show(); + if (typeof gl.diffNotesCompileComponents !== 'undefined') { + gl.diffNotesCompileComponents(); } - - $('.js-file-title, .click-to-expand', this.file).on('click', (function (e) { - this.toggleDiff($(e.target)); - }).bind(this)); + } else if (this.content) { + this.collapsedContent.hide(); + this.content.show(); + this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right'); + if (typeof gl.diffNotesCompileComponents !== 'undefined') { + gl.diffNotesCompileComponents(); + } + } else { + this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right'); + return this.getContentHTML(cb); } + } - SingleFileDiff.prototype.toggleDiff = function($target, cb) { - if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return; - this.isOpen = !this.isOpen; - if (!this.isOpen && !this.hasError) { - this.content.hide(); - this.$toggleIcon.addClass('fa-caret-right').removeClass('fa-caret-down'); - this.collapsedContent.show(); - if (typeof gl.diffNotesCompileComponents !== 'undefined') { - gl.diffNotesCompileComponents(); + getContentHTML(cb) { + this.collapsedContent.hide(); + this.loadingContent.show(); + $.get(this.diffForPath, (function(_this) { + return function(data) { + _this.loadingContent.hide(); + if (data.html) { + _this.content = $(data.html); + _this.content.syntaxHighlight(); + } else { + _this.hasError = true; + _this.content = $(ERROR_HTML); } - } else if (this.content) { - this.collapsedContent.hide(); - this.content.show(); - this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right'); + _this.collapsedContent.after(_this.content); + if (typeof gl.diffNotesCompileComponents !== 'undefined') { gl.diffNotesCompileComponents(); } - } else { - this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right'); - return this.getContentHTML(cb); - } - }; - - SingleFileDiff.prototype.getContentHTML = function(cb) { - this.collapsedContent.hide(); - this.loadingContent.show(); - $.get(this.diffForPath, (function(_this) { - return function(data) { - _this.loadingContent.hide(); - if (data.html) { - _this.content = $(data.html); - _this.content.syntaxHighlight(); - } else { - _this.hasError = true; - _this.content = $(ERROR_HTML); - } - _this.collapsedContent.after(_this.content); - if (typeof gl.diffNotesCompileComponents !== 'undefined') { - gl.diffNotesCompileComponents(); - } + FilesCommentButton.init($(_this.file)); - FilesCommentButton.init($(_this.file)); - - if (cb) cb(); - }; - })(this)); - }; - - return SingleFileDiff; - })(); - - $.fn.singleFileDiff = function() { - return this.each(function() { - if (!$.data(this, 'singleFileDiff')) { - return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this)); - } - }); - }; -}).call(window); + if (cb) cb(); + }; + })(this)); + } +} diff --git a/app/assets/javascripts/smart_interval.js b/app/assets/javascripts/smart_interval.js index d1bdc353be2..2bf7a3a5d61 100644 --- a/app/assets/javascripts/smart_interval.js +++ b/app/assets/javascripts/smart_interval.js @@ -1,158 +1,157 @@ -/* -* Instances of SmartInterval extend the functionality of `setInterval`, make it configurable -* and controllable by a public API. -* -* */ - -(() => { - class SmartInterval { - /** - * @param { function } opts.callback Function to be called on each iteration (required) - * @param { milliseconds } opts.startingInterval `currentInterval` is set to this initially - * @param { milliseconds } opts.maxInterval `currentInterval` will be incremented to this - * @param { milliseconds } opts.hiddenInterval `currentInterval` is set to this - * when the page is hidden - * @param { integer } opts.incrementByFactorOf `currentInterval` is incremented by this factor - * @param { boolean } opts.lazyStart Configure if timer is initialized on - * instantiation or lazily - * @param { boolean } opts.immediateExecution Configure if callback should - * be executed before the first interval. - */ - constructor(opts = {}) { - this.cfg = { - callback: opts.callback, - startingInterval: opts.startingInterval, - maxInterval: opts.maxInterval, - hiddenInterval: opts.hiddenInterval, - incrementByFactorOf: opts.incrementByFactorOf, - lazyStart: opts.lazyStart, - immediateExecution: opts.immediateExecution, - }; - - this.state = { - intervalId: null, - currentInterval: this.cfg.startingInterval, - pageVisibility: 'visible', - }; - - this.initInterval(); - } - /* public */ - - start() { - const cfg = this.cfg; - const state = this.state; - - if (cfg.immediateExecution) { - cfg.immediateExecution = false; - cfg.callback(); - } +/** + * Instances of SmartInterval extend the functionality of `setInterval`, make it configurable + * and controllable by a public API. + */ + +class SmartInterval { + /** + * @param { function } opts.callback Function to be called on each iteration (required) + * @param { milliseconds } opts.startingInterval `currentInterval` is set to this initially + * @param { milliseconds } opts.maxInterval `currentInterval` will be incremented to this + * @param { milliseconds } opts.hiddenInterval `currentInterval` is set to this + * when the page is hidden + * @param { integer } opts.incrementByFactorOf `currentInterval` is incremented by this factor + * @param { boolean } opts.lazyStart Configure if timer is initialized on + * instantiation or lazily + * @param { boolean } opts.immediateExecution Configure if callback should + * be executed before the first interval. + */ + constructor(opts = {}) { + this.cfg = { + callback: opts.callback, + startingInterval: opts.startingInterval, + maxInterval: opts.maxInterval, + hiddenInterval: opts.hiddenInterval, + incrementByFactorOf: opts.incrementByFactorOf, + lazyStart: opts.lazyStart, + immediateExecution: opts.immediateExecution, + }; + + this.state = { + intervalId: null, + currentInterval: this.cfg.startingInterval, + pageVisibility: 'visible', + }; + + this.initInterval(); + } - state.intervalId = window.setInterval(() => { - cfg.callback(); + /* public */ - if (this.getCurrentInterval() === cfg.maxInterval) { - return; - } + start() { + const cfg = this.cfg; + const state = this.state; - this.incrementInterval(); - this.resume(); - }, this.getCurrentInterval()); + if (cfg.immediateExecution) { + cfg.immediateExecution = false; + cfg.callback(); } - // cancel the existing timer, setting the currentInterval back to startingInterval - cancel() { - this.setCurrentInterval(this.cfg.startingInterval); - this.stopTimer(); - } + state.intervalId = window.setInterval(() => { + cfg.callback(); - onVisibilityHidden() { - if (this.cfg.hiddenInterval) { - this.setCurrentInterval(this.cfg.hiddenInterval); - this.resume(); - } else { - this.cancel(); + if (this.getCurrentInterval() === cfg.maxInterval) { + return; } - } - // start a timer, using the existing interval - resume() { - this.stopTimer(); // stop exsiting timer, in case timer was not previously stopped - this.start(); - } + this.incrementInterval(); + this.resume(); + }, this.getCurrentInterval()); + } - onVisibilityVisible() { - this.cancel(); - this.start(); - } + // cancel the existing timer, setting the currentInterval back to startingInterval + cancel() { + this.setCurrentInterval(this.cfg.startingInterval); + this.stopTimer(); + } - destroy() { + onVisibilityHidden() { + if (this.cfg.hiddenInterval) { + this.setCurrentInterval(this.cfg.hiddenInterval); + this.resume(); + } else { this.cancel(); - document.removeEventListener('visibilitychange', this.handleVisibilityChange); - $(document).off('visibilitychange').off('beforeunload'); } + } - /* private */ + // start a timer, using the existing interval + resume() { + this.stopTimer(); // stop exsiting timer, in case timer was not previously stopped + this.start(); + } - initInterval() { - const cfg = this.cfg; + onVisibilityVisible() { + this.cancel(); + this.start(); + } - if (!cfg.lazyStart) { - this.start(); - } + destroy() { + this.cancel(); + document.removeEventListener('visibilitychange', this.handleVisibilityChange); + $(document).off('visibilitychange').off('beforeunload'); + } - this.initVisibilityChangeHandling(); - this.initPageUnloadHandling(); - } + /* private */ - initVisibilityChangeHandling() { - // cancel interval when tab no longer shown (prevents cached pages from polling) - document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this)); - } + initInterval() { + const cfg = this.cfg; - initPageUnloadHandling() { - // TODO: Consider refactoring in light of turbolinks removal. - // prevent interval continuing after page change, when kept in cache by Turbolinks - $(document).on('beforeunload', () => this.cancel()); + if (!cfg.lazyStart) { + this.start(); } - handleVisibilityChange(e) { - this.state.pageVisibility = e.target.visibilityState; - const intervalAction = this.isPageVisible() ? - this.onVisibilityVisible : - this.onVisibilityHidden; + this.initVisibilityChangeHandling(); + this.initPageUnloadHandling(); + } - intervalAction.apply(this); - } + initVisibilityChangeHandling() { + // cancel interval when tab no longer shown (prevents cached pages from polling) + document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this)); + } - getCurrentInterval() { - return this.state.currentInterval; - } + initPageUnloadHandling() { + // TODO: Consider refactoring in light of turbolinks removal. + // prevent interval continuing after page change, when kept in cache by Turbolinks + $(document).on('beforeunload', () => this.cancel()); + } - setCurrentInterval(newInterval) { - this.state.currentInterval = newInterval; - } + handleVisibilityChange(e) { + this.state.pageVisibility = e.target.visibilityState; + const intervalAction = this.isPageVisible() ? + this.onVisibilityVisible : + this.onVisibilityHidden; - incrementInterval() { - const cfg = this.cfg; - const currentInterval = this.getCurrentInterval(); - if (cfg.hiddenInterval && !this.isPageVisible()) return; - let nextInterval = currentInterval * cfg.incrementByFactorOf; + intervalAction.apply(this); + } - if (nextInterval > cfg.maxInterval) { - nextInterval = cfg.maxInterval; - } + getCurrentInterval() { + return this.state.currentInterval; + } + + setCurrentInterval(newInterval) { + this.state.currentInterval = newInterval; + } + + incrementInterval() { + const cfg = this.cfg; + const currentInterval = this.getCurrentInterval(); + if (cfg.hiddenInterval && !this.isPageVisible()) return; + let nextInterval = currentInterval * cfg.incrementByFactorOf; - this.setCurrentInterval(nextInterval); + if (nextInterval > cfg.maxInterval) { + nextInterval = cfg.maxInterval; } - isPageVisible() { return this.state.pageVisibility === 'visible'; } + this.setCurrentInterval(nextInterval); + } - stopTimer() { - const state = this.state; + isPageVisible() { return this.state.pageVisibility === 'visible'; } - state.intervalId = window.clearInterval(state.intervalId); - } + stopTimer() { + const state = this.state; + + state.intervalId = window.clearInterval(state.intervalId); } - gl.SmartInterval = SmartInterval; -})(window.gl || (window.gl = {})); +} + +window.gl.SmartInterval = SmartInterval; diff --git a/app/assets/javascripts/snippets_list.js b/app/assets/javascripts/snippets_list.js index 2128007113f..3b6d999b1c3 100644 --- a/app/assets/javascripts/snippets_list.js +++ b/app/assets/javascripts/snippets_list.js @@ -1,13 +1,9 @@ -/* eslint-disable arrow-parens, no-param-reassign, space-before-function-paren, func-names, no-var, max-len */ +function SnippetsList() { + const $holder = $('.snippets-list-holder'); -(global => { - global.gl = global.gl || {}; + $holder.find('.pagination').on('ajax:success', (e, data) => { + $holder.replaceWith(data.html); + }); +} - gl.SnippetsList = function() { - var $holder = $('.snippets-list-holder'); - - $holder.find('.pagination').on('ajax:success', (e, data) => { - $holder.replaceWith(data.html); - }); - }; -})(window); +window.gl.SnippetsList = SnippetsList; diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index c75b44cc2fd..6d38124f1c1 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -1,30 +1,26 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, max-len */ /* global Flash */ -(function() { - this.Star = (function() { - function Star() { - $('.project-home-panel .toggle-star').on('ajax:success', function(e, data, status, xhr) { - var $starIcon, $starSpan, $this, toggleStar; - $this = $(this); - $starSpan = $this.find('span'); - $starIcon = $this.find('i'); - toggleStar = function(isStarred) { - $this.parent().find('.star-count').text(data.star_count); - if (isStarred) { - $starSpan.removeClass('starred').text('Star'); - $starIcon.removeClass('fa-star').addClass('fa-star-o'); - } else { - $starSpan.addClass('starred').text('Unstar'); - $starIcon.removeClass('fa-star-o').addClass('fa-star'); - } - }; - toggleStar($starSpan.hasClass('starred')); - }).on('ajax:error', function(e, xhr, status, error) { - new Flash('Star toggle failed. Try again later.', 'alert'); - }); - } - - return Star; - })(); -}).call(window); +export default class Star { + constructor() { + $('.project-home-panel .toggle-star').on('ajax:success', function(e, data, status, xhr) { + var $starIcon, $starSpan, $this, toggleStar; + $this = $(this); + $starSpan = $this.find('span'); + $starIcon = $this.find('i'); + toggleStar = function(isStarred) { + $this.parent().find('.star-count').text(data.star_count); + if (isStarred) { + $starSpan.removeClass('starred').text('Star'); + $starIcon.removeClass('fa-star').addClass('fa-star-o'); + } else { + $starSpan.addClass('starred').text('Unstar'); + $starIcon.removeClass('fa-star-o').addClass('fa-star'); + } + }; + toggleStar($starSpan.hasClass('starred')); + }).on('ajax:error', function(e, xhr, status, error) { + new Flash('Star toggle failed. Try again later.', 'alert'); + }); + } +} diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js index 5f9a3e00c22..bb4d68fcd49 100644 --- a/app/assets/javascripts/subscription.js +++ b/app/assets/javascripts/subscription.js @@ -1,47 +1,45 @@ -(() => { - class Subscription { - constructor(containerElm) { - this.containerElm = containerElm; +class Subscription { + constructor(containerElm) { + this.containerElm = containerElm; - const subscribeButton = containerElm.querySelector('.js-subscribe-button'); - if (subscribeButton) { - // remove class so we don't bind twice - subscribeButton.classList.remove('js-subscribe-button'); - subscribeButton.addEventListener('click', this.toggleSubscription.bind(this)); - } + const subscribeButton = containerElm.querySelector('.js-subscribe-button'); + if (subscribeButton) { + // remove class so we don't bind twice + subscribeButton.classList.remove('js-subscribe-button'); + subscribeButton.addEventListener('click', this.toggleSubscription.bind(this)); } + } - toggleSubscription(event) { - const button = event.currentTarget; - const buttonSpan = button.querySelector('span'); - if (!buttonSpan || button.classList.contains('disabled')) { - return; - } - button.classList.add('disabled'); + toggleSubscription(event) { + const button = event.currentTarget; + const buttonSpan = button.querySelector('span'); + if (!buttonSpan || button.classList.contains('disabled')) { + return; + } + button.classList.add('disabled'); - const isSubscribed = buttonSpan.innerHTML.trim().toLowerCase() !== 'subscribe'; - const toggleActionUrl = this.containerElm.dataset.url; + const isSubscribed = buttonSpan.innerHTML.trim().toLowerCase() !== 'subscribe'; + const toggleActionUrl = this.containerElm.dataset.url; - $.post(toggleActionUrl, () => { - button.classList.remove('disabled'); + $.post(toggleActionUrl, () => { + button.classList.remove('disabled'); - // hack to allow this to work with the issue boards Vue object - if (document.querySelector('html').classList.contains('issue-boards-page')) { - gl.issueBoards.boardStoreIssueSet( - 'subscribed', - !gl.issueBoards.BoardsStore.detail.issue.subscribed, - ); - } else { - buttonSpan.innerHTML = isSubscribed ? 'Subscribe' : 'Unsubscribe'; - } - }); - } + // hack to allow this to work with the issue boards Vue object + if (document.querySelector('html').classList.contains('issue-boards-page')) { + gl.issueBoards.boardStoreIssueSet( + 'subscribed', + !gl.issueBoards.BoardsStore.detail.issue.subscribed, + ); + } else { + buttonSpan.innerHTML = isSubscribed ? 'Subscribe' : 'Unsubscribe'; + } + }); + } - static bindAll(selector) { - [].forEach.call(document.querySelectorAll(selector), elm => new Subscription(elm)); - } + static bindAll(selector) { + [].forEach.call(document.querySelectorAll(selector), elm => new Subscription(elm)); } +} - window.gl = window.gl || {}; - window.gl.Subscription = Subscription; -})(); +window.gl = window.gl || {}; +window.gl.Subscription = Subscription; diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js index 0cd591c7320..37e39ce5477 100644 --- a/app/assets/javascripts/subscription_select.js +++ b/app/assets/javascripts/subscription_select.js @@ -1,34 +1,33 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, max-len */ -(function() { - this.SubscriptionSelect = (function() { - function SubscriptionSelect() { - $('.js-subscription-event').each(function(i, el) { - var fieldName; - fieldName = $(el).data("field-name"); - return $(el).glDropdown({ - selectable: true, - fieldName: fieldName, - toggleLabel: (function(_this) { - return function(selected, el, instance) { - var $item, label; - label = 'Subscription'; - $item = instance.dropdown.find('.is-active'); - if ($item.length) { - label = $item.text(); - } - return label; - }; - })(this), - clicked: function(options) { - return options.e.preventDefault(); - }, - id: function(obj, el) { - return $(el).data("id"); - } - }); + +class SubscriptionSelect { + constructor() { + $('.js-subscription-event').each(function(i, el) { + var fieldName; + fieldName = $(el).data("field-name"); + return $(el).glDropdown({ + selectable: true, + fieldName: fieldName, + toggleLabel: (function(_this) { + return function(selected, el, instance) { + var $item, label; + label = 'Subscription'; + $item = instance.dropdown.find('.is-active'); + if ($item.length) { + label = $item.text(); + } + return label; + }; + })(this), + clicked: function(options) { + return options.e.preventDefault(); + }, + id: function(obj, el) { + return $(el).data("id"); + } }); - } + }); + } +} - return SubscriptionSelect; - })(); -}).call(window); +window.SubscriptionSelect = SubscriptionSelect; diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js index 7c063fae045..662d6b36c16 100644 --- a/app/assets/javascripts/syntax_highlight.js +++ b/app/assets/javascripts/syntax_highlight.js @@ -9,19 +9,18 @@ // // <div class="js-syntax-highlight"></div> // -(function() { - $.fn.syntaxHighlight = function() { - var $children; - if ($(this).hasClass('js-syntax-highlight')) { - // Given the element itself, apply highlighting - return $(this).addClass(gon.user_color_scheme); - } else { - // Given a parent element, recurse to any of its applicable children - $children = $(this).find('.js-syntax-highlight'); - if ($children.length) { - return $children.syntaxHighlight(); - } +$.fn.syntaxHighlight = function() { + var $children; + + if ($(this).hasClass('js-syntax-highlight')) { + // Given the element itself, apply highlighting + return $(this).addClass(gon.user_color_scheme); + } else { + // Given a parent element, recurse to any of its applicable children + $children = $(this).find('.js-syntax-highlight'); + if ($children.length) { + return $children.syntaxHighlight(); } - }; -}).call(window); + } +}; diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index 419c458ff34..c39f569da5e 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -2,7 +2,7 @@ import 'deckar01-task_list'; -class TaskList { +export default class TaskList { constructor(options = {}) { this.selector = options.selector; this.dataType = options.dataType; @@ -48,6 +48,3 @@ class TaskList { }); } } - -window.gl = window.gl || {}; -window.gl.TaskList = TaskList; diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js index 7230946b484..cd305631c10 100644 --- a/app/assets/javascripts/todos.js +++ b/app/assets/javascripts/todos.js @@ -2,7 +2,7 @@ import UsersSelect from './users_select'; -class Todos { +export default class Todos { constructor() { this.initFilters(); this.bindEvents(); @@ -159,6 +159,3 @@ class Todos { } } } - -window.gl = window.gl || {}; -gl.Todos = Todos; diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js index 76a821c7a17..7777ed1c3dc 100644 --- a/app/assets/javascripts/tree.js +++ b/app/assets/javascripts/tree.js @@ -1,68 +1,64 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, max-len */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, class-methods-use-this */ -(function() { - this.TreeView = (function() { - function TreeView() { - this.initKeyNav(); - // Code browser tree slider - // Make the entire tree-item row clickable, but not if clicking another link (like a commit message) - $(".tree-content-holder .tree-item").on('click', function(e) { - var $clickedEl, path; - $clickedEl = $(e.target); - path = $('.tree-item-file-name a', this).attr('href'); - if (!$clickedEl.is('a') && !$clickedEl.is('.str-truncated')) { - if (e.metaKey || e.which === 2) { - e.preventDefault(); - return window.open(path, '_blank'); - } else { - return gl.utils.visitUrl(path); - } +export default class TreeView { + constructor() { + this.initKeyNav(); + // Code browser tree slider + // Make the entire tree-item row clickable, but not if clicking another link (like a commit message) + $(".tree-content-holder .tree-item").on('click', function(e) { + var $clickedEl, path; + $clickedEl = $(e.target); + path = $('.tree-item-file-name a', this).attr('href'); + if (!$clickedEl.is('a') && !$clickedEl.is('.str-truncated')) { + if (e.metaKey || e.which === 2) { + e.preventDefault(); + return window.open(path, '_blank'); + } else { + return gl.utils.visitUrl(path); } - }); - // Show the "Loading commit data" for only the first element - $('span.log_loading:first').removeClass('hide'); - } + } + }); + // Show the "Loading commit data" for only the first element + $('span.log_loading:first').removeClass('hide'); + } - TreeView.prototype.initKeyNav = function() { - var li, liSelected; - li = $("tr.tree-item"); - liSelected = null; - return $('body').keydown(function(e) { - var next, path; - if ($("input:focus").length > 0 && (e.which === 38 || e.which === 40)) { - return false; - } - if (e.which === 40) { - if (liSelected) { - next = liSelected.next(); - if (next.length > 0) { - liSelected.removeClass("selected"); - liSelected = next.addClass("selected"); - } - } else { - liSelected = li.eq(0).addClass("selected"); - } - return $(liSelected).focus(); - } else if (e.which === 38) { - if (liSelected) { - next = liSelected.prev(); - if (next.length > 0) { - liSelected.removeClass("selected"); - liSelected = next.addClass("selected"); - } - } else { - liSelected = li.last().addClass("selected"); + initKeyNav() { + var li, liSelected; + li = $("tr.tree-item"); + liSelected = null; + return $('body').keydown(function(e) { + var next, path; + if ($("input:focus").length > 0 && (e.which === 38 || e.which === 40)) { + return false; + } + if (e.which === 40) { + if (liSelected) { + next = liSelected.next(); + if (next.length > 0) { + liSelected.removeClass("selected"); + liSelected = next.addClass("selected"); } - return $(liSelected).focus(); - } else if (e.which === 13) { - path = $('.tree-item.selected .tree-item-file-name a').attr('href'); - if (path) { - return gl.utils.visitUrl(path); + } else { + liSelected = li.eq(0).addClass("selected"); + } + return $(liSelected).focus(); + } else if (e.which === 38) { + if (liSelected) { + next = liSelected.prev(); + if (next.length > 0) { + liSelected.removeClass("selected"); + liSelected = next.addClass("selected"); } + } else { + liSelected = li.last().addClass("selected"); } - }); - }; - - return TreeView; - })(); -}).call(window); + return $(liSelected).focus(); + } else if (e.which === 13) { + path = $('.tree-item.selected .tree-item-file-name a').attr('href'); + if (path) { + return gl.utils.visitUrl(path); + } + } + }); + } +} diff --git a/app/assets/javascripts/usage_ping.js b/app/assets/javascripts/usage_ping.js index fd3af7d7ab6..2389056bd02 100644 --- a/app/assets/javascripts/usage_ping.js +++ b/app/assets/javascripts/usage_ping.js @@ -1,4 +1,4 @@ -function UsagePing() { +export default function UsagePing() { const usageDataUrl = $('.usage-data').data('endpoint'); $.ajax({ @@ -10,6 +10,3 @@ function UsagePing() { }, }); } - -window.gl = window.gl || {}; -window.gl.UsagePing = UsagePing; diff --git a/app/assets/javascripts/user.js b/app/assets/javascripts/user.js deleted file mode 100644 index 19c9efe7fbd..00000000000 --- a/app/assets/javascripts/user.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign */ - -import Cookies from 'js-cookie'; - -((global) => { - global.User = class { - constructor({ action }) { - this.action = action; - this.placeProfileAvatarsToTop(); - this.initTabs(); - this.hideProjectLimitMessage(); - } - - placeProfileAvatarsToTop() { - $('.profile-groups-avatars').tooltip({ - placement: 'top' - }); - } - - initTabs() { - return new global.UserTabs({ - parentEl: '.user-profile', - action: this.action - }); - } - - hideProjectLimitMessage() { - $('.hide-project-limit-message').on('click', e => { - e.preventDefault(); - Cookies.set('hide_project_limit_message', 'false'); - $(this).parents('.project-limit-message').remove(); - }); - } - }; -})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/user_tabs.js b/app/assets/javascripts/user_tabs.js deleted file mode 100644 index ce7eb76dc71..00000000000 --- a/app/assets/javascripts/user_tabs.js +++ /dev/null @@ -1,175 +0,0 @@ -/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign, class-methods-use-this */ - -/* -UserTabs - -Handles persisting and restoring the current tab selection and lazily-loading -content on the Users#show page. - -### Example Markup - - <ul class="nav-links"> - <li class="activity-tab active"> - <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username"> - Activity - </a> - </li> - <li class="groups-tab"> - <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups"> - Groups - </a> - </li> - <li class="contributed-tab"> - <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed"> - Contributed projects - </a> - </li> - <li class="projects-tab"> - <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects"> - Personal projects - </a> - </li> - <li class="snippets-tab"> - <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets"> - </a> - </li> - </ul> - - <div class="tab-content"> - <div class="tab-pane" id="activity"> - Activity Content - </div> - <div class="tab-pane" id="groups"> - Groups Content - </div> - <div class="tab-pane" id="contributed"> - Contributed projects content - </div> - <div class="tab-pane" id="projects"> - Projects content - </div> - <div class="tab-pane" id="snippets"> - Snippets content - </div> - </div> - - <div class="loading-status"> - <div class="loading"> - Loading Animation - </div> - </div> -*/ -((global) => { - class UserTabs { - constructor ({ defaultAction, action, parentEl }) { - this.loaded = {}; - this.defaultAction = defaultAction || 'activity'; - this.action = action || this.defaultAction; - this.$parentEl = $(parentEl) || $(document); - this._location = window.location; - this.$parentEl.find('.nav-links a') - .each((i, navLink) => { - this.loaded[$(navLink).attr('data-action')] = false; - }); - this.actions = Object.keys(this.loaded); - this.bindEvents(); - - if (this.action === 'show') { - this.action = this.defaultAction; - } - - this.activateTab(this.action); - } - - bindEvents() { - this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this); - - this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') - .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)); - - this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper); - } - - changeProjectsPage(e) { - e.preventDefault(); - - $('.tab-pane.active').empty(); - const endpoint = $(e.target).attr('href'); - this.loadTab(this.getCurrentAction(), endpoint); - } - - tabShown(event) { - const $target = $(event.target); - const action = $target.data('action'); - const source = $target.attr('href'); - const endpoint = $target.data('endpoint'); - this.setTab(action, endpoint); - return this.setCurrentAction(source); - } - - activateTab(action) { - return this.$parentEl.find(`.nav-links .js-${action}-tab a`) - .tab('show'); - } - - setTab(action, endpoint) { - if (this.loaded[action]) { - return; - } - if (action === 'activity') { - this.loadActivities(); - } - - const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; - if (loadableActions.indexOf(action) > -1) { - return this.loadTab(action, endpoint); - } - } - - loadTab(action, endpoint) { - return $.ajax({ - beforeSend: () => this.toggleLoading(true), - complete: () => this.toggleLoading(false), - dataType: 'json', - type: 'GET', - url: endpoint, - success: (data) => { - const tabSelector = `div#${action}`; - this.$parentEl.find(tabSelector).html(data.html); - this.loaded[action] = true; - return gl.utils.localTimeAgo($('.js-timeago', tabSelector)); - } - }); - } - - loadActivities() { - if (this.loaded['activity']) { - return; - } - const $calendarWrap = this.$parentEl.find('.user-calendar'); - $calendarWrap.load($calendarWrap.data('href')); - new gl.Activities(); - return this.loaded['activity'] = true; - } - - toggleLoading(status) { - return this.$parentEl.find('.loading-status .loading') - .toggle(status); - } - - setCurrentAction(source) { - let new_state = source; - new_state = new_state.replace(/\/+$/, ''); - new_state += this._location.search + this._location.hash; - history.replaceState({ - url: new_state - }, document.title, new_state); - return new_state; - } - - getCurrentAction() { - return this.$parentEl.find('.nav-links .active a').data('action'); - } - } - global.UserTabs = UserTabs; -})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/username_validator.js b/app/assets/javascripts/username_validator.js index 137cefa3b8e..a348d69153c 100644 --- a/app/assets/javascripts/username_validator.js +++ b/app/assets/javascripts/username_validator.js @@ -1,135 +1,131 @@ /* eslint-disable comma-dangle, consistent-return, class-methods-use-this, arrow-parens, no-param-reassign, max-len */ -((global) => { - const debounceTimeoutDuration = 1000; - const invalidInputClass = 'gl-field-error-outline'; - const successInputClass = 'gl-field-success-outline'; - const unavailableMessageSelector = '.username .validation-error'; - const successMessageSelector = '.username .validation-success'; - const pendingMessageSelector = '.username .validation-pending'; - const invalidMessageSelector = '.username .gl-field-error'; - - class UsernameValidator { - constructor() { - this.inputElement = $('#new_user_username'); - this.inputDomElement = this.inputElement.get(0); - this.state = { - available: false, - valid: false, - pending: false, - empty: true - }; - - const debounceTimeout = _.debounce((username) => { - this.validateUsername(username); - }, debounceTimeoutDuration); - - this.inputElement.on('keyup.username_check', () => { - const username = this.inputElement.val(); - - this.state.valid = this.inputDomElement.validity.valid; - this.state.empty = !username.length; - - if (this.state.valid) { - return debounceTimeout(username); - } - - this.renderState(); - }); - - // Override generic field validation - this.inputElement.on('invalid', this.interceptInvalid.bind(this)); - } - - renderState() { - // Clear all state - this.clearFieldValidationState(); +const debounceTimeoutDuration = 1000; +const invalidInputClass = 'gl-field-error-outline'; +const successInputClass = 'gl-field-success-outline'; +const unavailableMessageSelector = '.username .validation-error'; +const successMessageSelector = '.username .validation-success'; +const pendingMessageSelector = '.username .validation-pending'; +const invalidMessageSelector = '.username .gl-field-error'; + +export default class UsernameValidator { + constructor() { + this.inputElement = $('#new_user_username'); + this.inputDomElement = this.inputElement.get(0); + this.state = { + available: false, + valid: false, + pending: false, + empty: true + }; + + const debounceTimeout = _.debounce((username) => { + this.validateUsername(username); + }, debounceTimeoutDuration); + + this.inputElement.on('keyup.username_check', () => { + const username = this.inputElement.val(); + + this.state.valid = this.inputDomElement.validity.valid; + this.state.empty = !username.length; - if (this.state.valid && this.state.available) { - return this.setSuccessState(); + if (this.state.valid) { + return debounceTimeout(username); } - if (this.state.empty) { - return this.clearFieldValidationState(); - } + this.renderState(); + }); - if (this.state.pending) { - return this.setPendingState(); - } + // Override generic field validation + this.inputElement.on('invalid', this.interceptInvalid.bind(this)); + } - if (!this.state.available) { - return this.setUnavailableState(); - } + renderState() { + // Clear all state + this.clearFieldValidationState(); - if (!this.state.valid) { - return this.setInvalidState(); - } + if (this.state.valid && this.state.available) { + return this.setSuccessState(); } - interceptInvalid(event) { - event.preventDefault(); - event.stopPropagation(); + if (this.state.empty) { + return this.clearFieldValidationState(); } - validateUsername(username) { - if (this.state.valid) { - this.state.pending = true; - this.state.available = false; - this.renderState(); - return $.ajax({ - type: 'GET', - url: `${gon.relative_url_root}/users/${username}/exists`, - dataType: 'json', - success: (res) => this.setAvailabilityState(res.exists) - }); - } + if (this.state.pending) { + return this.setPendingState(); } - setAvailabilityState(usernameTaken) { - if (usernameTaken) { - this.state.valid = false; - this.state.available = false; - } else { - this.state.available = true; - } - this.state.pending = false; - this.renderState(); + if (!this.state.available) { + return this.setUnavailableState(); } - clearFieldValidationState() { - this.inputElement.siblings('p').hide(); - - this.inputElement.removeClass(invalidInputClass) - .removeClass(successInputClass); + if (!this.state.valid) { + return this.setInvalidState(); } + } - setUnavailableState() { - const $usernameUnavailableMessage = this.inputElement.siblings(unavailableMessageSelector); - this.inputElement.addClass(invalidInputClass).removeClass(successInputClass); - $usernameUnavailableMessage.show(); - } + interceptInvalid(event) { + event.preventDefault(); + event.stopPropagation(); + } - setSuccessState() { - const $usernameSuccessMessage = this.inputElement.siblings(successMessageSelector); - this.inputElement.addClass(successInputClass).removeClass(invalidInputClass); - $usernameSuccessMessage.show(); + validateUsername(username) { + if (this.state.valid) { + this.state.pending = true; + this.state.available = false; + this.renderState(); + return $.ajax({ + type: 'GET', + url: `${gon.relative_url_root}/users/${username}/exists`, + dataType: 'json', + success: (res) => this.setAvailabilityState(res.exists) + }); } + } - setPendingState() { - const $usernamePendingMessage = $(pendingMessageSelector); - if (this.state.pending) { - $usernamePendingMessage.show(); - } else { - $usernamePendingMessage.hide(); - } + setAvailabilityState(usernameTaken) { + if (usernameTaken) { + this.state.valid = false; + this.state.available = false; + } else { + this.state.available = true; } + this.state.pending = false; + this.renderState(); + } - setInvalidState() { - const $inputErrorMessage = $(invalidMessageSelector); - this.inputElement.addClass(invalidInputClass).removeClass(successInputClass); - $inputErrorMessage.show(); + clearFieldValidationState() { + this.inputElement.siblings('p').hide(); + + this.inputElement.removeClass(invalidInputClass) + .removeClass(successInputClass); + } + + setUnavailableState() { + const $usernameUnavailableMessage = this.inputElement.siblings(unavailableMessageSelector); + this.inputElement.addClass(invalidInputClass).removeClass(successInputClass); + $usernameUnavailableMessage.show(); + } + + setSuccessState() { + const $usernameSuccessMessage = this.inputElement.siblings(successMessageSelector); + this.inputElement.addClass(successInputClass).removeClass(invalidInputClass); + $usernameSuccessMessage.show(); + } + + setPendingState() { + const $usernamePendingMessage = $(pendingMessageSelector); + if (this.state.pending) { + $usernamePendingMessage.show(); + } else { + $usernamePendingMessage.hide(); } } - global.UsernameValidator = UsernameValidator; -})(window); + setInvalidState() { + const $inputErrorMessage = $(invalidMessageSelector); + this.inputElement.addClass(invalidInputClass).removeClass(successInputClass); + $inputErrorMessage.show(); + } +} diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js new file mode 100644 index 00000000000..b7f50cfd083 --- /dev/null +++ b/app/assets/javascripts/users/activity_calendar.js @@ -0,0 +1,227 @@ +/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ + +import d3 from 'd3'; + +export default class ActivityCalendar { + constructor(timestamps, calendar_activities_path) { + this.calendar_activities_path = calendar_activities_path; + this.clickDay = this.clickDay.bind(this); + this.currentSelectedDate = ''; + this.daySpace = 1; + this.daySize = 15; + this.daySizeWithSpace = this.daySize + (this.daySpace * 2); + this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + this.months = []; + // Loop through the timestamps to create a group of objects + // The group of objects will be grouped based on the day of the week they are + this.timestampsTmp = []; + var group = 0; + + var today = new Date(); + today.setHours(0, 0, 0, 0, 0); + + var oneYearAgo = new Date(today); + oneYearAgo.setFullYear(today.getFullYear() - 1); + + var days = gl.utils.getDayDifference(oneYearAgo, today); + + for (var i = 0; i <= days; i += 1) { + var date = new Date(oneYearAgo); + date.setDate(date.getDate() + i); + + var day = date.getDay(); + var count = timestamps[date.format('yyyy-mm-dd')]; + + // Create a new group array if this is the first day of the week + // or if is first object + if ((day === 0 && i !== 0) || i === 0) { + this.timestampsTmp.push([]); + group += 1; + } + + var innerArray = this.timestampsTmp[group - 1]; + // Push to the inner array the values that will be used to render map + innerArray.push({ + count: count || 0, + date: date, + day: day + }); + } + + // Init color functions + this.colorKey = this.initColorKey(); + this.color = this.initColor(); + // Init the svg element + this.renderSvg(group); + this.renderDays(); + this.renderMonths(); + this.renderDayTitles(); + this.renderKey(); + this.initTooltips(); + } + + // Add extra padding for the last month label if it is also the last column + getExtraWidthPadding(group) { + var extraWidthPadding = 0; + var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); + var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); + + if (lastColMonth != secondLastColMonth) { + extraWidthPadding = 3; + } + + return extraWidthPadding; + } + + renderSvg(group) { + var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group); + return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar'); + } + + renderDays() { + return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) { + return function(group, i) { + _.each(group, function(stamp, a) { + var lastMonth, lastMonthX, month, x; + if (a === 0 && stamp.day === 0) { + month = stamp.date.getMonth(); + x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace; + lastMonth = _.last(_this.months); + if (lastMonth != null) { + lastMonthX = lastMonth.x; + } + if (lastMonth == null) { + return _this.months.push({ + month: month, + x: x + }); + } else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) { + return _this.months.push({ + month: month, + x: x + }); + } + } + }); + return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)"; + }; + })(this)).selectAll('rect').data(function(stamp) { + return stamp; + }).enter().append('rect').attr('x', '0').attr('y', (function(_this) { + return function(stamp, i) { + return _this.daySizeWithSpace * stamp.day; + }; + })(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) { + return function(stamp) { + var contribText, date, dateText; + date = new Date(stamp.date); + contribText = 'No contributions'; + if (stamp.count > 0) { + contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : ''); + } + dateText = date.format('mmm d, yyyy'); + return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText; + }; + })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) { + return function(stamp) { + if (stamp.count !== 0) { + return _this.color(Math.min(stamp.count, 40)); + } else { + return '#ededed'; + } + }; + })(this)).attr('data-container', 'body').on('click', this.clickDay); + } + + renderDayTitles() { + var days; + days = [ + { + text: 'M', + y: 29 + (this.daySizeWithSpace * 1) + }, { + text: 'W', + y: 29 + (this.daySizeWithSpace * 3) + }, { + text: 'F', + y: 29 + (this.daySizeWithSpace * 5) + } + ]; + return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) { + return day.y; + }).text(function(day) { + return day.text; + }).attr('class', 'user-contrib-text'); + } + + renderMonths() { + return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) { + return date.x; + }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) { + return function(date) { + return _this.monthNames[date.month]; + }; + })(this)); + } + + renderKey() { + const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions']; + const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; + + this.svg.append('g') + .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`) + .selectAll('rect') + .data(keyColors) + .enter() + .append('rect') + .attr('width', this.daySize) + .attr('height', this.daySize) + .attr('x', (color, i) => this.daySizeWithSpace * i) + .attr('y', 0) + .attr('fill', color => color) + .attr('class', 'js-tooltip') + .attr('title', (color, i) => keyValues[i]) + .attr('data-container', 'body'); + } + + initColor() { + var colorRange; + colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; + return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange); + } + + initColorKey() { + return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); + } + + clickDay(stamp) { + var formatted_date; + if (this.currentSelectedDate !== stamp.date) { + this.currentSelectedDate = stamp.date; + formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate(); + return $.ajax({ + url: this.calendar_activities_path, + data: { + date: formatted_date + }, + cache: false, + dataType: 'html', + beforeSend: function() { + return $('.user-calendar-activities').html('<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>'); + }, + success: function(data) { + return $('.user-calendar-activities').html(data); + } + }); + } else { + this.currentSelectedDate = ''; + return $('.user-calendar-activities').html(''); + } + } + + initTooltips() { + return $('.js-contrib-calendar .js-tooltip').tooltip({ + html: true + }); + } +} diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js deleted file mode 100644 index b11f691e424..00000000000 --- a/app/assets/javascripts/users/calendar.js +++ /dev/null @@ -1,231 +0,0 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len */ - -import d3 from 'd3'; - -(function() { - this.Calendar = (function() { - function Calendar(timestamps, calendar_activities_path) { - this.calendar_activities_path = calendar_activities_path; - this.clickDay = this.clickDay.bind(this); - this.currentSelectedDate = ''; - this.daySpace = 1; - this.daySize = 15; - this.daySizeWithSpace = this.daySize + (this.daySpace * 2); - this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - this.months = []; - // Loop through the timestamps to create a group of objects - // The group of objects will be grouped based on the day of the week they are - this.timestampsTmp = []; - var group = 0; - - var today = new Date(); - today.setHours(0, 0, 0, 0, 0); - - var oneYearAgo = new Date(today); - oneYearAgo.setFullYear(today.getFullYear() - 1); - - var days = gl.utils.getDayDifference(oneYearAgo, today); - - for (var i = 0; i <= days; i += 1) { - var date = new Date(oneYearAgo); - date.setDate(date.getDate() + i); - - var day = date.getDay(); - var count = timestamps[date.format('yyyy-mm-dd')]; - - // Create a new group array if this is the first day of the week - // or if is first object - if ((day === 0 && i !== 0) || i === 0) { - this.timestampsTmp.push([]); - group += 1; - } - - var innerArray = this.timestampsTmp[group - 1]; - // Push to the inner array the values that will be used to render map - innerArray.push({ - count: count || 0, - date: date, - day: day - }); - } - - // Init color functions - this.colorKey = this.initColorKey(); - this.color = this.initColor(); - // Init the svg element - this.renderSvg(group); - this.renderDays(); - this.renderMonths(); - this.renderDayTitles(); - this.renderKey(); - this.initTooltips(); - } - - // Add extra padding for the last month label if it is also the last column - Calendar.prototype.getExtraWidthPadding = function(group) { - var extraWidthPadding = 0; - var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); - var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); - - if (lastColMonth != secondLastColMonth) { - extraWidthPadding = 3; - } - - return extraWidthPadding; - }; - - Calendar.prototype.renderSvg = function(group) { - var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group); - return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar'); - }; - - Calendar.prototype.renderDays = function() { - return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) { - return function(group, i) { - _.each(group, function(stamp, a) { - var lastMonth, lastMonthX, month, x; - if (a === 0 && stamp.day === 0) { - month = stamp.date.getMonth(); - x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace; - lastMonth = _.last(_this.months); - if (lastMonth != null) { - lastMonthX = lastMonth.x; - } - if (lastMonth == null) { - return _this.months.push({ - month: month, - x: x - }); - } else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) { - return _this.months.push({ - month: month, - x: x - }); - } - } - }); - return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)"; - }; - })(this)).selectAll('rect').data(function(stamp) { - return stamp; - }).enter().append('rect').attr('x', '0').attr('y', (function(_this) { - return function(stamp, i) { - return _this.daySizeWithSpace * stamp.day; - }; - })(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) { - return function(stamp) { - var contribText, date, dateText; - date = new Date(stamp.date); - contribText = 'No contributions'; - if (stamp.count > 0) { - contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : ''); - } - dateText = date.format('mmm d, yyyy'); - return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText; - }; - })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) { - return function(stamp) { - if (stamp.count !== 0) { - return _this.color(Math.min(stamp.count, 40)); - } else { - return '#ededed'; - } - }; - })(this)).attr('data-container', 'body').on('click', this.clickDay); - }; - - Calendar.prototype.renderDayTitles = function() { - var days; - days = [ - { - text: 'M', - y: 29 + (this.daySizeWithSpace * 1) - }, { - text: 'W', - y: 29 + (this.daySizeWithSpace * 3) - }, { - text: 'F', - y: 29 + (this.daySizeWithSpace * 5) - } - ]; - return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) { - return day.y; - }).text(function(day) { - return day.text; - }).attr('class', 'user-contrib-text'); - }; - - Calendar.prototype.renderMonths = function() { - return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) { - return date.x; - }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) { - return function(date) { - return _this.monthNames[date.month]; - }; - })(this)); - }; - - Calendar.prototype.renderKey = function() { - const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions']; - const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; - - this.svg.append('g') - .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`) - .selectAll('rect') - .data(keyColors) - .enter() - .append('rect') - .attr('width', this.daySize) - .attr('height', this.daySize) - .attr('x', (color, i) => this.daySizeWithSpace * i) - .attr('y', 0) - .attr('fill', color => color) - .attr('class', 'js-tooltip') - .attr('title', (color, i) => keyValues[i]) - .attr('data-container', 'body'); - }; - - Calendar.prototype.initColor = function() { - var colorRange; - colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; - return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange); - }; - - Calendar.prototype.initColorKey = function() { - return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); - }; - - Calendar.prototype.clickDay = function(stamp) { - var formatted_date; - if (this.currentSelectedDate !== stamp.date) { - this.currentSelectedDate = stamp.date; - formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate(); - return $.ajax({ - url: this.calendar_activities_path, - data: { - date: formatted_date - }, - cache: false, - dataType: 'html', - beforeSend: function() { - return $('.user-calendar-activities').html('<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>'); - }, - success: function(data) { - return $('.user-calendar-activities').html(data); - } - }); - } else { - this.currentSelectedDate = ''; - return $('.user-calendar-activities').html(''); - } - }; - - Calendar.prototype.initTooltips = function() { - return $('.js-contrib-calendar .js-tooltip').tooltip({ - html: true - }); - }; - - return Calendar; - })(); -}).call(window); diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/users/index.js new file mode 100644 index 00000000000..ecd8e09161e --- /dev/null +++ b/app/assets/javascripts/users/index.js @@ -0,0 +1,7 @@ +import ActivityCalendar from './activity_calendar'; +import User from './user'; + +// use legacy exports until embedded javascript is refactored +window.Calendar = ActivityCalendar; +window.gl = window.gl || {}; +window.gl.User = User; diff --git a/app/assets/javascripts/users/user.js b/app/assets/javascripts/users/user.js new file mode 100644 index 00000000000..0b0a3e1afb4 --- /dev/null +++ b/app/assets/javascripts/users/user.js @@ -0,0 +1,34 @@ +/* eslint-disable class-methods-use-this */ + +import Cookies from 'js-cookie'; +import UserTabs from './user_tabs'; + +export default class User { + constructor({ action }) { + this.action = action; + this.placeProfileAvatarsToTop(); + this.initTabs(); + this.hideProjectLimitMessage(); + } + + placeProfileAvatarsToTop() { + $('.profile-groups-avatars').tooltip({ + placement: 'top', + }); + } + + initTabs() { + return new UserTabs({ + parentEl: '.user-profile', + action: this.action, + }); + } + + hideProjectLimitMessage() { + $('.hide-project-limit-message').on('click', (e) => { + e.preventDefault(); + Cookies.set('hide_project_limit_message', 'false'); + $(this).parents('.project-limit-message').remove(); + }); + } +} diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js new file mode 100644 index 00000000000..f8e23c8624d --- /dev/null +++ b/app/assets/javascripts/users/user_tabs.js @@ -0,0 +1,173 @@ +/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign, class-methods-use-this */ + +/* +UserTabs + +Handles persisting and restoring the current tab selection and lazily-loading +content on the Users#show page. + +### Example Markup + + <ul class="nav-links"> + <li class="activity-tab active"> + <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username"> + Activity + </a> + </li> + <li class="groups-tab"> + <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups"> + Groups + </a> + </li> + <li class="contributed-tab"> + <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed"> + Contributed projects + </a> + </li> + <li class="projects-tab"> + <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects"> + Personal projects + </a> + </li> + <li class="snippets-tab"> + <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets"> + </a> + </li> + </ul> + + <div class="tab-content"> + <div class="tab-pane" id="activity"> + Activity Content + </div> + <div class="tab-pane" id="groups"> + Groups Content + </div> + <div class="tab-pane" id="contributed"> + Contributed projects content + </div> + <div class="tab-pane" id="projects"> + Projects content + </div> + <div class="tab-pane" id="snippets"> + Snippets content + </div> + </div> + + <div class="loading-status"> + <div class="loading"> + Loading Animation + </div> + </div> +*/ + +export default class UserTabs { + constructor ({ defaultAction, action, parentEl }) { + this.loaded = {}; + this.defaultAction = defaultAction || 'activity'; + this.action = action || this.defaultAction; + this.$parentEl = $(parentEl) || $(document); + this._location = window.location; + this.$parentEl.find('.nav-links a') + .each((i, navLink) => { + this.loaded[$(navLink).attr('data-action')] = false; + }); + this.actions = Object.keys(this.loaded); + this.bindEvents(); + + if (this.action === 'show') { + this.action = this.defaultAction; + } + + this.activateTab(this.action); + } + + bindEvents() { + this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this); + + this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') + .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)); + + this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper); + } + + changeProjectsPage(e) { + e.preventDefault(); + + $('.tab-pane.active').empty(); + const endpoint = $(e.target).attr('href'); + this.loadTab(this.getCurrentAction(), endpoint); + } + + tabShown(event) { + const $target = $(event.target); + const action = $target.data('action'); + const source = $target.attr('href'); + const endpoint = $target.data('endpoint'); + this.setTab(action, endpoint); + return this.setCurrentAction(source); + } + + activateTab(action) { + return this.$parentEl.find(`.nav-links .js-${action}-tab a`) + .tab('show'); + } + + setTab(action, endpoint) { + if (this.loaded[action]) { + return; + } + if (action === 'activity') { + this.loadActivities(); + } + + const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; + if (loadableActions.indexOf(action) > -1) { + return this.loadTab(action, endpoint); + } + } + + loadTab(action, endpoint) { + return $.ajax({ + beforeSend: () => this.toggleLoading(true), + complete: () => this.toggleLoading(false), + dataType: 'json', + type: 'GET', + url: endpoint, + success: (data) => { + const tabSelector = `div#${action}`; + this.$parentEl.find(tabSelector).html(data.html); + this.loaded[action] = true; + return gl.utils.localTimeAgo($('.js-timeago', tabSelector)); + } + }); + } + + loadActivities() { + if (this.loaded['activity']) { + return; + } + const $calendarWrap = this.$parentEl.find('.user-calendar'); + $calendarWrap.load($calendarWrap.data('href')); + new gl.Activities(); + return this.loaded['activity'] = true; + } + + toggleLoading(status) { + return this.$parentEl.find('.loading-status .loading') + .toggle(status); + } + + setCurrentAction(source) { + let new_state = source; + new_state = new_state.replace(/\/+$/, ''); + new_state += this._location.search + this._location.hash; + history.replaceState({ + url: new_state + }, document.title, new_state); + return new_state; + } + + getCurrentAction() { + return this.$parentEl.find('.nav-links .active a').data('action'); + } +} diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js deleted file mode 100644 index a38ce4eb25e..00000000000 --- a/app/assets/javascripts/users/users_bundle.js +++ /dev/null @@ -1 +0,0 @@ -import './calendar'; diff --git a/app/assets/javascripts/version_check_image.js b/app/assets/javascripts/version_check_image.js index 88ba991af47..ec515e892c6 100644 --- a/app/assets/javascripts/version_check_image.js +++ b/app/assets/javascripts/version_check_image.js @@ -3,6 +3,3 @@ export default class VersionCheckImage { imageElement.off('error').on('error', () => imageElement.hide()); } } - -window.gl = window.gl || {}; -gl.VersionCheckImage = VersionCheckImage; diff --git a/app/assets/javascripts/visibility_select.js b/app/assets/javascripts/visibility_select.js index f712d7ba930..0c928d0d5f6 100644 --- a/app/assets/javascripts/visibility_select.js +++ b/app/assets/javascripts/visibility_select.js @@ -1,27 +1,21 @@ -(() => { - const gl = window.gl || (window.gl = {}); - - class VisibilitySelect { - constructor(container) { - if (!container) throw new Error('VisibilitySelect requires a container element as argument 1'); - this.container = container; - this.helpBlock = this.container.querySelector('.help-block'); - this.select = this.container.querySelector('select'); - } - - init() { - if (this.select) { - this.updateHelpText(); - this.select.addEventListener('change', this.updateHelpText.bind(this)); - } else { - this.helpBlock.textContent = this.container.querySelector('.js-locked').dataset.helpBlock; - } - } +export default class VisibilitySelect { + constructor(container) { + if (!container) throw new Error('VisibilitySelect requires a container element as argument 1'); + this.container = container; + this.helpBlock = this.container.querySelector('.help-block'); + this.select = this.container.querySelector('select'); + } - updateHelpText() { - this.helpBlock.textContent = this.select.querySelector('option:checked').dataset.description; + init() { + if (this.select) { + this.updateHelpText(); + this.select.addEventListener('change', this.updateHelpText.bind(this)); + } else { + this.helpBlock.textContent = this.container.querySelector('.js-locked').dataset.helpBlock; } } - gl.VisibilitySelect = VisibilitySelect; -})(); + updateHelpText() { + this.helpBlock.textContent = this.select.querySelector('option:checked').dataset.description; + } +} diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js index f8b3fb748ae..8430548903c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js @@ -92,13 +92,13 @@ export default { :class="{'label-truncated has-tooltip': isBranchTitleLong(mr.targetBranch)}" :title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''" data-placement="bottom"> - <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a> + <a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a> </span> </strong> <span v-if="shouldShowCommitsBehindText" class="diverged-commits-count"> - ({{mr.divergedCommitsCount}} {{commitsText}} behind) + (<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>) </span> </div> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js index e8b3cf2f729..c02e10128e2 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js @@ -17,9 +17,6 @@ export default { return hasCI && !ciStatus; }, - hasPipeline() { - return Object.keys(this.mr.pipeline || {}).length > 0; - }, svg() { return statusIconEntityMap.icon_status_failed; }, @@ -33,11 +30,7 @@ export default { template: ` <div class="mr-widget-heading"> <div class="ci-widget"> - <template v-if="!hasPipeline"> - <i class="fa fa-spinner fa-spin append-right-10" aria-hidden="true"></i> - Waiting for pipeline... - </template> - <template v-else-if="hasCIError"> + <template v-if="hasCIError"> <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error"> <span class="js-icon-link icon-link"> <span diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 69bc1436284..72a13108404 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -48,6 +48,7 @@ export default class MergeRequestStore { this.sourceBranchLink = data.source_branch_with_namespace_link; this.mergeError = data.merge_error; this.targetBranchPath = data.target_branch_commits_path; + this.targetBranchTreePath = data.target_branch_tree_path; this.conflictResolutionPath = data.conflict_resolution_path; this.cancelAutoMergePath = data.cancel_merge_when_pipeline_succeeds_path; this.removeWIPPath = data.remove_wip_path; diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 8303c556f64..4e10bbc7408 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -44,9 +44,8 @@ text: this.$slots.textarea[0].elm.value, }, ) - .then((res) => { - const data = res.json(); - + .then(resp => resp.json()) + .then((data) => { this.markdownPreviewLoading = false; this.markdownPreview = data.body; diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue index 5e7df22dd83..c9dbc048345 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.vue +++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue @@ -46,6 +46,8 @@ export default { }, methods: { changePage(e) { + if (e.target.parentElement.classList.contains('disabled')) return; + const text = e.target.innerText; const { totalPages, nextPage, previousPage } = this.pageInfo; @@ -82,7 +84,9 @@ export default { const page = this.pageInfo.page; const items = []; - if (page > 1) items.push({ title: FIRST }); + if (page > 1) { + items.push({ title: FIRST, first: true }); + } if (page > 1) { items.push({ title: PREV, prev: true }); @@ -110,7 +114,9 @@ export default { items.push({ title: NEXT, next: true }); } - if (total - page >= 1) items.push({ title: LAST, last: true }); + if (total - page >= 1) { + items.push({ title: LAST, last: true }); + } return items; }, @@ -124,13 +130,15 @@ export default { v-for="item in getItems" :class="{ page: item.page, - prev: item.prev, - next: item.next, + 'js-previous-button': item.prev, + 'js-next-button': item.next, + 'js-last-button': item.last, + 'js-first-button': item.first, separator: item.separator, active: item.active, disabled: item.disabled }"> - <a @click="changePage($event)">{{item.title}}</a> + <a @click.prevent="changePage($event)">{{item.title}}</a> </li> </ul> </div> diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js index 740930dce5b..7f8e514fda1 100644 --- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js +++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js @@ -14,11 +14,22 @@ Vue.http.interceptors.push((request, next) => { }); }); -// Inject CSRF token so we don't break any tests. +// Inject CSRF token and parse headers. +// New Vue Resource version uses Headers, we are expecting a plain object to render pagination +// and polling. Vue.http.interceptors.push((request, next) => { if ($.rails) { - // eslint-disable-next-line no-param-reassign - request.headers['X-CSRF-Token'] = $.rails.csrfToken(); + request.headers.set('X-CSRF-Token', $.rails.csrfToken()); } - next(); + + next((response) => { + // Headers object has a `forEach` property that iterates through all values. + const headers = {}; + + response.headers.forEach((value, key) => { + headers[key] = value; + }); + // eslint-disable-next-line no-param-reassign + response.headers = headers; + }); }); diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/wikis.js index 4194c1bc08d..00676bcb0b3 100644 --- a/app/assets/javascripts/wikis.js +++ b/app/assets/javascripts/wikis.js @@ -1,69 +1,64 @@ -/* eslint-disable no-param-reassign */ /* global Breakpoints */ import 'vendor/jquery.nicescroll'; import './breakpoints'; -((global) => { - class Wikis { - constructor() { - this.bp = Breakpoints.get(); - this.sidebarEl = document.querySelector('.js-wiki-sidebar'); - this.sidebarExpanded = false; - $(this.sidebarEl).niceScroll(); +export default class Wikis { + constructor() { + this.bp = Breakpoints.get(); + this.sidebarEl = document.querySelector('.js-wiki-sidebar'); + this.sidebarExpanded = false; + $(this.sidebarEl).niceScroll(); - const sidebarToggles = document.querySelectorAll('.js-sidebar-wiki-toggle'); - for (let i = 0; i < sidebarToggles.length; i += 1) { - sidebarToggles[i].addEventListener('click', e => this.handleToggleSidebar(e)); - } - - this.newWikiForm = document.querySelector('form.new-wiki-page'); - if (this.newWikiForm) { - this.newWikiForm.addEventListener('submit', e => this.handleNewWikiSubmit(e)); - } + const sidebarToggles = document.querySelectorAll('.js-sidebar-wiki-toggle'); + for (let i = 0; i < sidebarToggles.length; i += 1) { + sidebarToggles[i].addEventListener('click', e => this.handleToggleSidebar(e)); + } - window.addEventListener('resize', () => this.renderSidebar()); - this.renderSidebar(); + this.newWikiForm = document.querySelector('form.new-wiki-page'); + if (this.newWikiForm) { + this.newWikiForm.addEventListener('submit', e => this.handleNewWikiSubmit(e)); } - handleNewWikiSubmit(e) { - if (!this.newWikiForm) return; + window.addEventListener('resize', () => this.renderSidebar()); + this.renderSidebar(); + } - const slugInput = this.newWikiForm.querySelector('#new_wiki_path'); - const slug = gl.text.slugify(slugInput.value); + handleNewWikiSubmit(e) { + if (!this.newWikiForm) return; - if (slug.length > 0) { - const wikisPath = slugInput.getAttribute('data-wikis-path'); - window.location.href = `${wikisPath}/${slug}`; - e.preventDefault(); - } - } + const slugInput = this.newWikiForm.querySelector('#new_wiki_path'); + const slug = gl.text.slugify(slugInput.value); - handleToggleSidebar(e) { + if (slug.length > 0) { + const wikisPath = slugInput.getAttribute('data-wikis-path'); + window.location.href = `${wikisPath}/${slug}`; e.preventDefault(); - this.sidebarExpanded = !this.sidebarExpanded; - this.renderSidebar(); } + } - sidebarCanCollapse() { - const bootstrapBreakpoint = this.bp.getBreakpointSize(); - return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm'; - } + handleToggleSidebar(e) { + e.preventDefault(); + this.sidebarExpanded = !this.sidebarExpanded; + this.renderSidebar(); + } - renderSidebar() { - if (!this.sidebarEl) return; - const { classList } = this.sidebarEl; - if (this.sidebarExpanded || !this.sidebarCanCollapse()) { - if (!classList.contains('right-sidebar-expanded')) { - classList.remove('right-sidebar-collapsed'); - classList.add('right-sidebar-expanded'); - } - } else if (classList.contains('right-sidebar-expanded')) { - classList.add('right-sidebar-collapsed'); - classList.remove('right-sidebar-expanded'); + sidebarCanCollapse() { + const bootstrapBreakpoint = this.bp.getBreakpointSize(); + return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm'; + } + + renderSidebar() { + if (!this.sidebarEl) return; + const { classList } = this.sidebarEl; + if (this.sidebarExpanded || !this.sidebarCanCollapse()) { + if (!classList.contains('right-sidebar-expanded')) { + classList.remove('right-sidebar-collapsed'); + classList.add('right-sidebar-expanded'); } + } else if (classList.contains('right-sidebar-expanded')) { + classList.add('right-sidebar-collapsed'); + classList.remove('right-sidebar-expanded'); } } - - global.Wikis = Wikis; -})(window.gl || (window.gl = {})); +} diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js index b7fe552dec2..99c7644e4d9 100644 --- a/app/assets/javascripts/zen_mode.js +++ b/app/assets/javascripts/zen_mode.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, max-len */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, max-len, class-methods-use-this */ /* global Mousetrap */ // Zen Mode (full screen) textarea @@ -34,65 +34,62 @@ window.Dropzone = Dropzone; // **Cancelable** No // **Target** a.js-zen-leave // -(function() { - this.ZenMode = (function() { - function ZenMode() { - this.active_backdrop = null; - this.active_textarea = null; - $(document).on('click', '.js-zen-enter', function(e) { - e.preventDefault(); - return $(e.currentTarget).trigger('zen_mode:enter'); - }); - $(document).on('click', '.js-zen-leave', function(e) { - e.preventDefault(); - return $(e.currentTarget).trigger('zen_mode:leave'); - }); - $(document).on('zen_mode:enter', (function(_this) { - return function(e) { - return _this.enter($(e.target).closest('.md-area').find('.zen-backdrop')); - }; - })(this)); - $(document).on('zen_mode:leave', (function(_this) { - return function(e) { - return _this.exit(); - }; - })(this)); - $(document).on('keydown', function(e) { - // Esc - if (e.keyCode === 27) { - e.preventDefault(); - return $(document).trigger('zen_mode:leave'); - } - }); - } - - ZenMode.prototype.enter = function(backdrop) { - Mousetrap.pause(); - this.active_backdrop = $(backdrop); - this.active_backdrop.addClass('fullscreen'); - this.active_textarea = this.active_backdrop.find('textarea'); - // Prevent a user-resized textarea from persisting to fullscreen - this.active_textarea.removeAttr('style'); - return this.active_textarea.focus(); - }; - ZenMode.prototype.exit = function() { - if (this.active_textarea) { - Mousetrap.unpause(); - this.active_textarea.closest('.zen-backdrop').removeClass('fullscreen'); - this.scrollTo(this.active_textarea); - this.active_textarea = null; - this.active_backdrop = null; - return Dropzone.forElement('.div-dropzone').enable(); +export default class ZenMode { + constructor() { + this.active_backdrop = null; + this.active_textarea = null; + $(document).on('click', '.js-zen-enter', function(e) { + e.preventDefault(); + return $(e.currentTarget).trigger('zen_mode:enter'); + }); + $(document).on('click', '.js-zen-leave', function(e) { + e.preventDefault(); + return $(e.currentTarget).trigger('zen_mode:leave'); + }); + $(document).on('zen_mode:enter', (function(_this) { + return function(e) { + return _this.enter($(e.target).closest('.md-area').find('.zen-backdrop')); + }; + })(this)); + $(document).on('zen_mode:leave', (function(_this) { + return function(e) { + return _this.exit(); + }; + })(this)); + $(document).on('keydown', function(e) { + // Esc + if (e.keyCode === 27) { + e.preventDefault(); + return $(document).trigger('zen_mode:leave'); } - }; + }); + } + + enter(backdrop) { + Mousetrap.pause(); + this.active_backdrop = $(backdrop); + this.active_backdrop.addClass('fullscreen'); + this.active_textarea = this.active_backdrop.find('textarea'); + // Prevent a user-resized textarea from persisting to fullscreen + this.active_textarea.removeAttr('style'); + return this.active_textarea.focus(); + } - ZenMode.prototype.scrollTo = function(zen_area) { - return $.scrollTo(zen_area, 0, { - offset: -150 - }); - }; + exit() { + if (this.active_textarea) { + Mousetrap.unpause(); + this.active_textarea.closest('.zen-backdrop').removeClass('fullscreen'); + this.scrollTo(this.active_textarea); + this.active_textarea = null; + this.active_backdrop = null; + return Dropzone.forElement('.div-dropzone').enable(); + } + } - return ZenMode; - })(); -}).call(window); + scrollTo(zen_area) { + return $.scrollTo(zen_area, 0, { + offset: -150 + }); + } +} diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 83a8eeaafde..0665622fe4a 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -42,4 +42,4 @@ /* * Styles for JS behaviors. */ -@import "behaviors.scss"; +@import "behaviors"; diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 9dc9f9a9068..6ce331a9129 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -4,49 +4,49 @@ @import 'framework/tw_bootstrap'; @import "framework/layout"; -@import "framework/animations.scss"; -@import "framework/avatar.scss"; -@import "framework/asciidoctor.scss"; -@import "framework/blocks.scss"; -@import "framework/buttons.scss"; -@import "framework/badges.scss"; -@import "framework/calendar.scss"; -@import "framework/callout.scss"; -@import "framework/common.scss"; -@import "framework/dropdowns.scss"; -@import "framework/files.scss"; -@import "framework/filters.scss"; -@import "framework/flash.scss"; -@import "framework/forms.scss"; -@import "framework/gfm.scss"; -@import "framework/header.scss"; -@import "framework/highlight.scss"; -@import "framework/issue_box.scss"; -@import "framework/jquery.scss"; -@import "framework/lists.scss"; -@import "framework/logo.scss"; -@import "framework/markdown_area.scss"; -@import "framework/mobile.scss"; -@import "framework/modal.scss"; -@import "framework/nav.scss"; -@import "framework/pagination.scss"; -@import "framework/panels.scss"; -@import "framework/selects.scss"; -@import "framework/sidebar.scss"; -@import "framework/tables.scss"; -@import "framework/notes.scss"; -@import "framework/timeline.scss"; -@import "framework/typography.scss"; -@import "framework/zen.scss"; +@import "framework/animations"; +@import "framework/avatar"; +@import "framework/asciidoctor"; +@import "framework/blocks"; +@import "framework/buttons"; +@import "framework/badges"; +@import "framework/calendar"; +@import "framework/callout"; +@import "framework/common"; +@import "framework/dropdowns"; +@import "framework/files"; +@import "framework/filters"; +@import "framework/flash"; +@import "framework/forms"; +@import "framework/gfm"; +@import "framework/header"; +@import "framework/highlight"; +@import "framework/issue_box"; +@import "framework/jquery"; +@import "framework/lists"; +@import "framework/logo"; +@import "framework/markdown_area"; +@import "framework/mobile"; +@import "framework/modal"; +@import "framework/nav"; +@import "framework/pagination"; +@import "framework/panels"; +@import "framework/selects"; +@import "framework/sidebar"; +@import "framework/tables"; +@import "framework/notes"; +@import "framework/timeline"; +@import "framework/typography"; +@import "framework/zen"; @import "framework/blank"; -@import "framework/wells.scss"; -@import "framework/page-header.scss"; -@import "framework/awards.scss"; -@import "framework/images.scss"; +@import "framework/wells"; +@import "framework/page-header"; +@import "framework/awards"; +@import "framework/images"; @import "framework/broadcast-messages"; -@import "framework/emojis.scss"; -@import "framework/emoji-sprites.scss"; -@import "framework/icons.scss"; -@import "framework/snippets.scss"; -@import "framework/memory_graph.scss"; -@import "framework/responsive-tables.scss"; +@import "framework/emojis"; +@import "framework/emoji-sprites"; +@import "framework/icons"; +@import "framework/snippets"; +@import "framework/memory_graph"; +@import "framework/responsive-tables"; diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss index 19166757e64..bb30da4f4b2 100644 --- a/app/assets/stylesheets/framework/awards.scss +++ b/app/assets/stylesheets/framework/awards.scss @@ -24,7 +24,7 @@ opacity: 0; transform: scale(.2); transform-origin: 0 -45px; - transition: .3s cubic-bezier(.67,.06,.19,1.44); + transition: .3s cubic-bezier(.67, .06, .19, 1.44); transition-property: transform, opacity; &.is-aligned-right { @@ -231,11 +231,11 @@ .award-control-icon-positive, .award-control-icon-super-positive { + @include transition(opacity, transform); position: absolute; left: 10px; bottom: 6px; opacity: 0; - @include transition(opacity, transform); } .award-control-text { diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss index a2fa2e7769b..6bb096fc5bd 100644 --- a/app/assets/stylesheets/framework/blank.scss +++ b/app/assets/stylesheets/framework/blank.scss @@ -1,9 +1,14 @@ -.blank-state-welcome { - text-align: center; - border-bottom: 1px solid $border-color; +.blank-state-parent-container { + .section-container { + padding: 10px; + } - .blank-state-text { - margin-bottom: 0; + .section-body { + width: 100%; + height: 100%; + padding-bottom: 25px; + border: 1px solid $border-color; + border-radius: $border-radius-default; } } @@ -11,41 +16,35 @@ padding-top: 20px; padding-bottom: 20px; text-align: center; -} -.blank-state-no-icon { - padding-top: 40px; - padding-bottom: 40px; -} - -.blank-state-icon { - padding-bottom: 20px; - color: $gray-darkest; - font-size: 56px; + &.blank-state-welcome { + .blank-state-welcome-title { + font-size: 24px; + } - path, - polygon { - fill: currentColor; + .blank-state-text { + margin-bottom: 0; + } } -} -.blank-state-title { - margin-top: 0; - margin-bottom: 5px; - font-size: 18px; - font-weight: normal; -} + .blank-state-icon { + padding-bottom: 20px; -.blank-state-text { - margin-top: 0; - margin-bottom: $gl-padding; - font-size: 14px; + svg { + display: block; + margin: auto; + } + } - > strong { - font-weight: 600; + .blank-state-title { + margin-top: 0; + margin-bottom: 10px; + font-size: 18px; } -} -.blank-state-welcome-title { - font-size: 24px; + .blank-state-text { + max-width: $container-text-max-width; + margin: 0 auto $gl-padding; + font-size: 14px; + } } diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 4369ae78bde..6eabdc63d9e 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -20,17 +20,29 @@ color: $text; border-color: $border; + > .icon { + color: $text; + } + &:hover, &:focus { background-color: $hover-background; border-color: $hover-border; color: $hover-text; + + > .icon { + color: $hover-text; + } } &:active { background-color: $active-background; border-color: $active-border; color: $hover-text; + + > .icon { + color: $hover-text; + } } } @@ -163,7 +175,8 @@ @include btn-orange; } - &.btn-close { + &.btn-close, + &.btn-close-color { @include btn-outline($white-light, $orange-600, $orange-500, $orange-500, $white-light, $orange-600, $orange-600, $orange-700); } @@ -181,7 +194,8 @@ float: right; } - &.btn-reopen { + &.btn-reopen, + .btn-reopen-color { /* should be same as parent class for now */ } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 00c981f64c5..5e374360359 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -336,11 +336,6 @@ table { text-align: center; } -#nprogress .spinner { - top: 15px !important; - right: 10px !important; -} - .header-with-avatar { h3 { margin: 0; @@ -450,4 +445,3 @@ table { pointer-events: none; opacity: .5; } - diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 4f54ca24940..f89f2f30443 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -35,8 +35,8 @@ .open { .dropdown-menu, .dropdown-menu-nav { - display: block; @include set-visible; + display: block; @media (max-width: $screen-xs-max) { width: 100%; @@ -184,13 +184,15 @@ .dropdown-menu, .dropdown-menu-nav { + @include set-invisible; display: block; position: absolute; - width: 100%; + width: auto; top: 100%; left: 0; z-index: 9; min-width: 240px; + max-width: 500px; margin-top: 2px; margin-bottom: 0; font-size: 14px; @@ -200,7 +202,6 @@ border: 1px solid $dropdown-border-color; border-radius: $border-radius-base; box-shadow: 0 2px 4px $dropdown-shadow-color; - @include set-invisible; @media (max-width: $screen-sm-min) { width: 100%; @@ -295,9 +296,74 @@ } } -.filtered-search-box-input-container .dropdown-menu, -.filtered-search-box-input-container .dropdown-menu-nav, -.comment-type-dropdown .dropdown-menu { +.droplab-dropdown { + .description { + display: inline-block; + white-space: normal; + margin-left: 5px; + } + + .dropdown-toggle > i { + pointer-events: none; + } + + li { + padding: $gl-btn-padding $gl-btn-padding 2px; + cursor: pointer; + + > a, + > button { + display: flex; + margin: 0; + padding: 0; + border-radius: 0; + text-overflow: inherit; + background-color: inherit; + color: inherit; + border: inherit; + text-align: left; + + &:hover, + &:focus { + background-color: inherit; + color: inherit; + } + + &.btn .fa:not(:last-child) { + margin-left: 5px; + } + } + + &:hover, + &:focus { + background-color: $dropdown-hover-color; + color: $white-light; + } + + &.droplab-item-selected i { + visibility: visible; + } + + .icon { + visibility: hidden; + } + } + + .icon { + display: inline-block; + vertical-align: top; + padding-top: 2px; + } + + .divider { + margin: 0 8px; + padding: 0; + border-top: $gray-darkest; + } +} + +.droplab-dropdown .dropdown-menu, +.droplab-dropdown .dropdown-menu-nav { display: none; opacity: 1; visibility: visible; @@ -610,8 +676,8 @@ } .pika-single { - position: relative!important; - top: 0!important; + position: relative !important; + top: 0 !important; border: 0; box-shadow: none; } diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 767cf5ffea5..7e4e5fd7f1c 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -70,6 +70,13 @@ .input-token { max-width: 200px; + padding: 0; + + &:hover, + &:focus { + background-color: inherit; + color: inherit; + } } .input-token:only-child, @@ -156,6 +163,16 @@ } } +.droplab-dropdown li.filtered-search-token { + padding: 0; + + &:hover, + &:focus { + background-color: inherit; + color: inherit; + } +} + .filtered-search-term { .name { background-color: inherit; @@ -351,7 +368,7 @@ margin-right: 0.3em; } - & > .value { + > .value { font-weight: 600; } } @@ -450,7 +467,7 @@ -webkit-flex-direction: column; flex-direction: column; - &> span { + > span { white-space: normal; word-break: break-all; } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 5bd6c095109..20fb10c28d4 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -330,7 +330,7 @@ header { padding-left: 5px; .nav > li:not(.hidden-xs) { - display: table-cell!important; + display: table-cell !important; width: 25%; a { diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index 6d27d7568cf..71d5949b023 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -61,7 +61,7 @@ &:focus { outline: none; - & i { + i { visibility: visible; } } diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 38727e15c6f..e59cd0eea82 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -343,6 +343,12 @@ ul.indent-list { .group-row { padding: 0; border: none; + + &:last-of-type { + .group-row-contents:not(:hover) { + border-bottom: 1px solid transparent; + } + } } .group-row-contents { diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index b21bcc22a87..a2de4598167 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -165,8 +165,8 @@ .cur { .avatar { - border: 1px solid $white-light; @include disableAllAnimation; + border: 1px solid $white-light; } } diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index 7098203321d..a28f54936be 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -21,3 +21,9 @@ body.modal-open { width: 860px; } } + +@media (min-width: $screen-lg-min) { + .modal-full { + width: 98%; + } +} diff --git a/app/assets/stylesheets/framework/responsive-tables.scss b/app/assets/stylesheets/framework/responsive-tables.scss index d2c90908baa..8e653c443cf 100644 --- a/app/assets/stylesheets/framework/responsive-tables.scss +++ b/app/assets/stylesheets/framework/responsive-tables.scss @@ -100,9 +100,9 @@ } .table-mobile-header { + @include flex-max-width(40); color: $gl-text-color-secondary; text-align: left; - @include flex-max-width(40); @media (min-width: $screen-md-min) { display: none; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 5cf9330b8f8..542b641e3dd 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -92,7 +92,7 @@ @mixin maintain-sidebar-dimensions { display: block; width: $gutter-width; - padding: 10px 20px; + padding: 10px 0; } .issues-bulk-update.right-sidebar { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index da4d91511e0..8bd69faf84c 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -74,11 +74,17 @@ $red-700: #a62d19; $red-800: #8b2615; $red-900: #711e11; -$purple-600: #6e49cb; -$purple-650: #5c35ae; -$purple-700: #4a2192; -$purple-800: #2c0a5c; -$purple-900: #380d75; +$indigo-50: #f7f7ff; +$indigo-100: #ebebfa; +$indigo-200: #d1d1f0; +$indigo-300: #a6a6de; +$indigo-400: #7c7ccc; +$indigo-500: #6666c4; +$indigo-600: #5b5bbd; +$indigo-700: #4b4ba3; +$indigo-800: #393982; +$indigo-900: #292961; +$indigo-950: #1a1a40; $black: #000; $black-transparent: rgba(0, 0, 0, 0.3); @@ -104,10 +110,10 @@ $well-light-text-color: #5b6169; * Text */ $gl-font-size: 14px; -$gl-text-color: rgba(0, 0, 0, .85); -$gl-text-color-light: rgba(0, 0, 0, .7); -$gl-text-color-secondary: rgba(0, 0, 0, .55); -$gl-text-color-disabled: rgba(0, 0, 0, .35); +$gl-text-color: #2e2e2e; +$gl-text-color-secondary: #707070; +$gl-text-color-tertiary: #949494; +$gl-text-color-quaternary: #d6d6d6; $gl-text-color-inverted: rgba(255, 255, 255, 1.0); $gl-text-color-secondary-inverted: rgba(255, 255, 255, .85); $gl-text-green: $green-600; @@ -121,7 +127,7 @@ $gl-gray-dark: #313236; $gl-gray-light: #5c5c5c; $gl-header-color: #4c4e54; $gl-header-nav-hover-color: #434343; -$placeholder-text-color: rgba(0, 0, 0, .42); +$placeholder-text-color: $gl-text-color-tertiary; /* * Lists @@ -129,7 +135,7 @@ $placeholder-text-color: rgba(0, 0, 0, .42); $list-font-size: $gl-font-size; $list-title-color: $gl-text-color; $list-text-color: $gl-text-color; -$list-text-disabled-color: $gl-text-color-disabled; +$list-text-disabled-color: $gl-text-color-tertiary; $list-border-light: #eee; $list-border: rgba(0, 0, 0, 0.05); $list-text-height: 42px; @@ -153,6 +159,7 @@ $code_line_height: 1.6; * Padding */ $gl-padding: 16px; +$gl-col-padding: 15px; $gl-btn-padding: 10px; $gl-input-padding: 10px; $gl-vert-padding: 6px; @@ -169,6 +176,7 @@ $header-height: 50px; $fixed-layout-width: 1280px; $limited-layout-width: 990px; $limited-layout-width-sm: 790px; +$container-text-max-width: 540px; $gl-avatar-size: 40px; $error-exclamation-point: $red-500; $border-radius-default: 3px; @@ -264,7 +272,7 @@ $diff-view-modes-border: #c1c1c1; /* * Fonts */ -$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace; +$monospace_font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace; $regular_font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; /* @@ -309,7 +317,7 @@ $badge-color: $gl-text-color-secondary; /* * Award emoji */ -$award-emoji-menu-shadow: rgba(0,0,0,.175); +$award-emoji-menu-shadow: rgba(0, 0, 0, .175); $award-emoji-positive-add-bg: #fed159; $award-emoji-positive-add-lines: #bb9c13; @@ -443,6 +451,7 @@ $logs-p-color: #333; /* * Forms */ +$input-height: 34px; $input-danger-bg: #f2dede; $input-danger-border: $red-400; $input-group-addon-bg: #f7f8fa; @@ -559,7 +568,7 @@ $kdb-color: #555; $kdb-border: #ccc; $kdb-border-bottom: #bbb; $kdb-shadow: #bbb; -$body-text-shadow: rgba(255,255,255,0.01); +$body-text-shadow: rgba(255, 255, 255, 0.01); /* * UI Dev Kit @@ -575,6 +584,12 @@ $stage-hover-border: #d1e7fc; $action-icon-color: #d6d6d6; /* +Pipeline Schedules +*/ +$pipeline-variable-remove-button-width: calc(1em + #{2 * $gl-padding}); + + +/* Filtered Search */ $filter-name-resting-color: #f8f8f8; @@ -594,3 +609,15 @@ Convdev Index $color-high-score: $green-400; $color-average-score: $orange-400; $color-low-score: $red-400; + +/* +Performance Bar +*/ +$perf-bar-text: #999; +$perf-bar-production: #222; +$perf-bar-staging: #291430; +$perf-bar-development: #4c1210; +$perf-bar-bucket-bg: #111; +$perf-bar-bucket-color: #ccc; +$perf-bar-bucket-box-shadow-from: rgba($white-light, .2); +$perf-bar-bucket-box-shadow-to: rgba($black, .25); diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 1c1392f8f67..b1ff2659131 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -3,6 +3,7 @@ color: $gl-text-color; border: 1px solid $border-color; border-radius: $border-radius-default; + margin-bottom: $gl-padding; .well-segment { padding: $gl-padding; @@ -21,6 +22,11 @@ font-size: 12px; } } + + &.admin-well h4 { + border-bottom: 1px solid $border-color; + padding-bottom: 8px; + } } .icon-container { @@ -53,6 +59,14 @@ padding: 15px; } +.dark-well { + background-color: $gray-normal; + + .btn { + width: 100%; + } +} + .well-centered { h1 { font-weight: normal; diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 1daa10aef24..578f1902cce 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -113,7 +113,7 @@ $white-gc-bg: #eaf2f5; border-color: $line-removed-dark; a { - color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%); + color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%); } } @@ -122,7 +122,7 @@ $white-gc-bg: #eaf2f5; border-color: $line-added-dark; a { - color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%); + color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%); } } @@ -163,7 +163,7 @@ $white-gc-bg: #eaf2f5; background-color: $line-removed; &::before { - color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%); + color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%); } span.idiff { @@ -175,7 +175,7 @@ $white-gc-bg: #eaf2f5; background-color: $line-added; &::before { - color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%); + color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%); } span.idiff { diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index bfb7a0c7e25..393d5006e24 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -4,7 +4,7 @@ header.navbar-gitlab-new { color: $white-light; - background-color: $purple-900; + background: linear-gradient(to right, $indigo-900, $indigo-800); border-bottom: 0; .header-content { @@ -24,11 +24,9 @@ header.navbar-gitlab-new { > a { display: flex; align-items: center; - padding-top: 3px; padding-right: $gl-padding; padding-left: $gl-padding; margin-left: -$gl-padding; - border-bottom: 3px solid transparent; @media (min-width: $screen-sm-min) { padding-right: $gl-padding; @@ -45,9 +43,8 @@ header.navbar-gitlab-new { &:hover, &:focus { - color: currentColor; + color: $tanuki-yellow; text-decoration: none; - border-bottom-color: $white-light; } } } @@ -71,7 +68,7 @@ header.navbar-gitlab-new { .navbar-collapse { padding-left: 0; - color: $white-light; + color: $indigo-200; box-shadow: 0; @media (max-width: $screen-xs-max) { @@ -101,7 +98,7 @@ header.navbar-gitlab-new { font-size: 14px; text-align: center; color: currentColor; - border-left: 1px solid lighten($purple-700, 10%); + border-left: 1px solid lighten($indigo-700, 10%); &:hover, &:focus, @@ -120,6 +117,7 @@ header.navbar-gitlab-new { li { .badge { box-shadow: none; + font-weight: 600; } } } @@ -133,12 +131,11 @@ header.navbar-gitlab-new { > a { background: none; - opacity: .9; - will-change: opacity; + will-change: color; &.header-user-dropdown-toggle { .header-user-avatar { - border-color: $white-light; + border-color: $indigo-200; } } @@ -165,29 +162,34 @@ header.navbar-gitlab-new { .navbar-sub-nav { display: flex; margin-bottom: 0; - color: $white-light; + color: $indigo-200; > li { - &.active > a, - a:hover, - a:focus { - border-bottom-color: $white-light; + > a:hover, + > a:focus { + box-shadow: inset 0 -3px 0 rgba($indigo-200, .4); text-decoration: none; outline: 0; - opacity: 1; + color: $white-light; + } + + &.active > a { + box-shadow: inset 0 -3px 0 $indigo-500; + color: $white-light; + font-weight: 700; } > a { display: block; - padding: 16px 10px 13px; + padding: 16px 10px; font-size: 13px; color: currentColor; - border-bottom: 3px solid transparent; - opacity: .9; - will-change: opacity; + box-shadow: inset 0 0 0 transparent; + will-change: box-shadow; + transition: box-shadow 0.15s; @media (min-width: $screen-sm-min) { - padding: 15px $gl-padding 12px; + padding: 15px $gl-padding; font-size: 14px; } } @@ -207,55 +209,60 @@ header.navbar-gitlab-new { .search { form { - border-color: $purple-800; + border: 0; + background-color: rgba($indigo-200, .2); + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s, background-color ease-in-out 0.15s; &:hover { - border-color: rgba($white-light, .6); + background-color: rgba($indigo-200, .3); box-shadow: none; } } &.search-active form { - border-color: $white-light; - } - - form, - .search-input { - background-color: $purple-700; + background-color: rgba($indigo-200, .3); + box-shadow: none; } .search-input { color: $white-light; + background: none; } .search-input::placeholder { - color: rgba($white-light, .6); + color: rgba($indigo-200, .8); } .location-badge { font-size: 12px; - color: rgba($white-light, .6); - background-color: $purple-800; + color: $indigo-100; + background-color: rgba($indigo-200, .1); transition: color 0.15s; will-change: color; + margin: -4px 4px -4px -4px; + line-height: 25px; + padding: 4px 8px; + border-radius: 2px 0 0 2px; + border-right: 1px solid $indigo-800; + height: 34px; } .search-input-wrap { .search-icon, .clear-icon { - color: rgba($white-light, .6); + color: rgba($indigo-200, .8); } } &.search-active { .location-badge { color: $white-light; - background-color: $purple-800; + background-color: rgba($indigo-200, .2); } .search-input-wrap { .search-icon { - color: rgba($white-light, .6); + color: rgba($indigo-200, .8); } .clear-icon { @@ -277,7 +284,7 @@ header.navbar-gitlab-new { position: relative; top: -1px; padding: 0 5px; - color: rgba($black, .65); + color: $gl-text-color-secondary; font-size: 10px; line-height: 1; background: none; @@ -303,10 +310,10 @@ header.navbar-gitlab-new { .breadcrumbs-links { flex: 1; align-self: center; - color: $black-transparent; + color: $gl-text-color-quaternary; a { - color: rgba($black, .65); + color: $gl-text-color-secondary; &:not(:first-child), &.group-path { @@ -361,9 +368,10 @@ header.navbar-gitlab-new { } .breadcrumbs-sub-title { - margin: 2px 0 0; + margin: 2px 0; font-size: 16px; font-weight: normal; + line-height: 1; ul { margin: 0; diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 17f23f7fce3..82cabefa129 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -2,6 +2,15 @@ @import 'framework/tw_bootstrap_variables'; @import "bootstrap/variables"; +$active-background: rgba(0, 0, 0, .04); +$active-border: $indigo-500; +$active-color: $indigo-700; +$active-hover-background: $active-background; +$active-hover-color: $gl-text-color; +$inactive-badge-background: rgba(0, 0, 0, .08); +$hover-background: $indigo-700; +$hover-color: $white-light; +$inactive-color: $gl-text-color-secondary; $new-sidebar-width: 220px; .page-with-new-sidebar { @@ -17,24 +26,46 @@ $new-sidebar-width: 220px; } .context-header { - background-color: $gray-normal; border-bottom: 1px solid $border-color; font-weight: 600; display: flex; align-items: center; - padding: 10px 14px; + padding: 10px 16px 10px 10px; + color: $gl-text-color; .avatar-container { flex: 0 0 40px; + background-color: $white-light; } &:hover { - background-color: $border-color; + background-color: $hover-background; + color: $hover-color; + border-color: $hover-background; + + .avatar-container { + border-color: transparent; + } + + .settings-avatar { + background-color: $indigo-500; + + i { + color: $hover-color; + } + } + } + + .project-title, + .group-title { + overflow: hidden; + text-overflow: ellipsis; } } .settings-avatar { background-color: $white-light; + transition: background-color 100ms linear; i { font-size: 20px; @@ -42,6 +73,7 @@ $new-sidebar-width: 220px; color: $gl-text-color-secondary; text-align: center; align-self: center; + transition: color 100ms linear; } } @@ -54,11 +86,15 @@ $new-sidebar-width: 220px; bottom: 0; left: 0; overflow: auto; - background-color: $gray-light; - border-right: 1px solid $border-color; + background-color: $gray-normal; + box-shadow: inset -2px 0 0 $border-color; + + a { + text-decoration: none; + } ul { - padding: 0; + padding-left: 0; list-style: none; } @@ -67,13 +103,18 @@ $new-sidebar-width: 220px; a { display: block; - padding: 12px 14px; + padding: 12px 16px; + color: $inactive-color; } } - a { - color: $gl-text-color; - text-decoration: none; + li.active { + box-shadow: inset 4px 0 0 $active-border; + + > a { + color: $active-color; + font-weight: 700; + } } @media (max-width: $screen-xs-max) { @@ -83,22 +124,28 @@ $new-sidebar-width: 220px; .sidebar-sub-level-items { display: none; + padding-bottom: 8px; > li { a { - padding: 12px 24px; - color: $gl-text-color-light; + font-size: 12px; + padding: 8px 16px 8px 24px; - &:hover { - color: $gl-text-color; - background-color: $border-color; + &:hover, + &:focus { + background: $active-hover-background; + color: $active-hover-color; } } &.active { - > a { - color: $purple-650; - font-weight: 600; + a { + &, + &:hover, + &:focus { + background: $active-background; + color: $active-color; + } } } } @@ -108,35 +155,31 @@ $new-sidebar-width: 220px; > li { .badge { float: right; - background-color: $border-color; - color: $gl-text-color; + background-color: $inactive-badge-background; + color: $inactive-color; } &.active { - > a { - background-color: $purple-600; - color: $white-light; - font-weight: 600; - } + background: $active-background; .badge { - background-color: $purple-700; - color: $white-light; + color: $active-color; + font-weight: 600; } .sidebar-sub-level-items { - background-color: $gray-normal; - border-left: 6px solid $purple-600; display: block; } } - &:not(.active) > a:hover { - background-color: $border-color; + > a:hover { + background-color: $hover-background; + color: $hover-color; .badge { - transition: background-color 100ms linear; - background-color: $gray-normal; + transition: background-color 100ms linear, color 100ms linear; + background-color: $indigo-500; + color: $hover-color; } } } @@ -155,3 +198,13 @@ $new-sidebar-width: 220px; // scss-lint:enable DuplicateProperty } } + + +// Change color of all horizontal tabs to match the new indigo color +.nav-links li.active a { + border-bottom-color: $active-border; + + .badge { + font-weight: 600; + } +} diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 85109fec91a..df858cffe09 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -11,7 +11,7 @@ .is-dragging { // Important because plugin sets inline CSS - opacity: 1!important; + opacity: 1 !important; * { -webkit-user-select: none; @@ -19,8 +19,8 @@ -ms-user-select: none; user-select: none; // !important to make sure no style can override this when dragging - cursor: -webkit-grabbing!important; - cursor: grabbing!important; + cursor: -webkit-grabbing !important; + cursor: grabbing !important; } } diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 9cff99b839c..b6fc628c02b 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -6,26 +6,26 @@ @keyframes blinking-dots { 0% { background-color: rgba($white-light, 1); - box-shadow: 12px 0 0 0 rgba($white-light,0.2), - 24px 0 0 0 rgba($white-light,0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), + 24px 0 0 0 rgba($white-light, 0.2); } 25% { background-color: rgba($white-light, 0.4); - box-shadow: 12px 0 0 0 rgba($white-light,2), - 24px 0 0 0 rgba($white-light,0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 2), + 24px 0 0 0 rgba($white-light, 0.2); } 75% { background-color: rgba($white-light, 0.4); - box-shadow: 12px 0 0 0 rgba($white-light,0.2), - 24px 0 0 0 rgba($white-light,1); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), + 24px 0 0 0 rgba($white-light, 1); } 100% { background-color: rgba($white-light, 1); - box-shadow: 12px 0 0 0 rgba($white-light,0.2), - 24px 0 0 0 rgba($white-light,0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), + 24px 0 0 0 rgba($white-light, 0.2); } } @@ -37,65 +37,77 @@ } .build-page { - .sticky { - position: absolute; - left: 0; - right: 0; + .build-trace-container { + position: relative; } - .build-trace-container { - position: absolute; - top: 225px; - left: 15px; - bottom: 10px; + .build-trace { background: $black; color: $gray-darkest; - font-family: $monospace_font; + white-space: pre; + overflow-x: auto; font-size: 12px; + border-radius: 0; + border: none; - &.sidebar-expanded { - right: 305px; + .bash { + display: block; } + } - &.sidebar-collapsed { - right: 16px; + .top-bar { + height: 35px; + display: flex; + justify-content: flex-end; + background: $gray-light; + border: 1px solid $border-color; + color: $gl-text-color; + position: sticky; + position: -webkit-sticky; + top: 50px; + + &.affix { + top: 50px; } - code { - background: $black; - color: $gray-darkest; + // with sidebar + &.affix.sidebar-expanded { + right: 306px; + left: 16px; } - .top-bar { - top: 0; - height: 35px; - display: flex; - justify-content: flex-end; - background: $gray-light; - border: 1px solid $border-color; - color: $gl-text-color; + // without sidebar + &.affix.sidebar-collapsed { + right: 16px; + left: 16px; + } - .truncated-info { - margin: 0 auto; - align-self: center; + &.affix-top { + position: absolute; + right: 0; + left: 0; + } - .truncated-info-size { - margin: 0 5px; - } + .truncated-info { + margin: 0 auto; + align-self: center; - .raw-link { - color: $gl-text-color; - margin-left: 5px; - text-decoration: underline; - } + .truncated-info-size { + margin: 0 5px; + } + + .raw-link { + color: $gl-text-color; + margin-left: 5px; + text-decoration: underline; } } .controllers { display: flex; - align-self: center; font-size: 15px; - margin-bottom: 4px; + justify-content: center; + align-items: center; svg { height: 15px; @@ -103,17 +115,9 @@ fill: $gl-text-color; } - .controllers-buttons, - .btn-scroll { - color: $gl-text-color; - height: 15px; - vertical-align: middle; - padding: 0; - width: 12px; - } - .controllers-buttons { - margin: 1px 10px; + color: $gl-text-color; + margin: 0 10px; } .btn-scroll.animate { @@ -143,15 +147,6 @@ } } - .bash { - top: 35px; - left: 10px; - bottom: 0; - padding: 10px 20px 20px 5px; - white-space: pre-wrap; - overflow: auto; - } - .environment-information { border: 1px solid $border-color; padding: 8px $gl-padding 12px; @@ -249,6 +244,10 @@ } } + .block-last { + padding: 16px 0; + } + .trigger-build-variable { color: $code-color; } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 9db0f2075cb..a5e4c3311f8 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -250,8 +250,8 @@ } .committed_ago { - float: right; @extend .cgray; + float: right; } } } diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index 3039732ca5b..eeb90759f10 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -24,9 +24,9 @@ .col-headers { ul { + @include clearfix; margin: 0; padding: 0; - @include clearfix; } li { @@ -189,8 +189,8 @@ } li { - list-style-type: none; @include clearfix; + list-style-type: none; } .stage-nav-item { @@ -281,11 +281,11 @@ } .stage-event-item { + @include clearfix; list-style-type: none; padding: 0 0 $gl-padding; margin: 0 $gl-padding $gl-padding; border-bottom: 1px solid $gray-darker; - @include clearfix; &:last-child { border-bottom: none; @@ -307,9 +307,9 @@ &.issue-title, &.commit-title, &.merge-merquest-title { + @include text-overflow(); max-width: 100%; display: block; - @include text-overflow(); a { color: $gl-text-color; diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 55011e8a21b..398fd4444ea 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -91,6 +91,7 @@ .old_line, .new_line { + @include user-select(none); margin: 0; border: none; padding: 0 5px; @@ -99,7 +100,6 @@ min-width: 35px; max-width: 50px; width: 35px; - @include user-select(none); a { float: left; @@ -354,12 +354,12 @@ } &.active { + cursor: default; + color: $gl-text-color; + &:hover { text-decoration: none; } - - cursor: default; - color: $gl-text-color; } &.disabled { diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index a2be957655f..00ebf4e26ac 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -187,8 +187,7 @@ } .text-metric { - font-weight: 600; - font-size: 14px; + font-size: 12px; } .selected-metric-line { @@ -232,10 +231,6 @@ width: 100%; padding: 0; padding-bottom: 100%; - - .text-metric-bold { - font-weight: 600; - } } .prometheus-svg-container > svg { @@ -250,11 +245,15 @@ stroke-width: 0; } + .text-metric-bold { + font-weight: 600; + } + .label-axis-text, .text-metric-usage { fill: $black; font-weight: 500; - font-size: 14px; + font-size: 12px; } .legend-axis-text { @@ -262,7 +261,20 @@ } .tick > text { - font-size: 14px; + font-size: 12px; + } + + .text-metric-title { + font-size: 12px; + } + + .y-label-text, + .x-label-text { + fill: $gray-darkest; + } + + .axis-tick { + stroke: $gray-darker; } @media (max-width: $screen-sm-max) { @@ -277,3 +289,9 @@ } } } + +.prometheus-row { + h5 { + font-size: 16px; + } +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 057d457b3a2..56a4b53ed61 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -200,7 +200,6 @@ right: 0; transition: width .3s; background: $gray-light; - padding: 0 20px; z-index: 200; overflow: hidden; @@ -224,6 +223,10 @@ } } + .issuable-sidebar { + padding: 0 20px; + } + .issuable-sidebar-header { padding-top: 10px; } @@ -796,3 +799,28 @@ } } } + +.issuable-close-button, +.issuable-close-toggle { + @include transition(border-color, color); +} + +.issuable-close-dropdown { + .dropdown-menu { + min-width: 270px; + left: auto; + right: 0; + } + + .description { + margin-bottom: 10px; + + .text { + margin: 0; + } + } + + .dropdown-toggle > .icon { + margin: 0 3px; + } +} diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index f21005895e4..e7c07ef67f0 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -54,8 +54,6 @@ @media (min-width: $screen-sm-min) { display: -webkit-flex; display: flex; - width: 400px; - max-width: 50%; } } @@ -65,7 +63,6 @@ @media (min-width: $screen-sm-min) { display: -webkit-flex; display: flex; - width: 100%; margin-top: 3px; } } @@ -81,18 +78,10 @@ .member-form-control { @media (max-width: $screen-xs-max) { - padding: 5px 0; + padding-bottom: 5px; margin-left: 0; margin-right: 0; } - - @media (min-width: $screen-sm-min) { - width: 50%; - } - - .dropdown-menu-toggle { - width: 100%; - } } .member-access-text { @@ -216,3 +205,102 @@ } } } + +.content-list.members-list li { + display: flex; + justify-content: space-between; + + .list-item-name { + float: none; + display: flex; + flex: 1; + } + + .user-info { + padding-right: 10px; + } + + .member { + font-weight: bold; + overflow-wrap: break-word; + word-break: break-all; + } + + .member-group-link { + display: inline-block; + } + + .form-control { + width: inherit; + } + + .btn { + align-self: flex-start; + } + + .form-horizontal ~ .btn { + margin-right: 0; + } + + @media (max-width: $screen-xs-max) { + display: block; + + .controls > .btn { + margin-left: 0; + margin-right: 0; + display: block; + } + + .form-control { + width: 100%; + } + + .member-access-text { + line-height: 0; + margin-left: 50px; + } + + .member-controls { + margin-top: 5px; + } + + .form-horizontal { + margin-top: 10px; + } + } +} + +.panel-mobile { + .content-list.members-list li { + display: block; + + .member-controls { + float: none; + display: block; + } + + .dropdown-menu-toggle, + .dropdown-menu, + .form-control, + .list-item-name { + width: 100%; + } + + .dropdown-menu { + margin-top: 0; + } + + .form-horizontal { + display: block; + } + + .member-form-control { + margin: 5px 0; + } + + .btn { + width: 100%; + margin-left: 0; + } + } +} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 59e0624d94e..2db967547dd 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -96,7 +96,7 @@ overflow: visible; } - & > span { + > span { padding-right: 4px; } @@ -125,7 +125,7 @@ .dropdown-menu { margin-top: 11px; - z-index: 200; + z-index: 300; } .ci-action-icon-wrapper { @@ -731,11 +731,11 @@ .merge-request-tabs-holder { top: $header-height; - z-index: 100; + z-index: 200; background-color: $white-light; border-bottom: 1px solid $border-color; - @media(min-width: $screen-sm-min) { + @media (min-width: $screen-sm-min) { position: sticky; position: -webkit-sticky; } @@ -770,6 +770,12 @@ max-width: $limited-layout-width; margin-left: auto; margin-right: auto; + + .inner-page-scroll-tabs { + background-color: $white-light; + margin-left: -$gl-padding; + padding-left: $gl-padding; + } } } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 9877ed2cfd6..cdb1e65e4be 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -356,7 +356,6 @@ color: $white-light; padding-right: 2px; margin-top: 2px; - pointer-events: none; } } @@ -366,56 +365,6 @@ width: 298px; } - .description { - display: inline-block; - white-space: normal; - margin-left: 8px; - padding-right: 33px; - } - - li { - padding-top: 6px; - - & > a { - margin: 0; - padding: 0; - color: inherit; - border-radius: 0; - text-overflow: inherit; - - &:hover, - &:focus { - background-color: inherit; - color: inherit; - } - } - - &:hover, - &:focus { - background-color: $dropdown-hover-color; - color: $white-light; - } - - &.droplab-item-selected i { - visibility: visible; - } - - i { - visibility: hidden; - } - } - - i { - display: inline-block; - vertical-align: top; - padding-top: 2px; - } - - .divider { - margin: 0 8px; - padding: 0; - border-top: $gray-darkest; - } @media (max-width: $screen-xs-max) { display: flex; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 303425041df..2bb867052f6 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -121,10 +121,11 @@ ul.notes { overflow-y: hidden; .note-text { - word-wrap: break-word; @include md-typography; // Reset ul style types since we're nested inside a ul already @include bulleted-list; + word-wrap: break-word; + ul.task-list { ul:not(.task-list) { padding-left: 1.3em; @@ -250,7 +251,7 @@ ul.notes { } .note-text { - & p:first-child { + p:first-child { display: none; } diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss index 595eb40fec7..dc1654e006e 100644 --- a/app/assets/stylesheets/pages/pipeline_schedules.scss +++ b/app/assets/stylesheets/pages/pipeline_schedules.scss @@ -1,7 +1,7 @@ .js-pipeline-schedule-form { .dropdown-select, .dropdown-menu-toggle { - width: 100%!important; + width: 100% !important; } .gl-field-error { @@ -74,3 +74,84 @@ margin-right: 3px; } } + +.pipeline-variable-list { + margin-left: 0; + margin-bottom: 0; + padding-left: 0; + list-style: none; + clear: both; +} + +.pipeline-variable-row { + display: flex; + align-items: flex-end; + + &:not(:last-child) { + margin-bottom: $gl-btn-padding; + } + + @media (max-width: $screen-sm-max) { + padding-right: $gl-col-padding; + } + + &:last-child { + .pipeline-variable-row-remove-button { + display: none; + } + + @media (max-width: $screen-sm-max) { + .pipeline-variable-value-input { + margin-right: $pipeline-variable-remove-button-width; + } + } + + @media (max-width: $screen-xs-max) { + .pipeline-variable-row-body { + margin-right: $pipeline-variable-remove-button-width; + } + } + } +} + +.pipeline-variable-row-body { + display: flex; + width: calc(75% - #{$gl-col-padding}); + padding-left: $gl-col-padding; + + @media (max-width: $screen-sm-max) { + width: 100%; + } + + @media (max-width: $screen-xs-max) { + display: block; + } +} + +.pipeline-variable-key-input { + margin-right: $gl-btn-padding; + + @media (max-width: $screen-xs-max) { + margin-bottom: $gl-btn-padding; + } +} + +.pipeline-variable-row-remove-button { + @include transition(color); + flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; + width: $pipeline-variable-remove-button-width; + height: $input-height; + padding: 0; + background: transparent; + border: 0; + color: $gl-text-color-secondary; + + &:hover, + &:focus { + outline: none; + color: $gl-text-color; + } +} diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index c207159f606..235c475ff26 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -286,8 +286,7 @@ table.u2f-registrations { } .user-callout { - margin: 0 auto; - max-width: $screen-lg-min; + margin: 20px -5px 0; .bordered-box { border: 1px solid $blue-300; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 7d7c34115f9..c1423965d0a 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -26,7 +26,7 @@ margin-bottom: 5px; } - & > .form-group { + > .form-group { padding-left: 0; } @@ -83,7 +83,7 @@ border: 1px solid $border-color; } - & + .select2 a { + + .select2 a { border-top-left-radius: 0; border-bottom-left-radius: 0; } @@ -587,9 +587,9 @@ pre.light-well { } .project-row { + @include basic-list-stats; display: flex; align-items: center; - @include basic-list-stats; } h3 { diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index de652a79369..d7a9dda3770 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -81,7 +81,7 @@ .todo-title { display: flex; - & > .title-item { + > .title-item { -webkit-flex: 0 0 auto; flex: 0 0 auto; margin: 0 2px; diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss index 8c87bc3cafd..798e060a261 100644 --- a/app/assets/stylesheets/pages/ui_dev_kit.scss +++ b/app/assets/stylesheets/pages/ui_dev_kit.scss @@ -5,13 +5,13 @@ } .example { + padding: 15px; + border: 1px dashed $ui-dev-kit-example-border; + margin-bottom: 15px; + &::before { content: "Example"; color: $ui-dev-kit-example-color; } - - padding: 15px; - border: 1px dashed $ui-dev-kit-example-border; - margin-bottom: 15px; } } diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 94d0a39f397..45c21c5d274 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -147,13 +147,13 @@ } ul.wiki-pages-list.content-list { - & ul { + ul { list-style: none; margin-left: 0; padding-left: 15px; } - & ul li { + ul li { padding: 5px 0; } } diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss new file mode 100644 index 00000000000..2890b6b1e49 --- /dev/null +++ b/app/assets/stylesheets/performance_bar.scss @@ -0,0 +1,103 @@ +@import "framework/variables"; +@import "peek/views/performance_bar"; +@import "peek/views/rblineprof"; + +#peek { + height: 35px; + background: $black; + line-height: 35px; + color: $perf-bar-text; + + &.disabled { + display: none; + } + + &.production { + background-color: $perf-bar-production; + } + + &.staging { + background-color: $perf-bar-staging; + } + + &.development { + background-color: $perf-bar-development; + } + + .wrapper { + width: 1000px; + margin: 0 auto; + } + + // UI Elements + .bucket { + background: $perf-bar-bucket-bg; + display: inline-block; + padding: 4px 6px; + font-family: Consolas, "Liberation Mono", Courier, monospace; + line-height: 1; + color: $perf-bar-bucket-color; + border-radius: 3px; + box-shadow: 0 1px 0 $perf-bar-bucket-box-shadow-from, inset 0 1px 2px $perf-bar-bucket-box-shadow-to; + + .hidden { + display: none; + } + + &:hover .hidden { + display: inline; + } + } + + strong { + color: $white-light; + } + + table { + color: $black; + + strong { + color: $black; + } + } + + .view { + margin-right: 15px; + float: left; + + &:last-child { + margin-right: 0; + } + } + + .css-truncate { + &.css-truncate-target, + .css-truncate-target { + display: inline-block; + max-width: 125px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: top; + } + + &.expandable:hover .css-truncate-target, + &.expandable:hover.css-truncate-target { + max-width: 10000px !important; + } + } +} + +#modal-peek-pg-queries-content { + color: $black; +} + +.peek-rblineprof-file { + pre.duration { + width: 280px; + } + + .data { + overflow: visible; + } +} diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss index 136d0c79467..113e6e86bb5 100644 --- a/app/assets/stylesheets/print.scss +++ b/app/assets/stylesheets/print.scss @@ -37,7 +37,7 @@ ul.notes-form, .issuable-details .content-block-small, .edit-link, .note-action-button { - display: none!important; + display: none !important; } pre { diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 4d4b8a8425f..c1bc4c0d675 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -71,6 +71,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController params[:application_setting][:disabled_oauth_sign_in_sources] = AuthHelper.button_based_providers.map(&:to_s) - Array(enabled_oauth_sign_in_sources) + + params[:application_setting][:restricted_visibility_levels]&.delete("") params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file] params.require(:application_setting).permit( @@ -111,6 +113,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :html_emails_enabled, :koding_enabled, :koding_url, + :password_authentication_enabled, :plantuml_enabled, :plantuml_url, :max_artifacts_size, @@ -124,6 +127,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :metrics_port, :metrics_sample_interval, :metrics_timeout, + :performance_bar_allowed_group_id, + :performance_bar_enabled, :recaptcha_enabled, :recaptcha_private_key, :recaptcha_site_key, @@ -131,7 +136,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :require_two_factor_authentication, :session_expire_delay, :sign_in_text, - :signin_enabled, :signup_enabled, :sentry_dsn, :sentry_enabled, diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index a1975c0e341..984d5398708 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -40,14 +40,14 @@ class Admin::ProjectsController < Admin::ApplicationController ::Projects::TransferService.new(@project, current_user, params.dup).execute(namespace) @project.reload - redirect_to admin_namespace_project_path(@project.namespace, @project) + redirect_to admin_project_path(@project) end def repository_check RepositoryCheck::SingleRepositoryWorker.perform_async(@project.id) redirect_to( - admin_namespace_project_path(@project.namespace, @project), + admin_project_path(@project), notice: 'Repository check was triggered.' ) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 824ce845706..43462b13903 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -9,7 +9,7 @@ class ApplicationController < ActionController::Base include SentryHelper include WorkhorseHelper include EnforcesTwoFactorAuthentication - include Peek::Rblineprof::CustomControllerHelpers + include WithPerformanceBar before_action :authenticate_user_from_private_token! before_action :authenticate_user_from_rss_token! @@ -68,21 +68,6 @@ class ApplicationController < ActionController::Base end end - def peek_enabled? - return false unless Gitlab::PerformanceBar.enabled? - return false unless current_user - - if RequestStore.active? - if RequestStore.store.key?(:peek_enabled) - RequestStore.store[:peek_enabled] - else - RequestStore.store[:peek_enabled] = cookies[:perf_bar_enabled].present? - end - else - cookies[:perf_bar_enabled].present? - end - end - protected # This filter handles both private tokens and personal access tokens @@ -110,6 +95,8 @@ class ApplicationController < ActionController::Base end def log_exception(exception) + Raven.capture_exception(exception) if sentry_enabled? + application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace application_trace.map!{ |t| " #{t}\n" } logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}" @@ -183,7 +170,7 @@ class ApplicationController < ActionController::Base end def check_password_expiration - if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user? + if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && current_user.allow_password_authentication? return redirect_to new_profile_password_path end end diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index fe331a883c1..3120916c5bb 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -5,10 +5,10 @@ class AutocompleteController < ApplicationController def users @users ||= User.none - @users = @users.search(params[:search]) if params[:search].present? - @users = @users.where.not(id: params[:skip_users]) if params[:skip_users].present? @users = @users.active @users = @users.reorder(:name) + @users = @users.search(params[:search]) if params[:search].present? + @users = @users.where.not(id: params[:skip_users]) if params[:skip_users].present? @users = @users.page(params[:page]).per(params[:per_page]) if params[:todo_filter].present? && current_user diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb index f87db4d9e84..782f0be9c4a 100644 --- a/app/controllers/concerns/creates_commit.rb +++ b/app/controllers/concerns/creates_commit.rb @@ -78,8 +78,7 @@ module CreatesCommit end def new_merge_request_path - namespace_project_new_merge_request_path( - @project_to_commit_into.namespace, + project_new_merge_request_path( @project_to_commit_into, merge_request: { source_project_id: @project_to_commit_into.id, @@ -91,7 +90,7 @@ module CreatesCommit end def existing_merge_request_path - namespace_project_merge_request_path(@project.namespace, @project, @merge_request) + project_merge_request_path(@project, @merge_request) end def merge_request_exists? diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 650ec1e326a..e18778cdf80 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -1,6 +1,7 @@ module IssuableCollections extend ActiveSupport::Concern include SortingHelper + include Gitlab::IssuableMetadata included do helper_method :issues_finder @@ -9,45 +10,12 @@ module IssuableCollections private - def issuable_meta_data(issuable_collection, collection_type) - # map has to be used here since using pluck or select will - # throw an error when ordering issuables by priority which inserts - # a new order into the collection. - # We cannot use reorder to not mess up the paginated collection. - issuable_ids = issuable_collection.map(&:id) - - return {} if issuable_ids.empty? - - issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type) - issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type) - issuable_merge_requests_count = - if collection_type == 'Issue' - MergeRequestsClosingIssues.count_for_collection(issuable_ids) - else - [] - end - - issuable_ids.each_with_object({}) do |id, issuable_meta| - downvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.downvote? } - upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? } - notes = issuable_note_count.find { |notes| notes.noteable_id == id } - merge_requests = issuable_merge_requests_count.find { |mr| mr.first == id } - - issuable_meta[id] = Issuable::IssuableMeta.new( - upvotes.try(:count).to_i, - downvotes.try(:count).to_i, - notes.try(:count).to_i, - merge_requests.try(:last).to_i - ) - end - end - def issues_collection issues_finder.execute.preload(:project, :author, :assignees, :labels, :milestone, project: :namespace) end def merge_requests_collection - merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :merge_request_diff, :head_pipeline, target_project: :namespace) + merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :head_pipeline, target_project: :namespace, merge_request_diff: :merge_request_diff_commits) end def issues_finder diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index 47d9ae350ae..c6b1e443de6 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -70,7 +70,7 @@ module MembershipActions def members_page_url if membershipable.is_a?(Project) - project_settings_members_path(membershipable) + project_project_members_path(membershipable) else polymorphic_url([membershipable, :members]) end diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index 1ff785ac2ca..081f3336780 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -45,7 +45,7 @@ module MilestoneActions def milestone_redirect_path if @project - namespace_project_milestone_path(@project.namespace, @project, @milestone) + project_milestone_path(@project, @milestone) elsif @group group_milestone_path(@group, @milestone.safe_title, title: @milestone.title) else diff --git a/app/controllers/concerns/repository_settings_redirect.rb b/app/controllers/concerns/repository_settings_redirect.rb index 0854c73a02f..0576f0e6e70 100644 --- a/app/controllers/concerns/repository_settings_redirect.rb +++ b/app/controllers/concerns/repository_settings_redirect.rb @@ -2,6 +2,6 @@ module RepositorySettingsRedirect extend ActiveSupport::Concern def redirect_to_repository_settings(project) - redirect_to namespace_project_settings_repository_path(project.namespace, project) + redirect_to project_settings_repository_path(project) end end diff --git a/app/controllers/concerns/requires_health_token.rb b/app/controllers/concerns/requires_health_token.rb deleted file mode 100644 index 34ab1a97649..00000000000 --- a/app/controllers/concerns/requires_health_token.rb +++ /dev/null @@ -1,25 +0,0 @@ -module RequiresHealthToken - extend ActiveSupport::Concern - included do - before_action :validate_health_check_access! - end - - private - - def validate_health_check_access! - render_404 unless token_valid? - end - - def token_valid? - token = params[:token].presence || request.headers['TOKEN'] - token.present? && - ActiveSupport::SecurityUtils.variable_size_secure_compare( - token, - current_application_settings.health_check_access_token - ) - end - - def render_404 - render file: Rails.root.join('public', '404'), layout: false, status: '404' - end -end diff --git a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb new file mode 100644 index 00000000000..ad2f4bbc486 --- /dev/null +++ b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb @@ -0,0 +1,33 @@ +module RequiresWhitelistedMonitoringClient + extend ActiveSupport::Concern + included do + before_action :validate_ip_whitelisted_or_valid_token! + end + + private + + def validate_ip_whitelisted_or_valid_token! + render_404 unless client_ip_whitelisted? || valid_token? + end + + def client_ip_whitelisted? + ip_whitelist.any? { |e| e.include?(Gitlab::RequestContext.client_ip) } + end + + def ip_whitelist + @ip_whitelist ||= Settings.monitoring.ip_whitelist.map(&IPAddr.method(:new)) + end + + def valid_token? + token = params[:token].presence || request.headers['TOKEN'] + token.present? && + ActiveSupport::SecurityUtils.variable_size_secure_compare( + token, + current_application_settings.health_check_access_token + ) + end + + def render_404 + render file: Rails.root.join('public', '404'), layout: false, status: '404' + end +end diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb index b68d76aeff0..ada0dde87fb 100644 --- a/app/controllers/concerns/spammable_actions.rb +++ b/app/controllers/concerns/spammable_actions.rb @@ -9,9 +9,9 @@ module SpammableActions def mark_as_spam if SpamService.new(spammable).mark_as_spam! - redirect_to spammable, notice: "#{spammable.spammable_entity_type.titlecase} was submitted to Akismet successfully." + redirect_to spammable_path, notice: "#{spammable.spammable_entity_type.titlecase} was submitted to Akismet successfully." else - redirect_to spammable, alert: 'Error with Akismet. Please check the logs for more info.' + redirect_to spammable_path, alert: 'Error with Akismet. Please check the logs for more info.' end end @@ -25,7 +25,7 @@ module SpammableActions def recaptcha_check_with_fallback(&fallback) if spammable.valid? - redirect_to spammable + redirect_to spammable_path elsif render_recaptcha? ensure_spam_config_loaded! @@ -56,6 +56,10 @@ module SpammableActions raise NotImplementedError, "#{self.class} does not implement #{__method__}" end + def spammable_path + raise NotImplementedError, "#{self.class} does not implement #{__method__}" + end + def authorize_submit_spammable! access_denied! unless current_user.admin? end diff --git a/app/controllers/concerns/with_performance_bar.rb b/app/controllers/concerns/with_performance_bar.rb new file mode 100644 index 00000000000..ed253042701 --- /dev/null +++ b/app/controllers/concerns/with_performance_bar.rb @@ -0,0 +1,17 @@ +module WithPerformanceBar + extend ActiveSupport::Concern + + included do + include Peek::Rblineprof::CustomControllerHelpers + end + + def peek_enabled? + return false unless Gitlab::PerformanceBar.enabled?(current_user) + + if RequestStore.active? + RequestStore.fetch(:peek_enabled) { cookies[:perf_bar_enabled].present? } + else + cookies[:perf_bar_enabled].present? + end + end +end diff --git a/app/controllers/dashboard/labels_controller.rb b/app/controllers/dashboard/labels_controller.rb index dd1d46a68c7..9dcb3a0eb6d 100644 --- a/app/controllers/dashboard/labels_controller.rb +++ b/app/controllers/dashboard/labels_controller.rb @@ -1,9 +1,14 @@ class Dashboard::LabelsController < Dashboard::ApplicationController def index - labels = LabelsFinder.new(current_user).execute - respond_to do |format| format.json { render json: LabelSerializer.new.represent_appearance(labels) } end end + + def labels + finder_params = { project_ids: projects.select(:id) } + labels = LabelsFinder.new(current_user, finder_params).execute + + GlobalLabel.build_collection(labels) + end end diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 6b1d418fc9a..5c10d7bc261 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -2,13 +2,13 @@ class Groups::MilestonesController < Groups::ApplicationController include MilestoneActions before_action :group_projects - before_action :milestone, only: [:show, :update, :merge_requests, :participants, :labels] - before_action :authorize_admin_milestones!, only: [:new, :create, :update] + before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels] + before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update] def index respond_to do |format| format.html do - @milestone_states = GlobalMilestone.states_count(@projects) + @milestone_states = GlobalMilestone.states_count(group_projects, group) @milestones = Kaminari.paginate_array(milestones).page(params[:page]) end format.json do @@ -22,49 +22,41 @@ class Groups::MilestonesController < Groups::ApplicationController end def create - project_ids = params[:milestone][:project_ids].reject(&:blank?) - title = milestone_params[:title] + @milestone = Milestones::CreateService.new(group, current_user, milestone_params).execute - if create_milestones(project_ids) - redirect_to milestone_path(title) + if @milestone.persisted? + redirect_to milestone_path else - render_new_with_error(project_ids.empty?) + render "new" end end def show end - def update - @milestone.milestones.each do |milestone| - Milestones::UpdateService.new(milestone.project, current_user, milestone_params).execute(milestone) - end - - redirect_back_or_default(default: milestone_path(@milestone.title)) + def edit + render_404 if @milestone.is_legacy_group_milestone? end - private - - def create_milestones(project_ids) - return false unless project_ids.present? + def update + # Keep this compatible with legacy group milestones where we have to update + # all projects milestones states at once. + if @milestone.is_legacy_group_milestone? + update_params = milestone_params.select { |key| key == "state_event" } + milestones = @milestone.milestones + else + update_params = milestone_params + milestones = [@milestone] + end - ActiveRecord::Base.transaction do - @projects.where(id: project_ids).each do |project| - Milestones::CreateService.new(project, current_user, milestone_params).execute - end + milestones.each do |milestone| + Milestones::UpdateService.new(milestone.parent, current_user, update_params).execute(milestone) end - true - rescue ActiveRecord::ActiveRecordError => e - flash.now[:alert] = "An error occurred while creating the milestone: #{e.message}" - false + redirect_to milestone_path end - def render_new_with_error(empty_project_ids) - @milestone = Milestone.new(milestone_params) - @milestone.errors.add(:base, "Please select at least one project.") if empty_project_ids - render :new - end + private def authorize_admin_milestones! return render_404 unless can?(current_user, :admin_milestones, group) @@ -74,16 +66,31 @@ class Groups::MilestonesController < Groups::ApplicationController params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event) end - def milestone_path(title) - group_milestone_path(@group, title.to_slug.to_s, title: title) + def milestone_path + if @milestone.is_legacy_group_milestone? + group_milestone_path(group, @milestone.safe_title, title: @milestone.title) + else + group_milestone_path(group, @milestone.iid) + end end def milestones - @milestones = GroupMilestone.build_collection(@group, @projects, params) + search_params = params.merge(group_ids: group.id) + + milestones = MilestonesFinder.new(search_params).execute + legacy_milestones = GroupMilestone.build_collection(group, group_projects, params) + + milestones + legacy_milestones end def milestone - @milestone = GroupMilestone.build(@group, @projects, params[:title]) + @milestone = + if params[:title] + GroupMilestone.build(group, group_projects, params[:title]) + else + group.milestones.find_by_iid(params[:id]) + end + render_404 unless @milestone end end diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb new file mode 100644 index 00000000000..0142ad8278c --- /dev/null +++ b/app/controllers/groups/settings/ci_cd_controller.rb @@ -0,0 +1,24 @@ +module Groups + module Settings + class CiCdController < Groups::ApplicationController + before_action :authorize_admin_pipeline! + + def show + define_secret_variables + end + + private + + def define_secret_variables + @variable = Ci::GroupVariable.new(group: group) + .present(current_user: current_user) + @variables = group.variables.order_key_asc + .map { |variable| variable.present(current_user: current_user) } + end + + def authorize_admin_pipeline! + return render_404 unless can?(current_user, :admin_pipeline, group) + end + end + end +end diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb new file mode 100644 index 00000000000..10038ff3ad9 --- /dev/null +++ b/app/controllers/groups/variables_controller.rb @@ -0,0 +1,64 @@ +module Groups + class VariablesController < Groups::ApplicationController + before_action :variable, only: [:show, :update, :destroy] + before_action :authorize_admin_build! + + def index + redirect_to group_settings_ci_cd_path(group) + end + + def show + end + + def update + if variable.update(variable_params) + redirect_to group_variables_path(group), + notice: 'Variable was successfully updated.' + else + render "show" + end + end + + def create + @variable = group.variables.create(variable_params) + .present(current_user: current_user) + + if @variable.persisted? + redirect_to group_settings_ci_cd_path(group), + notice: 'Variable was successfully created.' + else + render "show" + end + end + + def destroy + if variable.destroy + redirect_to group_settings_ci_cd_path(group), + status: 302, + notice: 'Variable was successfully removed.' + else + redirect_to group_settings_ci_cd_path(group), + status: 302, + notice: 'Failed to remove the variable.' + end + end + + private + + def variable_params + params.require(:variable).permit(*variable_params_attributes) + end + + def variable_params_attributes + %i[key value protected] + end + + def variable + @variable ||= group.variables.find(params[:id]).present(current_user: current_user) + end + + def authorize_admin_build! + return render_404 unless can?(current_user, :admin_build, group) + end + end +end diff --git a/app/controllers/health_check_controller.rb b/app/controllers/health_check_controller.rb index 5d3109b7187..c3d18991fd4 100644 --- a/app/controllers/health_check_controller.rb +++ b/app/controllers/health_check_controller.rb @@ -1,3 +1,3 @@ class HealthCheckController < HealthCheck::HealthCheckController - include RequiresHealthToken + include RequiresWhitelistedMonitoringClient end diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb index abc832e6ddc..98c2aaa3526 100644 --- a/app/controllers/health_controller.rb +++ b/app/controllers/health_controller.rb @@ -1,10 +1,13 @@ class HealthController < ActionController::Base protect_from_forgery with: :exception - include RequiresHealthToken + include RequiresWhitelistedMonitoringClient CHECKS = [ Gitlab::HealthChecks::DbCheck, - Gitlab::HealthChecks::RedisCheck, + Gitlab::HealthChecks::Redis::RedisCheck, + Gitlab::HealthChecks::Redis::CacheCheck, + Gitlab::HealthChecks::Redis::QueuesCheck, + Gitlab::HealthChecks::Redis::SharedStateCheck, Gitlab::HealthChecks::FsShardsCheck ].freeze diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 7625187c7be..0982a61902b 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -63,7 +63,7 @@ class InvitesController < ApplicationController when Project project = member.source label = "project #{project.name_with_namespace}" - path = namespace_project_path(project.namespace, project) + path = project_path(project) when Group group = member.source label = "group #{group.name}" diff --git a/app/controllers/metrics_controller.rb b/app/controllers/metrics_controller.rb index 0e9a19c0b6f..37587a52eaf 100644 --- a/app/controllers/metrics_controller.rb +++ b/app/controllers/metrics_controller.rb @@ -1,12 +1,12 @@ class MetricsController < ActionController::Base - include RequiresHealthToken + include RequiresWhitelistedMonitoringClient protect_from_forgery with: :exception before_action :validate_prometheus_metrics def index - render text: metrics_service.metrics_text, content_type: 'text/plain; verssion=0.0.4' + render text: metrics_service.metrics_text, content_type: 'text/plain; version=0.0.4' end private diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index b82681b197e..323d5d26eb6 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,5 +1,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController include AuthenticatesWithTwoFactor + include Devise::Controllers::Rememberable protect_from_forgery except: [:kerberos, :saml, :cas3] @@ -115,8 +116,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController if @user.persisted? && @user.valid? log_audit_event(@user, with: oauth['provider']) if @user.two_factor_enabled? + params[:remember_me] = '1' if remember_me? prompt_for_two_factor(@user) else + remember_me(@user) if remember_me? sign_in_and_redirect(@user) end else @@ -147,4 +150,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController AuditEventService.new(user, user, options) .for_authentication.security_event end + + def remember_me? + request_params = request.env['omniauth.params'] + (request_params['remember_me'] == '1') if request_params.present? + end end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index a8575e037e4..aa8cf630032 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -1,6 +1,8 @@ class PasswordsController < Devise::PasswordsController + include Gitlab::CurrentSettings + before_action :resource_from_email, only: [:create] - before_action :prevent_ldap_reset, only: [:create] + before_action :check_password_authentication_available, only: [:create] before_action :throttle_reset, only: [:create] def edit @@ -25,7 +27,7 @@ class PasswordsController < Devise::PasswordsController def update super do |resource| - if resource.valid? && resource.require_password? + if resource.valid? && resource.require_password_creation? resource.update_attribute(:password_automatically_set, false) end end @@ -38,11 +40,11 @@ class PasswordsController < Devise::PasswordsController self.resource = resource_class.find_by_email(email) end - def prevent_ldap_reset - return unless resource && resource.ldap_user? + def check_password_authentication_available + return if current_application_settings.password_authentication_enabled? && (resource.nil? || resource.allow_password_authentication?) redirect_to after_sending_reset_password_instructions_path_for(resource_name), - alert: "Cannot reset password for LDAP user." + alert: "Password authentication is unavailable." end def throttle_reset diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb index 10145bae0d3..c423761ab24 100644 --- a/app/controllers/profiles/passwords_controller.rb +++ b/app/controllers/profiles/passwords_controller.rb @@ -77,7 +77,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController end def authorize_change_password! - return render_404 if @user.ldap_user? + render_404 unless @user.allow_password_authentication? end def user_params diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 3d7ce4f0222..95de3a44641 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -76,13 +76,13 @@ class Projects::ApplicationController < ApplicationController def require_non_empty_project # Be sure to return status code 303 to avoid a double DELETE: # http://api.rubyonrails.org/classes/ActionController/Redirecting.html - redirect_to namespace_project_path(@project.namespace, @project), status: 303 if @project.empty_repo? + redirect_to project_path(@project), status: 303 if @project.empty_repo? end def require_branch_head unless @repository.branch_exists?(@ref) redirect_to( - namespace_project_tree_path(@project.namespace, @project, @ref), + project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on a branch" ) end diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index ea036b1f705..f637a9a803b 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -46,7 +46,7 @@ class Projects::ArtifactsController < Projects::ApplicationController def keep build.keep_artifacts! - redirect_to namespace_project_job_path(project.namespace, project, build) + redirect_to project_job_path(project, build) end def latest_succeeded diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index a82d6fd5a4a..49ea2945675 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -27,9 +27,9 @@ class Projects::BlobController < Projects::ApplicationController def create create_commit(Files::CreateService, success_notice: "The file has been successfully created.", - success_path: -> { namespace_project_blob_path(@project.namespace, @project, File.join(@branch_name, @file_path)) }, + success_path: -> { project_blob_path(@project, File.join(@branch_name, @file_path)) }, failure_view: :new, - failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref)) + failure_path: project_new_blob_path(@project, @ref)) end def show @@ -63,7 +63,7 @@ class Projects::BlobController < Projects::ApplicationController @path = params[:file_path] if params[:file_path].present? create_commit(Files::UpdateService, success_path: -> { after_edit_path }, failure_view: :edit, - failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) + failure_path: project_blob_path(@project, @id)) rescue Files::UpdateService::FileChangedError @conflict = true @@ -83,9 +83,9 @@ class Projects::BlobController < Projects::ApplicationController def destroy create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.", - success_path: -> { namespace_project_tree_path(@project.namespace, @project, @branch_name) }, + success_path: -> { project_tree_path(@project, @branch_name) }, failure_view: :show, - failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) + failure_path: project_blob_path(@project, @id)) end def diff @@ -118,7 +118,7 @@ class Projects::BlobController < Projects::ApplicationController else if tree = @repository.tree(@commit.id, @path) if tree.entries.any? - return redirect_to namespace_project_tree_path(@project.namespace, @project, File.join(@ref, @path)) + return redirect_to project_tree_path(@project, File.join(@ref, @path)) end end @@ -143,10 +143,10 @@ class Projects::BlobController < Projects::ApplicationController def after_edit_path from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid]) if from_merge_request && @branch_name == @ref - diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + + diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) + "##{hexdigest(@path)}" else - namespace_project_blob_path(@project.namespace, @project, File.join(@branch_name, @path)) + project_blob_path(@project, File.join(@branch_name, @path)) end end diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 94a752c21eb..86058531179 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -52,7 +52,7 @@ class Projects::BranchesController < Projects::ApplicationController redirect_to url_to_autodeploy_setup(project, branch_name), notice: view_context.autodeploy_flash_notice(branch_name) else - redirect_to namespace_project_tree_path(@project.namespace, @project, branch_name) + redirect_to project_tree_path(@project, branch_name) end else @error = result[:message] @@ -62,7 +62,7 @@ class Projects::BranchesController < Projects::ApplicationController format.json do if result[:status] == :success - render json: { name: branch_name, url: namespace_project_tree_url(@project.namespace, @project, branch_name) } + render json: { name: branch_name, url: project_tree_url(@project, branch_name) } else render json: result[:messsage], status: :unprocessable_entity end @@ -79,7 +79,7 @@ class Projects::BranchesController < Projects::ApplicationController flash_type = result[:status] == :error ? :alert : :notice flash[flash_type] = result[:message] - redirect_to namespace_project_branches_path(@project.namespace, @project), status: 303 + redirect_to project_branches_path(@project), status: 303 end format.js { render nothing: true, status: result[:return_code] } @@ -90,7 +90,7 @@ class Projects::BranchesController < Projects::ApplicationController def destroy_all_merged DeleteMergedBranchesService.new(@project, current_user).async_execute - redirect_to namespace_project_branches_path(@project.namespace, @project), + redirect_to project_branches_path(@project), notice: 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.' end @@ -106,8 +106,7 @@ class Projects::BranchesController < Projects::ApplicationController end def url_to_autodeploy_setup(project, branch_name) - namespace_project_new_blob_path( - project.namespace, + project_new_blob_path( project, branch_name, file_name: '.gitlab-ci.yml', diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb index f34a198634e..b45e5d7ff43 100644 --- a/app/controllers/projects/build_artifacts_controller.rb +++ b/app/controllers/projects/build_artifacts_controller.rb @@ -7,23 +7,23 @@ class Projects::BuildArtifactsController < Projects::ApplicationController before_action :validate_artifacts! def download - redirect_to download_namespace_project_job_artifacts_path(project.namespace, project, job) + redirect_to download_project_job_artifacts_path(project, job) end def browse - redirect_to browse_namespace_project_job_artifacts_path(project.namespace, project, job, path: params[:path]) + redirect_to browse_project_job_artifacts_path(project, job, path: params[:path]) end def file - redirect_to file_namespace_project_job_artifacts_path(project.namespace, project, job, path: params[:path]) + redirect_to file_project_job_artifacts_path(project, job, path: params[:path]) end def raw - redirect_to raw_namespace_project_job_artifacts_path(project.namespace, project, job, path: params[:path]) + redirect_to raw_project_job_artifacts_path(project, job, path: params[:path]) end def latest_succeeded - redirect_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, job, ref_name_and_path: params[:ref_name_and_path], job: params[:job]) + redirect_to latest_succeeded_project_artifacts_path(project, job, ref_name_and_path: params[:ref_name_and_path], job: params[:job]) end private diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 1334a231788..230b072dcea 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -2,15 +2,15 @@ class Projects::BuildsController < Projects::ApplicationController before_action :authorize_read_build! def index - redirect_to namespace_project_jobs_path(project.namespace, project) + redirect_to project_jobs_path(project) end def show - redirect_to namespace_project_job_path(project.namespace, project, job) + redirect_to project_job_path(project, job) end def raw - redirect_to raw_namespace_project_job_path(project.namespace, project, job) + redirect_to raw_project_job_path(project, job) end private diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 7c3cce1c241..6de125e7e80 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -38,9 +38,14 @@ class Projects::CommitController < Projects::ApplicationController format.json do Gitlab::PollingInterval.set_header(response, interval: 10_000) - render json: PipelineSerializer - .new(project: @project, current_user: @current_user) - .represent(@pipelines) + render json: { + pipelines: PipelineSerializer + .new(project: @project, current_user: @current_user) + .represent(@pipelines), + count: { + all: @pipelines.count + } + } end end end @@ -80,16 +85,16 @@ class Projects::CommitController < Projects::ApplicationController end def successful_change_path - referenced_merge_request_url || namespace_project_commits_url(@project.namespace, @project, @branch_name) + referenced_merge_request_url || project_commits_url(@project, @branch_name) end def failed_change_path - referenced_merge_request_url || namespace_project_commit_url(@project.namespace, @project, params[:id]) + referenced_merge_request_url || project_commit_url(@project, params[:id]) end def referenced_merge_request_url if merge_request = @commit.merged_merge_request(current_user) - namespace_project_merge_request_url(merge_request.target_project.namespace, merge_request.target_project, merge_request) + project_merge_request_url(merge_request.target_project, merge_request) end end diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index ef400c4d745..c8613c0d634 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -31,9 +31,9 @@ class Projects::CompareController < Projects::ApplicationController from: params[:from].presence, to: params[:to].presence } - redirect_to namespace_project_compare_index_path(@project.namespace, @project, from_to_vars) + redirect_to project_compare_index_path(@project, from_to_vars) else - redirect_to namespace_project_compare_path(@project.namespace, @project, + redirect_to project_compare_path(@project, params[:from], params[:to]) end end diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index f88a1ffd1e9..29e223a5273 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -15,6 +15,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController respond_to do |format| format.html format.json do + Gitlab::PollingInterval.set_header(response, interval: 3_000) + render json: { environments: EnvironmentSerializer .new(project: @project, current_user: @current_user) @@ -63,7 +65,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController @environment = project.environments.create(environment_params) if @environment.persisted? - redirect_to namespace_project_environment_path(project.namespace, project, @environment) + redirect_to project_environment_path(project, @environment) else render :new end @@ -71,7 +73,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController def update if @environment.update(environment_params) - redirect_to namespace_project_environment_path(project.namespace, project, @environment) + redirect_to project_environment_path(project, @environment) else render :edit end @@ -86,7 +88,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController if stop_action polymorphic_url([project.namespace.becomes(Namespace), project, stop_action]) else - namespace_project_environment_url(project.namespace, project, @environment) + project_environment_url(project, @environment) end respond_to do |format| diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index 1eb3800e49d..3f83bef2c79 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -44,12 +44,12 @@ class Projects::ForksController < Projects::ApplicationController if @forked_project.saved? && @forked_project.forked? if @forked_project.import_in_progress? - redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project, continue: continue_params) + redirect_to project_import_path(@forked_project, continue: continue_params) else if continue_params redirect_to continue_params[:to], notice: continue_params[:notice] else - redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project '#{@forked_project.name}' was successfully forked." + redirect_to project_path(@forked_project), notice: "The project '#{@forked_project.name}' was successfully forked." end end else diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index df5221fe95f..57372f9e79d 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -29,7 +29,7 @@ class Projects::GraphsController < Projects::ApplicationController end def ci - redirect_to charts_namespace_project_pipelines_path(@project.namespace, @project) + redirect_to charts_project_pipelines_path(@project) end private diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb index deb33a2f0ff..f59200d3b1f 100644 --- a/app/controllers/projects/group_links_controller.rb +++ b/app/controllers/projects/group_links_controller.rb @@ -22,7 +22,7 @@ class Projects::GroupLinksController < Projects::ApplicationController flash[:alert] = 'Please select a group.' end - redirect_to namespace_project_settings_members_path(project.namespace, project) + redirect_to project_project_members_path(project) end def update @@ -36,7 +36,7 @@ class Projects::GroupLinksController < Projects::ApplicationController respond_to do |format| format.html do - redirect_to namespace_project_settings_members_path(project.namespace, project), status: 302 + redirect_to project_project_members_path(project), status: 302 end format.js { head :ok } end diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb index 354f0d6db3a..b9c4b29580a 100644 --- a/app/controllers/projects/hook_logs_controller.rb +++ b/app/controllers/projects/hook_logs_controller.rb @@ -18,7 +18,7 @@ class Projects::HookLogsController < Projects::ApplicationController set_hook_execution_notice(status, message) - redirect_to edit_namespace_project_hook_path(@project.namespace, @project, @hook) + redirect_to edit_project_hook_path(@project, @hook) end private diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index f5143280154..18895c3f0f3 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -17,7 +17,7 @@ class Projects::HooksController < Projects::ApplicationController @hooks = @project.hooks.select(&:persisted?) flash[:alert] = @hook.errors.full_messages.join.html_safe end - redirect_to namespace_project_settings_integrations_path(@project.namespace, @project) + redirect_to project_settings_integrations_path(@project) end def edit @@ -26,7 +26,7 @@ class Projects::HooksController < Projects::ApplicationController def update if hook.update_attributes(hook_params) flash[:notice] = 'Hook was successfully updated.' - redirect_to namespace_project_settings_integrations_path(@project.namespace, @project) + redirect_to project_settings_integrations_path(@project) else render 'edit' end @@ -47,7 +47,7 @@ class Projects::HooksController < Projects::ApplicationController def destroy hook.destroy - redirect_to namespace_project_settings_integrations_path(@project.namespace, @project), status: 302 + redirect_to project_settings_integrations_path(@project), status: 302 end private diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index 4b143434ea5..49aa32119ef 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -17,7 +17,7 @@ class Projects::ImportsController < Projects::ApplicationController @project.reload.import_schedule end - redirect_to namespace_project_import_path(@project.namespace, @project) + redirect_to project_import_path(@project) end def show @@ -25,10 +25,10 @@ class Projects::ImportsController < Projects::ApplicationController if continue_params redirect_to continue_params[:to], notice: continue_params[:notice] else - redirect_to namespace_project_path(@project.namespace, @project), notice: finished_notice + redirect_to project_path(@project), notice: finished_notice end elsif @project.import_failed? - redirect_to new_namespace_project_import_path(@project.namespace, @project) + redirect_to new_project_import_path(@project) else if continue_params && continue_params[:notice_now] flash.now[:notice] = continue_params[:notice_now] @@ -50,19 +50,19 @@ class Projects::ImportsController < Projects::ApplicationController def require_no_repo if @project.repository_exists? - redirect_to namespace_project_path(@project.namespace, @project) + redirect_to project_path(@project) end end def redirect_if_progress if @project.import_in_progress? - redirect_to namespace_project_import_path(@project.namespace, @project) + redirect_to project_import_path(@project) end end def redirect_if_no_import if @project.repository_exists? && @project.no_import? - redirect_to namespace_project_path(@project.namespace, @project) + redirect_to project_path(@project) end end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 54f108353cd..13f03e7e63e 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -227,7 +227,7 @@ class Projects::IssuesController < Projects::ApplicationController def issue return @issue if defined?(@issue) # The Sortable default scope causes performance issues when used with find_by - @noteable = @issue ||= @project.issues.find_by!(iid: params[:id]) + @noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take! return render_404 unless can?(current_user, :read_issue, @issue) @@ -238,6 +238,10 @@ class Projects::IssuesController < Projects::ApplicationController alias_method :awardable, :issue alias_method :spammable, :issue + def spammable_path + project_issue_path(@project, @issue) + end + def authorize_update_issue! return render_404 unless can?(current_user, :update_issue, @issue) end diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index cb4f46388fd..96abdac91b6 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -38,7 +38,7 @@ class Projects::JobsController < Projects::ApplicationController build.cancel if can?(current_user, :update_build, build) end - redirect_to namespace_project_jobs_path(project.namespace, project) + redirect_to project_jobs_path(project) end def show @@ -108,7 +108,7 @@ class Projects::JobsController < Projects::ApplicationController def erase if @build.erase(erased_by: current_user) - redirect_to namespace_project_job_path(project.namespace, project, @build), + redirect_to project_job_path(project, @build), notice: "Build has been successfully erased!" else respond_422 @@ -137,6 +137,6 @@ class Projects::JobsController < Projects::ApplicationController end def build_path(build) - namespace_project_job_path(build.project.namespace, build.project, build) + project_job_path(build.project, build) end end diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index daa973c9281..480a2dff262 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -33,7 +33,7 @@ class Projects::LabelsController < Projects::ApplicationController if @label.valid? respond_to do |format| - format.html { redirect_to namespace_project_labels_path(@project.namespace, @project) } + format.html { redirect_to project_labels_path(@project) } format.json { render json: @label } end else @@ -51,7 +51,7 @@ class Projects::LabelsController < Projects::ApplicationController @label = Labels::UpdateService.new(label_params).execute(@label) if @label.valid? - redirect_to namespace_project_labels_path(@project.namespace, @project) + redirect_to project_labels_path(@project) else render :edit end @@ -61,12 +61,11 @@ class Projects::LabelsController < Projects::ApplicationController Gitlab::IssuesLabels.generate(@project) if params[:redirect] == 'issues' - redirect_to namespace_project_issues_path(@project.namespace, @project) + redirect_to project_issues_path(@project) elsif params[:redirect] == 'merge_requests' - redirect_to namespace_project_merge_requests_path(@project.namespace, - @project) + redirect_to project_merge_requests_path(@project) else - redirect_to namespace_project_labels_path(@project.namespace, @project) + redirect_to project_labels_path(@project) end end @@ -74,7 +73,7 @@ class Projects::LabelsController < Projects::ApplicationController @label.destroy @labels = find_labels - redirect_to namespace_project_labels_path(@project.namespace, @project), + redirect_to project_labels_path(@project), status: 302, notice: 'Label was removed' end @@ -114,7 +113,7 @@ class Projects::LabelsController < Projects::ApplicationController return render_404 unless promote_service.execute(@label) respond_to do |format| format.html do - redirect_to(namespace_project_labels_path(@project.namespace, @project), + redirect_to(project_labels_path(@project), notice: 'Label was promoted to a Group Label') end format.js @@ -125,7 +124,7 @@ class Projects::LabelsController < Projects::ApplicationController respond_to do |format| format.html do - redirect_to(namespace_project_labels_path(@project.namespace, @project), + redirect_to(project_labels_path(@project), notice: 'Failed to promote label due to internal error. Please contact administrators.') end format.js diff --git a/app/controllers/projects/mattermosts_controller.rb b/app/controllers/projects/mattermosts_controller.rb index 38f7e6eb5e9..0f6add3e287 100644 --- a/app/controllers/projects/mattermosts_controller.rb +++ b/app/controllers/projects/mattermosts_controller.rb @@ -16,12 +16,10 @@ class Projects::MattermostsController < Projects::ApplicationController if result flash[:notice] = 'This service is now configured' - redirect_to edit_namespace_project_service_path( - @project.namespace, @project, service) + redirect_to edit_project_service_path(@project, service) else flash[:alert] = message || 'Failed to configure service' - redirect_to new_namespace_project_mattermost_path( - @project.namespace, @project) + redirect_to new_project_mattermost_path(@project) end end diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb index 5de0f828010..6602b204fcb 100644 --- a/app/controllers/projects/merge_requests/application_controller.rb +++ b/app/controllers/projects/merge_requests/application_controller.rb @@ -17,8 +17,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont end def merge_request_params - params.require(:merge_request) - .permit(merge_request_params_attributes) + params.require(:merge_request).permit(merge_request_params_attributes) end def merge_request_params_attributes diff --git a/app/controllers/projects/merge_requests/conflicts_controller.rb b/app/controllers/projects/merge_requests/conflicts_controller.rb index a71f23e790d..28afef101a9 100644 --- a/app/controllers/projects/merge_requests/conflicts_controller.rb +++ b/app/controllers/projects/merge_requests/conflicts_controller.rb @@ -52,7 +52,7 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap flash[:notice] = 'All merge conflicts were resolved. The merge request can now be merged.' - render json: { redirect_to: namespace_project_merge_request_url(@project.namespace, @project, @merge_request, resolved_conflicts: true) } + render json: { redirect_to: project_merge_request_url(@project, @merge_request, resolved_conflicts: true) } rescue Gitlab::Conflict::ResolutionError => e render status: :bad_request, json: { message: e.message } end diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index da058da795e..f35d53896ba 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -107,7 +107,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap @target_project = @merge_request.target_project @source_project = @merge_request.source_project - @commits = @merge_request.compare_commits.reverse + @commits = @merge_request.commits @commit = @merge_request.diff_head_commit @note_counts = Note.where(commit_id: @commits.map(&:id)) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 04f8e95aa09..70c41da4de5 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -112,9 +112,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo Gitlab::PollingInterval.set_header(response, interval: 10_000) - render json: PipelineSerializer - .new(project: @project, current_user: @current_user) - .represent(@pipelines) + render json: { + pipelines: PipelineSerializer + .new(project: @project, current_user: @current_user) + .represent(@pipelines), + count: { + all: @pipelines.count + } + } end def edit @@ -211,21 +216,18 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo stop_url = if environment.stop_action? && can?(current_user, :create_deployment, environment) - stop_namespace_project_environment_path(project.namespace, project, environment) + stop_project_environment_path(project, environment) end metrics_url = if can?(current_user, :read_environment, environment) && environment.has_metrics? - metrics_namespace_project_environment_deployment_path(environment.project.namespace, - environment.project, - environment, - deployment) + metrics_project_environment_deployment_path(environment.project, environment, deployment) end { id: environment.id, name: environment.name, - url: namespace_project_environment_path(project.namespace, project, environment), + url: project_environment_path(project, environment), metrics_url: metrics_url, stop_url: stop_url, external_url: environment.external_url, diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 953b1e83e49..c94384d2a1a 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -13,20 +13,16 @@ class Projects::MilestonesController < Projects::ApplicationController respond_to :html def index - @milestones = - case params[:state] - when 'all' then @project.milestones - when 'closed' then @project.milestones.closed - else @project.milestones.active - end - @sort = params[:sort] || 'due_date_asc' - @milestones = @milestones.sort(@sort) + @milestones = milestones.sort(@sort) respond_to do |format| format.html do @project_namespace = @project.namespace.becomes(Namespace) - @milestones = @milestones.includes(:project) + # We need to show group milestones in the JSON response + # so that people can filter by and assign group milestones, + # but we don't need to show them on the project milestones page itself. + @milestones = @milestones.for_projects @milestones = @milestones.page(params[:page]) end format.json do @@ -45,14 +41,14 @@ class Projects::MilestonesController < Projects::ApplicationController end def show + @project_namespace = @project.namespace.becomes(Namespace) end def create @milestone = Milestones::CreateService.new(project, current_user, milestone_params).execute - if @milestone.save - redirect_to namespace_project_milestone_path(@project.namespace, - @project, @milestone) + if @milestone.valid? + redirect_to project_milestone_path(@project, @milestone) else render "new" end @@ -65,8 +61,7 @@ class Projects::MilestonesController < Projects::ApplicationController format.js format.html do if @milestone.valid? - redirect_to namespace_project_milestone_path(@project.namespace, - @project, @milestone) + redirect_to project_milestone_path(@project, @milestone) else render :edit end @@ -87,6 +82,18 @@ class Projects::MilestonesController < Projects::ApplicationController protected + def milestones + @milestones ||= begin + if @project.group && can?(current_user, :read_group, @project.group) + group = @project.group + end + + search_params = params.merge(project_ids: @project.id, group_ids: group&.id) + + MilestonesFinder.new(search_params).execute + end + end + def milestone @milestone ||= @project.milestones.find_by!(iid: params[:id]) end diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb index 33a152ad34f..dfa5e4f7f46 100644 --- a/app/controllers/projects/network_controller.rb +++ b/app/controllers/projects/network_controller.rb @@ -8,8 +8,8 @@ class Projects::NetworkController < Projects::ApplicationController before_action :assign_commit def show - @url = namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json)) - @commit_url = namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s") + @url = project_network_path(@project, @ref, @options.merge(format: :json)) + @commit_url = project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s") respond_to do |format| format.html do diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index 28b383e69eb..d421b1a8eb5 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -15,7 +15,7 @@ class Projects::PagesController < Projects::ApplicationController respond_to do |format| format.html do - redirect_to namespace_project_pages_path(@project.namespace, @project), + redirect_to project_pages_path(@project), status: 302, notice: 'Pages were removed' end diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb index dbd011f6c5d..15e77d854dc 100644 --- a/app/controllers/projects/pages_domains_controller.rb +++ b/app/controllers/projects/pages_domains_controller.rb @@ -16,7 +16,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController @domain = @project.pages_domains.create(pages_domain_params) if @domain.valid? - redirect_to namespace_project_pages_path(@project.namespace, @project) + redirect_to project_pages_path(@project) else render 'new' end @@ -27,7 +27,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController respond_to do |format| format.html do - redirect_to namespace_project_pages_path(@project.namespace, @project), + redirect_to project_pages_path(@project), status: 302, notice: 'Domain was removed' end diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index 60db179277b..ec7c645df5a 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -1,11 +1,11 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController + before_action :schedule, except: [:index, :new, :create] + before_action :authorize_read_pipeline_schedule! before_action :authorize_create_pipeline_schedule!, only: [:new, :create] - before_action :authorize_update_pipeline_schedule!, only: [:edit, :take_ownership, :update] + before_action :authorize_update_pipeline_schedule!, except: [:index, :new, :create] before_action :authorize_admin_pipeline_schedule!, only: [:destroy] - before_action :schedule, only: [:edit, :update, :destroy, :take_ownership] - def index @scope = params[:scope] @all_schedules = PipelineSchedulesFinder.new(@project).execute @@ -34,7 +34,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController def update if schedule.update(schedule_params) - redirect_to namespace_project_pipeline_schedules_path(@project.namespace.becomes(Namespace), @project) + redirect_to project_pipeline_schedules_path(@project) else render :edit end @@ -53,7 +53,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController redirect_to pipeline_schedules_path(@project), status: 302 else redirect_to pipeline_schedules_path(@project), - status: 302, + status: :forbidden, alert: _("Failed to remove the pipeline schedule") end end @@ -66,6 +66,15 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController def schedule_params params.require(:schedule) - .permit(:description, :cron, :cron_timezone, :ref, :active) + .permit(:description, :cron, :cron_timezone, :ref, :active, + variables_attributes: [:id, :key, :value, :_destroy] ) + end + + def authorize_update_pipeline_schedule! + return access_denied! unless can?(current_user, :update_pipeline_schedule, schedule) + end + + def authorize_admin_pipeline_schedule! + return access_denied! unless can?(current_user, :admin_pipeline_schedule, schedule) end end diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 303e91a8dc0..a3bfbf0694e 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -60,7 +60,7 @@ class Projects::PipelinesController < Projects::ApplicationController .execute(:web, ignore_skip_ci: true, save_on_errors: false) if @pipeline.persisted? - redirect_to namespace_project_pipeline_path(project.namespace, project, @pipeline) + redirect_to project_pipeline_path(project, @pipeline) else render 'new' end @@ -111,7 +111,7 @@ class Projects::PipelinesController < Projects::ApplicationController respond_to do |format| format.html do - redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project) + redirect_back_or_default default: project_pipelines_path(project) end format.json { head :no_content } @@ -123,7 +123,7 @@ class Projects::PipelinesController < Projects::ApplicationController respond_to do |format| format.html do - redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project) + redirect_back_or_default default: project_pipelines_path(project) end format.json { head :no_content } diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb index 38a47651000..9d24ebe2138 100644 --- a/app/controllers/projects/pipelines_settings_controller.rb +++ b/app/controllers/projects/pipelines_settings_controller.rb @@ -2,13 +2,13 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController before_action :authorize_admin_pipeline! def show - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project, params: params) + redirect_to project_settings_ci_cd_path(@project, params: params) end def update if @project.update_attributes(update_params) flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated." - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project) + redirect_to project_settings_ci_cd_path(@project) else render 'show' end @@ -23,7 +23,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController def update_params params.require(:project).permit( :runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, - :public_builds, :auto_cancel_pending_pipelines + :public_builds, :auto_cancel_pending_pipelines, :ci_config_path ) end end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index d2d26738582..f8ff7413b53 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -6,8 +6,23 @@ class Projects::ProjectMembersController < Projects::ApplicationController before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access] def index - sort = params[:sort].presence || sort_value_name - redirect_to namespace_project_settings_members_path(@project.namespace, @project, sort: sort) + @sort = params[:sort].presence || sort_value_name + @group_links = @project.project_group_links + + @skip_groups = @group_links.pluck(:group_id) + @skip_groups << @project.namespace_id unless @project.personal? + @skip_groups += @project.group.ancestors.pluck(:id) if @project.group + + @project_members = MembersFinder.new(@project, current_user).execute + + if params[:search].present? + @project_members = @project_members.joins(:user).merge(User.search(params[:search])) + @group_links = @group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) + end + + @project_members = @project_members.sort(@sort).page(params[:page]) + @requesters = AccessRequestsFinder.new(@project).execute(current_user) + @project_member = @project.project_members.new end def update @@ -19,7 +34,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController end def resend_invite - redirect_path = namespace_project_settings_members_path(@project.namespace, @project) + redirect_path = project_project_members_path(@project) @project_member = @project.project_members.find(params[:id]) @@ -42,7 +57,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController return render_404 end - redirect_to(namespace_project_settings_members_path(project.namespace, project), + redirect_to(project_project_members_path(project), notice: notice) end diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index 2a0b58fae7c..1eb78d8b522 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -13,21 +13,21 @@ class Projects::RefsController < Projects::ApplicationController new_path = case params[:destination] when "tree" - namespace_project_tree_path(@project.namespace, @project, @id) + project_tree_path(@project, @id) when "blob" - namespace_project_blob_path(@project.namespace, @project, @id) + project_blob_path(@project, @id) when "graph" - namespace_project_network_path(@project.namespace, @project, @id, @options) + project_network_path(@project, @id, @options) when "graphs" - namespace_project_graph_path(@project.namespace, @project, @id) + project_graph_path(@project, @id) when "find_file" - namespace_project_find_file_path(@project.namespace, @project, @id) + project_find_file_path(@project, @id) when "graphs_commits" - commits_namespace_project_graph_path(@project.namespace, @project, @id) + commits_project_graph_path(@project, @id) when "badges" - namespace_project_pipelines_settings_path(@project.namespace, @project, ref: @id) + project_pipelines_settings_path(@project, ref: @id) else - namespace_project_commits_path(@project.namespace, @project, @id) + project_commits_path(@project, @id) end redirect_to new_path @@ -62,7 +62,7 @@ class Projects::RefsController < Projects::ApplicationController offset = (@offset + @limit) if contents.size > offset - @more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: offset) + @more_log_url = logs_file_project_ref_path(@project, @ref, @path || '', offset: offset) end respond_to do |format| diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb index 98e78585be8..71e7dc70a4d 100644 --- a/app/controllers/projects/registry/repositories_controller.rb +++ b/app/controllers/projects/registry/repositories_controller.rb @@ -10,11 +10,11 @@ module Projects def destroy if image.destroy - redirect_to project_container_registry_path(@project), + redirect_to project_container_registry_index_path(@project), status: 302, notice: 'Image repository has been removed successfully!' else - redirect_to project_container_registry_path(@project), + redirect_to project_container_registry_index_path(@project), status: 302, alert: 'Failed to remove image repository!' end diff --git a/app/controllers/projects/registry/tags_controller.rb b/app/controllers/projects/registry/tags_controller.rb index 5050dba3aab..ae72bd03cfb 100644 --- a/app/controllers/projects/registry/tags_controller.rb +++ b/app/controllers/projects/registry/tags_controller.rb @@ -5,11 +5,11 @@ module Projects def destroy if tag.delete - redirect_to project_container_registry_path(@project), + redirect_to project_container_registry_index_path(@project), status: 302, notice: 'Registry tag has been removed successfully!' else - redirect_to project_container_registry_path(@project), + redirect_to project_container_registry_index_path(@project), status: 302, alert: 'Failed to remove registry tag!' end diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index 2c097cb4d8d..3e0a530fdb9 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -19,7 +19,7 @@ class Projects::ReleasesController < Projects::ApplicationController release.destroy end - redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name) + redirect_to project_tag_path(@project, @tag.name) end private diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 160e632648a..9f9773575a5 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -5,7 +5,7 @@ class Projects::RunnersController < Projects::ApplicationController layout 'project_settings' def index - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project) + redirect_to project_settings_ci_cd_path(@project) end def edit @@ -49,7 +49,7 @@ class Projects::RunnersController < Projects::ApplicationController def toggle_shared_runners project.toggle!(:shared_runners_enabled) - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project) + redirect_to project_settings_ci_cd_path(@project) end protected diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 704f8cc8a79..d54a1111f11 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -15,7 +15,7 @@ class Projects::ServicesController < Projects::ApplicationController def update if @service.save(context: :manual_change) - redirect_to(namespace_project_settings_integrations_path(@project.namespace, @project), notice: success_message) + redirect_to(project_settings_integrations_path(@project), notice: success_message) else render 'edit' end diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index 24fe78bc1bd..ea7ceb3eaa5 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -21,7 +21,10 @@ module Projects end def define_secret_variables - @variable = Ci::Variable.new + @variable = Ci::Variable.new(project: project) + .present(current_user: current_user) + @variables = project.variables.order_key_asc + .map { |variable| variable.present(current_user: current_user) } end def define_triggers_variables diff --git a/app/controllers/projects/settings/members_controller.rb b/app/controllers/projects/settings/members_controller.rb deleted file mode 100644 index 54f9dceddef..00000000000 --- a/app/controllers/projects/settings/members_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Projects - module Settings - class MembersController < Projects::ApplicationController - include SortingHelper - - def show - @sort = params[:sort].presence || sort_value_name - @group_links = @project.project_group_links - - @skip_groups = @group_links.pluck(:group_id) - @skip_groups << @project.namespace_id unless @project.personal? - @skip_groups += @project.group.ancestors.pluck(:id) if @project.group - - @project_members = MembersFinder.new(@project, current_user).execute - - if params[:search].present? - @project_members = @project_members.joins(:user).merge(User.search(params[:search])) - @group_links = @group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) - end - - @project_members = @project_members.sort(@sort).page(params[:page]) - @requesters = AccessRequestsFinder.new(@project).execute(current_user) - @project_member = @project.project_members.new - end - end - end -end diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 98dd307bd9d..d07143d294f 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -30,7 +30,7 @@ class Projects::SnippetsController < Projects::ApplicationController ).execute @snippets = @snippets.page(params[:page]) if @snippets.out_of_range? && @snippets.total_pages != 0 - redirect_to namespace_project_snippets_path(page: @snippets.total_pages) + redirect_to project_snippets_path(@project, page: @snippets.total_pages) end end @@ -79,7 +79,7 @@ class Projects::SnippetsController < Projects::ApplicationController @snippet.destroy - redirect_to namespace_project_snippets_path(@project.namespace, @project), status: 302 + redirect_to project_snippets_path(@project), status: 302 end protected @@ -90,6 +90,10 @@ class Projects::SnippetsController < Projects::ApplicationController alias_method :awardable, :snippet alias_method :spammable, :snippet + def spammable_path + project_snippet_path(@project, @snippet) + end + def authorize_read_project_snippet! return render_404 unless can?(current_user, :read_project_snippet, @snippet) end diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index ebc9f4edab4..b62d7d9b7c5 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -35,7 +35,7 @@ class Projects::TagsController < Projects::ApplicationController if result[:status] == :success @tag = result[:tag] - redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name) + redirect_to project_tag_path(@project, @tag.name) else @error = result[:message] @message = params[:message] @@ -50,7 +50,7 @@ class Projects::TagsController < Projects::ApplicationController respond_to do |format| if result[:status] == :success format.html do - redirect_to namespace_project_tags_path(@project.namespace, @project), status: 303 + redirect_to project_tags_path(@project), status: 303 end format.js @@ -58,7 +58,7 @@ class Projects::TagsController < Projects::ApplicationController @error = result[:message] format.html do - redirect_to namespace_project_tags_path(@project.namespace, @project), + redirect_to project_tags_path(@project), alert: @error, status: 303 end diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 266a15c1cf9..30181ac3bdf 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -16,7 +16,7 @@ class Projects::TreeController < Projects::ApplicationController if tree.entries.empty? if @repository.blob_at(@commit.id, @path) return redirect_to( - namespace_project_blob_path(@project.namespace, @project, + project_blob_path(@project, File.join(@ref, @path)) ) elsif @path.present? @@ -37,8 +37,8 @@ class Projects::TreeController < Projects::ApplicationController return render_404 unless @commit_params.values.all? create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.", - success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@branch_name, @dir_name)), - failure_path: namespace_project_tree_path(@project.namespace, @project, @ref)) + success_path: project_tree_path(@project, File.join(@branch_name, @dir_name)), + failure_path: project_tree_path(@project, @ref)) end private diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb index e86adddd77f..e04145dd0b3 100644 --- a/app/controllers/projects/triggers_controller.rb +++ b/app/controllers/projects/triggers_controller.rb @@ -7,7 +7,7 @@ class Projects::TriggersController < Projects::ApplicationController layout 'project_settings' def index - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project) + redirect_to project_settings_ci_cd_path(@project) end def create @@ -19,7 +19,7 @@ class Projects::TriggersController < Projects::ApplicationController flash[:alert] = 'You could not create a new trigger.' end - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project) + redirect_to project_settings_ci_cd_path(@project) end def take_ownership @@ -29,7 +29,7 @@ class Projects::TriggersController < Projects::ApplicationController flash[:alert] = 'You could not take ownership of trigger.' end - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project) + redirect_to project_settings_ci_cd_path(@project) end def edit @@ -37,7 +37,7 @@ class Projects::TriggersController < Projects::ApplicationController def update if trigger.update(trigger_params) - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project), notice: 'Trigger was successfully updated.' + redirect_to project_settings_ci_cd_path(@project), notice: 'Trigger was successfully updated.' else render action: "edit" end @@ -50,7 +50,7 @@ class Projects::TriggersController < Projects::ApplicationController flash[:alert] = "Could not remove the trigger." end - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project), status: 302 + redirect_to project_settings_ci_cd_path(@project), status: 302 end private @@ -69,8 +69,7 @@ class Projects::TriggersController < Projects::ApplicationController def trigger_params params.require(:trigger).permit( - :description, - trigger_schedule_attributes: [:id, :active, :cron, :cron_timezone, :ref] + :description ) end end diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index 50e25a00f03..6a825137564 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -1,50 +1,60 @@ class Projects::VariablesController < Projects::ApplicationController + before_action :variable, only: [:show, :update, :destroy] before_action :authorize_admin_build! layout 'project_settings' def index - redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project) + redirect_to project_settings_ci_cd_path(@project) end def show - @variable = @project.variables.find(params[:id]) end def update - @variable = @project.variables.find(params[:id]) - - if @variable.update_attributes(project_params) - redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variable was successfully updated.' + if variable.update(variable_params) + redirect_to project_variables_path(project), + notice: 'Variable was successfully updated.' else - render action: "show" + render "show" end end def create - @variable = Ci::Variable.new(project_params) + @variable = project.variables.create(variable_params) + .present(current_user: current_user) - if @variable.valid? && @project.variables << @variable - flash[:notice] = 'Variables were successfully updated.' - redirect_to namespace_project_settings_ci_cd_path(project.namespace, project) + if @variable.persisted? + redirect_to project_settings_ci_cd_path(project), + notice: 'Variable was successfully created.' else render "show" end end def destroy - @key = @project.variables.find(params[:id]) - @key.destroy - - redirect_to namespace_project_settings_ci_cd_path(project.namespace, project), - status: 302, - notice: 'Variable was successfully removed.' + if variable.destroy + redirect_to project_settings_ci_cd_path(project), + status: 302, + notice: 'Variable was successfully removed.' + else + redirect_to project_settings_ci_cd_path(project), + status: 302, + notice: 'Failed to remove the variable.' + end end private - def project_params - params.require(:variable) - .permit([:id, :key, :value, :protected, :_destroy]) + def variable_params + params.require(:variable).permit(*variable_params_attributes) + end + + def variable_params_attributes + %i[id key value protected _destroy] + end + + def variable + @variable ||= project.variables.find(params[:id]).present(current_user: current_user) end end diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index e54b90b8d52..ac98470c2b1 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -49,7 +49,7 @@ class Projects::WikisController < Projects::ApplicationController if @page.valid? redirect_to( - namespace_project_wiki_path(@project.namespace, @project, @page), + project_wiki_path(@project, @page), notice: 'Wiki was successfully updated.' ) else @@ -62,7 +62,7 @@ class Projects::WikisController < Projects::ApplicationController if @page.persisted? redirect_to( - namespace_project_wiki_path(@project.namespace, @project, @page), + project_wiki_path(@project, @page), notice: 'Wiki was successfully updated.' ) else @@ -75,7 +75,7 @@ class Projects::WikisController < Projects::ApplicationController unless @page redirect_to( - namespace_project_wiki_path(@project.namespace, @project, :home), + project_wiki_path(@project, :home), notice: "Page not found" ) end @@ -85,7 +85,7 @@ class Projects::WikisController < Projects::ApplicationController @page = @project_wiki.find_page(params[:id]) WikiPages::DestroyService.new(@project, current_user).execute(@page) - redirect_to namespace_project_wiki_path(@project.namespace, @project, :home), + redirect_to project_wiki_path(@project, :home), status: 302, notice: "Page was successfully deleted" end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 450895cdf3a..c769693255c 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -50,10 +50,13 @@ class ProjectsController < Projects::ApplicationController respond_to do |format| if result[:status] == :success flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name } + format.html do redirect_to(edit_project_path(@project)) end else + flash[:alert] = result[:message] + format.html { render 'edit' } end @@ -92,7 +95,7 @@ class ProjectsController < Projects::ApplicationController def show if @project.import_in_progress? - redirect_to namespace_project_import_path(@project.namespace, @project) + redirect_to project_import_path(@project) return end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 4a579601785..d58c8d14a75 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -44,7 +44,7 @@ class SearchController < ApplicationController query = params[:search].strip.downcase found_by_commit_sha = Commit.valid_hash?(query) && only_commit.sha.start_with?(query) - redirect_to namespace_project_commit_path(@project.namespace, @project, only_commit) if found_by_commit_sha + redirect_to project_commit_path(@project, only_commit) if found_by_commit_sha end end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index f39441a281e..0e8a57f8e03 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -48,7 +48,7 @@ class SessionsController < Devise::SessionsController private def login_counter - @login_counter ||= Gitlab::Metrics.counter(:user_session_logins, 'User sign in count') + @login_counter ||= Gitlab::Metrics.counter(:user_session_logins_total, 'User sign in count') end # Handle an "initial setup" state, where there's only one user, it's an admin, @@ -58,7 +58,7 @@ class SessionsController < Devise::SessionsController user = User.admins.last - return unless user && user.require_password? + return unless user && user.require_password_creation? Users::UpdateService.new(user).execute do |user| @token = user.generate_reset_token diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 3d86dd2ea2c..8c3abd0a085 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -107,6 +107,10 @@ class SnippetsController < ApplicationController alias_method :awardable, :snippet alias_method :spammable, :snippet + def spammable_path + snippet_path(@snippet) + end + def authorize_read_snippet! return if can?(current_user, :read_personal_snippet, @snippet) diff --git a/app/finders/concerns/created_at_filter.rb b/app/finders/concerns/created_at_filter.rb new file mode 100644 index 00000000000..ac9ac77732c --- /dev/null +++ b/app/finders/concerns/created_at_filter.rb @@ -0,0 +1,8 @@ +module CreatedAtFilter + def by_created_at(items) + items = items.created_before(params[:created_before]) if params[:created_before].present? + items = items.created_after(params[:created_after]) if params[:created_after].present? + + items + end +end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 7bc2117f61e..2e5a6493134 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -19,6 +19,8 @@ # iids: integer[] # class IssuableFinder + include CreatedAtFilter + NONE = '0'.freeze IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze @@ -32,6 +34,7 @@ class IssuableFinder def execute items = init_collection items = by_scope(items) + items = by_created_at(items) items = by_state(items) items = by_group(items) items = by_search(items) @@ -42,7 +45,6 @@ class IssuableFinder items = by_iids(items) items = by_milestone(items) items = by_label(items) - items = by_created_at(items) # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far items = by_project(items) @@ -147,9 +149,17 @@ class IssuableFinder @milestones = if milestones? - scope = Milestone.where(project_id: projects) + if project? + group_id = project.group&.id + project_id = project.id + end + + group_id = group.id if group - scope.where(title: params[:milestone_title]) + search_params = + { title: params[:milestone_title], project_ids: project_id, group_ids: group_id } + + MilestonesFinder.new(search_params).execute else Milestone.none end @@ -331,11 +341,6 @@ class IssuableFinder items = items.left_joins_milestones.where('milestones.start_date <= NOW()') else items = items.with_milestone(params[:milestone_title]) - items_projects = projects(items) - - if items_projects - items = items.where(milestones: { project_id: items_projects }) - end end end @@ -408,18 +413,6 @@ class IssuableFinder params[:non_archived].present? ? items.non_archived : items end - def by_created_at(items) - if params[:created_after].present? - items = items.where(items.klass.arel_table[:created_at].gteq(params[:created_after])) - end - - if params[:created_before].present? - items = items.where(items.klass.arel_table[:created_at].lteq(params[:created_before])) - end - - items - end - def current_user_related? params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' end diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb index 630c73c2a94..0a5a0ea2f35 100644 --- a/app/finders/milestones_finder.rb +++ b/app/finders/milestones_finder.rb @@ -1,12 +1,56 @@ +# Search for milestones +# +# params - Hash +# project_ids: Array of project ids or single project id. +# group_ids: Array of group ids or single group id. +# order - Orders by field default due date asc. +# title - filter by title. +# state - filters by state. + class MilestonesFinder - def execute(projects, params) - milestones = Milestone.of_projects(projects) - milestones = milestones.reorder("due_date ASC") - - case params[:state] - when 'closed' then milestones.closed - when 'all' then milestones - else milestones.active + attr_reader :params, :project_ids, :group_ids + + def initialize(params = {}) + @project_ids = Array(params[:project_ids]) + @group_ids = Array(params[:group_ids]) + @params = params + end + + def execute + return Milestone.none if project_ids.empty? && group_ids.empty? + + items = Milestone.all + items = by_groups_and_projects(items) + items = by_title(items) + items = by_state(items) + + order(items) + end + + private + + def by_groups_and_projects(items) + items.for_projects_and_groups(project_ids, group_ids) + end + + def by_title(items) + if params[:title] + items.where(title: params[:title]) + else + items + end + end + + def by_state(items) + Milestone.filter_by_state(items, params[:state]) + end + + def order(items) + if params.has_key?(:order) + items.reorder(params[:order]) + else + order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC') + items.reorder(order_statement) end end end diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index 8bfbe37c543..aa80dfc3f37 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -28,7 +28,14 @@ class ProjectsFinder < UnionFinder end def execute - collection = init_collection + user = params.delete(:user) + collection = + if user + PersonalProjectsFinder.new(user).execute(current_user) + else + init_collection + end + collection = by_ids(collection) collection = by_personal(collection) collection = by_starred(collection) diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb index 07deceb827b..33f7ae90598 100644 --- a/app/finders/users_finder.rb +++ b/app/finders/users_finder.rb @@ -14,6 +14,8 @@ # external: boolean # class UsersFinder + include CreatedAtFilter + attr_accessor :current_user, :params def initialize(current_user, params = {}) @@ -29,6 +31,7 @@ class UsersFinder users = by_active(users) users = by_external_identity(users) users = by_external(users) + users = by_created_at(users) users end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index ca326dd0627..29b88c60dab 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -1,7 +1,7 @@ module ApplicationSettingsHelper delegate :gravatar_enabled?, :signup_enabled?, - :signin_enabled?, + :password_authentication_enabled?, :akismet_enabled?, :koding_enabled?, to: :current_application_settings @@ -34,17 +34,17 @@ module ApplicationSettingsHelper # Return a group of checkboxes that use Bootstrap's button plugin for a # toggle button effect. - def restricted_level_checkboxes(help_block_id) - Gitlab::VisibilityLevel.options.map do |name, level| + def restricted_level_checkboxes(help_block_id, checkbox_name) + Gitlab::VisibilityLevel.values.map do |level| checked = restricted_visibility_levels(true).include?(level) css_class = checked ? 'active' : '' - checkbox_name = "application_setting[restricted_visibility_levels][]" + tag_name = "application_setting_visibility_level_#{level}" - label_tag(name, class: css_class) do + label_tag(tag_name, class: css_class) do check_box_tag(checkbox_name, level, checked, autocomplete: 'off', 'aria-describedby' => help_block_id, - id: name) + visibility_level_icon(level) + name + id: tag_name) + visibility_level_icon(level) + visibility_level_label(level) end end end diff --git a/app/helpers/award_emoji_helper.rb b/app/helpers/award_emoji_helper.rb index 024cf38469e..86b19368cfd 100644 --- a/app/helpers/award_emoji_helper.rb +++ b/app/helpers/award_emoji_helper.rb @@ -7,7 +7,7 @@ module AwardEmojiHelper if awardable.for_personal_snippet? toggle_award_emoji_snippet_note_path(awardable.noteable, awardable) else - toggle_award_emoji_namespace_project_note_path(@project.namespace, @project, awardable.id) + toggle_award_emoji_project_note_path(@project, awardable.id) end else url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable]) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index ee36617ba9a..e964d7a5e16 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -9,7 +9,7 @@ module BlobHelper end def edit_path(project = @project, ref = @ref, path = @path, options = {}) - namespace_project_edit_blob_path(project.namespace, project, + project_edit_blob_path(project, tree_join(ref, path), options[:link_opts]) end @@ -33,7 +33,7 @@ module BlobHelper notice: edit_in_new_fork_notice, notice_now: edit_in_new_fork_notice_now } - fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params) + fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) button_tag 'Edit', class: "#{common_classes} js-edit-blob-link-fork-toggler", @@ -62,7 +62,7 @@ module BlobHelper notice: edit_in_new_fork_notice + " Try to #{action} this file again.", notice_now: edit_in_new_fork_notice_now } - fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params) + fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) button_tag label, class: "#{common_classes} js-edit-blob-link-fork-toggler", @@ -120,15 +120,15 @@ module BlobHelper def blob_raw_url if @build && @entry - raw_namespace_project_job_artifacts_path(@project.namespace, @project, @build, path: @entry.path) + raw_project_job_artifacts_path(@project, @build, path: @entry.path) elsif @snippet if @snippet.project_id - raw_namespace_project_snippet_path(@project.namespace, @project, @snippet) + raw_project_snippet_path(@project, @snippet) else raw_snippet_path(@snippet) end elsif @blob - namespace_project_raw_path(@project.namespace, @project, @id) + project_raw_path(@project, @id) end end @@ -279,12 +279,12 @@ module BlobHelper options = [] if can?(current_user, :create_issue, project) - options << link_to("submit an issue", new_namespace_project_issue_path(project.namespace, project)) + options << link_to("submit an issue", new_project_issue_path(project)) end merge_project = can?(current_user, :create_merge_request, project) ? project : (current_user && current_user.fork_of(project)) if merge_project - options << link_to("create a merge request", namespace_project_new_merge_request_path(project.namespace, project)) + options << link_to("create a merge request", project_new_merge_request_path(project)) end options diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index e2df52e3833..8b33c362a9c 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -3,12 +3,12 @@ module BoardsHelper board = @board || @boards.first { - endpoint: namespace_project_boards_path(@project.namespace, @project), + endpoint: project_boards_path(@project), board_id: board.id, disabled: "#{!can?(current_user, :admin_list, @project)}", - issue_link_base: namespace_project_issues_path(@project.namespace, @project), + issue_link_base: project_issues_path(@project), root_path: root_path, - bulk_update_path: bulk_update_namespace_project_issues_path(@project.namespace, @project), + bulk_update_path: bulk_update_project_issues_path(@project), default_avatar: image_path(default_avatar) } end diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb index 59519c1335b..686437fc99a 100644 --- a/app/helpers/branches_helper.rb +++ b/app/helpers/branches_helper.rb @@ -7,7 +7,7 @@ module BranchesHelper options = exist_opts.merge(options) - namespace_project_branches_path(@project.namespace, @project, @id, options) + project_branches_path(@project, @id, options) end def can_push_branch?(project, branch_name) diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb index f0a0d245dc0..85bc784d53c 100644 --- a/app/helpers/builds_helper.rb +++ b/app/helpers/builds_helper.rb @@ -20,8 +20,8 @@ module BuildsHelper def javascript_build_options { - page_url: namespace_project_job_url(@project.namespace, @project, @build), - build_url: namespace_project_job_url(@project.namespace, @project, @build, :json), + page_url: project_job_url(@project, @build), + build_url: project_job_url(@project, @build, :json), build_status: @build.status, build_stage: @build.stage, log_state: '' @@ -31,7 +31,7 @@ module BuildsHelper def build_failed_issue_options { title: "Build Failed ##{@build.id}", - description: namespace_project_job_url(@project.namespace, @project, @build) + description: project_job_url(@project, @build) } end end diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index ba84dbe4a7a..bf9ad95b7c2 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -50,12 +50,12 @@ module ButtonHelper def http_clone_button(project, placement = 'right', append_link: true) klass = 'http-selector' - klass << ' has-tooltip' if current_user.try(:require_password?) || current_user.try(:require_personal_access_token?) + klass << ' has-tooltip' if current_user.try(:require_password_creation?) || current_user.try(:require_personal_access_token_creation_for_git_auth?) protocol = gitlab_config.protocol.upcase tooltip_title = - if current_user.try(:require_password?) + if current_user.try(:require_password_creation?) _("Set a password on your account to pull or push via %{protocol}.") % { protocol: protocol } else _("Create a personal access token on your account to pull or push via %{protocol}.") % { protocol: protocol } diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 21c0eb8b54c..8022547a6ad 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -8,7 +8,7 @@ module CiStatusHelper def ci_status_path(pipeline) project = pipeline.project - namespace_project_pipeline_path(project.namespace, project, pipeline) + project_pipeline_path(project, pipeline) end def ci_label_for_status(status) @@ -99,10 +99,7 @@ module CiStatusHelper def render_project_pipeline_status(pipeline_status, tooltip_placement: 'auto left') project = pipeline_status.project - path = pipelines_namespace_project_commit_path( - project.namespace, - project, - pipeline_status.sha) + path = pipelines_project_commit_path(project, pipeline_status.sha) render_status_with_link( 'commit', @@ -113,10 +110,7 @@ module CiStatusHelper def render_commit_status(commit, ref: nil, tooltip_placement: 'auto left') project = commit.project - path = pipelines_namespace_project_commit_path( - project.namespace, - project, - commit) + path = pipelines_project_commit_path(project, commit) render_status_with_link( 'commit', @@ -127,7 +121,7 @@ module CiStatusHelper def render_pipeline_status(pipeline, tooltip_placement: 'auto left') project = pipeline.project - path = namespace_project_pipeline_path(project.namespace, project, pipeline) + path = project_pipeline_path(project, pipeline) render_status_with_link('pipeline', pipeline.status, path, tooltip_placement: tooltip_placement) end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 0accd1f8d77..d08e346d605 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -30,7 +30,7 @@ module CommitsHelper crumbs = content_tag(:li) do link_to( @project.path, - namespace_project_commits_path(@project.namespace, @project, @ref) + project_commits_path(@project, @ref) ) end @@ -42,8 +42,7 @@ module CommitsHelper # The text is just the individual part, but the link needs all the parts before it link_to( part, - namespace_project_commits_path( - @project.namespace, + project_commits_path( @project, tree_join(@ref, parts[0..i].join('/')) ) @@ -86,20 +85,20 @@ module CommitsHelper if @path.blank? return link_to( _("Browse Files"), - namespace_project_tree_path(project.namespace, project, commit), + project_tree_path(project, commit), class: "btn btn-default" ) elsif @repo.blob_at(commit.id, @path) return link_to( _("Browse File"), - namespace_project_blob_path(project.namespace, project, + project_blob_path(project, tree_join(commit.id, @path)), class: "btn btn-default" ) elsif @path.present? return link_to( _("Browse Directory"), - namespace_project_tree_path(project.namespace, project, + project_tree_path(project, tree_join(commit.id, @path)), class: "btn btn-default" ) @@ -165,7 +164,7 @@ module CommitsHelper notice: "#{edit_in_new_fork_notice} Try to #{action} this commit again.", notice_now: edit_in_new_fork_notice_now } - fork_path = namespace_project_forks_path(@project.namespace, @project, + fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params) @@ -175,7 +174,7 @@ module CommitsHelper def view_file_button(commit_sha, diff_new_path, project) link_to( - namespace_project_blob_path(project.namespace, project, + project_blob_path(project, tree_join(commit_sha, diff_new_path)), class: 'btn view-file js-view-file' ) do diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb index 424ded2b69d..2c28dd81c87 100644 --- a/app/helpers/compare_helper.rb +++ b/app/helpers/compare_helper.rb @@ -9,8 +9,7 @@ module CompareHelper end def create_mr_path(from = params[:from], to = params[:to], project = @project) - namespace_project_new_merge_request_path( - project.namespace, + project_new_merge_request_path( project, merge_request: { source_branch: to, diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 16a99addd0b..926502bf239 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -103,18 +103,18 @@ module DiffHelper end def diff_file_blob_raw_path(diff_file) - namespace_project_raw_path(@project.namespace, @project, tree_join(diff_file.content_sha, diff_file.file_path)) + project_raw_path(@project, tree_join(diff_file.content_sha, diff_file.file_path)) end def diff_file_old_blob_raw_path(diff_file) sha = diff_file.old_content_sha return unless sha - namespace_project_raw_path(@project.namespace, @project, tree_join(diff_file.old_content_sha, diff_file.old_path)) + project_raw_path(@project, tree_join(diff_file.old_content_sha, diff_file.old_path)) end def diff_file_html_data(project, diff_file_path, diff_commit_id) { - blob_diff_path: namespace_project_blob_diff_path(project.namespace, project, + blob_diff_path: project_blob_diff_path(project, tree_join(diff_commit_id, diff_file_path)), view: diff_view } @@ -142,7 +142,7 @@ module DiffHelper diff_file = viewer.diff_file options = [] - blob_url = namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.content_sha, diff_file.file_path)) + blob_url = project_blob_path(@project, tree_join(diff_file.content_sha, diff_file.file_path)) options << link_to('view the blob', blob_url) options @@ -163,17 +163,17 @@ module DiffHelper end def commit_diff_whitespace_link(project, commit, options) - url = namespace_project_commit_path(project.namespace, project, commit.id, params_with_whitespace) + url = project_commit_path(project, commit.id, params_with_whitespace) toggle_whitespace_link(url, options) end def diff_merge_request_whitespace_link(project, merge_request, options) - url = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, params_with_whitespace) + url = diffs_project_merge_request_path(project, merge_request, params_with_whitespace) toggle_whitespace_link(url, options) end def diff_compare_whitespace_link(project, from, to, options) - url = namespace_project_compare_path(project.namespace, project, from, to, params_with_whitespace) + url = project_compare_path(project, from, to, params_with_whitespace) toggle_whitespace_link(url, options) end diff --git a/app/helpers/environment_helper.rb b/app/helpers/environment_helper.rb index ff8550439d0..1e78a189c08 100644 --- a/app/helpers/environment_helper.rb +++ b/app/helpers/environment_helper.rb @@ -8,7 +8,7 @@ module EnvironmentHelper def environment_link_for_build(project, build) environment = environment_for_build(project, build) if environment - link_to environment.name, namespace_project_environment_path(project.namespace, project, environment) + link_to environment.name, project_environment_path(project, environment) else content_tag :span, build.expanded_environment_name end diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 515e802e01e..4ce89f89fa9 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -1,7 +1,7 @@ module EnvironmentsHelper def environments_list_data { - endpoint: namespace_project_environments_path(@project.namespace, @project, format: :json) + endpoint: project_environments_path(@project, format: :json) } end end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 751d61955b7..48c87dca217 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -99,13 +99,12 @@ module EventsHelper def event_feed_url(event) if event.issue? - namespace_project_issue_url(event.project.namespace, event.project, + project_issue_url(event.project, event.issue) elsif event.merge_request? - namespace_project_merge_request_url(event.project.namespace, - event.project, event.merge_request) + project_merge_request_url(event.project, event.merge_request) elsif event.commit_note? - namespace_project_commit_url(event.project.namespace, event.project, + project_commit_url(event.project, event.note_target) elsif event.note? if event.note_target @@ -119,15 +118,15 @@ module EventsHelper def push_event_feed_url(event) if event.push_with_commits? && event.md_ref? if event.commits_count > 1 - namespace_project_compare_url(event.project.namespace, event.project, + project_compare_url(event.project, from: event.commit_from, to: event.commit_to) else - namespace_project_commit_url(event.project.namespace, event.project, + project_commit_url(event.project, id: event.commit_to) end else - namespace_project_commits_url(event.project.namespace, event.project, + project_commits_url(event.project, event.ref_name) end end @@ -146,15 +145,9 @@ module EventsHelper def event_note_target_path(event) if event.commit_note? - namespace_project_commit_path(event.project.namespace, - event.project, - event.note_target, - anchor: dom_id(event.target)) + project_commit_path(event.project, event.note_target, anchor: dom_id(event.target)) elsif event.project_snippet_note? - namespace_project_snippet_path(event.project.namespace, - event.project, - event.note_target, - anchor: dom_id(event.target)) + project_snippet_path(event.project, event.note_target, anchor: dom_id(event.target)) else polymorphic_path([event.project.namespace.becomes(Namespace), event.project, event.note_target], diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb index defd87d6bbe..8cf890b74a8 100644 --- a/app/helpers/external_wiki_helper.rb +++ b/app/helpers/external_wiki_helper.rb @@ -4,7 +4,7 @@ module ExternalWikiHelper if external_wiki_service external_wiki_service.properties['external_wiki_url'] else - namespace_project_wiki_path(project.namespace, project, :home) + project_wiki_path(project, :home) end end end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 8c7af62e199..0517a699ae0 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -1,144 +1,93 @@ -# Shorter routing method for project and project items -# Since update to rails 4.1.9 we are now allowed to use `/` in project routing -# so we use nested routing for project resources which include project and -# project namespace. To avoid writing long methods every time we define shortcuts for -# some of routing. -# -# For example instead of this: -# -# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) -# -# We can simply use shortcut: -# -# merge_request_path(merge_request) -# +# Shorter routing method for some project items module GitlabRoutingHelper - # Project - def project_path(project, *args) - namespace_project_path(project.namespace, project, *args) - end - - def project_url(project, *args) - namespace_project_url(project.namespace, project, *args) - end - - def edit_project_path(project, *args) - edit_namespace_project_path(project.namespace, project, *args) - end - - def edit_project_url(project, *args) - edit_namespace_project_url(project.namespace, project, *args) - end + extend ActiveSupport::Concern - def project_files_path(project, *args) - namespace_project_tree_path(project.namespace, project, @ref || project.repository.root_ref) + included do + Gitlab::Routing.includes_helpers(self) end - def project_commits_path(project, *args) - namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref) - end - - def project_pipelines_path(project, *args) - namespace_project_pipelines_path(project.namespace, project, *args) - end - - def project_environments_path(project, *args) - namespace_project_environments_path(project.namespace, project, *args) - end - - def project_cycle_analytics_path(project, *args) - namespace_project_cycle_analytics_path(project.namespace, project, *args) + # Project + def project_tree_path(project, ref = nil, *args) + namespace_project_tree_path(project.namespace, project, ref || @ref || project.repository.root_ref, *args) # rubocop:disable Cop/ProjectPathHelper end - def project_jobs_path(project, *args) - namespace_project_jobs_path(project.namespace, project, *args) + def project_commits_path(project, ref = nil, *args) + namespace_project_commits_path(project.namespace, project, ref || @ref || project.repository.root_ref, *args) # rubocop:disable Cop/ProjectPathHelper end def project_ref_path(project, ref_name, *args) - namespace_project_commits_path(project.namespace, project, ref_name, *args) - end - - def project_container_registry_path(project, *args) - namespace_project_container_registry_index_path(project.namespace, project, *args) - end - - def activity_project_path(project, *args) - activity_namespace_project_path(project.namespace, project, *args) + project_commits_path(project, ref_name, *args) end def runners_path(project, *args) - namespace_project_runners_path(project.namespace, project, *args) + project_runners_path(project, *args) end def runner_path(runner, *args) - namespace_project_runner_path(@project.namespace, @project, runner, *args) + project_runner_path(@project, runner, *args) end def environment_path(environment, *args) - namespace_project_environment_path(environment.project.namespace, environment.project, environment, *args) + project_environment_path(environment.project, environment, *args) end def environment_metrics_path(environment, *args) - metrics_namespace_project_environment_path(environment.project.namespace, environment.project, environment, *args) + metrics_project_environment_path(environment.project, environment, *args) end def issue_path(entity, *args) - namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) + project_issue_path(entity.project, entity, *args) end def merge_request_path(entity, *args) - namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args) + project_merge_request_path(entity.project, entity, *args) end def pipeline_path(pipeline, *args) - namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, *args) + project_pipeline_path(pipeline.project, pipeline.id, *args) end def milestone_path(entity, *args) - namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args) + project_milestone_path(entity.project, entity, *args) end def issue_url(entity, *args) - namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args) + project_issue_url(entity.project, entity, *args) end def merge_request_url(entity, *args) - namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args) + project_merge_request_url(entity.project, entity, *args) end def pipeline_url(pipeline, *args) - namespace_project_pipeline_url(pipeline.project.namespace, pipeline.project, pipeline.id, *args) + project_pipeline_url(pipeline.project, pipeline.id, *args) end def pipeline_job_url(pipeline, build, *args) - namespace_project_job_url(pipeline.project.namespace, pipeline.project, build.id, *args) + project_job_url(pipeline.project, build.id, *args) end def commits_url(entity, *args) - namespace_project_commits_url(entity.project.namespace, entity.project, entity.ref, *args) + project_commits_url(entity.project, entity.ref, *args) end def commit_url(entity, *args) - namespace_project_commit_url(entity.project.namespace, entity.project, entity.sha, *args) - end - - def project_snippet_url(entity, *args) - namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args) + project_commit_url(entity.project, entity.sha, *args) end def preview_markdown_path(project, *args) if @snippet.is_a?(PersonalSnippet) preview_markdown_snippets_path else - preview_markdown_namespace_project_path(project.namespace, project, *args) + preview_markdown_project_path(project, *args) end end def toggle_subscription_path(entity, *args) if entity.is_a?(Issue) - toggle_subscription_namespace_project_issue_path(entity.project.namespace, entity.project, entity) + toggle_subscription_project_issue_path(entity.project, entity) else - toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity) + toggle_subscription_project_merge_request_path(entity.project, entity) end end @@ -152,32 +101,27 @@ module GitlabRoutingHelper ## Members def project_members_url(project, *args) - namespace_project_project_members_url(project.namespace, project) + project_project_members_url(project, *args) end def project_member_path(project_member, *args) - namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member) + project_project_member_path(project_member.source, project_member) end def request_access_project_members_path(project, *args) - request_access_namespace_project_project_members_path(project.namespace, project) + request_access_project_project_members_path(project) end def leave_project_members_path(project, *args) - leave_namespace_project_project_members_path(project.namespace, project) + leave_project_project_members_path(project) end def approve_access_request_project_member_path(project_member, *args) - approve_access_request_namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member) + approve_access_request_project_project_member_path(project_member.source, project_member) end def resend_invite_project_member_path(project_member, *args) - resend_invite_namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member) - end - - # Snippets - def personal_snippet_url(snippet, *args) - snippet_url(snippet) + resend_invite_project_project_member_path(project_member.source, project_member) end # Groups @@ -211,50 +155,37 @@ module GitlabRoutingHelper def artifacts_action_path(path, project, build) action, path_params = path.split('/', 2) - args = [project.namespace, project, build, path_params] + args = [project, build, path_params] case action when 'download' - download_namespace_project_job_artifacts_path(*args) + download_project_job_artifacts_path(*args) when 'browse' - browse_namespace_project_job_artifacts_path(*args) + browse_project_job_artifacts_path(*args) when 'file' - file_namespace_project_job_artifacts_path(*args) + file_project_job_artifacts_path(*args) when 'raw' - raw_namespace_project_job_artifacts_path(*args) + raw_project_job_artifacts_path(*args) end end # Pipeline Schedules def pipeline_schedules_path(project, *args) - namespace_project_pipeline_schedules_path(project.namespace, project, *args) + project_pipeline_schedules_path(project, *args) end def pipeline_schedule_path(schedule, *args) project = schedule.project - namespace_project_pipeline_schedule_path(project.namespace, project, schedule, *args) + project_pipeline_schedule_path(project, schedule, *args) end def edit_pipeline_schedule_path(schedule) project = schedule.project - edit_namespace_project_pipeline_schedule_path(project.namespace, project, schedule) + edit_project_pipeline_schedule_path(project, schedule) end def take_ownership_pipeline_schedule_path(schedule, *args) project = schedule.project - take_ownership_namespace_project_pipeline_schedule_path(project.namespace, project, schedule, *args) - end - - # Settings - def project_settings_integrations_path(project, *args) - namespace_project_settings_integrations_path(project.namespace, project, *args) - end - - def project_settings_members_path(project, *args) - namespace_project_settings_members_path(project.namespace, project, *args) - end - - def project_settings_ci_cd_path(project, *args) - namespace_project_settings_ci_cd_path(project.namespace, project, *args) + take_ownership_project_pipeline_schedule_path(project, schedule, *args) end end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 05177e58c5a..d0c518f81f7 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -26,9 +26,9 @@ module IssuablesHelper project = issuable.project if issuable.is_a?(MergeRequest) - namespace_project_merge_request_path(project.namespace, project, issuable.iid, :json) + project_merge_request_path(project, issuable.iid, :json) else - namespace_project_issue_path(project.namespace, project, issuable.iid, :json) + project_issue_path(project, issuable.iid, :json) end end @@ -197,7 +197,7 @@ module IssuablesHelper def issuable_initial_data(issuable) data = { - endpoint: namespace_project_issue_path(@project.namespace, @project, issuable), + endpoint: project_issue_path(@project, issuable), canUpdate: can?(current_user, :update_issue, issuable), canDestroy: can?(current_user, :destroy_issue, issuable), canMove: current_user ? issuable.can_move?(current_user) : false, @@ -245,6 +245,53 @@ module IssuablesHelper @counts[cache_key][state] end + def close_issuable_url(issuable) + issuable_url(issuable, close_reopen_params(issuable, :close)) + end + + def reopen_issuable_url(issuable) + issuable_url(issuable, close_reopen_params(issuable, :reopen)) + end + + def close_reopen_issuable_url(issuable, should_inverse = false) + issuable.closed? ^ should_inverse ? reopen_issuable_url(issuable) : close_issuable_url(issuable) + end + + def issuable_url(issuable, *options) + case issuable + when Issue + issue_url(issuable, *options) + when MergeRequest + merge_request_url(issuable, *options) + end + end + + def issuable_button_visibility(issuable, closed) + case issuable + when Issue + issue_button_visibility(issuable, closed) + when MergeRequest + merge_request_button_visibility(issuable, closed) + end + end + + def issuable_close_reopen_button_method(issuable) + case issuable + when Issue + '' + when MergeRequest + 'put' + end + end + + def issuable_author_is_current_user(issuable) + issuable.author == current_user + end + + def issuable_display_type(issuable) + issuable.model_name.human.downcase + end + private def sidebar_gutter_collapsed? @@ -270,8 +317,6 @@ module IssuablesHelper issue_template_names when MergeRequest merge_request_template_names - else - raise 'Unknown issuable type!' end end @@ -295,10 +340,18 @@ module IssuablesHelper mark_icon: (is_collapsed ? icon('check-square', class: 'todo-undone') : nil), issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, - url: namespace_project_todos_path(@project.namespace, @project), + url: project_todos_path(@project), delete_path: (dashboard_todo_path(todo) if todo), placement: (is_collapsed ? 'left' : nil), container: (is_collapsed ? 'body' : nil) } end + + def close_reopen_params(issuable, action) + { + issuable.model_name.to_s.underscore => { state_event: action } + }.tap do |params| + params[:format] = :json if issuable.is_a?(Issue) + end + end end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 82288f1da35..42b6cfdf02f 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -150,7 +150,7 @@ module IssuesHelper Gitlab::UrlBuilder.build(single_discussion.first_note) else project = merge_request.project - namespace_project_merge_request_path(project.namespace, project, merge_request) + project_merge_request_path(project, merge_request) end link_to link_text, path diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 6baf6f31d8f..4b99de1b6a5 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -57,14 +57,14 @@ module LabelsHelper def edit_label_path(label) case label when GroupLabel then edit_group_label_path(label.group, label) - when ProjectLabel then edit_namespace_project_label_path(label.project.namespace, label.project, label) + when ProjectLabel then edit_project_label_path(label.project, label) end end def destroy_label_path(label) case label when GroupLabel then group_label_path(label.group, label) - when ProjectLabel then namespace_project_label_path(label.project.namespace, label.project, label) + when ProjectLabel then project_label_path(label.project, label) end end @@ -127,7 +127,7 @@ module LabelsHelper project = @target_project || @project if project - namespace_project_labels_path(project.namespace, project, :json) + project_labels_path(project, :json) else dashboard_labels_path(:json) end @@ -149,8 +149,8 @@ module LabelsHelper case label_subscription_status(label, project) when 'group-level' then toggle_subscription_group_label_path(label.group, label) - when 'project-level' then toggle_subscription_namespace_project_label_path(project.namespace, project, label) - when 'unsubscribed' then toggle_subscription_namespace_project_label_path(project.namespace, project, label) + when 'project-level' then toggle_subscription_project_label_path(project, label) + when 'unsubscribed' then toggle_subscription_project_label_path(project, label) end end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 54d6f86fa11..78cf7b26a31 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,8 +1,7 @@ module MergeRequestsHelper def new_mr_path_from_push_event(event) target_project = event.project.default_merge_request_target - namespace_project_new_merge_request_path( - event.project.namespace, + project_new_merge_request_path( event.project, new_mr_from_push_event(event, target_project) ) @@ -48,8 +47,8 @@ module MergeRequestsHelper end def mr_change_branches_path(merge_request) - namespace_project_new_merge_request_path( - @project.namespace, @project, + project_new_merge_request_path( + @project, merge_request: { source_project_id: merge_request.source_project_id, target_project_id: merge_request.target_project_id, @@ -82,9 +81,7 @@ module MergeRequestsHelper end def merge_request_version_path(project, merge_request, merge_request_diff, start_sha = nil) - diffs_namespace_project_merge_request_path( - project.namespace, project, merge_request, - diff_id: merge_request_diff.id, start_sha: start_sha) + diffs_project_merge_request_path(project, merge_request, diff_id: merge_request_diff.id, start_sha: start_sha) end def version_index(merge_request_diff) diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index f346e20e807..f8860bfee99 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -1,7 +1,7 @@ module MilestonesHelper def milestones_filter_path(opts = {}) if @project - namespace_project_milestones_path(@project.namespace, @project, opts) + project_milestones_path(@project, opts) elsif @group group_milestones_path(@group, opts) else @@ -11,7 +11,7 @@ module MilestonesHelper def milestones_label_path(opts = {}) if @project - namespace_project_issues_path(@project.namespace, @project, opts) + project_issues_path(@project, opts) elsif @group issues_group_path(@group, opts) else @@ -54,8 +54,10 @@ module MilestonesHelper def milestone_class_for_state(param, check, match_blank_param = false) if match_blank_param 'active' if param.blank? || param == check + elsif param == check + 'active' else - 'active' if param == check + check end end @@ -73,7 +75,7 @@ module MilestonesHelper def milestones_filter_dropdown_path project = @target_project || @project if project - namespace_project_milestones_path(project.namespace, project, :json) + project_milestones_path(project, :json) elsif @group group_milestones_path(@group, :json) else @@ -120,7 +122,7 @@ module MilestonesHelper def milestone_merge_request_tab_path(milestone) if @project - merge_requests_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) + merge_requests_project_milestone_path(@project, milestone, format: :json) elsif @group merge_requests_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json) else @@ -130,7 +132,7 @@ module MilestonesHelper def milestone_participants_tab_path(milestone) if @project - participants_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) + participants_project_milestone_path(@project, milestone, format: :json) elsif @group participants_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json) else @@ -140,11 +142,21 @@ module MilestonesHelper def milestone_labels_tab_path(milestone) if @project - labels_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) + labels_project_milestone_path(@project, milestone, format: :json) elsif @group labels_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json) else labels_dashboard_milestone_path(milestone, title: milestone.title, format: :json) end end + + def group_milestone_route(milestone, params = {}) + params = nil if params.empty? + + if milestone.is_legacy_group_milestone? + group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params) + else + group_milestone_path(@group, milestone.iid, milestone: params) + end + end end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index e589ed4e56d..b769462abc2 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -23,7 +23,6 @@ module NavHelper def nav_header_class class_name = '' class_name << " with-horizontal-nav" if defined?(nav) && nav - class_name << " with-peek" if peek_enabled? class_name end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index ecc6cd6c6c5..0a0881d95cf 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -81,11 +81,11 @@ module NotesHelper path_params = version_params.merge(anchor: discussion.line_code) - diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, path_params) + diffs_project_merge_request_path(discussion.project, discussion.noteable, path_params) elsif discussion.for_commit? anchor = discussion.line_code if discussion.diff_discussion? - namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor) + project_commit_path(discussion.project, discussion.noteable, anchor: anchor) end end @@ -93,12 +93,7 @@ module NotesHelper if @snippet.is_a?(PersonalSnippet) snippet_notes_path(@snippet) else - namespace_project_noteable_notes_path( - namespace_id: @project.namespace, - project_id: @project, - target_id: @noteable.id, - target_type: @noteable.class.name.underscore - ) + project_noteable_notes_path(@project, target_id: @noteable.id, target_type: @noteable.class.name.underscore) end end @@ -106,7 +101,7 @@ module NotesHelper if note.noteable.is_a?(PersonalSnippet) snippet_note_path(note.noteable, note) else - namespace_project_note_path(project.namespace, project, note) + project_note_path(project, note) end end diff --git a/app/helpers/performance_bar_helper.rb b/app/helpers/performance_bar_helper.rb new file mode 100644 index 00000000000..d24efe37f5f --- /dev/null +++ b/app/helpers/performance_bar_helper.rb @@ -0,0 +1,7 @@ +module PerformanceBarHelper + # This is a hack since using `alias_method :performance_bar_enabled?, :peek_enabled?` + # in WithPerformanceBar breaks tests (but works in the browser). + def performance_bar_enabled? + peek_enabled? + end +end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 53d95c2de94..26d11f9ab46 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -214,11 +214,11 @@ module ProjectsHelper def show_no_password_message? cookies[:hide_no_password_message].blank? && !current_user.hide_no_password && - ( current_user.require_password? || current_user.require_personal_access_token? ) + ( current_user.require_password_creation? || current_user.require_personal_access_token_creation_for_git_auth? ) end def link_to_set_password - if current_user.require_password? + if current_user.require_password_creation? link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path else link_to s_('CreateTokenToCloneLink|create a personal access token'), profile_personal_access_tokens_path @@ -267,15 +267,15 @@ module ProjectsHelper def tab_ability_map { - environments: :read_environment, - milestones: :read_milestone, - snippets: :read_project_snippet, - settings: :admin_project, - builds: :read_build, - labels: :read_label, - issues: :read_issue, - team: :read_project_member, - wiki: :read_wiki + environments: :read_environment, + milestones: :read_milestone, + snippets: :read_project_snippet, + settings: :admin_project, + builds: :read_build, + labels: :read_label, + issues: :read_issue, + project_members: :read_project_member, + wiki: :read_wiki } end @@ -347,8 +347,7 @@ module ProjectsHelper def add_special_file_path(project, file_name:, commit_message: nil, branch_name: nil, context: nil) commit_message ||= s_("CommitMessage|Add %{file_name}") % { file_name: file_name.downcase } - namespace_project_new_blob_path( - project.namespace, + project_new_blob_path( project, project.default_branch || 'master', file_name: file_name, @@ -359,8 +358,7 @@ module ProjectsHelper end def add_koding_stack_path(project) - namespace_project_new_blob_path( - project.namespace, + project_new_blob_path( project, project.default_branch || 'master', file_name: '.koding.yml', @@ -414,8 +412,7 @@ module ProjectsHelper def contribution_guide_path(project) if project && contribution_guide = project.repository.contribution_guide - namespace_project_blob_path( - project.namespace, + project_blob_path( project, tree_join(project.default_branch, contribution_guide.name) @@ -445,7 +442,7 @@ module ProjectsHelper def project_wiki_path_with_version(proj, page, version, is_newest) url_params = is_newest ? {} : { version_id: version } - namespace_project_wiki_path(proj.namespace, proj, page, url_params) + project_wiki_path(proj, page, url_params) end def project_status_css_class(status) @@ -470,8 +467,7 @@ module ProjectsHelper def filename_path(project, filename) if project && blob = project.repository.send(filename) - namespace_project_blob_path( - project.namespace, + project_blob_path( project, tree_join(project.default_branch, blob.name) ) @@ -522,4 +518,12 @@ module ProjectsHelper current_application_settings.restricted_visibility_levels || [] end + + def find_file_path + return unless @project && !@project.empty_repo? + + ref = @ref || @project.repository.root_ref + + project_find_file_path(@project, ref) + end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index f39a3bb55a8..fd7ab59ce64 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -67,16 +67,16 @@ module SearchHelper ref = @ref || @project.repository.root_ref [ - { category: "Current Project", label: "Files", url: namespace_project_tree_path(@project.namespace, @project, ref) }, - { category: "Current Project", label: "Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) }, - { category: "Current Project", label: "Network", url: namespace_project_network_path(@project.namespace, @project, ref) }, - { category: "Current Project", label: "Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) }, - { category: "Current Project", label: "Issues", url: namespace_project_issues_path(@project.namespace, @project) }, - { category: "Current Project", label: "Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, - { category: "Current Project", label: "Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, - { category: "Current Project", label: "Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, - { category: "Current Project", label: "Members", url: namespace_project_settings_members_path(@project.namespace, @project) }, - { category: "Current Project", label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) } + { category: "Current Project", label: "Files", url: project_tree_path(@project, ref) }, + { category: "Current Project", label: "Commits", url: project_commits_path(@project, ref) }, + { category: "Current Project", label: "Network", url: project_network_path(@project, ref) }, + { category: "Current Project", label: "Graph", url: project_graph_path(@project, ref) }, + { category: "Current Project", label: "Issues", url: project_issues_path(@project) }, + { category: "Current Project", label: "Merge Requests", url: project_merge_requests_path(@project) }, + { category: "Current Project", label: "Milestones", url: project_milestones_path(@project) }, + { category: "Current Project", label: "Snippets", url: project_snippets_path(@project) }, + { category: "Current Project", label: "Members", url: project_project_members_path(@project) }, + { category: "Current Project", label: "Wiki", url: project_wikis_path(@project) } ] else [] @@ -104,7 +104,7 @@ module SearchHelper id: p.id, value: "#{search_result_sanitize(p.name)}", label: "#{search_result_sanitize(p.name_with_namespace)}", - url: namespace_project_path(p.namespace, p) + url: project_path(p) } end end @@ -133,7 +133,7 @@ module SearchHelper data: { 'project-id' => @project.id, 'username-params' => @users.to_json(only: [:id, :username]), - 'base-endpoint' => namespace_project_path(@project.namespace, @project) + 'base-endpoint' => project_path(@project) } } end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 2fd64b3441e..b447d4952e7 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -1,8 +1,7 @@ module SnippetsHelper def reliable_snippet_path(snippet, opts = nil) if snippet.project_id? - namespace_project_snippet_path(snippet.project.namespace, - snippet.project, snippet, opts) + project_snippet_path(snippet.project, snippet, opts) else snippet_path(snippet, opts) end @@ -10,7 +9,7 @@ module SnippetsHelper def download_snippet_path(snippet) if snippet.project_id - raw_namespace_project_snippet_path(@project.namespace, @project, snippet, inline: false) + raw_project_snippet_path(@project, snippet, inline: false) else raw_snippet_path(snippet, inline: false) end @@ -21,7 +20,7 @@ module SnippetsHelper # @returns String, path to snippet index def subject_snippets_path(subject = nil, opts = nil) if subject.is_a?(Project) - namespace_project_snippets_path(subject.namespace, subject, opts) + project_snippets_path(subject, opts) else # assume subject === User dashboard_snippets_path(opts) end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 1a55ee05996..ee701076a14 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -107,8 +107,7 @@ module TabHelper def branches_tab_class if current_controller?(:protected_branches) || current_controller?(:branches) || - current_page?(namespace_project_repository_path(@project.namespace, - @project)) + current_page?(project_repository_path(@project)) 'active' end end diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb index 31aaf9e5607..d000d6b1c0a 100644 --- a/app/helpers/tags_helper.rb +++ b/app/helpers/tags_helper.rb @@ -10,7 +10,7 @@ module TagsHelper } options = exist_opts.merge(options) - namespace_project_tags_path(@project.namespace, @project, @id, options) + project_tags_path(@project, @id, options) end def tag_list(project) diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 3d1b3a4711a..2a7aa299e83 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -39,7 +39,7 @@ module TodosHelper anchor = dom_id(todo.note) if todo.note.present? if todo.for_commit? - namespace_project_commit_path(todo.project.namespace.becomes(Namespace), todo.project, + project_commit_path(todo.project, todo.target, anchor: anchor) else path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target] diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index 0f847841295..64ca2d2eacf 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -31,7 +31,7 @@ module Emails setup_issue_mail(issue_id, recipient_id) @label_names = label_names - @labels_url = namespace_project_labels_url(@project.namespace, @project) + @labels_url = project_labels_url(@project) mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) end @@ -56,7 +56,7 @@ module Emails def setup_issue_mail(issue_id, recipient_id) @issue = Issue.find(issue_id) @project = @issue.project - @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) + @target_url = project_issue_url(@project, @issue) @sent_notification = SentNotification.record(@issue, recipient_id, reply_key) end diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index ec27ac517db..3626f8ce416 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -22,7 +22,7 @@ module Emails setup_merge_request_mail(merge_request_id, recipient_id) @label_names = label_names - @labels_url = namespace_project_labels_url(@project.namespace, @project) + @labels_url = project_labels_url(@project) mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id)) end @@ -59,7 +59,7 @@ module Emails def setup_merge_request_mail(merge_request_id, recipient_id) @merge_request = MergeRequest.find(merge_request_id) @project = @merge_request.project - @target_url = namespace_project_merge_request_url(@project.namespace, @project, @merge_request) + @target_url = project_merge_request_url(@project, @merge_request) @sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key) end diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 00707a0023e..77a82b895ce 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -4,7 +4,7 @@ module Emails setup_note_mail(note_id, recipient_id) @commit = @note.noteable - @target_url = namespace_project_commit_url(*note_target_url_options) + @target_url = project_commit_url(*note_target_url_options) mail_answer_thread(@commit, note_thread_options(recipient_id)) end @@ -12,7 +12,7 @@ module Emails setup_note_mail(note_id, recipient_id) @issue = @note.noteable - @target_url = namespace_project_issue_url(*note_target_url_options) + @target_url = project_issue_url(*note_target_url_options) mail_answer_thread(@issue, note_thread_options(recipient_id)) end @@ -20,7 +20,7 @@ module Emails setup_note_mail(note_id, recipient_id) @merge_request = @note.noteable - @target_url = namespace_project_merge_request_url(*note_target_url_options) + @target_url = project_merge_request_url(*note_target_url_options) mail_answer_thread(@merge_request, note_thread_options(recipient_id)) end @@ -28,7 +28,7 @@ module Emails setup_note_mail(note_id, recipient_id) @snippet = @note.noteable - @target_url = namespace_project_snippet_url(*note_target_url_options) + @target_url = project_snippet_url(*note_target_url_options) mail_answer_thread(@snippet, note_thread_options(recipient_id)) end @@ -43,7 +43,7 @@ module Emails private def note_target_url_options - [@project.namespace, @project, @note.noteable, anchor: "note_#{@note.id}"] + [@project, @note.noteable, anchor: "note_#{@note.id}"] end def note_thread_options(recipient_id) diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index e0af7081411..761d873c01c 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -3,7 +3,7 @@ module Emails def project_was_moved_email(project_id, user_id, old_path_with_namespace) @current_user = @user = User.find user_id @project = Project.find project_id - @target_url = namespace_project_url(@project.namespace, @project) + @target_url = project_url(@project) @old_path_with_namespace = old_path_with_namespace mail(to: @user.notification_email, subject: subject("Project was moved")) diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index f315e38bcaa..eaac6fcb548 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -1,5 +1,6 @@ class Notify < BaseMailer include ActionDispatch::Routing::PolymorphicRoutes + include GitlabRoutingHelper include Emails::Issues include Emails::MergeRequests diff --git a/app/models/appearance.rb b/app/models/appearance.rb index c79326e8427..f9c48482be7 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -10,5 +10,5 @@ class Appearance < ActiveRecord::Base mount_uploader :logo, AttachmentUploader mount_uploader :header_logo, AttachmentUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 668caef0d2c..898ce45f60e 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -13,13 +13,13 @@ class ApplicationSetting < ActiveRecord::Base [\r\n] # any number of newline characters }x - serialize :restricted_visibility_levels # rubocop:disable Cop/ActiverecordSerialize - serialize :import_sources # rubocop:disable Cop/ActiverecordSerialize - serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :domain_whitelist, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :domain_blacklist, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :repository_storages # rubocop:disable Cop/ActiverecordSerialize - serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :restricted_visibility_levels # rubocop:disable Cop/ActiveRecordSerialize + serialize :import_sources # rubocop:disable Cop/ActiveRecordSerialize + serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :domain_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :domain_blacklist, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :repository_storages # rubocop:disable Cop/ActiveRecordSerialize + serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiveRecordSerialize cache_markdown_field :sign_in_text cache_markdown_field :help_page_text @@ -184,6 +184,9 @@ class ApplicationSetting < ActiveRecord::Base Rails.cache.fetch(CACHE_KEY) do ApplicationSetting.last end + rescue + # Fall back to an uncached value if there are any problems (e.g. redis down) + ApplicationSetting.last end def self.expire @@ -234,6 +237,8 @@ class ApplicationSetting < ActiveRecord::Base koding_url: nil, max_artifacts_size: Settings.artifacts['max_size'], max_attachment_size: Settings.gitlab['max_attachment_size'], + password_authentication_enabled: Settings.gitlab['password_authentication_enabled'], + performance_bar_allowed_group_id: nil, plantuml_enabled: false, plantuml_url: nil, recaptcha_enabled: false, @@ -247,7 +252,6 @@ class ApplicationSetting < ActiveRecord::Base shared_runners_text: nil, sidekiq_throttling_enabled: false, sign_in_text: nil, - signin_enabled: Settings.gitlab['signin_enabled'], signup_enabled: Settings.gitlab['signup_enabled'], terminal_max_session_time: 0, two_factor_grace_period: 48, @@ -336,6 +340,48 @@ class ApplicationSetting < ActiveRecord::Base super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) }) end + def performance_bar_allowed_group_id=(group_full_path) + group_full_path = nil if group_full_path.blank? + + if group_full_path.nil? + if group_full_path != performance_bar_allowed_group_id + super(group_full_path) + Gitlab::PerformanceBar.expire_allowed_user_ids_cache + end + return + end + + group = Group.find_by_full_path(group_full_path) + + if group + if group.id != performance_bar_allowed_group_id + super(group.id) + Gitlab::PerformanceBar.expire_allowed_user_ids_cache + end + else + super(nil) + Gitlab::PerformanceBar.expire_allowed_user_ids_cache + end + end + + def performance_bar_allowed_group + Group.find_by_id(performance_bar_allowed_group_id) + end + + # Return true if the Performance Bar is enabled for a given group + def performance_bar_enabled + performance_bar_allowed_group_id.present? + end + + # - If `enable` is true, we early return since the actual attribute that holds + # the enabling/disabling is `performance_bar_allowed_group_id` + # - If `enable` is false, we set `performance_bar_allowed_group_id` to `nil` + def performance_bar_enabled=(enable) + return if enable + + self.performance_bar_allowed_group_id = nil + end + # Choose one of the available repository storage options. Currently all have # equal weighting. def pick_repository_storage diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb index 46d412fbd72..112a8778b4e 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -1,5 +1,5 @@ class AuditEvent < ActiveRecord::Base - serialize :details, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize belongs_to :user, foreign_key: :author_id diff --git a/app/models/blob_viewer/readme.rb b/app/models/blob_viewer/readme.rb index 75c373a03bb..4604a9934a0 100644 --- a/app/models/blob_viewer/readme.rb +++ b/app/models/blob_viewer/readme.rb @@ -10,5 +10,11 @@ module BlobViewer def visible_to?(current_user) can?(current_user, :read_wiki, project) end + + def render_error + return if project.has_external_wiki? || (project.wiki_enabled? && project.wiki.has_home_page?) + + :no_wiki + end end end diff --git a/app/models/board.rb b/app/models/board.rb index 18081a32157..97d0f550925 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -1,7 +1,7 @@ class Board < ActiveRecord::Base belongs_to :project - has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all + has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent validates :project, presence: true diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 2e7a80d308b..432f3f242eb 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -19,8 +19,8 @@ module Ci ) end - serialize :options # rubocop:disable Cop/ActiverecordSerialize - serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiverecordSerialize + serialize :options # rubocop:disable Cop/ActiveRecordSerialize + serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiveRecordSerialize delegate :name, to: :project, prefix: true @@ -186,6 +186,12 @@ module Ci # Variables whose value does not depend on environment def simple_variables + variables(environment: nil) + end + + # All variables, including those dependent on environment, which could + # contain unexpanded variables. + def variables(environment: persisted_environment) variables = predefined_variables variables += project.predefined_variables variables += pipeline.predefined_variables @@ -194,15 +200,13 @@ module Ci variables += project.deployment_variables if has_environment? variables += yaml_variables variables += user_variables - variables += project.secret_variables_for(ref).map(&:to_runner_variable) + variables += project.group.secret_variables_for(ref, project).map(&:to_runner_variable) if project.group + variables += secret_variables(environment: environment) variables += trigger_request.user_variables if trigger_request - variables - end + variables += pipeline.pipeline_schedule.job_variables if pipeline.pipeline_schedule + variables += persisted_environment_variables if environment - # All variables, including those dependent on environment, which could - # contain unexpanded variables. - def variables - simple_variables.concat(persisted_environment_variables) + variables end def merge_request @@ -216,7 +220,7 @@ module Ci .reorder(iid: :desc) merge_requests.find do |merge_request| - merge_request.commits_sha.include?(pipeline.sha) + merge_request.commit_shas.include?(pipeline.sha) end end end @@ -370,6 +374,11 @@ module Ci ] end + def secret_variables(environment: persisted_environment) + project.secret_variables_for(ref: ref, environment: environment) + .map(&:to_runner_variable) + end + def steps [Gitlab::Ci::Build::Step.from_commands(self), Gitlab::Ci::Build::Step.from_after_script(self)].compact diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb new file mode 100644 index 00000000000..f64bc245a67 --- /dev/null +++ b/app/models/ci/group_variable.rb @@ -0,0 +1,13 @@ +module Ci + class GroupVariable < ActiveRecord::Base + extend Ci::Model + include HasVariable + include Presentable + + belongs_to :group + + validates :key, uniqueness: { scope: :group_id } + + scope :unprotected, -> { where(protected: false) } + end +end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 8d1beca9771..e5b615a7cc0 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -14,7 +14,7 @@ module Ci has_many :stages has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id has_many :builds, foreign_key: :commit_id - has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id + has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent # Merge requests for which the current pipeline is running against # the merge request's latest commit. @@ -326,10 +326,24 @@ module Ci end end + def ci_yaml_file_path + if project.ci_config_path.blank? + '.gitlab-ci.yml' + else + project.ci_config_path + end + end + def ci_yaml_file return @ci_yaml_file if defined?(@ci_yaml_file) - @ci_yaml_file = project.repository.gitlab_ci_yml_for(sha) rescue nil + @ci_yaml_file = begin + project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path) + rescue Rugged::ReferenceError, GRPC::NotFound, GRPC::Internal + self.yaml_errors = + "Failed to load CI/CD config file at #{ci_yaml_file_path}" + nil + end end def has_yaml_errors? @@ -377,7 +391,8 @@ module Ci def predefined_variables [ - { key: 'CI_PIPELINE_ID', value: id.to_s, public: true } + { key: 'CI_PIPELINE_ID', value: id.to_s, public: true }, + { key: 'CI_CONFIG_PATH', value: ci_yaml_file_path, public: true } ] end diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb index 49455e79c15..085eeeae157 100644 --- a/app/models/ci/pipeline_schedule.rb +++ b/app/models/ci/pipeline_schedule.rb @@ -9,17 +9,21 @@ module Ci belongs_to :owner, class_name: 'User' has_one :last_pipeline, -> { order(id: :desc) }, class_name: 'Ci::Pipeline' has_many :pipelines + has_many :variables, class_name: 'Ci::PipelineScheduleVariable' validates :cron, unless: :importing?, cron: true, presence: { unless: :importing? } validates :cron_timezone, cron_timezone: true, presence: { unless: :importing? } validates :ref, presence: { unless: :importing? } validates :description, presence: true + validates :variables, variable_duplicates: true before_save :set_next_run_at scope :active, -> { where(active: true) } scope :inactive, -> { where(active: false) } + accepts_nested_attributes_for :variables, allow_destroy: true + def owned_by?(current_user) owner == current_user end @@ -52,5 +56,9 @@ module Ci Gitlab::Ci::CronParser.new(worker_cron, worker_time_zone) .next_time_from(next_run_at) end + + def job_variables + variables&.map(&:to_runner_variable) || [] + end end end diff --git a/app/models/ci/pipeline_schedule_variable.rb b/app/models/ci/pipeline_schedule_variable.rb new file mode 100644 index 00000000000..1ff177616e8 --- /dev/null +++ b/app/models/ci/pipeline_schedule_variable.rb @@ -0,0 +1,8 @@ +module Ci + class PipelineScheduleVariable < ActiveRecord::Base + extend Ci::Model + include HasVariable + + belongs_to :pipeline_schedule + end +end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index d12f96f3d0b..c6d23898560 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -3,12 +3,12 @@ module Ci extend Ci::Model RUNNER_QUEUE_EXPIRY_TIME = 60.minutes - LAST_CONTACT_TIME = 1.hour.ago + ONLINE_CONTACT_TIMEOUT = 1.hour AVAILABLE_SCOPES = %w[specific shared active paused online].freeze FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze has_many :builds - has_many :runner_projects, dependent: :destroy + has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :runner_projects has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' @@ -19,7 +19,7 @@ module Ci scope :shared, ->() { where(is_shared: true) } scope :active, ->() { where(active: true) } scope :paused, ->() { where(active: false) } - scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } + scope :online, ->() { where('contacted_at > ?', contact_time_deadline) } scope :ordered, ->() { order(id: :desc) } scope :owned_or_shared, ->(project_id) do @@ -59,6 +59,10 @@ module Ci where(t[:token].matches(pattern).or(t[:description].matches(pattern))) end + def self.contact_time_deadline + ONLINE_CONTACT_TIMEOUT.ago + end + def set_default_values self.token = SecureRandom.hex(15) if self.token.blank? end @@ -80,7 +84,7 @@ module Ci end def online? - contacted_at && contacted_at > LAST_CONTACT_TIME + contacted_at && contacted_at > self.class.contact_time_deadline end def status @@ -145,7 +149,7 @@ module Ci private def cleanup_runner_queue - Gitlab::Redis.with do |redis| + Gitlab::Redis::Queues.with do |redis| redis.del(runner_queue_key) end end diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index 564334ad1ad..c58ce5c3717 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -6,7 +6,7 @@ module Ci belongs_to :pipeline, foreign_key: :commit_id has_many :builds - serialize :variables # rubocop:disable Cop/ActiverecordSerialize + serialize :variables # rubocop:disable Cop/ActiveRecordSerialize def user_variables return [] unless variables diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 0b8d0ff881a..cf0fe04ddaf 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -2,6 +2,7 @@ module Ci class Variable < ActiveRecord::Base extend Ci::Model include HasVariable + include Presentable belongs_to :project diff --git a/app/models/commit.rb b/app/models/commit.rb index 20206d57c4c..c7f62617c4c 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -138,7 +138,7 @@ class Commit safe_message.split("\n", 2)[1].try(:chomp) end - + def description? description.present? end diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index a7fd0a15f0f..f4f9b037957 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -2,7 +2,7 @@ module Awardable extend ActiveSupport::Concern included do - has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, dependent: :destroy + has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent if self < Participable # By default we always load award_emoji user association diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index eb32bf3d32a..95152dcd68c 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -78,7 +78,7 @@ module CacheMarkdownField def cached_html_up_to_date?(markdown_field) html_field = cached_markdown_fields.html_field(markdown_field) - cached = !cached_html_for(markdown_field).nil? && !__send__(markdown_field).nil? + cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present? return false unless cached markdown_changed = attribute_changed?(markdown_field) || false diff --git a/app/models/concerns/created_at_filterable.rb b/app/models/concerns/created_at_filterable.rb new file mode 100644 index 00000000000..e8a3e41203d --- /dev/null +++ b/app/models/concerns/created_at_filterable.rb @@ -0,0 +1,12 @@ +module CreatedAtFilterable + extend ActiveSupport::Concern + + included do + scope :created_before, ->(date) { where(scoped_table[:created_at].lteq(date)) } + scope :created_after, ->(date) { where(scoped_table[:created_at].gteq(date)) } + + def self.scoped_table + arel_table.alias(table_name) + end + end +end diff --git a/app/models/concerns/each_batch.rb b/app/models/concerns/each_batch.rb new file mode 100644 index 00000000000..6ddbb8da1a9 --- /dev/null +++ b/app/models/concerns/each_batch.rb @@ -0,0 +1,81 @@ +module EachBatch + extend ActiveSupport::Concern + + module ClassMethods + # Iterates over the rows in a relation in batches, similar to Rails' + # `in_batches` but in a more efficient way. + # + # Unlike `in_batches` provided by Rails this method does not support a + # custom start/end range, nor does it provide support for the `load:` + # keyword argument. + # + # This method will yield an ActiveRecord::Relation to the supplied block, or + # return an Enumerator if no block is given. + # + # Example: + # + # User.each_batch do |relation| + # relation.update_all(updated_at: Time.now) + # end + # + # The supplied block is also passed an optional batch index: + # + # User.each_batch do |relation, index| + # puts index # => 1, 2, 3, ... + # end + # + # You can also specify an alternative column to use for ordering the rows: + # + # User.each_batch(column: :created_at) do |relation| + # ... + # end + # + # This will produce SQL queries along the lines of: + # + # User Load (0.7ms) SELECT "users"."id" FROM "users" WHERE ("users"."id" >= 41654) ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1000 + # (0.7ms) SELECT COUNT(*) FROM "users" WHERE ("users"."id" >= 41654) AND ("users"."id" < 42687) + # + # of - The number of rows to retrieve per batch. + # column - The column to use for ordering the batches. + def each_batch(of: 1000, column: primary_key) + unless column + raise ArgumentError, + 'the column: argument must be set to a column name to use for ordering rows' + end + + start = except(:select) + .select(column) + .reorder(column => :asc) + .take + + return unless start + + start_id = start[column] + arel_table = self.arel_table + + 1.step do |index| + stop = except(:select) + .select(column) + .where(arel_table[column].gteq(start_id)) + .reorder(column => :asc) + .offset(of) + .limit(1) + .take + + relation = where(arel_table[column].gteq(start_id)) + + if stop + stop_id = stop[column] + start_id = stop_id + relation = relation.where(arel_table[column].lt(stop_id)) + end + + # Any ORDER BYs are useless for this relation and can lead to less + # efficient UPDATE queries, hence we get rid of it. + yield relation.except(:order), index + + break unless stop + end + end + end +end diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/internal_id.rb index 5382dde6765..67a0adfcd56 100644 --- a/app/models/concerns/internal_id.rb +++ b/app/models/concerns/internal_id.rb @@ -8,7 +8,8 @@ module InternalId def set_iid if iid.blank? - records = project.send(self.class.name.tableize) + parent = project || group + records = parent.send(self.class.name.tableize) records = records.with_deleted if self.paranoid? max_iid = records.maximum(:iid) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 41c8b525273..13fe9d09c69 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -30,7 +30,8 @@ module Issuable belongs_to :updated_by, class_name: "User" belongs_to :last_edited_by, class_name: 'User' belongs_to :milestone - has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :destroy do + + has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :destroy do # rubocop:disable Cop/ActiveRecordDependent def authors_loaded? # We check first if we're loaded to not load unnecessarily. loaded? && to_a.all? { |note| note.association(:author).loaded? } @@ -42,9 +43,9 @@ module Issuable end end - has_many :label_links, as: :target, dependent: :destroy + has_many :label_links, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :labels, through: :label_links - has_many :todos, as: :target, dependent: :destroy + has_many :todos, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :metrics diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index 01599ce49c6..f0998465822 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -70,6 +70,22 @@ module Milestoneish due_date && due_date.past? end + def is_group_milestone? + false + end + + def is_project_milestone? + false + end + + def is_legacy_group_milestone? + false + end + + def is_dashboard_milestone? + false + end + private def count_issues_by_state(user) diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb index 47e71c58557..fc6b840f7a8 100644 --- a/app/models/concerns/protected_ref.rb +++ b/app/models/concerns/protected_ref.rb @@ -17,7 +17,7 @@ module ProtectedRef class_methods do def protected_ref_access_levels(*types) types.each do |type| - has_many :"#{type}_access_levels", dependent: :destroy + has_many :"#{type}_access_levels", dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." } diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb index ee108f010a6..f5048d17d80 100644 --- a/app/models/concerns/routable.rb +++ b/app/models/concerns/routable.rb @@ -4,8 +4,8 @@ module Routable extend ActiveSupport::Concern included do - has_one :route, as: :source, autosave: true, dependent: :destroy - has_many :redirect_routes, as: :source, autosave: true, dependent: :destroy + has_one :route, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :redirect_routes, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates_associated :route validates :route, presence: true diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb index c28974a3cdf..67ecf470f7e 100644 --- a/app/models/concerns/sha_attribute.rb +++ b/app/models/concerns/sha_attribute.rb @@ -3,6 +3,8 @@ module ShaAttribute module ClassMethods def sha_attribute(name) + return unless table_exists? + column = columns.find { |c| c.name == name.to_s } # In case the table doesn't exist we won't be able to find the column, diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index fdacfa5a194..a155a064032 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -5,25 +5,6 @@ module Sortable extend ActiveSupport::Concern - module DropDefaultScopeOnFinders - # Override these methods to drop the `ORDER BY id DESC` default scope. - # See http://dba.stackexchange.com/a/110919 for why we do this. - %i[find find_by find_by!].each do |meth| - define_method meth do |*args, &block| - return super(*args, &block) if block - - unordered_relation = unscope(:order) - - # We cannot simply call `meth` on `unscope(:order)`, since that is also - # an instance of the same relation class this module is included into, - # which means we'd get infinite recursion. - # We explicitly use the original implementation to prevent this. - original_impl = method(__method__).super_method.unbind - original_impl.bind(unordered_relation).call(*args) - end - end - end - included do # By default all models should be ordered # by created_at field starting from newest @@ -37,10 +18,6 @@ module Sortable scope :order_updated_asc, -> { reorder(updated_at: :asc) } scope :order_name_asc, -> { reorder(name: :asc) } scope :order_name_desc, -> { reorder(name: :desc) } - - # All queries (relations) on this model are instances of this `relation_klass`. - relation_klass = relation_delegate_class(ActiveRecord::Relation) - relation_klass.prepend DropDefaultScopeOnFinders end module ClassMethods diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb index 647a6cad3d7..bd75f25a210 100644 --- a/app/models/concerns/spammable.rb +++ b/app/models/concerns/spammable.rb @@ -8,7 +8,7 @@ module Spammable end included do - has_one :user_agent_detail, as: :subject, dependent: :destroy + has_one :user_agent_detail, as: :subject, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent attr_accessor :spam attr_accessor :spam_log diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb index f60a0f8f438..274b38a7708 100644 --- a/app/models/concerns/subscribable.rb +++ b/app/models/concerns/subscribable.rb @@ -9,7 +9,7 @@ module Subscribable extend ActiveSupport::Concern included do - has_many :subscriptions, dependent: :destroy, as: :subscribable + has_many :subscriptions, dependent: :destroy, as: :subscribable # rubocop:disable Cop/ActiveRecordDependent end def subscribed?(user, project = nil) diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb index 9cf83440784..b517ddaebd7 100644 --- a/app/models/concerns/time_trackable.rb +++ b/app/models/concerns/time_trackable.rb @@ -18,7 +18,7 @@ module TimeTrackable validates :time_estimate, numericality: { message: 'has an invalid format' }, allow_nil: false validate :check_negative_time_spent - has_many :timelogs, dependent: :destroy + has_many :timelogs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent end def spend_time(options) diff --git a/app/models/dashboard_milestone.rb b/app/models/dashboard_milestone.rb index 646c1e5ce1a..fac7c5e5c85 100644 --- a/app/models/dashboard_milestone.rb +++ b/app/models/dashboard_milestone.rb @@ -2,4 +2,8 @@ class DashboardMilestone < GlobalMilestone def issues_finder_params { authorized_only: true } end + + def is_dashboard_milestone? + true + end end diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 053f2a11aa0..51768dd96bc 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -1,5 +1,5 @@ class DeployKey < Key - has_many :deploy_keys_projects, dependent: :destroy + has_many :deploy_keys_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :deploy_keys_projects scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where('deploy_keys_projects.project_id in (?)', projects) } diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 20ef1378500..e9a60e6ce09 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -6,9 +6,9 @@ class DiffNote < Note NOTEABLE_TYPES = %w(MergeRequest Commit).freeze - serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize - serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize - serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize + serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize + serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize + serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize validates :original_position, presence: true validates :position, presence: true diff --git a/app/models/environment.rb b/app/models/environment.rb index 66c96d0f586..e9ebf0637f3 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -6,7 +6,7 @@ class Environment < ActiveRecord::Base belongs_to :project, required: true, validate: true - has_many :deployments, dependent: :destroy + has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment' before_validation :nullify_external_url @@ -218,8 +218,7 @@ class Environment < ActiveRecord::Base end def etag_cache_key - Gitlab::Routing.url_helpers.namespace_project_environments_path( - project.namespace, + Gitlab::Routing.url_helpers.project_environments_path( project, format: :json) end diff --git a/app/models/event.rb b/app/models/event.rb index 29bc141c5cd..8d93a228494 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -50,7 +50,7 @@ class Event < ActiveRecord::Base belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations # For Hash only - serialize :data # rubocop:disable Cop/ActiverecordSerialize + serialize :data # rubocop:disable Cop/ActiveRecordSerialize # Callbacks after_create :reset_project_activity diff --git a/app/models/global_label.rb b/app/models/global_label.rb index 698a7bbd327..2a1b7564962 100644 --- a/app/models/global_label.rb +++ b/app/models/global_label.rb @@ -2,7 +2,7 @@ class GlobalLabel attr_accessor :title, :labels alias_attribute :name, :title - delegate :color, :description, to: :@first_label + delegate :color, :text_color, :description, to: :@first_label def for_display @first_label diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 538615130a7..c0864769314 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -2,6 +2,7 @@ class GlobalMilestone include Milestoneish EPOCH = DateTime.parse('1970-01-01') + STATE_COUNT_HASH = { opened: 0, closed: 0, all: 0 }.freeze attr_accessor :title, :milestones alias_attribute :name, :title @@ -11,7 +12,10 @@ class GlobalMilestone end def self.build_collection(projects, params) - child_milestones = MilestonesFinder.new.execute(projects, params) + params = + { project_ids: projects.map(&:id), state: params[:state] } + + child_milestones = MilestonesFinder.new(params).execute milestones = child_milestones.select(:id, :title).group_by(&:title).map do |title, grouped| milestones_relation = Milestone.where(id: grouped.map(&:id)) @@ -28,13 +32,42 @@ class GlobalMilestone new(title, child_milestones) end - def self.states_count(projects) - relation = MilestonesFinder.new.execute(projects, state: 'all') - milestones_by_state_and_title = relation.reorder(nil).group(:state, :title).count + def self.states_count(projects, group = nil) + legacy_group_milestones_count = legacy_group_milestone_states_count(projects) + group_milestones_count = group_milestones_states_count(group) + + legacy_group_milestones_count.merge(group_milestones_count) do |k, legacy_group_milestones_count, group_milestones_count| + legacy_group_milestones_count + group_milestones_count + end + end + + def self.group_milestones_states_count(group) + return STATE_COUNT_HASH unless group + + params = { group_ids: [group.id], state: 'all', order: nil } + + relation = MilestonesFinder.new(params).execute + grouped_by_state = relation.group(:state).count + + { + opened: grouped_by_state['active'] || 0, + closed: grouped_by_state['closed'] || 0, + all: grouped_by_state.values.sum + } + end + + # Counts the legacy group milestones which must be grouped by title + def self.legacy_group_milestone_states_count(projects) + return STATE_COUNT_HASH unless projects + + params = { project_ids: projects.map(&:id), state: 'all', order: nil } + + relation = MilestonesFinder.new(params).execute + project_milestones_by_state_and_title = relation.group(:state, :title).count - opened = count_by_state(milestones_by_state_and_title, 'active') - closed = count_by_state(milestones_by_state_and_title, 'closed') - all = milestones_by_state_and_title.map { |(_, title), _| title }.uniq.count + opened = count_by_state(project_milestones_by_state_and_title, 'active') + closed = count_by_state(project_milestones_by_state_and_title, 'closed') + all = project_milestones_by_state_and_title.map { |(_, title), _| title }.uniq.count { opened: opened, diff --git a/app/models/group.rb b/app/models/group.rb index a6fdb30f84c..dfa4e8adedd 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -2,13 +2,12 @@ require 'carrierwave/orm/activerecord' class Group < Namespace include Gitlab::ConfigHelper - include Gitlab::VisibilityLevel include AccessRequestable include Avatarable include Referable include SelectForProjectAuthorization - has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent alias_method :members, :group_members has_many :users, through: :group_members has_many :owners, @@ -16,12 +15,14 @@ class Group < Namespace through: :group_members, source: :user - has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' + has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent - has_many :project_group_links, dependent: :destroy + has_many :milestones + has_many :project_group_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :shared_projects, through: :project_group_links, source: :project - has_many :notification_settings, dependent: :destroy, as: :source + has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent has_many :labels, class_name: 'GroupLabel' + has_many :variables, class_name: 'Ci::GroupVariable' validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validate :visibility_level_allowed_by_projects @@ -31,7 +32,7 @@ class Group < Namespace validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 } mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent after_create :post_create_hook after_destroy :post_destroy_hook @@ -101,10 +102,6 @@ class Group < Namespace full_name end - def visibility_level_field - :visibility_level - end - def visibility_level_allowed_by_projects allowed_by_projects = self.projects.where('visibility_level > ?', self.visibility_level).none? @@ -248,6 +245,14 @@ class Group < Namespace } end + def secret_variables_for(ref, project) + list_of_ids = [self] + ancestors + variables = Ci::GroupVariable.where(group: list_of_ids) + variables = variables.unprotected unless project.protected_for?(ref) + variables = variables.group_by(&:group_id) + list_of_ids.reverse.map { |group| variables[group.id] }.compact.flatten + end + protected def update_two_factor_requirement diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb index 86d38e5468b..65249bd7bfc 100644 --- a/app/models/group_milestone.rb +++ b/app/models/group_milestone.rb @@ -16,4 +16,8 @@ class GroupMilestone < GlobalMilestone def issues_finder_params { group_id: group.id } end + + def is_legacy_group_milestone? + true + end end diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 7503f3739c3..7a9f8997959 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -12,7 +12,7 @@ class WebHook < ActiveRecord::Base default_value_for :repository_update_events, false default_value_for :enable_ssl_verification, true - has_many :web_hook_logs, dependent: :destroy + has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent scope :push_hooks, -> { where(push_events: true) } scope :tag_push_hooks, -> { where(tag_push_events: true) } diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb index d73cfcf630d..e72c125fb69 100644 --- a/app/models/hooks/web_hook_log.rb +++ b/app/models/hooks/web_hook_log.rb @@ -1,9 +1,9 @@ class WebHookLog < ActiveRecord::Base belongs_to :web_hook - serialize :request_headers, Hash # rubocop:disable Cop/ActiverecordSerialize - serialize :request_data, Hash # rubocop:disable Cop/ActiverecordSerialize - serialize :response_headers, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :request_headers, Hash # rubocop:disable Cop/ActiveRecordSerialize + serialize :request_data, Hash # rubocop:disable Cop/ActiveRecordSerialize + serialize :response_headers, Hash # rubocop:disable Cop/ActiveRecordSerialize validates :web_hook, presence: true diff --git a/app/models/issue.rb b/app/models/issue.rb index 3a9a6dba601..400bb55d2f0 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -10,6 +10,7 @@ class Issue < ActiveRecord::Base include FasterCacheKeys include RelativePositioning include IgnorableColumn + include CreatedAtFilterable ignore_column :position @@ -23,9 +24,14 @@ class Issue < ActiveRecord::Base belongs_to :project belongs_to :moved_to, class_name: 'Issue' - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all + has_many :merge_requests_closing_issues, + class_name: 'MergeRequestsClosingIssues', + dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + + has_many :issue_assignees + has_many :assignees, class_name: "User", through: :issue_assignees has_many :issue_assignees has_many :assignees, class_name: "User", through: :issue_assignees @@ -45,8 +51,6 @@ class Issue < ActiveRecord::Base scope :order_due_date_asc, -> { reorder('issues.due_date IS NULL, issues.due_date ASC') } scope :order_due_date_desc, -> { reorder('issues.due_date IS NULL, issues.due_date DESC') } - scope :created_after, -> (datetime) { where("created_at >= ?", datetime) } - scope :preload_associations, -> { preload(:labels, project: :namespace) } after_save :expire_etag_cache @@ -295,11 +299,7 @@ class Issue < ActiveRecord::Base end def expire_etag_cache - key = Gitlab::Routing.url_helpers.realtime_changes_namespace_project_issue_path( - project.namespace, - project, - self - ) + key = Gitlab::Routing.url_helpers.realtime_changes_project_issue_path(project, self) Gitlab::EtagCaching::Store.new.touch(key) end end diff --git a/app/models/label.rb b/app/models/label.rb index ed6a8411da9..674bb3f2720 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -15,9 +15,9 @@ class Label < ActiveRecord::Base default_value_for :color, DEFAULT_COLOR - has_many :lists, dependent: :destroy + has_many :lists, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :priorities, class_name: 'LabelPriority' - has_many :label_links, dependent: :destroy + has_many :label_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :issues, through: :label_links, source: :target, source_type: 'Issue' has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest' diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index 2d5909ab25e..c36be956ff0 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -7,7 +7,7 @@ class LegacyDiffNote < Note include NoteOnDiff - serialize :st_diff # rubocop:disable Cop/ActiverecordSerialize + serialize :st_diff # rubocop:disable Cop/ActiveRecordSerialize validates :line_code, presence: true, line_code: true diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 7712d5783e0..b7cf96abe83 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -1,5 +1,5 @@ class LfsObject < ActiveRecord::Base - has_many :lfs_objects_projects, dependent: :destroy + has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :lfs_objects_projects validates :oid, presence: true, uniqueness: true diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 808212c780c..e4e7999d0f2 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -5,6 +5,7 @@ class MergeRequest < ActiveRecord::Base include Referable include Sortable include IgnorableColumn + include CreatedAtFilterable ignore_column :position @@ -12,26 +13,25 @@ class MergeRequest < ActiveRecord::Base belongs_to :source_project, class_name: "Project" belongs_to :merge_user, class_name: "User" - has_many :merge_request_diffs, dependent: :destroy + has_many :merge_request_diffs has_one :merge_request_diff, - -> { order('merge_request_diffs.id DESC') } + -> { order('merge_request_diffs.id DESC') }, inverse_of: :merge_request belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline" - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all + has_many :merge_requests_closing_issues, + class_name: 'MergeRequestsClosingIssues', + dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent belongs_to :assignee, class_name: "User" - serialize :merge_params, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize after_create :ensure_merge_request_diff, unless: :importing? after_update :reload_diff_if_branch_changed - delegate :commits, :real_size, :commits_sha, :commits_count, - to: :merge_request_diff, prefix: nil - # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests attr_accessor :allow_broken @@ -221,6 +221,36 @@ class MergeRequest < ActiveRecord::Base "#{project.to_reference(from, full: full)}#{reference}" end + def commits + if persisted? + merge_request_diff.commits + elsif compare_commits + compare_commits.reverse + else + [] + end + end + + def commits_count + if persisted? + merge_request_diff.commits_count + elsif compare_commits + compare_commits.size + else + 0 + end + end + + def commit_shas + if persisted? + merge_request_diff.commit_shas + elsif compare_commits + compare_commits.reverse.map(&:sha) + else + [] + end + end + def first_commit merge_request_diff ? merge_request_diff.first_commit : compare_commits.first end @@ -243,9 +273,7 @@ class MergeRequest < ActiveRecord::Base def diff_size # Calling `merge_request_diff.diffs.real_size` will also perform # highlighting, which we don't need here. - return real_size if merge_request_diff - - diffs.real_size + merge_request_diff&.real_size || diffs.real_size end def diff_base_commit @@ -516,7 +544,7 @@ class MergeRequest < ActiveRecord::Base def related_notes # Fetch comments only from last 100 commits commits_for_notes_limit = 100 - commit_ids = commits.last(commits_for_notes_limit).map(&:id) + commit_ids = commit_shas.take(commits_for_notes_limit) Note.where( "(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" + @@ -839,15 +867,18 @@ class MergeRequest < ActiveRecord::Base return Ci::Pipeline.none unless source_project @all_pipelines ||= source_project.pipelines - .where(sha: all_commits_sha, ref: source_branch) + .where(sha: all_commit_shas, ref: source_branch) .order(id: :desc) end # Note that this could also return SHA from now dangling commits # - def all_commits_sha + def all_commit_shas if persisted? - merge_request_diffs.flat_map(&:commits_sha).uniq + column_shas = MergeRequestDiffCommit.where(merge_request_diff: merge_request_diffs).pluck('DISTINCT(sha)') + serialised_shas = merge_request_diffs.where.not(st_commits: nil).flat_map(&:commit_shas) + + (column_shas + serialised_shas).uniq elsif compare_commits compare_commits.to_a.reverse.map(&:id) else diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index f1ee4d3f7a9..4b141945ab4 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -11,9 +11,10 @@ class MergeRequestDiff < ActiveRecord::Base belongs_to :merge_request has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) } + has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) } - serialize :st_commits # rubocop:disable Cop/ActiverecordSerialize - serialize :st_diffs # rubocop:disable Cop/ActiverecordSerialize + serialize :st_commits # rubocop:disable Cop/ActiveRecordSerialize + serialize :st_diffs # rubocop:disable Cop/ActiveRecordSerialize state_machine :state, initial: :empty do state :collected @@ -47,14 +48,13 @@ class MergeRequestDiff < ActiveRecord::Base # Collect information about commits and diff from repository # and save it to the database as serialized data def save_git_content - ensure_commits_sha + ensure_commit_shas save_commits - reload_commits save_diffs keep_around_commits end - def ensure_commits_sha + def ensure_commit_shas merge_request.fetch_ref self.start_commit_sha ||= merge_request.target_branch_sha self.head_commit_sha ||= merge_request.source_branch_sha @@ -66,7 +66,7 @@ class MergeRequestDiff < ActiveRecord::Base # created before version 8.4 that does not store head_commit_sha in separate db field. def head_commit_sha if persisted? && super.nil? - last_commit.try(:sha) + last_commit_sha else super end @@ -97,16 +97,11 @@ class MergeRequestDiff < ActiveRecord::Base end def commits - @commits ||= load_commits(st_commits) + @commits ||= load_commits end - def reload_commits - @commits = nil - commits - end - - def last_commit - commits.first + def last_commit_sha + commit_shas.first end def first_commit @@ -131,8 +126,12 @@ class MergeRequestDiff < ActiveRecord::Base project.commit(head_commit_sha) end - def commits_sha - st_commits.map { |commit| commit[:id] } + def commit_shas + if st_commits.present? + st_commits.map { |commit| commit[:id] } + else + merge_request_diff_commits.map(&:sha) + end end def diff_refs=(new_diff_refs) @@ -207,7 +206,11 @@ class MergeRequestDiff < ActiveRecord::Base end def commits_count - st_commits.count + if st_commits.present? + st_commits.size + else + merge_request_diff_commits.size + end end def utf8_st_diffs @@ -231,29 +234,6 @@ class MergeRequestDiff < ActiveRecord::Base raw.any? { |element| VALID_CLASSES.include?(element.class) } end - def dump_commits(commits) - commits.map(&:to_hash) - end - - def load_commits(array) - array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash), merge_request.source_project) } - end - - # Load all commits related to current merge request diff from repo - # and save it as array of hashes in st_commits db field - def save_commits - new_attributes = {} - - commits = compare.commits - - if commits.present? - commits = Commit.decorate(commits, merge_request.source_project).reverse - new_attributes[:st_commits] = dump_commits(commits) - end - - update_columns_serialized(new_attributes) - end - def create_merge_request_diff_files(diffs) rows = diffs.map.with_index do |diff, index| diff.to_hash.merge( @@ -294,12 +274,18 @@ class MergeRequestDiff < ActiveRecord::Base end end - # Load diffs between branches related to current merge request diff from repo - # and save it as array of hashes in st_diffs db field + def load_commits + commits = st_commits.presence || merge_request_diff_commits + + commits.map do |commit| + Commit.new(Gitlab::Git::Commit.new(commit.to_hash), merge_request.source_project) + end + end + def save_diffs new_attributes = {} - if commits.size.zero? + if compare.commits.size.zero? new_attributes[:state] = :empty else diff_collection = compare.diffs(Commit.max_diff_options) @@ -319,7 +305,13 @@ class MergeRequestDiff < ActiveRecord::Base new_attributes[:state] = :overflow if diff_collection.overflow? end - update_columns_serialized(new_attributes) + update(new_attributes) + end + + def save_commits + MergeRequestDiffCommit.create_bulk(self.id, compare.commits.reverse) + + merge_request_diff_commits.reload end def repository @@ -332,29 +324,6 @@ class MergeRequestDiff < ActiveRecord::Base project.merge_base_commit(head_commit_sha, start_commit_sha).try(:sha) end - # - # #save or #update_attributes providing changes on serialized attributes do a lot of - # serialization and deserialization calls resulting in bad performance. - # Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide. - # As a tradeoff we need to reload the current instance to properly manage time objects on those serialized - # attributes. So to keep the same behaviour as the attribute assignment we reload the instance. - # The difference is in the usage of - # #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns) - # - # Ex: - # - # new_attributes[:st_commits].first.slice(:committed_date) - # => {:committed_date=>2014-02-27 11:01:38 +0200} - # YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date))) - # => {:committed_date=>2014-02-27 10:01:38 +0100} - # - def update_columns_serialized(new_attributes) - return unless new_attributes.any? - - update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone)) - reload - end - def keep_around_commits [repository, merge_request.source_project.repository].each do |repo| repo.keep_around(start_commit_sha) diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb new file mode 100644 index 00000000000..cafdbe11849 --- /dev/null +++ b/app/models/merge_request_diff_commit.rb @@ -0,0 +1,38 @@ +class MergeRequestDiffCommit < ActiveRecord::Base + include ShaAttribute + + belongs_to :merge_request_diff + + sha_attribute :sha + alias_attribute :id, :sha + + def self.create_bulk(merge_request_diff_id, commits) + sha_attribute = Gitlab::Database::ShaAttribute.new + + rows = commits.map.with_index do |commit, index| + # See #parent_ids. + commit_hash = commit.to_hash.except(:parent_ids) + sha = commit_hash.delete(:id) + + commit_hash.merge( + merge_request_diff_id: merge_request_diff_id, + relative_order: index, + sha: sha_attribute.type_cast_for_database(sha) + ) + end + + Gitlab::Database.bulk_insert(self.table_name, rows) + end + + def to_hash + Gitlab::Git::Commit::SERIALIZE_KEYS.each_with_object({}) do |key, hash| + hash[key] = public_send(key) + end + end + + # We don't save these, because they would need a table or a serialised + # field. They aren't used anywhere, so just pretend the commit has no parents. + def parent_ids + [] + end +end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index d2e2749f70d..48d00764965 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -18,17 +18,32 @@ class Milestone < ActiveRecord::Base cache_markdown_field :description belongs_to :project + belongs_to :group + has_many :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues has_many :merge_requests - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + scope :of_projects, ->(ids) { where(project_id: ids) } + scope :of_groups, ->(ids) { where(group_id: ids) } scope :active, -> { with_state(:active) } scope :closed, -> { with_state(:closed) } - scope :of_projects, ->(ids) { where(project_id: ids) } + scope :for_projects, -> { where(group: nil).includes(:project) } + + scope :for_projects_and_groups, -> (project_ids, group_ids) do + conditions = [] + conditions << arel_table[:project_id].in(project_ids) if project_ids.compact.any? + conditions << arel_table[:group_id].in(group_ids) if group_ids.compact.any? + + where(conditions.reduce(:or)) + end + + validates :group, presence: true, unless: :project + validates :project, presence: true, unless: :group - validates :title, presence: true, uniqueness: { scope: :project_id } - validates :project, presence: true + validate :uniqueness_of_title, if: :title_changed? + validate :milestone_type_check validate :start_date_should_be_less_than_due_date, if: proc { |m| m.start_date.present? && m.due_date.present? } strip_attributes :title @@ -63,6 +78,14 @@ class Milestone < ActiveRecord::Base where(t[:title].matches(pattern).or(t[:description].matches(pattern))) end + + def filter_by_state(milestones, state) + case state + when 'closed' then milestones.closed + when 'all' then milestones + else milestones.active + end + end end def self.reference_prefix @@ -138,6 +161,8 @@ class Milestone < ActiveRecord::Base # Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1" # def to_reference(from_project = nil, format: :iid, full: false) + return if is_group_milestone? + format_reference = milestone_format_reference(format) reference = "#{self.class.reference_prefix}#{format_reference}" @@ -152,6 +177,10 @@ class Milestone < ActiveRecord::Base id end + def for_display + self + end + def can_be_closed? active? && issues.opened.count.zero? end @@ -164,8 +193,45 @@ class Milestone < ActiveRecord::Base write_attribute(:title, sanitize_title(value)) if value.present? end + def safe_title + title.to_slug.normalize.to_s + end + + def parent + group || project + end + + def is_group_milestone? + group_id.present? + end + + def is_project_milestone? + project_id.present? + end + private + # Milestone titles must be unique across project milestones and group milestones + def uniqueness_of_title + if project + relation = Milestone.for_projects_and_groups([project_id], [project.group&.id]) + elsif group + project_ids = group.projects.map(&:id) + relation = Milestone.for_projects_and_groups(project_ids, [group.id]) + end + + title_exists = relation.find_by_title(title) + errors.add(:title, "already being used for another group or project milestone.") if title_exists + end + + # Milestone should be either a project milestone or a group milestone + def milestone_type_check + if group_id && project_id + field = project_id_changed? ? :project_id : :group_id + errors.add(field, "milestone should belong either to a project or a group.") + end + end + def milestone_format_reference(format = :iid) raise ArgumentError, 'Unknown format' unless [:iid, :name].include?(format) diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 672eab94c07..0bb04194bdb 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -5,6 +5,7 @@ class Namespace < ActiveRecord::Base include Sortable include Gitlab::ShellAdapter include Gitlab::CurrentSettings + include Gitlab::VisibilityLevel include Routable include AfterCommitQueue @@ -15,13 +16,13 @@ class Namespace < ActiveRecord::Base cache_markdown_field :description, pipeline: :description - has_many :projects, dependent: :destroy + has_many :projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :project_statistics belongs_to :owner, class_name: "User" belongs_to :parent, class_name: "Namespace" has_many :children, class_name: "Namespace", foreign_key: :parent_id - has_one :chat_team, dependent: :destroy + has_one :chat_team, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, @@ -105,6 +106,10 @@ class Namespace < ActiveRecord::Base end end + def visibility_level_field + :visibility_level + end + def to_param full_path end diff --git a/app/models/note.rb b/app/models/note.rb index ca6999427c0..3d39047d32f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -46,8 +46,8 @@ class Note < ActiveRecord::Base belongs_to :updated_by, class_name: "User" belongs_to :last_edited_by, class_name: 'User' - has_many :todos, dependent: :destroy - has_many :events, as: :target, dependent: :destroy + has_many :todos, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :system_note_metadata delegate :gfm_reference, :local_reference, to: :noteable @@ -330,8 +330,7 @@ class Note < ActiveRecord::Base def expire_etag_cache return unless for_issue? - key = Gitlab::Routing.url_helpers.namespace_project_noteable_notes_path( - noteable.project.namespace, + key = Gitlab::Routing.url_helpers.project_noteable_notes_path( noteable.project, target_type: noteable_type.underscore, target_id: noteable.id diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index 6e13f9b2089..654be927ed8 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -3,7 +3,7 @@ class PersonalAccessToken < ActiveRecord::Base include TokenAuthenticatable add_authentication_token_field :token - serialize :scopes, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :scopes, Array # rubocop:disable Cop/ActiveRecordSerialize belongs_to :user diff --git a/app/models/project.rb b/app/models/project.rb index 8e9e42d28ab..0b357d5d003 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -59,6 +59,7 @@ class Project < ActiveRecord::Base update_column(:last_repository_updated_at, self.created_at) end + before_destroy :remove_private_deploy_keys after_destroy :remove_pages # update visibility_level of forks @@ -80,96 +81,108 @@ class Project < ActiveRecord::Base belongs_to :namespace has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event' - has_many :boards, before_add: :validate_board_limit, dependent: :destroy + has_many :boards, before_add: :validate_board_limit # Project services - has_one :campfire_service, dependent: :destroy - has_one :drone_ci_service, dependent: :destroy - has_one :emails_on_push_service, dependent: :destroy - has_one :pipelines_email_service, dependent: :destroy - has_one :irker_service, dependent: :destroy - has_one :pivotaltracker_service, dependent: :destroy - has_one :hipchat_service, dependent: :destroy - has_one :flowdock_service, dependent: :destroy - has_one :assembla_service, dependent: :destroy - has_one :asana_service, dependent: :destroy - has_one :gemnasium_service, dependent: :destroy - has_one :mattermost_slash_commands_service, dependent: :destroy - has_one :mattermost_service, dependent: :destroy - has_one :slack_slash_commands_service, dependent: :destroy - has_one :slack_service, dependent: :destroy - has_one :buildkite_service, dependent: :destroy - has_one :bamboo_service, dependent: :destroy - has_one :teamcity_service, dependent: :destroy - has_one :pushover_service, dependent: :destroy - has_one :jira_service, dependent: :destroy - has_one :redmine_service, dependent: :destroy - has_one :custom_issue_tracker_service, dependent: :destroy - has_one :bugzilla_service, dependent: :destroy - has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project - has_one :external_wiki_service, dependent: :destroy - has_one :kubernetes_service, dependent: :destroy, inverse_of: :project - has_one :prometheus_service, dependent: :destroy, inverse_of: :project - has_one :mock_ci_service, dependent: :destroy - has_one :mock_deployment_service, dependent: :destroy - has_one :mock_monitoring_service, dependent: :destroy - has_one :microsoft_teams_service, dependent: :destroy - - has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" + has_one :campfire_service + has_one :drone_ci_service + has_one :emails_on_push_service + has_one :pipelines_email_service + has_one :irker_service + has_one :pivotaltracker_service + has_one :hipchat_service + has_one :flowdock_service + has_one :assembla_service + has_one :asana_service + has_one :gemnasium_service + has_one :mattermost_slash_commands_service + has_one :mattermost_service + has_one :slack_slash_commands_service + has_one :slack_service + has_one :buildkite_service + has_one :bamboo_service + has_one :teamcity_service + has_one :pushover_service + has_one :jira_service + has_one :redmine_service + has_one :custom_issue_tracker_service + has_one :bugzilla_service + has_one :gitlab_issue_tracker_service, inverse_of: :project + has_one :external_wiki_service + has_one :kubernetes_service, inverse_of: :project + has_one :prometheus_service, inverse_of: :project + has_one :mock_ci_service + has_one :mock_deployment_service + has_one :mock_monitoring_service + has_one :microsoft_teams_service + + has_one :forked_project_link, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link has_many :forked_project_links, foreign_key: "forked_from_project_id" has_many :forks, through: :forked_project_links, source: :forked_to_project # Merge Requests for target project should be removed with it - has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id' - has_many :issues, dependent: :destroy - has_many :labels, dependent: :destroy, class_name: 'ProjectLabel' - has_many :services, dependent: :destroy - has_many :events, dependent: :destroy - has_many :milestones, dependent: :destroy - has_many :notes, dependent: :destroy - has_many :snippets, dependent: :destroy, class_name: 'ProjectSnippet' - has_many :hooks, dependent: :destroy, class_name: 'ProjectHook' - has_many :protected_branches, dependent: :destroy - has_many :protected_tags, dependent: :destroy + has_many :merge_requests, foreign_key: 'target_project_id' + has_many :issues + has_many :labels, class_name: 'ProjectLabel' + has_many :services + has_many :events + has_many :milestones + has_many :notes + has_many :snippets, class_name: 'ProjectSnippet' + has_many :hooks, class_name: 'ProjectHook' + has_many :protected_branches + has_many :protected_tags has_many :project_authorizations has_many :authorized_users, through: :project_authorizations, source: :user, class_name: 'User' - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source + has_many :project_members, -> { where(requested_at: nil) }, + as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + alias_method :members, :project_members has_many :users, through: :project_members - has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'ProjectMember' + has_many :requesters, -> { where.not(requested_at: nil) }, + as: :source, class_name: 'ProjectMember', dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent - has_many :deploy_keys_projects, dependent: :destroy + has_many :deploy_keys_projects has_many :deploy_keys, through: :deploy_keys_projects - has_many :users_star_projects, dependent: :destroy + has_many :users_star_projects has_many :starrers, through: :users_star_projects, source: :user - has_many :releases, dependent: :destroy - has_many :lfs_objects_projects, dependent: :destroy + has_many :releases + has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :lfs_objects, through: :lfs_objects_projects - has_many :project_group_links, dependent: :destroy + has_many :project_group_links has_many :invited_groups, through: :project_group_links, source: :group - has_many :pages_domains, dependent: :destroy - has_many :todos, dependent: :destroy - has_many :notification_settings, dependent: :destroy, as: :source - - has_one :import_data, dependent: :delete, class_name: 'ProjectImportData' - has_one :project_feature, dependent: :destroy - has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete - has_many :container_repositories, dependent: :destroy - - has_many :commit_statuses, dependent: :destroy - has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline' - has_many :builds, class_name: 'Ci::Build' # the builds are created from the commit_statuses - has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' + has_many :pages_domains + has_many :todos + has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + + has_one :import_data, class_name: 'ProjectImportData' + has_one :project_feature + has_one :statistics, class_name: 'ProjectStatistics' + + # Container repositories need to remove data from the container registry, + # which is not managed by the DB. Hence we're still using dependent: :destroy + # here. + has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + + has_many :commit_statuses + has_many :pipelines, class_name: 'Ci::Pipeline' + + # Ci::Build objects store data on the file system such as artifact files and + # build traces. Currently there's no efficient way of removing this data in + # bulk that doesn't involve loading the rows into memory. As a result we're + # still using `dependent: :destroy` here. + has_many :builds, class_name: 'Ci::Build', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :runner_projects, class_name: 'Ci::RunnerProject' has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' has_many :variables, class_name: 'Ci::Variable' - has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger' - has_many :environments, dependent: :destroy - has_many :deployments, dependent: :destroy - has_many :pipeline_schedules, dependent: :destroy, class_name: 'Ci::PipelineSchedule' + has_many :triggers, class_name: 'Ci::Trigger' + has_many :environments + has_many :deployments + has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule' has_many :active_runners, -> { active }, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' @@ -186,6 +199,11 @@ class Project < ActiveRecord::Base # Validations validates :creator, presence: true, on: :create validates :description, length: { maximum: 2000 }, allow_blank: true + validates :ci_config_path, + format: { without: /\.{2}/, + message: 'cannot include directory traversal.' }, + length: { maximum: 255 }, + allow_blank: true validates :name, presence: true, length: { maximum: 255 }, @@ -219,7 +237,7 @@ class Project < ActiveRecord::Base before_save :ensure_runners_token mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Scopes scope :pending_delete, -> { where(pending_delete: true) } @@ -468,7 +486,9 @@ class Project < ActiveRecord::Base end def has_container_registry_tags? - container_repositories.to_a.any?(&:has_tags?) || + return @images if defined?(@images) + + @images = container_repositories.to_a.any?(&:has_tags?) || has_root_container_repository_tags? end @@ -518,9 +538,19 @@ class Project < ActiveRecord::Base ProjectCacheWorker.perform_async(self.id) end + remove_import_data + end + + # This method is overriden in EE::Project model + def remove_import_data import_data&.destroy end + def ci_config_path=(value) + # Strip all leading slashes so that //foo -> foo + super(value&.sub(%r{\A/+}, '')&.delete("\0")) + end + def import_url=(value) return super(value) unless Gitlab::UrlSanitizer.valid?(value) @@ -675,7 +705,7 @@ class Project < ActiveRecord::Base end def web_url - Gitlab::Routing.url_helpers.namespace_project_url(self.namespace, self) + Gitlab::Routing.url_helpers.project_url(self) end def new_issue_address(author) @@ -773,10 +803,12 @@ class Project < ActiveRecord::Base update_column(:has_external_wiki, services.external_wikis.any?) end - def find_or_initialize_services + def find_or_initialize_services(exceptions: []) services_templates = Service.where(template: true) - Service.available_services_names.map do |service_name| + available_services_names = Service.available_services_names - exceptions + + available_services_names.map do |service_name| service = find_service(services, service_name) if service @@ -815,7 +847,7 @@ class Project < ActiveRecord::Base end def ci_service - @ci_service ||= ci_services.find_by(active: true) + @ci_service ||= ci_services.reorder(nil).find_by(active: true) end def deployment_services @@ -823,7 +855,7 @@ class Project < ActiveRecord::Base end def deployment_service - @deployment_service ||= deployment_services.find_by(active: true) + @deployment_service ||= deployment_services.reorder(nil).find_by(active: true) end def monitoring_services @@ -831,7 +863,7 @@ class Project < ActiveRecord::Base end def monitoring_service - @monitoring_service ||= monitoring_services.find_by(active: true) + @monitoring_service ||= monitoring_services.reorder(nil).find_by(active: true) end def jira_tracker? @@ -851,7 +883,7 @@ class Project < ActiveRecord::Base def avatar_url(**args) # We use avatar_path instead of overriding avatar_url because of carrierwave. # See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11001/diffs#note_28659864 - avatar_path(args) || (Gitlab::Routing.url_helpers.namespace_project_avatar_url(namespace, self) if avatar_in_git) + avatar_path(args) || (Gitlab::Routing.url_helpers.project_avatar_url(self) if avatar_in_git) end # For compatibility with old code @@ -947,8 +979,6 @@ class Project < ActiveRecord::Base Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}" - expire_caches_before_rename(old_path_with_namespace) - if has_container_registry_tags? Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!" @@ -956,6 +986,8 @@ class Project < ActiveRecord::Base raise StandardError.new('Project cannot be renamed, because images are present in its container registry') end + expire_caches_before_rename(old_path_with_namespace) + if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace) # If repository moved successfully we need to send update instructions to users. # However we cannot allow rollback since we moved repository @@ -1015,7 +1047,8 @@ class Project < ActiveRecord::Base namespace: namespace.name, visibility_level: visibility_level, path_with_namespace: path_with_namespace, - default_branch: default_branch + default_branch: default_branch, + ci_config_path: ci_config_path } # Backward compatibility @@ -1229,7 +1262,13 @@ class Project < ActiveRecord::Base File.join(pages_path, 'public') end + def remove_private_deploy_keys + deploy_keys.where(public: false).delete_all + end + def remove_pages + ::Projects::UpdatePagesConfigurationService.new(self).execute + # 1. We rename pages to temporary directory # 2. We wait 5 minutes, due to NFS caching # 3. We asynchronously remove pages with force @@ -1315,7 +1354,8 @@ class Project < ActiveRecord::Base variables end - def secret_variables_for(ref) + def secret_variables_for(ref:, environment: nil) + # EE would use the environment if protected_for?(ref) variables else @@ -1348,15 +1388,15 @@ class Project < ActiveRecord::Base end def pushes_since_gc - Gitlab::Redis.with { |redis| redis.get(pushes_since_gc_redis_key).to_i } + Gitlab::Redis::SharedState.with { |redis| redis.get(pushes_since_gc_redis_shared_state_key).to_i } end def increment_pushes_since_gc - Gitlab::Redis.with { |redis| redis.incr(pushes_since_gc_redis_key) } + Gitlab::Redis::SharedState.with { |redis| redis.incr(pushes_since_gc_redis_shared_state_key) } end def reset_pushes_since_gc - Gitlab::Redis.with { |redis| redis.del(pushes_since_gc_redis_key) } + Gitlab::Redis::SharedState.with { |redis| redis.del(pushes_since_gc_redis_shared_state_key) } end def route_map_for(commit_sha) @@ -1419,7 +1459,7 @@ class Project < ActiveRecord::Base from && self != from end - def pushes_since_gc_redis_key + def pushes_since_gc_redis_shared_state_key "projects/#{id}/pushes_since_gc" end diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index e3cafd4d1c6..37730474324 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -10,7 +10,7 @@ class ProjectImportData < ActiveRecord::Base insecure_mode: true, algorithm: 'aes-256-cbc' - serialize :data, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :data, JSON # rubocop:disable Cop/ActiveRecordSerialize validates :project, presence: true diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index ad4eb9536e1..420102875a5 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -1,5 +1,5 @@ class GitlabIssueTrackerService < IssueTrackerService - include Gitlab::Routing.url_helpers + include Gitlab::Routing validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated? @@ -12,26 +12,26 @@ class GitlabIssueTrackerService < IssueTrackerService end def project_url - namespace_project_issues_url(project.namespace, project) + project_issues_url(project) end def new_issue_url - new_namespace_project_issue_url(namespace_id: project.namespace, project_id: project) + new_project_issue_url(project) end def issue_url(iid) - namespace_project_issue_url(namespace_id: project.namespace, project_id: project, id: iid) + project_issue_url(project, id: iid) end def project_path - namespace_project_issues_path(project.namespace, project) + project_issues_path(project) end def new_issue_path - new_namespace_project_issue_path(namespace_id: project.namespace, project_id: project) + new_project_issue_path(project) end def issue_path(iid) - namespace_project_issue_path(namespace_id: project.namespace, project_id: project, id: iid) + project_issue_path(project, id: iid) end end diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index fcc7c4bec06..1fa4cd4db30 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -21,7 +21,7 @@ class IssueTrackerService < Service end def project_path - project_url + read_attribute(:project_url) end def new_issue_path diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 00328892b4a..5498a2e17b2 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -1,5 +1,5 @@ class JiraService < IssueTrackerService - include Gitlab::Routing.url_helpers + include Gitlab::Routing validates :url, url: true, presence: true, if: :activated? validates :api_url, url: true, allow_blank: true @@ -152,8 +152,8 @@ class JiraService < IssueTrackerService url: resource_url(user_path(author)) }, project: { - name: self.project.path_with_namespace, - url: resource_url(namespace_project_path(project.namespace, self.project)) + name: project.path_with_namespace, + url: resource_url(namespace_project_path(project.namespace, project)) # rubocop:disable Cop/ProjectPathHelper }, entity: { name: noteable_type.humanize.downcase, diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 48e7802c557..62f7c057c5b 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -96,10 +96,13 @@ class KubernetesService < DeploymentService end def predefined_variables + config = YAML.dump(kubeconfig) + variables = [ { key: 'KUBE_URL', value: api_url, public: true }, { key: 'KUBE_TOKEN', value: token, public: false }, - { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true } + { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true }, + { key: 'KUBECONFIG', value: config, public: false, file: true } ] if ca_pem.present? @@ -135,6 +138,14 @@ class KubernetesService < DeploymentService private + def kubeconfig + to_kubeconfig( + url: api_url, + namespace: actual_namespace, + token: token, + ca_pem: ca_pem) + end + def namespace_placeholder default_namespace || TEMPLATE_PLACEHOLDER end diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb index 4592cb747a0..eb4da68bb7e 100644 --- a/app/models/project_services/slash_commands_service.rb +++ b/app/models/project_services/slash_commands_service.rb @@ -5,7 +5,7 @@ class SlashCommandsService < Service prop_accessor :token - has_many :chat_names, foreign_key: :service_id, dependent: :destroy + has_many :chat_names, foreign_key: :service_id, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent def valid_token?(token) self.respond_to?(:token) && diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index f26ee57510c..dfca0031af8 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -31,7 +31,7 @@ class ProjectWiki end def web_url - Gitlab::Routing.url_helpers.namespace_project_wiki_url(@project.namespace, @project, :home) + Gitlab::Routing.url_helpers.project_wiki_url(@project, :home) end def url_to_repo @@ -63,6 +63,10 @@ class ProjectWiki !!repository.exists? end + def has_home_page? + !!find_page('home') + end + # Returns an Array of Gitlab WikiPage instances or an # empty Array if this Wiki has no pages. def pages diff --git a/app/models/repository.rb b/app/models/repository.rb index 8c24e722a8b..8663cf5e602 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -123,6 +123,7 @@ class Repository commits end + # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/384 def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0) unless exists? && has_visible_content? && query.present? return [] @@ -610,6 +611,7 @@ class Repository commit(sha) end + # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/383 def last_commit_id_for_path(sha, path) key = path.blank? ? "last_commit_id_for_path:#{sha}" : "last_commit_id_for_path:#{sha}:#{Digest::SHA1.hexdigest(path)}" @@ -931,7 +933,7 @@ class Repository def is_ancestor?(ancestor_id, descendant_id) return false if ancestor_id.nil? || descendant_id.nil? - + Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| if is_enabled raw_repository.is_ancestor?(ancestor_id, descendant_id) @@ -1078,8 +1080,8 @@ class Repository blob_data_at(sha, '.gitlab/route-map.yml') end - def gitlab_ci_yml_for(sha) - blob_data_at(sha, '.gitlab-ci.yml') + def gitlab_ci_yml_for(sha, path = '.gitlab-ci.yml') + blob_data_at(sha, path) end private diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index edde7bedbab..298569cb7a6 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -1,5 +1,5 @@ class SentNotification < ActiveRecord::Base - serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize + serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize belongs_to :project belongs_to :noteable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/service.rb b/app/models/service.rb index 6a0b0a5c522..6b64079215f 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -2,7 +2,7 @@ # and implement a set of methods class Service < ActiveRecord::Base include Sortable - serialize :properties, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize default_value_for :active, false default_value_for :push_events, true @@ -51,6 +51,14 @@ class Service < ActiveRecord::Base active end + def show_active_box? + true + end + + def editable? + true + end + def template? template end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 54014df43b0..09d5ff46618 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -30,16 +30,14 @@ class Snippet < ActiveRecord::Base belongs_to :author, class_name: 'User' belongs_to :project - has_many :notes, as: :noteable, dependent: :destroy + has_many :notes, as: :noteable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent delegate :name, :email, to: :author, prefix: true, allow_nil: true validates :author, presence: true validates :title, presence: true, length: { maximum: 255 } validates :file_name, - length: { maximum: 255 }, - format: { with: Gitlab::Regex.file_name_regex, - message: Gitlab::Regex.file_name_regex_message } + length: { maximum: 255 } validates :content, presence: true validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } diff --git a/app/models/user.rb b/app/models/user.rb index 0febae84873..8f40af24e20 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -12,6 +12,7 @@ class User < ActiveRecord::Base include TokenAuthenticatable include IgnorableColumn include FeatureGate + include CreatedAtFilterable DEFAULT_NOTIFICATION_LEVEL = :participating @@ -41,7 +42,7 @@ class User < ActiveRecord::Base otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base devise :two_factor_backupable, otp_number_of_backup_codes: 10 - serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiveRecordSerialize devise :lockable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :confirmable, :registerable @@ -67,24 +68,24 @@ class User < ActiveRecord::Base # # Namespace for personal projects - has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, autosave: true + has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, autosave: true # rubocop:disable Cop/ActiveRecordDependent # Profile has_many :keys, -> do type = Key.arel_table[:type] where(type.not_eq('DeployKey').or(type.eq(nil))) - end, dependent: :destroy - has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy + end, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :emails, dependent: :destroy - has_many :personal_access_tokens, dependent: :destroy - has_many :identities, dependent: :destroy, autosave: true - has_many :u2f_registrations, dependent: :destroy - has_many :chat_names, dependent: :destroy + has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :personal_access_tokens, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :identities, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent + has_many :u2f_registrations, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :chat_names, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Groups - has_many :members, dependent: :destroy - has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' + has_many :members, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent has_many :groups, through: :group_members has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group @@ -92,35 +93,35 @@ class User < ActiveRecord::Base # Projects has_many :groups_projects, through: :groups, source: :projects has_many :personal_projects, through: :namespace, source: :projects - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy + has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :project_members has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' - has_many :users_star_projects, dependent: :destroy + has_many :users_star_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :starred_projects, through: :users_star_projects, source: :project has_many :project_authorizations has_many :authorized_projects, through: :project_authorizations, source: :project - has_many :snippets, dependent: :destroy, foreign_key: :author_id - has_many :notes, dependent: :destroy, foreign_key: :author_id - has_many :issues, dependent: :destroy, foreign_key: :author_id - has_many :merge_requests, dependent: :destroy, foreign_key: :author_id - has_many :events, dependent: :destroy, foreign_key: :author_id - has_many :subscriptions, dependent: :destroy + has_many :snippets, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :notes, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :issues, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :merge_requests, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :events, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :subscriptions, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event" - has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy - has_one :abuse_report, dependent: :destroy, foreign_key: :user_id - has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" - has_many :spam_logs, dependent: :destroy - has_many :builds, dependent: :nullify, class_name: 'Ci::Build' - has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline' - has_many :todos, dependent: :destroy - has_many :notification_settings, dependent: :destroy - has_many :award_emoji, dependent: :destroy - has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id + has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_one :abuse_report, dependent: :destroy, foreign_key: :user_id # rubocop:disable Cop/ActiveRecordDependent + has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" # rubocop:disable Cop/ActiveRecordDependent + has_many :spam_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :builds, dependent: :nullify, class_name: 'Ci::Build' # rubocop:disable Cop/ActiveRecordDependent + has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline' # rubocop:disable Cop/ActiveRecordDependent + has_many :todos, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :notification_settings, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :award_emoji, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id # rubocop:disable Cop/ActiveRecordDependent has_many :issue_assignees has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue - has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" + has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" # rubocop:disable Cop/ActiveRecordDependent # # Validations @@ -211,7 +212,7 @@ class User < ActiveRecord::Base end mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Scopes scope :admins, -> { where(admin: true) } @@ -313,7 +314,7 @@ class User < ActiveRecord::Base table[:name].matches(pattern) .or(table[:email].matches(pattern)) .or(table[:username].matches(pattern)) - ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, id: :desc) + ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name) end # searches user by given pattern @@ -579,16 +580,20 @@ class User < ActiveRecord::Base keys.count == 0 && Gitlab::ProtocolAccess.allowed?('ssh') end - def require_password? - password_automatically_set? && !ldap_user? && current_application_settings.signin_enabled? + def require_password_creation? + password_automatically_set? && allow_password_authentication? end - def require_personal_access_token? - return false if current_application_settings.signin_enabled? || ldap_user? + def require_personal_access_token_creation_for_git_auth? + return false if allow_password_authentication? || ldap_user? PersonalAccessTokensFinder.new(user: self, impersonation: false, state: 'active').execute.none? end + def allow_password_authentication? + !ldap_user? && current_application_settings.password_authentication_enabled? + end + def can_change_username? gitlab_config.username_changing_enabled end @@ -698,7 +703,7 @@ class User < ActiveRecord::Base end def sanitize_attrs - %w[name username skype linkedin twitter].each do |attr| + %w[username skype linkedin twitter].each do |attr| value = public_send(attr) public_send("#{attr}=", Sanitize.clean(value)) if value.present? end diff --git a/app/policies/ci/pipeline_schedule_policy.rb b/app/policies/ci/pipeline_schedule_policy.rb index 1877e89bb23..6b7598e1821 100644 --- a/app/policies/ci/pipeline_schedule_policy.rb +++ b/app/policies/ci/pipeline_schedule_policy.rb @@ -1,4 +1,14 @@ module Ci class PipelineSchedulePolicy < PipelinePolicy + alias_method :pipeline_schedule, :subject + + condition(:owner_of_schedule) do + can?(:developer_access) && pipeline_schedule.owned_by?(@user) + end + + rule { can?(:master_access) | owner_of_schedule }.policy do + enable :update_pipeline_schedule + enable :admin_pipeline_schedule + end end end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index dcb37416ca3..6defab75fce 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -31,6 +31,8 @@ class GroupPolicy < BasePolicy rule { master }.policy do enable :create_projects enable :admin_milestones + enable :admin_pipeline + enable :admin_build end rule { owner }.policy do diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 7cbca63fab4..323131c0f7e 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -162,7 +162,6 @@ class ProjectPolicy < BasePolicy enable :create_pipeline enable :update_pipeline enable :create_pipeline_schedule - enable :update_pipeline_schedule enable :create_merge_request enable :create_wiki enable :push_code @@ -188,7 +187,6 @@ class ProjectPolicy < BasePolicy enable :admin_build enable :admin_container_image enable :admin_pipeline - enable :admin_pipeline_schedule enable :admin_environment enable :admin_deployment enable :admin_pages diff --git a/app/presenters/ci/group_variable_presenter.rb b/app/presenters/ci/group_variable_presenter.rb new file mode 100644 index 00000000000..81fea106a5c --- /dev/null +++ b/app/presenters/ci/group_variable_presenter.rb @@ -0,0 +1,25 @@ +module Ci + class GroupVariablePresenter < Gitlab::View::Presenter::Delegated + presents :variable + + def placeholder + 'GROUP_VARIABLE' + end + + def form_path + if variable.persisted? + group_variable_path(group, variable) + else + group_variables_path(group) + end + end + + def edit_path + group_variable_path(group, variable) + end + + def delete_path + group_variable_path(group, variable) + end + end +end diff --git a/app/presenters/ci/variable_presenter.rb b/app/presenters/ci/variable_presenter.rb new file mode 100644 index 00000000000..5d7998393a6 --- /dev/null +++ b/app/presenters/ci/variable_presenter.rb @@ -0,0 +1,25 @@ +module Ci + class VariablePresenter < Gitlab::View::Presenter::Delegated + presents :variable + + def placeholder + 'PROJECT_VARIABLE' + end + + def form_path + if variable.persisted? + project_variable_path(project, variable) + else + project_variables_path(project) + end + end + + def edit_path + project_variable_path(project, variable) + end + + def delete_path + project_variable_path(project, variable) + end + end +end diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index 8bf35953d29..2df84e58575 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -20,30 +20,25 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated def cancel_merge_when_pipeline_succeeds_path if can_cancel_merge_when_pipeline_succeeds?(current_user) - cancel_merge_when_pipeline_succeeds_namespace_project_merge_request_path( - project.namespace, - project, - merge_request) + cancel_merge_when_pipeline_succeeds_project_merge_request_path(project, merge_request) end end def create_issue_to_resolve_discussions_path if can?(current_user, :create_issue, project) && project.issues_enabled? - new_namespace_project_issue_path(project.namespace, - project, - merge_request_to_resolve_discussions_of: iid) + new_project_issue_path(project, merge_request_to_resolve_discussions_of: iid) end end def remove_wip_path if can?(current_user, :update_merge_request, merge_request.project) - remove_wip_namespace_project_merge_request_path(project.namespace, project, merge_request) + remove_wip_project_merge_request_path(project, merge_request) end end def merge_path if can_be_merged_by?(current_user) - merge_namespace_project_merge_request_path(project.namespace, project, merge_request) + merge_project_merge_request_path(project, merge_request) end end @@ -55,7 +50,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated notice_now: edit_in_new_fork_notice_now } - namespace_project_forks_path(merge_request.project.namespace, merge_request.project, + project_forks_path(merge_request.project, namespace_key: current_user.namespace.id, continue: continue_params) end @@ -69,7 +64,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated notice_now: edit_in_new_fork_notice_now } - namespace_project_forks_path(project.namespace, project, + project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) end @@ -77,19 +72,25 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated def conflict_resolution_path if conflicts.can_be_resolved_in_ui? && conflicts.can_be_resolved_by?(current_user) - conflicts_namespace_project_merge_request_path(project.namespace, project, merge_request) + conflicts_project_merge_request_path(project, merge_request) + end + end + + def target_branch_tree_path + if target_branch_exists? + project_tree_path(project, target_branch) end end def target_branch_commits_path if target_branch_exists? - namespace_project_commits_path(project.namespace, project, target_branch) + project_commits_path(project, target_branch) end end def source_branch_path if source_branch_exists? - namespace_project_branch_path(source_project.namespace, source_project, source_branch) + project_branch_path(source_project, source_branch) end end @@ -99,7 +100,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated if source_branch_exists? namespace = link_to(namespace, project_path(source_project)) - branch = link_to(branch, namespace_project_commits_path(source_project.namespace, source_project, source_branch)) + branch = link_to(branch, project_tree_path(source_project, source_branch)) end if for_fork? @@ -136,7 +137,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated merge_request: merge_request, closes_issues: closing_issues ).assignable_issues - path = assign_related_issues_namespace_project_merge_request_path(project.namespace, project, merge_request) + path = assign_related_issues_project_merge_request_path(project, merge_request) if issues.present? pluralize_this_issue = issues.count > 1 ? "these issues" : "this issue" link_to "Assign yourself to #{pluralize_this_issue}", path, method: :post diff --git a/app/serializers/build_action_entity.rb b/app/serializers/build_action_entity.rb index 301b718d060..f2d76a8ad81 100644 --- a/app/serializers/build_action_entity.rb +++ b/app/serializers/build_action_entity.rb @@ -6,10 +6,7 @@ class BuildActionEntity < Grape::Entity end expose :path do |build| - play_namespace_project_job_path( - build.project.namespace, - build.project, - build) + play_project_job_path(build.project, build) end expose :playable?, as: :playable diff --git a/app/serializers/build_artifact_entity.rb b/app/serializers/build_artifact_entity.rb index cb55c98f7c6..6e0e33bc09b 100644 --- a/app/serializers/build_artifact_entity.rb +++ b/app/serializers/build_artifact_entity.rb @@ -9,24 +9,15 @@ class BuildArtifactEntity < Grape::Entity expose :artifacts_expire_at, as: :expire_at expose :path do |job| - download_namespace_project_job_artifacts_path( - project.namespace, - project, - job) + download_project_job_artifacts_path(project, job) end expose :keep_path, if: -> (*) { job.has_expiring_artifacts? } do |job| - keep_namespace_project_job_artifacts_path( - project.namespace, - project, - job) + keep_project_job_artifacts_path(project, job) end expose :browse_path do |job| - browse_namespace_project_job_artifacts_path( - project.namespace, - project, - job) + browse_project_job_artifacts_path(project, job) end private diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index eeb5399aa8b..20f9938f038 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -7,7 +7,7 @@ class BuildDetailsEntity < JobEntity expose :erased_by, if: -> (*) { build.erased? }, using: UserEntity expose :erase_path, if: -> (*) { build.erasable? && can?(current_user, :update_build, project) } do |build| - erase_namespace_project_job_path(project.namespace, project, build) + erase_project_job_path(project, build) end expose :merge_request, if: -> (*) { can?(current_user, :read_merge_request, build.merge_request) } do @@ -16,23 +16,23 @@ class BuildDetailsEntity < JobEntity end expose :path do |build| - namespace_project_merge_request_path(project.namespace, project, build.merge_request) + project_merge_request_path(project, build.merge_request) end end expose :new_issue_path, if: -> (*) { can?(request.current_user, :create_issue, project) && build.failed? } do |build| - new_namespace_project_issue_path(project.namespace, project, issue: build_failed_issue_options) + new_project_issue_path(project, issue: build_failed_issue_options) end expose :raw_path do |build| - raw_namespace_project_job_path(project.namespace, project, build) + raw_project_job_path(project, build) end private def build_failed_issue_options { title: "Build Failed ##{build.id}", - description: namespace_project_job_path(project.namespace, project, build) } + description: project_job_path(project, build) } end def current_user diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb index 31763955f97..e4e9d8ef90a 100644 --- a/app/serializers/commit_entity.rb +++ b/app/serializers/commit_entity.rb @@ -8,16 +8,10 @@ class CommitEntity < API::Entities::RepoCommit end expose :commit_url do |commit| - namespace_project_commit_url( - request.project.namespace, - request.project, - commit) + project_commit_url(request.project, commit) end expose :commit_path do |commit| - namespace_project_commit_path( - request.project.namespace, - request.project, - commit) + project_commit_path(request.project, commit) end end diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index e493c9162fd..241c689bccd 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -11,10 +11,7 @@ class DeploymentEntity < Grape::Entity end expose :ref_path do |deployment| - namespace_project_tree_path( - deployment.project.namespace, - deployment.project, - id: deployment.ref) + project_tree_path(deployment.project, id: deployment.ref) end end diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb index 4e8a3c67b21..dcaccc3007d 100644 --- a/app/serializers/environment_entity.rb +++ b/app/serializers/environment_entity.rb @@ -10,32 +10,20 @@ class EnvironmentEntity < Grape::Entity expose :stop_action? expose :metrics_path, if: -> (environment, _) { environment.has_metrics? } do |environment| - metrics_namespace_project_environment_path( - environment.project.namespace, - environment.project, - environment) + metrics_project_environment_path(environment.project, environment) end expose :environment_path do |environment| - namespace_project_environment_path( - environment.project.namespace, - environment.project, - environment) + project_environment_path(environment.project, environment) end expose :stop_path do |environment| - stop_namespace_project_environment_path( - environment.project.namespace, - environment.project, - environment) + stop_project_environment_path(environment.project, environment) end expose :terminal_path, if: ->(environment, _) { environment.has_terminals? } do |environment| can?(request.current_user, :admin_environment, environment.project) && - terminal_namespace_project_environment_path( - environment.project.namespace, - environment.project, - environment) + terminal_project_environment_path(environment.project, environment) end expose :created_at, :updated_at diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index 35df95549b7..c189a4992da 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -11,6 +11,6 @@ class IssueEntity < IssuableEntity expose :labels, using: LabelEntity expose :web_url do |issue| - namespace_project_issue_path(issue.project.namespace, issue.project, issue) + project_issue_path(issue.project, issue) end end diff --git a/app/serializers/label_entity.rb b/app/serializers/label_entity.rb index ad565654342..4452161051e 100644 --- a/app/serializers/label_entity.rb +++ b/app/serializers/label_entity.rb @@ -1,5 +1,6 @@ class LabelEntity < Grape::Entity - expose :id + expose :id, if: ->(label, _) { !label.is_a?(GlobalLabel) } + expose :title expose :color expose :description diff --git a/app/serializers/merge_request_entity.rb b/app/serializers/merge_request_entity.rb index 7bb981041cc..7f17f2bf604 100644 --- a/app/serializers/merge_request_entity.rb +++ b/app/serializers/merge_request_entity.rb @@ -97,11 +97,13 @@ class MergeRequestEntity < IssuableEntity presenter(merge_request).target_branch_commits_path end + expose :target_branch_tree_path do |merge_request| + presenter(merge_request).target_branch_tree_path + end + expose :new_blob_path do |merge_request| if can?(current_user, :push_code, merge_request.project) - namespace_project_new_blob_path(merge_request.project.namespace, - merge_request.project, - merge_request.source_branch) + project_new_blob_path(merge_request.project, merge_request.source_branch) end end @@ -134,30 +136,19 @@ class MergeRequestEntity < IssuableEntity end expose :email_patches_path do |merge_request| - namespace_project_merge_request_path(merge_request.project.namespace, - merge_request.project, - merge_request, - format: :patch) + project_merge_request_path(merge_request.project, merge_request, format: :patch) end expose :plain_diff_path do |merge_request| - namespace_project_merge_request_path(merge_request.project.namespace, - merge_request.project, - merge_request, - format: :diff) + project_merge_request_path(merge_request.project, merge_request, format: :diff) end expose :status_path do |merge_request| - namespace_project_merge_request_path(merge_request.target_project.namespace, - merge_request.target_project, - merge_request, - format: :json) + project_merge_request_path(merge_request.target_project, merge_request, format: :json) end expose :ci_environments_status_path do |merge_request| - ci_environments_status_namespace_project_merge_request_path(merge_request.project.namespace, - merge_request.project, - merge_request) + ci_environments_status_project_merge_request_path(merge_request.project, merge_request) end expose :merge_commit_message_with_description do |merge_request| @@ -173,9 +164,7 @@ class MergeRequestEntity < IssuableEntity end expose :commit_change_content_path do |merge_request| - commit_change_content_namespace_project_merge_request_path(merge_request.project.namespace, - merge_request.project, - merge_request) + commit_change_content_project_merge_request_path(merge_request.project, merge_request) end private diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb index 6d1fd9d459f..c4f000b0ca3 100644 --- a/app/serializers/pipeline_entity.rb +++ b/app/serializers/pipeline_entity.rb @@ -10,10 +10,7 @@ class PipelineEntity < Grape::Entity expose :created_at, :updated_at expose :path do |pipeline| - namespace_project_pipeline_path( - pipeline.project.namespace, - pipeline.project, - pipeline) + project_pipeline_path(pipeline.project, pipeline) end expose :flags do @@ -48,15 +45,11 @@ class PipelineEntity < Grape::Entity expose :commit, using: CommitEntity expose :retry_path, if: -> (*) { can_retry? } do |pipeline| - retry_namespace_project_pipeline_path(pipeline.project.namespace, - pipeline.project, - pipeline.id) + retry_project_pipeline_path(pipeline.project, pipeline) end expose :cancel_path, if: -> (*) { can_cancel? } do |pipeline| - cancel_namespace_project_pipeline_path(pipeline.project.namespace, - pipeline.project, - pipeline.id) + cancel_project_pipeline_path(pipeline.project, pipeline) end expose :yaml_errors, if: -> (pipeline, _) { pipeline.has_yaml_errors? } diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb index a471a7e6a88..dc283ba3e7a 100644 --- a/app/serializers/project_entity.rb +++ b/app/serializers/project_entity.rb @@ -5,7 +5,7 @@ class ProjectEntity < Grape::Entity expose :name expose :full_path do |project| - namespace_project_path(project.namespace, project) + project_path(project) end expose :full_name do |project| diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb index ed7dacc2dbd..e9999a36d8a 100644 --- a/app/serializers/runner_entity.rb +++ b/app/serializers/runner_entity.rb @@ -5,7 +5,7 @@ class RunnerEntity < Grape::Entity expose :edit_path, if: -> (*) { can?(request.current_user, :admin_build, project) && runner.specific? } do |runner| - edit_namespace_project_runner_path(project.namespace, project, runner) + edit_project_runner_path(project, runner) end private diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb index cee0089056f..4523b15152e 100644 --- a/app/serializers/stage_entity.rb +++ b/app/serializers/stage_entity.rb @@ -14,16 +14,14 @@ class StageEntity < Grape::Entity expose :detailed_status, as: :status, with: StatusEntity expose :path do |stage| - namespace_project_pipeline_path( - stage.pipeline.project.namespace, + project_pipeline_path( stage.pipeline.project, stage.pipeline, anchor: stage.name) end expose :dropdown_path do |stage| - stage_namespace_project_pipeline_path( - stage.pipeline.project.namespace, + stage_project_pipeline_path( stage.pipeline.project, stage.pipeline, stage: stage.name, diff --git a/app/services/access_token_validation_service.rb b/app/services/access_token_validation_service.rb index b2a543daa00..9c00ea789ec 100644 --- a/app/services/access_token_validation_service.rb +++ b/app/services/access_token_validation_service.rb @@ -5,10 +5,11 @@ class AccessTokenValidationService REVOKED = :revoked INSUFFICIENT_SCOPE = :insufficient_scope - attr_reader :token + attr_reader :token, :request - def initialize(token) + def initialize(token, request: nil) @token = token + @request = request end def validate(scopes: []) @@ -27,12 +28,23 @@ class AccessTokenValidationService end # True if the token's scope contains any of the passed scopes. - def include_any_scope?(scopes) - if scopes.blank? + def include_any_scope?(required_scopes) + if required_scopes.blank? true else - # Check whether the token is allowed access to any of the required scopes. - Set.new(scopes).intersection(Set.new(token.scopes)).present? + # We're comparing each required_scope against all token scopes, which would + # take quadratic time. This consideration is irrelevant here because of the + # small number of records involved. + # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12300/#note_33689006 + token_scopes = token.scopes.map(&:to_sym) + + required_scopes.any? do |scope| + if scope.respond_to?(:sufficient?) + scope.sufficient?(token_scopes, request) + else + API::Scope.new(scope).sufficient?(token_scopes, request) + end + end end end end diff --git a/app/services/boards/create_service.rb b/app/services/boards/create_service.rb index 68f6a8619e5..9eedb9e65a2 100644 --- a/app/services/boards/create_service.rb +++ b/app/services/boards/create_service.rb @@ -1,19 +1,22 @@ module Boards class CreateService < BaseService def execute - if project.boards.empty? - create_board! - else - project.boards.first - end + create_board! if can_create_board? end private + def can_create_board? + project.boards.size == 0 + end + def create_board! - board = project.boards.create - board.lists.create(list_type: :backlog) - board.lists.create(list_type: :closed) + board = project.boards.create(params) + + if board.persisted? + board.lists.create(list_type: :backlog) + board.lists.create(list_type: :closed) + end board end diff --git a/app/services/chat_names/authorize_user_service.rb b/app/services/chat_names/authorize_user_service.rb index 321bf3a9205..7256466c9e8 100644 --- a/app/services/chat_names/authorize_user_service.rb +++ b/app/services/chat_names/authorize_user_service.rb @@ -1,6 +1,6 @@ module ChatNames class AuthorizeUserService - include Gitlab::Routing.url_helpers + include Gitlab::Routing def initialize(service, params) @service = service diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index a8034e30a85..8e2184a1f19 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -37,7 +37,7 @@ module Ci unless pipeline.config_processor unless pipeline.ci_yaml_file - return error('Missing .gitlab-ci.yml file') + return error("Missing #{pipeline.ci_yaml_file_path} file") end return error(pipeline.yaml_errors, save: save_on_errors) end diff --git a/app/services/git_operation_service.rb b/app/services/git_operation_service.rb index 43636fde0be..32925e9c1f2 100644 --- a/app/services/git_operation_service.rb +++ b/app/services/git_operation_service.rb @@ -129,6 +129,7 @@ class GitOperationService end end + # Gitaly note: JV: wait with migrating #update_ref until we know how to migrate its call sites. def update_ref(ref, newrev, oldrev) # We use 'git update-ref' because libgit2/rugged currently does not # offer 'compare and swap' ref updates. Without compare-and-swap we can diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 8dd0846f3bc..a03a7abfeb1 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -2,8 +2,11 @@ class IssuableBaseService < BaseService private def create_milestone_note(issuable) + milestone = issuable.milestone + return if milestone && milestone.is_group_milestone? + SystemNoteService.change_milestone( - issuable, issuable.project, current_user, issuable.milestone) + issuable, issuable.project, current_user, milestone) end def create_labels_note(issuable, old_labels) @@ -89,10 +92,12 @@ class IssuableBaseService < BaseService milestone_id = params[:milestone_id] return unless milestone_id - if milestone_id == IssuableFinder::NONE || - project.milestones.find_by(id: milestone_id).nil? - params[:milestone_id] = '' - end + params[:milestone_id] = '' if milestone_id == IssuableFinder::NONE + + milestone = + Milestone.for_projects_and_groups([project.id], [project.group&.id]).find_by_id(milestone_id) + + params[:milestone_id] = '' unless milestone end def filter_labels diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index 711f4035c55..29def25719d 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -61,8 +61,18 @@ module Issues end def cloneable_milestone_id - @new_project.milestones - .find_by(title: @old_issue.milestone.try(:title)).try(: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 diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb index 71d37797bb4..19189e64acf 100644 --- a/app/services/merge_requests/create_service.rb +++ b/app/services/merge_requests/create_service.rb @@ -7,9 +7,8 @@ module MergeRequests source_project = @project @project = Project.find(params[:target_project_id]) if params[:target_project_id] - params[:target_project_id] ||= source_project.id - merge_request = MergeRequest.new + merge_request.target_project = @project merge_request.source_project = source_project merge_request.source_branch = params[:source_branch] merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) diff --git a/app/services/merge_requests/get_urls_service.rb b/app/services/merge_requests/get_urls_service.rb index 5dd40e07c0d..668a1741736 100644 --- a/app/services/merge_requests/get_urls_service.rb +++ b/app/services/merge_requests/get_urls_service.rb @@ -49,13 +49,13 @@ module MergeRequests def url_for_new_merge_request(branch_name) merge_request_params = { source_branch: branch_name } - url = Gitlab::Routing.url_helpers.namespace_project_new_merge_request_url(project.namespace, project, merge_request: merge_request_params) + url = Gitlab::Routing.url_helpers.project_new_merge_request_url(project, merge_request: merge_request_params) { branch_name: branch_name, url: url, new_merge_request: true } end def url_for_existing_merge_request(merge_request) target_project = merge_request.target_project - url = Gitlab::Routing.url_helpers.namespace_project_merge_request_url(target_project.namespace, target_project, merge_request) + url = Gitlab::Routing.url_helpers.project_merge_request_url(target_project, merge_request) { branch_name: merge_request.source_branch, url: url, new_merge_request: false } end end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index e0e7c43f802..bc4a13cf4bc 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -35,11 +35,12 @@ module MergeRequests # target branch manually def close_merge_requests commit_ids = @commits.map(&:id) - merge_requests = @project.merge_requests.opened.where(target_branch: @branch_name).to_a + merge_requests = @project.merge_requests.preload(:merge_request_diff).opened.where(target_branch: @branch_name).to_a merge_requests = merge_requests.select(&:diff_head_commit) merge_requests = merge_requests.select do |merge_request| - commit_ids.include?(merge_request.diff_head_sha) + commit_ids.include?(merge_request.diff_head_sha) && + merge_request.merge_request_diff.state != 'empty' end filter_merge_requests(merge_requests).each do |merge_request| @@ -68,7 +69,7 @@ module MergeRequests if merge_request.source_branch == @branch_name || force_push? merge_request.reload_diff(current_user) else - mr_commit_ids = merge_request.commits_sha + mr_commit_ids = merge_request.commit_shas push_commit_ids = @commits.map(&:id) matches = mr_commit_ids & push_commit_ids merge_request.reload_diff(current_user) if matches.any? @@ -128,7 +129,7 @@ module MergeRequests return unless @commits.present? merge_requests_for_source_branch.each do |merge_request| - mr_commit_ids = Set.new(merge_request.commits_sha) + mr_commit_ids = Set.new(merge_request.commit_shas) new_commits, existing_commits = @commits.partition do |commit| mr_commit_ids.include?(commit.id) @@ -144,7 +145,7 @@ module MergeRequests return unless @commits.present? merge_requests_for_source_branch.each do |merge_request| - commit_shas = merge_request.commits_sha + commit_shas = merge_request.commit_shas wip_commit = @commits.detect do |commit| commit.work_in_progress? && commit_shas.include?(commit.sha) diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb index d726db4e99b..c92f070601c 100644 --- a/app/services/metrics_service.rb +++ b/app/services/metrics_service.rb @@ -3,7 +3,10 @@ require 'prometheus/client/formats/text' class MetricsService CHECKS = [ Gitlab::HealthChecks::DbCheck, - Gitlab::HealthChecks::RedisCheck, + Gitlab::HealthChecks::Redis::RedisCheck, + Gitlab::HealthChecks::Redis::CacheCheck, + Gitlab::HealthChecks::Redis::QueuesCheck, + Gitlab::HealthChecks::Redis::SharedStateCheck, Gitlab::HealthChecks::FsShardsCheck ].freeze diff --git a/app/services/milestones/base_service.rb b/app/services/milestones/base_service.rb index 176ab9f1ab5..4963601ea8b 100644 --- a/app/services/milestones/base_service.rb +++ b/app/services/milestones/base_service.rb @@ -1,4 +1,10 @@ module Milestones class BaseService < ::BaseService + # Parent can either a group or a project + attr_accessor :parent, :current_user, :params + + def initialize(parent, user, params = {}) + @parent, @current_user, @params = parent, user, params.dup + end end end diff --git a/app/services/milestones/close_service.rb b/app/services/milestones/close_service.rb index 608fc49d766..776ec4b287b 100644 --- a/app/services/milestones/close_service.rb +++ b/app/services/milestones/close_service.rb @@ -1,7 +1,7 @@ module Milestones class CloseService < Milestones::BaseService def execute(milestone) - if milestone.close + if milestone.close && milestone.is_project_milestone? event_service.close_milestone(milestone, current_user) end diff --git a/app/services/milestones/create_service.rb b/app/services/milestones/create_service.rb index b8e08c9f1eb..aef3124c7e3 100644 --- a/app/services/milestones/create_service.rb +++ b/app/services/milestones/create_service.rb @@ -1,9 +1,9 @@ module Milestones class CreateService < Milestones::BaseService def execute - milestone = project.milestones.new(params) + milestone = parent.milestones.new(params) - if milestone.save + if milestone.save && milestone.is_project_milestone? event_service.open_milestone(milestone, current_user) end diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb index e457212508f..600ebcfbecb 100644 --- a/app/services/milestones/destroy_service.rb +++ b/app/services/milestones/destroy_service.rb @@ -1,15 +1,17 @@ module Milestones class DestroyService < Milestones::BaseService def execute(milestone) + return unless milestone.is_project_milestone? + Milestone.transaction do update_params = { milestone: nil } milestone.issues.each do |issue| - Issues::UpdateService.new(project, current_user, update_params).execute(issue) + Issues::UpdateService.new(parent, current_user, update_params).execute(issue) end milestone.merge_requests.each do |merge_request| - MergeRequests::UpdateService.new(project, current_user, update_params).execute(merge_request) + MergeRequests::UpdateService.new(parent, current_user, update_params).execute(merge_request) end event_service.destroy_milestone(milestone, current_user) diff --git a/app/services/milestones/reopen_service.rb b/app/services/milestones/reopen_service.rb index 573f9ee5c21..5b8b682caaf 100644 --- a/app/services/milestones/reopen_service.rb +++ b/app/services/milestones/reopen_service.rb @@ -1,7 +1,7 @@ module Milestones class ReopenService < Milestones::BaseService def execute(milestone) - if milestone.activate + if milestone.activate && milestone.is_project_milestone? event_service.reopen_milestone(milestone, current_user) end diff --git a/app/services/milestones/update_service.rb b/app/services/milestones/update_service.rb index ed64847f429..31b441ed476 100644 --- a/app/services/milestones/update_service.rb +++ b/app/services/milestones/update_service.rb @@ -5,9 +5,9 @@ module Milestones case state when 'activate' - Milestones::ReopenService.new(project, current_user, {}).execute(milestone) + Milestones::ReopenService.new(parent, current_user, {}).execute(milestone) when 'close' - Milestones::CloseService.new(project, current_user, {}).execute(milestone) + Milestones::CloseService.new(parent, current_user, {}).execute(milestone) end if params.present? diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 55d9cb13ae4..30ca95eef7a 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -1,22 +1,16 @@ module Projects class UpdateService < BaseService def execute - # check that user is allowed to set specified visibility_level - new_visibility = params[:visibility_level] - - if new_visibility && new_visibility.to_i != project.visibility_level - unless can?(current_user, :change_visibility_level, project) && - Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) - - deny_visibility_level(project, new_visibility) - return error('Visibility level unallowed') - end + unless visibility_level_allowed? + return error('New visibility level not allowed!') end - new_branch = params[:default_branch] + if project.has_container_registry_tags? + return error('Cannot rename project because it contains container registry tags!') + end - if project.repository.exists? && new_branch && new_branch != project.default_branch - project.change_head(new_branch) + if changing_default_branch? + project.change_head(params[:default_branch]) end if project.update_attributes(params.except(:default_branch)) @@ -28,8 +22,33 @@ module Projects success else - error('Project could not be updated') + error('Project could not be updated!') end end + + private + + def visibility_level_allowed? + # check that user is allowed to set specified visibility_level + new_visibility = params[:visibility_level] + + if new_visibility && new_visibility.to_i != project.visibility_level + unless can?(current_user, :change_visibility_level, project) && + Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) + + deny_visibility_level(project, new_visibility) + return false + end + end + + true + end + + def changing_default_branch? + new_branch = params[:default_branch] + + project.repository.exists? && + new_branch && new_branch != project.default_branch + end end end diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index e4dfe87e614..6f82159e6c7 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -146,32 +146,6 @@ module QuickActions end end - desc do - "Change assignee#{'(s)' if issuable.allows_multiple_assignees?}" - end - explanation do |users| - users = issuable.allows_multiple_assignees? ? users : users.take(1) - "Change #{'assignee'.pluralize(users.size)} to #{users.map(&:to_reference).to_sentence}." - end - params do - issuable.allows_multiple_assignees? ? '@user1 @user2' : '@user' - end - condition do - issuable.persisted? && - current_user.can?(:"admin_#{issuable.to_ability_name}", project) - end - parse_params do |assignee_param| - extract_users(assignee_param) - end - command :reassign do |users| - @updates[:assignee_ids] = - if issuable.allows_multiple_assignees? - users.map(&:id) - else - [users.last.id] - end - end - desc 'Set milestone' explanation do |milestone| "Sets the milestone to #{milestone.to_reference}." if milestone diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 0837c07e6aa..da0f21d449a 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -282,7 +282,7 @@ module SystemNoteService body = "changed this line in" if version_params = merge_request.version_params_for(diff_refs) line_code = change_position.line_code(project.repository) - url = url_helpers.diffs_namespace_project_merge_request_url(project.namespace, project, merge_request, version_params.merge(anchor: line_code)) + url = url_helpers.diffs_project_merge_request_url(project, merge_request, version_params.merge(anchor: line_code)) body << " [version #{version_index} of the diff](#{url})" else @@ -413,7 +413,7 @@ module SystemNoteService # # "created branch `201-issue-branch-button`" def new_issue_branch(issue, project, author, branch) - link = url_helpers.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch) + link = url_helpers.project_compare_url(project, from: project.default_branch, to: branch) body = "created branch [`#{branch}`](#{link})" @@ -630,10 +630,9 @@ module SystemNoteService def diff_comparison_url(merge_request, project, oldrev) diff_id = merge_request.merge_request_diff.id - url_helpers.diffs_namespace_project_merge_request_url( - project.namespace, + url_helpers.diffs_project_merge_request_url( project, - merge_request.iid, + merge_request, diff_id: diff_id, start_sha: oldrev ) diff --git a/app/validators/variable_duplicates_validator.rb b/app/validators/variable_duplicates_validator.rb new file mode 100644 index 00000000000..8a9d8892e9b --- /dev/null +++ b/app/validators/variable_duplicates_validator.rb @@ -0,0 +1,13 @@ +# VariableDuplicatesValidator +# +# This validtor is designed for especially the following condition +# - Use `accepts_nested_attributes_for :xxx` in a parent model +# - Use `validates :xxx, uniqueness: { scope: :xxx_id }` in a child model +class VariableDuplicatesValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + duplicates = value.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first) + if duplicates.any? + record.errors.add(attribute, "Duplicate variables: #{duplicates.join(", ")}") + end + end +end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index b21d5665970..26f7c1a473a 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -22,7 +22,9 @@ .form-group = f.label :restricted_visibility_levels, class: 'control-label col-sm-2' .col-sm-10 - - restricted_level_checkboxes('restricted-visibility-help').each do |level| + - checkbox_name = 'application_setting[restricted_visibility_levels][]' + = hidden_field_tag(checkbox_name) + - restricted_level_checkboxes('restricted-visibility-help', checkbox_name).each do |level| .checkbox = level %span.help-block#restricted-visibility-help @@ -143,9 +145,9 @@ .form-group .col-sm-offset-2.col-sm-10 .checkbox - = f.label :signin_enabled do - = f.check_box :signin_enabled - Sign-in enabled + = f.label :password_authentication_enabled do + = f.check_box :password_authentication_enabled + Password authentication enabled - if omniauth_enabled? && button_based_providers.any? .form-group = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'control-label col-sm-2' @@ -331,6 +333,22 @@ Environment variable `prometheus_multiproc_dir` does not exist or is not pointing to a valid directory. %fieldset + %legend Profiling - Performance Bar + %p + Enable the Performance Bar for a given group. + = link_to icon('question-circle'), help_page_path('administration/monitoring/performance/performance_bar') + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :performance_bar_enabled do + = f.check_box :performance_bar_enabled + Enable the Performance Bar + .form-group + = f.label :performance_bar_allowed_group_id, 'Allowed group', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_field :performance_bar_allowed_group_id, class: 'form-control', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path + + %fieldset %legend Background Jobs %p These settings require a restart to take effect. diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 3c9f932a225..128b5dc01ab 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -5,182 +5,182 @@ .admin-dashboard.prepend-top-default .row .col-md-4 - %h4 Statistics - %hr - %p - Forks - %span.light.pull-right - = number_with_delimiter(ForkedProjectLink.count) - %p - Issues - %span.light.pull-right - = number_with_delimiter(Issue.count) - %p - Merge Requests - %span.light.pull-right - = number_with_delimiter(MergeRequest.count) - %p - Notes - %span.light.pull-right - = number_with_delimiter(Note.count) - %p - Snippets - %span.light.pull-right - = number_with_delimiter(Snippet.count) - %p - SSH Keys - %span.light.pull-right - = number_with_delimiter(Key.count) - %p - Milestones - %span.light.pull-right - = number_with_delimiter(Milestone.count) - %p - Active Users - %span.light.pull-right - = number_with_delimiter(User.active.count) + .info-well + .well-segment.admin-well + %h4 Statistics + %p + Forks + %span.light.pull-right + = number_with_delimiter(ForkedProjectLink.count) + %p + Issues + %span.light.pull-right + = number_with_delimiter(Issue.count) + %p + Merge Requests + %span.light.pull-right + = number_with_delimiter(MergeRequest.count) + %p + Notes + %span.light.pull-right + = number_with_delimiter(Note.count) + %p + Snippets + %span.light.pull-right + = number_with_delimiter(Snippet.count) + %p + SSH Keys + %span.light.pull-right + = number_with_delimiter(Key.count) + %p + Milestones + %span.light.pull-right + = number_with_delimiter(Milestone.count) + %p + Active Users + %span.light.pull-right + = number_with_delimiter(User.active.count) .col-md-4 - %h4 - Features - %hr - - sign_up = "Sign up" - %p{ "aria-label" => "#{sign_up}: status " + (signup_enabled? ? "on" : "off") } - = sign_up - %span.light.pull-right - = boolean_to_icon signup_enabled? - - ldap = "LDAP" - %p{ "aria-label" => "#{ldap}: status " + (Gitlab.config.ldap.enabled ? "on" : "off") } - = ldap - %span.light.pull-right - = boolean_to_icon Gitlab.config.ldap.enabled - - gravatar = "Gravatar" - %p{ "aria-label" => "#{gravatar}: status " + (gravatar_enabled? ? "on" : "off") } - = gravatar - %span.light.pull-right - = boolean_to_icon gravatar_enabled? - - omniauth = "OmniAuth" - %p{ "aria-label" => "#{omniauth}: status " + (Gitlab.config.omniauth.enabled ? "on" : "off") } - = omniauth - %span.light.pull-right - = boolean_to_icon Gitlab.config.omniauth.enabled - - reply_email = "Reply by email" - %p{ "aria-label" => "#{reply_email}: status " + (Gitlab::IncomingEmail.enabled? ? "on" : "off") } - = reply_email - %span.light.pull-right - = boolean_to_icon Gitlab::IncomingEmail.enabled? - - container_reg = "Container Registry" - %p{ "aria-label" => "#{container_reg}: status " + (Gitlab.config.registry.enabled ? "on" : "off") } - = container_reg - %span.light.pull-right - = boolean_to_icon Gitlab.config.registry.enabled - - gitlab_pages = 'GitLab Pages' - - gitlab_pages_enabled = Gitlab.config.pages.enabled - %p{ "aria-label" => "#{gitlab_pages}: status " + (gitlab_pages_enabled ? "on" : "off") } - = gitlab_pages - %span.light.pull-right - = boolean_to_icon gitlab_pages_enabled - - gitlab_shared_runners = 'Shared Runners' - - gitlab_shared_runners_enabled = Gitlab.config.gitlab_ci.shared_runners_enabled - %p{ "aria-label" => "#{gitlab_shared_runners}: status " + (gitlab_shared_runners_enabled ? "on" : "off") } - = gitlab_shared_runners - %span.light.pull-right - = boolean_to_icon gitlab_shared_runners_enabled - + .info-well + .well-segment.admin-well + %h4 Features + - sign_up = "Sign up" + %p{ "aria-label" => "#{sign_up}: status " + (signup_enabled? ? "on" : "off") } + = sign_up + %span.light.pull-right + = boolean_to_icon signup_enabled? + - ldap = "LDAP" + %p{ "aria-label" => "#{ldap}: status " + (Gitlab.config.ldap.enabled ? "on" : "off") } + = ldap + %span.light.pull-right + = boolean_to_icon Gitlab.config.ldap.enabled + - gravatar = "Gravatar" + %p{ "aria-label" => "#{gravatar}: status " + (gravatar_enabled? ? "on" : "off") } + = gravatar + %span.light.pull-right + = boolean_to_icon gravatar_enabled? + - omniauth = "OmniAuth" + %p{ "aria-label" => "#{omniauth}: status " + (Gitlab.config.omniauth.enabled ? "on" : "off") } + = omniauth + %span.light.pull-right + = boolean_to_icon Gitlab.config.omniauth.enabled + - reply_email = "Reply by email" + %p{ "aria-label" => "#{reply_email}: status " + (Gitlab::IncomingEmail.enabled? ? "on" : "off") } + = reply_email + %span.light.pull-right + = boolean_to_icon Gitlab::IncomingEmail.enabled? + - container_reg = "Container Registry" + %p{ "aria-label" => "#{container_reg}: status " + (Gitlab.config.registry.enabled ? "on" : "off") } + = container_reg + %span.light.pull-right + = boolean_to_icon Gitlab.config.registry.enabled + - gitlab_pages = 'GitLab Pages' + - gitlab_pages_enabled = Gitlab.config.pages.enabled + %p{ "aria-label" => "#{gitlab_pages}: status " + (gitlab_pages_enabled ? "on" : "off") } + = gitlab_pages + %span.light.pull-right + = boolean_to_icon gitlab_pages_enabled + - gitlab_shared_runners = 'Shared Runners' + - gitlab_shared_runners_enabled = Gitlab.config.gitlab_ci.shared_runners_enabled + %p{ "aria-label" => "#{gitlab_shared_runners}: status " + (gitlab_shared_runners_enabled ? "on" : "off") } + = gitlab_shared_runners + %span.light.pull-right + = boolean_to_icon gitlab_shared_runners_enabled .col-md-4 - %h4 - Components - - if current_application_settings.version_check_enabled - .pull-right - = version_status_badge - - %hr - %p - GitLab - %span.pull-right - = Gitlab::VERSION - %p - GitLab Shell - %span.pull-right - = Gitlab::Shell.new.version - %p - GitLab Workhorse - %span.pull-right - = gitlab_workhorse_version - %p - GitLab API - %span.pull-right - = API::API::version - %p - Git - %span.pull-right - = Gitlab::Git.version - %p - Ruby - %span.pull-right - #{RUBY_VERSION}p#{RUBY_PATCHLEVEL} - - %p - Rails - %span.pull-right - #{Rails::VERSION::STRING} - - %p - = Gitlab::Database.adapter_name - %span.pull-right - = Gitlab::Database.version - %hr + .info-well + .well-segment.admin-well + %h4 + Components + - if current_application_settings.version_check_enabled + .pull-right + = version_status_badge + %p + GitLab + %span.pull-right + = Gitlab::VERSION + %p + GitLab Shell + %span.pull-right + = Gitlab::Shell.new.version + %p + GitLab Workhorse + %span.pull-right + = gitlab_workhorse_version + %p + GitLab API + %span.pull-right + = API::API::version + %p + Git + %span.pull-right + = Gitlab::Git.version + %p + Ruby + %span.pull-right + #{RUBY_VERSION}p#{RUBY_PATCHLEVEL} + %p + Rails + %span.pull-right + #{Rails::VERSION::STRING} + %p + = Gitlab::Database.adapter_name + %span.pull-right + = Gitlab::Database.version .row .col-sm-4 - .light-well.well-centered - %h4 Projects - .data + .info-well.dark-well + .well-segment.well-centered = link_to admin_projects_path do - %h1= number_with_delimiter(Project.cached_count) + %h3.text-center + Projects: + = number_with_delimiter(Project.cached_count) %hr = link_to('New project', new_project_path, class: "btn btn-new") .col-sm-4 - .light-well.well-centered - %h4 Users - .data + .info-well.dark-well + .well-segment.well-centered = link_to admin_users_path do - %h1= number_with_delimiter(User.count) + %h3.text-center + Users: + = number_with_delimiter(User.count) %hr = link_to 'New user', new_admin_user_path, class: "btn btn-new" .col-sm-4 - .light-well.well-centered - %h4 Groups - .data + .info-well.dark-well + .well-segment.well-centered = link_to admin_groups_path do - %h1= number_with_delimiter(Group.count) + %h3.text-center + Groups + = number_with_delimiter(Group.count) %hr = link_to 'New group', new_admin_group_path, class: "btn btn-new" - - .row.prepend-top-10 + .row .col-md-4 - %h4 Latest projects - %hr - - @projects.each do |project| - %p - = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated-60' - %span.light.pull-right - #{time_ago_with_tooltip(project.created_at)} - + .info-well + .well-segment.admin-well + %h4 Latest projects + - @projects.each do |project| + %p + = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated-60' + %span.light.pull-right + #{time_ago_with_tooltip(project.created_at)} .col-md-4 - %h4 Latest users - %hr - - @users.each do |user| - %p - = link_to [:admin, user], class: 'str-truncated-60' do - = user.name - %span.light.pull-right - #{time_ago_with_tooltip(user.created_at)} - + .info-well + .well-segment.admin-well + %h4 Latest users + - @users.each do |user| + %p + = link_to [:admin, user], class: 'str-truncated-60' do + = user.name + %span.light.pull-right + #{time_ago_with_tooltip(user.created_at)} .col-md-4 - %h4 Latest groups - %hr - - @groups.each do |group| - %p - = link_to [:admin, group], class: 'str-truncated-60' do - = group.full_name - %span.light.pull-right - #{time_ago_with_tooltip(group.created_at)} + .info-well + .well-segment.admin-well + %h4 Latest groups + - @groups.each do |group| + %p + = link_to [:admin, group], class: 'str-truncated-60' do + = group.full_name + %span.light.pull-right + #{time_ago_with_tooltip(group.created_at)} diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 9149b8e7fb9..843c71af466 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -107,8 +107,7 @@ = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" %hr = button_tag 'Add users to group', class: "btn btn-create" - - = render 'shared/members/requests', membership_source: @group, requesters: @requesters + = render 'shared/members/requests', membership_source: @group, requesters: @requesters, force_mobile_view: true .panel.panel-default .panel-heading @@ -117,7 +116,7 @@ %span.badge= @group.members.size .pull-right = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@group, :members]), class: "btn btn-xs" - %ul.well-list.group-users-list.content-list + %ul.well-list.group-users-list.content-list.members-list = render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false } .panel-footer = paginate @members, param_name: 'members_page', theme: 'gitlab' diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml index 596f367a00d..c69c2761189 100644 --- a/app/views/admin/projects/_projects.html.haml +++ b/app/views/admin/projects/_projects.html.haml @@ -4,7 +4,7 @@ - @projects.each_with_index do |project| %li.project-row{ class: ('no-description' if project.description.blank?) } .controls - = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn" + = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn" = link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove" .stats %span.badge diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 08a8f627113..7b1b15cfeb8 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -108,7 +108,7 @@ .panel-heading Transfer project .panel-body - = form_for @project, url: transfer_admin_namespace_project_path(@project.namespace, @project), method: :put, html: { class: 'form-horizontal' } do |f| + = form_for @project, url: transfer_admin_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f| .form-group = f.label :new_namespace_id, "Namespace", class: 'control-label' .col-sm-10 @@ -128,7 +128,7 @@ .panel-heading Repository check .panel-body - = form_for @project, url: repository_check_admin_namespace_project_path(@project.namespace, @project), method: :post do |f| + = form_for @project, url: repository_check_admin_project_path(@project), method: :post do |f| .form-group - if @project.last_repository_check_at.nil? This repository has never been checked. @@ -160,12 +160,12 @@ .pull-right = link_to admin_group_path(@group), class: 'btn btn-xs' do = icon('pencil-square-o', text: 'Manage access') - %ul.well-list.content-list + %ul.well-list.content-list.members-list = render partial: 'shared/members/member', collection: @group_members, as: :member, locals: { show_controls: false } .panel-footer = paginate @group_members, param_name: 'group_members_page', theme: 'gitlab' - = render 'shared/members/requests', membership_source: @project, requesters: @requesters + = render 'shared/members/requests', membership_source: @project, requesters: @requesters, force_mobile_view: true .panel.panel-default .panel-heading @@ -174,7 +174,7 @@ %span.badge= @project.users.size .pull-right = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@project, :members]), class: "btn btn-xs" - %ul.well-list.project_members.content-list + %ul.well-list.project_members.content-list.members-list = render partial: 'shared/members/member', collection: @project_members, as: :member, locals: { show_controls: false } .panel-footer = paginate @project_members, param_name: 'project_members_page', theme: 'gitlab' diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 801430e525e..df2bf27be9d 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -85,7 +85,7 @@ %tr.build %td.id - if project - = link_to namespace_project_job_path(project.namespace, project, build) do + = link_to project_job_path(project, build) do %strong ##{build.id} - else %strong ##{build.id} diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml index 15eaf1c0e67..4a440f3f6d4 100644 --- a/app/views/admin/users/projects.html.haml +++ b/app/views/admin/users/projects.html.haml @@ -33,7 +33,7 @@ - member = project.team.find_member(@user.id) %li.project_member .list-item-name - = link_to admin_namespace_project_path(project.namespace, project), class: dom_class(project) do + = link_to admin_project_path(project), class: dom_class(project) do = project.name_with_namespace - if member @@ -44,5 +44,5 @@ %span.light.vertical-align-middle= member.human_access - if member.respond_to? :project - = link_to namespace_project_project_member_path(project.namespace, project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn-xs btn btn-remove prepend-left-10", title: 'Remove user from project' do + = link_to project_project_member_path(project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn-xs btn btn-remove prepend-left-10", title: 'Remove user from project' do %i.fa.fa-times diff --git a/app/views/projects/variables/_content.html.haml b/app/views/ci/variables/_content.html.haml index 98f618ca3b8..98f618ca3b8 100644 --- a/app/views/projects/variables/_content.html.haml +++ b/app/views/ci/variables/_content.html.haml diff --git a/app/views/projects/variables/_form.html.haml b/app/views/ci/variables/_form.html.haml index 0a70a301cb4..eebd0955c80 100644 --- a/app/views/projects/variables/_form.html.haml +++ b/app/views/ci/variables/_form.html.haml @@ -1,12 +1,12 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @variable] do |f| += form_for @variable, as: :variable, url: @variable.form_path do |f| = form_errors(@variable) .form-group = f.label :key, "Key", class: "label-light" - = f.text_field :key, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true + = f.text_field :key, class: "form-control", placeholder: @variable.placeholder, required: true .form-group = f.label :value, "Value", class: "label-light" - = f.text_area :value, class: "form-control", placeholder: "PROJECT_VARIABLE" + = f.text_area :value, class: "form-control", placeholder: @variable.placeholder .form-group .checkbox = f.label :protected do diff --git a/app/views/projects/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml index 5e6786f6698..007c2344b5a 100644 --- a/app/views/projects/variables/_index.html.haml +++ b/app/views/ci/variables/_index.html.haml @@ -1,16 +1,16 @@ .row.prepend-top-default.append-bottom-default .col-lg-4 - = render "projects/variables/content" + = render "ci/variables/content" .col-lg-8 %h5.prepend-top-0 Add a variable - = render "projects/variables/form", btn_text: "Add new variable" + = render "ci/variables/form", btn_text: "Add new variable" %hr %h5.prepend-top-0 - Your variables (#{@project.variables.size}) - - if @project.variables.empty? + Your variables (#{@variables.size}) + - if @variables.empty? %p.settings-message.text-center.append-bottom-0 No variables found, add one with the form above. - else - = render "projects/variables/table" + = render "ci/variables/table" %button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values diff --git a/app/views/ci/variables/_show.html.haml b/app/views/ci/variables/_show.html.haml new file mode 100644 index 00000000000..2bfb290629d --- /dev/null +++ b/app/views/ci/variables/_show.html.haml @@ -0,0 +1,9 @@ +- page_title "Variables" + +.row.prepend-top-default.append-bottom-default + .col-lg-3 + = render "ci/variables/content" + .col-lg-9 + %h5.prepend-top-0 + Update variable + = render "ci/variables/form", btn_text: "Save variable" diff --git a/app/views/projects/variables/_table.html.haml b/app/views/ci/variables/_table.html.haml index 59cd3c4b592..71a0b56c4f4 100644 --- a/app/views/projects/variables/_table.html.haml +++ b/app/views/ci/variables/_table.html.haml @@ -11,18 +11,18 @@ %th Protected %th %tbody - - @project.variables.order_key_asc.each do |variable| + - @variables.each do |variable| - if variable.id? %tr %td.variable-key= variable.key %td.variable-value{ "data-value" => variable.value }****** %td.variable-protected= Gitlab::Utils.boolean_to_yes_no(variable.protected) %td.variable-menu - = link_to namespace_project_variable_path(@project.namespace, @project, variable), class: "btn btn-transparent btn-variable-edit" do + = link_to variable.edit_path, class: "btn btn-transparent btn-variable-edit" do %span.sr-only Update = icon("pencil") - = link_to namespace_project_variable_path(@project.namespace, @project, variable), class: "btn btn-transparent btn-variable-delete", method: :delete, data: { confirm: "Are you sure?" } do + = link_to variable.delete_path, class: "btn btn-transparent btn-variable-delete", method: :delete, data: { confirm: "Are you sure?" } do %span.sr-only Remove = icon("trash") diff --git a/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml b/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml new file mode 100644 index 00000000000..209afd4aab4 --- /dev/null +++ b/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml @@ -0,0 +1,33 @@ +.blank-state + .blank-state-icon + = custom_icon("add_new_user", size: 50) + .blank-state-body + %h3.blank-state-title + Add user + %p.blank-state-text + Add your team members and others to GitLab. + = link_to new_admin_user_path, class: "btn btn-new" do + New user + +.blank-state + .blank-state-icon + = custom_icon("configure_server", size: 50) + .blank-state-body + %h3.blank-state-title + Configure GitLab + %p.blank-state-text + Make adjustments to how your GitLab instance is set up. + = link_to admin_root_path, class: "btn btn-new" do + Configure + +- if current_user.can_create_group? + .blank-state + .blank-state-icon + = custom_icon("add_new_group", size: 50) + .blank-state-body + %h3.blank-state-title + Create a group + %p.blank-state-text + Groups are a great way to organise projects and people. + = link_to new_group_path, class: "btn btn-new" do + New group diff --git a/app/views/dashboard/projects/_blank_state_welcome.html.haml b/app/views/dashboard/projects/_blank_state_welcome.html.haml new file mode 100644 index 00000000000..a93a3415ee1 --- /dev/null +++ b/app/views/dashboard/projects/_blank_state_welcome.html.haml @@ -0,0 +1,48 @@ +- public_project_count = ProjectsFinder.new(current_user: current_user).execute.count + +- if current_user.can_create_group? + .blank-state + .blank-state-icon + = custom_icon("add_new_group", size: 50) + .blank-state-body + %h3.blank-state-title + Create a group for several dependent projects. + %p.blank-state-text + Groups are the best way to manage projects and members. + = link_to new_group_path, class: "btn btn-new" do + New group + +.blank-state + .blank-state-icon + = custom_icon("add_new_project", size: 50) + .blank-state-body + %h3.blank-state-title + Create a project + %p.blank-state-text + - if current_user.can_create_project? + You don't have access to any projects right now. + You can create up to + %strong= number_with_delimiter(current_user.projects_limit) + = succeed "." do + = "project".pluralize(current_user.projects_limit) + - else + If you are added to a project, it will be displayed here. + - if current_user.can_create_project? + = link_to new_project_path, class: "btn btn-new" do + New project + +- if public_project_count > 0 + .blank-state + .blank-state-icon + = custom_icon("globe", size: 50) + .blank-state-body + %h3.blank-state-title + Explore public projects + %p.blank-state-text + There are + = number_with_delimiter(public_project_count) + public projects on this server. + Public projects are an easy way to allow + everyone to have read-only access. + = link_to trending_explore_projects_path, class: "btn btn-new" do + Browse projects diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml index 8843d4e7c84..ad3fac6d164 100644 --- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml @@ -1,47 +1,12 @@ -- publicish_project_count = ProjectsFinder.new(current_user: current_user).execute.count -.blank-state.blank-state-welcome - %h2.blank-state-welcome-title - Welcome to GitLab - %p.blank-state-text - Code, test, and deploy together - -- if current_user.can_create_group? - .blank-state - .blank-state-icon - = custom_icon("group", size: 50) - %h3.blank-state-title - You can create a group for several dependent projects. - %p.blank-state-text - Groups are the best way to manage projects and members. - = link_to new_group_path, class: "btn btn-new" do - New group - -.blank-state - .blank-state-icon - = custom_icon("project", size: 50) - %h3.blank-state-title - You don't have access to any projects right now - %p.blank-state-text - - if current_user.can_create_project? - You can create up to - %strong= number_with_delimiter(current_user.projects_limit) - = succeed "." do - = "project".pluralize(current_user.projects_limit) - - else - If you are added to a project, it will be displayed here. - - if current_user.can_create_project? - = link_to new_project_path, class: "btn btn-new" do - New project - -- if publicish_project_count > 0 - .blank-state - .blank-state-icon - = icon("globe") - %h3.blank-state-title - There are - = number_with_delimiter(publicish_project_count) - public projects on this server. - %p.blank-state-text - Public projects are an easy way to allow everyone to have read-only access. - = link_to trending_explore_projects_path, class: "btn btn-new" do - Browse projects +.row.blank-state-parent-container + .section-container.section-welcome{ class: "#{ 'section-admin-welcome' if current_user.admin? }" } + .container.section-body + .blank-state.blank-state-welcome + %h2.blank-state-welcome-title + Welcome to GitLab + %p.blank-state-text + Code, test, and deploy together + - if current_user.admin? + = render "blank_state_admin_welcome" + - else + = render "blank_state_welcome" diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index af87129e49e..dd61dcf2a7b 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -6,15 +6,15 @@ - else = render 'devise/shared/tabs_normal' .tab-content - - if signin_enabled? || ldap_enabled? || crowd_enabled? + - if password_authentication_enabled? || ldap_enabled? || crowd_enabled? = render 'devise/shared/signin_box' -# Signup only makes sense if you can also sign-in - - if signin_enabled? && signup_enabled? + - if password_authentication_enabled? && signup_enabled? = render 'devise/shared/signup_box' -# Show a message if none of the mechanisms above are enabled - - if !signin_enabled? && !ldap_enabled? && !(omniauth_enabled? && devise_mapping.omniauthable?) + - if !password_authentication_enabled? && !ldap_enabled? && !(omniauth_enabled? && devise_mapping.omniauthable?) %div No authentication methods configured. diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml index f92f89e73ff..e80d10dc8f1 100644 --- a/app/views/devise/shared/_omniauth_box.html.haml +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -6,4 +6,7 @@ - providers.each do |provider| %span.light - has_icon = provider_has_icon?(provider) - = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn') + = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}" + %fieldset + = check_box_tag :remember_me + = label_tag :remember_me, 'Remember Me' diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index da4769e214e..3b06008febe 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -7,12 +7,12 @@ .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && !crowd_enabled?) } .login-body = render 'devise/sessions/new_ldap', server: server - - if signin_enabled? + - if password_authentication_enabled? .login-box.tab-pane{ id: 'ldap-standard', role: 'tabpanel' } .login-body = render 'devise/sessions/new_base' -- elsif signin_enabled? +- elsif password_authentication_enabled? .login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' } .login-body = render 'devise/sessions/new_base' diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml index dd34600490e..6d0243a325d 100644 --- a/app/views/devise/shared/_tabs_ldap.html.haml +++ b/app/views/devise/shared/_tabs_ldap.html.haml @@ -5,9 +5,9 @@ - @ldap_servers.each_with_index do |server, i| %li{ class: active_when(i.zero? && !crowd_enabled?) } = link_to server['label'], "##{server['provider_name']}", 'data-toggle' => 'tab' - - if signin_enabled? + - if password_authentication_enabled? %li = link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab' - - if signin_enabled? && signup_enabled? + - if password_authentication_enabled? && signup_enabled? %li = link_to 'Register', '#register-pane', 'data-toggle' => 'tab' diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml index c225d800a98..212856c0676 100644 --- a/app/views/devise/shared/_tabs_normal.html.haml +++ b/app/views/devise/shared/_tabs_normal.html.haml @@ -1,6 +1,6 @@ %ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist' } %li.active{ role: 'presentation' } %a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in - - if signin_enabled? && signup_enabled? + - if password_authentication_enabled? && signup_enabled? %li{ role: 'presentation' } %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register diff --git a/app/views/discussions/_new_issue_for_all_discussions.html.haml b/app/views/discussions/_new_issue_for_all_discussions.html.haml index ca9e0e8728a..cab346fb514 100644 --- a/app/views/discussions/_new_issue_for_all_discussions.html.haml +++ b/app/views/discussions/_new_issue_for_all_discussions.html.haml @@ -3,4 +3,4 @@ .btn.btn-default.discussion-create-issue-btn.has-tooltip{ title: "Resolve all discussions in new issue", "aria-label" => "Resolve all discussions in a new issue", "data-container" => "body" } - = link_to custom_icon('icon_mr_issue'), new_namespace_project_issue_path(@project.namespace, @project, merge_request_to_resolve_discussions_of: merge_request.iid), title: "Resolve all discussions in new issue", class: 'new-issue-for-discussion' + = link_to custom_icon('icon_mr_issue'), new_project_issue_path(@project, merge_request_to_resolve_discussions_of: merge_request.iid), title: "Resolve all discussions in new issue", class: 'new-issue-for-discussion' diff --git a/app/views/discussions/_new_issue_for_discussion.html.haml b/app/views/discussions/_new_issue_for_discussion.html.haml index df5546a1e32..a9bc317b8f8 100644 --- a/app/views/discussions/_new_issue_for_discussion.html.haml +++ b/app/views/discussions/_new_issue_for_discussion.html.haml @@ -5,4 +5,4 @@ .btn.btn-default.discussion-create-issue-btn.has-tooltip{ title: "Resolve this discussion in a new issue", "aria-label" => "Resolve this discussion in a new issue", "data-container" => "body" } - = link_to custom_icon('icon_mr_issue'), new_namespace_project_issue_path(@project.namespace, @project, merge_request_to_resolve_discussions_of: merge_request.iid, discussion_to_resolve: discussion.id), title: "Resolve this discussion in a new issue", class: 'new-issue-for-discussion' + = link_to custom_icon('icon_mr_issue'), new_project_issue_path(@project, merge_request_to_resolve_discussions_of: merge_request.iid, discussion_to_resolve: discussion.id), title: "Resolve this discussion in a new issue", class: 'new-issue-for-discussion' diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml index 3c64f1be5ff..ad434a64556 100644 --- a/app/views/events/_commit.html.haml +++ b/app/views/events/_commit.html.haml @@ -1,5 +1,5 @@ %li.commit .commit-row-title - = link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit-sha", alt: '', title: truncate_sha(commit[:id]) + = link_to truncate_sha(commit[:id]), project_commit_path(project, commit[:id]), class: "commit-sha", alt: '', title: truncate_sha(commit[:id]) · = markdown event_commit_title(commit[:message]), project: project, pipeline: :single_line, author: event.author diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml index f8f0bcb7608..9fcacfbbf36 100644 --- a/app/views/events/_event_push.atom.haml +++ b/app/views/events/_event_push.atom.haml @@ -2,7 +2,7 @@ - event.commits.first(15).each do |commit| %p %strong= commit[:author][:name] - = link_to "(##{truncate_sha(commit[:id])})", namespace_project_commit_path(event.project.namespace, event.project, id: commit[:id]) + = link_to "(##{truncate_sha(commit[:id])})", project_commit_path(event.project, id: commit[:id]) %i at = commit[:timestamp].to_time.to_s(:short) diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index 769ac655d0a..54b414cc62a 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -6,7 +6,7 @@ %span.author_name= link_to_author event %span.pushed #{event.action_name} #{event.ref_type} %strong - - commits_link = namespace_project_commits_path(project.namespace, project, event.ref_name) + - commits_link = project_commits_path(project, event.ref_name) = link_to_if project.repository.branch_exists?(event.ref_name), event.ref_name, commits_link, class: 'ref-name' = render "events/event_scope", event: event @@ -31,7 +31,7 @@ - from = project.default_branch - from_label = from - = link_to namespace_project_compare_path(project.namespace, project, from: from, to: event.commit_to) do + = link_to project_compare_path(project, from: from, to: event.commit_to) do Compare #{from_label}...#{truncate_sha(event.commit_to)} - if create_mr diff --git a/app/views/groups/_settings_head.html.haml b/app/views/groups/_settings_head.html.haml index 2454e7355a7..623d233a46a 100644 --- a/app/views/groups/_settings_head.html.haml +++ b/app/views/groups/_settings_head.html.haml @@ -12,3 +12,8 @@ = link_to projects_group_path(@group), title: 'Projects' do %span Projects + + = nav_link(controller: :ci_cd) do + = link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do + %span + Pipelines diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 2e4e4511bb6..ad9d5562ded 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -27,6 +27,6 @@ Members with access to %strong= @group.name %span.badge= @members.total_count - %ul.content-list + %ul.content-list.members-list = render partial: 'shared/members/member', collection: @members, as: :member = paginate @members, theme: 'gitlab' diff --git a/app/views/groups/milestones/_form.html.haml b/app/views/groups/milestones/_form.html.haml new file mode 100644 index 00000000000..7f450cd9a93 --- /dev/null +++ b/app/views/groups/milestones/_form.html.haml @@ -0,0 +1,27 @@ += form_for [@group, @milestone], html: { class: 'form-horizontal milestone-form common-note-form js-quick-submit js-requires-input' } do |f| + .row + = form_errors(@milestone) + + .col-md-6 + .form-group + = f.label :title, "Title", class: "control-label" + .col-sm-10 + = f.text_field :title, maxlength: 255, class: "form-control", required: true, autofocus: true + .form-group.milestone-description + = f.label :description, "Description", class: "control-label" + .col-sm-10 + = render layout: 'projects/md_preview', locals: { url: '' } do + = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: 'Write milestone description...' + .clearfix + .error-alert + + = render "shared/milestones/form_dates", f: f + + .form-actions + - if @milestone.new_record? + = f.submit 'Create milestone', class: "btn-create btn" + = link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel" + - else + = f.submit 'Update milestone', class: "btn-create btn" + = link_to "Cancel", group_milestone_path(@group, @milestone), class: "btn btn-cancel" + diff --git a/app/views/groups/milestones/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml index 4c4e0a26728..bae8997e24c 100644 --- a/app/views/groups/milestones/_milestone.html.haml +++ b/app/views/groups/milestones/_milestone.html.haml @@ -1,5 +1,6 @@ + = render 'shared/milestones/milestone', - milestone_path: group_milestone_path(@group, milestone.safe_title, title: milestone.title), + milestone_path: group_milestone_route(milestone), issues_path: issues_group_path(@group, milestone_title: milestone.title), merge_requests_path: merge_requests_group_path(@group, milestone_title: milestone.title), milestone: milestone diff --git a/app/views/groups/milestones/edit.html.haml b/app/views/groups/milestones/edit.html.haml new file mode 100644 index 00000000000..5f6d7d209d0 --- /dev/null +++ b/app/views/groups/milestones/edit.html.haml @@ -0,0 +1,7 @@ +- page_title "Milestones" +- render "header_title" + +%h3.page-title + Edit Milestone + += render "form" diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index f91bee0b610..6ceb4092307 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -9,11 +9,6 @@ = link_to new_group_milestone_path(@group), class: "btn btn-new" do New milestone -.row-content-block - Only milestones from - %strong= @group.name - group are listed here. - .milestones %ul.content-list - if @milestones.blank? diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index 7c7573862d0..e24844661ee 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -4,40 +4,4 @@ %h3.page-title New Milestone -%p.light - This will create milestone in every selected project -%hr - -= form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form common-note-form js-quick-submit js-requires-input' } do |f| - .row - - if @milestone.errors.any? - #error_explanation - .alert.alert-danger - %ul - - @milestone.errors.full_messages.each do |msg| - %li - = msg - - .col-md-6 - .form-group - = f.label :title, "Title", class: "control-label" - .col-sm-10 - = f.text_field :title, maxlength: 255, class: "form-control", required: true, autofocus: true - .form-group.milestone-description - = f.label :description, "Description", class: "control-label" - .col-sm-10 - = render layout: 'projects/md_preview', locals: { url: '' } do - = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: 'Write milestone description...' - .clearfix - .error-alert - .form-group - = f.label :projects, "Projects", class: "control-label" - .col-sm-10 - = f.collection_select :project_ids, @group.projects.non_archived, :id, :name, - { selected: @group.projects.non_archived.pluck(:id) }, required: true, multiple: true, class: 'select2' - - = render "shared/milestones/form_dates", f: f - - .form-actions - = f.submit 'Create milestone', class: "btn-create btn" - = link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel" += render "form" diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 33e68bc766e..54b1b7a734a 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,4 +1,4 @@ = render "header_title" = render 'shared/milestones/top', milestone: @milestone, group: @group -= render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true += render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true if @milestone.is_legacy_group_milestone? = render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102 diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 62ad47972b9..7a2e688a114 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -20,8 +20,8 @@ %span.label.label-warning archived %span.badge = storage_counter(project.statistics.storage_size) - = link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" - = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" + = link_to 'Members', project_project_members_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" + = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove" - if @projects.blank? .nothing-here-block This group has no projects yet diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml new file mode 100644 index 00000000000..bf36baf48ab --- /dev/null +++ b/app/views/groups/settings/ci_cd/show.html.haml @@ -0,0 +1,4 @@ +- page_title "Pipelines" += render "groups/settings_head" + += render 'ci/variables/index' diff --git a/app/views/groups/variables/show.html.haml b/app/views/groups/variables/show.html.haml new file mode 100644 index 00000000000..df533952b76 --- /dev/null +++ b/app/views/groups/variables/show.html.haml @@ -0,0 +1 @@ += render 'ci/variables/show' diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 331d1181220..56e628a2b74 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -27,10 +27,11 @@ %td.shortcut .key f %td Focus Filter - %tr - %td.shortcut - .key p b - %td Show/hide the Performance Bar + - if performance_bar_enabled? + %tr + %td.shortcut + .key p b + %td Show/hide the Performance Bar %tr %td.shortcut .key ? diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 615dd56afbd..48edbb8c16f 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -525,7 +525,7 @@ %h4 %code .file-holder - - blob = Snippet.new(content: "Wow\nSuch\nFile") + - blob = Snippet.new(content: "Wow\nSuch\nFile").blob .example .file-holder .js-file-title.file-title diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml index 57e8c3ca1e1..fde671e25a9 100644 --- a/app/views/import/base/create.js.haml +++ b/app/views/import/base/create.js.haml @@ -4,7 +4,7 @@ job.attr("id", "project_#{@project.id}") target_field = job.find(".import-target") target_field.empty() - target_field.append('#{link_to @project.path_with_namespace, namespace_project_path(@project.namespace, @project)}') + target_field.append('#{link_to @project.path_with_namespace, project_path(@project)}') $("table.import-jobs tbody").prepend(job) job.addClass("active").find(".import-actions").html("<i class='fa fa-spinner fa-spin'></i> started") - else diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml index 882fdf1317d..ad6213b4efd 100644 --- a/app/views/invites/show.html.haml +++ b/app/views/invites/show.html.haml @@ -12,7 +12,7 @@ - project = @member.source project %strong - = link_to project.name_with_namespace, namespace_project_url(project.namespace, project) + = link_to project.name_with_namespace, project_url(project) - when Group - group = @member.source group diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder index 2ed78bb3b65..0c113c08526 100644 --- a/app/views/issues/_issue.atom.builder +++ b/app/views/issues/_issue.atom.builder @@ -1,6 +1,6 @@ xml.entry do - xml.id namespace_project_issue_url(issue.project.namespace, issue.project, issue) - xml.link href: namespace_project_issue_url(issue.project.namespace, issue.project, issue) + xml.id project_issue_url(issue.project, issue) + xml.link href: project_issue_url(issue.project, issue) xml.title truncate(issue.title, length: 80) xml.updated issue.updated_at.xmlschema xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) diff --git a/app/views/layouts/_broadcast.html.haml b/app/views/layouts/_broadcast.html.haml index bcd2f03e83c..e2dbdcbb939 100644 --- a/app/views/layouts/_broadcast.html.haml +++ b/app/views/layouts/_broadcast.html.haml @@ -1,2 +1,2 @@ -- BroadcastMessage.current.each do |message| +- BroadcastMessage.current&.each do |message| = broadcast_message(message) diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index abb6dc2e9f3..6ad22958df3 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -30,7 +30,7 @@ = stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "print", media: "print" = stylesheet_link_tag "test", media: "all" if Rails.env.test? - = stylesheet_link_tag 'peek' if peek_enabled? + = stylesheet_link_tag 'performance_bar' if performance_bar_enabled? - if show_new_nav? = stylesheet_link_tag "new_nav", media: "all" @@ -44,7 +44,7 @@ = webpack_bundle_tag "main" = webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled = webpack_bundle_tag "test" if Rails.env.test? - = webpack_bundle_tag 'peek' if peek_enabled? + = webpack_bundle_tag 'performance_bar' if performance_bar_enabled? - if content_for?(:page_specific_javascripts) = yield :page_specific_javascripts diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 6caaba240bb..4bb0dfc73fd 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -5,10 +5,10 @@ :javascript gl.GfmAutoComplete = gl.GfmAutoComplete || {}; gl.GfmAutoComplete.dataSources = { - members: "#{members_namespace_project_autocomplete_sources_path(project.namespace, project, type: noteable_type, type_id: params[:id])}", - issues: "#{issues_namespace_project_autocomplete_sources_path(project.namespace, project)}", - mergeRequests: "#{merge_requests_namespace_project_autocomplete_sources_path(project.namespace, project)}", - labels: "#{labels_namespace_project_autocomplete_sources_path(project.namespace, project)}", - milestones: "#{milestones_namespace_project_autocomplete_sources_path(project.namespace, project)}", - commands: "#{commands_namespace_project_autocomplete_sources_path(project.namespace, project, type: noteable_type, type_id: params[:id])}" + members: "#{members_project_autocomplete_sources_path(project, type: noteable_type, type_id: params[:id])}", + issues: "#{issues_project_autocomplete_sources_path(project)}", + mergeRequests: "#{merge_requests_project_autocomplete_sources_path(project)}", + labels: "#{labels_project_autocomplete_sources_path(project)}", + milestones: "#{milestones_project_autocomplete_sources_path(project)}", + commands: "#{commands_project_autocomplete_sources_path(project, type: noteable_type, type_id: params[:id])}" }; diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 1a9f5401a78..cc9219cb6fe 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -12,10 +12,12 @@ .content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" } .alert-wrapper = render "layouts/broadcast" + - if show_new_nav? + - if content_for?(:new_global_flash) + = yield :new_global_flash + = render "layouts/nav/breadcrumbs" = render "layouts/flash" = yield :flash_message - - if show_new_nav? - = render "layouts/nav/breadcrumbs" %div{ class: "#{(container_class unless @no_container)} #{@content_class}" } .content{ id: "content-body" } = yield diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index b689991bb6d..59f16b47bf7 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -5,7 +5,7 @@ - if @group && @group.persisted? && @group.path - group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) } - if @project && @project.persisted? - - project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: namespace_project_issues_path(@project.namespace, @project), mr_path: namespace_project_merge_requests_path(@project.namespace, @project) } + - project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: project_issues_path(@project), mr_path: project_merge_requests_path(@project) } .search.search-form{ class: "#{'has-location-badge' if label.present?}" } = form_tag search_path, method: :get, class: 'navbar-form' do |f| .search-input-container diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index d879df8fc82..38b95d11fd4 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,9 +1,8 @@ !!! 5 %html{ lang: I18n.locale, class: "#{page_class}" } = render "layouts/head" - %body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } } + %body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } } = render "layouts/init_auto_complete" if @gfm_form - = render 'peek/bar' - if show_new_nav? = render "layouts/header/new" - else @@ -11,3 +10,5 @@ = render 'layouts/page', sidebar: sidebar, nav: nav = yield :scripts_body + + = render 'peek/bar' diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 7e8b9cb9ad0..bc3293fd100 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -75,7 +75,7 @@ %li = link_to "Settings", profile_path %li - = link_to "Turn on new nav", profile_preferences_path(anchor: "new-navigation") + = link_to "Turn on new navigation", profile_preferences_path(anchor: "new-navigation") %li.divider %li = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link" @@ -91,8 +91,3 @@ = yield :header_content = render 'shared/outdated_browser' - -- if @project && !@project.empty_repo? - - if ref = @ref || @project.repository.root_ref - :javascript - var findFileURL = "#{namespace_project_find_file_path(@project.namespace, @project, ref)}"; diff --git a/app/views/layouts/header/_new.html.haml b/app/views/layouts/header/_new.html.haml index 5859f689dd1..4697d91724b 100644 --- a/app/views/layouts/header/_new.html.haml +++ b/app/views/layouts/header/_new.html.haml @@ -69,7 +69,7 @@ %li = link_to "Settings", profile_path %li - = link_to "Turn off new nav", profile_preferences_path(anchor: "new-navigation") + = link_to "Turn off new navigation", profile_preferences_path(anchor: "new-navigation") %li.divider %li = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link" @@ -84,8 +84,3 @@ = icon('times', class: 'js-navbar-toggle-left', style: 'display: none;') = render 'shared/outdated_browser' - -- if @project && !@project.empty_repo? - - if ref = @ref || @project.repository.root_ref - :javascript - var findFileURL = "#{namespace_project_find_file_path(@project.namespace, @project, ref)}"; diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml index 4d41579168c..9da739b0974 100644 --- a/app/views/layouts/header/_new_dropdown.haml +++ b/app/views/layouts/header/_new_dropdown.haml @@ -30,13 +30,13 @@ %li.dropdown-bold-header This project - if create_project_issue %li - = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project) + = link_to 'New issue', new_project_issue_path(@project) - if merge_project %li - = link_to 'New merge request', namespace_project_new_merge_request_path(merge_project.namespace, merge_project) + = link_to 'New merge request', project_new_merge_request_path(merge_project) - if create_project_snippet %li.header-new-project-snippet - = link_to 'New snippet', new_namespace_project_snippet_path(@project.namespace, @project) + = link_to 'New snippet', new_project_snippet_path(@project) %li.divider %li.dropdown-bold-header GitLab - if current_user.can_create_project? diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml index 5f1641f4300..b0c1ab7420f 100644 --- a/app/views/layouts/nav/_breadcrumbs.html.haml +++ b/app/views/layouts/nav/_breadcrumbs.html.haml @@ -2,7 +2,7 @@ - hide_top_links = @hide_top_links || false %nav.breadcrumbs{ role: "navigation" } - .breadcrumbs-container{ class: container_class } + .breadcrumbs-container{ class: [container_class, @content_class] } .breadcrumbs-links.js-title-container - unless hide_top_links .title diff --git a/app/views/layouts/nav/_new_admin_sidebar.html.haml b/app/views/layouts/nav/_new_admin_sidebar.html.haml index 40c1ca7b53e..d7a9e530983 100644 --- a/app/views/layouts/nav/_new_admin_sidebar.html.haml +++ b/app/views/layouts/nav/_new_admin_sidebar.html.haml @@ -4,7 +4,7 @@ = icon('wrench') .project-title Admin Area %ul.sidebar-top-level-items - = nav_link(controller: %w(dashboard admin projects users groups builds runners cohorts), html_options: {class: 'home'}) do + = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do %span Overview @@ -26,7 +26,7 @@ = link_to admin_groups_path, title: 'Groups' do %span Groups - = nav_link path: 'builds#index' do + = nav_link path: 'jobs#index' do = link_to admin_jobs_path, title: 'Jobs' do %span Jobs diff --git a/app/views/layouts/nav/_new_group_sidebar.html.haml b/app/views/layouts/nav/_new_group_sidebar.html.haml index b7ac04cc3e5..7b68d11041e 100644 --- a/app/views/layouts/nav/_new_group_sidebar.html.haml +++ b/app/views/layouts/nav/_new_group_sidebar.html.haml @@ -1,5 +1,5 @@ .nav-sidebar - = link_to group_path(@group), title: 'Group', class: 'context-header' do + = link_to group_path(@group), title: @group.name, class: 'context-header' do .avatar-container.s40.group-avatar = image_tag group_icon(@group), class: "avatar s40 avatar-tile" .group-title diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml index eae9da5da14..8838852803b 100644 --- a/app/views/layouts/nav/_new_project_sidebar.html.haml +++ b/app/views/layouts/nav/_new_project_sidebar.html.haml @@ -1,6 +1,6 @@ .nav-sidebar - can_edit = can?(current_user, :admin_project, @project) - = link_to project_path(@project), title: 'Project', class: 'context-header' do + = link_to project_path(@project), title: @project.name, class: 'context-header' do .avatar-container.s40.project-avatar = project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile') .project-title @@ -27,93 +27,93 @@ - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do - = link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do + = link_to project_tree_path(@project), title: 'Repository', class: 'shortcuts-tree' do %span Repository %ul.sidebar-sub-level-items = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do - = link_to project_files_path(@project) do + = link_to project_tree_path(@project) do #{ _('Files') } = nav_link(controller: [:commit, :commits]) do - = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do + = link_to project_commits_path(@project, current_ref) do #{ _('Commits') } = nav_link(html_options: {class: branches_tab_class}) do - = link_to namespace_project_branches_path(@project.namespace, @project) do + = link_to project_branches_path(@project) do #{ _('Branches') } = nav_link(controller: [:tags, :releases]) do - = link_to namespace_project_tags_path(@project.namespace, @project) do + = link_to project_tags_path(@project) do #{ _('Tags') } = nav_link(path: 'graphs#show') do - = link_to namespace_project_graph_path(@project.namespace, @project, current_ref) do + = link_to project_graph_path(@project, current_ref) do #{ _('Contributors') } = nav_link(controller: %w(network)) do - = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do + = link_to project_network_path(@project, current_ref) do #{ s_('ProjectNetworkGraph|Graph') } = nav_link(controller: :compare) do - = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do + = link_to project_compare_index_path(@project, from: @repository.root_ref, to: current_ref) do #{ _('Compare') } = nav_link(path: 'graphs#charts') do - = link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do + = link_to charts_project_graph_path(@project, current_ref) do #{ _('Charts') } - if project_nav_tab? :container_registry = nav_link(controller: %w[projects/registry/repositories]) do - = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do + = link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do %span Registry - if project_nav_tab? :issues = nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do - = link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do + = link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do %span - Issues - if @project.default_issues_tracker? %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count) + Issues %ul.sidebar-sub-level-items - if project_nav_tab?(:issues) && !current_controller?(:merge_requests) = nav_link(controller: :issues) do - = link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues' do + = link_to project_issues_path(@project), title: 'Issues' do %span List = nav_link(controller: :boards) do - = link_to namespace_project_boards_path(@project.namespace, @project), title: 'Board' do + = link_to project_boards_path(@project), title: 'Board' do %span Board - if project_nav_tab?(:merge_requests) && current_controller?(:merge_requests) = nav_link(controller: :merge_requests) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests' do + = link_to project_merge_requests_path(@project), title: 'Merge Requests' do %span Merge Requests - if project_nav_tab? :labels = nav_link(controller: :labels) do - = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do + = link_to project_labels_path(@project), title: 'Labels' do %span Labels - if project_nav_tab? :milestones = nav_link(controller: :milestones) do - = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do + = link_to project_milestones_path(@project), title: 'Milestones' do %span Milestones - if project_nav_tab? :merge_requests = nav_link(controller: @project.default_issues_tracker? ? :merge_requests : [:merge_requests, :labels, :milestones]) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do + = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do %span - Merge Requests %span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count) + Merge Requests - if project_nav_tab? :pipelines = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do @@ -148,7 +148,7 @@ - if @project.feature_available?(:builds, current_user) && !@project.empty_repo? = nav_link(path: 'pipelines#charts') do - = link_to charts_namespace_project_pipelines_path(@project.namespace, @project), title: 'Charts', class: 'shortcuts-pipelines-charts' do + = link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do %span Charts @@ -160,7 +160,7 @@ - if project_nav_tab? :snippets = nav_link(controller: :snippets) do - = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do + = link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do %span Snippets @@ -187,23 +187,23 @@ %span Integrations = nav_link(controller: :repository) do - = link_to namespace_project_settings_repository_path(@project.namespace, @project), title: 'Repository' do + = link_to project_settings_repository_path(@project), title: 'Repository' do %span Repository - if @project.feature_available?(:builds, current_user) = nav_link(controller: :ci_cd) do - = link_to namespace_project_settings_ci_cd_path(@project.namespace, @project), title: 'Pipelines' do + = link_to project_settings_ci_cd_path(@project), title: 'Pipelines' do %span Pipelines - if Gitlab.config.pages.enabled = nav_link(controller: :pages) do - = link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages' do + = link_to project_pages_path(@project), title: 'Pages' do %span Pages - else = nav_link(path: %w[members#show]) do - = link_to namespace_project_settings_members_path(@project.namespace, @project), title: 'Settings', class: 'shortcuts-tree' do + = link_to project_settings_members_path(@project), title: 'Settings', class: 'shortcuts-tree' do %span Settings @@ -216,18 +216,18 @@ -# Shortcut to Repository > Graph (formerly, Network) - if project_nav_tab? :network %li.hidden - = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do + = link_to project_network_path(@project, current_ref), title: 'Network', class: 'shortcuts-network' do Graph -# Shortcut to Repository > Charts (formerly, top-nav item "Graphs") - unless @project.empty_repo? %li.hidden - = link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do + = link_to charts_project_graph_path(@project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do Charts -# Shortcut to Issues > New Issue %li.hidden - = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do + = link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do Create a new issue -# Shortcut to Pipelines > Jobs @@ -244,4 +244,4 @@ -# Shortcut to issue boards %li.hidden - = link_to 'Issue Boards', namespace_project_boards_path(@project.namespace, @project), title: 'Issue Boards', class: 'shortcuts-issue-boards' + = link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards' diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index ae1e1361f0f..424905ea890 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -29,7 +29,7 @@ = link_to profile_emails_path, title: 'Emails' do %span Emails - - unless current_user.ldap_user? + - if current_user.allow_password_authentication? = nav_link(controller: :passwords) do = link_to edit_profile_password_path, title: 'Password' do %span diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index b095adcfe7e..fb90bb4b472 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -12,19 +12,19 @@ - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do - = link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do + = link_to project_tree_path(@project), title: 'Repository', class: 'shortcuts-tree' do %span Repository - if project_nav_tab? :container_registry = nav_link(controller: %w[projects/registry/repositories]) do - = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do + = link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do %span Registry - if project_nav_tab? :issues = nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do - = link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do + = link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do %span Issues - if @project.default_issues_tracker? @@ -34,7 +34,7 @@ - controllers = [:merge_requests, 'projects/merge_requests/conflicts'] - controllers.push(:merge_requests, :labels, :milestones) unless @project.default_issues_tracker? = nav_link(controller: controllers) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do + = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do %span Merge Requests %span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(issuables_count_for_state(:merge_requests, :opened, finder: MergeRequestsFinder.new(current_user, project_id: @project.id))) @@ -53,20 +53,21 @@ - if project_nav_tab? :snippets = nav_link(controller: :snippets) do - = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do + = link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do %span Snippets + - if project_nav_tab? :project_members + = nav_link(controller: :project_members) do + = link_to project_project_members_path(@project), title: 'Members', class: 'shortcuts-members' do + %span + Members + - if project_nav_tab? :settings = nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do = link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do %span Settings - - else - = nav_link(path: %w[members#show]) do - = link_to namespace_project_settings_members_path(@project.namespace, @project), title: 'Settings', class: 'shortcuts-tree' do - %span - Settings -# Shortcut to Project > Activity %li.hidden @@ -77,18 +78,18 @@ -# Shortcut to Repository > Graph (formerly, Network) - if project_nav_tab? :network %li.hidden - = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do + = link_to project_network_path(@project, current_ref), title: 'Network', class: 'shortcuts-network' do Graph -# Shortcut to Repository > Charts (formerly, top-nav item "Graphs") - unless @project.empty_repo? %li.hidden - = link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do + = link_to charts_project_graph_path(@project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do Charts -# Shortcut to Issues > New Issue %li.hidden - = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do + = link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do Create a new issue -# Shortcut to Pipelines > Jobs @@ -105,4 +106,4 @@ -# Shortcut to issue boards %li.hidden - = link_to 'Issue Boards', namespace_project_boards_path(@project.namespace, @project), title: 'Issue Boards', class: 'shortcuts-issue-boards' + = link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards' diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 4458c3c2c23..99adb83cd1f 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -11,7 +11,7 @@ - project = @target_project || @project - if current_user :javascript - window.uploads_path = "#{namespace_project_uploads_path project.namespace,project}"; + window.uploads_path = "#{project_uploads_path(project)}"; - content_for :header_content do .js-dropdown-menu-projects diff --git a/app/views/notify/closed_issue_email.text.haml b/app/views/notify/closed_issue_email.text.haml index bc12e38675f..b35d4b7502d 100644 --- a/app/views/notify/closed_issue_email.text.haml +++ b/app/views/notify/closed_issue_email.text.haml @@ -1,3 +1,3 @@ Issue was closed by #{@updated_by.name} -Issue ##{@issue.iid}: #{namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)} +Issue ##{@issue.iid}: #{project_issue_url(@issue.project, @issue)} diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml index d0c96b83976..c4e06cb3cb1 100644 --- a/app/views/notify/closed_merge_request_email.text.haml +++ b/app/views/notify/closed_merge_request_email.text.haml @@ -1,6 +1,6 @@ Merge Request #{@merge_request.to_reference} was closed by #{@updated_by.name} -Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)} +Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} = merge_path_description(@merge_request, 'to') diff --git a/app/views/notify/issue_moved_email.html.haml b/app/views/notify/issue_moved_email.html.haml index 40f7d61fe19..472c31e9a5e 100644 --- a/app/views/notify/issue_moved_email.html.haml +++ b/app/views/notify/issue_moved_email.html.haml @@ -2,5 +2,5 @@ Issue was moved to another project. %p New issue: - = link_to namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) do + = link_to project_issue_url(@new_project, @new_issue) do = @new_issue.title diff --git a/app/views/notify/issue_moved_email.text.erb b/app/views/notify/issue_moved_email.text.erb index b3bd43c2055..66ede43635b 100644 --- a/app/views/notify/issue_moved_email.text.erb +++ b/app/views/notify/issue_moved_email.text.erb @@ -1,4 +1,4 @@ Issue was moved to another project. New issue location: -<%= namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) %> +<%= project_issue_url(@new_project, @new_issue) %> diff --git a/app/views/notify/issue_status_changed_email.text.erb b/app/views/notify/issue_status_changed_email.text.erb index e6ab3fcde77..4200881f7e8 100644 --- a/app/views/notify/issue_status_changed_email.text.erb +++ b/app/views/notify/issue_status_changed_email.text.erb @@ -1,4 +1,4 @@ Issue was <%= @issue_status %> by <%= @updated_by.name %> -Issue <%= @issue.iid %>: <%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)) %> +Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> diff --git a/app/views/notify/merge_request_status_email.text.haml b/app/views/notify/merge_request_status_email.text.haml index 4c9719ba732..ae2a2933865 100644 --- a/app/views/notify/merge_request_status_email.text.haml +++ b/app/views/notify/merge_request_status_email.text.haml @@ -1,6 +1,6 @@ Merge Request #{@merge_request.to_reference} was #{@mr_status} by #{@updated_by.name} -Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)} +Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} = merge_path_description(@merge_request, 'to') diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml index 46c1c9dee0b..661c23bcbe2 100644 --- a/app/views/notify/merged_merge_request_email.text.haml +++ b/app/views/notify/merged_merge_request_email.text.haml @@ -1,6 +1,6 @@ Merge Request #{@merge_request.to_reference} was merged -Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)} +Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} = merge_path_description(@merge_request, 'to') diff --git a/app/views/notify/new_issue_email.text.erb b/app/views/notify/new_issue_email.text.erb index 13f1ac08e94..3c716f77296 100644 --- a/app/views/notify/new_issue_email.text.erb +++ b/app/views/notify/new_issue_email.text.erb @@ -1,6 +1,6 @@ New Issue was created. -Issue <%= @issue.iid %>: <%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)) %> +Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> Author: <%= @issue.author_name %> Assignee: <%= @issue.assignee_list %> diff --git a/app/views/notify/new_mention_in_issue_email.text.erb b/app/views/notify/new_mention_in_issue_email.text.erb index f19ac3adfc7..23213106c5b 100644 --- a/app/views/notify/new_mention_in_issue_email.text.erb +++ b/app/views/notify/new_mention_in_issue_email.text.erb @@ -1,6 +1,6 @@ You have been mentioned in an issue. -Issue <%= @issue.iid %>: <%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)) %> +Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> Author: <%= @issue.author_name %> Assignee: <%= @issue.assignee_list %> diff --git a/app/views/notify/new_mention_in_merge_request_email.text.erb b/app/views/notify/new_mention_in_merge_request_email.text.erb index 5bf0282e097..6fcebb22fc4 100644 --- a/app/views/notify/new_mention_in_merge_request_email.text.erb +++ b/app/views/notify/new_mention_in_merge_request_email.text.erb @@ -1,6 +1,6 @@ You have been mentioned in Merge Request <%= @merge_request.to_reference %> -<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)) %> +<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> <%= merge_path_description(@merge_request, 'to') %> Author: <%= @merge_request.author_name %> diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb index 3c8f178ac77..7d98400e6fe 100644 --- a/app/views/notify/new_merge_request_email.text.erb +++ b/app/views/notify/new_merge_request_email.text.erb @@ -1,6 +1,6 @@ New Merge Request <%= @merge_request.to_reference %> -<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)) %> +<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> <%= merge_path_description(@merge_request, 'to') %> Author: <%= @merge_request.author_name %> diff --git a/app/views/notify/project_was_exported_email.html.haml b/app/views/notify/project_was_exported_email.html.haml index 3def26342a1..f0ba7827cef 100644 --- a/app/views/notify/project_was_exported_email.html.haml +++ b/app/views/notify/project_was_exported_email.html.haml @@ -2,7 +2,7 @@ Project #{@project.name} was exported successfully. %p The project export can be downloaded from: - = link_to download_export_namespace_project_url(@project.namespace, @project), rel: 'nofollow', download: '' do + = link_to download_export_project_url(@project), rel: 'nofollow', download: '' do = @project.name_with_namespace + " export" %p The download link will expire in 24 hours. diff --git a/app/views/notify/project_was_exported_email.text.erb b/app/views/notify/project_was_exported_email.text.erb index 42c4d176876..cd3a1f7934f 100644 --- a/app/views/notify/project_was_exported_email.text.erb +++ b/app/views/notify/project_was_exported_email.text.erb @@ -1,6 +1,6 @@ Project <%= @project.name %> was exported successfully. The project export can be downloaded from: -<%= download_export_namespace_project_url(@project.namespace, @project) %> +<%= download_export_project_url(@project) %> The download link will expire in 24 hours. diff --git a/app/views/notify/project_was_moved_email.html.haml b/app/views/notify/project_was_moved_email.html.haml index 87b3ff7f0b3..c476a39b661 100644 --- a/app/views/notify/project_was_moved_email.html.haml +++ b/app/views/notify/project_was_moved_email.html.haml @@ -2,7 +2,7 @@ Project #{@old_path_with_namespace} was moved to another location %p The project is now located under - = link_to namespace_project_url(@project.namespace, @project) do + = link_to project_url(@project) do = @project.name_with_namespace %p To update the remote url in your local repository run (for ssh): diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb index b2c5f71e465..7c45163e0e8 100644 --- a/app/views/notify/project_was_moved_email.text.erb +++ b/app/views/notify/project_was_moved_email.text.erb @@ -1,7 +1,7 @@ Project <%= @old_path_with_namespace %> was moved to another location The project is now located under -<%= namespace_project_url(@project.namespace, @project) %> +<%= project_url(@project) %> To update the remote url in your local repository run (for ssh): diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index 546376aeed8..5c5520f4cb8 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -3,7 +3,7 @@ %h3 #{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name} - at #{link_to(@message.project_name_with_namespace, namespace_project_url(@message.project_namespace, @message.project))} + at #{link_to(@message.project_name_with_namespace, project_url(@message.project))} - if @message.compare - if @message.reverse_compare? @@ -17,7 +17,7 @@ %ul - @message.commits.each do |commit| %li - %strong= link_to(commit.short_id, namespace_project_commit_url(@message.project_namespace, @message.project, commit)) + %strong= link_to(commit.short_id, project_commit_url(@message.project, commit)) %div %span by #{commit.author_name} %i at #{commit.committed_date.to_s(:iso8601)} diff --git a/app/views/notify/resolved_all_discussions_email.text.erb b/app/views/notify/resolved_all_discussions_email.text.erb index b0d380af8fc..2881f3e699e 100644 --- a/app/views/notify/resolved_all_discussions_email.text.erb +++ b/app/views/notify/resolved_all_discussions_email.text.erb @@ -1,3 +1,3 @@ All discussions on Merge Request <%= @merge_request.to_reference %> were resolved by <%= @resolved_by.name %> -<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)) %> +<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> diff --git a/app/views/peek/views/_rblineprof.html.haml b/app/views/peek/views/_rblineprof.html.haml new file mode 100644 index 00000000000..6c037930ca9 --- /dev/null +++ b/app/views/peek/views/_rblineprof.html.haml @@ -0,0 +1,7 @@ +Profile: + += link_to 'all', url_for(lineprofiler: 'true'), class: 'js-toggle-modal-peek-line-profile' +\/ += link_to 'app & lib', url_for(lineprofiler: 'app'), class: 'js-toggle-modal-peek-line-profile' +\/ += link_to 'views', url_for(lineprofiler: 'views'), class: 'js-toggle-modal-peek-line-profile' diff --git a/app/views/peek/views/_sql.html.haml b/app/views/peek/views/_sql.html.haml index 16fc010f66f..dd8b524064f 100644 --- a/app/views/peek/views/_sql.html.haml +++ b/app/views/peek/views/_sql.html.haml @@ -1,13 +1,13 @@ %strong - %a#peek-show-queries{ href: '#' } + %a.js-toggle-modal-peek-sql %span{ data: { defer_to: "#{view.defer_key}-duration" } }... \/ %span{ data: { defer_to: "#{view.defer_key}-calls" } }... #modal-peek-pg-queries.modal{ tabindex: -1 } - .modal-dialog - #modal-peek-pg-queries-content.modal-content + .modal-dialog.modal-full + .modal-content .modal-header - %a.close{ href: "#", "data-dismiss" => "modal" } × + %button.close.btn.btn-link.btn-sm{ type: 'button', data: { dismiss: 'modal' } } X %h4 SQL queries .modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }... diff --git a/app/views/profiles/chat_names/_chat_name.html.haml b/app/views/profiles/chat_names/_chat_name.html.haml index 1ec1e7c70e4..fe1cf802971 100644 --- a/app/views/profiles/chat_names/_chat_name.html.haml +++ b/app/views/profiles/chat_names/_chat_name.html.haml @@ -10,7 +10,7 @@ %td %strong - if can?(current_user, :admin_project, project) - = link_to service.title, edit_namespace_project_service_path(project.namespace, project, service) + = link_to service.title, edit_project_service_path(project, service) - else = service.title %td diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 2dd938f8379..bd602071384 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -18,12 +18,12 @@ = scheme.name .col-sm-12 %hr - .col-lg-3.profile-settings-sidebar#new-navigation + .col-lg-4.profile-settings-sidebar#new-navigation %h4.prepend-top-0 New Navigation %p This setting allows you to turn on or off the new upcoming navigation concept. - .col-lg-9.syntax-theme + .col-lg-8.syntax-theme = label_tag do .preview= image_tag "old_nav.png" %input.js-experiment-feature-toggle{ type: "radio", value: "false", name: "new_nav", checked: !show_new_nav? } diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index 10f581d751b..ecc966ed453 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -1,7 +1,7 @@ %div{ class: container_class } .nav-block.activity-filter-block.activities .controls - = link_to namespace_project_path(@project.namespace, @project, rss_url_options), title: "Subscribe", class: 'btn rss-btn has-tooltip' do + = link_to project_path(@project, rss_url_options), title: "Subscribe", class: 'btn rss-btn has-tooltip' do = icon('rss') = render 'shared/event_filter' diff --git a/app/views/projects/_find_file_link.html.haml b/app/views/projects/_find_file_link.html.haml index cb4d2bbacf5..da1b2d7f9b6 100644 --- a/app/views/projects/_find_file_link.html.haml +++ b/app/views/projects/_find_file_link.html.haml @@ -1,3 +1,3 @@ -= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn shortcuts-find-file', rel: 'nofollow' do += link_to project_find_file_path(@project, @ref), class: 'btn shortcuts-find-file', rel: 'nofollow' do = icon('search') %span= _('Find file') diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml index f1ef50d2de2..1a71bfca2e2 100644 --- a/app/views/projects/_last_push.html.haml +++ b/app/views/projects/_last_push.html.haml @@ -5,7 +5,7 @@ .event-last-push-text %span You pushed to %strong - = link_to event.ref_name, namespace_project_commits_path(event.project.namespace, event.project, event.ref_name), class: 'ref-name' + = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name' - if event.project != @project %span at diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml index 2bab22e125d..a56c3503c77 100644 --- a/app/views/projects/_wiki.html.haml +++ b/app/views/projects/_wiki.html.haml @@ -14,5 +14,5 @@ Add a homepage to your wiki that contains information about your project %p We recommend you - = link_to "add a homepage", namespace_project_wiki_path(@project.namespace, @project, :home) + = link_to "add a homepage", project_wiki_path(@project, :home) to your project's wiki and GitLab will show it here instead of this message. diff --git a/app/views/projects/artifacts/_tree_directory.html.haml b/app/views/projects/artifacts/_tree_directory.html.haml index e2966ec33c2..03be6f15313 100644 --- a/app/views/projects/artifacts/_tree_directory.html.haml +++ b/app/views/projects/artifacts/_tree_directory.html.haml @@ -1,4 +1,4 @@ -- path_to_directory = browse_namespace_project_job_artifacts_path(@project.namespace, @project, @build, path: directory.path) +- path_to_directory = browse_project_job_artifacts_path(@project, @build, path: directory.path) %tr.tree-item{ 'data-link' => path_to_directory } %td.tree-item-file-name diff --git a/app/views/projects/artifacts/_tree_file.html.haml b/app/views/projects/artifacts/_tree_file.html.haml index ea0b43b85cf..8edb9be049a 100644 --- a/app/views/projects/artifacts/_tree_file.html.haml +++ b/app/views/projects/artifacts/_tree_file.html.haml @@ -1,4 +1,4 @@ -- path_to_file = file_namespace_project_job_artifacts_path(@project.namespace, @project, @build, path: file.path) +- path_to_file = file_project_job_artifacts_path(@project, @build, path: file.path) %tr.tree-item{ 'data-link' => path_to_file } - blob = file.blob diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index 961c805dc7c..576e5b385af 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -6,17 +6,17 @@ .tree-holder .nav-block .tree-controls - = link_to download_namespace_project_job_artifacts_path(@project.namespace, @project, @build), + = link_to download_project_job_artifacts_path(@project, @build), rel: 'nofollow', download: '', class: 'btn btn-default download' do = icon('download') Download artifacts archive %ul.breadcrumb.repo-breadcrumb %li - = link_to 'Artifacts', browse_namespace_project_job_artifacts_path(@project.namespace, @project, @build) + = link_to 'Artifacts', browse_project_job_artifacts_path(@project, @build) - path_breadcrumbs do |title, path| %li - = link_to truncate(title, length: 40), browse_namespace_project_job_artifacts_path(@project.namespace, @project, @build, path) + = link_to truncate(title, length: 40), browse_project_job_artifacts_path(@project, @build, path) .tree-content-holder %table.table.tree-table diff --git a/app/views/projects/artifacts/file.html.haml b/app/views/projects/artifacts/file.html.haml index b25c7c95196..18e86ac5a92 100644 --- a/app/views/projects/artifacts/file.html.haml +++ b/app/views/projects/artifacts/file.html.haml @@ -7,15 +7,15 @@ .nav-block %ul.breadcrumb.repo-breadcrumb %li - = link_to 'Artifacts', browse_namespace_project_job_artifacts_path(@project.namespace, @project, @build) + = link_to 'Artifacts', browse_project_job_artifacts_path(@project, @build) - path_breadcrumbs do |title, path| - title = truncate(title, length: 40) %li - if path == @path - = link_to file_namespace_project_job_artifacts_path(@project.namespace, @project, @build, path) do + = link_to file_project_job_artifacts_path(@project, @build, path) do %strong= title - else - = link_to title, browse_namespace_project_job_artifacts_path(@project.namespace, @project, @build, path) + = link_to title, browse_project_job_artifacts_path(@project, @build, path) %article.file-holder diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index 3627f72f5e1..f11afe8fc22 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -22,9 +22,9 @@ = author_avatar(commit, size: 36) .commit-row-title %strong - = link_to_gfm truncate(commit.title, length: 35), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "cdark" + = link_to_gfm truncate(commit.title, length: 35), project_commit_path(@project, commit.id), class: "cdark" .pull-right - = link_to commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit), class: "commit-sha" + = link_to commit.short_id, project_commit_path(@project, commit), class: "commit-sha" .light = commit_author_link(commit, avatar: false) diff --git a/app/views/projects/blob/_breadcrumb.html.haml b/app/views/projects/blob/_breadcrumb.html.haml index 2c944b516a4..1c148de9678 100644 --- a/app/views/projects/blob/_breadcrumb.html.haml +++ b/app/views/projects/blob/_breadcrumb.html.haml @@ -6,16 +6,16 @@ %ul.breadcrumb.repo-breadcrumb %li - = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do + = link_to project_tree_path(@project, @ref) do = @project.path - path_breadcrumbs do |title, path| - title = truncate(title, length: 40) %li - if path == @path - = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, path)) do + = link_to project_blob_path(@project, tree_join(@ref, path)) do %strong= title - else - = link_to title, namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path)) + = link_to title, project_tree_path(@project, tree_join(@ref, path)) .tree-controls = render 'projects/find_file_link' @@ -24,14 +24,14 @@ -# only show normal/blame view links for text files - if blob.readable_text? - if blame - = link_to 'Normal view', namespace_project_blob_path(@project.namespace, @project, @id), + = link_to 'Normal view', project_blob_path(@project, @id), class: 'btn' - else - = link_to 'Blame', namespace_project_blame_path(@project.namespace, @project, @id), + = link_to 'Blame', project_blame_path(@project, @id), class: 'btn js-blob-blame-link' unless blob.empty? - = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), + = link_to 'History', project_commits_path(@project, @id), class: 'btn' - = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project, + = link_to 'Permalink', project_blob_path(@project, tree_join(@commit.sha, @path)), class: 'btn js-data-file-blob-permalink-url' diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 40978583e8b..b2959ef6d31 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -5,7 +5,7 @@ %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title= _('Create New Directory') .modal-body - = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-quick-submit js-requires-input' do + = form_tag project_create_dir_path(@project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-quick-submit js-requires-input' do .form-group = label_tag :dir_name, _('Directory name'), class: 'control-label' .col-sm-10 diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index c8ca0406213..6a4a657fa8c 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -6,7 +6,7 @@ %h3.page-title Delete #{@blob.name} .modal-body - = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-delete-blob-form js-quick-submit js-requires-input' do + = form_tag project_blob_path(@project, @id), method: :delete, class: 'form-horizontal js-delete-blob-form js-quick-submit js-requires-input' do = render 'shared/new_commit_form', placeholder: "Delete #{@blob.name}" .form-group diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 4af62461151..f8cb612a2b4 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -9,7 +9,7 @@ - if @conflict .alert.alert-danger Someone edited the file the same time you did. Please check out - = link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@branch_name, @file_path)), target: "_blank", rel: 'noopener noreferrer' + = link_to "the file", project_blob_path(@project, tree_join(@branch_name, @file_path)), target: "_blank", rel: 'noopener noreferrer' and make sure your changes will not unintentionally remove theirs. .editor-title-row %h3.page-title.blob-edit-page-title @@ -22,13 +22,13 @@ Write %li - = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do + = link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id) do = editing_preview_title(@blob.name) - = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths) do + = form_tag(project_update_blob_path(@project, @id), method: :put, class: 'form-horizontal js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths) do = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}" = hidden_field_tag 'last_commit_sha', @last_commit_sha = hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'from_merge_request_iid', params[:from_merge_request_iid] - = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id) + = render 'projects/commit_button', ref: @ref, cancel_path: project_blob_path(@project, @id) diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 2afb909572a..8620a470041 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -7,10 +7,10 @@ New file = render 'template_selectors' .file-editor - = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-edit-blob-form js-new-blob-form js-quick-submit js-requires-input', data: blob_editor_paths) do + = form_tag(project_create_blob_path(@project, @id), method: :post, class: 'form-horizontal js-edit-blob-form js-new-blob-form js-quick-submit js-requires-input', data: blob_editor_paths) do = render 'projects/blob/editor', ref: @ref = render 'shared/new_commit_form', placeholder: "Add new file" = hidden_field_tag 'content', '', id: 'file-content' = render 'projects/commit_button', ref: @ref, - cancel_path: namespace_project_tree_path(@project.namespace, @project, @id) + cancel_path: project_tree_path(@project, @id) diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 41f75a491a5..6e2ae4717cd 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -16,4 +16,4 @@ = render 'projects/blob/remove' - title = "Replace #{@blob.name}" - = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put + = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: project_update_blob_path(@project, @id), method: :put diff --git a/app/views/projects/blob/viewers/_changelog.html.haml b/app/views/projects/blob/viewers/_changelog.html.haml index 53921e63b5f..46e3e7f798a 100644 --- a/app/views/projects/blob/viewers/_changelog.html.haml +++ b/app/views/projects/blob/viewers/_changelog.html.haml @@ -1,4 +1,4 @@ = icon('history fw') = succeed '.' do To find the state of this project's repository at the time of any of these versions, check out - = link_to "the tags", namespace_project_tags_path(viewer.project.namespace, viewer.project) + = link_to "the tags", project_tags_path(viewer.project) diff --git a/app/views/projects/blob/viewers/_readme.html.haml b/app/views/projects/blob/viewers/_readme.html.haml index 334b33faf48..d8492abc638 100644 --- a/app/views/projects/blob/viewers/_readme.html.haml +++ b/app/views/projects/blob/viewers/_readme.html.haml @@ -1,4 +1,4 @@ = icon('info-circle fw') = succeed '.' do To learn more about this project, read - = link_to "the wiki", namespace_project_wikis_path(viewer.project.namespace, viewer.project) + = link_to "the wiki", get_project_wiki_path(viewer.project) diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml index 3622720a8b7..07272ea2df1 100644 --- a/app/views/projects/boards/_show.html.haml +++ b/app/views/projects/boards/_show.html.haml @@ -4,7 +4,7 @@ - if show_new_nav? - content_for :sub_title_before do - %li= link_to "Issues", namespace_project_issues_path(@project.namespace, @project) + %li= link_to "Issues", project_issues_path(@project) - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' @@ -34,7 +34,7 @@ ":key" => "_uid" } = render "projects/boards/components/sidebar" %board-add-issues-modal{ "blank-state-image" => render('shared/empty_states/icons/issues.svg'), - "new-issue-path" => new_namespace_project_issue_path(@project.namespace, @project), + "new-issue-path" => new_project_issue_path(@project), "milestone-path" => milestones_filter_dropdown_path, "label-path" => labels_filter_path, ":issue-link-base" => "issueLinkBase", diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml index c314573bdea..8d957613be1 100644 --- a/app/views/projects/boards/components/sidebar/_assignee.html.haml +++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml @@ -22,7 +22,7 @@ - dropdown_options = issue_assignees_dropdown_options %button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: 'button', ref: 'assigneeDropdown', data: { toggle: 'dropdown', field_name: 'issue[assignee_ids][]', first_user: current_user&.username, current_user: 'true', project_id: @project.id, null_user: 'true', multi_select: 'true', 'dropdown-header': dropdown_options[:data][:'dropdown-header'], 'max-select': dropdown_options[:data][:'max-select'] }, ":data-issuable-id" => "issue.id", - ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } + ":data-issue-update" => "'#{project_issues_path(@project)}/' + issue.id + '.json'" } = dropdown_options[:title] = icon("chevron-down") .dropdown-menu.dropdown-select.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author diff --git a/app/views/projects/boards/components/sidebar/_due_date.html.haml b/app/views/projects/boards/components/sidebar/_due_date.html.haml index 1a3b88e28c5..f44a9d49a54 100644 --- a/app/views/projects/boards/components/sidebar/_due_date.html.haml +++ b/app/views/projects/boards/components/sidebar/_due_date.html.haml @@ -23,7 +23,7 @@ .dropdown %button.dropdown-menu-toggle.js-due-date-select.js-issue-boards-due-date{ type: 'button', data: { toggle: 'dropdown', field_name: "issue[due_date]", ability_name: "issue" }, - ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } + ":data-issue-update" => "'#{project_issues_path(@project)}/' + issue.id + '.json'" } %span.dropdown-toggle-text Due date = icon('chevron-down') .dropdown-menu.dropdown-menu-due-date diff --git a/app/views/projects/boards/components/sidebar/_labels.html.haml b/app/views/projects/boards/components/sidebar/_labels.html.haml index bee0f3dd065..7d0c35fe183 100644 --- a/app/views/projects/boards/components/sidebar/_labels.html.haml +++ b/app/views/projects/boards/components/sidebar/_labels.html.haml @@ -19,8 +19,8 @@ ":value" => "label.id" } .dropdown %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-board-sidebar{ type: "button", - data: { toggle: "dropdown", field_name: "issue[label_names][]", show_no: "true", show_any: "true", project_id: @project.id, labels: namespace_project_labels_path(@project.namespace, @project, :json), namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path) }, - ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } + data: { toggle: "dropdown", field_name: "issue[label_names][]", show_no: "true", show_any: "true", project_id: @project.id, labels: project_labels_path(@project, :json), namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path) }, + ":data-issue-update" => "'#{project_issues_path(@project)}/' + issue.id + '.json'" } %span.dropdown-toggle-text Label = icon('chevron-down') diff --git a/app/views/projects/boards/components/sidebar/_milestone.html.haml b/app/views/projects/boards/components/sidebar/_milestone.html.haml index 4e46351bf8a..002e9994ee0 100644 --- a/app/views/projects/boards/components/sidebar/_milestone.html.haml +++ b/app/views/projects/boards/components/sidebar/_milestone.html.haml @@ -16,10 +16,10 @@ name: "issue[milestone_id]", "v-if" => "issue.milestone" } .dropdown - %button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true", default_no: "true" }, + %button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: project_milestones_path(@project, :json), ability_name: "issue", use_id: "true", default_no: "true" }, ":data-selected" => "milestoneTitle", ":data-issuable-id" => "issue.id", - ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } + ":data-issue-update" => "'#{project_issues_path(@project)}/' + issue.id + '.json'" } Milestone = icon("chevron-down") .dropdown-menu.dropdown-select.dropdown-menu-selectable diff --git a/app/views/projects/boards/components/sidebar/_notifications.html.haml b/app/views/projects/boards/components/sidebar/_notifications.html.haml index a08c7f2af09..aaddd7e249f 100644 --- a/app/views/projects/boards/components/sidebar/_notifications.html.haml +++ b/app/views/projects/boards/components/sidebar/_notifications.html.haml @@ -1,5 +1,5 @@ - if current_user - .block.light.subscription{ ":data-url" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '/toggle_subscription'" } + .block.light.subscription{ ":data-url" => "'#{project_issues_path(@project)}/' + issue.id + '/toggle_subscription'" } %span.issuable-header-text.hide-collapsed.pull-left Notifications %button.btn.btn-default.pull-right.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" } diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 869633e016d..19712a8f1be 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -6,7 +6,7 @@ - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) %li{ class: "js-branch-#{branch.name}" } %div - = link_to namespace_project_tree_path(@project.namespace, @project, branch.name), class: 'item-title str-truncated ref-name' do + = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do = icon('code-fork') = branch.name @@ -25,7 +25,7 @@ Merge request - if branch.name != @repository.root_ref - = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: "btn btn-default #{'prepend-left-10' unless merge_project}", method: :post, title: "Compare" do + = link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name), class: "btn btn-default #{'prepend-left-10' unless merge_project}", method: :post, title: "Compare" do Compare = render 'projects/buttons/download', project: @project, ref: branch.name, pipeline: @refs_pipelines[branch.name] @@ -42,7 +42,7 @@ title: "Delete protected branch", data: { toggle: "modal", target: "#modal-delete-branch", - delete_path: namespace_project_branch_path(@project.namespace, @project, branch.name), + delete_path: project_branch_path(@project, branch.name), branch_name: branch.name } } = icon("trash-o") - else @@ -51,7 +51,7 @@ title: "Only a project master or owner can delete a protected branch" } = icon("trash-o") - else - = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), + = link_to project_branch_path(@project, branch.name), class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip", title: "Delete branch", method: :delete, diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml index ad8f9da0621..18fbb81c167 100644 --- a/app/views/projects/branches/_commit.html.haml +++ b/app/views/projects/branches/_commit.html.haml @@ -1,9 +1,9 @@ .branch-commit .icon-container.commit-icon = custom_icon("icon_commit") - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-sha" + = link_to commit.short_id, project_commit_path(project, commit.id), class: "commit-sha" · %span.str-truncated - = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" + = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" · #{time_ago_with_tooltip(commit.committed_date)} diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 4bade77a077..8bc1996452b 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -6,7 +6,7 @@ .top-area.adjust .nav-text Protected branches can be managed in - = link_to 'project settings', namespace_project_protected_branches_path(@project.namespace, @project) + = link_to 'project settings', project_protected_branches_path(@project) .nav-controls = form_tag(filter_branches_path, method: :get) do @@ -25,9 +25,9 @@ = link_to title, filter_branches_path(sort: value), class: ("is-active" if @sort == value) - if can? current_user, :push_code, @project - = link_to namespace_project_merged_branches_path(@project.namespace, @project), class: 'btn btn-inverted btn-remove has-tooltip', title: "Delete all branches that are merged into '#{@project.repository.root_ref}'", method: :delete, data: { confirm: "Deleting the merged branches cannot be undone. Are you sure?", container: 'body' } do + = link_to project_merged_branches_path(@project), class: 'btn btn-inverted btn-remove has-tooltip', title: "Delete all branches that are merged into '#{@project.repository.root_ref}'", method: :delete, data: { confirm: "Deleting the merged branches cannot be undone. Are you sure?", container: 'body' } do Delete merged branches - = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do + = link_to new_project_branch_path(@project), class: 'btn btn-create' do New branch - if @branches.any? diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 5a0eba3551f..03eefcc2b4d 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -27,7 +27,7 @@ .help-block Existing branch name, tag, or commit SHA .form-actions = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 - = link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel' + = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' :javascript var availableRefs = #{@project.repository.ref_names.to_json}; diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index a73ddd5eb33..883922dbf04 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -10,19 +10,19 @@ %li.dropdown-header #{ _('Source code') } %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow', download: '' do + = link_to archive_project_repository_path(project, ref: ref, format: 'zip'), rel: 'nofollow', download: '' do %i.fa.fa-download %span= _('Download zip') %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow', download: '' do + = link_to archive_project_repository_path(project, ref: ref, format: 'tar.gz'), rel: 'nofollow', download: '' do %i.fa.fa-download %span= _('Download tar.gz') %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.bz2'), rel: 'nofollow', download: '' do + = link_to archive_project_repository_path(project, ref: ref, format: 'tar.bz2'), rel: 'nofollow', download: '' do %i.fa.fa-download %span= _('Download tar.bz2') %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar'), rel: 'nofollow', download: '' do + = link_to archive_project_repository_path(project, ref: ref, format: 'tar'), rel: 'nofollow', download: '' do %i.fa.fa-download %span= _('Download tar') @@ -37,7 +37,7 @@ %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li - = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' do + = link_to latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' do %i.fa.fa-download %span #{ s_('DownloadArtifacts|Download') } '#{job.name}' diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index aa1a533b5cb..b04d6a1fa5e 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -10,19 +10,19 @@ - if can_create_issue %li - = link_to new_namespace_project_issue_path(@project.namespace, @project) do + = link_to new_project_issue_path(@project) do = icon('exclamation-circle fw') #{ _('New issue') } - if merge_project %li - = link_to namespace_project_new_merge_request_path(merge_project.namespace, merge_project) do + = link_to project_new_merge_request_path(merge_project) do = icon('tasks fw') #{ _('New merge request') } - if can_create_snippet %li - = link_to new_namespace_project_snippet_path(@project.namespace, @project) do + = link_to new_project_snippet_path(@project) do = icon('file-text-o fw') #{ _('New snippet') } @@ -31,28 +31,28 @@ - if can?(current_user, :push_code, @project) %li - = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do + = link_to project_new_blob_path(@project, @project.default_branch || 'master') do = icon('file fw') #{ _('New file') } %li - = link_to new_namespace_project_branch_path(@project.namespace, @project) do + = link_to new_project_branch_path(@project) do = icon('code-fork fw') #{ _('New branch') } %li - = link_to new_namespace_project_tag_path(@project.namespace, @project) do + = link_to new_project_tag_path(@project) do = icon('tags fw') #{ _('New tag') } - elsif current_user && current_user.already_forked?(@project) %li - = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do + = link_to project_new_blob_path(@project, @project.default_branch || 'master') do = icon('file fw') #{ _('New file') } - elsif can?(current_user, :fork_project, @project) %li - - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), + - continue_params = { to: project_new_blob_path(@project, @project.default_branch || 'master'), notice: edit_in_new_fork_notice, notice_now: edit_in_new_fork_notice_now } - - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id, + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params) = link_to fork_path, method: :post do = icon('file fw') diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index 42f8c75f57b..f45cc7f0f45 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -5,14 +5,14 @@ = custom_icon('icon_fork') %span= s_('GoToYourFork|Fork') - elsif !current_user.can_create_project? - = link_to new_namespace_project_fork_path(@project.namespace, @project), title: _('You have reached your project limit'), class: 'btn has-tooltip disabled' do + = link_to new_project_fork_path(@project), title: _('You have reached your project limit'), class: 'btn has-tooltip disabled' do = custom_icon('icon_fork') %span= s_('CreateNewFork|Fork') - else - = link_to new_namespace_project_fork_path(@project.namespace, @project), class: 'btn' do + = link_to new_project_fork_path(@project), class: 'btn' do = custom_icon('icon_fork') %span= s_('CreateNewFork|Fork') .count-with-arrow %span.arrow - = link_to namespace_project_forks_path(@project.namespace, @project), title: n_('Fork', 'Forks', @project.forks_count), class: 'count' do + = link_to project_forks_path(@project), title: n_('Fork', 'Forks', @project.forks_count), class: 'count' do = @project.forks_count diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 58413e2fc52..e248676be0d 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -1,5 +1,5 @@ - if current_user - = link_to toggle_star_namespace_project_path(@project.namespace, @project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do + = link_to toggle_star_project_path(@project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do - if current_user.starred?(@project) = icon('star') %span.starred= _('Unstar') diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index d9f28d66b66..c1842527480 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -14,7 +14,7 @@ %td.branch-commit - if can?(current_user, :read_build, job) - = link_to namespace_project_job_url(job.project.namespace, job.project, job) do + = link_to project_job_url(job.project, job) do %span.build-link ##{job.id} - else %span.build-link ##{job.id} @@ -30,7 +30,7 @@ = custom_icon("icon_commit") - if commit_sha - = link_to job.short_sha, namespace_project_commit_path(job.project.namespace, job.project, job.sha), class: "commit-sha" + = link_to job.short_sha, project_commit_path(job.project, job.sha), class: "commit-sha" - if job.stuck? = icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.') @@ -63,7 +63,7 @@ - if admin %td - if job.project - = link_to job.project.name_with_namespace, admin_namespace_project_path(job.project.namespace, job.project) + = link_to job.project.name_with_namespace, admin_project_path(job.project) %td - if job.try(:runner) = runner_link(job.runner) @@ -95,16 +95,16 @@ %td .pull-right - if can?(current_user, :read_build, job) && job.artifacts? - = link_to download_namespace_project_job_artifacts_path(job.project.namespace, job.project, job), rel: 'nofollow', download: '', title: 'Download artifacts', class: 'btn btn-build' do + = link_to download_project_job_artifacts_path(job.project, job), rel: 'nofollow', download: '', title: 'Download artifacts', class: 'btn btn-build' do = icon('download') - if can?(current_user, :update_build, job) - if job.active? - = link_to cancel_namespace_project_job_path(job.project.namespace, job.project, job, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do + = link_to cancel_project_job_path(job.project, job, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do = icon('remove', class: 'cred') - elsif allow_retry - if job.playable? && !admin && can?(current_user, :update_build, job) - = link_to play_namespace_project_job_path(job.project.namespace, job.project, job, return_to: request.original_url), method: :post, title: 'Play', class: 'btn btn-build' do + = link_to play_project_job_path(job.project, job, return_to: request.original_url), method: :post, title: 'Play', class: 'btn btn-build' do = custom_icon('icon_play') - elsif job.retryable? - = link_to retry_namespace_project_job_path(job.project.namespace, job.project, job, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do + = link_to retry_project_job_path(job.project, job, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do = icon('repeat') diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml index 2267f123e38..d0a380516f9 100644 --- a/app/views/projects/commit/_change.html.haml +++ b/app/views/projects/commit/_change.html.haml @@ -22,7 +22,7 @@ = label_tag 'start_branch', branch_label, class: 'control-label' .col-sm-10 = hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch' - = dropdown_tag(@project.default_branch, options: { title: s_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: s_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } }) + = dropdown_tag(@project.default_branch, options: { title: s_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: s_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: project_branches_path(@project), submit_form_on_click: false } }) - if can?(current_user, :push_code, @project) = render 'shared/new_merge_request_checkbox' diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml index 8aed88da38b..7338468967f 100644 --- a/app/views/projects/commit/_ci_menu.html.haml +++ b/app/views/projects/commit/_ci_menu.html.haml @@ -1,10 +1,10 @@ %ul.nav-links.no-top.no-bottom.commit-ci-menu = nav_link(path: 'commit#show') do - = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do + = link_to project_commit_path(@project, @commit.id) do Changes %span.badge= @diffs.size - if can?(current_user, :read_pipeline, @project) = nav_link(path: 'commit#pipelines') do - = link_to pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id) do + = link_to pipelines_project_commit_path(@project, @commit.id) do Pipelines - %span.badge= @commit.pipelines.size + %span.badge.js-pipelines-mr-count= @commit.pipelines.size diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 7fe44816bae..45109f2c58b 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -21,7 +21,7 @@ %span.btn.disabled.btn-grouped.hidden-xs.append-right-10 = icon('comment') = @notes_count - = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-default append-right-10 hidden-xs hidden-sm" do + = link_to project_tree_path(@project, @commit), class: "btn btn-default append-right-10 hidden-xs hidden-sm" do #{ _('Browse files') } .dropdown.inline %a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } } @@ -29,22 +29,22 @@ = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right %li.visible-xs-block.visible-sm-block - = link_to namespace_project_tree_path(@project.namespace, @project, @commit) do - _('Browse Files') + = link_to project_tree_path(@project, @commit) do + #{ _('Browse Files') } - unless @commit.has_been_reverted?(current_user) %li.clearfix - = revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false) + = revert_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false) %li.clearfix - = cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false) + = cherry_pick_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false) - if can_collaborate_with_project? %li.clearfix - = link_to s_("CreateTag|Tag"), new_namespace_project_tag_path(@project.namespace, @project, ref: @commit) + = link_to s_("CreateTag|Tag"), new_project_tag_path(@project, ref: @commit) %li.divider %li.dropdown-header #{ _('Download') } - unless @commit.parents.length > 1 - %li= link_to s_("DownloadCommit|Email Patches"), namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch) - %li= link_to s_("DownloadCommit|Plain Diff"), namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff) + %li= link_to s_("DownloadCommit|Email Patches"), project_commit_path(@project, @commit, format: :patch) + %li= link_to s_("DownloadCommit|Plain Diff"), project_commit_path(@project, @commit, format: :diff) .commit-box %h3.commit-title @@ -59,7 +59,7 @@ = custom_icon("icon_commit") %span.cgray= n_('parent', 'parents', @commit.parents.count) - @commit.parents.each do |parent| - = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "commit-sha" + = link_to parent.short_id, project_commit_path(@project, parent), class: "commit-sha" %span.commit-info.branches %i.fa.fa-spinner.fa-spin @@ -67,10 +67,10 @@ - last_pipeline = @commit.last_pipeline .well-segment.pipeline-info .status-icon-container{ class: "ci-status-icon-#{@commit.status}" } - = link_to namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id) do + = link_to project_pipeline_path(@project, last_pipeline.id) do = ci_icon_for_status(last_pipeline.status) #{ _('Pipeline') } - = link_to "##{last_pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id) + = link_to "##{last_pipeline.id}", project_pipeline_path(@project, last_pipeline.id) = ci_label_for_status(last_pipeline.status) - if last_pipeline.stages_count.nonzero? #{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), last_pipeline.stages_count) } @@ -80,4 +80,4 @@ = time_interval_in_words last_pipeline.duration :javascript - $(".commit-info.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}"); + $(".commit-info.branches").load("#{branches_project_commit_path(@project, @commit.id)}"); diff --git a/app/views/projects/commit/pipelines.html.haml b/app/views/projects/commit/pipelines.html.haml index ac93eac41ac..c66ea873dba 100644 --- a/app/views/projects/commit/pipelines.html.haml +++ b/app/views/projects/commit/pipelines.html.haml @@ -2,4 +2,4 @@ = render 'commit_box' = render 'ci_menu' -= render 'projects/commit/pipelines_list', endpoint: pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id) += render 'projects/commit/pipelines_list', endpoint: pipelines_project_commit_path(@project, @commit.id) diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index b778e8af121..07c83c0a590 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,6 +1,7 @@ - @no_container = true - container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : '' - limited_container_width = fluid_layout ? '' : 'limit-container-width' +- @content_class = limited_container_width - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_description @commit.description = render "projects/commits/head" diff --git a/app/views/projects/commits/_commit.atom.builder b/app/views/projects/commits/_commit.atom.builder index 1657fb46163..d806acdda13 100644 --- a/app/views/projects/commits/_commit.atom.builder +++ b/app/views/projects/commits/_commit.atom.builder @@ -1,6 +1,6 @@ xml.entry do - xml.id namespace_project_commit_url(@project.namespace, @project, id: commit.id) - xml.link href: namespace_project_commit_url(@project.namespace, @project, id: commit.id) + xml.id project_commit_url(@project, id: commit.id) + xml.link href: project_commit_url(@project, id: commit.id) xml.title truncate(commit.title, length: 80) xml.updated commit.committed_date.xmlschema xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 8a4ef5a45b3..1033bad0d49 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -16,7 +16,7 @@ .commit-detail .commit-content - = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message item-title" + = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message item-title" %span.commit-row-message.visible-xs-inline · = commit.short_id @@ -39,6 +39,6 @@ .commit-actions.flex-row.hidden-xs - if commit.status(ref) = render_commit_status(commit, ref: ref) - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-sha btn btn-transparent" + = link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent" = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = link_to_browse_code(project, commit) diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index cf8dffc8957..c764e35dd2a 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -12,4 +12,4 @@ - if hidden > 0 %li.alert.alert-warning - = n_('%d additional commit has been omitted to prevent performance issues.', '%d additional commits have been omitted to prevent performance issues.', hidden) % number_with_delimiter(hidden) + = n_('%s additional commit has been omitted to prevent performance issues.', '%s additional commits have been omitted to prevent performance issues.', hidden) % number_with_delimiter(hidden) diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index ebeaab863bc..e1549baef89 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -4,33 +4,33 @@ .nav-links.sub-nav.scrolling-tabs %ul{ class: (container_class) } = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do - = link_to project_files_path(@project) do + = link_to project_tree_path(@project) do #{ _('Files') } = nav_link(controller: [:commit, :commits]) do - = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do + = link_to project_commits_path(@project, current_ref) do #{ _('Commits') } = nav_link(html_options: {class: branches_tab_class}) do - = link_to namespace_project_branches_path(@project.namespace, @project) do + = link_to project_branches_path(@project) do #{ _('Branches') } = nav_link(controller: [:tags, :releases]) do - = link_to namespace_project_tags_path(@project.namespace, @project) do + = link_to project_tags_path(@project) do #{ _('Tags') } = nav_link(path: 'graphs#show') do - = link_to namespace_project_graph_path(@project.namespace, @project, current_ref) do + = link_to project_graph_path(@project, current_ref) do #{ _('Contributors') } = nav_link(controller: %w(network)) do - = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do + = link_to project_network_path(@project, current_ref) do #{ s_('ProjectNetworkGraph|Graph') } = nav_link(controller: :compare) do - = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do + = link_to project_compare_index_path(@project, from: @repository.root_ref, to: current_ref) do #{ _('Compare') } = nav_link(path: 'graphs#charts') do - = link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do + = link_to charts_project_graph_path(@project, current_ref) do #{ _('Charts') } diff --git a/app/views/projects/commits/_inline_commit.html.haml b/app/views/projects/commits/_inline_commit.html.haml index 5fb89935467..48cefbe45f2 100644 --- a/app/views/projects/commits/_inline_commit.html.haml +++ b/app/views/projects/commits/_inline_commit.html.haml @@ -1,8 +1,8 @@ %li.commit.inline-commit .commit-row-title - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-sha" + = link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha" %span.str-truncated - = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" + = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" .pull-right #{time_ago_with_tooltip(commit.committed_date)} diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index 9cf792e1721..a9b77631474 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -1,7 +1,7 @@ xml.title "#{@project.name}:#{@ref} commits" -xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), rel: "self", type: "application/atom+xml" -xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref), rel: "alternate", type: "text/html" -xml.id namespace_project_commits_url(@project.namespace, @project, @ref) +xml.link href: project_commits_url(@project, @ref, rss_url_options), rel: "self", type: "application/atom+xml" +xml.link href: project_commits_url(@project, @ref), rel: "alternate", type: "text/html" +xml.id project_commits_url(@project, @ref) xml.updated @commits.first.committed_date.xmlschema if @commits.any? xml << render(@commits) if @commits.any? diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 7ed7e441344..b8547c10c73 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -2,7 +2,7 @@ - page_title _("Commits"), @ref = content_for :meta_tags do - = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") + = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") = content_for :sub_nav do = render "head" @@ -19,16 +19,16 @@ .tree-controls.hidden-xs.hidden-sm - if @merge_request.present? .control - = link_to _("View open merge request"), namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn' + = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn' - elsif create_mr_button?(@repository.root_ref, @ref) .control = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' .control - = form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'commits-search-form') do + = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form') do = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false } .control - = link_to namespace_project_commits_path(@project.namespace, @project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do + = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do = icon("rss") %div{ id: dom_id(@project) } diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index adb724c1b8d..94b7db5eb25 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -1,4 +1,4 @@ -= form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do += form_tag project_compare_index_path(@project), method: :post, class: 'form-inline js-requires-input' do .clearfix - if params[:to] && params[:from] .compare-switch-container @@ -7,7 +7,7 @@ .input-group.inline-input-group %span.input-group-addon from = hidden_field_tag :from, params[:from] - = button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do + = button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do .dropdown-toggle-text.str-truncated= params[:from] || 'Select branch/tag' = render 'shared/ref_dropdown' .compare-ellipsis.inline ... @@ -15,12 +15,12 @@ .input-group.inline-input-group %span.input-group-addon to = hidden_field_tag :to, params[:to] - = button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do + = button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do .dropdown-toggle-text.str-truncated= params[:to] || 'Select branch/tag' = render 'shared/ref_dropdown' = button_tag "Compare", class: "btn btn-create commits-compare-btn" - if @merge_request.present? - = link_to "View open merge request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'prepend-left-10 btn' + = link_to "View open merge request", project_merge_request_path(@project, @merge_request), class: 'prepend-left-10 btn' - elsif create_mr_button? = link_to "Create merge request", create_mr_path, class: 'prepend-left-10 btn' diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index 7000b289f75..7e7b7335597 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -9,8 +9,8 @@ #cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } } - if @cycle_analytics_no_data .landing.content-block{ "v-if" => "!isOverviewDialogDismissed" } - %button.dismiss-button{ type: 'button', 'aria-label': 'Dismiss Cycle Analytics introduction box' } - = icon("times", "@click" => "dismissOverviewDialog()") + %button.dismiss-button{ type: 'button', 'aria-label': 'Dismiss Cycle Analytics introduction box', "@click" => "dismissOverviewDialog()" } + = icon("times") .svg-container = custom_icon('icon_cycle_analytics_splash') .inner-content diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml index cb98ce04430..45985a5ecef 100644 --- a/app/views/projects/deploy_keys/_index.html.haml +++ b/app/views/projects/deploy_keys/_index.html.haml @@ -12,4 +12,4 @@ Create a new deploy key for this project = render @deploy_keys.form_partial_path %hr - #js-deploy-keys{ data: { endpoint: namespace_project_deploy_keys_path } } + #js-deploy-keys{ data: { endpoint: project_deploy_keys_path(@project) } } diff --git a/app/views/projects/deploy_keys/edit.html.haml b/app/views/projects/deploy_keys/edit.html.haml index 37219f8d7ae..cd910b82b57 100644 --- a/app/views/projects/deploy_keys/edit.html.haml +++ b/app/views/projects/deploy_keys/edit.html.haml @@ -7,4 +7,4 @@ = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key } .form-actions = f.submit 'Save changes', class: 'btn-save btn' - = link_to 'Cancel', namespace_project_settings_repository_path(@project.namespace, @project), class: 'btn btn-cancel' + = link_to 'Cancel', project_settings_repository_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml index 4502c397d29..4c22166c256 100644 --- a/app/views/projects/deployments/_commit.html.haml +++ b/app/views/projects/deployments/_commit.html.haml @@ -6,12 +6,12 @@ = link_to deployment.ref, project_ref_path(@project, deployment.ref), class: "ref-name" .icon-container.commit-icon = custom_icon("icon_commit") - = link_to deployment.short_sha, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-sha" + = link_to deployment.short_sha, project_commit_path(@project, deployment.sha), class: "commit-sha" %p.commit-title.flex-truncate-parent %span.flex-truncate-child - if commit_title = deployment.commit_title = author_avatar(deployment.commit, size: 20) - = link_to_gfm commit_title, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-row-message" + = link_to_gfm commit_title, project_commit_path(@project, deployment.sha), class: "commit-row-message" - else Cant find HEAD commit for this branch diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml index 402c18c447e..da34a83d8e0 100644 --- a/app/views/projects/diffs/_warning.html.haml +++ b/app/views/projects/diffs/_warning.html.haml @@ -3,8 +3,8 @@ Too many changes to show. .pull-right - if current_controller?(:commit) - = link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-sm" - = link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-sm" + = link_to "Plain diff", project_commit_path(@project, @commit, format: :diff), class: "btn btn-sm" + = link_to "Email patch", project_commit_path(@project, @commit, format: :patch), class: "btn btn-sm" - elsif current_controller?('projects/merge_requests/diffs') && @merge_request&.persisted? = link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-sm" = link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-sm" diff --git a/app/views/projects/diffs/viewers/_image.html.haml b/app/views/projects/diffs/viewers/_image.html.haml index 19d08181223..33d3dcbeafa 100644 --- a/app/views/projects/diffs/viewers/_image.html.haml +++ b/app/views/projects/diffs/viewers/_image.html.haml @@ -15,7 +15,7 @@ .two-up.view %span.wrap .frame.deleted - %a{ href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.old_content_sha, diff_file.old_path)) } + %a{ href: project_blob_path(@project, tree_join(diff_file.old_content_sha, diff_file.old_path)) } %img{ src: old_blob_raw_path, alt: diff_file.old_path } %p.image-info.hide %span.meta-filesize= number_to_human_size(old_blob.size) @@ -27,7 +27,7 @@ %span.meta-height %span.wrap .frame.added - %a{ href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.content_sha, diff_file.new_path)) } + %a{ href: project_blob_path(@project, tree_join(diff_file.content_sha, diff_file.new_path)) } %img{ src: blob_raw_path, alt: diff_file.new_path } %p.image-info.hide %span.meta-filesize= number_to_human_size(blob.size) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 78057facde7..087cb804449 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -134,7 +134,7 @@ .help-block The maximum file size allowed is 200KB. - if @project.avatar? %hr - = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" + = link_to 'Remove avatar', project_avatar_path(@project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" = f.submit 'Save changes', class: "btn btn-save" .row.prepend-top-default @@ -148,7 +148,7 @@ Runs a number of housekeeping tasks within the current repository, such as compressing file revisions and removing unreachable objects. .col-lg-8 - = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project), + = link_to 'Housekeeping', housekeeping_project_path(@project), method: :post, class: "btn btn-default" %hr .row.prepend-top-default @@ -164,12 +164,12 @@ .col-lg-8 - if @project.export_project_path - = link_to 'Download export', download_export_namespace_project_path(@project.namespace, @project), + = link_to 'Download export', download_export_project_path(@project), rel: 'nofollow', download: '', method: :get, class: "btn btn-default" - = link_to 'Generate new export', generate_new_export_namespace_project_path(@project.namespace, @project), + = link_to 'Generate new export', generate_new_export_project_path(@project), method: :post, class: "btn btn-default" - else - = link_to 'Export project', export_namespace_project_path(@project.namespace, @project), + = link_to 'Export project', export_project_path(@project), method: :post, class: "btn btn-default" .bs-callout.bs-callout-info @@ -207,13 +207,13 @@ - if @project.archived? %p %strong Once active this project shows up in the search and on the dashboard. - = link_to 'Unarchive project', unarchive_namespace_project_path(@project.namespace, @project), + = link_to 'Unarchive project', unarchive_project_path(@project), data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, method: :post, class: "btn btn-success" - else %p %strong Archived projects cannot be committed to! - = link_to 'Archive project', archive_namespace_project_path(@project.namespace, @project), + = link_to 'Archive project', archive_project_path(@project), data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, method: :post, class: "btn btn-warning" %hr @@ -252,7 +252,7 @@ %p.append-bottom-0 Please select the group you want to transfer this project to in the dropdown to the right. .col-lg-8 - = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } ) do |f| + = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } ) do |f| .form-group = label_tag :new_namespace_id, nil, class: 'label-light' do %span Select a new namespace @@ -276,7 +276,7 @@ = succeed "." do = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) .col-lg-8 - = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f| + = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f| %p %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } @@ -289,7 +289,7 @@ %p.append-bottom-0 Removing the project will delete its repository and all related resources including issues, merge requests etc. .col-lg-8 - = form_tag(namespace_project_path(@project.namespace, @project), method: :delete) do + = form_tag(project_path(@project), method: :delete) do %p %strong Removed projects cannot be restored! = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 50e0bad3ccf..0f132a68ce1 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,6 +1,7 @@ - @no_container = true +- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message -= content_for :flash_message do += content_for flash_message_container do - if current_user && can?(current_user, :download_code, @project) = render 'shared/no_ssh' = render 'shared/no_password' diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml index 6d040f5cfe6..1605f3a3351 100644 --- a/app/views/projects/environments/_form.html.haml +++ b/app/views/projects/environments/_form.html.haml @@ -19,4 +19,4 @@ .form-actions = f.submit 'Save', class: 'btn btn-save' - = link_to 'Cancel', namespace_project_environments_path(@project.namespace, @project), class: 'btn btn-cancel' + = link_to 'Cancel', project_environments_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/environments/_stop.html.haml b/app/views/projects/environments/_stop.html.haml index 14a2d627203..c35f9af2873 100644 --- a/app/views/projects/environments/_stop.html.haml +++ b/app/views/projects/environments/_stop.html.haml @@ -1,5 +1,5 @@ - if can?(current_user, :create_deployment, environment) && environment.stop_action? .inline - = link_to stop_namespace_project_environment_path(@project.namespace, @project, environment), method: :post, + = link_to stop_project_environment_path(@project, environment), method: :post, class: 'btn stop-env-link', rel: 'nofollow', data: { confirm: 'Are you sure you want to stop this environment?' } do = icon('stop', class: 'stop-env-icon') diff --git a/app/views/projects/environments/_terminal_button.html.haml b/app/views/projects/environments/_terminal_button.html.haml index 97de9c95de7..a6201bdbc42 100644 --- a/app/views/projects/environments/_terminal_button.html.haml +++ b/app/views/projects/environments/_terminal_button.html.haml @@ -1,3 +1,3 @@ - if environment.has_terminals? && can?(current_user, :admin_environment, @project) - = link_to terminal_namespace_project_environment_path(@project.namespace, @project, environment), class: 'btn terminal-button' do + = link_to terminal_project_environment_path(@project, environment), class: 'btn terminal-button' do = icon('terminal') diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 80d2b6f5d95..30cdbc5ae04 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -12,6 +12,6 @@ "can-create-environment" => can?(current_user, :create_environment, @project).to_s, "project-environments-path" => project_environments_path(@project), "project-stopped-environments-path" => project_environments_path(@project, scope: :stopped), - "new-environment-path" => new_namespace_project_environment_path(@project.namespace, @project), + "new-environment-path" => new_project_environment_path(@project), "help-page-path" => help_page_path("ci/environments"), "css-class" => container_class } } diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index c5722cf5997..e9e1ad9ef30 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -10,12 +10,12 @@ .top-area .row .col-sm-6 - %h3.page-title + %h3 Environment: = link_to @environment.name, environment_path(@environment) - #prometheus-graphs{ data: { "settings-path": edit_namespace_project_service_path(@project.namespace, @project, 'prometheus'), + #prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'), "documentation-path": help_page_path('administration/monitoring/prometheus/index.md'), - "additional-metrics": additional_metrics_namespace_project_environment_path(@project.namespace, @project, @environment, format: :json), - "has-metrics": "#{@environment.has_metrics?}", deployment_endpoint: namespace_project_environment_deployments_path(@project.namespace, @project, @environment, format: :json) } } + "additional-metrics": additional_metrics_project_environment_path(@project, @environment, format: :json), + "has-metrics": "#{@environment.has_metrics?}", deployment_endpoint: project_environment_deployments_path(@project, @environment, format: :json) } } diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index 31e2bb11ce8..0ce0f5465fc 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -12,9 +12,9 @@ = 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_namespace_project_environment_path(@project.namespace, @project, @environment), class: 'btn' + = link_to 'Edit', edit_project_environment_path(@project, @environment), class: 'btn' - if can?(current_user, :stop_environment, @environment) - = link_to 'Stop', stop_namespace_project_environment_path(@project.namespace, @project, @environment), data: { confirm: 'Are you sure you want to stop this environment?' }, class: 'btn btn-danger', method: :post + = link_to 'Stop', stop_project_environment_path(@project, @environment), data: { confirm: 'Are you sure you want to stop this environment?' }, class: 'btn btn-danger', method: :post .environments-container - if @deployments.blank? diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml index 4c4aa0baff3..464135b5ac7 100644 --- a/app/views/projects/environments/terminal.html.haml +++ b/app/views/projects/environments/terminal.html.haml @@ -22,4 +22,4 @@ = render 'projects/deployments/actions', deployment: @environment.last_deployment .terminal-container{ class: container_class } - #terminal{ data: { project_path: "#{terminal_namespace_project_environment_path(@project.namespace, @project, @environment)}.ws" } } + #terminal{ data: { project_path: "#{terminal_project_environment_path(@project, @environment)}.ws" } } diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index 8a409541fe5..e3bf48ee47f 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -7,7 +7,7 @@ = render 'shared/ref_switcher', destination: 'find_file', path: @path %ul.breadcrumb.repo-breadcrumb %li - = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do + = link_to project_tree_path(@project, @ref) do = @project.path %li.file-finder %input#file_find.form-control.file-finder-input{ type: "text", placeholder: _('Find by path'), autocomplete: 'off' } @@ -20,8 +20,8 @@ :javascript var projectFindFile = new ProjectFindFile($(".file-finder-holder"), { - url: "#{escape_javascript(namespace_project_files_path(@project.namespace, @project, @ref, @options.merge(format: :json)))}", - treeUrl: "#{escape_javascript(namespace_project_tree_path(@project.namespace, @project, @ref))}", - blobUrlTemplate: "#{escape_javascript(namespace_project_blob_path(@project.namespace, @project, @id || @commit.id))}" + url: "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}", + treeUrl: "#{escape_javascript(project_tree_path(@project, @ref))}", + blobUrlTemplate: "#{escape_javascript(project_blob_path(@project, @id || @commit.id))}" }); new ShortcutsFindFile(projectFindFile); diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml index 524b77783ef..d365bcd4ecc 100644 --- a/app/views/projects/forks/error.html.haml +++ b/app/views/projects/forks/error.html.haml @@ -20,6 +20,6 @@ = error %p - = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork", class: "btn" do + = link_to new_project_fork_path(@project), title: "Fork", class: "btn" do %i.fa.fa-code-fork Try to fork again diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index f4aa523b32d..111cbcda266 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -34,7 +34,7 @@ = custom_icon('icon_fork') %span Fork - else - = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do + = link_to new_project_fork_path(@project), title: "Fork project", class: 'btn btn-new' do = custom_icon('icon_fork') %span Fork diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index 5242bc72b71..0f36e1a7353 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -30,7 +30,7 @@ = namespace.human_name - else .fork-thumbnail - = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), method: "POST" do + = link_to project_forks_path(@project, namespace_key: namespace.id), method: "POST" do - if /no_((\w*)_)*avatar/.match(avatar) .no-avatar = icon 'question' diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index b23bbadbdb4..b98dc09534f 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -20,14 +20,14 @@ - if generic_commit_status.ref .icon-container = generic_commit_status.tags.any? ? icon('tag') : icon('code-fork') - = link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref) + = link_to generic_commit_status.ref, project_commits_path(generic_commit_status.project, generic_commit_status.ref) - else .light none .icon-container.commit-icon = custom_icon("icon_commit") - if commit_sha - = link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "commit-sha" + = link_to generic_commit_status.short_sha, project_commit_path(generic_commit_status.project, generic_commit_status.sha), class: "commit-sha" - if retried = icon('warning', class: 'text-warning has-tooltip', title: 'Status was retried.') @@ -53,7 +53,7 @@ - if admin %td - if generic_commit_status.project - = link_to generic_commit_status.project.name_with_namespace, admin_namespace_project_path(generic_commit_status.project.namespace, generic_commit_status.project) + = link_to generic_commit_status.project.name_with_namespace, admin_project_path(generic_commit_status.project) %td - if generic_commit_status.try(:runner) = runner_link(generic_commit_status.runner) diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 680f8ae6c8f..640e0d689ca 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -35,7 +35,7 @@ :javascript $.ajax({ type: "GET", - url: "#{namespace_project_graph_path(@project.namespace, @project, current_ref, format: :json)}", + url: "#{project_graph_path(@project, current_ref, format: :json)}", dataType: "json", success: function (data) { var graph = new ContributorsStatGraph(); diff --git a/app/views/projects/hook_logs/_index.html.haml b/app/views/projects/hook_logs/_index.html.haml index 6962b223451..05b06cfc8b2 100644 --- a/app/views/projects/hook_logs/_index.html.haml +++ b/app/views/projects/hook_logs/_index.html.haml @@ -28,7 +28,7 @@ %td.light = time_ago_with_tooltip(hook_log.created_at) %td - = link_to 'View details', namespace_project_hook_hook_log_path(project.namespace, project, hook, hook_log) + = link_to 'View details', project_hook_hook_log_path(project, hook, hook_log) = paginate hook_logs, theme: 'gitlab' diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml index 2eabe92f8eb..ab5a7b117d7 100644 --- a/app/views/projects/hook_logs/show.html.haml +++ b/app/views/projects/hook_logs/show.html.haml @@ -6,6 +6,6 @@ Request details .col-lg-9 - = link_to 'Resend Request', retry_namespace_project_hook_hook_log_path(@project.namespace, @project, @hook, @hook_log), class: "btn btn-default pull-right prepend-left-10" + = link_to 'Resend Request', retry_project_hook_hook_log_path(@project, @hook, @hook_log), class: "btn btn-default pull-right prepend-left-10" = render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log } diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml index fd382c1d63f..4944e0c8041 100644 --- a/app/views/projects/hooks/edit.html.haml +++ b/app/views/projects/hooks/edit.html.haml @@ -9,14 +9,13 @@ #{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be used for binding events when something is happening within the project. .col-lg-9.append-bottom-default - = form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hook_path do |f| + = form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: project_hook_path(@project, @hook) do |f| = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } = f.submit 'Save changes', class: 'btn btn-create' - = link_to 'Test hook', test_namespace_project_hook_path(@project.namespace, @project, @hook), class: 'btn btn-default' - = link_to 'Remove', namespace_project_hook_path(@project.namespace, @project, @hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' } + = link_to 'Test hook', test_project_hook_path(@project, @hook), class: 'btn btn-default' + = link_to 'Remove', project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' } %hr = render partial: 'projects/hook_logs/index', locals: { hook: @hook, hook_logs: @hook_logs, project: @project } - diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index 25a87411cac..778ff91362d 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -12,7 +12,7 @@ :preserve #{h(sanitize_repo_path(@project, @project.import_error))} -= form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| += form_for @project, url: project_import_path(@project), method: :post, html: { class: 'form-horizontal' } do |f| = render "shared/import_form", f: f .form-actions diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml index 7a188cb6445..e9f21594a71 100644 --- a/app/views/projects/issues/_head.html.haml +++ b/app/views/projects/issues/_head.html.haml @@ -5,29 +5,29 @@ %ul{ class: (container_class) } - if project_nav_tab?(:issues) && !current_controller?(:merge_requests) = nav_link(controller: :issues) do - = link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues' do + = link_to project_issues_path(@project), title: 'Issues' do %span List = nav_link(controller: :boards) do - = link_to namespace_project_boards_path(@project.namespace, @project), title: 'Board' do + = link_to project_boards_path(@project), title: 'Board' do %span Board - if project_nav_tab?(:merge_requests) && current_controller?(:merge_requests) = nav_link(controller: :merge_requests) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests' do + = link_to project_merge_requests_path(@project), title: 'Merge Requests' do %span Merge Requests - if project_nav_tab? :labels = nav_link(controller: :labels) do - = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do + = link_to project_labels_path(@project), title: 'Labels' do %span Labels - if project_nav_tab? :milestones = nav_link(controller: :milestones) do - = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do + = link_to project_milestones_path(@project), title: 'Milestones' do %span Milestones diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 6a0d96f50cd..7dc35be57a6 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -24,7 +24,7 @@ - if issue.milestone %span.issuable-milestone.hidden-xs - = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do + = link_to project_issues_path(issue.project, milestone_title: issue.milestone.title) do = icon('clock-o') = issue.milestone.title - if issue.due_date diff --git a/app/views/projects/issues/_issue_by_email.html.haml b/app/views/projects/issues/_issue_by_email.html.haml index 35b7d1b920c..264032a3a31 100644 --- a/app/views/projects/issues/_issue_by_email.html.haml +++ b/app/views/projects/issues/_issue_by_email.html.haml @@ -30,5 +30,5 @@ Anyone who gets ahold of it can create issues as if they were you. You should - = link_to 'reset it', new_issue_address_namespace_project_path(@project.namespace, @project), class: 'incoming-email-token-reset' + = link_to 'reset it', new_issue_address_project_path(@project), class: 'incoming-email-token-reset' if that ever happens. diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml index bda52fe461c..6a567487514 100644 --- a/app/views/projects/issues/_merge_requests.html.haml +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -18,7 +18,7 @@ - unless @issue.project.id == merge_request.target_project.id in - project = merge_request.target_project - = link_to project.name_with_namespace, namespace_project_path(project.namespace, project) + = link_to project.name_with_namespace, project_path(project) - if merge_request.merged? %span.merge-request-status.prepend-left-10.merged diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml index 698959ec74f..756faf4625e 100644 --- a/app/views/projects/issues/_nav_btns.html.haml +++ b/app/views/projects/issues/_nav_btns.html.haml @@ -2,10 +2,9 @@ = icon('rss') - if @can_bulk_update = button_tag "Edit Issues", class: "btn btn-default append-right-10 js-bulk-update-toggle" -= link_to "New issue", new_namespace_project_issue_path(@project.namespace, - @project, - issue: { assignee_id: issues_finder.assignee.try(:id), - milestone_id: issues_finder.milestones.first.try(:id) }), - class: "btn btn-new", - title: "New issue", - id: "new_issue_link" += link_to "New issue", new_project_issue_path(@project, + issue: { assignee_id: issues_finder.assignee.try(:id), + milestone_id: issues_finder.milestones.first.try(:id) }), + class: "btn btn-new", + title: "New issue", + id: "new_issue_link" diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml index dba092c8844..e1b4a49850a 100644 --- a/app/views/projects/issues/_new_branch.html.haml +++ b/app/views/projects/issues/_new_branch.html.haml @@ -1,5 +1,5 @@ - if can?(current_user, :push_code, @project) - .create-mr-dropdown-wrap{ data: { can_create_path: can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue), create_mr_path: create_merge_request_namespace_project_issue_path(@project.namespace, @project, @issue), create_branch_path: namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid) } } + .create-mr-dropdown-wrap{ data: { can_create_path: can_create_branch_project_issue_path(@project, @issue), create_mr_path: create_merge_request_project_issue_path(@project, @issue), create_branch_path: project_branches_path(@project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid) } } .btn-group.unavailable %button.btn.btn-grouped{ type: 'button', disabled: 'disabled' } = icon('spinner', class: 'fa-spin') diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml index 8c9f6f3b4df..1df38db9fd4 100644 --- a/app/views/projects/issues/_related_branches.html.haml +++ b/app/views/projects/issues/_related_branches.html.haml @@ -11,4 +11,4 @@ = render_pipeline_status(pipeline) %span.related-branch-info %strong - = link_to branch, namespace_project_compare_path(@project.namespace, @project, from: @project.default_branch, to: branch), class: "ref-name" + = link_to branch, project_compare_path(@project, from: @project.default_branch, to: branch), class: "ref-name" diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index 61346884346..4029926f373 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -1,7 +1,7 @@ xml.title "#{@project.name} issues" xml.link href: url_for(params), rel: "self", type: "application/atom+xml" -xml.link href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html" -xml.id namespace_project_issues_url(@project.namespace, @project) +xml.link href: project_issues_url(@project), rel: "alternate", type: "text/html" +xml.id project_issues_url(@project) xml.updated @issues.first.updated_at.xmlschema if @issues.reorder(nil).any? xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any? diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 89ac5ff7128..aacb057840d 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -33,4 +33,4 @@ - if new_issue_email = render 'issue_by_email', email: new_issue_email - else - = render 'shared/empty_states/issues', button_path: new_namespace_project_issue_path(@project.namespace, @project) + = render 'shared/empty_states/issues', button_path: new_project_issue_path(@project) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index d909b0bfbbd..a57844f974e 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -30,27 +30,26 @@ .dropdown-menu.dropdown-menu-align-right.hidden-lg %ul - if can_update_issue - %li - = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'issuable-edit' - %li - = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' - %li - = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' + %li= link_to 'Edit', edit_project_issue_path(@project, @issue) + - unless current_user == @issue.author + %li= link_to 'Report abuse', new_abuse_report_path(user_id: @issue.author.id, ref_url: issue_url(@issue)) + - if can_update_issue + %li= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' + %li= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' - if can_report_spam - %li - = link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam' + %li= link_to 'Submit as spam', mark_as_spam_project_issue_path(@project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam' - if can_update_issue || can_report_spam %li.divider - %li - = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link' + %li= link_to 'New issue', new_project_issue_path(@project), title: 'New issue', id: 'new_issue_link' - if can_update_issue - = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' - = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' - = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' + = link_to 'Edit', edit_project_issue_path(@project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' + + = render 'shared/issuable/close_reopen_button', issuable: @issue, can_update: can_update_issue + - if can_report_spam - = link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam' - = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do + = link_to 'Submit as spam', mark_as_spam_project_issue_path(@project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam' + = link_to new_project_issue_path(@project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do New issue .issue-details.issuable-details @@ -65,10 +64,10 @@ = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago') - #merge-requests{ data: { url: referenced_merge_requests_namespace_project_issue_url(@project.namespace, @project, @issue) } } + #merge-requests{ data: { url: referenced_merge_requests_project_issue_url(@project, @issue) } } // This element is filled in using JavaScript. - #related-branches{ data: { url: related_branches_namespace_project_issue_url(@project.namespace, @project, @issue) } } + #related-branches{ data: { url: related_branches_project_issue_url(@project, @issue) } } // This element is filled in using JavaScript. .content-block.emoji-block diff --git a/app/views/projects/jobs/_header.html.haml b/app/views/projects/jobs/_header.html.haml index ad72ab5b199..d81b8f6bb4c 100644 --- a/app/views/projects/jobs/_header.html.haml +++ b/app/views/projects/jobs/_header.html.haml @@ -6,13 +6,13 @@ = render 'ci/status/badge', status: @build.detailed_status(current_user), link: false, title: @build.status_title %strong Job - = link_to "##{@build.id}", namespace_project_job_path(@project.namespace, @project, @build), class: 'js-build-id' + = link_to "##{@build.id}", project_job_path(@project, @build), class: 'js-build-id' in pipeline %strong = link_to "##{pipeline.id}", pipeline_path(pipeline) for %strong - = link_to pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, pipeline.sha), class: 'commit-sha' + = link_to pipeline.short_sha, project_commit_path(@project, pipeline.sha), class: 'commit-sha' from %strong = link_to @build.ref, project_ref_path(@project, @build.ref), class: 'ref-name' @@ -24,8 +24,8 @@ - if show_controls .nav-controls - if can?(current_user, :create_issue, @project) && @build.failed? - = link_to "New issue", new_namespace_project_issue_path(@project.namespace, @project, issue: build_failed_issue_options), class: 'btn btn-new btn-inverted' + = link_to "New issue", new_project_issue_path(@project, issue: build_failed_issue_options), class: 'btn btn-new btn-inverted' - if can?(current_user, :update_build, @build) && @build.retryable? - = link_to "Retry job", retry_namespace_project_job_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary', method: :post + = link_to "Retry job", retry_project_job_path(@project, @build), class: 'btn btn-inverted-secondary', method: :post %button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" } = icon('angle-double-left') diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml index 93e8a4e385c..f2db71e8838 100644 --- a/app/views/projects/jobs/_sidebar.html.haml +++ b/app/views/projects/jobs/_sidebar.html.haml @@ -11,7 +11,7 @@ #js-details-block-vue - if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?) - .block{ class: ("block-first" if !@build.coverage) } + .block .title Job artifacts - if @build.artifacts_expired? @@ -26,18 +26,18 @@ - if @build.artifacts? .btn-group.btn-group-justified{ role: :group } - if @build.has_expiring_artifacts? && can?(current_user, :update_build, @build) - = link_to keep_namespace_project_job_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post do + = link_to keep_project_job_artifacts_path(@project, @build), class: 'btn btn-sm btn-default', method: :post do Keep - = link_to download_namespace_project_job_artifacts_path(@project.namespace, @project, @build), rel: 'nofollow', download: '', class: 'btn btn-sm btn-default' do + = link_to download_project_job_artifacts_path(@project, @build), rel: 'nofollow', download: '', class: 'btn btn-sm btn-default' do Download - if @build.artifacts_metadata? - = link_to browse_namespace_project_job_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do + = link_to browse_project_job_artifacts_path(@project, @build), class: 'btn btn-sm btn-default' do Browse - if @build.trigger_request - .build-widget + .build-widget.block %h4.title Trigger @@ -55,10 +55,10 @@ .js-build-variable.trigger-build-variable= key .js-build-value.trigger-build-value= value - .block + %div{ class: (@build.pipeline.stages_count > 1 ? "block" : "block-last") } %p Commit - = link_to @build.pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, @build.pipeline.sha), class: 'commit-sha link-commit' + = link_to @build.pipeline.short_sha, project_commit_path(@project, @build.pipeline.sha), class: 'commit-sha link-commit' = clipboard_button(text: @build.pipeline.short_sha, title: "Copy commit SHA to clipboard") - if @build.merge_request in @@ -69,13 +69,13 @@ - if @build.pipeline.stages_count > 1 .dropdown.build-dropdown - .title + %div %span{ class: "ci-status-icon-#{@build.pipeline.status}" } = ci_icon_for_status(@build.pipeline.status) Pipeline - = link_to "##{@build.pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, @build.pipeline), class: 'link-commit' + = link_to "##{@build.pipeline.id}", project_pipeline_path(@project, @build.pipeline), class: 'link-commit' from - = link_to "#{@build.pipeline.ref}", namespace_project_branch_path(@project.namespace, @project, @build.pipeline.ref), class: 'link-commit' + = link_to "#{@build.pipeline.ref}", project_branch_path(@project, @build.pipeline.ref), class: 'link-commit' %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.stage-selection More = icon('chevron-down') @@ -88,7 +88,7 @@ - HasStatus::ORDERED_STATUSES.each do |build_status| - builds.select{|build| build.status == build_status}.each do |build| .build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } } - = link_to namespace_project_job_path(@project.namespace, @project, build) do + = link_to project_job_path(@project, build) do = icon('arrow-right') %span{ class: "ci-status-icon-#{build.status}" } = ci_icon_for_status(build.status) diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml index a33e3978ee1..8604c7d3ea4 100644 --- a/app/views/projects/jobs/index.html.haml +++ b/app/views/projects/jobs/index.html.haml @@ -10,7 +10,7 @@ .nav-controls - if can?(current_user, :update_build, @project) - if @all_builds.running_or_pending.any? - = link_to 'Cancel running', cancel_all_namespace_project_jobs_path(@project.namespace, @project), + = link_to 'Cancel running', cancel_all_project_jobs_path(@project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post - unless @repository.gitlab_ci_yml diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index c73bae0a2c9..fa086413fbe 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -21,7 +21,7 @@ %br Go to - = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do + = link_to project_runners_path(@build.project) do Runners page - if @build.starts_environment? @@ -54,23 +54,24 @@ - else Job has been erased #{time_ago_with_tooltip(@build.erased_at)} - .build-trace-container#build-trace - .top-bar.sticky + .build-trace-container.prepend-top-default + .top-bar.js-top-bar .js-truncated-info.truncated-info.hidden< Showing last %span.js-truncated-info-size.truncated-info-size>< KiB of log - - %a.js-raw-link.raw-link{ href: raw_namespace_project_job_path(@project.namespace, @project, @build) }>< Complete Raw + %a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw + .controllers - if @build.has_trace? - = link_to raw_namespace_project_job_path(@project.namespace, @project, @build), + = link_to raw_project_job_path(@project, @build), title: 'Show complete raw', data: { placement: 'top', container: 'body' }, class: 'js-raw-link-controller has-tooltip controllers-buttons' do = icon('file-text-o') - if can?(current_user, :update_build, @project) && @build.erasable? - = link_to erase_namespace_project_job_path(@project.namespace, @project, @build), + = link_to erase_project_job_path(@project, @build), method: :post, data: { confirm: 'Are you sure you want to erase this build?', placement: 'top', container: 'body' }, title: 'Erase job log', @@ -82,15 +83,17 @@ .has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} } %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } = custom_icon('scroll_down') - .bash.sticky.js-scroll-container - %code.js-build-output + + %pre.build-trace#build-trace + %code.bash.js-build-output .build-loader-animation.js-build-refresh + = render "sidebar" .js-build-options{ data: javascript_build_options } -#js-job-details-vue{ data: { endpoint: namespace_project_job_path(@project.namespace, @project, @build, format: :json) } } +#js-job-details-vue{ data: { endpoint: project_job_path(@project, @build, format: :json) } } - content_for :page_specific_javascripts do = webpack_bundle_tag('common_vue') diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index 7f0059cdcda..84b0b65d1c0 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -6,4 +6,4 @@ %h3.page-title Edit Label %hr - = render 'shared/labels/form', url: namespace_project_label_path(@project.namespace.becomes(Namespace), @project, @label), back_path: namespace_project_labels_path(@project.namespace, @project) + = render 'shared/labels/form', url: project_label_path(@project, @label), back_path: project_labels_path(@project) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index fc72c4fb635..8fbc4588902 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -11,7 +11,7 @@ .nav-controls - if can?(current_user, :admin_label, @project) - = link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do + = link_to new_project_label_path(@project), class: "btn btn-new" do New label .labels @@ -20,7 +20,7 @@ - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') .prioritized-labels{ class: ('hide' if hide) } %h5 Prioritized Labels - %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) } + %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) } #js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' - if @prioritized_labels.present? diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index 8f6c085a361..79e90b7ca3b 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -6,4 +6,4 @@ %h3.page-title New Label %hr - = render 'shared/labels/form', url: namespace_project_labels_path(@project.namespace.becomes(Namespace), @project), back_path: namespace_project_labels_path(@project.namespace, @project) + = render 'shared/labels/form', url: project_labels_path(@project), back_path: project_labels_path(@project) diff --git a/app/views/projects/mattermosts/_no_teams.html.haml b/app/views/projects/mattermosts/_no_teams.html.haml index aac74a25b75..243dcfdc187 100644 --- a/app/views/projects/mattermosts/_no_teams.html.haml +++ b/app/views/projects/mattermosts/_no_teams.html.haml @@ -13,4 +13,4 @@ and try again. %hr .clearfix - = link_to 'Go back', edit_namespace_project_service_path(@project.namespace, @project, @service), class: 'btn btn-lg pull-right' + = link_to 'Go back', edit_project_service_path(@project, @service), class: 'btn btn-lg pull-right' diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml index 04bd4e8b683..3bdb5d0adc4 100644 --- a/app/views/projects/mattermosts/_team_selection.html.haml +++ b/app/views/projects/mattermosts/_team_selection.html.haml @@ -2,7 +2,7 @@ This service will be installed on the Mattermost instance at %strong= link_to Gitlab.config.mattermost.host, Gitlab.config.mattermost.host %hr -= form_for(:mattermost, method: :post, url: namespace_project_mattermost_path(@project.namespace, @project), html: { class: 'js-requires-input'} ) do |f| += form_for(:mattermost, method: :post, url: project_mattermost_path(@project), html: { class: 'js-requires-input'} ) do |f| %h4 Team %p = @teams.one? ? 'The team' : 'Select the team' @@ -42,5 +42,5 @@ %hr .clearfix .pull-right - = link_to 'Cancel', edit_namespace_project_service_path(@project.namespace, @project, @service), class: 'btn btn-lg' + = link_to 'Cancel', edit_project_service_path(@project, @service), class: 'btn btn-lg' = f.submit 'Install', class: 'btn btn-save btn-lg' diff --git a/app/views/projects/merge_requests/_head.html.haml b/app/views/projects/merge_requests/_head.html.haml index b7f73fe5339..1e505222887 100644 --- a/app/views/projects/merge_requests/_head.html.haml +++ b/app/views/projects/merge_requests/_head.html.haml @@ -4,18 +4,18 @@ .nav-links.sub-nav.scrolling-tabs %ul{ class: (container_class) } = nav_link(controller: :merge_requests) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests' do + = link_to project_merge_requests_path(@project), title: 'Merge Requests' do %span List - if project_nav_tab? :labels = nav_link(controller: :labels) do - = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do + = link_to project_labels_path(@project), title: 'Labels' do %span Labels - if project_nav_tab? :milestones = nav_link(controller: :milestones) do - = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do + = link_to project_milestones_path(@project), title: 'Milestones' do %span Milestones diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 3599f2271b5..0a1ebcb8124 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -23,7 +23,7 @@ - if merge_request.milestone %span.issuable-milestone.hidden-xs - = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do + = link_to project_merge_requests_path(merge_request.project, milestone_title: merge_request.milestone.title) do = icon('clock-o') = merge_request.milestone.title - if merge_request.target_project.default_branch != merge_request.target_branch diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml index d9428b8562e..a2e819fb3a7 100644 --- a/app/views/projects/merge_requests/_mr_title.html.haml +++ b/app/views/projects/merge_requests/_mr_title.html.haml @@ -1,3 +1,5 @@ +- can_update_merge_request = can?(current_user, :update_merge_request, @merge_request) + - if @merge_request.closed_without_fork? .alert.alert-danger %p The source project of this merge request has been removed. @@ -15,21 +17,24 @@ .issuable-meta = issuable_meta(@merge_request, @project, "Merge request") - - if can?(current_user, :update_merge_request, @merge_request) - .issuable-actions - .clearfix.issue-btn-group.dropdown - %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ type: "button", data: { toggle: "dropdown" } } - Options - = icon('caret-down') - .dropdown-menu.dropdown-menu-align-right.hidden-lg - %ul + .issuable-actions + .clearfix.issue-btn-group.dropdown + %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ type: "button", data: { toggle: "dropdown" } } + Options + = icon('caret-down') + .dropdown-menu.dropdown-menu-align-right.hidden-lg + %ul + - if can_update_merge_request + %li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'issuable-edit' + - unless current_user == @merge_request.author + %li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)) + - if can_update_merge_request %li{ class: merge_request_button_visibility(@merge_request, true) } = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request' %li{ class: merge_request_button_visibility(@merge_request, false) } = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request' - %li - = link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'issuable-edit' - = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{merge_request_button_visibility(@merge_request, true)}", title: 'Close merge request' - = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen reopen-mr-link #{merge_request_button_visibility(@merge_request, false)}", title: 'Reopen merge request' - = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "hidden-xs hidden-sm btn btn-grouped issuable-edit" do - Edit + + - if can_update_merge_request + = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "hidden-xs hidden-sm btn btn-grouped issuable-edit" + + = render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request diff --git a/app/views/projects/merge_requests/_pipelines.html.haml b/app/views/projects/merge_requests/_pipelines.html.haml index 2f1dbe87619..473b7b919c8 100644 --- a/app/views/projects/merge_requests/_pipelines.html.haml +++ b/app/views/projects/merge_requests/_pipelines.html.haml @@ -1,4 +1,4 @@ -- endpoint_path = local_assigns[:endpoint] || pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :json) +- endpoint_path = local_assigns[:endpoint] || pipelines_project_merge_request_path(@project, @merge_request, format: :json) - disable_initialization = local_assigns.fetch(:disable_initialization, false) = render 'projects/commit/pipelines_list', endpoint: endpoint_path, disable_initialization: disable_initialization diff --git a/app/views/projects/merge_requests/conflicts.html.haml b/app/views/projects/merge_requests/conflicts.html.haml index f016b9c13b3..454bc359b6b 100644 --- a/app/views/projects/merge_requests/conflicts.html.haml +++ b/app/views/projects/merge_requests/conflicts.html.haml @@ -10,8 +10,8 @@ = render 'shared/issuable/sidebar', issuable: @merge_request -#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request, format: :json), - resolve_conflicts_path: resolve_conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request) } } +#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_project_merge_request_path(@merge_request.project, @merge_request, format: :json), + resolve_conflicts_path: resolve_conflicts_project_merge_request_path(@merge_request.project, @merge_request) } } .loading{ "v-if" => "isLoading" } %i.fa.fa-spinner.fa-spin diff --git a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml index e675e1830d0..13026b7566a 100644 --- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml +++ b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml @@ -13,4 +13,4 @@ %button.btn.btn-success.js-submit-button{ type: "button", "@click" => "commit()", ":disabled" => "!readyToCommit" } %span {{commitButtonText}} .col-xs-6.text-right - = link_to "Cancel", namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request), class: "btn btn-cancel" + = link_to "Cancel", project_merge_request_path(@merge_request.project, @merge_request), class: "btn btn-cancel" diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml index f016b9c13b3..454bc359b6b 100644 --- a/app/views/projects/merge_requests/conflicts/show.html.haml +++ b/app/views/projects/merge_requests/conflicts/show.html.haml @@ -10,8 +10,8 @@ = render 'shared/issuable/sidebar', issuable: @merge_request -#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request, format: :json), - resolve_conflicts_path: resolve_conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request) } } +#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_project_merge_request_path(@merge_request.project, @merge_request, format: :json), + resolve_conflicts_path: resolve_conflicts_project_merge_request_path(@merge_request.project, @merge_request) } } .loading{ "v-if" => "isLoading" } %i.fa.fa-spinner.fa-spin diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index 7cda326afef..4e5aae496b1 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -1,7 +1,7 @@ %h3.page-title New Merge Request -= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: namespace_project_new_merge_request_path(@project.namespace, @project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f| .hide.alert.alert-danger.mr-compare-errors .merge-request-branches.row .col-md-6 @@ -69,7 +69,7 @@ :javascript new Compare({ - targetProjectUrl: "#{namespace_project_new_merge_request_update_branches_path(@source_project.namespace, @source_project)}", - sourceBranchUrl: "#{namespace_project_new_merge_request_branch_from_path(@source_project.namespace, @source_project)}", - targetBranchUrl: "#{namespace_project_new_merge_request_branch_to_path(@source_project.namespace, @source_project)}" + targetProjectUrl: "#{project_new_merge_request_update_branches_path(@source_project)}", + sourceBranchUrl: "#{project_new_merge_request_branch_from_path(@source_project)}", + targetBranchUrl: "#{project_new_merge_request_branch_to_path(@source_project)}" }); diff --git a/app/views/projects/merge_requests/diffs/_versions.html.haml b/app/views/projects/merge_requests/diffs/_versions.html.haml index 0999b95c9c9..9f7152b9824 100644 --- a/app/views/projects/merge_requests/diffs/_versions.html.haml +++ b/app/views/projects/merge_requests/diffs/_versions.html.haml @@ -77,7 +77,7 @@ = icon('info-circle') Selected versions have different base commits. Changes will include - = link_to namespace_project_compare_path(@project.namespace, @project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do + = link_to project_compare_path(@project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do new commits from = succeed '.' do @@ -94,4 +94,4 @@ of the diff. .pull-right - = link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm' + = link_to 'Show latest version', diffs_project_merge_request_path(@project, @merge_request), class: 'btn btn-sm' diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 6fe44ba3c3d..bfeb746ee83 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -1,7 +1,7 @@ - @no_container = true - @can_bulk_update = can?(current_user, :admin_merge_request, @project) - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) -- new_merge_request_path = namespace_project_new_merge_request_path(merge_project.namespace, merge_project) if merge_project +- new_merge_request_path = project_new_merge_request_path(merge_project) if merge_project - page_title "Merge Requests" - unless @project.default_issues_tracker? diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index dbbf1bde088..2efc1d68190 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -35,21 +35,21 @@ .nav-links.scrolling-tabs %ul.merge-request-tabs %li.notes-tab - = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'show', toggle: 'tab' } do + = link_to project_merge_request_path(@project, @merge_request), data: { target: 'div#notes', action: 'show', toggle: 'tab' } do Discussion %span.badge= @merge_request.related_notes.user.count - if @merge_request.source_project %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do + = link_to commits_project_merge_request_path(@project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do Commits %span.badge= @commits_count - if @pipelines.any? %li.pipelines-tab - = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do + = link_to pipelines_project_merge_request_path(@project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do Pipelines - %span.badge= @pipelines.size + %span.badge.js-pipelines-mr-count= @pipelines.size %li.diffs-tab - = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do + = link_to diffs_project_merge_request_path(@project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do Changes %span.badge= @merge_request.diff_size #resolve-count-app.line-resolve-all-container.prepend-top-10{ "v-cloak" => true } @@ -76,7 +76,7 @@ -# This tab is always loaded via AJAX #pipelines.pipelines.tab-pane - if @pipelines.any? - = render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) + = render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_project_merge_request_path(@project, @merge_request) #diffs.diffs.tab-pane -# This tab is always loaded via AJAX diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 9a95b2a82ff..2e74b1b83cb 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -19,7 +19,7 @@ .form-actions - if @milestone.new_record? = f.submit 'Create milestone', class: "btn-create btn" - = link_to "Cancel", namespace_project_milestones_path(@project.namespace, @project), class: "btn btn-cancel" + = link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel" - else = f.submit 'Save changes', class: "btn-save btn" - = link_to "Cancel", namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-cancel" + = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn btn-cancel" diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml index 77b566db6b6..bc82b45f902 100644 --- a/app/views/projects/milestones/_milestone.html.haml +++ b/app/views/projects/milestones/_milestone.html.haml @@ -1,5 +1,5 @@ = render 'shared/milestones/milestone', - milestone_path: namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), - issues_path: namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title), - merge_requests_path: namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title), + milestone_path: project_milestone_path(milestone.project, milestone), + issues_path: project_issues_path(milestone.project, milestone_title: milestone.title), + merge_requests_path: project_merge_requests_path(milestone.project, milestone_title: milestone.title), milestone: milestone diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index e1096bd1d67..e53fcd6e425 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -9,7 +9,7 @@ .nav-controls = render 'shared/milestones_sort_dropdown' - if can?(current_user, :admin_milestone, @project) - = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New milestone' do + = link_to new_project_milestone_path(@project), class: 'btn btn-new', title: 'New milestone' do New milestone .milestones diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 4b692aba11c..0bf0e11c107 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -23,14 +23,14 @@ .milestone-buttons - if can?(current_user, :admin_milestone, @project) - if @milestone.active? - = link_to 'Close milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-nr btn-grouped" + = link_to 'Close milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-nr btn-grouped" - else - = link_to 'Reopen milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped" + = link_to 'Reopen milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped" - = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped btn-nr" do + = link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-grouped btn-nr" do Edit - = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do + = link_to project_milestone_path(@project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do Delete %a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" } diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index ed6077f6c6b..e8c26636be9 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -6,7 +6,7 @@ %div{ class: container_class } .project-network .controls - = form_tag namespace_project_network_path(@project.namespace, @project, @id), method: :get, class: 'form-inline network-form' do |f| + = form_tag project_network_path(@project, @id), method: :get, class: 'form-inline network-form' do |f| = text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Git revision", class: 'search-input form-control input-mx-250 search-sha' = button_tag class: 'btn btn-success' do = icon('search') diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 7b8be58554a..b0b7575f0d1 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -9,8 +9,9 @@ .col-lg-3.profile-settings-sidebar %h4.prepend-top-0 New project - %p - Create or Import your project from popular Git services + - if import_sources_enabled? + %p + Create or Import your project from popular Git services .col-lg-9 = form_for @project, html: { class: 'new_project' } do |f| .row diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml index 1cf286ddc40..ba5845877e5 100644 --- a/app/views/projects/no_repo.html.haml +++ b/app/views/projects/no_repo.html.haml @@ -9,12 +9,12 @@ %hr .no-repo-actions - = link_to namespace_project_repository_path(@project.namespace, @project), method: :post, class: 'btn btn-primary' do + = link_to project_repository_path(@project), method: :post, class: 'btn btn-primary' do #{ _('Create empty bare repository') } %strong.prepend-left-10.append-right-10 or - = link_to new_namespace_project_import_path(@project.namespace, @project), class: 'btn' do + = link_to new_project_import_path(@project), class: 'btn' do #{ _('Import repository') } - if can? current_user, :remove_project, @project diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml index 42d9ef5ccba..7d6c30b7f8d 100644 --- a/app/views/projects/pages/_destroy.haml +++ b/app/views/projects/pages/_destroy.haml @@ -7,6 +7,6 @@ %p Removing the pages will prevent from exposing them to outside world. .form-actions - = link_to 'Remove pages', namespace_project_pages_path(@project.namespace, @project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove" + = link_to 'Remove pages', project_pages_path(@project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove" - else .nothing-here-block Only the project owner can remove pages diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml index 4f2dd1a1398..a85cda407af 100644 --- a/app/views/projects/pages/_list.html.haml +++ b/app/views/projects/pages/_list.html.haml @@ -6,8 +6,8 @@ - @domains.each do |domain| %li .pull-right - = link_to 'Details', namespace_project_pages_domain_path(@project.namespace, @project, domain), class: "btn btn-sm btn-grouped" - = link_to 'Remove', namespace_project_pages_domain_path(@project.namespace, @project, domain), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" + = link_to 'Details', project_pages_domain_path(@project, domain), class: "btn btn-sm btn-grouped" + = link_to 'Remove', project_pages_domain_path(@project, domain), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" .clearfix %span= link_to domain.domain, domain.url %p diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index b22a54d75c8..098b0ef56ef 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -5,7 +5,7 @@ Pages - if can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https) - = link_to new_namespace_project_pages_domain_path(@project.namespace, @project), class: 'btn btn-new pull-right', title: 'New Domain' do + = link_to new_project_pages_domain_path(@project), class: 'btn btn-new pull-right', title: 'New Domain' do %i.fa.fa-plus New Domain diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml index fc7fa5c1876..857ae00d0ab 100644 --- a/app/views/projects/pipeline_schedules/_form.html.haml +++ b/app/views/projects/pipeline_schedules/_form.html.haml @@ -24,6 +24,14 @@ = f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true .form-group .col-md-9 + %label.label-light + #{ s_('PipelineSchedules|Variables') } + %ul.js-pipeline-variable-list.pipeline-variable-list + - @schedule.variables.each do |variable| + = render 'variable_row', id: variable.id, key: variable.key, value: variable.value + = render 'variable_row' + .form-group + .col-md-9 = f.label :active, s_('PipelineSchedules|Activated'), class: 'label-light' %div = f.check_box :active, required: false, value: @schedule.active? diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml index 966d6cd8495..97c0407a01d 100644 --- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml +++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml @@ -9,7 +9,7 @@ %td - if pipeline_schedule.last_pipeline .status-icon-container{ class: "ci-status-icon-#{pipeline_schedule.last_pipeline.status}" } - = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline_schedule.last_pipeline.id) do + = link_to project_pipeline_path(@project, pipeline_schedule.last_pipeline.id) do = ci_icon_for_status(pipeline_schedule.last_pipeline.status) %span ##{pipeline_schedule.last_pipeline.id} - else @@ -26,7 +26,7 @@ = pipeline_schedule.owner&.name %td .pull-right.btn-group - - if can?(current_user, :update_pipeline_schedule, @project) && !pipeline_schedule.owned_by?(current_user) + - if can?(current_user, :update_pipeline_schedule, pipeline_schedule) = link_to take_ownership_pipeline_schedule_path(pipeline_schedule), method: :post, title: s_('PipelineSchedules|Take ownership'), class: 'btn' do = s_('PipelineSchedules|Take ownership') - if can?(current_user, :update_pipeline_schedule, pipeline_schedule) diff --git a/app/views/projects/pipeline_schedules/_variable_row.html.haml b/app/views/projects/pipeline_schedules/_variable_row.html.haml new file mode 100644 index 00000000000..564cb5d1ca9 --- /dev/null +++ b/app/views/projects/pipeline_schedules/_variable_row.html.haml @@ -0,0 +1,17 @@ +- id = local_assigns.fetch(:id, nil) +- key = local_assigns.fetch(:key, "") +- value = local_assigns.fetch(:value, "") +%li.js-row.pipeline-variable-row{ data: { is_persisted: "#{!id.nil?}" } } + .pipeline-variable-row-body + %input{ type: "hidden", name: "schedule[variables_attributes][][id]", value: id } + %input.js-destroy-input{ type: "hidden", name: "schedule[variables_attributes][][_destroy]" } + %input.js-user-input.pipeline-variable-key-input.form-control{ type: "text", + name: "schedule[variables_attributes][][key]", + value: key, + placeholder: s_('PipelineSchedules|Input variable key') } + %textarea.js-user-input.pipeline-variable-value-input.form-control{ rows: 1, + name: "schedule[variables_attributes][][value]", + placeholder: s_('PipelineSchedules|Input variable value') } + = value + %button.js-row-remove-button.pipeline-variable-row-remove-button{ 'aria-label': s_('PipelineSchedules|Remove variable row') } + %i.fa.fa-minus-circle{ 'aria-hidden': "true" } diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml index c296152e54f..c4ee064ac43 100644 --- a/app/views/projects/pipeline_schedules/index.html.haml +++ b/app/views/projects/pipeline_schedules/index.html.haml @@ -12,9 +12,10 @@ - schedule_path_proc = ->(scope) { pipeline_schedules_path(@project, scope: scope) } = render "tabs", schedule_path_proc: schedule_path_proc, all_schedules: @all_schedules, scope: @scope - .nav-controls - = link_to new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create' do - %span= _('New schedule') + - if can?(current_user, :create_pipeline_schedule, @project) + .nav-controls + = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do + %span= _('New schedule') - if @schedules.present? %ul.content-list diff --git a/app/views/projects/pipelines/_head.html.haml b/app/views/projects/pipelines/_head.html.haml index d2f0cb0806f..ee2f236cec4 100644 --- a/app/views/projects/pipelines/_head.html.haml +++ b/app/views/projects/pipelines/_head.html.haml @@ -29,6 +29,6 @@ - if @project.feature_available?(:builds, current_user) && !@project.empty_repo? = nav_link(path: 'pipelines#charts') do - = link_to charts_namespace_project_pipelines_path(@project.namespace, @project), title: 'Charts', class: 'shortcuts-pipelines-charts' do + = link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do %span Charts diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 673c3370b62..f5149306734 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -26,10 +26,10 @@ .well-segment.branch-info .icon-container.commit-icon = custom_icon("icon_commit") - = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @pipeline.sha), class: "commit-sha js-details-short" + = link_to @commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short" = link_to("#", class: "js-details-expand hidden-xs hidden-sm") do %span.text-expander \... %span.js-details-content.hide - = link_to @pipeline.sha, namespace_project_commit_path(@project.namespace, @project, @pipeline.sha), class: "commit-sha commit-hash-full" + = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard") diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 85550e8fd32..ad61f033a1c 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -3,15 +3,15 @@ .tabs-holder %ul.pipelines-tabs.nav-links.no-top.no-bottom %li.js-pipeline-tab-link - = link_to namespace_project_pipeline_path(@project.namespace, @project, @pipeline), data: { target: 'div#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do + = link_to project_pipeline_path(@project, @pipeline), data: { target: 'div#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do Pipeline %li.js-builds-tab-link - = link_to builds_namespace_project_pipeline_path(@project.namespace, @project, @pipeline), data: {target: 'div#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do + = link_to builds_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do Jobs %span.badge.js-builds-counter= pipeline.statuses.count - if failed_builds.present? %li.js-failures-tab-link - = link_to failures_namespace_project_pipeline_path(@project.namespace, @project, @pipeline), data: {target: 'div#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do + = link_to failures_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do Failed Jobs %span.badge.js-failures-counter= failed_builds.count diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 38237d2d97d..c1729850cf4 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -2,10 +2,10 @@ - page_title "Pipelines" = render "projects/pipelines/head" -#pipelines-list-vue{ data: { endpoint: namespace_project_pipelines_path(@project.namespace, @project, format: :json), +#pipelines-list-vue{ data: { endpoint: project_pipelines_path(@project, format: :json), "css-class" => container_class, "help-page-path" => help_page_path('ci/quick_start/README'), - "new-pipeline-path" => new_namespace_project_pipeline_path(@project.namespace, @project), + "new-pipeline-path" => new_project_pipeline_path(@project), "can-create-pipeline" => can?(current_user, :create_pipeline, @project).to_s, "all-path" => project_pipelines_path(@project), "pending-path" => project_pipelines_path(@project, scope: :pending), diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index 71a8e490c3e..308f2611e02 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -4,7 +4,7 @@ New Pipeline %hr -= form_for @pipeline, as: :pipeline, url: namespace_project_pipelines_path(@project.namespace, @project), html: { id: "new-pipeline-form", class: "form-horizontal js-new-pipeline-form js-requires-input" } do |f| += form_for @pipeline, as: :pipeline, url: project_pipelines_path(@project), html: { id: "new-pipeline-form", class: "form-horizontal js-new-pipeline-form js-requires-input" } do |f| = form_errors(@pipeline) .form-group = f.label :ref, 'Create for', class: 'control-label' @@ -17,7 +17,7 @@ .help-block Existing branch name, tag .form-actions = f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3 - = link_to 'Cancel', namespace_project_pipelines_path(@project.namespace, @project), class: 'btn btn-cancel' + = link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-cancel' :javascript var availableRefs = #{@project.repository.ref_names.to_json}; diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index b39453a50fb..63f85fc69a2 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -8,7 +8,7 @@ = render "projects/pipelines/with_tabs", pipeline: @pipeline -.js-pipeline-details-vue{ data: { endpoint: namespace_project_pipeline_path(@project.namespace, @project, @pipeline, format: :json) } } +.js-pipeline-details-vue{ data: { endpoint: project_pipeline_path(@project, @pipeline, format: :json) } } - content_for :page_specific_javascripts do = webpack_bundle_tag('common_vue') diff --git a/app/views/projects/pipelines_settings/_show.html.haml b/app/views/projects/pipelines_settings/_show.html.haml index 580129ca809..255d7ef38e0 100644 --- a/app/views/projects/pipelines_settings/_show.html.haml +++ b/app/views/projects/pipelines_settings/_show.html.haml @@ -3,7 +3,7 @@ %h4.prepend-top-0 Pipelines .col-lg-8 - = form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project) do |f| + = form_for @project, url: project_pipelines_settings_path(@project) do |f| %fieldset.builds-feature - unless @repository.gitlab_ci_yml .form-group @@ -47,6 +47,14 @@ %hr .form-group + = f.label :ci_config_path, 'Custom CI config path', class: 'label-light' + = f.text_field :ci_config_path, class: 'form-control', placeholder: '.gitlab-ci.yml' + %p.help-block + The path to CI config file. Defaults to <code>.gitlab-ci.yml</code> + = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'custom-ci-config-path'), target: '_blank' + + %hr + .form-group .checkbox = f.label :public_builds do = f.check_box :public_builds diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml deleted file mode 100644 index c7996077bc7..00000000000 --- a/app/views/projects/project_members/_group_members.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -.panel.panel-default - .panel-heading - Group members with access to - %strong= @group.name - %span.badge= members.size - - if can?(current_user, :admin_group_member, @group) - .controls - = link_to 'Manage group members', - group_group_members_path(@group), - class: 'btn' - %ul.content-list - = render partial: 'shared/members/member', - collection: members.limit(20), - as: :member, - locals: { show_controls: false } - - if members.size > 20 - %li - and #{members.count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(@group)} diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml index 8bf2246662a..bf5b11ea30c 100644 --- a/app/views/projects/project_members/_new_project_member.html.haml +++ b/app/views/projects/project_members/_new_project_member.html.haml @@ -1,6 +1,6 @@ .row .col-sm-12 - = form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'users-project-form' } do |f| + = form_for @project_member, as: :project_member, url: project_project_members_path(@project), html: { class: 'users-project-form' } do |f| .form-group = label_tag :user_ids, "Select members to invite", class: "label-light" = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite") @@ -18,4 +18,4 @@ = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date' %i.clear-icon.js-clear-input = f.submit "Add to project", class: "btn btn-create" - = link_to "Import", import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-default", title: "Import members from another project" + = link_to "Import", import_project_project_members_path(@project), class: "btn btn-default", title: "Import members from another project" diff --git a/app/views/projects/project_members/_new_shared_group.html.haml b/app/views/projects/project_members/_new_shared_group.html.haml index 643569db646..c10ef648a8f 100644 --- a/app/views/projects/project_members/_new_shared_group.html.haml +++ b/app/views/projects/project_members/_new_shared_group.html.haml @@ -1,6 +1,6 @@ .row .col-sm-12 - = form_tag namespace_project_group_links_path(@project.namespace, @project), class: 'js-requires-input', method: :post do + = form_tag project_group_links_path(@project), class: 'js-requires-input', method: :post do .form-group = label_tag :link_group_id, "Select a group to share with", class: "label-light" = groups_select_tag(:link_group_id, data: { skip_groups: @skip_groups }, class: "input-clamp", required: true) diff --git a/app/views/projects/project_members/_shared_group_members.html.haml b/app/views/projects/project_members/_shared_group_members.html.haml deleted file mode 100644 index 7902ddb1ae9..00000000000 --- a/app/views/projects/project_members/_shared_group_members.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -- @project_group_links.each do |group_links| - - shared_group = group_links.group - - shared_group_members = shared_group.members - - shared_group_users_count = shared_group_members.size - .panel.panel-default - .panel-heading - Shared with - %strong= shared_group.name - group, members with - %strong= group_links.human_access - role (#{shared_group_users_count}) - - if can?(current_user, :admin_group, shared_group) - .panel-head-actions - = link_to group_group_members_path(shared_group), class: 'btn btn-sm' do - %i.fa.fa-pencil-square-o - Edit group members - %ul.content-list - = render partial: 'shared/members/member', - collection: shared_group_members.order(access_level: :desc).limit(20), - as: :member, - locals: { show_controls: false, show_roles: false } - - if shared_group_users_count > 20 - %li - and #{shared_group_users_count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(shared_group)} diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index 7b1a26043e1..e71d58ec26d 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -5,11 +5,11 @@ %strong #{@project.name} %span.badge= @project_members.total_count - = form_tag namespace_project_settings_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do + = form_tag project_project_members_path(@project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } = icon("search") = render 'shared/members/sort_dropdown' - %ul.content-list + %ul.content-list.members-list = render partial: 'shared/members/member', collection: members, as: :member diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml index 42ce4f8001b..f6ca8d5a921 100644 --- a/app/views/projects/project_members/import.html.haml +++ b/app/views/projects/project_members/import.html.haml @@ -5,11 +5,11 @@ %p.light Only project members will be imported. Group members will be skipped. %hr -= form_tag apply_import_namespace_project_project_members_path(@project.namespace, @project), method: 'post', class: 'form-horizontal' do += form_tag apply_import_project_project_members_path(@project), method: 'post', class: 'form-horizontal' do .form-group = label_tag :source_project_id, "Project", class: 'control-label' .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true) .form-actions = button_tag 'Import project members', class: "btn btn-create" - = link_to "Cancel", namespace_project_settings_members_path(@project.namespace, @project), class: "btn btn-cancel" + = link_to "Cancel", project_project_members_path(@project), class: "btn btn-cancel" diff --git a/app/views/projects/project_members/_index.html.haml b/app/views/projects/project_members/index.html.haml index fa99610c0be..25153fd0b6f 100644 --- a/app/views/projects/project_members/_index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,6 +1,8 @@ +- page_title "Members" + .row.prepend-top-default - .col-lg-4.settings-sidebar - %h4.prepend-top-0 + .col-lg-12 + %h4 Project members - if can?(current_user, :admin_project_member, @project) %p @@ -13,7 +15,6 @@ %i Masters or %i Owners - .col-lg-8 .light - if can?(current_user, :admin_project_member, @project) %ul.nav-links.project-member-tabs{ role: 'tablist' } diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml index cf0db943865..5377d745371 100644 --- a/app/views/projects/protected_branches/_branches_list.html.haml +++ b/app/views/projects/protected_branches/_branches_list.html.haml @@ -1,28 +1,4 @@ -.panel.panel-default.protected-branches-list - - if @protected_branches.empty? - .panel-heading - %h3.panel-title - Protected branch (#{@protected_branches.size}) - %p.settings-message.text-center - There are currently no protected branches, protect a branch with the form above. - - else - - can_admin_project = can?(current_user, :admin_project, @project) +- can_admin_project = can?(current_user, :admin_project, @project) - %table.table.table-bordered - %colgroup - %col{ width: "25%" } - %col{ width: "30%" } - %col{ width: "25%" } - %col{ width: "20%" } - %thead - %tr - %th Protected branch (#{@protected_branches.size}) - %th Last commit - %th Allowed to merge - %th Allowed to push - - if can_admin_project - %th - %tbody - = render partial: 'projects/protected_branches/protected_branch', collection: @protected_branches, locals: { can_admin_project: can_admin_project} - - = paginate @protected_branches, theme: 'gitlab' += render layout: 'projects/protected_branches/shared/branches_list', locals: { can_admin_project: can_admin_project } do + = render partial: 'projects/protected_branches/protected_branch', collection: @protected_branches, locals: { can_admin_project: can_admin_project} diff --git a/app/views/projects/protected_branches/_create_protected_branch.html.haml b/app/views/projects/protected_branches/_create_protected_branch.html.haml index 99bc2516366..98d56a3e5c5 100644 --- a/app/views/projects/protected_branches/_create_protected_branch.html.haml +++ b/app/views/projects/protected_branches/_create_protected_branch.html.haml @@ -1,41 +1,14 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f| - .panel.panel-default - .panel-heading - %h3.panel-title - Protect a branch - .panel-body - .form-horizontal - = form_errors(@protected_branch) - .form-group - = f.label :name, class: 'col-md-2 text-right' do - Branch: - .col-md-10 - = render partial: "projects/protected_branches/dropdown", locals: { f: f } - .help-block - = link_to 'Wildcards', help_page_path('user/project/protected_branches', anchor: 'wildcard-protected-branches') - such as - %code *-stable - or - %code production/* - are supported - .form-group - %label.col-md-2.text-right{ for: 'merge_access_levels_attributes' } - Allowed to merge: - .col-md-10 - .merge_access_levels-container - = dropdown_tag('Select', - options: { toggle_class: 'js-allowed-to-merge wide', - dropdown_class: 'dropdown-menu-selectable capitalize-header', - data: { field_name: 'protected_branch[merge_access_levels_attributes][0][access_level]', input_id: 'merge_access_levels_attributes' }}) - .form-group - %label.col-md-2.text-right{ for: 'push_access_levels_attributes' } - Allowed to push: - .col-md-10 - .push_access_levels-container - = dropdown_tag('Select', - options: { toggle_class: 'js-allowed-to-push wide', - dropdown_class: 'dropdown-menu-selectable capitalize-header', - data: { field_name: 'protected_branch[push_access_levels_attributes][0][access_level]', input_id: 'push_access_levels_attributes' }}) +- content_for :merge_access_levels do + .merge_access_levels-container + = dropdown_tag('Select', + options: { toggle_class: 'js-allowed-to-merge wide', + dropdown_class: 'dropdown-menu-selectable capitalize-header', + data: { field_name: 'protected_branch[merge_access_levels_attributes][0][access_level]', input_id: 'merge_access_levels_attributes' }}) +- content_for :push_access_levels do + .push_access_levels-container + = dropdown_tag('Select', + options: { toggle_class: 'js-allowed-to-push wide', + dropdown_class: 'dropdown-menu-selectable capitalize-header', + data: { field_name: 'protected_branch[push_access_levels_attributes][0][access_level]', input_id: 'push_access_levels_attributes' }}) - .panel-footer - = f.submit 'Protect', class: 'btn-create btn', disabled: true += render 'projects/protected_branches/shared/create_protected_branch' diff --git a/app/views/projects/protected_branches/_index.html.haml b/app/views/projects/protected_branches/_index.html.haml index 5d2422bdf54..2f30fe33a97 100644 --- a/app/views/projects/protected_branches/_index.html.haml +++ b/app/views/projects/protected_branches/_index.html.haml @@ -1,26 +1,10 @@ -- expanded = Rails.env.test? - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('protected_branches') -%section.settings - .settings-header - %h4 - Protected Branches - %button.btn.js-settings-toggle - = expanded ? 'Collapse' : 'Expand' - %p - Keep stable branches secure and force developers to use merge requests. - .settings-content.no-animate{ class: ('expanded' if expanded) } - %p - By default, protected branches are designed to: - %ul - %li prevent their creation, if not already created, from everybody except Masters - %li prevent pushes from everybody except Masters - %li prevent <strong>anyone</strong> from force pushing to the branch - %li prevent <strong>anyone</strong> from deleting the branch - %p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}. +- content_for :create_protected_branch do + = render 'projects/protected_branches/create_protected_branch' - - if can? current_user, :admin_project, @project - = render 'projects/protected_branches/create_protected_branch' +- content_for :branches_list do + = render "projects/protected_branches/branches_list" - = render "projects/protected_branches/branches_list" += render 'projects/protected_branches/shared/index' diff --git a/app/views/projects/protected_branches/_protected_branch.html.haml b/app/views/projects/protected_branches/_protected_branch.html.haml index 0f80de94392..b12ae995ece 100644 --- a/app/views/projects/protected_branches/_protected_branch.html.haml +++ b/app/views/projects/protected_branches/_protected_branch.html.haml @@ -1,22 +1,2 @@ -%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } } - %td - %span.ref-name= protected_branch.name - - - if @project.root_ref?(protected_branch.name) - %span.label.label-info.prepend-left-5 default - %td - - if protected_branch.wildcard? - - matching_branches = protected_branch.matching(repository.branches) - = link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) - - else - - if commit = protected_branch.commit - = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit-sha') - = time_ago_with_tooltip(commit.committed_date) - - else - (branch was removed from repository) - += render layout: 'projects/protected_branches/shared/protected_branch', locals: { protected_branch: protected_branch } do = render partial: 'projects/protected_branches/update_protected_branch', locals: { protected_branch: protected_branch } - - - if can_admin_project - %td - = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning' diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml new file mode 100644 index 00000000000..5c00bb6883c --- /dev/null +++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml @@ -0,0 +1,28 @@ +.panel.panel-default.protected-branches-list + - if @protected_branches.empty? + .panel-heading + %h3.panel-title + Protected branch (#{@protected_branches.size}) + %p.settings-message.text-center + There are currently no protected branches, protect a branch with the form above. + - else + %table.table.table-bordered + %colgroup + %col{ width: "20%" } + %col{ width: "20%" } + %col{ width: "20%" } + %col{ width: "20%" } + - if can_admin_project + %col + %thead + %tr + %th Protected branch (#{@protected_branches.size}) + %th Last commit + %th Allowed to merge + %th Allowed to push + - if can_admin_project + %th + %tbody + = yield + + = paginate @protected_branches, theme: 'gitlab' diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml new file mode 100644 index 00000000000..b619fa57e05 --- /dev/null +++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml @@ -0,0 +1,33 @@ += form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f| + .panel.panel-default + .panel-heading + %h3.panel-title + Protect a branch + .panel-body + .form-horizontal + = form_errors(@protected_branch) + .form-group + = f.label :name, class: 'col-md-2 text-right' do + Branch: + .col-md-10 + = render partial: "projects/protected_branches/shared/dropdown", locals: { f: f } + .help-block + = link_to 'Wildcards', help_page_path('user/project/protected_branches', anchor: 'wildcard-protected-branches') + such as + %code *-stable + or + %code production/* + are supported + .form-group + %label.col-md-2.text-right{ for: 'merge_access_levels_attributes' } + Allowed to merge: + .col-md-10 + = yield :merge_access_levels + .form-group + %label.col-md-2.text-right{ for: 'push_access_levels_attributes' } + Allowed to push: + .col-md-10 + = yield :push_access_levels + + .panel-footer + = f.submit 'Protect', class: 'btn-create btn', disabled: true diff --git a/app/views/projects/protected_branches/_dropdown.html.haml b/app/views/projects/protected_branches/shared/_dropdown.html.haml index 6e9c473494e..6e9c473494e 100644 --- a/app/views/projects/protected_branches/_dropdown.html.haml +++ b/app/views/projects/protected_branches/shared/_dropdown.html.haml diff --git a/app/views/projects/protected_branches/shared/_index.html.haml b/app/views/projects/protected_branches/shared/_index.html.haml new file mode 100644 index 00000000000..6a47cbdf724 --- /dev/null +++ b/app/views/projects/protected_branches/shared/_index.html.haml @@ -0,0 +1,24 @@ +- expanded = Rails.env.test? + +%section.settings + .settings-header + %h4 + Protected Branches + %button.btn.js-settings-toggle + = expanded ? 'Collapse' : 'Expand' + %p + Keep stable branches secure and force developers to use merge requests. + .settings-content.no-animate{ class: ('expanded' if expanded) } + %p + By default, protected branches are designed to: + %ul + %li prevent their creation, if not already created, from everybody except Masters + %li prevent pushes from everybody except Masters + %li prevent <strong>anyone</strong> from force pushing to the branch + %li prevent <strong>anyone</strong> from deleting the branch + %p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}. + + - if can? current_user, :admin_project, @project + = content_for :create_protected_branch + + = content_for :branches_list diff --git a/app/views/projects/protected_branches/_matching_branch.html.haml b/app/views/projects/protected_branches/shared/_matching_branch.html.haml index 27896272733..98793d632e6 100644 --- a/app/views/projects/protected_branches/_matching_branch.html.haml +++ b/app/views/projects/protected_branches/shared/_matching_branch.html.haml @@ -6,5 +6,5 @@ %span.label.label-info.prepend-left-5 default %td - commit = @project.commit(matching_branch.name) - = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit-sha') + = link_to(commit.short_id, project_commit_path(@project, commit.id), class: 'commit-sha') = time_ago_with_tooltip(commit.committed_date) diff --git a/app/views/projects/protected_branches/shared/_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_protected_branch.html.haml new file mode 100644 index 00000000000..10b81e42572 --- /dev/null +++ b/app/views/projects/protected_branches/shared/_protected_branch.html.haml @@ -0,0 +1,24 @@ +- can_admin_project = can?(current_user, :admin_project, @project) + +%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } } + %td + %span.ref-name= protected_branch.name + + - if @project.root_ref?(protected_branch.name) + %span.label.label-info.prepend-left-5 default + %td + - if protected_branch.wildcard? + - matching_branches = protected_branch.matching(repository.branches) + = link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) + - else + - if commit = protected_branch.commit + = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit-sha') + = time_ago_with_tooltip(commit.committed_date) + - else + (branch was removed from repository) + + = yield + + - if can_admin_project + %td + = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning' diff --git a/app/views/projects/protected_branches/show.html.haml b/app/views/projects/protected_branches/show.html.haml index a806a0756ec..1012ceefe93 100644 --- a/app/views/projects/protected_branches/show.html.haml +++ b/app/views/projects/protected_branches/show.html.haml @@ -19,7 +19,7 @@ %th Last commit %tbody - @matching_refs.each do |matching_branch| - = render partial: "matching_branch", object: matching_branch + = render partial: "projects/protected_branches/shared/matching_branch", object: matching_branch - else %p.settings-message.text-center Couldn't find any matching branches. diff --git a/app/views/projects/protected_tags/_create_protected_tag.html.haml b/app/views/projects/protected_tags/_create_protected_tag.html.haml index dd5b346d922..ea91e8af70e 100644 --- a/app/views/projects/protected_tags/_create_protected_tag.html.haml +++ b/app/views/projects/protected_tags/_create_protected_tag.html.haml @@ -1,32 +1,8 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @protected_tag], html: { class: 'new-protected-tag js-new-protected-tag' } do |f| - .panel.panel-default - .panel-heading - %h3.panel-title - Protect a tag - .panel-body - .form-horizontal - = form_errors(@protected_tag) - .form-group - = f.label :name, class: 'col-md-2 text-right' do - Tag: - .col-md-10.protected-tags-dropdown - = render partial: "projects/protected_tags/dropdown", locals: { f: f } - .help-block - = link_to 'Wildcards', help_page_path('user/project/protected_tags', anchor: 'wildcard-protected-tags') - such as - %code v* - or - %code *-release - are supported - .form-group - %label.col-md-2.text-right{ for: 'create_access_levels_attributes' } - Allowed to create: - .col-md-10 - .create_access_levels-container - = dropdown_tag('Select', - options: { toggle_class: 'js-allowed-to-create wide', - dropdown_class: 'dropdown-menu-selectable', - data: { field_name: 'protected_tag[create_access_levels_attributes][0][access_level]', input_id: 'create_access_levels_attributes' }}) +- content_for :create_access_levels do + .create_access_levels-container + = dropdown_tag('Select', + options: { toggle_class: 'js-allowed-to-create wide', + dropdown_class: 'dropdown-menu-selectable', + data: { field_name: 'protected_tag[create_access_levels_attributes][0][access_level]', input_id: 'create_access_levels_attributes' }}) - .panel-footer - = f.submit 'Protect', class: 'btn-create btn', disabled: true += render 'projects/protected_tags/shared/create_protected_tag' diff --git a/app/views/projects/protected_tags/_index.html.haml b/app/views/projects/protected_tags/_index.html.haml index 8250f692a69..955220562a6 100644 --- a/app/views/projects/protected_tags/_index.html.haml +++ b/app/views/projects/protected_tags/_index.html.haml @@ -1,26 +1,10 @@ -- expanded = Rails.env.test? - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('protected_tags') -%section.settings - .settings-header - %h4 - Protected Tags - %button.btn.js-settings-toggle - = expanded ? 'Collapse' : 'Expand' - %p - Limit access to creating and updating tags. - .settings-content.no-animate{ class: ('expanded' if expanded) } - %p - By default, protected tags are designed to: - %ul - %li Prevent tag creation by everybody except Masters - %li Prevent <strong>anyone</strong> from updating the tag - %li Prevent <strong>anyone</strong> from deleting the tag +- content_for :create_protected_tag do + = render 'projects/protected_tags/create_protected_tag' - %p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags"), class: "underlined-link"}. +- content_for :tag_list do + = render "projects/protected_tags/tags_list" - - if can? current_user, :admin_project, @project - = render 'projects/protected_tags/create_protected_tag' - - = render "projects/protected_tags/tags_list" += render 'projects/protected_tags/shared/index' diff --git a/app/views/projects/protected_tags/_protected_tag.html.haml b/app/views/projects/protected_tags/_protected_tag.html.haml index f11ce0483a9..da1f97c8d6a 100644 --- a/app/views/projects/protected_tags/_protected_tag.html.haml +++ b/app/views/projects/protected_tags/_protected_tag.html.haml @@ -1,22 +1,2 @@ -%tr.js-protected-tag-edit-form{ data: { url: namespace_project_protected_tag_path(@project.namespace, @project, protected_tag) } } - %td - %span.ref-name= protected_tag.name - - - if @project.root_ref?(protected_tag.name) - %span.label.label-info.prepend-left-5 default - %td - - if protected_tag.wildcard? - - matching_tags = protected_tag.matching(repository.tags) - = link_to pluralize(matching_tags.count, "matching tag"), namespace_project_protected_tag_path(@project.namespace, @project, protected_tag) - - else - - if commit = protected_tag.commit - = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit-sha') - = time_ago_with_tooltip(commit.committed_date) - - else - (tag was removed from repository) - += render layout: 'projects/protected_tags/shared/protected_tag', locals: { protected_tag: protected_tag } do = render partial: 'projects/protected_tags/update_protected_tag', locals: { protected_tag: protected_tag } - - - if can_admin_project - %td - = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_tag], data: { confirm: 'Tag will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning' diff --git a/app/views/projects/protected_tags/_tags_list.html.haml b/app/views/projects/protected_tags/_tags_list.html.haml index d432a5c9113..a6b18cc9f8f 100644 --- a/app/views/projects/protected_tags/_tags_list.html.haml +++ b/app/views/projects/protected_tags/_tags_list.html.haml @@ -1,30 +1,4 @@ -.panel.panel-default.protected-tags-list - - if @protected_tags.empty? - .panel-heading - %h3.panel-title - Protected tag (#{@protected_tags.size}) - %p.settings-message.text-center - There are currently no protected tags, protect a tag with the form above. - - else - - can_admin_project = can?(current_user, :admin_project, @project) +- can_admin_project = can?(current_user, :admin_project, @project) - %table.table.table-bordered - %colgroup - %col{ width: "25%" } - %col{ width: "25%" } - %col{ width: "50%" } - - if can_admin_project - %col - %thead - %tr - %th Protected tag (#{@protected_tags.size}) - %th Last commit - %th Allowed to create - - if can_admin_project - %th - %tbody - %tr - %td.flash-container{ colspan: 4 } - = render partial: 'projects/protected_tags/protected_tag', collection: @protected_tags, locals: { can_admin_project: can_admin_project} - - = paginate @protected_tags, theme: 'gitlab' += render layout: 'projects/protected_tags/shared/tags_list' do + = render partial: 'projects/protected_tags/protected_tag', collection: @protected_tags, locals: { can_admin_project: can_admin_project} diff --git a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml new file mode 100644 index 00000000000..5a53c704fcb --- /dev/null +++ b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml @@ -0,0 +1,29 @@ += form_for [@project.namespace.becomes(Namespace), @project, @protected_tag], html: { class: 'new-protected-tag js-new-protected-tag' } do |f| + .panel.panel-default + .panel-heading + %h3.panel-title + Protect a tag + .panel-body + .form-horizontal + = form_errors(@protected_tag) + .form-group + = f.label :name, class: 'col-md-2 text-right' do + Tag: + .col-md-10.protected-tags-dropdown + = render partial: "projects/protected_tags/shared/dropdown", locals: { f: f } + .help-block + = link_to 'Wildcards', help_page_path('user/project/protected_tags', anchor: 'wildcard-protected-tags') + such as + %code v* + or + %code *-release + are supported + .form-group + %label.col-md-2.text-right{ for: 'create_access_levels_attributes' } + Allowed to create: + .col-md-10 + .create_access_levels-container + = yield :create_access_levels + + .panel-footer + = f.submit 'Protect', class: 'btn-create btn', disabled: true diff --git a/app/views/projects/protected_tags/_dropdown.html.haml b/app/views/projects/protected_tags/shared/_dropdown.html.haml index 9b6923210f7..9b6923210f7 100644 --- a/app/views/projects/protected_tags/_dropdown.html.haml +++ b/app/views/projects/protected_tags/shared/_dropdown.html.haml diff --git a/app/views/projects/protected_tags/shared/_index.html.haml b/app/views/projects/protected_tags/shared/_index.html.haml new file mode 100644 index 00000000000..c07bd454ff6 --- /dev/null +++ b/app/views/projects/protected_tags/shared/_index.html.haml @@ -0,0 +1,24 @@ +- expanded = Rails.env.test? + +%section.settings + .settings-header + %h4 + Protected Tags + %button.btn.js-settings-toggle + = expanded ? 'Collapse' : 'Expand' + %p + Limit access to creating and updating tags. + .settings-content.no-animate{ class: ('expanded' if expanded) } + %p + By default, protected tags are designed to: + %ul + %li Prevent tag creation by everybody except Masters + %li Prevent <strong>anyone</strong> from updating the tag + %li Prevent <strong>anyone</strong> from deleting the tag + + %p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags"), class: "underlined-link"}. + + - if can? current_user, :admin_project, @project + = yield :create_protected_tag + + = yield :tag_list diff --git a/app/views/projects/protected_tags/_matching_tag.html.haml b/app/views/projects/protected_tags/shared/_matching_tag.html.haml index f17353df122..05f102d1ca3 100644 --- a/app/views/projects/protected_tags/_matching_tag.html.haml +++ b/app/views/projects/protected_tags/shared/_matching_tag.html.haml @@ -6,5 +6,5 @@ %span.label.label-info.prepend-left-5 default %td - commit = @project.commit(matching_tag.name) - = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit-sha') + = link_to(commit.short_id, project_commit_path(@project, commit.id), class: 'commit-sha') = time_ago_with_tooltip(commit.committed_date) diff --git a/app/views/projects/protected_tags/shared/_protected_tag.html.haml b/app/views/projects/protected_tags/shared/_protected_tag.html.haml new file mode 100644 index 00000000000..c778f7b9781 --- /dev/null +++ b/app/views/projects/protected_tags/shared/_protected_tag.html.haml @@ -0,0 +1,22 @@ +%tr.js-protected-tag-edit-form{ data: { url: project_protected_tag_path(@project, protected_tag) } } + %td + %span.ref-name= protected_tag.name + + - if @project.root_ref?(protected_tag.name) + %span.label.label-info.prepend-left-5 default + %td + - if protected_tag.wildcard? + - matching_tags = protected_tag.matching(repository.tags) + = link_to pluralize(matching_tags.count, "matching tag"), project_protected_tag_path(@project, protected_tag) + - else + - if commit = protected_tag.commit + = link_to(commit.short_id, project_commit_path(@project, commit.id), class: 'commit-sha') + = time_ago_with_tooltip(commit.committed_date) + - else + (tag was removed from repository) + + = yield + + - if can? current_user, :admin_project, @project + %td + = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_tag], data: { confirm: 'Tag will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning' diff --git a/app/views/projects/protected_tags/shared/_tags_list.html.haml b/app/views/projects/protected_tags/shared/_tags_list.html.haml new file mode 100644 index 00000000000..6e3cd4ada71 --- /dev/null +++ b/app/views/projects/protected_tags/shared/_tags_list.html.haml @@ -0,0 +1,30 @@ +.panel.panel-default.protected-tags-list + - if @protected_tags.empty? + .panel-heading + %h3.panel-title + Protected tag (#{@protected_tags.size}) + %p.settings-message.text-center + There are currently no protected tags, protect a tag with the form above. + - else + - can_admin_project = can?(current_user, :admin_project, @project) + + %table.table.table-bordered + %colgroup + %col{ width: "25%" } + %col{ width: "25%" } + %col{ width: "50%" } + - if can_admin_project + %col + %thead + %tr + %th Protected tag (#{@protected_tags.size}) + %th Last commit + %th Allowed to create + - if can_admin_project + %th + %tbody + %tr + %td.flash-container{ colspan: 4 } + = yield + + = paginate @protected_tags, theme: 'gitlab' diff --git a/app/views/projects/protected_tags/show.html.haml b/app/views/projects/protected_tags/show.html.haml index 16fc02fe9f4..86629f1753b 100644 --- a/app/views/projects/protected_tags/show.html.haml +++ b/app/views/projects/protected_tags/show.html.haml @@ -19,7 +19,7 @@ %th Last commit %tbody - @matching_refs.each do |matching_tag| - = render partial: "matching_tag", object: matching_tag + = render partial: "projects/protected_tags/shared/matching_tag", object: matching_tag - else %p.settings-message.text-center Couldn't find any matching tags. diff --git a/app/views/projects/registry/repositories/_image.html.haml b/app/views/projects/registry/repositories/_image.html.haml index dcdc432b654..a0535edafc3 100644 --- a/app/views/projects/registry/repositories/_image.html.haml +++ b/app/views/projects/registry/repositories/_image.html.haml @@ -8,7 +8,7 @@ - if can?(current_user, :update_container_image, @project) .controls.hidden-xs.pull-right - = link_to namespace_project_container_registry_path(@project.namespace, @project, image), + = link_to project_container_registry_path(@project, image), class: 'btn btn-remove has-tooltip', title: 'Remove repository', data: { confirm: 'Are you sure?' }, @@ -30,4 +30,3 @@ = render partial: 'tag', collection: image.tags - else .nothing-here-block No tags in Container Registry for this container image. - diff --git a/app/views/projects/registry/repositories/_tag.html.haml b/app/views/projects/registry/repositories/_tag.html.haml index 378a23f07e6..0b082a2137f 100644 --- a/app/views/projects/registry/repositories/_tag.html.haml +++ b/app/views/projects/registry/repositories/_tag.html.haml @@ -25,7 +25,7 @@ - if can?(current_user, :update_container_image, @project) %td.content .controls.hidden-xs.pull-right - = link_to namespace_project_registry_repository_tag_path(@project.namespace, @project, tag.repository, tag.name), + = link_to project_registry_repository_tag_path(@project, tag.repository, tag.name), method: :delete, class: 'btn btn-remove has-tooltip', title: 'Remove tag', diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index 93ee9382a6e..0a5a38a3694 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -10,11 +10,11 @@ %strong= @tag.name - = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal common-note-form release-form js-quick-submit' }) do |f| + = form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name), html: { class: 'form-horizontal common-note-form release-form js-quick-submit' }) do |f| = render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..." = render 'shared/notes/hints' .error-alert .prepend-top-default = f.submit 'Save changes', class: 'btn btn-save' - = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" + = link_to "Cancel", project_tag_path(@project, @tag.name), class: "btn btn-default btn-cancel" diff --git a/app/views/projects/remove_fork.js.haml b/app/views/projects/remove_fork.js.haml index 17b9fecfeb1..6d083c5c516 100644 --- a/app/views/projects/remove_fork.js.haml +++ b/app/views/projects/remove_fork.js.haml @@ -1,2 +1,2 @@ :plain - location.href = "#{edit_namespace_project_path(@project.namespace, @project)}"; + location.href = "#{edit_project_path(@project)}"; diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml index d9c39fb87b7..170f9e259df 100644 --- a/app/views/projects/repositories/_feed.html.haml +++ b/app/views/projects/repositories/_feed.html.haml @@ -1,7 +1,7 @@ - commit = update %tr %td - = link_to namespace_project_commits_path(@project.namespace, @project, commit.head.name) do + = link_to project_commits_path(@project, commit.head.name) do %strong = commit.head.name - if @project.root_ref?(commit.head.name) @@ -9,7 +9,7 @@ %td %div - = link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do + = link_to project_commits_path(@project, commit.id) do %code= commit.short_id = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: '' = markdown(truncate(commit.title, length: 40), pipeline: :single_line, author: commit.author) diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index 674f87e8220..abc97bcdff5 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -9,7 +9,7 @@ = icon('lock', class: 'has-tooltip', title: 'Locked to current projects') %small - = link_to edit_namespace_project_runner_path(@project.namespace, @project, runner) do + = link_to edit_project_runner_path(@project, runner) do %i.fa.fa-edit.btn - else %span.commit-sha @@ -21,7 +21,7 @@ = link_to 'Remove Runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - else - runner_project = @project.runner_projects.find_by(runner_id: runner) - = link_to 'Disable for this project', namespace_project_runner_project_path(@project.namespace, @project, runner_project), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + = link_to 'Disable for this project', project_runner_project_path(@project, runner_project), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - elsif runner.specific? = form_for [@project.namespace.becomes(Namespace), @project, @project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: runner.id diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml index 0671dd66e78..a4e820628f3 100644 --- a/app/views/projects/runners/_shared_runners.html.haml +++ b/app/views/projects/runners/_shared_runners.html.haml @@ -9,10 +9,10 @@ on GitLab.com). %hr - if @project.shared_runners_enabled? - = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do + = link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-warning', method: :post do Disable shared Runners - else - = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-success', method: :post do + = link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-success', method: :post do Enable shared Runners for this project diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 6dffc026392..b842fd57cf3 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -9,20 +9,21 @@ %p= @service.description .col-lg-9 - = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'gl-show-field-errors form-horizontal js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_namespace_project_service_path } }) do |form| + = form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'gl-show-field-errors form-horizontal js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form| = render 'shared/service_settings', form: form, subject: @service - .footer-block.row-content-block - %button.btn.btn-save{ type: 'submit' } - = icon('spinner spin', class: 'hidden js-btn-spinner') - %span.js-btn-label - Save changes - - - if @service.valid? && @service.activated? - - unless @service.can_test? - - disabled_class = 'disabled' - - disabled_title = @service.disabled_title + - if @service.editable? + .footer-block.row-content-block + %button.btn.btn-save{ type: 'submit' } + = icon('spinner spin', class: 'hidden js-btn-spinner') + %span.js-btn-label + Save changes + + - if @service.valid? && @service.activated? + - unless @service.can_test? + - disabled_class = 'disabled' + - disabled_title = @service.disabled_title - = link_to 'Cancel', namespace_project_settings_integrations_path(@project.namespace, @project), class: 'btn btn-cancel' + = link_to 'Cancel', project_settings_integrations_path(@project), class: 'btn btn-cancel' - if lookup_context.template_exists?('show', "projects/services/#{@service.to_param}", true) %hr diff --git a/app/views/projects/services/_index.html.haml b/app/views/projects/services/_index.html.haml index 997b702da33..915c6b22162 100644 --- a/app/views/projects/services/_index.html.haml +++ b/app/views/projects/services/_index.html.haml @@ -21,7 +21,7 @@ %td{ "aria-label" => "#{service.title}: status " + (service.activated? ? "on" : "off") } = boolean_to_icon service.activated? %td - = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do + = link_to edit_project_service_path(@project, service.to_param) do %strong= service.title %td.hidden-xs = service.description diff --git a/app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml b/app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml index fcc91be11cd..44c0b7a90dc 100644 --- a/app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml +++ b/app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml @@ -2,6 +2,6 @@ - unless @service.activated? .row .col-sm-9.col-sm-offset-3 - = link_to new_namespace_project_mattermost_path(@project.namespace, @project), class: 'btn btn-lg' do + = link_to new_project_mattermost_path(@project), class: 'btn btn-lg' do = custom_icon('mattermost_logo', size: 15) Add to Mattermost diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index c4ac384ca1a..d8e11500964 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -8,10 +8,10 @@ %p Metrics are automatically configured and monitored based on a library of metrics from popular exporters. - = link_to 'More information', '#' + = link_to 'More information', help_page_path('user/project/integrations/prometheus') .col-lg-9 - .panel.panel-default.js-panel-monitored-metrics{ data: { "active-metrics" => "#{namespace_project_prometheus_active_metrics_path(@project.namespace, @project, :json)}" } } + .panel.panel-default.js-panel-monitored-metrics{ data: { "active-metrics" => "#{project_prometheus_active_metrics_path(@project, :json)}" } } .panel-heading %h3.panel-title Monitored @@ -41,5 +41,5 @@ %code $CI_ENVIRONMENT_SLUG to exporter’s queries. - = link_to 'More information', '#' + = link_to 'More information', help_page_path('user/project/integrations/prometheus', anchor: 'metrics-and-labels') %ul.list-unstyled.metrics-list.js-missing-var-metrics-list diff --git a/app/views/projects/settings/_head.html.haml b/app/views/projects/settings/_head.html.haml index 00bd563999f..15ba09b10ba 100644 --- a/app/views/projects/settings/_head.html.haml +++ b/app/views/projects/settings/_head.html.haml @@ -9,26 +9,22 @@ = link_to edit_project_path(@project), title: 'General' do %span General - = nav_link(controller: :members) do - = link_to project_settings_members_path(@project), title: 'Members' do - %span - Members - if can_edit = nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do = link_to project_settings_integrations_path(@project), title: 'Integrations' do %span Integrations = nav_link(controller: :repository) do - = link_to namespace_project_settings_repository_path(@project.namespace, @project), title: 'Repository' do + = link_to project_settings_repository_path(@project), title: 'Repository' do %span Repository - if @project.feature_available?(:builds, current_user) = nav_link(controller: :ci_cd) do - = link_to namespace_project_settings_ci_cd_path(@project.namespace, @project), title: 'Pipelines' do + = link_to project_settings_ci_cd_path(@project), title: 'Pipelines' do %span Pipelines - if Gitlab.config.pages.enabled = nav_link(controller: :pages) do - = link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages' do + = link_to project_pages_path(@project), title: 'Pages' do %span Pages diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 00ccc3ec41e..6afb38c5709 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -3,6 +3,6 @@ = render "projects/settings/head" = render 'projects/runners/index' -= render 'projects/variables/index' += render 'ci/variables/index' = render 'projects/triggers/index' = render 'projects/pipelines_settings/show' diff --git a/app/views/projects/settings/integrations/_project_hook.html.haml b/app/views/projects/settings/integrations/_project_hook.html.haml index a6640592dba..00700e286c3 100644 --- a/app/views/projects/settings/integrations/_project_hook.html.haml +++ b/app/views/projects/settings/integrations/_project_hook.html.haml @@ -9,8 +9,8 @@ .col-md-4.col-lg-5.text-right-lg.prepend-top-5 %span.append-right-10.inline SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} - = link_to "Edit", edit_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm" - = link_to "Test", test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm" - = link_to namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do + = link_to "Edit", edit_project_hook_path(@project, hook), class: "btn btn-sm" + = link_to "Test", test_project_hook_path(@project, hook), class: "btn btn-sm" + = link_to project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do %span.sr-only Remove = icon('trash') diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index 40ea02abce9..0f20ecf8c69 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -6,6 +6,10 @@ = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('deploy_keys') +-# Protected branches & tags use a lot of nested partials. +-# The shared parts of the views can be found in the `shared` directory. +-# Those are used throughout the actual views. These `shared` views are then +-# reused in EE. = render "projects/protected_branches/index" = render "projects/protected_tags/index" = render @deploy_keys diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder index ed34f5c0520..39f8cb9a0e0 100644 --- a/app/views/projects/show.atom.builder +++ b/app/views/projects/show.atom.builder @@ -1,7 +1,7 @@ xml.title "#{@project.name} activity" -xml.link href: namespace_project_url(@project.namespace, @project, rss_url_options), rel: "self", type: "application/atom+xml" -xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html" -xml.id namespace_project_url(@project.namespace, @project) +xml.link href: project_url(@project, rss_url_options), rel: "self", type: "application/atom+xml" +xml.link href: project_url(@project), rel: "alternate", type: "text/html" +xml.id project_url(@project) xml.updated @events[0].updated_at.xmlschema if @events[0] xml << render(@events) if @events.any? diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 152e50a79bb..a73e111ad6d 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,9 +1,11 @@ - @no_container = true +- @content_class = "limit-container-width" unless fluid_layout +- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message = content_for :meta_tags do - = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, rss_url_options), title: "#{@project.name} activity") + = auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity") -= content_for :flash_message do += content_for flash_message_container do - if current_user && can?(current_user, :download_code, @project) = render 'shared/no_ssh' = render 'shared/no_password' @@ -16,16 +18,16 @@ %nav.project-stats{ class: container_class } %ul.nav %li - = link_to project_files_path(@project) do + = link_to project_tree_path(@project) do #{_('Files')} (#{storage_counter(@project.statistics.total_repository_size)}) %li - = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do + = link_to project_commits_path(@project, current_ref) do #{n_('Commit', 'Commits', @project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)}) %li - = link_to namespace_project_branches_path(@project.namespace, @project) do + = link_to project_branches_path(@project) do #{n_('Branch', 'Branches', @repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)}) %li - = link_to namespace_project_tags_path(@project.namespace, @project) do + = link_to project_tags_path(@project) do #{n_('Tag', 'Tags', @repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)}) - if default_project_view != 'readme' && @repository.readme diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml index 34ee4ff1937..f09871c7fcc 100644 --- a/app/views/projects/snippets/_actions.html.haml +++ b/app/views/projects/snippets/_actions.html.haml @@ -2,16 +2,16 @@ .hidden-xs - if can?(current_user, :update_project_snippet, @snippet) - = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped" do + = link_to edit_project_snippet_path(@project, @snippet), class: "btn btn-grouped" do Edit - if can?(current_user, :update_project_snippet, @snippet) - = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do + = link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do Delete - if can?(current_user, :create_project_snippet, @project) - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-inverted btn-create', title: "New snippet" do + = link_to new_project_snippet_path(@project), class: 'btn btn-grouped btn-inverted btn-create', title: "New snippet" do New snippet - if @snippet.submittable_as_spam_by?(current_user) - = link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam' + = link_to 'Submit as spam', mark_as_spam_project_snippet_path(@project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam' - if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet) .visible-xs-block.dropdown %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } @@ -21,16 +21,16 @@ %ul - if can?(current_user, :create_project_snippet, @project) %li - = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New snippet" do + = link_to new_project_snippet_path(@project), title: "New snippet" do New snippet - if can?(current_user, :update_project_snippet, @snippet) %li - = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do + = link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do Delete - if can?(current_user, :update_project_snippet, @snippet) %li - = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do + = link_to edit_project_snippet_path(@project, @snippet) do Edit - if @snippet.submittable_as_spam_by?(current_user) %li - = link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post + = link_to 'Submit as spam', mark_as_spam_project_snippet_path(@project, @snippet), method: :post diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml index 24b92094b7d..d41cc8e0425 100644 --- a/app/views/projects/snippets/edit.html.haml +++ b/app/views/projects/snippets/edit.html.haml @@ -3,4 +3,4 @@ %h3.page-title Edit Snippet %hr -= render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet) += render "shared/snippets/form", url: project_snippet_path(@project, @snippet) diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 84e05cd6d88..4f8ce526c83 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -7,13 +7,13 @@ .nav-controls.hidden-xs - if can?(current_user, :create_project_snippet, @project) - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New snippet" do + = link_to new_project_snippet_path(@project), class: "btn btn-new", title: "New snippet" do New snippet - if can?(current_user, :create_project_snippet, @project) .visible-xs - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new btn-block", title: "New snippet" do + = link_to new_project_snippet_path(@project), class: "btn btn-new btn-block", title: "New snippet" do New snippet = render 'snippets/snippets' diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml index cfed3a79bc5..d3e6b456f48 100644 --- a/app/views/projects/snippets/new.html.haml +++ b/app/views/projects/snippets/new.html.haml @@ -3,4 +3,4 @@ %h3.page-title New Snippet %hr -= render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet) += render "shared/snippets/form", url: project_snippets_path(@project, @snippet) diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 44cb734d7b9..468ab922542 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -2,7 +2,7 @@ - release = @releases.find { |release| release.tag == tag.name } %li.flex-row .row-main-content.str-truncated - = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'item-title ref-name' do + = link_to project_tag_path(@project, tag.name), class: 'item-title ref-name' do = icon('tag') = tag.name @@ -29,9 +29,9 @@ = render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name] - if can?(current_user, :push_code, @project) - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do + = link_to edit_project_tag_release_path(@project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do = icon("pencil") - if can?(current_user, :admin_project, @project) - = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: "btn btn-remove remove-row has-tooltip #{protected_tag?(@project, tag) ? 'disabled' : ''}", title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do + = link_to project_tag_path(@project, tag.name), class: "btn btn-remove remove-row has-tooltip #{protected_tag?(@project, tag) ? 'disabled' : ''}", title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do = icon("trash-o") diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 56656ea3d86..bf97cbc1f68 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -24,7 +24,7 @@ %li = link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value) - if can?(current_user, :push_code, @project) - = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do + = link_to new_project_tag_path(@project), class: 'btn btn-create new-tag-btn' do New tag .tags diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 52af295bddd..f1bbaf40387 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -39,7 +39,7 @@ .help-block Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 - = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' + = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel' :javascript window.gl = window.gl || { }; diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 2b81ce4b9fa..d02cd70f4c3 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -19,17 +19,17 @@ .nav-controls.controls-flex - if can?(current_user, :push_code, @project) - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Edit release notes' do + = link_to edit_project_tag_release_path(@project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Edit release notes' do = icon("pencil") - = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse files' do + = link_to project_tree_path(@project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse files' do = icon('files-o') - = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse commits' do + = link_to project_commits_path(@project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse commits' do = icon('history') .btn-container.controls-item = render 'projects/buttons/download', project: @project, ref: @tag.name - if can?(current_user, :admin_project, @project) .btn-container.controls-item-full - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-remove remove-row has-tooltip #{protected_tag?(@project, @tag) ? 'disabled' : ''}", title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do + = link_to project_tag_path(@project, @tag.name), class: "btn btn-remove remove-row has-tooltip #{protected_tag?(@project, @tag) ? 'disabled' : ''}", title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do %i.fa.fa-trash-o - if @tag.message.present? diff --git a/app/views/projects/transfer.js.haml b/app/views/projects/transfer.js.haml index 17b9fecfeb1..6d083c5c516 100644 --- a/app/views/projects/transfer.js.haml +++ b/app/views/projects/transfer.js.haml @@ -1,2 +1,2 @@ :plain - location.href = "#{edit_namespace_project_path(@project.namespace, @project)}"; + location.href = "#{edit_project_path(@project)}"; diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml index 425b460eb09..fd8175e1e01 100644 --- a/app/views/projects/tree/_blob_item.html.haml +++ b/app/views/projects/tree/_blob_item.html.haml @@ -2,7 +2,7 @@ %td.tree-item-file-name = tree_icon(type, blob_item.mode, blob_item.name) - file_name = blob_item.name - = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do + = link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do %span.str-truncated= file_name %td.hidden-xs.tree-commit %td.tree-time-ago.cgray.text-right diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml index f9147815427..4579a912f39 100644 --- a/app/views/projects/tree/_readme.html.haml +++ b/app/views/projects/tree/_readme.html.haml @@ -2,8 +2,8 @@ %article.file-holder.readme-holder{ class: ("limited-width-container" unless fluid_layout) } .js-file-title.file-title = blob_icon readme.mode, readme.name - = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, readme.path)) do + = link_to project_blob_path(@project, tree_join(@ref, readme.path)) do %strong = readme.name - = render 'projects/blob/viewer', viewer: readme.rich_viewer, viewer_url: namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, readme.path), viewer: :rich, format: :json) + = render 'projects/blob/viewer', viewer: readme.rich_viewer, viewer_url: project_blob_path(@project, tree_join(@ref, readme.path), viewer: :rich, format: :json) diff --git a/app/views/projects/tree/_tree_commit_column.html.haml b/app/views/projects/tree/_tree_commit_column.html.haml index 84da16b6bb1..f3d4706809f 100644 --- a/app/views/projects/tree/_tree_commit_column.html.haml +++ b/app/views/projects/tree/_tree_commit_column.html.haml @@ -1,2 +1,2 @@ %span.str-truncated - = link_to_gfm commit.full_title, namespace_project_commit_path(@project.namespace, @project, commit.id), class: "tree-commit-link" + = link_to_gfm commit.full_title, project_commit_path(@project, commit.id), class: "tree-commit-link" diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index 7854e1305db..6560bd5ab3f 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -10,7 +10,7 @@ - if @path.present? %tr.tree-item %td.tree-item-file-name - = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10' + = link_to "..", project_tree_path(@project, up_dir_path), class: 'prepend-left-10' %td %td.hidden-xs @@ -20,7 +20,7 @@ = render "projects/tree/readme", readme: tree.readme - if can_edit_tree? - = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post + = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post = render 'projects/blob/new_dir' :javascript diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 00da76349da..858418ff8df 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -4,11 +4,11 @@ %ul.breadcrumb.repo-breadcrumb %li - = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do + = link_to project_tree_path(@project, @ref) do = @project.path - path_breadcrumbs do |title, path| %li - = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path)) + = link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path)) - if current_user %li @@ -23,7 +23,7 @@ %ul.dropdown-menu - if can_edit_tree? %li - = link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do + = link_to project_new_blob_path(@project, @id) do = icon('pencil fw') #{ _('New file') } %li @@ -36,10 +36,10 @@ #{ _('New directory') } - elsif can?(current_user, :fork_project, @project) %li - - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id), + - continue_params = { to: project_new_blob_path(@project, @id), notice: edit_in_new_fork_notice, notice_now: edit_in_new_fork_notice_now } - - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id, + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params) = link_to fork_path, method: :post do = icon('pencil fw') @@ -48,7 +48,7 @@ - continue_params = { to: request.fullpath, notice: edit_in_new_fork_notice + " Try to upload a file again.", notice_now: edit_in_new_fork_notice_now } - - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id, + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params) = link_to fork_path, method: :post do = icon('file fw') @@ -57,7 +57,7 @@ - continue_params = { to: request.fullpath, notice: edit_in_new_fork_notice + " Try to create a new directory again.", notice_now: edit_in_new_fork_notice_now } - - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id, + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params) = link_to fork_path, method: :post do = icon('folder fw') @@ -65,17 +65,17 @@ %li.divider %li - = link_to new_namespace_project_branch_path(@project.namespace, @project) do + = link_to new_project_branch_path(@project) do = icon('code-fork fw') #{ _('New branch') } %li - = link_to new_namespace_project_tag_path(@project.namespace, @project) do + = link_to new_project_tag_path(@project) do = icon('tags fw') #{ _('New tag') } .tree-controls = render 'projects/find_file_link' - = link_to s_('Commits|History'), namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn' + = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' = render 'projects/buttons/download', project: @project, ref: @ref diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml index 15c9536133c..0c9c8750f2c 100644 --- a/app/views/projects/tree/_tree_item.html.haml +++ b/app/views/projects/tree/_tree_item.html.haml @@ -2,7 +2,7 @@ %td.tree-item-file-name = tree_icon(type, tree_item.mode, tree_item.name) - path = flatten_tree(tree_item) - = link_to namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path)), title: path do + = link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path do %span.str-truncated= path %td.hidden-xs.tree-commit %td.tree-time-ago.text-right diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 96a08f9f8be..f727f340bb9 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -1,11 +1,11 @@ - @no_container = true +- @content_class = "limit-container-width" unless fluid_layout - page_title @path.presence || _("Files"), @ref = content_for :meta_tags do - = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") + = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") = render "projects/commits/head" -= render 'projects/last_push' - -%div{ class: container_class } +%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } + = render 'projects/last_push' = render 'projects/files', commit: @last_commit, project: @project, ref: @ref diff --git a/app/views/projects/triggers/_trigger.html.haml b/app/views/projects/triggers/_trigger.html.haml index 9b5f63ae81a..6249c32b7cc 100644 --- a/app/views/projects/triggers/_trigger.html.haml +++ b/app/views/projects/triggers/_trigger.html.haml @@ -33,10 +33,10 @@ - take_ownership_confirmation = "By taking ownership you will bind this trigger to your user account. With this the trigger will have access to all your projects as if it was you. Are you sure?" - revoke_trigger_confirmation = "By revoking a trigger you will break any processes making use of it. Are you sure?" - if trigger.owner != current_user && can?(current_user, :manage_trigger, trigger) - = link_to 'Take ownership', take_ownership_namespace_project_trigger_path(@project.namespace, @project, trigger), data: { confirm: take_ownership_confirmation }, method: :post, class: "btn btn-default btn-sm btn-trigger-take-ownership" + = link_to 'Take ownership', take_ownership_project_trigger_path(@project, trigger), data: { confirm: take_ownership_confirmation }, method: :post, class: "btn btn-default btn-sm btn-trigger-take-ownership" - if can?(current_user, :admin_trigger, trigger) - = link_to edit_namespace_project_trigger_path(@project.namespace, @project, trigger), method: :get, title: "Edit", class: "btn btn-default btn-sm" do + = link_to edit_project_trigger_path(@project, trigger), method: :get, title: "Edit", class: "btn btn-default btn-sm" do %i.fa.fa-pencil - if can?(current_user, :manage_trigger, trigger) - = link_to namespace_project_trigger_path(@project.namespace, @project, trigger), data: { confirm: revoke_trigger_confirmation }, method: :delete, title: "Revoke", class: "btn btn-default btn-warning btn-sm btn-trigger-revoke" do + = link_to project_trigger_path(@project, trigger), data: { confirm: revoke_trigger_confirmation }, method: :delete, title: "Revoke", class: "btn btn-default btn-warning btn-sm btn-trigger-revoke" do %i.fa.fa-trash diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml index dcf1f767bf7..2c05ebe52ae 100644 --- a/app/views/projects/update.js.haml +++ b/app/views/projects/update.js.haml @@ -1,6 +1,6 @@ - if @project.valid? :plain - location.href = "#{edit_namespace_project_path(@project.namespace, @project)}"; + location.href = "#{edit_project_path(@project)}"; - else :plain $(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml index 297a53ca98c..df533952b76 100644 --- a/app/views/projects/variables/show.html.haml +++ b/app/views/projects/variables/show.html.haml @@ -1,9 +1 @@ -- page_title "Variables" - -.row.prepend-top-default.append-bottom-default - .col-lg-3 - = render "content" - .col-lg-9 - %h5.prepend-top-0 - Update variable - = render "form", btn_text: "Save variable" += render 'ci/variables/show' diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index c10b3004bc3..fc6b7a33943 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -12,7 +12,7 @@ .form-group .col-sm-12= f.label :content, class: 'control-label-full-width' .col-sm-12 - = render layout: 'projects/md_preview', locals: { url: namespace_project_wiki_preview_markdown_path(@project.namespace, @project, @page.slug) } do + = render layout: 'projects/md_preview', locals: { url: project_wiki_preview_markdown_path(@project, @page.slug) } do = render 'projects/zen', f: f, attr: :content, classes: 'note-textarea', placeholder: 'Write your content or drag files here...' = render 'shared/notes/hints' @@ -36,8 +36,8 @@ - if @page && @page.persisted? = f.submit 'Save changes', class: "btn-save btn" .pull-right - = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-cancel btn-grouped" + = link_to "Cancel", project_wiki_path(@project, @page), class: "btn btn-cancel btn-grouped" - else = f.submit 'Create page', class: "btn-create btn" .pull-right - = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel" + = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel" diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml index 6a578dbf640..3bbd8042c3a 100644 --- a/app/views/projects/wikis/_main_links.html.haml +++ b/app/views/projects/wikis/_main_links.html.haml @@ -2,8 +2,8 @@ - if can?(current_user, :create_wiki, @project) = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do New page - = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do + = link_to project_wiki_history_path(@project, @page), class: "btn" do Page history - if can?(current_user, :create_wiki, @project) && @page.latest? - = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn js-wiki-edit" do + = link_to project_wiki_edit_path(@project, @page), class: "btn js-wiki-edit" do Edit diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index 1e553940593..13dd8461433 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -9,7 +9,7 @@ .form-group = label_tag :new_wiki_path do %span Page slug - = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true + = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project), autofocus: true %span.new-wiki-page-slug-tip = icon('lightbulb-o') Tip: You can specify the full path for the new file. diff --git a/app/views/projects/wikis/_pages_wiki_page.html.haml b/app/views/projects/wikis/_pages_wiki_page.html.haml index 6298cf6c8da..7c2f562d422 100644 --- a/app/views/projects/wikis/_pages_wiki_page.html.haml +++ b/app/views/projects/wikis/_pages_wiki_page.html.haml @@ -1,5 +1,5 @@ %li - = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) + = link_to wiki_page.title, project_wiki_path(@project, wiki_page) %small (#{wiki_page.format}) .pull-right %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml index c2f9e65015d..62873d3aa66 100644 --- a/app/views/projects/wikis/_sidebar.html.haml +++ b/app/views/projects/wikis/_sidebar.html.haml @@ -3,7 +3,7 @@ %a.gutter-toggle.pull-right.visible-xs-block.visible-sm-block.js-sidebar-wiki-toggle{ href: "#" } = icon('angle-double-right') - - git_access_url = namespace_project_wikis_git_access_path(@project.namespace, @project) + - git_access_url = project_wikis_git_access_path(@project) = link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do = succeed ' ' do = icon('cloud-download') @@ -15,7 +15,7 @@ = render @sidebar_wiki_entries, context: 'sidebar' .block - = link_to namespace_project_wikis_pages_path(@project.namespace, @project), class: 'btn btn-block' do + = link_to project_wikis_pages_path(@project), class: 'btn btn-block' do More Pages = render 'projects/wikis/new' diff --git a/app/views/projects/wikis/_sidebar_wiki_page.html.haml b/app/views/projects/wikis/_sidebar_wiki_page.html.haml index 0a61d90177b..2423ac6abce 100644 --- a/app/views/projects/wikis/_sidebar_wiki_page.html.haml +++ b/app/views/projects/wikis/_sidebar_wiki_page.html.haml @@ -1,3 +1,3 @@ %li{ class: active_when(params[:id] == wiki_page.slug) } - = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do + = link_to project_wiki_path(@project, wiki_page) do = wiki_page.title.capitalize diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index fbe192a40ec..df0ec14eb3b 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -8,7 +8,7 @@ .nav-text %h2.wiki-page-title - if @page.persisted? - = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page) + = link_to @page.title.capitalize, project_wiki_path(@project, @page) - else = @page.title.capitalize %span.light @@ -23,10 +23,10 @@ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do New page - if @page.persisted? - = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do + = link_to project_wiki_history_path(@project, @page), class: "btn" do Page history - if can?(current_user, :admin_wiki, @project) - = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-danger" do + = link_to project_wiki_path(@project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-danger" do Delete = render 'form' diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml index 0e47e2a5fa3..306feeff259 100644 --- a/app/views/projects/wikis/history.html.haml +++ b/app/views/projects/wikis/history.html.haml @@ -6,7 +6,7 @@ .nav-text %h2.wiki-page-title - = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page) + = link_to @page.title.capitalize, project_wiki_path(@project, @page) %span.light · History diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index 5fba2b1a5ae..dece1fad0bb 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -9,7 +9,7 @@ Wiki Pages .nav-controls - = link_to namespace_project_wikis_git_access_path(@project.namespace, @project), class: 'btn' do + = link_to project_wikis_git_access_path(@project), class: 'btn' do = icon('cloud-download') Clone repository diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index f003ff6b63f..13591dd8e74 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -22,7 +22,7 @@ - if @page.historical? .warning_message This is an old version of this page. - You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}. + You can view the #{link_to "most recent version", project_wiki_path(@project, @page)} or browse the #{link_to "history", project_wiki_history_path(@project, @page)}. .wiki-holder.prepend-top-default.append-bottom-default .wiki diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index 7f1f807e2e7..de473c23d66 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -3,7 +3,7 @@ .file-holder .js-file-title.file-title - ref = @search_results.repository_ref - - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(ref, file_name)) + - blob_link = project_blob_path(@project, tree_join(ref, file_name)) = link_to blob_link do %i.fa.fa-file %strong diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml index 026f404ce07..aef825691e0 100644 --- a/app/views/search/results/_snippet_title.html.haml +++ b/app/views/search/results/_snippet_title.html.haml @@ -11,7 +11,7 @@ %small.pull-right.cgray - if snippet_title.project_id? - = link_to snippet_title.project.name_with_namespace, namespace_project_path(snippet_title.project.namespace, snippet_title.project) + = link_to snippet_title.project.name_with_namespace, project_path(snippet_title.project) .snippet-info = snippet_title.to_reference diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml index d87f9df2677..16a0e432d62 100644 --- a/app/views/search/results/_wiki_blob.html.haml +++ b/app/views/search/results/_wiki_blob.html.haml @@ -2,7 +2,7 @@ .blob-result .file-holder .js-file-title.file-title - = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_blob.basename) do + = link_to project_wiki_path(@project, wiki_blob.basename) do %i.fa.fa-file %strong = wiki_blob.basename diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index de0281e97c6..2f776a17f45 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -23,7 +23,7 @@ - if can_subscribe_to_label_in_different_levels?(label) %a.js-unsubscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } } %span Unsubscribe - %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } + %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } } %span Subscribe at project level %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } %span Subscribe at group level @@ -56,7 +56,7 @@ = icon('chevron-down') %ul.dropdown-menu %li - %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } + %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } } Project level %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } Group level @@ -66,7 +66,7 @@ = icon('spinner spin', class: 'label-subscribe-button-loading') - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) - = link_to promote_namespace_project_label_path(label.project.namespace, label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "Promoting this label will make this label available to all projects inside this group. Existing project labels with the same name will be merged. Are you sure?", toggle: "tooltip"}, method: :post do + = link_to promote_project_label_path(label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "Promoting this label will make this label available to all projects inside this group. Existing project labels with the same name will be merged. Are you sure?", toggle: "tooltip"}, method: :post do %span.sr-only Promote to Group = icon('level-up') - if can?(current_user, :admin_label, label) diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 7b599dff0e3..7f58298c60f 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -2,7 +2,7 @@ - if can?(current_user, :admin_label, @project) .draggable-handler = icon('bars') - .js-toggle-priority.toggle-priority{ data: { url: remove_priority_namespace_project_label_path(@project.namespace, @project, label), + .js-toggle-priority.toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), dom_id: dom_id(label), type: label.type } } %button.add-priority.btn.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } = icon('star-o') diff --git a/app/views/shared/_mini_pipeline_graph.html.haml b/app/views/shared/_mini_pipeline_graph.html.haml index aa93572bf94..dff847159d3 100644 --- a/app/views/shared/_mini_pipeline_graph.html.haml +++ b/app/views/shared/_mini_pipeline_graph.html.haml @@ -6,7 +6,7 @@ - status_klass = "ci-status-icon ci-status-icon-#{detailed_status.group}" .stage-container.dropdown{ class: klass } - %button.mini-pipeline-graph-dropdown-toggle.has-tooltip.js-builds-dropdown-button{ class: "ci-status-icon-#{detailed_status.group}", type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name) } } + %button.mini-pipeline-graph-dropdown-toggle.has-tooltip.js-builds-dropdown-button{ class: "ci-status-icon-#{detailed_status.group}", type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_project_pipeline_path(pipeline.project, pipeline, stage: stage.name) } } = custom_icon(icon_status) = icon('caret-down') diff --git a/app/views/shared/_personal_access_tokens_table.html.haml b/app/views/shared/_personal_access_tokens_table.html.haml index ab7a2db002e..c5e4d6e2871 100644 --- a/app/views/shared/_personal_access_tokens_table.html.haml +++ b/app/views/shared/_personal_access_tokens_table.html.haml @@ -39,22 +39,3 @@ - else .settings-message.text-center This user has no active #{type} Tokens. - -%hr - -%h5 Inactive #{type} Tokens (#{inactive_tokens.length}) -- if inactive_tokens.present? - .table-responsive - %table.table.inactive-tokens - %thead - %tr - %th Name - %th Created - %tbody - - inactive_tokens.each do |token| - %tr - %td= token.name - %td= token.created_at.to_date.to_s(:medium) -- else - .settings-message.text-center - This user has no inactive #{type} Tokens. diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml index d52bb6b4dd7..4498c8f8349 100644 --- a/app/views/shared/_ref_switcher.html.haml +++ b/app/views/shared/_ref_switcher.html.haml @@ -1,12 +1,12 @@ - dropdown_toggle_text = @ref || @project.default_branch -= form_tag switch_namespace_project_refs_path(@project.namespace, @project), method: :get, class: "project-refs-form" do += form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do = hidden_field_tag :destination, destination - if defined?(path) = hidden_field_tag :path, path - @options && @options.each do |key, value| = hidden_field_tag key, value, id: nil .dropdown - = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_namespace_project_path(@project.namespace, @project), field_name: 'ref', submit_form_on_click: true }, { toggle_class: "js-project-refs-dropdown" } + = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_project_path(@project), field_name: 'ref', submit_form_on_click: true }, { toggle_class: "js-project-refs-dropdown" } .dropdown-menu.dropdown-menu-selectable.git-revision-dropdown{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) } = dropdown_title _("Switch branch/tag") = dropdown_filter _("Search branches and tags") diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml index b200e5fc528..7ca14ac93cc 100644 --- a/app/views/shared/_service_settings.html.haml +++ b/app/views/shared/_service_settings.html.haml @@ -7,10 +7,11 @@ = markdown @service.help .service-settings - .form-group - = form.label :active, "Active", class: "control-label" - .col-sm-10 - = form.check_box :active + - if @service.show_active_box? + .form-group + = form.label :active, "Active", class: "control-label" + .col-sm-10 + = form.check_box :active - if @service.supported_events.present? .form-group diff --git a/app/views/shared/empty_states/_labels.html.haml b/app/views/shared/empty_states/_labels.html.haml index 5e2f4cf109d..bfda522f2f6 100644 --- a/app/views/shared/empty_states/_labels.html.haml +++ b/app/views/shared/empty_states/_labels.html.haml @@ -7,5 +7,5 @@ %h4 Labels can be applied to issues and merge requests to categorize them. %p You can also star a label to make it a priority label. - if can?(current_user, :admin_label, @project) - = link_to 'New label', new_namespace_project_label_path(@project.namespace, @project), class: 'btn btn-new', title: 'New label', id: 'new_label_link' - = link_to 'Generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post, class: 'btn btn-success btn-inverted', title: 'Generate a default set of labels', id: 'generate_labels_link' + = link_to 'New label', new_project_label_path(@project), class: 'btn btn-new', title: 'New label', id: 'new_label_link' + = link_to 'Generate a default set of labels', generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: 'Generate a default set of labels', id: 'generate_labels_link' diff --git a/app/views/shared/icons/_add_new_group.svg b/app/views/shared/icons/_add_new_group.svg new file mode 100644 index 00000000000..ecd52c5e99f --- /dev/null +++ b/app/views/shared/icons/_add_new_group.svg @@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"> + <g fill="none" fill-rule="evenodd"> + <path fill="#000" fill-opacity=".03" d="M2.12 42C2.04 43 2 44 2 45c0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3C74.35 61.03 58.42 76 39 76S3.65 61.03 2.12 42z"/> + <path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/> + <path fill="#E1DBF2" fill-rule="nonzero" d="M59.65 32.65H60l-2-2.42-2 2.4-2-2.4-2 2.4-2-2.4-2 2.4-2-2.4-2 2.42h.77C45.57 34.6 46 36.75 46 39c0 2.84-.7 5.5-1.92 7.86 1.97 2.28 4.83 3.64 7.92 3.64 5.8 0 10.5-4.74 10.5-10.6 0-2.8-1.08-5.36-2.85-7.25zM43.18 29.6c2.4-2.1 5.52-3.3 8.82-3.3 7.46 0 13.5 6.1 13.5 13.6S59.46 53.5 52 53.5c-3.68 0-7.1-1.5-9.6-4.04C39.3 53.44 34.44 56 29 56c-9.4 0-17-7.6-17-17s7.6-17 17-17c3.22 0 6.23.9 8.8 2.45 2.13 1.3 3.97 3.05 5.38 5.16zM17 34c-.65 1.54-1 3.23-1 5 0 7.18 5.82 13 13 13s13-5.82 13-13c0-1.77-.35-3.46-1-5h-9c-.53 0-1.04-.2-1.4-.6L29 31.84l-1.6 1.58c-.36.4-.87.6-1.4.6h-9zm21.38-4c-2.4-2.5-5.76-4-9.38-4-3.62 0-6.98 1.5-9.38 4h5.55l2.42-2.4c.74-.8 2-.8 2.8 0l2.4 2.4h5.54z"/> + <path fill="#6B4FBB" d="M47.6 42.32c-.66 0-1.2-.54-1.2-1.2 0-.68.54-1.22 1.2-1.22.66 0 1.2.54 1.2 1.2 0 .68-.54 1.22-1.2 1.22zm8.8 0c-.66 0-1.2-.54-1.2-1.2 0-.68.54-1.22 1.2-1.22.66 0 1.2.54 1.2 1.2 0 .68-.54 1.22-1.2 1.22zM25 44h8c0 2.2-1.8 4-4 4s-4-1.8-4-4zm-1.5-1c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/> + </g> +</svg> diff --git a/app/views/shared/icons/_add_new_project.svg b/app/views/shared/icons/_add_new_project.svg new file mode 100644 index 00000000000..3c1e15453df --- /dev/null +++ b/app/views/shared/icons/_add_new_project.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M2.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3C74.353 61.032 58.425 76 39 76 19.575 76 3.647 61.032 2.12 42z"/><path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/><path fill="#FEE1D3" fill-rule="nonzero" d="M30 24a4 4 0 0 0-4 4v22a4 4 0 0 0 4 4h18a4 4 0 0 0 4-4V28a4 4 0 0 0-4-4H30zm0-4h18a8 8 0 0 1 8 8v22a8 8 0 0 1-8 8H30a8 8 0 0 1-8-8V28a8 8 0 0 1 8-8z"/><path fill="#FC6D26" d="M33 30h8a2 2 0 1 1 0 4h-8a2 2 0 1 1 0-4zm0 7h12a2 2 0 1 1 0 4H33a2 2 0 1 1 0-4zm0 7h12a2 2 0 1 1 0 4H33a2 2 0 1 1 0-4z"/></g></svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_add_new_user.svg b/app/views/shared/icons/_add_new_user.svg new file mode 100644 index 00000000000..0ad40498d7b --- /dev/null +++ b/app/views/shared/icons/_add_new_user.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"> + <g fill="none" fill-rule="evenodd"> + <path fill="#000" fill-opacity=".03" d="M2.12 42C2.04 43 2 44 2 45c0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3C74.35 61.03 58.42 76 39 76S3.65 61.03 2.12 42z"/> + <path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/> + <path fill="#E1DBF2" d="M44 31l-2.5-3-2.5 3-2.5-3-2.5 3-2.5-3-2.5 3h-2.72c2.65-4.2 7.36-7 12.72-7s10.07 2.8 12.72 7H49l-2.5-3-2.5 3z"/> + <path fill="#E1DBF2" fill-rule="nonzero" d="M39 57c-9.4 0-17-7.6-17-17s7.6-17 17-17 17 7.6 17 17-7.6 17-17 17zm0-4c7.18 0 13-5.82 13-13s-5.82-13-13-13-13 5.82-13 13 5.82 13 13 13z"/> + <path fill="#6B4FBB" d="M35 45h8c0 2.2-1.8 4-4 4s-4-1.8-4-4zm-1.5-2c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/> + </g> +</svg> diff --git a/app/views/shared/icons/_configure_server.svg b/app/views/shared/icons/_configure_server.svg new file mode 100644 index 00000000000..b1137b7ec94 --- /dev/null +++ b/app/views/shared/icons/_configure_server.svg @@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"> + <g fill="none" fill-rule="evenodd"> + <path fill="#000" fill-opacity=".03" d="M2.12 42C2.04 43 2 44 2 45c0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3C74.35 61.03 58.42 76 39 76S3.65 61.03 2.12 42z"/> + <path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/> + <path fill="#FEE1D3" fill-rule="nonzero" d="M24.92 35.15c-1.72-1.4-1.98-3.9-.6-5.63l1.26-1.55c1.4-1.72 3.9-2 5.63-.6l.7.56c.7-.4 1.4-.73 2.1-1V26c0-2.2 1.8-4 4-4h2c2.2 0 4 1.8 4 4v.92c.8.28 1.5.62 2.1 1l.7-.55c1.7-1.4 4.3-1.12 5.7.6l1.3 1.55c1.4 1.72 1.2 4.23-.6 5.63l-.7.6c.3.74.4 1.5.5 2.3l.9.2c2.2.5 3.5 2.64 3 4.8L56.4 45c-.5 2.15-2.64 3.5-4.8 3l-.88-.2c-.44.63-.92 1.24-1.46 1.8l.4.82c.9 1.98.1 4.38-1.9 5.35l-1.8.87c-2 .97-4.37.15-5.34-1.84l-.46-.85c-.34.03-.74.05-1.13.05-.4 0-.8-.02-1.2-.05l-.4.85c-.95 2-3.34 2.8-5.33 1.84l-1.8-.87c-1.97-.97-2.8-3.37-1.83-5.35l.4-.8c-.54-.58-1.02-1.2-1.46-1.83l-.8.2c-2.2.5-4.3-.9-4.8-3l-.4-2c-.5-2.2.85-4.3 3-4.8l.9-.2c.1-.8.3-1.6.5-2.3l-.7-.6zm4.95.77c-.53 1.2-.83 2.47-.87 3.8-.02.9-.66 1.68-1.55 1.9l-2.32.53.45 1.94 2.3-.6c.9-.2 1.8.2 2.23 1 .7 1.1 1.5 2.2 2.5 3 .7.6.9 1.6.5 2.4l-1 2.1 1.8.9 1.1-2.1c.4-.8 1.3-1.3 2.2-1.1.7.1 1.3.2 2 .2s1.3-.1 2-.2c.9-.2 1.8.3 2.2 1.1l1 2.1 1.8-.9-1.2-2c-.4-.8-.2-1.8.5-2.4 1-.85 1.84-1.88 2.45-3.05.4-.82 1.33-1.24 2.2-1.04l2.33.54.45-1.95-2.32-.54c-.9-.2-1.52-.97-1.54-1.88-.03-1.4-.33-2.6-.86-3.8-.4-.9-.2-1.8.5-2.4l1.9-1.5-1.3-1.6-1.8 1.5c-.8.5-1.8.6-2.5 0-1.1-.8-2.3-1.4-3.5-1.7-.9-.2-1.5-1-1.5-1.9V26h-2v2.38c0 .9-.6 1.7-1.5 1.93-1.3.4-2.5 1-3.5 1.7-.8.6-1.8.6-2.5 0l-1.9-1.5-1.26 1.6 1.8 1.5c.7.6.94 1.6.6 2.4z"/> + <path fill="#FC6D26" fill-rule="nonzero" d="M39 46c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm0-4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"/> + </g> +</svg> diff --git a/app/views/shared/icons/_globe.svg b/app/views/shared/icons/_globe.svg new file mode 100644 index 00000000000..c2daae5f317 --- /dev/null +++ b/app/views/shared/icons/_globe.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M2.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3C74.353 61.032 58.425 76 39 76 19.575 76 3.647 61.032 2.12 42z"/><path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/><path fill="#E1DBF2" d="M30.24 27.823A14.98 14.98 0 0 0 24 40c0 2.549.636 4.949 1.757 7.051-.297-2.684.644-4.026 2.823-4.026 3.707 0 2.462 5.365 4.473 5.761 2.01.396 4.175.396 4.267 3.29.04 1.257-.265 2.157-.917 2.7a15.095 15.095 0 0 0 8.555-1.006c.035-1.91.303-4.941 2.21-5.61 2.373-.833-.55-1.431.734-3.368 1.17-1.762-3.297-5.2 0-4.832 3.477.388 5.044-.816 6.024-1.456a14.903 14.903 0 0 0-1.373-4.94c-.873.4-2.19.465-3.702-.538-.757-.502-1.084-3.944-2.107-3.944-3.823 0-4.065 3.17-5.994 3.944-1.076.431-4.193 3.773-5.614 3.596-1.126-.14-1.071-4.417-2.45-5.166-1.359-.738-2.174-1.948-2.447-3.633zM39 59c-10.493 0-19-8.507-19-19s8.507-19 19-19 19 8.507 19 19-8.507 19-19 19z"/></g></svg>
\ No newline at end of file diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml index 7cfdfb6e6ee..964fe5220f7 100644 --- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml +++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml @@ -31,7 +31,7 @@ .title Milestone .filter-item - = dropdown_tag("Select milestone", options: { title: "Assign milestone", toggle_class: "js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: "Search milestones", data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), use_id: true, default_label: "Milestone" } }) + = dropdown_tag("Select milestone", options: { title: "Assign milestone", toggle_class: "js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: "Search milestones", data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: project_milestones_path(@project, :json), use_id: true, default_label: "Milestone" } }) .block .title Labels diff --git a/app/views/shared/issuable/_close_reopen_button.html.haml b/app/views/shared/issuable/_close_reopen_button.html.haml new file mode 100644 index 00000000000..8a1268a1c6d --- /dev/null +++ b/app/views/shared/issuable/_close_reopen_button.html.haml @@ -0,0 +1,14 @@ +- is_current_user = issuable_author_is_current_user(issuable) +- display_issuable_type = issuable_display_type(issuable) +- button_method = issuable_close_reopen_button_method(issuable) + +- if can_update && is_current_user + = link_to "Close #{display_issuable_type}", close_issuable_url(issuable), method: button_method, + class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issuable_button_visibility(issuable, true)}", title: "Close #{display_issuable_type}" + = link_to "Reopen #{display_issuable_type}", reopen_issuable_url(issuable), method: button_method, + class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}" +- elsif can_update && !is_current_user + = render 'shared/issuable/close_reopen_report_toggle', issuable: issuable +- else + = link_to 'Report abuse', new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)), + class: 'hidden-xs hidden-sm btn btn-grouped btn-close-color', title: 'Report abuse' diff --git a/app/views/shared/issuable/_close_reopen_report_toggle.html.haml b/app/views/shared/issuable/_close_reopen_report_toggle.html.haml new file mode 100644 index 00000000000..6756a7f17fd --- /dev/null +++ b/app/views/shared/issuable/_close_reopen_report_toggle.html.haml @@ -0,0 +1,49 @@ +- display_issuable_type = issuable_display_type(issuable) +- button_action = issuable.closed? ? 'reopen' : 'close' +- display_button_action = button_action.capitalize +- button_responsive_class = 'hidden-xs hidden-sm' +- button_class = "#{button_responsive_class} btn btn-grouped js-issuable-close-button issuable-close-button" +- toggle_class = "#{button_responsive_class} btn btn-nr dropdown-toggle js-issuable-close-toggle" +- button_method = issuable_close_reopen_button_method(issuable) + +.pull-left.btn-group.prepend-left-10.issuable-close-dropdown.droplab-dropdown.js-issuable-close-dropdown + = link_to "#{display_button_action} #{display_issuable_type}", close_reopen_issuable_url(issuable), + method: button_method, class: "#{button_class} btn-#{button_action}", title: "#{display_button_action} #{display_issuable_type}" + + = button_tag type: 'button', class: "#{toggle_class} btn-#{button_action}-color", + data: { 'dropdown-trigger' => '#issuable-close-menu' }, 'aria-label' => 'Toggle dropdown' do + = icon('caret-down', class: 'toggle-icon icon') + + %ul#issuable-close-menu.js-issuable-close-menu.dropdown-menu{ class: button_responsive_class, data: { dropdown: true } } + %li.close-item{ class: "#{issuable_button_visibility(issuable, true) || 'droplab-item-selected'}", + data: { text: "Close #{display_issuable_type}", url: close_issuable_url(issuable), + button_class: "#{button_class} btn-close", toggle_class: "#{toggle_class} btn-close-color", method: button_method } } + %button.btn.btn-transparent + = icon('check', class: 'icon') + .description + %strong.title + Close + = display_issuable_type + + %li.reopen-item{ class: "#{issuable_button_visibility(issuable, false) || 'droplab-item-selected'}", + data: { text: "Reopen #{display_issuable_type}", url: reopen_issuable_url(issuable), + button_class: "#{button_class} btn-reopen", toggle_class: "#{toggle_class} btn-reopen-color", method: button_method } } + %button.btn.btn-transparent + = icon('check', class: 'icon') + .description + %strong.title + Reopen + = display_issuable_type + + %li.divider.droplab-item-ignore + + %li.report-item{ data: { text: 'Report abuse', url: new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)), + button_class: "#{button_class} btn-close-color", toggle_class: "#{toggle_class} btn-close-color", method: '' } } + %button.btn.btn-transparent + = icon('check', class: 'icon') + .description + %strong.title Report abuse + %p.text + Report + = display_issuable_type.pluralize + that are abusive, inappropriate or spam. diff --git a/app/views/shared/issuable/_label_page_default.html.haml b/app/views/shared/issuable/_label_page_default.html.haml index 9a8529c6cbb..e8feff32d26 100644 --- a/app/views/shared/issuable/_label_page_default.html.haml +++ b/app/views/shared/issuable/_label_page_default.html.haml @@ -20,7 +20,7 @@ %a.dropdown-toggle-page{ href: "#" } Create new label %li - = link_to namespace_project_labels_path(@project.namespace, @project), :"data-is-link" => true do + = link_to project_labels_path(@project), :"data-is-link" => true do - if show_create && @project && can?(current_user, :admin_label, @project) Manage labels - else diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml index 6750921338a..955b8866c2c 100644 --- a/app/views/shared/issuable/_milestone_dropdown.html.haml +++ b/app/views/shared/issuable/_milestone_dropdown.html.haml @@ -11,10 +11,10 @@ %ul.dropdown-footer-list - if can? current_user, :admin_milestone, project %li - = link_to new_namespace_project_milestone_path(project.namespace, project), title: "New Milestone" do + = link_to new_project_milestone_path(project), title: "New Milestone" do Create new %li - = link_to namespace_project_milestones_path(project.namespace, project) do + = link_to project_milestones_path(project) do - if can? current_user, :admin_milestone, project Manage milestones - else diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index ae890567225..6f0b7600698 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -19,7 +19,7 @@ content_class: "filtered-search-history-dropdown-content", title: "Recent searches" }) do .js-filtered-search-history-dropdown{ data: { project_full_path: @project.full_path } } - .filtered-search-box-input-container + .filtered-search-box-input-container.droplab-dropdown .scroll-container %ul.tokens-container.list-unstyled %li.input-token @@ -98,7 +98,7 @@ - if type == :boards - if can?(current_user, :admin_list, @project) .dropdown.prepend-left-10#js-add-list - %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path) } } + %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path) } } Add list .dropdown-menu.dropdown-menu-paging.dropdown-menu-align-right.dropdown-menu-issues-board-new.dropdown-menu-selectable = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" } diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 745f1ee62da..ecbaa901792 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -37,13 +37,13 @@ = link_to 'Edit', '#', class: 'edit-link pull-right' .value.hide-collapsed - if issuable.milestone - = link_to issuable.milestone.title, namespace_project_milestone_path(@project.namespace, @project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } + = link_to issuable.milestone.title, project_milestone_path(@project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } - else %span.no-value None .selectbox.hide-collapsed = f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil - = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true, default_no: true, selected: (issuable.milestone.name if issuable.milestone), null_default: true }}) + = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: project_milestones_path(@project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true, default_no: true, selected: (issuable.milestone.name if issuable.milestone), null_default: true }}) - if issuable.has_attribute?(:time_estimate) #issuable-time-tracker.block // Fallback while content is loading @@ -106,7 +106,7 @@ - selected_labels.each do |label| = hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil .dropdown - %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project) } } + %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (project_labels_path(@project, :json) if @project) } } %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) } = multi_label_name(selected_labels, "Labels") = icon('chevron-down', 'aria-hidden': 'true') diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml index 1d5a61cffce..bcdad3c153a 100644 --- a/app/views/shared/members/_group.html.haml +++ b/app/views/shared/members/_group.html.haml @@ -14,7 +14,7 @@ %span{ class: ('text-warning' if group_link.expires_soon?) } Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)} .controls.member-controls - = form_tag namespace_project_group_link_path(@project.namespace, @project, group_link), method: :put, remote: true, class: 'form-horizontal js-edit-member-form' do + = form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'form-horizontal js-edit-member-form' do = hidden_field_tag "group_link[group_access]", group_link.group_access .member-form-control.dropdown.append-right-5 %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button", @@ -36,7 +36,7 @@ = text_field_tag 'group_link[expires_at]', group_link.expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{group.id}", disabled: !can_admin_member %i.clear-icon.js-clear-input - if can_admin_member - = link_to namespace_project_group_link_path(@project.namespace, @project, group_link), + = link_to project_group_link_path(@project, group_link), method: :delete, data: { confirm: "Are you sure you want to remove #{group.name}?" }, class: 'btn btn-remove prepend-left-10' do diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index a5aa768b1b2..951b4dd7b36 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -1,5 +1,6 @@ - show_roles = local_assigns.fetch(:show_roles, true) - show_controls = local_assigns.fetch(:show_controls, true) +- force_mobile_view = local_assigns.fetch(:force_mobile_view, false) - user = local_assigns.fetch(:user, member.user) - source = member.source - can_admin_member = can?(current_user, action_member_permission(:update, member), member) @@ -8,45 +9,53 @@ %span.list-item-name - if user = image_tag avatar_icon(user, 40), class: "avatar s40", alt: '' - %strong - = link_to user.name, user_path(user) - %span.cgray= user.to_reference + .user-info + = link_to user.name, user_path(user), class: 'member' + %span.cgray= user.to_reference - - if user == current_user - %span.label.label-success.prepend-left-5 It's you + - if user == current_user + %span.label.label-success.prepend-left-5 It's you - - if user.blocked? - %label.label.label-danger - %strong Blocked + - if user.blocked? + %label.label.label-danger + %strong Blocked - - if source.instance_of?(Group) && source != @group - · - = link_to source.full_name, source, class: "member-group-link" + - if source.instance_of?(Group) && source != @group + · + = link_to source.full_name, source, class: "member-group-link" - .hidden-xs.cgray - - if member.request? - Requested - = time_ago_with_tooltip(member.requested_at) - - else - Joined #{time_ago_with_tooltip(member.created_at)} - - if member.expires? - · - %span{ class: "#{"text-warning" if member.expires_soon?} has-tooltip", title: member.expires_at.to_time.in_time_zone.to_s(:medium) } - Expires in #{distance_of_time_in_words_to_now(member.expires_at)} + .cgray + - if member.request? + Requested + = time_ago_with_tooltip(member.requested_at) + - else + Joined #{time_ago_with_tooltip(member.created_at)} + - if member.expires? + · + %span{ class: "#{"text-warning" if member.expires_soon?} has-tooltip", title: member.expires_at.to_time.in_time_zone.to_s(:medium) } + Expires in #{distance_of_time_in_words_to_now(member.expires_at)} - else = image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: '' - %strong= member.invite_email - .cgray - Invited - - if member.created_by - by - = link_to member.created_by.name, user_path(member.created_by) - = time_ago_with_tooltip(member.created_at) + .user-info + .member= member.invite_email + .cgray + Invited + - if member.created_by + by + = link_to member.created_by.name, user_path(member.created_by) + = time_ago_with_tooltip(member.created_at) - if show_roles - current_resource = @project || @group .controls.member-controls - if show_controls && member.source == current_resource + + - if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source) + = link_to icon('paper-plane'), polymorphic_path([:resend_invite, member]), + method: :post, + class: 'btn btn-default prepend-left-10 hidden-xs', + title: 'Resend invite' + - if user != current_user && can_admin_member = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| = f.hidden_field :access_level @@ -75,13 +84,17 @@ - if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source) = link_to 'Resend invite', polymorphic_path([:resend_invite, member]), method: :post, - class: 'btn btn-default prepend-left-10' + class: 'btn btn-default prepend-left-10 visible-xs-block' - elsif member.request? && can_admin_member - = link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]), + = link_to polymorphic_path([:approve_access_request, member]), method: :post, class: 'btn btn-success prepend-left-10', - title: 'Grant access' + title: 'Grant access' do + %span{ class: ('visible-xs-block' unless force_mobile_view) } + Grant access + - unless force_mobile_view + = icon('check inverse', class: 'hidden-xs') - if can?(current_user, action_member_permission(:destroy, member), member) - if current_user == user @@ -96,8 +109,9 @@ data: { confirm: remove_member_message(member) }, class: 'btn btn-remove prepend-left-10', title: remove_member_title(member) do - %span.visible-xs-block + %span{ class: ('visible-xs-block' unless force_mobile_view) } Delete - = icon('trash', class: 'hidden-xs') + - unless force_mobile_view + = icon('trash', class: 'hidden-xs') - else %span.member-access-text= member.human_access diff --git a/app/views/shared/members/_requests.html.haml b/app/views/shared/members/_requests.html.haml index 92f6e7428ae..09b9944082f 100644 --- a/app/views/shared/members/_requests.html.haml +++ b/app/views/shared/members/_requests.html.haml @@ -1,8 +1,10 @@ +- force_mobile_view = local_assigns.fetch(:force_mobile_view, false) + - if requesters.any? - .panel.panel-default.prepend-top-default + .panel.panel-default.prepend-top-default{ class: ('panel-mobile' if force_mobile_view ) } .panel-heading Users requesting access to %strong= membership_source.name %span.badge= requesters.size - %ul.content-list - = render partial: 'shared/members/member', collection: requesters, as: :member + %ul.content-list.members-list + = render partial: 'shared/members/member', collection: requesters, as: :member, locals: { force_mobile_view: force_mobile_view } diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index a7c67ac9980..3739f4c221d 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -1,14 +1,13 @@ -# @project is present when viewing Project's milestone - project = @project || issuable.project - namespace = @project_namespace || project.namespace.becomes(Namespace) +- labels = issuable.labels - assignees = issuable.assignees -- issuable_type = issuable.class.table_name - base_url_args = [namespace, project] -- issuable_type_args = base_url_args + [issuable_type] +- issuable_type_args = base_url_args + [issuable.class.table_name] - issuable_url_args = base_url_args + [issuable] -- can_update = can?(current_user, :"update_#{issuable.to_ability_name}", issuable) -%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row #{'is-disabled' unless can_update}", 'data-iid' => issuable.iid, 'data-id' => issuable.id, 'data-url' => polymorphic_path(issuable_url_args) } +%li.issuable-row %span - if show_project_name %strong #{project.name} · @@ -18,10 +17,10 @@ = confidential_icon(issuable) = link_to issuable.title, issuable_url_args, title: issuable.title .issuable-detail - = link_to [project.namespace.becomes(Namespace), project, issuable] do + = link_to [namespace, project, issuable] do %span.issuable-number= issuable.to_reference - - issuable.labels.each do |label| + - labels.each do |label| = link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do - render_colored_label(label) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 680e1f3a4ea..6f6a036b13f 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -1,10 +1,15 @@ - dashboard = local_assigns[:dashboard] -- custom_dom_id = dom_id(@project ? milestone : milestone.milestones.first) +- custom_dom_id = dom_id(milestone.try(:milestones) ? milestone.milestones.first : milestone) %li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } .row .col-sm-6 %strong= link_to truncate(milestone.title, length: 100), milestone_path + - if milestone.is_group_milestone? + %span - Group Milestone + - else + %span - Project Milestone + .col-sm-6 .pull-right.light #{milestone.percent_complete(current_user)}% complete .row @@ -13,31 +18,37 @@ · = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path .col-sm-6= milestone_progress_bar(milestone) - - if milestone.is_a?(GlobalMilestone) + - if milestone.is_a?(GlobalMilestone) || milestone.is_group_milestone? .row .col-sm-6 - .expiration= render('shared/milestone_expired', milestone: milestone) - .projects - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.label.label-gray - = dashboard ? milestone.project.name_with_namespace : milestone.project.name + - if milestone.is_legacy_group_milestone? + .expiration= render('shared/milestone_expired', milestone: milestone) + .projects + - milestone.milestones.each do |milestone| + = link_to milestone_path(milestone) do + %span.label.label-gray + = dashboard ? milestone.project.name_with_namespace : milestone.project.name - if @group - .col-sm-6 + .col-sm-6.milestone-actions - if can?(current_user, :admin_milestones, @group) + - if milestone.is_group_milestone? + = link_to edit_group_milestone_path(@group, milestone.id), class: "btn btn-xs btn-grouped" do + Edit + \ - if milestone.closed? - = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen" + = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen" - else - = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-xs btn-close" + = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-xs btn-grouped btn-close" - if @project .row - .col-sm-6= render('shared/milestone_expired', milestone: milestone) + .col-sm-6 + = render('shared/milestone_expired', milestone: milestone) .col-sm-6.milestone-actions - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs btn-grouped" do + = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-xs btn-grouped" do Edit \ - = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped" - = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove btn-grouped" do + = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped" + = link_to project_milestone_path(milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove btn-grouped" do Delete diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml index 9bb87640319..895fb8247b5 100644 --- a/app/views/shared/milestones/_sidebar.html.haml +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -21,7 +21,7 @@ .title Start date - if @project && can?(current_user, :admin_milestone, @project) - = link_to 'Edit', edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: 'edit-link pull-right' + = link_to 'Edit', edit_project_milestone_path(@project, @milestone), class: 'edit-link pull-right' .value %span.value-content - if milestone.start_date @@ -51,7 +51,7 @@ .title.hide-collapsed Due date - if @project && can?(current_user, :admin_milestone, @project) - = link_to 'Edit', edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: 'edit-link pull-right' + = link_to 'Edit', edit_project_milestone_path(@project, @milestone), class: 'edit-link pull-right' .value.hide-collapsed %span.value-content - if milestone.due_date @@ -73,7 +73,7 @@ Issues %span.badge= milestone.issues_visible_to_user(current_user).count - if project && can?(current_user, :create_issue, project) - = link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "pull-right", title: "New Issue" do + = link_to new_project_issue_path(project, issue: { milestone_id: milestone.id }), class: "pull-right", title: "New Issue" do New issue .value.hide-collapsed.bold %span.milestone-stat diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml index 4de8a6cb15f..b95a4ea674d 100644 --- a/app/views/shared/milestones/_tabs.html.haml +++ b/app/views/shared/milestones/_tabs.html.haml @@ -1,8 +1,10 @@ +- issues_accessible = milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project) + .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller .fade-left= icon('angle-left') .fade-right= icon('angle-right') %ul.nav-links.scrolling-tabs.js-milestone-tabs - - if milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project) + - if issues_accessible %li.active = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do Issues @@ -25,13 +27,14 @@ Labels %span.badge= milestone.labels.count +- issues = milestone.sorted_issues(current_user) - show_project_name = local_assigns.fetch(:show_project_name, false) - show_full_project_name = local_assigns.fetch(:show_full_project_name, false) .tab-content.milestone-content - - if milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project) - .tab-pane.active#tab-issues{ data: { sort_endpoint: (sort_issues_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } } - = render 'shared/milestones/issues_tab', issues: milestone.sorted_issues(current_user), show_project_name: show_project_name, show_full_project_name: show_full_project_name + - if issues_accessible + .tab-pane.active#tab-issues{ data: { sort_endpoint: (sort_issues_project_milestone_path(@project, @milestone) if @project && current_user) } } + = render 'shared/milestones/issues_tab', issues: issues, show_project_name: show_project_name, show_full_project_name: show_full_project_name .tab-pane#tab-merge-requests -# loaded async = render "shared/milestones/tab_loading" diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 2562f085338..b93837e3087 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -22,39 +22,55 @@ - if group .pull-right - if can?(current_user, :admin_milestones, group) + - if milestone.is_group_milestone? + = link_to edit_group_milestone_path(group, milestone.iid), class: "btn btn btn-grouped" do + Edit - if milestone.active? - = link_to 'Close Milestone', group_milestone_path(group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" + = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" - else - = link_to 'Reopen Milestone', group_milestone_path(group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" + = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" .detail-page-description.milestone-detail %h2.title = markdown_field(milestone, :title) + - if @milestone.is_group_milestone? && @milestone.description.present? + %div + .description + .wiki + = markdown_field(@milestone, :description) - if milestone.complete?(current_user) && milestone.active? .alert.alert-success.prepend-top-default - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.' %span All issues for this milestone are closed. #{close_msg} -.table-holder - %table.table - %thead - %tr - %th Project - %th Open issues - %th State - %th Due date - - milestone.milestones.each do |ms| - %tr - %td - - project_name = group ? ms.project.name : ms.project.name_with_namespace - = link_to project_name, namespace_project_milestone_path(ms.project.namespace, ms.project, ms) - %td - = ms.issues_visible_to_user(current_user).opened.count - %td - - if ms.closed? - Closed - - else - Open - %td - = ms.expires_at +- if @milestone.is_legacy_group_milestone? || @milestone.is_dashboard_milestone? + .table-holder + %table.table + %thead + %tr + %th Project + %th Open issues + %th State + %th Due date + - milestone.milestones.each do |ms| + %tr + %td + - project_name = group ? ms.project.name : ms.project.name_with_namespace + = link_to project_name, project_milestone_path(ms.project, ms) + %td + = ms.issues_visible_to_user(current_user).opened.count + %td + - if ms.closed? + Closed + - else + Open + %td + = ms.expires_at +- elsif @milestone.is_group_milestone? + %br + View + = link_to 'Issues', issues_group_path(@group, milestone_title: milestone.title) + or + = link_to 'Merge Requests', merge_requests_group_path(@group, milestone_title: milestone.title) + in this milestone diff --git a/app/views/shared/notes/_comment_button.html.haml b/app/views/shared/notes/_comment_button.html.haml index 29cf5825292..1dfe380db16 100644 --- a/app/views/shared/notes/_comment_button.html.haml +++ b/app/views/shared/notes/_comment_button.html.haml @@ -1,6 +1,6 @@ - noteable_name = @note.noteable.human_class_name -.pull-left.btn-group.append-right-10.comment-type-dropdown.js-comment-type-dropdown +.pull-left.btn-group.append-right-10.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown %input.btn.btn-nr.btn-create.comment-btn.js-comment-button.js-comment-submit-button{ type: 'submit', value: 'Comment' } - if @note.can_be_discussion_note? @@ -9,8 +9,8 @@ %ul#resolvable-comment-menu.dropdown-menu{ data: { dropdown: true } } %li#comment.droplab-item-selected{ data: { value: '', 'submit-text' => 'Comment', 'close-text' => "Comment & close #{noteable_name}", 'reopen-text' => "Comment & reopen #{noteable_name}" } } - %a{ href: '#' } - = icon('check') + %button.btn.btn-transparent + = icon('check', class: 'icon') .description %strong Comment %p @@ -19,8 +19,8 @@ %li.divider.droplab-item-ignore %li#discussion{ data: { value: 'DiscussionNote', 'submit-text' => 'Start discussion', 'close-text' => "Start discussion & close #{noteable_name}", 'reopen-text' => "Start discussion & reopen #{noteable_name}" } } - %a{ href: '#' } - = icon('check') + %button.btn.btn-transparent + = icon('check', class: 'icon') .description %strong Start discussion %p diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index 1e34b7c1e76..7174855e176 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -60,6 +60,6 @@ = link_to note.attachment.url, target: '_blank' do = icon('paperclip') = note.attachment_identifier - = link_to delete_attachment_namespace_project_note_path(note.project.namespace, note.project, note), + = link_to delete_attachment_project_note_path(note.project, note), title: 'Delete this attachment', method: :delete, remote: true, data: { confirm: 'Are you sure you want to remove the attachment?' }, class: 'danger js-note-attachment-delete' do = icon('trash-o', class: 'cred') diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 8c3d6351ac2..4bdbc26a4c3 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -31,7 +31,7 @@ - if show_last_commit_as_description .description.prepend-top-5 - = link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit), + = link_to_gfm project.commit.title, project_commit_path(project, project.commit), class: "commit-row-message" - elsif project.description.present? .description.prepend-top-5 diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 8549cb91b03..43322978749 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -36,6 +36,6 @@ = f.submit 'Save changes', class: "btn-save btn" - if @snippet.project_id - = link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel" + = link_to "Cancel", project_snippets_path(@project), class: "btn btn-cancel" - else = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel" diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index 5d2d2317f22..7388f20a9fd 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -30,7 +30,7 @@ - if link_project && snippet.project_id? %span.hidden-xs in - = link_to namespace_project_path(snippet.project.namespace, snippet.project) do + = link_to project_path(snippet.project) do = snippet.project.name_with_namespace .pull-right.snippet-updated-at diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml index d1e88274878..805a346a85e 100644 --- a/app/views/users/calendar_activities.html.haml +++ b/app/views/users/calendar_activities.html.haml @@ -12,7 +12,7 @@ - if event.push? #{event.action_name} #{event.ref_type} %strong - - commits_path = namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) + - commits_path = project_commits_path(event.project, event.ref_name) = link_to_if event.project.repository.branch_exists?(event.ref_name), event.ref_name, commits_path - else = event_action_name(event) diff --git a/app/workers/background_migration_worker.rb b/app/workers/background_migration_worker.rb index e85e221d353..45ce49bb5c0 100644 --- a/app/workers/background_migration_worker.rb +++ b/app/workers/background_migration_worker.rb @@ -2,18 +2,34 @@ class BackgroundMigrationWorker include Sidekiq::Worker include DedicatedSidekiqQueue - # Schedules a number of jobs in bulk + # Enqueues a number of jobs in bulk. # # The `jobs` argument should be an Array of Arrays, each sub-array must be in # the form: # # [migration-class, [arg1, arg2, ...]] - def self.perform_bulk(*jobs) + def self.perform_bulk(jobs) Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => jobs) end + # Schedules multiple jobs in bulk, with a delay. + # + def self.perform_bulk_in(delay, jobs) + now = Time.now.to_i + schedule = now + delay.to_i + + if schedule <= now + raise ArgumentError, 'The schedule time must be in the future!' + end + + Sidekiq::Client.push_bulk('class' => self, + 'queue' => sidekiq_options['queue'], + 'args' => jobs, + 'at' => schedule) + end + # Performs the background migration. # # See Gitlab::BackgroundMigration.perform for more information. diff --git a/app/workers/expire_job_cache_worker.rb b/app/workers/expire_job_cache_worker.rb index 08e281e7350..e383202260d 100644 --- a/app/workers/expire_job_cache_worker.rb +++ b/app/workers/expire_job_cache_worker.rb @@ -18,18 +18,10 @@ class ExpireJobCacheWorker private def project_pipeline_path(project, pipeline) - Gitlab::Routing.url_helpers.namespace_project_pipeline_path( - project.namespace, - project, - pipeline, - format: :json) + Gitlab::Routing.url_helpers.project_pipeline_path(project, pipeline, format: :json) end def project_job_path(project, job) - Gitlab::Routing.url_helpers.namespace_project_build_path( - project.namespace, - project, - job.id, - format: :json) + Gitlab::Routing.url_helpers.project_build_path(project, job.id, format: :json) end end diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb index 92e622285de..7c02d6cf892 100644 --- a/app/workers/expire_pipeline_cache_worker.rb +++ b/app/workers/expire_pipeline_cache_worker.rb @@ -23,42 +23,24 @@ class ExpirePipelineCacheWorker private def project_pipelines_path(project) - Gitlab::Routing.url_helpers.namespace_project_pipelines_path( - project.namespace, - project, - format: :json) + Gitlab::Routing.url_helpers.project_pipelines_path(project, format: :json) end def project_pipeline_path(project, pipeline) - Gitlab::Routing.url_helpers.namespace_project_pipeline_path( - project.namespace, - project, - pipeline, - format: :json) + Gitlab::Routing.url_helpers.project_pipeline_path(project, pipeline, format: :json) end def commit_pipelines_path(project, commit) - Gitlab::Routing.url_helpers.pipelines_namespace_project_commit_path( - project.namespace, - project, - commit.id, - format: :json) + Gitlab::Routing.url_helpers.pipelines_project_commit_path(project, commit.id, format: :json) end def new_merge_request_pipelines_path(project) - Gitlab::Routing.url_helpers.namespace_project_new_merge_request_path( - project.namespace, - project, - format: :json) + Gitlab::Routing.url_helpers.project_new_merge_request_path(project, format: :json) end def each_pipelines_merge_request_path(project, pipeline) pipeline.all_merge_requests.each do |merge_request| - path = Gitlab::Routing.url_helpers.pipelines_namespace_project_merge_request_path( - project.namespace, - project, - merge_request, - format: :json) + path = Gitlab::Routing.url_helpers.pipelines_project_merge_request_path(project, merge_request, format: :json) yield(path) end |