diff options
author | Phil Hughes <me@iamphill.com> | 2017-07-24 14:36:31 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-07-24 14:36:31 +0100 |
commit | 4d2be5bbec25d60a8d478bda5bc83159f2c845b1 (patch) | |
tree | c3c28a833ccd2a6628d928acee44e62ccfcc6fd8 /app | |
parent | 1a2d180e3dfd8bdad94766a2e8b1195288c2b146 (diff) | |
parent | d4c4dec80dc3abd39116440a3c291c19b27258e1 (diff) | |
download | gitlab-ce-4d2be5bbec25d60a8d478bda5bc83159f2c845b1.tar.gz |
Merge branch 'master' into sidebar-fly-out-sub-nav
Diffstat (limited to 'app')
173 files changed, 1175 insertions, 531 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index ae19592ecbe..ffe97c071ba 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -1,4 +1,5 @@ /* 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 ProjectSelect */ /* global ShortcutsNavigation */ /* global IssuableIndex */ /* global ShortcutsIssuable */ @@ -40,7 +41,6 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater'; import Landing from './landing'; import BlobForkSuggestion from './blob/blob_fork_suggestion'; import UserCallout from './user_callout'; -import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags'; import ShortcutsWiki from './shortcuts_wiki'; import Pipelines from './pipelines'; import BlobViewer from './blob/viewer/index'; @@ -157,6 +157,9 @@ import PerformanceBar from './performance_bar'; shortcut_handler = new ShortcutsIssuable(); new ZenMode(); break; + case 'dashboard:milestones:index': + new ProjectSelect(); + break; case 'projects:milestones:show': case 'groups:milestones:show': case 'dashboard:milestones:show': @@ -166,6 +169,7 @@ import PerformanceBar from './performance_bar'; case 'groups:issues': case 'groups:merge_requests': new UsersSelect(); + new ProjectSelect(); break; case 'dashboard:todos:index': new Todos(); @@ -259,6 +263,7 @@ import PerformanceBar from './performance_bar'; break; case 'dashboard:issues': case 'dashboard:merge_requests': + new ProjectSelect(); new UsersSelect(); break; case 'projects:commit:show': @@ -390,12 +395,6 @@ import PerformanceBar from './performance_bar'; new Search(); break; case 'projects:settings:repository:show': - // Initialize Protected Branch Settings - new gl.ProtectedBranchCreate(); - new gl.ProtectedBranchEditList(); - // Initialize Protected Tag Settings - new ProtectedTagCreate(); - new ProtectedTagEditList(); // Initialize expandable settings panels initSettingsPanels(); break; diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js index a8fc5b41fb4..2856c8e2862 100644 --- a/app/assets/javascripts/due_date_select.js +++ b/app/assets/javascripts/due_date_select.js @@ -2,6 +2,8 @@ /* global dateFormat */ /* global Pikaday */ +import DateFix from './lib/utils/datefix'; + class DueDateSelect { constructor({ $dropdown, $loading } = {}) { const $dropdownParent = $dropdown.closest('.dropdown'); @@ -43,14 +45,13 @@ class DueDateSelect { initDatePicker() { const $dueDateInput = $(`input[name='${this.fieldName}']`); - + const dateFix = DateFix.dashedFix($dueDateInput.val()); const calendar = new Pikaday({ field: $dueDateInput.get(0), theme: 'gitlab-theme', format: 'yyyy-mm-dd', onSelect: (dateText) => { const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd'); - $dueDateInput.val(formattedDate); if (this.$dropdown.hasClass('js-issue-boards-due-date')) { @@ -62,7 +63,7 @@ class DueDateSelect { } }); - calendar.setDate(new Date($dueDateInput.val())); + calendar.setDate(dateFix); this.$datePicker.append(calendar.el); this.$datePicker.data('pikaday', calendar); } @@ -168,6 +169,7 @@ class DueDateSelectors { initMilestoneDatePicker() { $('.datepicker').each(function() { const $datePicker = $(this); + const dateFix = DateFix.dashedFix($datePicker.val()); const calendar = new Pikaday({ field: $datePicker.get(0), theme: 'gitlab-theme animate-picker', @@ -177,7 +179,8 @@ class DueDateSelectors { $datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); } }); - calendar.setDate(new Date($datePicker.val())); + + calendar.setDate(dateFix); $datePicker.data('pikaday', calendar); }); diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js index 5838b1bdbb7..a81389ab088 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js @@ -2,8 +2,9 @@ import Filter from '~/droplab/plugins/filter'; import './filtered_search_dropdown'; class DropdownHint extends gl.FilteredSearchDropdown { - constructor(droplab, dropdown, input, tokenKeys, filter) { - super(droplab, dropdown, input, filter); + constructor(options = {}) { + const { input, tokenKeys } = options; + super(options); this.config = { Filter: { template: 'hint', diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js index 34a9e34070c..2615d626c4c 100644 --- a/app/assets/javascripts/filtered_search/dropdown_non_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js @@ -5,8 +5,9 @@ import Filter from '~/droplab/plugins/filter'; import './filtered_search_dropdown'; class DropdownNonUser extends gl.FilteredSearchDropdown { - constructor(droplab, dropdown, input, tokenKeys, filter, endpoint, symbol) { - super(droplab, dropdown, input, filter); + constructor(options = {}) { + const { input, endpoint, symbol } = options; + super(options); this.symbol = symbol; this.config = { Ajax: { diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js index 19fed771197..7246ccbb281 100644 --- a/app/assets/javascripts/filtered_search/dropdown_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_user.js @@ -5,8 +5,9 @@ import './filtered_search_dropdown'; import { addClassIfElementExists } from '../lib/utils/dom_utils'; class DropdownUser extends gl.FilteredSearchDropdown { - constructor(droplab, dropdown, input, tokenKeys, filter) { - super(droplab, dropdown, input, filter); + constructor(options = {}) { + const { tokenKeys } = options; + super(options); this.config = { AjaxFilter: { endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`, diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js index 4209ca0d6e2..9e9a9ef74be 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js @@ -1,7 +1,7 @@ const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger'; class FilteredSearchDropdown { - constructor(droplab, dropdown, input, filter) { + constructor({ droplab, dropdown, input, filter }) { this.droplab = droplab; this.hookId = input && input.id; this.input = input; diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index 6bc6bc43f51..61cef435209 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -42,13 +42,19 @@ class FilteredSearchDropdownManager { milestone: { reference: null, gl: 'DropdownNonUser', - extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'], + extraArguments: { + endpoint: `${this.baseEndpoint}/milestones.json`, + symbol: '%', + }, element: this.container.querySelector('#js-dropdown-milestone'), }, label: { reference: null, gl: 'DropdownNonUser', - extraArguments: [`${this.baseEndpoint}/labels.json`, '~'], + extraArguments: { + endpoint: `${this.baseEndpoint}/labels.json`, + symbol: '~', + }, element: this.container.querySelector('#js-dropdown-label'), }, hint: { @@ -97,13 +103,19 @@ class FilteredSearchDropdownManager { let forceShowList = false; if (!mappingKey.reference) { - const dl = this.droplab; - const defaultArguments = - [null, dl, element, this.filteredSearchInput, this.filteredSearchTokenKeys, key]; - const glArguments = defaultArguments.concat(mappingKey.extraArguments || []); + const defaultArguments = { + droplab: this.droplab, + dropdown: element, + input: this.filteredSearchInput, + tokenKeys: this.filteredSearchTokenKeys, + filter: key, + }; + const extraArguments = mappingKey.extraArguments || {}; + const glArguments = Object.assign({}, defaultArguments, extraArguments); // Passing glArguments to `new gl[glClass](<arguments>)` - mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))(); + mappingKey.reference = + new (Function.prototype.bind.apply(gl[glClass], [null, glArguments]))(); } if (firstLoad) { diff --git a/app/assets/javascripts/group_name.js b/app/assets/javascripts/group_name.js index 37c6765d942..3e483b69fd2 100644 --- a/app/assets/javascripts/group_name.js +++ b/app/assets/javascripts/group_name.js @@ -5,12 +5,15 @@ export default class GroupName { constructor() { this.titleContainer = document.querySelector('.js-title-container'); this.title = this.titleContainer.querySelector('.title'); - this.titleWidth = this.title.offsetWidth; - this.groupTitle = this.titleContainer.querySelector('.group-title'); - this.groups = this.titleContainer.querySelectorAll('.group-path'); - this.toggle = null; - this.isHidden = false; - this.init(); + + if (this.title) { + this.titleWidth = this.title.offsetWidth; + this.groupTitle = this.titleContainer.querySelector('.group-title'); + this.groups = this.titleContainer.querySelectorAll('.group-path'); + this.toggle = null; + this.isHidden = false; + this.init(); + } } init() { diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js index 4f376599ba9..d314f3c4d43 100644 --- a/app/assets/javascripts/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js @@ -86,10 +86,23 @@ export default class IssuableBulkUpdateSidebar { this.toggleCheckboxDisplay(enable); if (enable) { + this.initAffix(); SidebarHeightManager.init(); } } + initAffix() { + if (!this.$sidebar.hasClass('affix-top')) { + const offsetTop = $('.scrolling-tabs-container').outerHeight() + $('.sub-nav-scroll').outerHeight(); + + this.$sidebar.affix({ + offset: { + top: offsetTop, + }, + }); + } + } + updateSelectedIssuableIds() { this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds()); } diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index e0363e091a7..5c1ba416a03 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -1,6 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, vars-on-top, max-len */ -import Cookies from 'js-cookie'; import _ from 'underscore'; +import Cookies from 'js-cookie'; +import NewNavSidebar from './new_sidebar'; import initFlyOutNav from './fly_out_nav'; (function() { @@ -55,10 +56,13 @@ import initFlyOutNav from './fly_out_nav'; } $(() => { - $(window).on('scroll', _.throttle(applyScrollNavClass, 100)); - if (Cookies.get('new_nav') === 'true') { + const newNavSidebar = new NewNavSidebar(); + newNavSidebar.bindEvents(); + initFlyOutNav(); } + + $(window).on('scroll', _.throttle(applyScrollNavClass, 100)); }); }).call(window); diff --git a/app/assets/javascripts/lib/utils/datefix.js b/app/assets/javascripts/lib/utils/datefix.js new file mode 100644 index 00000000000..990dc3f6d1a --- /dev/null +++ b/app/assets/javascripts/lib/utils/datefix.js @@ -0,0 +1,8 @@ +const DateFix = { + dashedFix(val) { + const [y, m, d] = val.split('-'); + return new Date(y, m - 1, d); + }, +}; + +export default DateFix; diff --git a/app/assets/javascripts/new_sidebar.js b/app/assets/javascripts/new_sidebar.js new file mode 100644 index 00000000000..5f98aff8ced --- /dev/null +++ b/app/assets/javascripts/new_sidebar.js @@ -0,0 +1,23 @@ +export default class NewNavSidebar { + constructor() { + this.initDomElements(); + } + + initDomElements() { + this.$sidebar = $('.nav-sidebar'); + this.$overlay = $('.mobile-overlay'); + this.$openSidebar = $('.toggle-mobile-nav'); + this.$closeSidebar = $('.close-nav-button'); + } + + bindEvents() { + this.$openSidebar.on('click', () => this.toggleSidebarNav(true)); + this.$closeSidebar.on('click', () => this.toggleSidebarNav(false)); + this.$overlay.on('click', () => this.toggleSidebarNav(false)); + } + + toggleSidebarNav(show) { + this.$sidebar.toggleClass('nav-sidebar-expanded', show); + this.$overlay.toggleClass('mobile-nav-open', show); + } +} diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index 9896b88d487..ebcefc819f5 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -104,6 +104,14 @@ import Api from './api'; dropdownCssClass: "ajax-project-dropdown" }); }); + + $('.new-project-item-select-button').on('click', function() { + $('.project-item-select', this.parentNode).select2('open'); + }); + + $('.project-item-select').on('click', function() { + window.location = `${$(this).val()}/${this.dataset.relativePath}`; + }); } return ProjectSelect; diff --git a/app/assets/javascripts/protected_branches/index.js b/app/assets/javascripts/protected_branches/index.js new file mode 100644 index 00000000000..c9e7af127d2 --- /dev/null +++ b/app/assets/javascripts/protected_branches/index.js @@ -0,0 +1,9 @@ +/* eslint-disable no-unused-vars */ + +import ProtectedBranchCreate from './protected_branch_create'; +import ProtectedBranchEditList from './protected_branch_edit_list'; + +$(() => { + const protectedBranchCreate = new ProtectedBranchCreate(); + const protectedBranchEditList = new ProtectedBranchEditList(); +}); diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js index 42993a252c3..38b1406a99f 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js +++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js @@ -1,31 +1,26 @@ -/* eslint-disable arrow-parens, no-param-reassign, object-shorthand, no-else-return, comma-dangle, max-len */ +export default class ProtectedBranchAccessDropdown { + constructor(options) { + this.options = options; + this.initDropdown(); + } -(global => { - global.gl = global.gl || {}; - - gl.ProtectedBranchAccessDropdown = class { - constructor(options) { - const { $dropdown, data, onSelect } = options; - - $dropdown.glDropdown({ - data: data, - selectable: true, - inputId: $dropdown.data('input-id'), - fieldName: $dropdown.data('field-name'), - toggleLabel(item, el) { - if (el.is('.is-active')) { - return item.text; - } else { - return 'Select'; - } - }, - clicked(opts) { - const { e } = opts; - - e.preventDefault(); - onSelect(); + initDropdown() { + const { $dropdown, data, onSelect } = this.options; + $dropdown.glDropdown({ + data, + selectable: true, + inputId: $dropdown.data('input-id'), + fieldName: $dropdown.data('field-name'), + toggleLabel(item, $el) { + if ($el.is('.is-active')) { + return item.text; } - }); - } - }; -})(window); + return 'Select'; + }, + clicked(options) { + options.e.preventDefault(); + onSelect(); + }, + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js index 57ea2f52814..10da3783123 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_create.js +++ b/app/assets/javascripts/protected_branches/protected_branch_create.js @@ -1,55 +1,51 @@ -/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ -/* global ProtectedBranchDropdown */ - -(global => { - global.gl = global.gl || {}; - - gl.ProtectedBranchCreate = class { - constructor() { - this.$wrap = this.$form = $('#new_protected_branch'); - this.buildDropdowns(); - } - - buildDropdowns() { - const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); - const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); - - // Cache callback - this.onSelectCallback = this.onSelect.bind(this); - - // Allowed to Merge dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: $allowedToMergeDropdown, - data: gon.merge_access_levels, - onSelect: this.onSelectCallback - }); - - // Allowed to Push dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: $allowedToPushDropdown, - data: gon.push_access_levels, - onSelect: this.onSelectCallback - }); - - // Select default - $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0); - $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0); - - // Protected branch dropdown - new ProtectedBranchDropdown({ - $dropdown: this.$wrap.find('.js-protected-branch-select'), - onSelect: this.onSelectCallback - }); - } - - // This will run after clicked callback - onSelect() { - // Enable submit button - const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]'); - const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); - const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); - - this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length)); - } - }; -})(window); +import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; +import ProtectedBranchDropdown from './protected_branch_dropdown'; + +export default class ProtectedBranchCreate { + constructor() { + this.$form = $('.js-new-protected-branch'); + this.buildDropdowns(); + } + + buildDropdowns() { + const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge'); + const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push'); + + // Cache callback + this.onSelectCallback = this.onSelect.bind(this); + + // Allowed to Merge dropdown + this.protectedBranchMergeAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: $allowedToMergeDropdown, + data: gon.merge_access_levels, + onSelect: this.onSelectCallback, + }); + + // Allowed to Push dropdown + this.protectedBranchPushAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: $allowedToPushDropdown, + data: gon.push_access_levels, + onSelect: this.onSelectCallback, + }); + + // Select default + $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0); + $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0); + + // Protected branch dropdown + this.protectedBranchDropdown = new ProtectedBranchDropdown({ + $dropdown: this.$form.find('.js-protected-branch-select'), + onSelect: this.onSelectCallback, + }); + } + + // This will run after clicked callback + onSelect() { + // Enable submit button + const $branchInput = this.$form.find('input[name="protected_branch[name]"]'); + const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); + const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); + + this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length)); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js index bc6110fcd4e..cc0b2ebe071 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js +++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js @@ -1,6 +1,10 @@ -/* eslint-disable comma-dangle, no-unused-vars */ - -class ProtectedBranchDropdown { +export default class ProtectedBranchDropdown { + /** + * @param {Object} options containing + * `$dropdown` target element + * `onSelect` event callback + * $dropdown must be an element created using `dropdown_branch()` rails helper + */ constructor(options) { this.onSelect = options.onSelect; this.$dropdown = options.$dropdown; @@ -12,7 +16,7 @@ class ProtectedBranchDropdown { this.bindEvents(); // Hide footer - this.$dropdownFooter.addClass('hidden'); + this.toggleFooter(true); } buildDropdown() { @@ -21,7 +25,7 @@ class ProtectedBranchDropdown { filterable: true, remote: false, search: { - fields: ['title'] + fields: ['title'], }, selectable: true, toggleLabel(selected) { @@ -36,10 +40,9 @@ class ProtectedBranchDropdown { }, onFilter: this.toggleCreateNewButton.bind(this), clicked: (options) => { - const { $el, e } = options; - e.preventDefault(); + options.e.preventDefault(); this.onSelect(); - } + }, }); } @@ -64,20 +67,22 @@ class ProtectedBranchDropdown { } toggleCreateNewButton(branchName) { - this.selectedBranch = { - title: branchName, - id: branchName, - text: branchName - }; - if (branchName) { + this.selectedBranch = { + title: branchName, + id: branchName, + text: branchName, + }; + this.$dropdownContainer .find('.js-create-new-protected-branch code') .text(branchName); } - this.$dropdownFooter.toggleClass('hidden', !branchName); + this.toggleFooter(!branchName); } -} -window.ProtectedBranchDropdown = ProtectedBranchDropdown; + toggleFooter(toggleState) { + this.$dropdownFooter.toggleClass('hidden', toggleState); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js index 6ef59e94384..3b920942a3f 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js @@ -1,69 +1,67 @@ -/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ +/* eslint-disable no-new */ /* global Flash */ -(global => { - global.gl = global.gl || {}; +import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; - gl.ProtectedBranchEdit = class { - constructor(options) { - this.$wrap = options.$wrap; - this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); - this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); +export default class ProtectedBranchEdit { + constructor(options) { + this.$wrap = options.$wrap; + this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); + this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); + this.onSelectCallback = this.onSelect.bind(this); - this.buildDropdowns(); - } + this.buildDropdowns(); + } - buildDropdowns() { - // Allowed to merge dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: this.$allowedToMergeDropdown, - data: gon.merge_access_levels, - onSelect: this.onSelect.bind(this) - }); + buildDropdowns() { + // Allowed to merge dropdown + this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: this.$allowedToMergeDropdown, + data: gon.merge_access_levels, + onSelect: this.onSelectCallback, + }); - // Allowed to push dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: this.$allowedToPushDropdown, - data: gon.push_access_levels, - onSelect: this.onSelect.bind(this) - }); - } + // Allowed to push dropdown + this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: this.$allowedToPushDropdown, + data: gon.push_access_levels, + onSelect: this.onSelectCallback, + }); + } - onSelect() { - const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`); - const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`); + onSelect() { + const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`); + const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`); - // Do not update if one dropdown has not selected any option - if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; + // Do not update if one dropdown has not selected any option + if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; - this.$allowedToMergeDropdown.disable(); - this.$allowedToPushDropdown.disable(); + this.$allowedToMergeDropdown.disable(); + this.$allowedToPushDropdown.disable(); - $.ajax({ - type: 'POST', - url: this.$wrap.data('url'), - dataType: 'json', - data: { - _method: 'PATCH', - protected_branch: { - merge_access_levels_attributes: [{ - id: this.$allowedToMergeDropdown.data('access-level-id'), - access_level: $allowedToMergeInput.val() - }], - push_access_levels_attributes: [{ - id: this.$allowedToPushDropdown.data('access-level-id'), - access_level: $allowedToPushInput.val() - }] - } + $.ajax({ + type: 'POST', + url: this.$wrap.data('url'), + dataType: 'json', + data: { + _method: 'PATCH', + protected_branch: { + merge_access_levels_attributes: [{ + id: this.$allowedToMergeDropdown.data('access-level-id'), + access_level: $allowedToMergeInput.val(), + }], + push_access_levels_attributes: [{ + id: this.$allowedToPushDropdown.data('access-level-id'), + access_level: $allowedToPushInput.val(), + }], }, - error() { - $.scrollTo(0); - new Flash('Failed to update branch!'); - } - }).always(() => { - this.$allowedToMergeDropdown.enable(); - this.$allowedToPushDropdown.enable(); - }); - } - }; -})(window); + }, + error() { + new Flash('Failed to update branch!', null, $('.js-protected-branches-list')); + }, + }).always(() => { + this.$allowedToMergeDropdown.enable(); + this.$allowedToPushDropdown.enable(); + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js index 336fa6c57a7..b40d3827c30 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js @@ -1,18 +1,18 @@ -/* eslint-disable arrow-parens, no-param-reassign, no-new, comma-dangle */ +/* eslint-disable no-new */ -(global => { - global.gl = global.gl || {}; +import ProtectedBranchEdit from './protected_branch_edit'; - gl.ProtectedBranchEditList = class { - constructor() { - this.$wrap = $('.protected-branches-list'); +export default class ProtectedBranchEditList { + constructor() { + this.$wrap = $('.protected-branches-list'); + this.initEditForm(); + } - // Build edit forms - this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { - new gl.ProtectedBranchEdit({ - $wrap: $(el) - }); + initEditForm() { + this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { + new ProtectedBranchEdit({ + $wrap: $(el), }); - } - }; -})(window); + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js deleted file mode 100644 index 874d70a1431..00000000000 --- a/app/assets/javascripts/protected_branches/protected_branches_bundle.js +++ /dev/null @@ -1,5 +0,0 @@ -import './protected_branch_access_dropdown'; -import './protected_branch_create'; -import './protected_branch_dropdown'; -import './protected_branch_edit'; -import './protected_branch_edit_list'; diff --git a/app/assets/javascripts/protected_tags/index.js b/app/assets/javascripts/protected_tags/index.js index 61e7ba53862..b1618e24e49 100644 --- a/app/assets/javascripts/protected_tags/index.js +++ b/app/assets/javascripts/protected_tags/index.js @@ -1,2 +1,9 @@ -export { default as ProtectedTagCreate } from './protected_tag_create'; -export { default as ProtectedTagEditList } from './protected_tag_edit_list'; +/* eslint-disable no-unused-vars */ + +import ProtectedTagCreate from './protected_tag_create'; +import ProtectedTagEditList from './protected_tag_edit_list'; + +$(() => { + const protectedtTagCreate = new ProtectedTagCreate(); + const protectedtTagEditList = new ProtectedTagEditList(); +}); diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index e71bf04aec7..99eea97377c 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -190,14 +190,6 @@ display: none; } - .btn, - .dropdown, - .dropdown-toggle, - input, - form { - height: 35px; - } - input { display: inline-block; position: relative; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 542b641e3dd..49b2f0e43a4 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -92,7 +92,6 @@ @mixin maintain-sidebar-dimensions { display: block; width: $gutter-width; - padding: 10px 0; } .issues-bulk-update.right-sidebar { @@ -104,6 +103,15 @@ &.right-sidebar-expanded { @include maintain-sidebar-dimensions; width: $gutter-width; + + .issuable-sidebar-header { + // matches `.top-area .nav-controls` for issuable index pages + padding: 11px 0; + } + + .block:last-of-type { + border: none; + } } &.right-sidebar-collapsed { diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 77b7d901f9a..8a58c1ed567 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -116,9 +116,12 @@ blockquote p { color: $gl-grayish-blue !important; - margin: 0; font-size: inherit; line-height: 1.5; + + &:last-child { + margin: 0; + } } p { diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index 393d5006e24..9f3e278ebfc 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -41,10 +41,22 @@ header.navbar-gitlab-new { } } + .logo-text { + line-height: initial; + + svg { + width: 55px; + height: 15px; + margin: 0; + fill: $white-light; + } + } + &:hover, &:focus { - color: $tanuki-yellow; - text-decoration: none; + .logo-text svg { + fill: $tanuki-yellow; + } } } } @@ -274,9 +286,7 @@ header.navbar-gitlab-new { .breadcrumbs { display: flex; - min-height: 60px; - padding-top: $gl-padding-top; - padding-bottom: $gl-padding-top; + min-height: 61px; color: $gl-text-color; border-bottom: 1px solid $border-color; @@ -300,6 +310,7 @@ header.navbar-gitlab-new { display: flex; width: 100%; position: relative; + align-items: center; .dropdown-menu-projects { margin-top: -$gl-padding; @@ -330,7 +341,7 @@ header.navbar-gitlab-new { white-space: nowrap; > a { - &:last-of-type { + &:last-of-type:not(:first-child) { font-weight: 600; } } @@ -384,6 +395,7 @@ header.navbar-gitlab-new { &::after { content: "/"; margin: 0 2px 0 5px; + color: rgba($black, .65); } } @@ -396,3 +408,13 @@ header.navbar-gitlab-new { color: $gl-text-color; } } + +.top-area { + .nav-controls-new-nav { + .dropdown { + @media (min-width: $screen-sm-min) { + margin-right: 0; + } + } + } +} diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 43c4d52d1e2..78f278c1669 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -23,44 +23,82 @@ $new-sidebar-width: 220px; position: fixed; height: 100%; } + + .issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header { + padding: 10px 0 15px; + } } .context-header { - border-bottom: 1px solid $border-color; - font-weight: 600; - display: flex; - align-items: center; - padding: 10px 16px 10px 10px; - color: $gl-text-color; - - .avatar-container { - flex: 0 0 40px; - background-color: $white-light; - } + position: relative; - &:hover { - background-color: $hover-background; - color: $hover-color; - border-color: $hover-background; + a { + border-bottom: 1px solid $border-color; + font-weight: 600; + display: flex; + align-items: center; + padding: 10px 16px 10px 10px; + color: $gl-text-color; - .avatar-container { - border-color: transparent; + @media (max-width: $screen-xs-max) { + padding-right: 30px; } - .settings-avatar { - background-color: $indigo-500; + &:hover { + background-color: $hover-background; + color: $hover-color; + border-color: $hover-background; - i { - color: $hover-color; + .avatar-container { + border-color: transparent; + } + + .settings-avatar { + background-color: $indigo-500; + + i { + color: $hover-color; + } } } } + .avatar-container { + flex: 0 0 40px; + background-color: $white-light; + } + .project-title, .group-title { overflow: hidden; text-overflow: ellipsis; } + + + &:hover { + .close-nav-button { + color: $white-light; + } + } + + .close-nav-button { + display: none; + position: absolute; + top: 0; + right: 0; + height: 100%; + background-color: transparent; + border: 0; + padding: 0 10px; + + @media (max-width: $screen-xs-max) { + display: block; + } + + &:hover { + color: $gl-text-color; + } + } } .settings-avatar { @@ -79,7 +117,7 @@ $new-sidebar-width: 220px; position: fixed; z-index: 400; width: $new-sidebar-width; - transition: width $sidebar-transition-duration; + transition: left $sidebar-transition-duration; top: 50px; bottom: 0; left: 0; @@ -87,6 +125,10 @@ $new-sidebar-width: 220px; background-color: $gray-normal; box-shadow: inset -2px 0 0 $border-color; + &.nav-sidebar-expanded { + left: 0; + } + a { transition: none; text-decoration: none; @@ -117,7 +159,7 @@ $new-sidebar-width: 220px; } @media (max-width: $screen-xs-max) { - width: 0; + left: (-$new-sidebar-width); } } @@ -223,6 +265,38 @@ $new-sidebar-width: 220px; } } +.toggle-mobile-nav { + display: none; + background-color: transparent; + border: 0; + padding: 6px 16px; + margin: 0 16px 0 -15px; + height: 46px; + border-right: 1px solid $gl-text-color-quaternary; + + i { + font-size: 20px; + color: $gl-text-color-secondary; + } + + @media (max-width: $screen-xs-max) { + display: inline-block; + } +} + +.mobile-overlay { + display: none; + + &.mobile-nav-open { + display: block; + position: fixed; + background-color: $black-transparent; + height: 100%; + width: 100%; + z-index: 300; + } +} + // Make issue boards full-height now that sub-nav is gone @@ -232,7 +306,7 @@ $new-sidebar-width: 220px; @media (min-width: $screen-sm-min) { height: 475px; // Needed for PhantomJS // scss-lint:disable DuplicateProperty - height: calc(100vh - 120px); + height: calc(100vh - 180px); // scss-lint:enable DuplicateProperty } } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index a5e4c3311f8..fd0871ec0b8 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -54,7 +54,11 @@ .mr-widget-pipeline-graph { display: inline-block; vertical-align: middle; - margin: 0 -6px 0 0; + margin-right: 4px; + + .stage-cell .stage-container { + margin: 3px 3px 3px 0; + } .dropdown-menu { margin-top: 11px; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 235c475ff26..22672614e0d 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -376,3 +376,18 @@ table.u2f-registrations { } } } + +.nav-wip { + border: 1px solid $blue-500; + background: $blue-25; + padding: $gl-padding; + margin-bottom: $gl-padding; + + a { + color: $blue-500; + } + + p:last-child { + margin-bottom: 0; + } +} diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index c1423965d0a..a3e07a36c33 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -742,7 +742,8 @@ pre.light-well { } } -.protected-tags-list { +.protected-tags-list, +.protected-branches-list { .dropdown-menu-toggle { width: 100%; max-width: 300px; diff --git a/app/controllers/admin/hook_logs_controller.rb b/app/controllers/admin/hook_logs_controller.rb index aa069b89563..3017f96c26f 100644 --- a/app/controllers/admin/hook_logs_controller.rb +++ b/app/controllers/admin/hook_logs_controller.rb @@ -10,9 +10,9 @@ class Admin::HookLogsController < Admin::ApplicationController end def retry - status, message = hook.execute(hook_log.request_data, hook_log.trigger) + result = hook.execute(hook_log.request_data, hook_log.trigger) - set_hook_execution_notice(status, message) + set_hook_execution_notice(result) redirect_to edit_admin_hook_path(@hook) end diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 054c3500b35..77e3c95d197 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -38,9 +38,9 @@ class Admin::HooksController < Admin::ApplicationController end def test - status, message = hook.execute(sample_hook_data, 'system_hooks') + result = TestHooks::SystemService.new(hook, current_user, params[:trigger]).execute - set_hook_execution_notice(status, message) + set_hook_execution_notice(result) redirect_back_or_default end @@ -66,15 +66,4 @@ class Admin::HooksController < Admin::ApplicationController :url ) end - - def sample_hook_data - { - event_name: "project_create", - name: "Ruby", - path: "ruby", - project_id: 1, - owner_name: "Someone", - owner_email: "example@gitlabhq.com" - } - end end diff --git a/app/controllers/concerns/hooks_execution.rb b/app/controllers/concerns/hooks_execution.rb index 846cd60518f..a22e46b4860 100644 --- a/app/controllers/concerns/hooks_execution.rb +++ b/app/controllers/concerns/hooks_execution.rb @@ -3,11 +3,14 @@ module HooksExecution private - def set_hook_execution_notice(status, message) - if status && status >= 200 && status < 400 - flash[:notice] = "Hook executed successfully: HTTP #{status}" - elsif status - flash[:alert] = "Hook executed successfully but returned HTTP #{status} #{message}" + def set_hook_execution_notice(result) + http_status = result[:http_status] + message = result[:message] + + if http_status && http_status >= 200 && http_status < 400 + flash[:notice] = "Hook executed successfully: HTTP #{http_status}" + elsif http_status + flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}" else flash[:alert] = "Hook execution failed: #{message}" end diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index e18778cdf80..b43b2c5621f 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -32,10 +32,10 @@ module IssuableCollections def filter_params set_sort_order_from_cookie - set_default_scope set_default_state - @filter_params = params.dup + # Skip irrelevant Rails routing params + @filter_params = params.dup.except(:controller, :action, :namespace_id) @filter_params[:sort] ||= default_sort_order @sort = @filter_params[:sort] @@ -55,10 +55,6 @@ module IssuableCollections @filter_params end - def set_default_scope - params[:scope] = 'all' if params[:scope].blank? - end - def set_default_state params[:state] = 'opened' if params[:state].blank? end diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index 28c90548cc1..59e5b5e4775 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -1,6 +1,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController include ActionView::Helpers::NumberHelper + before_action :authorize_read_project!, only: :index before_action :find_todos, only: [:index, :destroy_all] def index @@ -49,6 +50,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController private + def authorize_read_project! + project_id = params[:project_id] + + if project_id.present? + project = Project.find(project_id) + render_404 unless can?(current_user, :read_project, project) + end + end + def find_todos @todos ||= TodosFinder.new(current_user, params).execute end diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb index 6c25cd83a24..06ba73d8e8d 100644 --- a/app/controllers/projects/badges_controller.rb +++ b/app/controllers/projects/badges_controller.rb @@ -3,11 +3,11 @@ class Projects::BadgesController < Projects::ApplicationController before_action :authorize_admin_project!, only: [:index] before_action :no_cache_headers, except: [:index] - def build - build_status = Gitlab::Badge::Build::Status + def pipeline + pipeline_status = Gitlab::Badge::Pipeline::Status .new(project, params[:ref]) - render_badge build_status + render_badge pipeline_status end def coverage diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb index b9c4b29580a..745e89fc843 100644 --- a/app/controllers/projects/hook_logs_controller.rb +++ b/app/controllers/projects/hook_logs_controller.rb @@ -14,9 +14,9 @@ class Projects::HookLogsController < Projects::ApplicationController end def retry - status, message = hook.execute(hook_log.request_data, hook_log.trigger) + result = hook.execute(hook_log.request_data, hook_log.trigger) - set_hook_execution_notice(status, message) + set_hook_execution_notice(result) redirect_to edit_project_hook_path(@project, @hook) end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 18895c3f0f3..85d35900c71 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -9,6 +9,10 @@ class Projects::HooksController < Projects::ApplicationController layout "project_settings" + def index + redirect_to project_settings_integrations_path(@project) + end + def create @hook = @project.hooks.new(hook_params) @hook.save @@ -33,13 +37,9 @@ class Projects::HooksController < Projects::ApplicationController end def test - if !@project.empty_repo? - status, message = TestHookService.new.execute(hook, current_user) + result = TestHooks::ProjectService.new(hook, current_user, params[:trigger]).execute - set_hook_execution_notice(status, message) - else - flash[:alert] = 'Hook execution failed. Ensure the project has commits.' - end + set_hook_execution_notice(result) redirect_back_or_default(default: { action: 'index' }) end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 13f03e7e63e..0ac9da2ff0f 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -266,7 +266,7 @@ class Projects::IssuesController < Projects::ApplicationController if action_name == 'new' redirect_to external.new_issue_path else - redirect_to external.project_path + redirect_to external.issue_tracker_path end end diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index ea7ceb3eaa5..15a2ff56b92 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -35,7 +35,7 @@ module Projects def define_badges_variables @ref = params[:ref] || @project.default_branch || 'master' - @badges = [Gitlab::Badge::Build::Status, + @badges = [Gitlab::Badge::Pipeline::Status, Gitlab::Badge::Coverage::Report] @badges.map! do |badge| diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 2e5a6493134..fc63e30c8fb 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -20,9 +20,9 @@ # class IssuableFinder include CreatedAtFilter - + NONE = '0'.freeze - IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze + IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page state].freeze attr_accessor :current_user, :params @@ -89,8 +89,14 @@ class IssuableFinder execute.find_by!(*params) end - def state_counter_cache_key(state) - Digest::SHA1.hexdigest(state_counter_cache_key_components(state).flatten.join('-')) + def state_counter_cache_key + cache_key(state_counter_cache_key_components) + end + + def clear_caches! + state_counter_cache_key_components_permutations.each do |components| + Rails.cache.delete(cache_key(components)) + end end def group @@ -417,12 +423,19 @@ class IssuableFinder params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' end - def state_counter_cache_key_components(state) + def state_counter_cache_key_components opts = params.with_indifferent_access - opts[:state] = state opts.except!(*IRRELEVANT_PARAMS_FOR_CACHE_KEY) opts.delete_if { |_, value| value.blank? } ['issuables_count', klass.to_ability_name, opts.sort] end + + def state_counter_cache_key_components_permutations + [state_counter_cache_key_components] + end + + def cache_key(components) + Digest::SHA1.hexdigest(components.flatten.join('-')) + end end diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index 85230ff1293..0ec42a4e6eb 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -75,7 +75,7 @@ class IssuesFinder < IssuableFinder current_user.blank? || for_counting || params[:for_counting] end - def state_counter_cache_key_components(state) + def state_counter_cache_key_components extra_components = [ user_can_see_all_confidential_issues?, user_cannot_see_confidential_issues?(for_counting: true) @@ -84,6 +84,16 @@ class IssuesFinder < IssuableFinder super + extra_components end + def state_counter_cache_key_components_permutations + # Ignore the last two, as we'll provide both options for them. + components = super.first[0..-3] + + [ + components + [false, true], + components + [true, false] + ] + end + def by_assignee(items) if assignee items.assigned_to(assignee) diff --git a/app/helpers/breadcrumbs_helper.rb b/app/helpers/breadcrumbs_helper.rb new file mode 100644 index 00000000000..abe8edd6a8c --- /dev/null +++ b/app/helpers/breadcrumbs_helper.rb @@ -0,0 +1,25 @@ +module BreadcrumbsHelper + def add_to_breadcrumbs(text, link) + @breadcrumbs_extra_links ||= [] + @breadcrumbs_extra_links.push({ + text: text, + link: link + }) + end + + def breadcrumb_title_link + return @breadcrumb_link if @breadcrumb_link + + if controller.available_action?(:index) + url_for(action: "index") + else + request.path + end + end + + def breadcrumb_title(title) + return if defined?(@breadcrumb_title) + + @breadcrumb_title = title + end +end diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb new file mode 100644 index 00000000000..551b9cca6b1 --- /dev/null +++ b/app/helpers/hooks_helper.rb @@ -0,0 +1,17 @@ +module HooksHelper + def link_to_test_hook(hook, trigger) + path = case hook + when ProjectHook + project = hook.project + test_project_hook_path(project, hook, trigger: trigger) + when SystemHook + test_admin_hook_path(hook, trigger: trigger) + end + + trigger_human_name = trigger.to_s.tr('_', ' ').camelize + + link_to path, rel: 'nofollow' do + content_tag(:span, trigger_human_name) + end + end +end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index d0c518f81f7..425af547330 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -235,7 +235,7 @@ module IssuablesHelper def issuables_count_for_state(issuable_type, state, finder: nil) finder ||= public_send("#{issuable_type}_finder") - cache_key = finder.state_counter_cache_key(state) + cache_key = finder.state_counter_cache_key @counts ||= {} @counts[cache_key] ||= Rails.cache.fetch(cache_key, expires_in: 2.minutes) do diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 3286a92a8a7..b30b2eb1d03 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -4,6 +4,10 @@ module PageLayoutHelper @page_title.push(*titles.compact) if titles.any? + if show_new_nav? && titles.any? && !defined?(@breadcrumb_title) + @breadcrumb_title = @page_title.last + end + # Segments are seperated by middot @page_title.join(" \u00b7 ") end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 26d11f9ab46..9a8d296d514 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -195,7 +195,7 @@ module ProjectsHelper controller.controller_name, controller.action_name, current_application_settings.cache_key, - 'v2.4' + 'v2.5' ] key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status? diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb index a48d4475e97..ce435ca2241 100644 --- a/app/helpers/triggers_helper.rb +++ b/app/helpers/triggers_helper.rb @@ -8,6 +8,6 @@ module TriggersHelper end def service_trigger_url(service) - "#{Settings.gitlab.url}/api/v3/projects/#{service.project_id}/services/#{service.to_param}/trigger" + "#{Settings.gitlab.url}/api/v4/projects/#{service.project_id}/services/#{service.to_param}/trigger" end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 432f3f242eb..416a2a33378 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -96,6 +96,14 @@ module Ci BuildSuccessWorker.perform_async(id) end end + + before_transition any => [:failed] do |build| + next if build.retries_max.zero? + + if build.retries_count < build.retries_max + Ci::Build.retry(build, build.user) + end + end end def detailed_status(current_user) @@ -130,6 +138,14 @@ module Ci success? || failed? || canceled? end + def retries_count + pipeline.builds.retried.where(name: self.name).count + end + + def retries_max + self.options.fetch(:retry, 0).to_i + end + def latest? !retried? end diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb index c62c7e1e936..28623d257a6 100644 --- a/app/models/concerns/editable.rb +++ b/app/models/concerns/editable.rb @@ -4,4 +4,8 @@ module Editable def is_edited? last_edited_at.present? && last_edited_at != created_at end + + def last_edited_by + super || User.ghost + end end diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index ee6165fd32d..a8c424a6614 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -1,11 +1,20 @@ class ProjectHook < WebHook - belongs_to :project + TRIGGERS = { + push_hooks: :push_events, + tag_push_hooks: :tag_push_events, + issue_hooks: :issues_events, + confidential_issue_hooks: :confidential_issues_events, + note_hooks: :note_events, + merge_request_hooks: :merge_requests_events, + job_hooks: :job_events, + pipeline_hooks: :pipeline_events, + wiki_page_hooks: :wiki_page_events + }.freeze + + TRIGGERS.each do |trigger, event| + scope trigger, -> { where(event => true) } + end - scope :issue_hooks, -> { where(issues_events: true) } - scope :confidential_issue_hooks, -> { where(confidential_issues_events: true) } - scope :note_hooks, -> { where(note_events: true) } - scope :merge_request_hooks, -> { where(merge_requests_events: true) } - scope :job_hooks, -> { where(job_events: true) } - scope :pipeline_hooks, -> { where(pipeline_events: true) } - scope :wiki_page_hooks, -> { where(wiki_page_events: true) } + belongs_to :project + validates :project, presence: true end diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index 40e43c27f91..aef11514945 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -1,5 +1,6 @@ class ServiceHook < WebHook belongs_to :service + validates :service, presence: true def execute(data) WebHookService.new(self, data, 'service_hook').execute diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index 1584235ab00..180c479c41b 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -1,5 +1,13 @@ class SystemHook < WebHook - scope :repository_update_hooks, -> { where(repository_update_events: true) } + TRIGGERS = { + repository_update_hooks: :repository_update_events, + push_hooks: :push_events, + tag_push_hooks: :tag_push_events + }.freeze + + TRIGGERS.each do |trigger, event| + scope trigger, -> { where(event => true) } + end default_value_for :push_events, false default_value_for :repository_update_events, true diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 7a9f8997959..5a70e114f56 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -1,22 +1,8 @@ class WebHook < ActiveRecord::Base include Sortable - default_value_for :push_events, true - default_value_for :issues_events, false - default_value_for :confidential_issues_events, false - default_value_for :note_events, false - default_value_for :merge_requests_events, false - default_value_for :tag_push_events, false - default_value_for :job_events, false - default_value_for :pipeline_events, false - default_value_for :repository_update_events, false - default_value_for :enable_ssl_verification, true - 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) } - validates :url, presence: true, url: true def execute(data, hook_name) diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index 420102875a5..88c428b4aae 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -23,7 +23,7 @@ class GitlabIssueTrackerService < IssueTrackerService project_issue_url(project, id: iid) end - def project_path + def issue_tracker_path project_issues_path(project) end diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 1fa4cd4db30..6d6a3ae3647 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -20,8 +20,8 @@ class IssueTrackerService < Service self.issues_url.gsub(':id', iid.to_s) end - def project_path - read_attribute(:project_url) + def issue_tracker_path + project_url end def new_issue_path diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 62f7c057c5b..dee99bbb859 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -59,21 +59,21 @@ class KubernetesService < DeploymentService def fields [ { type: 'text', - name: 'namespace', - title: 'Kubernetes namespace', - placeholder: namespace_placeholder }, - { type: 'text', name: 'api_url', title: 'API URL', placeholder: 'Kubernetes API URL, like https://kube.example.com/' }, - { type: 'text', - name: 'token', - title: 'Service token', - placeholder: 'Service token' }, { type: 'textarea', name: 'ca_pem', - title: 'Custom CA bundle', - placeholder: 'Certificate Authority bundle (PEM format)' } + title: 'CA Certificate', + placeholder: 'Certificate Authority bundle (PEM format)' }, + { type: 'text', + name: 'namespace', + title: 'Project namespace (optional/unique)', + placeholder: namespace_placeholder }, + { type: 'text', + name: 'token', + title: 'Token', + placeholder: 'Service token' } ] end diff --git a/app/models/user.rb b/app/models/user.rb index 8f40af24e20..c26be6d05a2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -385,9 +385,11 @@ class User < ActiveRecord::Base # Return (create if necessary) the ghost user. The ghost user # owns records previously belonging to deleted users. def ghost - unique_internal(where(ghost: true), 'ghost', 'ghost%s@example.com') do |u| + email = 'ghost%s@example.com' + unique_internal(where(ghost: true), 'ghost', email) do |u| u.bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.' u.name = 'Ghost User' + u.notification_email = email end end end diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb index a1d67cbc244..eb345fead2d 100644 --- a/app/services/boards/issues/list_service.rb +++ b/app/services/boards/issues/list_service.rb @@ -33,17 +33,12 @@ module Boards end def filter_params - set_default_scope set_project set_state params end - def set_default_scope - params[:scope] = 'all' - end - def set_project params[:project_id] = project.id end diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 4f35255fb53..273386776fa 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -135,7 +135,7 @@ module Ci end def pipeline_created_counter - @pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_count, "Pipelines created count") + @pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_total, "Counter of pipelines created") end end end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index a03a7abfeb1..9078b1f0983 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -183,7 +183,7 @@ class IssuableBaseService < BaseService after_create(issuable) issuable.create_cross_references!(current_user) execute_hooks(issuable) - invalidate_cache_counts(issuable.assignees, issuable) + invalidate_cache_counts(issuable, users: issuable.assignees) end issuable @@ -240,12 +240,12 @@ class IssuableBaseService < BaseService old_assignees: old_assignees ) - if old_assignees != issuable.assignees - new_assignees = issuable.assignees.to_a - affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees) - invalidate_cache_counts(affected_assignees.compact, issuable) - end + new_assignees = issuable.assignees.to_a + affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees) + # Don't clear the project cache, because it will be handled by the + # appropriate service (close / reopen / merge / etc.). + invalidate_cache_counts(issuable, users: affected_assignees.compact, skip_project_cache: true) after_update(issuable) issuable.create_new_cross_references!(current_user) execute_hooks(issuable, 'update') @@ -339,9 +339,18 @@ class IssuableBaseService < BaseService create_labels_note(issuable, old_labels) if issuable.labels != old_labels end - def invalidate_cache_counts(users, issuable) + def invalidate_cache_counts(issuable, users: [], skip_project_cache: false) users.each do |user| user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts") end + + unless skip_project_cache + case issuable + when Issue + IssuesFinder.new(nil, project_id: issuable.project_id).clear_caches! + when MergeRequest + MergeRequestsFinder.new(nil, project_id: issuable.target_project_id).clear_caches! + end + end end end diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb index 85c616ca576..ddef5281498 100644 --- a/app/services/issues/close_service.rb +++ b/app/services/issues/close_service.rb @@ -28,7 +28,7 @@ module Issues notification_service.close_issue(issue, current_user) if notifications todo_service.close_issue(issue, current_user) execute_hooks(issue, 'close') - invalidate_cache_counts(issue.assignees, issue) + invalidate_cache_counts(issue, users: issue.assignees) end issue diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb index 80ea6312768..73b2e85cba3 100644 --- a/app/services/issues/reopen_service.rb +++ b/app/services/issues/reopen_service.rb @@ -8,7 +8,7 @@ module Issues create_note(issue) notification_service.reopen_issue(issue, current_user) execute_hooks(issue, 'reopen') - invalidate_cache_counts(issue.assignees, issue) + invalidate_cache_counts(issue, users: issue.assignees) end issue diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb index 2ffc989ed71..c0ce01f7523 100644 --- a/app/services/merge_requests/close_service.rb +++ b/app/services/merge_requests/close_service.rb @@ -13,7 +13,7 @@ module MergeRequests notification_service.close_mr(merge_request, current_user) todo_service.close_merge_request(merge_request, current_user) execute_hooks(merge_request, 'close') - invalidate_cache_counts(merge_request.assignees, merge_request) + invalidate_cache_counts(merge_request, users: merge_request.assignees) end merge_request diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb index f0d998731d7..261a8bfa200 100644 --- a/app/services/merge_requests/post_merge_service.rb +++ b/app/services/merge_requests/post_merge_service.rb @@ -13,7 +13,7 @@ module MergeRequests create_note(merge_request) notification_service.merge_mr(merge_request, current_user) execute_hooks(merge_request, 'merge') - invalidate_cache_counts(merge_request.assignees, merge_request) + invalidate_cache_counts(merge_request, users: merge_request.assignees) end private diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb index f2fddf7f345..52f6d511f98 100644 --- a/app/services/merge_requests/reopen_service.rb +++ b/app/services/merge_requests/reopen_service.rb @@ -10,7 +10,7 @@ module MergeRequests execute_hooks(merge_request, 'reopen') merge_request.reload_diff(current_user) merge_request.mark_as_unchecked - invalidate_cache_counts(merge_request.assignees, merge_request) + invalidate_cache_counts(merge_request, users: merge_request.assignees) end merge_request diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb index c92f070601c..a02eee4961b 100644 --- a/app/services/metrics_service.rb +++ b/app/services/metrics_service.rb @@ -31,6 +31,6 @@ class MetricsService end def multiprocess_metrics_path - @multiprocess_metrics_path ||= Rails.root.join(ENV['prometheus_multiproc_dir']).freeze + ::Prometheus::Client.configuration.multiprocess_files_dir end end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index ed476fc9d0c..bd58a54592f 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -4,7 +4,7 @@ class SystemHooksService end def execute_hooks(data, hooks_scope = :all) - SystemHook.send(hooks_scope).each do |hook| + SystemHook.public_send(hooks_scope).find_each do |hook| hook.async_execute(data, 'system_hooks') end end diff --git a/app/services/test_hook_service.rb b/app/services/test_hook_service.rb deleted file mode 100644 index 280c81f7d2d..00000000000 --- a/app/services/test_hook_service.rb +++ /dev/null @@ -1,6 +0,0 @@ -class TestHookService - def execute(hook, current_user) - data = Gitlab::DataBuilder::Push.build_sample(hook.project, current_user) - hook.execute(data, 'push_hooks') - end -end diff --git a/app/services/test_hooks/base_service.rb b/app/services/test_hooks/base_service.rb new file mode 100644 index 00000000000..74ba814afff --- /dev/null +++ b/app/services/test_hooks/base_service.rb @@ -0,0 +1,41 @@ +module TestHooks + class BaseService + attr_accessor :hook, :current_user, :trigger + + def initialize(hook, current_user, trigger) + @hook = hook + @current_user = current_user + @trigger = trigger + end + + def execute + trigger_data_method = "#{trigger}_data" + + if !self.respond_to?(trigger_data_method, true) || + !hook.class::TRIGGERS.value?(trigger.to_sym) + + return error('Testing not available for this hook') + end + + error_message = catch(:validation_error) do + sample_data = self.__send__(trigger_data_method) + + return hook.execute(sample_data, trigger) + end + + error(error_message) + end + + private + + def error(message, http_status = nil) + result = { + message: message, + status: :error + } + + result[:http_status] = http_status if http_status + result + end + end +end diff --git a/app/services/test_hooks/project_service.rb b/app/services/test_hooks/project_service.rb new file mode 100644 index 00000000000..01d5d774cd5 --- /dev/null +++ b/app/services/test_hooks/project_service.rb @@ -0,0 +1,63 @@ +module TestHooks + class ProjectService < TestHooks::BaseService + private + + def project + @project ||= hook.project + end + + def push_events_data + throw(:validation_error, 'Ensure the project has at least one commit.') if project.empty_repo? + + Gitlab::DataBuilder::Push.build_sample(project, current_user) + end + + alias_method :tag_push_events_data, :push_events_data + + def note_events_data + note = project.notes.first + throw(:validation_error, 'Ensure the project has notes.') unless note.present? + + Gitlab::DataBuilder::Note.build(note, current_user) + end + + def issues_events_data + issue = project.issues.first + throw(:validation_error, 'Ensure the project has issues.') unless issue.present? + + issue.to_hook_data(current_user) + end + + alias_method :confidential_issues_events_data, :issues_events_data + + def merge_requests_events_data + merge_request = project.merge_requests.first + throw(:validation_error, 'Ensure the project has merge requests.') unless merge_request.present? + + merge_request.to_hook_data(current_user) + end + + def job_events_data + build = project.builds.first + throw(:validation_error, 'Ensure the project has CI jobs.') unless build.present? + + Gitlab::DataBuilder::Build.build(build) + end + + def pipeline_events_data + pipeline = project.pipelines.first + throw(:validation_error, 'Ensure the project has CI pipelines.') unless pipeline.present? + + Gitlab::DataBuilder::Pipeline.build(pipeline) + end + + def wiki_page_events_data + page = project.wiki.pages.first + if !project.wiki_enabled? || page.blank? + throw(:validation_error, 'Ensure the wiki is enabled and has pages.') + end + + Gitlab::DataBuilder::WikiPage.build(page, current_user, 'create') + end + end +end diff --git a/app/services/test_hooks/system_service.rb b/app/services/test_hooks/system_service.rb new file mode 100644 index 00000000000..76c3c19bd74 --- /dev/null +++ b/app/services/test_hooks/system_service.rb @@ -0,0 +1,48 @@ +module TestHooks + class SystemService < TestHooks::BaseService + private + + def project + @project ||= begin + project = Project.first + + throw(:validation_error, 'Ensure that at least one project exists.') unless project + + project + end + end + + def push_events_data + if project.empty_repo? + throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.") + end + + Gitlab::DataBuilder::Push.build_sample(project, current_user) + end + + def tag_push_events_data + if project.repository.tags.empty? + throw(:validation_error, "Ensure project \"#{project.human_name}\" has tags.") + end + + Gitlab::DataBuilder::Push.build_sample(project, current_user) + end + + def repository_update_events_data + commit = project.commit + ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}" + + unless commit + throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.") + end + + change = Gitlab::DataBuilder::Repository.single_change( + commit.parent_id || Gitlab::Git::BLANK_SHA, + commit.id, + ref + ) + + Gitlab::DataBuilder::Repository.update(project, current_user, [change], [ref]) + end + end +end diff --git a/app/services/users/migrate_to_ghost_user_service.rb b/app/services/users/migrate_to_ghost_user_service.rb index 4628c4c6f6e..3a9c151cf9b 100644 --- a/app/services/users/migrate_to_ghost_user_service.rb +++ b/app/services/users/migrate_to_ghost_user_service.rb @@ -50,10 +50,12 @@ module Users def migrate_issues user.issues.update_all(author_id: ghost_user.id) + Issue.where(last_edited_by_id: user.id).update_all(last_edited_by_id: ghost_user.id) end def migrate_merge_requests user.merge_requests.update_all(author_id: ghost_user.id) + MergeRequest.where(merge_user_id: user.id).update_all(merge_user_id: ghost_user.id) end def migrate_notes diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb index 4241b912d5b..a5110a23cad 100644 --- a/app/services/web_hook_service.rb +++ b/app/services/web_hook_service.rb @@ -39,7 +39,11 @@ class WebHookService execution_duration: Time.now - start_time ) - [response.code, response.to_s] + { + status: :success, + http_status: response.code, + message: response.to_s + } rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e log_execution( trigger: hook_name, @@ -52,7 +56,10 @@ class WebHookService Rails.logger.error("WebHook Error => #{e}") - [nil, e.to_s] + { + status: :error, + message: e.to_s + } end def async_execute diff --git a/app/services/wiki_pages/base_service.rb b/app/services/wiki_pages/base_service.rb index 14317ea65c8..260c04a8b94 100644 --- a/app/services/wiki_pages/base_service.rb +++ b/app/services/wiki_pages/base_service.rb @@ -1,23 +1,9 @@ module WikiPages class BaseService < ::BaseService - def hook_data(page, action) - hook_data = { - object_kind: page.class.name.underscore, - user: current_user.hook_attrs, - project: @project.hook_attrs, - wiki: @project.wiki.hook_attrs, - object_attributes: page.hook_attrs - } - - page_url = Gitlab::UrlBuilder.build(page) - hook_data[:object_attributes].merge!(url: page_url, action: action) - hook_data - end - private def execute_hooks(page, action = 'create') - page_data = hook_data(page, action) + page_data = Gitlab::DataBuilder::WikiPage.build(page, current_user, action) @project.execute_hooks(page_data, :wiki_page_hooks) @project.execute_services(page_data, :wiki_page_hooks) end diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb index 0da7a025591..05a2091633a 100644 --- a/app/uploaders/gitlab_uploader.rb +++ b/app/uploaders/gitlab_uploader.rb @@ -16,7 +16,7 @@ class GitlabUploader < CarrierWave::Uploader::Base def self.base_dir return root_dir unless file_storage? - File.join(root_dir, 'system') + File.join(root_dir, '-', 'system') end def self.file_storage? diff --git a/app/uploaders/personal_file_uploader.rb b/app/uploaders/personal_file_uploader.rb index 7f857765fbf..ef70871624b 100644 --- a/app/uploaders/personal_file_uploader.rb +++ b/app/uploaders/personal_file_uploader.rb @@ -3,6 +3,10 @@ class PersonalFileUploader < FileUploader File.join(CarrierWave.root, model_path(model)) end + def self.base_dir + File.join(root_dir, 'system') + end + private def secure_url diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 26f7c1a473a..8bb2a563990 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -315,7 +315,9 @@ %fieldset %legend Metrics - Prometheus %p - Enable a Prometheus metrics endpoint at `#{metrics_path}` to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available + Enable a Prometheus metrics endpoint at + %code= metrics_path + to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available = link_to 'here', admin_health_check_path \. This setting requires a = link_to 'restart', help_page_path('administration/restart_gitlab') @@ -327,10 +329,13 @@ = f.label :prometheus_metrics_enabled do = f.check_box :prometheus_metrics_enabled Enable Prometheus Metrics - - unless Gitlab::Metrics.metrics_folder_present? - .help-block - %strong.cred WARNING: - Environment variable `prometheus_multiproc_dir` does not exist or is not pointing to a valid directory. + - unless Gitlab::Metrics.metrics_folder_present? + .help-block + %strong.cred WARNING: + Environment variable + %code prometheus_multiproc_dir + does not exist or is not pointing to a valid directory. + = link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory') %fieldset %legend Profiling - Performance Bar diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml index c596866bde2..13b583e6072 100644 --- a/app/views/admin/applications/edit.html.haml +++ b/app/views/admin/applications/edit.html.haml @@ -1,4 +1,5 @@ - page_title "Edit", @application.name, "Applications" + %h3.page-title Edit application - @url = admin_application_path(@application) = render 'form', application: @application diff --git a/app/views/admin/applications/new.html.haml b/app/views/admin/applications/new.html.haml index 6310d89bd6b..346c58877d9 100644 --- a/app/views/admin/applications/new.html.haml +++ b/app/views/admin/applications/new.html.haml @@ -1,4 +1,6 @@ +- breadcrumb_title "Applications" - page_title "New Application" + %h3.page-title New application - @url = admin_applications_path = render 'form', application: @application diff --git a/app/views/admin/broadcast_messages/edit.html.haml b/app/views/admin/broadcast_messages/edit.html.haml index 45e053eb31d..8cbc4597e32 100644 --- a/app/views/admin/broadcast_messages/edit.html.haml +++ b/app/views/admin/broadcast_messages/edit.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Messages" - page_title "Broadcast Messages" = render 'form' diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml index 4f2ae081d7a..b806882eee3 100644 --- a/app/views/admin/broadcast_messages/index.html.haml +++ b/app/views/admin/broadcast_messages/index.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Messages" - page_title "Broadcast Messages" %h3.page-title diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml index 0e35a1905bf..665e8c7e74f 100644 --- a/app/views/admin/hooks/edit.html.haml +++ b/app/views/admin/hooks/edit.html.haml @@ -12,7 +12,7 @@ = render partial: 'form', locals: { form: f, hook: @hook } .form-actions = f.submit 'Save changes', class: 'btn btn-create' - = link_to 'Test hook', test_admin_hook_path(@hook), class: 'btn btn-default' + = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: @hook = link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' } %hr diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index e92b8bc39f4..fed6002528d 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -22,12 +22,12 @@ - @hooks.each do |hook| %li .controls - = link_to 'Test hook', test_admin_hook_path(hook), class: 'btn btn-sm' + = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: hook, button_class: 'btn-small' = link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm' = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm' .monospace= hook.url %div - - %w(repository_update_events push_events tag_push_events issues_events note_events merge_requests_events job_events).each do |trigger| - - if hook.send(trigger) - %span.label.label-gray= trigger.titleize + - SystemHook::TRIGGERS.each_value do |event| + - if hook.public_send(event) + %span.label.label-gray= event.to_s.titleize %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'} diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 2da8f615470..126550ee10e 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -2,26 +2,6 @@ = render "admin/dashboard/head" %div{ class: container_class } - - %p.prepend-top-default - %span - To register a new Runner you should enter the following registration - token. - With this token the Runner will request a unique Runner token and use - that for future communication. - %br - Registration token is - %code#runners-token= current_application_settings.runners_registration_token - - .bs-callout.clearfix - .pull-left - %p - You can reset runners registration token by pressing a button below. - .prepend-top-10 - = button_to "Reset runners registration token", reset_runners_token_admin_application_settings_path, - method: :put, class: 'btn btn-default', - data: { confirm: 'Are you sure you want to reset registration token?' } - .bs-callout %p A 'Runner' is a process which runs a job. @@ -46,6 +26,19 @@ %span.label.label-danger paused \- Runner will not receive any new jobs + .bs-callout.clearfix + .pull-left + %p + You can reset runners registration token by pressing a button below. + .prepend-top-10 + = button_to _("Reset runners registration token"), reset_runners_token_admin_application_settings_path, + method: :put, class: 'btn btn-default', + data: { confirm: _("Are you sure you want to reset registration token?") } + + = render partial: 'ci/runner/how_to_setup_runner', + locals: { registration_token: current_application_settings.runners_registration_token, + type: 'shared' } + .append-bottom-20.clearfix .pull-left = form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml new file mode 100644 index 00000000000..b75dab0acc5 --- /dev/null +++ b/app/views/ci/runner/_how_to_setup_runner.html.haml @@ -0,0 +1,16 @@ +- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank' +.bs-callout.help-callout + %h4= _("How to setup a #{type} Runner for a new project") + + %ol + %li + = _("Install a Runner compatible with GitLab CI") + = (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe + %li + = _("Specify the following URL during the Runner setup:") + %code= root_url(only_path: false) + %li + = _("Use the following registration token during setup:") + %code#registration_token= registration_token + %li + = _("Start the Runner!") diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml index 4594c52b34b..5a379eae8f4 100644 --- a/app/views/dashboard/_groups_head.html.haml +++ b/app/views/dashboard/_groups_head.html.haml @@ -1,3 +1,7 @@ +- if show_new_nav? && current_user.can_create_group? + - content_for :breadcrumbs_extra do + = link_to "New group", new_group_path, class: "btn btn-new" + .top-area %ul.nav-links = nav_link(page: dashboard_groups_path) do @@ -6,9 +10,8 @@ = nav_link(page: explore_groups_path) do = link_to explore_groups_path, title: 'Explore public groups' do Explore public groups - .nav-controls + .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) } = render 'shared/groups/search_form' = render 'shared/groups/dropdown' - if current_user.can_create_group? - = link_to new_group_path, class: "btn btn-new" do - New group + = link_to "New group", new_group_path, class: "btn btn-new #{("visible-xs" if show_new_nav?)}" diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 64b737ee886..1f9a5b401b6 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -1,5 +1,10 @@ = content_for :flash_message do = render 'shared/project_limit' + +- if show_new_nav? && current_user.can_create_project? + - content_for :breadcrumbs_extra do + = link_to "New project", new_project_path, class: 'btn btn-new' + .top-area.scrolling-tabs-container.inner-page-scroll-tabs .fade-left= icon('angle-left') .fade-right= icon('angle-right') @@ -14,9 +19,8 @@ = link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do Explore projects - .nav-controls + .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) } = render 'shared/projects/search_form' = render 'shared/projects/dropdown' - if current_user.can_create_project? - = link_to new_project_path, class: 'btn btn-new' do - New project + = link_to "New project", new_project_path, class: "btn btn-new #{("visible-xs" if show_new_nav?)}" diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml index 02e90bbfa55..fd5389106bb 100644 --- a/app/views/dashboard/_snippets_head.html.haml +++ b/app/views/dashboard/_snippets_head.html.haml @@ -1,3 +1,7 @@ +- if show_new_nav? && current_user + - content_for :breadcrumbs_extra do + = link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet" + .top-area %ul.nav-links = nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do @@ -8,6 +12,5 @@ Explore Snippets - if current_user - .nav-controls.hidden-xs - = link_to new_snippet_path, class: "btn btn-new", title: "New snippet" do - New snippet + .nav-controls.hidden-xs{ class: ("hidden-sm hidden-md hidden-lg" if show_new_nav?) } + = link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet" diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index d6b46dee0e4..52e0012fd7d 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -1,11 +1,18 @@ +- @hide_top_links = true - page_title "Issues" - header_title "Issues", issues_dashboard_path(assignee_id: current_user.id) = content_for :meta_tags do = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{current_user.name} issues") +- if show_new_nav? + - content_for :breadcrumbs_extra do + = link_to params.merge(rss_url_options), class: 'btn has-tooltip append-right-10', title: 'Subscribe' do + = icon('rss') + = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues' + .top-area = render 'shared/issuable/nav', type: :issues - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } = link_to params.merge(rss_url_options), class: 'btn has-tooltip', title: 'Subscribe' do = icon('rss') = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues' diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml index 6f6afe161d1..c3fe14da2b2 100644 --- a/app/views/dashboard/merge_requests.html.haml +++ b/app/views/dashboard/merge_requests.html.haml @@ -1,9 +1,14 @@ +- @hide_top_links = true - page_title "Merge Requests" - header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id) +- if show_new_nav? + - content_for :breadcrumbs_extra do + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests' + .top-area = render 'shared/issuable/nav', type: :merge_requests - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests' = render 'shared/issuable/filter', type: :merge_requests diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml index ef1467c4d78..37dbcaf5cb8 100644 --- a/app/views/dashboard/milestones/index.html.haml +++ b/app/views/dashboard/milestones/index.html.haml @@ -2,10 +2,14 @@ - page_title 'Milestones' - header_title 'Milestones', dashboard_milestones_path +- if show_new_nav? + - content_for :breadcrumbs_extra do + = render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true + .top-area = render 'shared/milestones_filter', counts: @milestone_states - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } = render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true .milestones diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index 7ac6cf06fb9..ec6cb1a9624 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -1,6 +1,5 @@ - @no_container = true - @hide_top_links = true -- @breadcrumb_title = "Projects" = content_for :meta_tags do = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity") diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml index 99efe9c9b86..ae1d733a516 100644 --- a/app/views/dashboard/projects/starred.html.haml +++ b/app/views/dashboard/projects/starred.html.haml @@ -1,5 +1,6 @@ +- @hide_top_links = true - @no_container = true - +- breadcrumb_title "Projects" - page_title "Starred Projects" - header_title "Projects", dashboard_projects_path diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 52d6ebd8a14..9b615ec999e 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -1,3 +1,4 @@ +- @hide_top_links = true - page_title "Todos" - header_title "Todos", dashboard_todos_path diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml index e80d10dc8f1..bfd7dd25a7d 100644 --- a/app/views/devise/shared/_omniauth_box.html.haml +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -7,6 +7,6 @@ %span.light - has_icon = provider_has_icon?(provider) = 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 + %fieldset.prepend-top-10 = check_box_tag :remember_me - = label_tag :remember_me, 'Remember Me' + = label_tag :remember_me, 'Remember me' diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index ffe07b217a7..2651ef37e67 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -1,3 +1,4 @@ +- @hide_top_links = true - page_title "Groups" - header_title "Groups", dashboard_groups_path diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index ec461755103..f00802e0af7 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -1,3 +1,4 @@ +- @hide_top_links = true - page_title "Projects" - header_title "Projects", dashboard_projects_path diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml index ec461755103..f00802e0af7 100644 --- a/app/views/explore/projects/starred.html.haml +++ b/app/views/explore/projects/starred.html.haml @@ -1,3 +1,4 @@ +- @hide_top_links = true - page_title "Projects" - header_title "Projects", dashboard_projects_path diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml index ec461755103..f00802e0af7 100644 --- a/app/views/explore/projects/trending.html.haml +++ b/app/views/explore/projects/trending.html.haml @@ -1,3 +1,4 @@ +- @hide_top_links = true - page_title "Projects" - header_title "Projects", dashboard_projects_path diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml index e5706d04736..94fc4ac21d2 100644 --- a/app/views/explore/snippets/index.html.haml +++ b/app/views/explore/snippets/index.html.haml @@ -1,3 +1,4 @@ +- @hide_top_links = true - page_title "Snippets" - header_title "Snippets", snippets_path diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 182dbe2f98a..735d9390699 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,12 +1,19 @@ - page_title "Issues" +- group_issues_exists = group_issues(@group).exists? = render "head_issues" = content_for :meta_tags do = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues") -- if group_issues(@group).exists? +- if show_new_nav? && group_issues_exists + - content_for :breadcrumbs_extra do + = link_to params.merge(rss_url_options), class: 'btn btn-default append-right-10' do + = icon('rss') + = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue" + +- if group_issues_exists .top-area = render 'shared/issuable/nav', type: :issues - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } = link_to params.merge(rss_url_options), class: 'btn' do = icon('rss') %span.icon-label diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 2bc00fb16c8..50179a47797 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -1,14 +1,18 @@ - page_title 'Labels' +- if show_new_nav? && can?(current_user, :admin_label, @group) + - content_for :breadcrumbs_extra do + = link_to "New label", new_group_label_path(@group), class: "btn btn-new" + = render "groups/head_issues" + .top-area.adjust .nav-text Labels can be applied to issues and merge requests. Group labels are available for any project within the group. - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } - if can?(current_user, :admin_label, @group) - = link_to new_group_label_path(@group), class: "btn btn-new" do - New label + = link_to "New label", new_group_label_path(@group), class: "btn btn-new" .labels .other-labels diff --git a/app/views/groups/labels/new.html.haml b/app/views/groups/labels/new.html.haml index 2be87460b1d..ae240490bbd 100644 --- a/app/views/groups/labels/new.html.haml +++ b/app/views/groups/labels/new.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Labels" - page_title 'New Label' - header_title group_title(@group, 'Labels', group_labels_path(@group)) diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 45e39252e16..997c82c77d9 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,12 +1,16 @@ - page_title "Merge Requests" +- if show_new_nav? && current_user + - content_for :breadcrumbs_extra do + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request" + - if @group_merge_requests.empty? = render 'shared/empty_states/merge_requests', project_select_button: true - else .top-area = render 'shared/issuable/nav', type: :merge_requests - if current_user - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request" = render 'shared/issuable/filter', type: :merge_requests diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index 6ceb4092307..66c6cc9e279 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -1,13 +1,16 @@ - page_title "Milestones" +- if show_new_nav? && can?(current_user, :admin_milestones, @group) + - content_for :breadcrumbs_extra do + = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new" + = render "groups/head_issues" .top-area = render 'shared/milestones_filter', counts: @milestone_states - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } - if can?(current_user, :admin_milestones, @group) - = link_to new_group_milestone_path(@group), class: "btn btn-new" do - New milestone + = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new" .milestones %ul.content-list diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index e24844661ee..eca7fb9ddb1 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Milestones" - page_title "Milestones" - header_title group_title(@group, "Milestones", group_milestones_path(@group)) diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 000c7af2326..e9daac95ca1 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -1,3 +1,6 @@ +- @breadcrumb_link = dashboard_groups_path +- breadcrumb_title "Groups" +- @hide_top_links = true - page_title 'New Group' - header_title "Groups", dashboard_groups_path diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 80a8ba4a755..e07f61c94e4 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- breadcrumb_title "Group" = content_for :meta_tags do = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity") diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index cc9219cb6fe..873220cc73d 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -10,12 +10,15 @@ - if content_for?(:sub_nav) = yield :sub_nav .content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" } + - if show_new_nav? + .mobile-overlay .alert-wrapper = render "layouts/broadcast" - if show_new_nav? - if content_for?(:new_global_flash) = yield :new_global_flash - = render "layouts/nav/breadcrumbs" + - unless @hide_breadcrumbs + = render "layouts/nav/breadcrumbs" = render "layouts/flash" = yield :flash_message %div{ class: "#{(container_class unless @no_container)} #{@content_class}" } diff --git a/app/views/layouts/header/_new.html.haml b/app/views/layouts/header/_new.html.haml index 4697d91724b..76367bd9734 100644 --- a/app/views/layouts/header/_new.html.haml +++ b/app/views/layouts/header/_new.html.haml @@ -6,8 +6,8 @@ %h1.title = link_to root_path, title: 'Dashboard' do = brand_header_logo - %span.hidden-xs - GitLab + %span.logo-text.hidden-xs + = render 'shared/logo_type.svg' - if current_user = render "layouts/nav/new_dashboard" diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml index b0c1ab7420f..4db84771f4e 100644 --- a/app/views/layouts/nav/_breadcrumbs.html.haml +++ b/app/views/layouts/nav/_breadcrumbs.html.haml @@ -1,19 +1,27 @@ -- breadcrumb_title = @breadcrumb_title || controller.controller_name.humanize +- breadcrumb_link = breadcrumb_title_link - hide_top_links = @hide_top_links || false %nav.breadcrumbs{ role: "navigation" } .breadcrumbs-container{ class: [container_class, @content_class] } + - if defined?(@new_sidebar) + = button_tag class: 'toggle-mobile-nav', type: 'button' do + %span.sr-only Open sidebar + = icon ('bars') .breadcrumbs-links.js-title-container - unless hide_top_links .title = link_to "GitLab", root_path \/ + - if content_for?(:header_title_before) + = yield :header_title_before + \/ = header_title %h2.breadcrumbs-sub-title %ul.list-unstyled - - if content_for?(:sub_title_before) - = yield :sub_title_before - %li= link_to breadcrumb_title, request.path + - if @breadcrumbs_extra_links + - @breadcrumbs_extra_links.each do |extra| + %li= link_to extra[:text], extra[:link] + %li= link_to @breadcrumb_title, breadcrumb_link - if content_for?(:breadcrumbs_extra) .breadcrumbs-extra.hidden-xs= yield :breadcrumbs_extra = yield :header_content diff --git a/app/views/layouts/nav/_new_admin_sidebar.html.haml b/app/views/layouts/nav/_new_admin_sidebar.html.haml index d7a9e530983..95443de40c2 100644 --- a/app/views/layouts/nav/_new_admin_sidebar.html.haml +++ b/app/views/layouts/nav/_new_admin_sidebar.html.haml @@ -1,8 +1,12 @@ .nav-sidebar - = link_to admin_root_path, title: 'Admin Overview', class: 'context-header' do - .avatar-container.s40.settings-avatar - = icon('wrench') - .project-title Admin Area + .context-header + = link_to admin_root_path, title: 'Admin Overview' do + .avatar-container.s40.settings-avatar + = icon('wrench') + .project-title Admin Area + = button_tag class: 'close-nav-button', type: 'button' do + %span.sr-only Close sidebar + = icon ('times') %ul.sidebar-top-level-items = 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 @@ -13,7 +17,7 @@ = nav_link(controller: :dashboard, html_options: {class: 'home'}) do = link_to admin_root_path, title: 'Overview' do %span - Overview + Dashboard = nav_link(controller: [:admin, :projects]) do = link_to admin_projects_path, title: 'Projects' do %span diff --git a/app/views/layouts/nav/_new_dashboard.html.haml b/app/views/layouts/nav/_new_dashboard.html.haml index 7109baa4dad..cfdfcbebc9f 100644 --- a/app/views/layouts/nav/_new_dashboard.html.haml +++ b/app/views/layouts/nav/_new_dashboard.html.haml @@ -3,7 +3,7 @@ = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do Projects - = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do + = nav_link(controller: ['dashboard/groups', 'explore/groups']) do = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do Groups diff --git a/app/views/layouts/nav/_new_group_sidebar.html.haml b/app/views/layouts/nav/_new_group_sidebar.html.haml index 6e0c45739f1..a7897c09e79 100644 --- a/app/views/layouts/nav/_new_group_sidebar.html.haml +++ b/app/views/layouts/nav/_new_group_sidebar.html.haml @@ -1,9 +1,13 @@ .nav-sidebar - = 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 - = @group.name + .context-header + = link_to group_path(@group), title: @group.name do + .avatar-container.s40.group-avatar + = image_tag group_icon(@group), class: "avatar s40 avatar-tile" + .group-title + = @group.name + = button_tag class: 'close-nav-button', type: 'button' do + %span.sr-only Close sidebar + = icon ('times') %ul.sidebar-top-level-items = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do = link_to group_path(@group), title: 'About group' do diff --git a/app/views/layouts/nav/_new_profile_sidebar.html.haml b/app/views/layouts/nav/_new_profile_sidebar.html.haml index 033ea149cfb..239e6b949e2 100644 --- a/app/views/layouts/nav/_new_profile_sidebar.html.haml +++ b/app/views/layouts/nav/_new_profile_sidebar.html.haml @@ -1,8 +1,12 @@ .nav-sidebar - = link_to profile_path, title: 'Profile Settings', class: 'context-header' do - .avatar-container.s40.settings-avatar - = icon('user') - .project-title User Settings + .context-header + = link_to profile_path, title: 'Profile Settings' do + .avatar-container.s40.settings-avatar + = icon('user') + .project-title User Settings + = button_tag class: 'close-nav-button', type: 'button' do + %span.sr-only Close sidebar + = icon ('times') %ul.sidebar-top-level-items = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = link_to profile_path, title: 'Profile Settings' do diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml index da8f9ce1908..15327847b17 100644 --- a/app/views/layouts/nav/_new_project_sidebar.html.haml +++ b/app/views/layouts/nav/_new_project_sidebar.html.haml @@ -1,10 +1,14 @@ .nav-sidebar - can_edit = can?(current_user, :admin_project, @project) - = 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 - = @project.name + .context-header + = link_to project_path(@project), title: @project.name do + .avatar-container.s40.project-avatar + = project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile') + .project-title + = @project.name + = button_tag class: 'close-nav-button', type: 'button' do + %span.sr-only Close sidebar + = icon ('times') %ul.sidebar-top-level-items = nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do = link_to project_path(@project), title: 'About project', class: 'shortcuts-project' do diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index bd602071384..9aed498a8a0 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -24,6 +24,12 @@ %p This setting allows you to turn on or off the new upcoming navigation concept. .col-lg-8.syntax-theme + .nav-wip + %p + The new navigation is currently a work-in-progress concept and is currently only usable on wide-screens. There are a number of improvements that we are working on in order to further refine our navigation. + %p + %a{ href: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/32794', target: 'blank' } Learn more + about the improvements that are coming soon! = 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/profiles/show.html.haml b/app/views/profiles/show.html.haml index bac75a49075..a8ae0b92334 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Profile" - @content_class = "limit-container-width" unless fluid_layout = render 'profiles/head' diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 67792de3870..037cb30efb9 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -1,6 +1,10 @@ - page_title 'Two-Factor Authentication', 'Account' -- header_title "Two-Factor Authentication", profile_two_factor_auth_path +- if show_new_nav? + - add_to_breadcrumbs("Account", profile_account_path) +- else + - header_title "Two-Factor Authentication", profile_two_factor_auth_path - @content_class = "limit-container-width" unless fluid_layout + = render 'profiles/head' - if inject_u2f_api? diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml index ef8d8051cbf..9e2688e492e 100644 --- a/app/views/projects/activity.html.haml +++ b/app/views/projects/activity.html.haml @@ -1,5 +1,8 @@ - @no_container = true +- if show_new_nav? + - add_to_breadcrumbs("Project", project_path(@project)) + - page_title "Activity" = render "projects/head" diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index f8cb612a2b4..992fe7f717f 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Repository" - @no_container = true - page_title "Edit", @blob.path, @ref - content_for :page_specific_javascripts do diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 8620a470041..a4263774dfd 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Repository" - page_title "New File", @path.presence, @ref - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 6e2ae4717cd..7dd834e84b5 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Repository" - @no_container = true - page_title @blob.path, @ref diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml index 07272ea2df1..2076e46fde8 100644 --- a/app/views/projects/boards/_show.html.haml +++ b/app/views/projects/boards/_show.html.haml @@ -3,8 +3,7 @@ - page_title "Boards" - if show_new_nav? - - content_for :sub_title_before do - %li= link_to "Issues", project_issues_path(@project) + - add_to_breadcrumbs("Issues", project_issues_path(@project)) - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 8bc1996452b..945a5c11d6d 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -2,11 +2,15 @@ - page_title "Branches" = render "projects/commits/head" +- if show_new_nav? + - add_to_breadcrumbs("Repository", project_tree_path(@project)) + %div{ class: container_class } .top-area.adjust - .nav-text - Protected branches can be managed in - = link_to 'project settings', project_protected_branches_path(@project) + - if can?(current_user, :admin_project, @project) + .nav-text + Protected branches can be managed in + = link_to 'project settings', project_protected_branches_path(@project) .nav-controls = form_tag(filter_branches_path, method: :get) do diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index b8547c10c73..844ebb65148 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -1,9 +1,13 @@ - @no_container = true +- breadcrumb_title _("Commits") - page_title _("Commits"), @ref = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") +- if show_new_nav? + - add_to_breadcrumbs("Repository", project_tree_path(@project)) + = content_for :sub_nav do = render "head" diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 2cf14859f30..05de21e8dbf 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -1,5 +1,7 @@ - @no_container = true - page_title "Compare" +- if show_new_nav? + - add_to_breadcrumbs("Repository", project_tree_path(@project)) = render "projects/commits/head" %div{ class: container_class } diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index a1bca2cf83a..8bc863f77b3 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -1,5 +1,8 @@ - @no_container = true +- breadcrumb_title "Compare" - page_title "#{params[:from]}...#{params[:to]}" +- if show_new_nav? + - add_to_breadcrumbs("Repository", project_tree_path(@project)) = render "projects/commits/head" %div{ class: container_class } diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index 7e7b7335597..c704635ead3 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -1,5 +1,7 @@ - @no_container = true - page_title "Cycle Analytics" +- if show_new_nav? + - add_to_breadcrumbs("Project", project_path(@project)) - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('cycle_analytics') diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 30cdbc5ae04..d0f723af5bf 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -2,6 +2,9 @@ - page_title "Environments" = render "projects/pipelines/head" +- if show_new_nav? + - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) + - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag("environments") diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml index 24638c77cbb..88f43a1e7e4 100644 --- a/app/views/projects/environments/new.html.haml +++ b/app/views/projects/environments/new.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- breadcrumb_title "Environments" - page_title 'New Environment' = render "projects/pipelines/head" diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml index 464ac34d961..249b9d82ad9 100644 --- a/app/views/projects/graphs/charts.html.haml +++ b/app/views/projects/graphs/charts.html.haml @@ -1,5 +1,7 @@ - @no_container = true - page_title "Charts" +- if show_new_nav? + - add_to_breadcrumbs("Repository", project_tree_path(@project)) - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_d3') = page_specific_javascript_bundle_tag('graphs') diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 640e0d689ca..4256a8c4d7e 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -3,6 +3,10 @@ - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_d3') = page_specific_javascript_bundle_tag('graphs') + +- if show_new_nav? + - add_to_breadcrumbs("Repository", project_tree_path(@project)) + = render 'projects/commits/head' %div{ class: container_class } diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml index 4944e0c8041..c8c17d2d828 100644 --- a/app/views/projects/hooks/edit.html.haml +++ b/app/views/projects/hooks/edit.html.haml @@ -13,9 +13,10 @@ = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } = f.submit 'Save changes', class: 'btn btn-create' - = link_to 'Test hook', test_project_hook_path(@project, @hook), class: 'btn btn-default' + = render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: @hook = 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/issues/new.html.haml b/app/views/projects/issues/new.html.haml index e8aae0f47e2..60fe442014f 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Issues" - page_title "New Issue" %h3.page-title diff --git a/app/views/projects/jobs/_header.html.haml b/app/views/projects/jobs/_header.html.haml index d81b8f6bb4c..83a2af1dc74 100644 --- a/app/views/projects/jobs/_header.html.haml +++ b/app/views/projects/jobs/_header.html.haml @@ -1,7 +1,7 @@ - show_controls = local_assigns.fetch(:show_controls, true) - pipeline = @build.pipeline -.content-block.build-header.top-area +.content-block.build-header.top-area.page-content-header .header-content = render 'ci/status/badge', status: @build.detailed_status(current_user), link: false, title: @build.status_title %strong diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml index 8604c7d3ea4..d78891546f7 100644 --- a/app/views/projects/jobs/index.html.haml +++ b/app/views/projects/jobs/index.html.haml @@ -2,6 +2,9 @@ - page_title "Jobs" = render "projects/pipelines/head" +- if show_new_nav? + - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) + %div{ class: container_class } .top-area - build_path_proc = ->(scope) { project_jobs_path(@project, scope: scope) } diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 8fbc4588902..d02ea5cccc3 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,6 +1,11 @@ - @no_container = true - page_title "Labels" - hide_class = '' + +- if show_new_nav? && can?(current_user, :admin_label, @project) + - content_for :breadcrumbs_extra do + = link_to "New label", new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" + = render "shared/mr_head" - if @labels.exists? || @prioritized_labels.exists? @@ -9,7 +14,7 @@ .nav-text Labels can be applied to issues and merge requests. Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } - if can?(current_user, :admin_label, @project) = link_to new_project_label_path(@project), class: "btn btn-new" do New label diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index 79e90b7ca3b..562b6fb8d8c 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- breadcrumb_title "Labels" - page_title "New Label" = render "shared/mr_head" diff --git a/app/views/projects/merge_requests/creations/new.html.haml b/app/views/projects/merge_requests/creations/new.html.haml index 2e798ce780a..3220512d60d 100644 --- a/app/views/projects/merge_requests/creations/new.html.haml +++ b/app/views/projects/merge_requests/creations/new.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Merge Requests" - page_title "New Merge Request" - if @merge_request.can_be_created && !params[:change_branches] diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index e53fcd6e425..a89387bc8f1 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -1,5 +1,10 @@ - @no_container = true - page_title 'Milestones' + +- if show_new_nav? + - content_for :breadcrumbs_extra do + = link_to "New milestone", new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New milestone' + = render "shared/mr_head" %div{ class: container_class } diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml index 586eb909afa..84ffbc0a926 100644 --- a/app/views/projects/milestones/new.html.haml +++ b/app/views/projects/milestones/new.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- breadcrumb_title "Milestones" - page_title "New Milestone" = render "shared/mr_head" diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index e8c26636be9..ab948df4a3f 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,6 +1,9 @@ +- breadcrumb_title "Graph" - page_title "Graph", @ref - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('network') +- if show_new_nav? + - add_to_breadcrumbs("Repository", project_tree_path(@project)) = render "projects/commits/head" = render "head" %div{ class: container_class } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index b0b7575f0d1..a2d7a21d5f6 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,3 +1,6 @@ +- @breadcrumb_link = dashboard_projects_path +- breadcrumb_title "Projects" +- @hide_top_links = true - page_title 'New Project' - header_title "Projects", dashboard_projects_path - visibility_level = params.dig(:project, :visibility_level) || default_project_visibility diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml index 97c0407a01d..7343d6e039c 100644 --- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml +++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml @@ -4,7 +4,7 @@ = pipeline_schedule.description %td.branch-name-cell = icon('code-fork') - - if pipeline_schedule.ref + - if pipeline_schedule.ref.present? = link_to pipeline_schedule.ref, project_ref_path(@project, pipeline_schedule.ref), class: "ref-name" %td - if pipeline_schedule.last_pipeline diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml index c4ee064ac43..8426b29bb14 100644 --- a/app/views/projects/pipeline_schedules/index.html.haml +++ b/app/views/projects/pipeline_schedules/index.html.haml @@ -1,9 +1,18 @@ +- breadcrumb_title "Schedules" + - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'schedules_index' - @no_container = true - page_title _("Pipeline Schedules") + +- if show_new_nav? && can?(current_user, :create_pipeline_schedule, @project) + - content_for :breadcrumbs_extra do + = link_to _('New schedule'), new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create' + + - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) + = render "projects/pipelines/head" %div{ class: container_class } @@ -13,7 +22,7 @@ = render "tabs", schedule_path_proc: schedule_path_proc, all_schedules: @all_schedules, scope: @scope - if can?(current_user, :create_pipeline_schedule, @project) - .nav-controls + .nav-controls{ class: ("visible-xs" if show_new_nav?) } = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do %span= _('New schedule') diff --git a/app/views/projects/pipeline_schedules/new.html.haml b/app/views/projects/pipeline_schedules/new.html.haml index 87390d4dd02..c7237cb96d8 100644 --- a/app/views/projects/pipeline_schedules/new.html.haml +++ b/app/views/projects/pipeline_schedules/new.html.haml @@ -1,5 +1,10 @@ +- breadcrumb_title "Schedules" +- @breadcrumb_link = namespace_project_pipeline_schedules_path(@project.namespace, @project) - page_title _("New Pipeline Schedule") +- if show_new_nav? + - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) + %h3.page-title = _("Schedule a new pipeline") %hr diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml index 78002e8cd64..fd3ad69d85d 100644 --- a/app/views/projects/pipelines/charts.html.haml +++ b/app/views/projects/pipelines/charts.html.haml @@ -1,5 +1,7 @@ - @no_container = true - page_title _("Charts"), _("Pipelines") +- if show_new_nav? + - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_d3') = page_specific_javascript_bundle_tag('graphs') diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index 308f2611e02..c966df62856 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Pipelines" - page_title "New Pipeline" %h3.page-title diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 25153fd0b6f..9f7c5a315eb 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,5 +1,8 @@ - page_title "Members" +- if show_new_nav? + - add_to_breadcrumbs("Settings", edit_project_path(@project)) + .row.prepend-top-default .col-lg-12 %h4 diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml index 5c00bb6883c..2a0704bc7af 100644 --- a/app/views/projects/protected_branches/shared/_branches_list.html.haml +++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml @@ -1,4 +1,4 @@ -.panel.panel-default.protected-branches-list +.panel.panel-default.protected-branches-list.js-protected-branches-list - if @protected_branches.empty? .panel-heading %h3.panel-title @@ -23,6 +23,8 @@ - if can_admin_project %th %tbody + %tr + %td.flash-container{ colspan: 5 } = 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 index b619fa57e05..9f0c4f3b3a8 100644 --- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml +++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f| += form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f| .panel.panel-default .panel-heading %h3.panel-title diff --git a/app/views/projects/protected_tags/shared/_tags_list.html.haml b/app/views/projects/protected_tags/shared/_tags_list.html.haml index 6e3cd4ada71..3f42ae58438 100644 --- a/app/views/projects/protected_tags/shared/_tags_list.html.haml +++ b/app/views/projects/protected_tags/shared/_tags_list.html.haml @@ -1,4 +1,4 @@ -.panel.panel-default.protected-tags-list +.panel.panel-default.protected-tags-list.js-protected-tags-list - if @protected_tags.empty? .panel-heading %h3.panel-title diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index f8835454140..28ccbf7eb15 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -1,21 +1,8 @@ %h3 Specific Runners -.bs-callout.help-callout - %h4 How to setup a specific Runner for a new project - - %ol - %li - Install a Runner compatible with GitLab CI - (checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} for information on how to install it). - %li - Specify the following URL during the Runner setup: - %code= root_url(only_path: false) - %li - Use the following registration token during setup: - %code= @project.runners_token - %li - Start the Runner! - += render partial: 'ci/runner/how_to_setup_runner', + locals: { registration_token: @project.runners_token, + type: 'specific' } - if @project_runners.any? %h4.underlined-title Runners activated for this project diff --git a/app/views/projects/services/edit.html.haml b/app/views/projects/services/edit.html.haml index 0f1a76a104a..8056217bb1e 100644 --- a/app/views/projects/services/edit.html.haml +++ b/app/views/projects/services/edit.html.haml @@ -1,3 +1,8 @@ +- breadcrumb_title "Integrations" - page_title @service.title, "Services" + +- if show_new_nav? + - add_to_breadcrumbs("Settings", edit_project_path(@project)) + = render "projects/settings/head" = render 'form' diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 6afb38c5709..0c4130857da 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -1,5 +1,9 @@ - @content_class = "limit-container-width" unless fluid_layout - page_title "Pipelines" + +- if show_new_nav? + - add_to_breadcrumbs("Settings", edit_project_path(@project)) + = render "projects/settings/head" = render 'projects/runners/index' diff --git a/app/views/projects/settings/integrations/_project_hook.html.haml b/app/views/projects/settings/integrations/_project_hook.html.haml index 00700e286c3..d5792e95f5a 100644 --- a/app/views/projects/settings/integrations/_project_hook.html.haml +++ b/app/views/projects/settings/integrations/_project_hook.html.haml @@ -3,14 +3,14 @@ .col-md-8.col-lg-7 %strong.light-header= hook.url %div - - %w(push_events tag_push_events issues_events confidential_issues_events note_events merge_requests_events job_events pipeline_events wiki_page_events).each do |trigger| - - if hook.send(trigger) - %span.label.label-gray.deploy-project-label= trigger.titleize + - ProjectHook::TRIGGERS.each_value do |event| + - if hook.public_send(event) + %span.label.label-gray.deploy-project-label= event.to_s.titleize .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_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 + SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'} + = link_to 'Edit', edit_project_hook_path(@project, hook), class: 'btn btn-sm' + = render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: hook, button_class: 'btn-small' + = 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/integrations/show.html.haml b/app/views/projects/settings/integrations/show.html.haml index 1d1d0849289..149da96d3f6 100644 --- a/app/views/projects/settings/integrations/show.html.haml +++ b/app/views/projects/settings/integrations/show.html.haml @@ -1,5 +1,7 @@ - @content_class = "limit-container-width" unless fluid_layout - page_title 'Integrations' +- if show_new_nav? + - add_to_breadcrumbs("Settings", edit_project_path(@project)) = render "projects/settings/head" = render 'projects/hooks/index' = render 'projects/services/index' diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index 0f20ecf8c69..cb37f3c7580 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -1,5 +1,9 @@ - page_title "Repository" - @content_class = "limit-container-width" unless fluid_layout + +- if show_new_nav? + - add_to_breadcrumbs("Settings", edit_project_path(@project)) + = render "projects/settings/head" - content_for :page_specific_javascripts do diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index a73e111ad6d..49d0a6828fe 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- breadcrumb_title "Project" - @content_class = "limit-container-width" unless fluid_layout - flash_message_container = show_new_nav? ? :new_global_flash : :flash_message diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 4f8ce526c83..ccc5fe80755 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -1,19 +1,16 @@ - page_title "Snippets" +- if show_new_nav? && can?(current_user, :create_project_snippet, @project) + - content_for :breadcrumbs_extra do + = link_to "New snippet", new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New snippet" + - if current_user .top-area - include_private = @project.team.member?(current_user) || current_user.admin? = render partial: 'snippets/snippets_scope_menu', locals: { subject: @project, include_private: include_private } - .nav-controls.hidden-xs + .nav-controls{ class: ("visible-xs" if show_new_nav?) } - if can?(current_user, :create_project_snippet, @project) - = 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_project_snippet_path(@project), class: "btn btn-new btn-block", title: "New snippet" do - New snippet + = link_to "New snippet", new_project_snippet_path(@project), class: "btn btn-new", title: "New snippet" = render 'snippets/snippets' diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index bf97cbc1f68..00000e0667c 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -3,6 +3,9 @@ - page_title "Tags" = render "projects/commits/head" +- if show_new_nav? + - add_to_breadcrumbs("Repository", project_tree_path(@project)) + .flex-list{ class: container_class } .top-area.adjust .nav-text.row-main-content diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index f727f340bb9..c8587245f88 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- breadcrumb_title _("Repository") - @content_class = "limit-container-width" unless fluid_layout - page_title @path.presence || _("Files"), @ref diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 13591dd8e74..9dadd685ea2 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -1,4 +1,5 @@ - @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout +- breadcrumb_title "Wiki" - page_title @page.title.capitalize, "Wiki" .wiki-page-header.has-sidebar-toggle diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index 215dbb3909e..499697f2777 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -1,3 +1,5 @@ +- @hide_top_links = true +- breadcrumb_title "Search" - page_title @search_term .prepend-top-default diff --git a/app/views/shared/_logo_type.svg b/app/views/shared/_logo_type.svg new file mode 100644 index 00000000000..7e9276a0a83 --- /dev/null +++ b/app/views/shared/_logo_type.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 617 169" enable-background="new 0 0 617 169"><path d="m315.26 2.969h-21.8l.1 162.5h88.3v-20.1h-66.5l-.1-142.4"/><path d="m-511.44-6.331c-5.5 5.7-14.6 11.4-27 11.4-16.6 0-23.3-8.2-23.3-18.9 0-16.1 11.2-23.8 35-23.8 4.5 0 11.7.5 15.4 1.2v30.1h-.1m-22.6-98.5c-17.6 0-33.8 6.2-46.4 16.7l7.7 13.4c8.9-5.2 19.8-10.4 35.5-10.4 17.9 0 25.8 9.2 25.8 24.6v7.9c-3.5-.7-10.7-1.2-15.1-1.2-38.2 0-57.6 13.4-57.6 41.4 0 25.1 15.4 37.7 38.7 37.7 15.7 0 30.8-7.2 36-18.9l4 15.9h15.4v-83.2c-.1-26.3-11.5-43.9-44-43.9" transform="translate(977.33 143.28)"/><path d="m-542.14 5.969c-8.2 0-15.4-1-20.8-3.5v-67.3-7.8c7.4-6.2 16.6-10.7 28.3-10.7 21.1 0 29.2 14.9 29.2 39 0 34.2-13.1 50.3-36.7 50.3m9.2-110.6c-19.5 0-30 13.3-30 13.3v-21l-.1-27.8h-9.8-11.5l.1 158.5c10.7 4.5 25.3 6.9 41.2 6.9 40.7 0 60.3-26 60.3-70.9-.1-35.5-18.2-59-50.2-59" transform="translate(1099.77 143.13)"/><path d="m-506.14-123.03c19.3 0 31.8 6.4 39.9 12.9l9.4-16.3c-12.7-11.2-29.9-17.2-48.3-17.2-46.4 0-78.9 28.3-78.9 85.4 0 59.8 35.1 83.1 75.2 83.1 20.1 0 37.2-4.7 48.4-9.4l-.5-63.9v-7.5-12.6h-59.5v20.1h38l.5 48.5c-5 2.5-13.6 4.5-25.3 4.5-32.2 0-53.8-20.3-53.8-63-.1-43.5 22.2-64.6 54.9-64.6" transform="translate(584.04 143.63)"/><path d="m-562.14-139.63h-21.3l.1 27.3v11.2 6.5 11.4 65 .2c0 26.3 11.4 43.9 43.9 43.9 4.5 0 8.9-.4 13.1-1.2v-19.1c-3.1.5-6.4.7-9.9.7-17.9 0-25.8-9.2-25.8-24.6v-65h35.7v-17.8h-35.7l-.1-38.5" transform="translate(793.57 142.58)"/><path d="m155.96 165.47h21.3v-124h-21.3v124"/><path d="m155.96 24.369h21.3v-21.3h-21.3v21.3"/></svg> diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml index 9ed844cf5e7..c1acee1a211 100644 --- a/app/views/shared/_new_project_item_select.html.haml +++ b/app/views/shared/_new_project_item_select.html.haml @@ -1,19 +1,6 @@ - if @projects.any? .project-item-select-holder = project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at' }, with_feature_enabled: local_assigns[:with_feature_enabled] - %a.btn.btn-new.new-project-item-select-button + %a.btn.btn-new.new-project-item-select-button{ data: { relative_path: local_assigns[:path] } } = local_assigns[:label] = icon('caret-down') - - :javascript - $('.new-project-item-select-button').on('click', function() { - $('.project-item-select').select2('open'); - }); - - var relativePath = '#{local_assigns[:path]}'; - - $('.project-item-select').on('click', function() { - window.location = $(this).val() + '/' + relativePath; - }); - - new ProjectSelect() diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml index 046b127f73c..b0c0ab523c7 100644 --- a/app/views/shared/empty_states/_issues.html.haml +++ b/app/views/shared/empty_states/_issues.html.haml @@ -16,7 +16,8 @@ Also, issues are searchable and filterable. - if project_select_button = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue' - = link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link' + - else + = link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link' - else .text-center %h4 There are no issues to show. diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml index 964fe5220f7..0d507cc7a6e 100644 --- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml +++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml @@ -1,9 +1,9 @@ - type = local_assigns.fetch(:type) -%aside.issues-bulk-update.js-right-sidebar.right-sidebar.affix-top{ data: { "offset-top" => "50", "spy" => "affix" }, "aria-live" => "polite" } +%aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } } .issuable-sidebar.hidden = form_tag [:bulk_update, @project.namespace.becomes(Namespace), @project, type], method: :post, class: "bulk-update" do - .block + .block.issuable-sidebar-header .filter-item.inline.update-issues-btn.pull-left = button_tag "Update all", class: "btn update-selected-issues btn-info", disabled: true = button_tag "Cancel", class: "btn btn-default js-bulk-update-menu-hide pull-right" diff --git a/app/views/shared/web_hooks/_test_button.html.haml b/app/views/shared/web_hooks/_test_button.html.haml new file mode 100644 index 00000000000..cf1d5e061c6 --- /dev/null +++ b/app/views/shared/web_hooks/_test_button.html.haml @@ -0,0 +1,12 @@ +- triggers = local_assigns.fetch(:triggers) +- button_class = local_assigns.fetch(:button_class, '') +- hook = local_assigns.fetch(:hook) + +.hook-test-button.dropdown.inline + %button.btn{ 'data-toggle' => 'dropdown', class: button_class } + Test + = icon('caret-down') + %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + - triggers.each_value do |event| + %li + = link_to_test_hook(hook, event) diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml index ca8afb4bb6a..f01915107e3 100644 --- a/app/views/snippets/new.html.haml +++ b/app/views/snippets/new.html.haml @@ -1,3 +1,5 @@ +- @hide_top_links = true +- breadcrumb_title "Snippets" - page_title "New Snippet" %h3.page-title New Snippet diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 8818590362d..706f13dd004 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -1,3 +1,4 @@ +- @hide_top_links = true - @content_class = "limit-container-width limited-inner-width-container" unless fluid_layout - page_title "#{@snippet.title} (#{@snippet.to_reference})", "Snippets" diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index f246bd7a586..919ba5d15d3 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,3 +1,5 @@ +- @hide_top_links = true +- @hide_breadcrumbs = true - page_title @user.name - page_description @user.bio - content_for :page_specific_javascripts do |