Commit 43aa8866 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into 30634-protected-pipeline

* upstream/master: (130 commits)
  Change auto-retry count to a correct value in docs
  Fix background migration cleanup specs
  Fix CI/CD job auto-retry specs
  Fix JS; make buttons sr accessibile; fix overlay
  remove redundant changelog entries
  Merge branch '24570-use-re2-for-user-supplied-regexp-9-3' into 'security-9-3'
  Merge branch '33303-404-for-unauthorized-project' into 'security-9-3'
  Merge branch '33359-pers-snippet-files-location' into 'security-9-3'
  Merge branch 'bvl-remove-appearance-symlink' into 'security-9-3'
  Hide description about protected branches to non-member
  Update CHANGELOG.md for 9.0.11
  Update CHANGELOG.md for 9.1.8
  Update CHANGELOG.md for 8.17.7
  Update CHANGELOG.md for 9.2.8
  Update CHANGELOG.md for 9.3.8
  Respect blockquote line breaks in markdown
  35209 Add wip message to new navigation preference section
  Add github imported projects count to usage data
  Add versions to Prometheus metrics doc
  Add Bulgarian translations of Pipeline Schedules
  ...
parents e9862a99 542b675c
...@@ -160,6 +160,9 @@ build-package: ...@@ -160,6 +160,9 @@ build-package:
when: manual when: manual
script: script:
- scripts/trigger-build - scripts/trigger-build
only:
- //@gitlab-org/gitlab-ce
- //@gitlab-org/gitlab-ee
# Prepare and merge knapsack tests # Prepare and merge knapsack tests
knapsack: knapsack:
...@@ -180,6 +183,7 @@ update-knapsack: ...@@ -180,6 +183,7 @@ update-knapsack:
<<: *only-canonical-masters <<: *only-canonical-masters
stage: post-test stage: post-test
script: script:
- retry gem install fog-aws mime-types
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach-pg_node_*.json - scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach-pg_node_*.json
- '[[ -z ${KNAPSACK_S3_BUCKET} ]] || scripts/sync-reports put $KNAPSACK_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH' - '[[ -z ${KNAPSACK_S3_BUCKET} ]] || scripts/sync-reports put $KNAPSACK_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH'
......
...@@ -2,6 +2,18 @@ ...@@ -2,6 +2,18 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 9.3.8 (2017-07-19)
- Improve support for external issue references. !12485
- Renders 404 if given project is not readable by the user on Todos dashboard.
- Use uploads/system directory for personal snippets.
- Remove uploads/appearance symlink. A leftover from a previous migration.
## 9.3.7 (2017-07-18)
- Prevent bad data being added to application settings when Redis is unavailable. !12750
- Return `is_admin` attribute in the GET /user endpoint for admins. !12811
## 9.3.6 (2017-07-12) ## 9.3.6 (2017-07-12)
- Fix API Scoping. !12300 - Fix API Scoping. !12300
...@@ -258,6 +270,13 @@ entry. ...@@ -258,6 +270,13 @@ entry.
- Remove foreigh key on ci_trigger_schedules only if it exists. - Remove foreigh key on ci_trigger_schedules only if it exists.
- Allow translation of Pipeline Schedules. - Allow translation of Pipeline Schedules.
## 9.2.8 (2017-07-19)
- Improve support for external issue references. !12485
- Renders 404 if given project is not readable by the user on Todos dashboard.
- Fix incorrect project authorizations.
- Remove uploads/appearance symlink. A leftover from a previous migration.
## 9.2.7 (2017-06-21) ## 9.2.7 (2017-06-21)
- Reinstate is_admin flag in users api when authenticated user is an admin. !12211 (rickettm) - Reinstate is_admin flag in users api when authenticated user is an admin. !12211 (rickettm)
...@@ -502,6 +521,13 @@ entry. ...@@ -502,6 +521,13 @@ entry.
- Fix preemptive scroll bar on user activity calendar. - Fix preemptive scroll bar on user activity calendar.
- Pipeline chat notifications convert seconds to minutes and hours. - Pipeline chat notifications convert seconds to minutes and hours.
## 9.1.8 (2017-07-19)
- Improve support for external issue references. !12485
- Renders 404 if given project is not readable by the user on Todos dashboard.
- Fix incorrect project authorizations.
- Remove uploads/appearance symlink. A leftover from a previous migration.
## 9.1.7 (2017-06-07) ## 9.1.7 (2017-06-07)
- No changes. - No changes.
...@@ -814,6 +840,12 @@ entry. ...@@ -814,6 +840,12 @@ entry.
- Only send chat notifications for the default branch. - Only send chat notifications for the default branch.
- Don't fill in the default kubernetes namespace. - Don't fill in the default kubernetes namespace.
## 9.0.11 (2017-07-19)
- Renders 404 if given project is not readable by the user on Todos dashboard.
- Fix incorrect project authorizations.
- Remove uploads/appearance symlink. A leftover from a previous migration.
## 9.0.10 (2017-06-07) ## 9.0.10 (2017-06-07)
- No changes. - No changes.
...@@ -1184,6 +1216,11 @@ entry. ...@@ -1184,6 +1216,11 @@ entry.
- Change development tanuki favicon colors to match logo color order. - Change development tanuki favicon colors to match logo color order.
- API issues - support filtering by iids. - API issues - support filtering by iids.
## 8.17.7 (2017-07-19)
- Renders 404 if given project is not readable by the user on Todos dashboard.
- Fix incorrect project authorizations.
## 8.17.6 (2017-05-05) ## 8.17.6 (2017-05-05)
- Enforce project features when searching blobs and wikis. - Enforce project features when searching blobs and wikis.
......
...@@ -37,7 +37,7 @@ gem 'omniauth-saml', '~> 1.7.0' ...@@ -37,7 +37,7 @@ gem 'omniauth-saml', '~> 1.7.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.3.0' gem 'omniauth-authentiq', '~> 0.3.1'
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt', '~> 1.5.6' gem 'jwt', '~> 1.5.6'
...@@ -163,6 +163,9 @@ gem 'rainbow', '~> 2.2' ...@@ -163,6 +163,9 @@ gem 'rainbow', '~> 2.2'
# GitLab settings # GitLab settings
gem 'settingslogic', '~> 2.0.9' gem 'settingslogic', '~> 2.0.9'
# Linear-time regex library for untrusted regular expressions
gem 're2', '~> 1.0.0'
# Misc # Misc
gem 'version_sorter', '~> 2.1.0' gem 'version_sorter', '~> 2.1.0'
...@@ -268,7 +271,7 @@ gem 'peek', '~> 1.0.1' ...@@ -268,7 +271,7 @@ gem 'peek', '~> 1.0.1'
gem 'peek-gc', '~> 0.0.2' gem 'peek-gc', '~> 0.0.2'
gem 'peek-host', '~> 1.0.0' gem 'peek-host', '~> 1.0.0'
gem 'peek-mysql2', '~> 1.1.0', group: :mysql gem 'peek-mysql2', '~> 1.1.0', group: :mysql
gem 'peek-performance_bar', '~> 1.2.1' gem 'peek-performance_bar', '~> 1.3.0'
gem 'peek-pg', '~> 1.3.0', group: :postgres gem 'peek-pg', '~> 1.3.0', group: :postgres
gem 'peek-rblineprof', '~> 0.2.0' gem 'peek-rblineprof', '~> 0.2.0'
gem 'peek-redis', '~> 1.2.0' gem 'peek-redis', '~> 1.2.0'
...@@ -281,7 +284,7 @@ group :metrics do ...@@ -281,7 +284,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false gem 'influxdb', '~> 0.2', require: false
# Prometheus # Prometheus
gem 'prometheus-client-mmap', '~>0.7.0.beta5' gem 'prometheus-client-mmap', '~>0.7.0.beta9'
gem 'raindrops', '~> 0.18' gem 'raindrops', '~> 0.18'
end end
...@@ -390,3 +393,6 @@ gem 'toml-rb', '~> 0.3.15', require: false ...@@ -390,3 +393,6 @@ gem 'toml-rb', '~> 0.3.15', require: false
# Feature toggles # Feature toggles
gem 'flipper', '~> 0.10.2' gem 'flipper', '~> 0.10.2'
gem 'flipper-active_record', '~> 0.10.2' gem 'flipper-active_record', '~> 0.10.2'
# Structured logging
gem 'lograge', '~> 0.5'
...@@ -443,6 +443,10 @@ GEM ...@@ -443,6 +443,10 @@ GEM
logging (2.2.2) logging (2.2.2)
little-plugger (~> 1.1) little-plugger (~> 1.1)
multi_json (~> 1.10) multi_json (~> 1.10)
lograge (0.5.1)
actionpack (>= 4, < 5.2)
activesupport (>= 4, < 5.2)
railties (>= 4, < 5.2)
loofah (2.0.3) loofah (2.0.3)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.6.5) mail (2.6.5)
...@@ -484,7 +488,7 @@ GEM ...@@ -484,7 +488,7 @@ GEM
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1) omniauth-auth0 (1.4.1)
omniauth-oauth2 (~> 1.1) omniauth-oauth2 (~> 1.1)
omniauth-authentiq (0.3.0) omniauth-authentiq (0.3.1)
omniauth-oauth2 (~> 1.3, >= 1.3.1) omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.6) omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0) jwt (~> 1.0)
...@@ -553,7 +557,7 @@ GEM ...@@ -553,7 +557,7 @@ GEM
atomic (>= 1.0.0) atomic (>= 1.0.0)
mysql2 mysql2
peek peek
peek-performance_bar (1.2.1) peek-performance_bar (1.3.0)
peek (>= 0.1.0) peek (>= 0.1.0)
peek-pg (1.3.0) peek-pg (1.3.0)
concurrent-ruby concurrent-ruby
...@@ -588,7 +592,7 @@ GEM ...@@ -588,7 +592,7 @@ GEM
premailer-rails (1.9.7) premailer-rails (1.9.7)
actionmailer (>= 3, < 6) actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9) premailer (~> 1.7, >= 1.7.9)
prometheus-client-mmap (0.7.0.beta8) prometheus-client-mmap (0.7.0.beta9)
mmap2 (~> 2.2, >= 2.2.7) mmap2 (~> 2.2, >= 2.2.7)
pry (0.10.4) pry (0.10.4)
coderay (~> 1.1.0) coderay (~> 1.1.0)
...@@ -653,6 +657,7 @@ GEM ...@@ -653,6 +657,7 @@ GEM
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rdoc (4.2.2) rdoc (4.2.2)
json (~> 1.4) json (~> 1.4)
re2 (1.0.0)
recaptcha (3.0.0) recaptcha (3.0.0)
json json
recursive-open-struct (1.0.0) recursive-open-struct (1.0.0)
...@@ -998,6 +1003,7 @@ DEPENDENCIES ...@@ -998,6 +1003,7 @@ DEPENDENCIES
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
license_finder (~> 2.1.0) license_finder (~> 2.1.0)
licensee (~> 8.7.0) licensee (~> 8.7.0)
lograge (~> 0.5)
loofah (~> 2.0.3) loofah (~> 2.0.3)
mail_room (~> 0.9.1) mail_room (~> 0.9.1)
method_source (~> 0.8) method_source (~> 0.8)
...@@ -1010,7 +1016,7 @@ DEPENDENCIES ...@@ -1010,7 +1016,7 @@ DEPENDENCIES
oj (~> 2.17.4) oj (~> 2.17.4)
omniauth (~> 1.4.2) omniauth (~> 1.4.2)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.3.0) omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.6) omniauth-azure-oauth2 (~> 0.0.6)
omniauth-cas3 (~> 1.1.2) omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 4.0.0) omniauth-facebook (~> 4.0.0)
...@@ -1029,7 +1035,7 @@ DEPENDENCIES ...@@ -1029,7 +1035,7 @@ DEPENDENCIES
peek-gc (~> 0.0.2) peek-gc (~> 0.0.2)
peek-host (~> 1.0.0) peek-host (~> 1.0.0)
peek-mysql2 (~> 1.1.0) peek-mysql2 (~> 1.1.0)
peek-performance_bar (~> 1.2.1) peek-performance_bar (~> 1.3.0)
peek-pg (~> 1.3.0) peek-pg (~> 1.3.0)
peek-rblineprof (~> 0.2.0) peek-rblineprof (~> 0.2.0)
peek-redis (~> 1.2.0) peek-redis (~> 1.2.0)
...@@ -1037,7 +1043,7 @@ DEPENDENCIES ...@@ -1037,7 +1043,7 @@ DEPENDENCIES
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.9.0) poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.7) premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.7.0.beta5) prometheus-client-mmap (~> 0.7.0.beta9)
pry-byebug (~> 3.4.1) pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1) rack-attack (~> 4.4.1)
...@@ -1051,6 +1057,7 @@ DEPENDENCIES ...@@ -1051,6 +1057,7 @@ DEPENDENCIES
raindrops (~> 0.18) raindrops (~> 0.18)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
rdoc (~> 4.2) rdoc (~> 4.2)
re2 (~> 1.0.0)
recaptcha (~> 3.0) recaptcha (~> 3.0)
redcarpet (~> 3.4) redcarpet (~> 3.4)
redis (~> 3.2) redis (~> 3.2)
......
/* 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 */ /* 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 ShortcutsNavigation */
/* global IssuableIndex */ /* global IssuableIndex */
/* global ShortcutsIssuable */ /* global ShortcutsIssuable */
...@@ -157,6 +158,9 @@ import PerformanceBar from './performance_bar'; ...@@ -157,6 +158,9 @@ import PerformanceBar from './performance_bar';
shortcut_handler = new ShortcutsIssuable(); shortcut_handler = new ShortcutsIssuable();
new ZenMode(); new ZenMode();
break; break;
case 'dashboard:milestones:index':
new ProjectSelect();
break;
case 'projects:milestones:show': case 'projects:milestones:show':
case 'groups:milestones:show': case 'groups:milestones:show':
case 'dashboard:milestones:show': case 'dashboard:milestones:show':
...@@ -166,6 +170,7 @@ import PerformanceBar from './performance_bar'; ...@@ -166,6 +170,7 @@ import PerformanceBar from './performance_bar';
case 'groups:issues': case 'groups:issues':
case 'groups:merge_requests': case 'groups:merge_requests':
new UsersSelect(); new UsersSelect();
new ProjectSelect();
break; break;
case 'dashboard:todos:index': case 'dashboard:todos:index':
new Todos(); new Todos();
...@@ -259,6 +264,7 @@ import PerformanceBar from './performance_bar'; ...@@ -259,6 +264,7 @@ import PerformanceBar from './performance_bar';
break; break;
case 'dashboard:issues': case 'dashboard:issues':
case 'dashboard:merge_requests': case 'dashboard:merge_requests':
new ProjectSelect();
new UsersSelect(); new UsersSelect();
break; break;
case 'projects:commit:show': case 'projects:commit:show':
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
/* global dateFormat */ /* global dateFormat */
/* global Pikaday */ /* global Pikaday */
import DateFix from './lib/utils/datefix';
class DueDateSelect { class DueDateSelect {
constructor({ $dropdown, $loading } = {}) { constructor({ $dropdown, $loading } = {}) {
const $dropdownParent = $dropdown.closest('.dropdown'); const $dropdownParent = $dropdown.closest('.dropdown');
...@@ -43,14 +45,13 @@ class DueDateSelect { ...@@ -43,14 +45,13 @@ class DueDateSelect {
initDatePicker() { initDatePicker() {
const $dueDateInput = $(`input[name='${this.fieldName}']`); const $dueDateInput = $(`input[name='${this.fieldName}']`);
const dateFix = DateFix.dashedFix($dueDateInput.val());
const calendar = new Pikaday({ const calendar = new Pikaday({
field: $dueDateInput.get(0), field: $dueDateInput.get(0),
theme: 'gitlab-theme', theme: 'gitlab-theme',
format: 'yyyy-mm-dd', format: 'yyyy-mm-dd',
onSelect: (dateText) => { onSelect: (dateText) => {
const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd'); const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd');
$dueDateInput.val(formattedDate); $dueDateInput.val(formattedDate);
if (this.$dropdown.hasClass('js-issue-boards-due-date')) { if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
...@@ -62,7 +63,7 @@ class DueDateSelect { ...@@ -62,7 +63,7 @@ class DueDateSelect {
} }
}); });
calendar.setDate(new Date($dueDateInput.val())); calendar.setDate(dateFix);
this.$datePicker.append(calendar.el); this.$datePicker.append(calendar.el);
this.$datePicker.data('pikaday', calendar); this.$datePicker.data('pikaday', calendar);
} }
...@@ -168,6 +169,7 @@ class DueDateSelectors { ...@@ -168,6 +169,7 @@ class DueDateSelectors {
initMilestoneDatePicker() { initMilestoneDatePicker() {
$('.datepicker').each(function() { $('.datepicker').each(function() {
const $datePicker = $(this); const $datePicker = $(this);
const dateFix = DateFix.dashedFix($datePicker.val());
const calendar = new Pikaday({ const calendar = new Pikaday({
field: $datePicker.get(0), field: $datePicker.get(0),
theme: 'gitlab-theme animate-picker', theme: 'gitlab-theme animate-picker',
...@@ -177,7 +179,8 @@ class DueDateSelectors { ...@@ -177,7 +179,8 @@ class DueDateSelectors {
$datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); $datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
} }
}); });
calendar.setDate(new Date($datePicker.val()));
calendar.setDate(dateFix);
$datePicker.data('pikaday', calendar); $datePicker.data('pikaday', calendar);
}); });
......
...@@ -5,12 +5,15 @@ export default class GroupName { ...@@ -5,12 +5,15 @@ export default class GroupName {
constructor() { constructor() {
this.titleContainer = document.querySelector('.js-title-container'); this.titleContainer = document.querySelector('.js-title-container');
this.title = this.titleContainer.querySelector('.title'); this.title = this.titleContainer.querySelector('.title');
this.titleWidth = this.title.offsetWidth;
this.groupTitle = this.titleContainer.querySelector('.group-title'); if (this.title) {
this.groups = this.titleContainer.querySelectorAll('.group-path'); this.titleWidth = this.title.offsetWidth;
this.toggle = null; this.groupTitle = this.titleContainer.querySelector('.group-title');
this.isHidden = false; this.groups = this.titleContainer.querySelectorAll('.group-path');
this.init(); this.toggle = null;
this.isHidden = false;
this.init();
}
} }
init() { init() {
......
/* 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 */ /* 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 _ from 'underscore'; import _ from 'underscore';
import Cookies from 'js-cookie';
import NewNavSidebar from './new_sidebar';
(function() { (function() {
var hideEndFade; var hideEndFade;
...@@ -53,6 +55,11 @@ import _ from 'underscore'; ...@@ -53,6 +55,11 @@ import _ from 'underscore';
} }
$(() => { $(() => {
if (Cookies.get('new_nav') === 'true') {
const newNavSidebar = new NewNavSidebar();
newNavSidebar.bindEvents();
}
$(window).on('scroll', _.throttle(applyScrollNavClass, 100)); $(window).on('scroll', _.throttle(applyScrollNavClass, 100));
}); });
}).call(window); }).call(window);
const DateFix = {
dashedFix(val) {
const [y, m, d] = val.split('-');
return new Date(y, m - 1, d);
},
};
export default DateFix;
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);
}
}
...@@ -104,6 +104,14 @@ import Api from './api'; ...@@ -104,6 +104,14 @@ import Api from './api';
dropdownCssClass: "ajax-project-dropdown" 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; return ProjectSelect;
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
} }
&:not([href]):hover { &:not([href]):hover {
border-color: rgba($avatar-border, .2); border-color: darken($avatar-border, 10%);
} }
} }
...@@ -99,7 +99,7 @@ ...@@ -99,7 +99,7 @@
.avatar-counter { .avatar-counter {
background-color: $gray-darkest; background-color: $gray-darkest;
color: $white-light; color: $white-light;
border: 1px solid $border-color; border: 1px solid $avatar-border;
border-radius: 1em; border-radius: 1em;
font-family: $regular_font; font-family: $regular_font;
font-size: 9px; font-size: 9px;
......
...@@ -205,6 +205,7 @@ ...@@ -205,6 +205,7 @@
@media (max-width: $screen-sm-min) { @media (max-width: $screen-sm-min) {
width: 100%; width: 100%;
min-width: 180px;
} }
&.dropdown-open-left { &.dropdown-open-left {
...@@ -288,12 +289,6 @@ ...@@ -288,12 +289,6 @@
padding: 5px 8px; padding: 5px 8px;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
} }
.badge {
position: absolute;
right: 8px;
top: 5px;
}
} }
.droplab-dropdown { .droplab-dropdown {
...@@ -466,10 +461,6 @@ ...@@ -466,10 +461,6 @@
left: auto; left: auto;
right: 0; right: 0;
margin-top: -5px; margin-top: -5px;
@media (max-width: $screen-xs-max) {
left: 0;
}
} }
.dropdown-menu-selectable { .dropdown-menu-selectable {
......
...@@ -236,6 +236,8 @@ ul.content-list { ...@@ -236,6 +236,8 @@ ul.content-list {
ul.controls { ul.controls {
float: right; float: right;
list-style: none; list-style: none;
display: flex;
align-items: center;
.btn { .btn {
padding: 10px 14px; padding: 10px 14px;
...@@ -259,6 +261,12 @@ ul.controls { ...@@ -259,6 +261,12 @@ ul.controls {
} }
} }
} }
.issuable-pipeline-broken a,
.issuable-pipeline-status a,
.author_link {
display: flex;
}
} }
ul.indent-list { ul.indent-list {
......
...@@ -325,7 +325,7 @@ ...@@ -325,7 +325,7 @@
position: absolute; position: absolute;
top: 7px; top: 7px;
right: 15px; right: 15px;
z-index: 2; z-index: 300;
li.active { li.active {
font-weight: bold; font-weight: bold;
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
color: $gl-text-color; color: $gl-text-color;
word-wrap: break-word; word-wrap: break-word;
[dir="auto"] {
text-align: initial;
}
a { a {
color: $md-link-color; color: $md-link-color;
} }
...@@ -112,9 +116,12 @@ ...@@ -112,9 +116,12 @@
blockquote p { blockquote p {
color: $gl-grayish-blue !important; color: $gl-grayish-blue !important;
margin: 0;
font-size: inherit; font-size: inherit;
line-height: 1.5; line-height: 1.5;
&:last-child {
margin: 0;
}
} }
p { p {
......
...@@ -379,7 +379,7 @@ $issue-boards-card-shadow: rgba(186, 186, 186, 0.5); ...@@ -379,7 +379,7 @@ $issue-boards-card-shadow: rgba(186, 186, 186, 0.5);
* Avatar * Avatar
*/ */
$avatar_radius: 50%; $avatar_radius: 50%;
$avatar-border: rgba(0, 0, 0, .1); $avatar-border: $border-color;
$gl-avatar-size: 40px; $gl-avatar-size: 40px;
/* /*
......
...@@ -275,8 +275,6 @@ header.navbar-gitlab-new { ...@@ -275,8 +275,6 @@ header.navbar-gitlab-new {
.breadcrumbs { .breadcrumbs {
display: flex; display: flex;
min-height: 60px; min-height: 60px;
padding-top: $gl-padding-top;
padding-bottom: $gl-padding-top;
color: $gl-text-color; color: $gl-text-color;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
...@@ -300,6 +298,7 @@ header.navbar-gitlab-new { ...@@ -300,6 +298,7 @@ header.navbar-gitlab-new {
display: flex; display: flex;
width: 100%; width: 100%;
position: relative; position: relative;
align-items: center;
.dropdown-menu-projects { .dropdown-menu-projects {
margin-top: -$gl-padding; margin-top: -$gl-padding;
...@@ -330,7 +329,7 @@ header.navbar-gitlab-new { ...@@ -330,7 +329,7 @@ header.navbar-gitlab-new {
white-space: nowrap; white-space: nowrap;
> a { > a {
&:last-of-type { &:last-of-type:not(:first-child) {
font-weight: 600; font-weight: 600;
} }
} }
...@@ -384,6 +383,7 @@ header.navbar-gitlab-new { ...@@ -384,6 +383,7 @@ header.navbar-gitlab-new {
&::after { &::after {
content: "/"; content: "/";
margin: 0 2px 0 5px; margin: 0 2px 0 5px;
color: rgba($black, .65);
} }
} }
...@@ -396,3 +396,13 @@ header.navbar-gitlab-new { ...@@ -396,3 +396,13 @@ header.navbar-gitlab-new {
color: $gl-text-color; color: $gl-text-color;
} }
} }
.top-area {
.nav-controls-new-nav {
.dropdown {
@media (min-width: $screen-sm-min) {
margin-right: 0;
}
}
}
}
...@@ -26,46 +26,79 @@ $new-sidebar-width: 220px; ...@@ -26,46 +26,79 @@ $new-sidebar-width: 220px;
} }
.context-header { .context-header {
border-bottom: 1px solid $border-color; position: relative;
font-weight: 600;
display: flex;
align-items: center;
padding: 10px 16px 10px 10px;
color: $gl-text-color;
.avatar-container { a {
flex: 0 0 40px; border-bottom: 1px solid $border-color;
background-color: $white-light; font-weight: 600;
} display: flex;
align-items: center;
&:hover { padding: 10px 16px 10px 10px;
background-color: $hover-background; color: $gl-text-color;
color: $hover-color;
border-color: $hover-background;
.avatar-container { @media (max-width: $screen-xs-max) {
border-color: transparent; padding-right: 30px;
} }
.settings-avatar { &:hover {
background-color: $indigo-500; background-color: $hover-background;
color: $hover-color;
border-color: $hover-background;
i { .avatar-container {
color: $hover-color; 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, .project-title,
.group-title { .group-title {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; 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 { .settings-avatar {
background-color: $white-light; background-color: $white-light;
transition: background-color 100ms linear;
i { i {
font-size: 20px; font-size: 20px;
...@@ -73,7 +106,6 @@ $new-sidebar-width: 220px; ...@@ -73,7 +106,6 @@ $new-sidebar-width: 220px;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
text-align: center; text-align: center;
align-self: center; align-self: center;
transition: color 100ms linear;
} }
} }
...@@ -81,7 +113,7 @@ $new-sidebar-width: 220px; ...@@ -81,7 +113,7 @@ $new-sidebar-width: 220px;
position: fixed; position: fixed;
z-index: 400; z-index: 400;
width: $new-sidebar-width; width: $new-sidebar-width;
transition: width $sidebar-transition-duration; transition: left $sidebar-transition-duration;
top: 50px; top: 50px;
bottom: 0; bottom: 0;
left: 0; left: 0;
...@@ -89,7 +121,12 @@ $new-sidebar-width: 220px; ...@@ -89,7 +121,12 @@ $new-sidebar-width: 220px;
background-color: $gray-normal; background-color: $gray-normal;
box-shadow: inset -2px 0 0 $border-color; box-shadow: inset -2px 0 0 $border-color;
&.nav-sidebar-expanded {
left: 0;
}
a { a {
transition: none;
text-decoration: none; text-decoration: none;
} }
...@@ -118,7 +155,7 @@ $new-sidebar-width: 220px; ...@@ -118,7 +155,7 @@ $new-sidebar-width: 220px;
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
width: 0; left: (-$new-sidebar-width);
} }
} }
...@@ -177,7 +214,6 @@ $new-sidebar-width: 220px; ...@@ -177,7 +214,6 @@ $new-sidebar-width: 220px;
color: $hover-color; color: $hover-color;
.badge { .badge {
transition: background-color 100ms linear, color 100ms linear;
background-color: $indigo-500; background-color: $indigo-500;
color: $hover-color; color: $hover-color;
} }
...@@ -185,6 +221,38 @@ $new-sidebar-width: 220px; ...@@ -185,6 +221,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 // Make issue boards full-height now that sub-nav is gone
......
...@@ -346,13 +346,9 @@ ...@@ -346,13 +346,9 @@
display: none; display: none;
} }
.avatar:hover,
.avatar-counter:hover {
border-color: $issuable-sidebar-color;
}
.avatar-counter:hover { .avatar-counter:hover {
color: $issuable-sidebar-color; color: $issuable-sidebar-color;
border-color: $issuable-sidebar-color;
} }
.btn-clipboard { .btn-clipboard {
......
...@@ -376,3 +376,18 @@ table.u2f-registrations { ...@@ -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;
}
}
...@@ -32,10 +32,10 @@ module IssuableCollections ...@@ -32,10 +32,10 @@ module IssuableCollections
def filter_params def filter_params
set_sort_order_from_cookie set_sort_order_from_cookie
set_default_scope
set_default_state 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 @filter_params[:sort] ||= default_sort_order
@sort = @filter_params[:sort] @sort = @filter_params[:sort]
...@@ -55,10 +55,6 @@ module IssuableCollections ...@@ -55,10 +55,6 @@ module IssuableCollections
@filter_params @filter_params
end end
def set_default_scope
params[:scope] = 'all' if params[:scope].blank?
end
def set_default_state def set_default_state
params[:state] = 'opened' if params[:state].blank? params[:state] = 'opened' if params[:state].blank?
end end
......
class Dashboard::TodosController < Dashboard::ApplicationController class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper
before_action :authorize_read_project!, only: :index
before_action :find_todos, only: [:index, :destroy_all] before_action :find_todos, only: [:index, :destroy_all]
def index def index
...@@ -49,6 +50,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -49,6 +50,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController
private 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 def find_todos
@todos ||= TodosFinder.new(current_user, params).execute @todos ||= TodosFinder.new(current_user, params).execute
end end
......
...@@ -266,7 +266,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -266,7 +266,7 @@ class Projects::IssuesController < Projects::ApplicationController
if action_name == 'new' if action_name == 'new'
redirect_to external.new_issue_path redirect_to external.new_issue_path
else else
redirect_to external.project_path redirect_to external.issue_tracker_path
end end
end end
......
...@@ -20,9 +20,9 @@ ...@@ -20,9 +20,9 @@
# #
class IssuableFinder class IssuableFinder
include CreatedAtFilter include CreatedAtFilter
NONE = '0'.freeze 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 attr_accessor :current_user, :params
...@@ -89,8 +89,14 @@ class IssuableFinder ...@@ -89,8 +89,14 @@ class IssuableFinder
execute.find_by!(*params) execute.find_by!(*params)
end end
def state_counter_cache_key(state) def state_counter_cache_key
Digest::SHA1.hexdigest(state_counter_cache_key_components(state).flatten.join('-')) 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 end
def group def group
...@@ -417,12 +423,19 @@ class IssuableFinder ...@@ -417,12 +423,19 @@ class IssuableFinder
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end end
def state_counter_cache_key_components(state) def state_counter_cache_key_components
opts = params.with_indifferent_access opts = params.with_indifferent_access
opts[:state] = state
opts.except!(*IRRELEVANT_PARAMS_FOR_CACHE_KEY) opts.except!(*IRRELEVANT_PARAMS_FOR_CACHE_KEY)
opts.delete_if { |_, value| value.blank? } opts.delete_if { |_, value| value.blank? }
['issuables_count', klass.to_ability_name, opts.sort] ['issuables_count', klass.to_ability_name, opts.sort]
end 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 end
...@@ -75,7 +75,7 @@ class IssuesFinder < IssuableFinder ...@@ -75,7 +75,7 @@ class IssuesFinder < IssuableFinder
current_user.blank? || for_counting || params[:for_counting] current_user.blank? || for_counting || params[:for_counting]
end end
def state_counter_cache_key_components(state) def state_counter_cache_key_components
extra_components = [ extra_components = [
user_can_see_all_confidential_issues?, user_can_see_all_confidential_issues?,
user_cannot_see_confidential_issues?(for_counting: true) user_cannot_see_confidential_issues?(for_counting: true)
...@@ -84,6 +84,16 @@ class IssuesFinder < IssuableFinder ...@@ -84,6 +84,16 @@ class IssuesFinder < IssuableFinder
super + extra_components super + extra_components
end 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) def by_assignee(items)
if assignee if assignee
items.assigned_to(assignee) items.assigned_to(assignee)
......
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
...@@ -235,7 +235,7 @@ module IssuablesHelper ...@@ -235,7 +235,7 @@ module IssuablesHelper
def issuables_count_for_state(issuable_type, state, finder: nil) def issuables_count_for_state(issuable_type, state, finder: nil)
finder ||= public_send("#{issuable_type}_finder") finder ||= public_send("#{issuable_type}_finder")
cache_key = finder.state_counter_cache_key(state) cache_key = finder.state_counter_cache_key
@counts ||= {} @counts ||= {}
@counts[cache_key] ||= Rails.cache.fetch(cache_key, expires_in: 2.minutes) do @counts[cache_key] ||= Rails.cache.fetch(cache_key, expires_in: 2.minutes) do
......
...@@ -4,6 +4,10 @@ module PageLayoutHelper ...@@ -4,6 +4,10 @@ module PageLayoutHelper
@page_title.push(*titles.compact) if titles.any? @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 # Segments are seperated by middot
@page_title.join(" \u00b7 ") @page_title.join(" \u00b7 ")
end end
......
...@@ -195,7 +195,7 @@ module ProjectsHelper ...@@ -195,7 +195,7 @@ module ProjectsHelper
controller.controller_name, controller.controller_name,
controller.action_name, controller.action_name,
current_application_settings.cache_key, current_application_settings.cache_key,
'v2.4' 'v2.5'
] ]
key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status? key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status?
......
...@@ -96,6 +96,14 @@ module Ci ...@@ -96,6 +96,14 @@ module Ci
BuildSuccessWorker.perform_async(id) BuildSuccessWorker.perform_async(id)
end end
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 end
def detailed_status(current_user) def detailed_status(current_user)
...@@ -130,6 +138,14 @@ module Ci ...@@ -130,6 +138,14 @@ module Ci
success? || failed? || canceled? success? || failed? || canceled?
end 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? def latest?
!retried? !retried?
end end
......
...@@ -4,4 +4,8 @@ module Editable ...@@ -4,4 +4,8 @@ module Editable
def is_edited? def is_edited?
last_edited_at.present? && last_edited_at != created_at last_edited_at.present? && last_edited_at != created_at
end end
def last_edited_by
super || User.ghost
end
end end
...@@ -23,7 +23,7 @@ class GitlabIssueTrackerService < IssueTrackerService ...@@ -23,7 +23,7 @@ class GitlabIssueTrackerService < IssueTrackerService
project_issue_url(project, id: iid) project_issue_url(project, id: iid)
end end
def project_path def issue_tracker_path
project_issues_path(project) project_issues_path(project)
end end
......
...@@ -20,8 +20,8 @@ class IssueTrackerService < Service ...@@ -20,8 +20,8 @@ class IssueTrackerService < Service
self.issues_url.gsub(':id', iid.to_s) self.issues_url.gsub(':id', iid.to_s)
end end
def project_path def issue_tracker_path
read_attribute(:project_url) project_url
end end
def new_issue_path def new_issue_path
......
...@@ -58,22 +58,22 @@ class KubernetesService < DeploymentService ...@@ -58,22 +58,22 @@ class KubernetesService < DeploymentService
def fields def fields
[ [
{ type: 'text',
name: 'namespace',
title: 'Kubernetes namespace',
placeholder: namespace_placeholder },
{ type: 'text', { type: 'text',
name: 'api_url', name: 'api_url',
title: 'API URL', title: 'API URL',
placeholder: 'Kubernetes API URL, like https://kube.example.com/' }, placeholder: 'Kubernetes API URL, like https://kube.example.com/' },
{ type: 'text',
name: 'token',
title: 'Service token',
placeholder: 'Service token' },
{ type: 'textarea', { type: 'textarea',
name: 'ca_pem', name: 'ca_pem',
title: 'Custom CA bundle', title: 'CA Certificate',
placeholder: 'Certificate Authority bundle (PEM format)' } 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 end
......
...@@ -385,9 +385,11 @@ class User < ActiveRecord::Base ...@@ -385,9 +385,11 @@ class User < ActiveRecord::Base
# Return (create if necessary) the ghost user. The ghost user # Return (create if necessary) the ghost user. The ghost user
# owns records previously belonging to deleted users. # owns records previously belonging to deleted users.
def ghost 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.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.name = 'Ghost User'
u.notification_email = email
end end
end end
end end
......
...@@ -33,17 +33,12 @@ module Boards ...@@ -33,17 +33,12 @@ module Boards
end end
def filter_params def filter_params
set_default_scope
set_project set_project
set_state set_state
params params
end end
def set_default_scope
params[:scope] = 'all'
end
def set_project def set_project
params[:project_id] = project.id params[:project_id] = project.id
end end
......
...@@ -174,7 +174,7 @@ module Ci ...@@ -174,7 +174,7 @@ module Ci
end end
def pipeline_created_counter 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 end
end end
...@@ -183,7 +183,7 @@ class IssuableBaseService < BaseService ...@@ -183,7 +183,7 @@ class IssuableBaseService < BaseService
after_create(issuable) after_create(issuable)
issuable.create_cross_references!(current_user) issuable.create_cross_references!(current_user)
execute_hooks(issuable) execute_hooks(issuable)
invalidate_cache_counts(issuable.assignees, issuable) invalidate_cache_counts(issuable, users: issuable.assignees)
end end
issuable issuable
...@@ -240,12 +240,12 @@ class IssuableBaseService < BaseService ...@@ -240,12 +240,12 @@ class IssuableBaseService < BaseService
old_assignees: old_assignees old_assignees: old_assignees
) )
if old_assignees != issuable.assignees new_assignees = issuable.assignees.to_a
new_assignees = issuable.assignees.to_a affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees)
affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees)
invalidate_cache_counts(affected_assignees.compact, issuable)
end
# 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) after_update(issuable)
issuable.create_new_cross_references!(current_user) issuable.create_new_cross_references!(current_user)
execute_hooks(issuable, 'update') execute_hooks(issuable, 'update')
...@@ -339,9 +339,18 @@ class IssuableBaseService < BaseService ...@@ -339,9 +339,18 @@ class IssuableBaseService < BaseService
create_labels_note(issuable, old_labels) if issuable.labels != old_labels create_labels_note(issuable, old_labels) if issuable.labels != old_labels
end end
def invalidate_cache_counts(users, issuable) def invalidate_cache_counts(issuable, users: [], skip_project_cache: false)
users.each do |user| users.each do |user|
user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts") user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts")
end 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
end end
...@@ -28,7 +28,7 @@ module Issues ...@@ -28,7 +28,7 @@ module Issues
notification_service.close_issue(issue, current_user) if notifications notification_service.close_issue(issue, current_user) if notifications
todo_service.close_issue(issue, current_user) todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close') execute_hooks(issue, 'close')
invalidate_cache_counts(issue.assignees, issue) invalidate_cache_counts(issue, users: issue.assignees)
end end
issue issue
......
...@@ -8,7 +8,7 @@ module Issues ...@@ -8,7 +8,7 @@ module Issues
create_note(issue) create_note(issue)
notification_service.reopen_issue(issue, current_user) notification_service.reopen_issue(issue, current_user)
execute_hooks(issue, 'reopen') execute_hooks(issue, 'reopen')
invalidate_cache_counts(issue.assignees, issue) invalidate_cache_counts(issue, users: issue.assignees)
end end
issue issue
......
...@@ -13,7 +13,7 @@ module MergeRequests ...@@ -13,7 +13,7 @@ module MergeRequests
notification_service.close_mr(merge_request, current_user) notification_service.close_mr(merge_request, current_user)
todo_service.close_merge_request(merge_request, current_user) todo_service.close_merge_request(merge_request, current_user)
execute_hooks(merge_request, 'close') execute_hooks(merge_request, 'close')
invalidate_cache_counts(merge_request.assignees, merge_request) invalidate_cache_counts(merge_request, users: merge_request.assignees)
end end
merge_request merge_request
......
...@@ -13,7 +13,7 @@ module MergeRequests ...@@ -13,7 +13,7 @@ module MergeRequests
create_note(merge_request) create_note(merge_request)
notification_service.merge_mr(merge_request, current_user) notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge') execute_hooks(merge_request, 'merge')
invalidate_cache_counts(merge_request.assignees, merge_request) invalidate_cache_counts(merge_request, users: merge_request.assignees)
end end
private private
......
...@@ -10,7 +10,7 @@ module MergeRequests ...@@ -10,7 +10,7 @@ module MergeRequests
execute_hooks(merge_request, 'reopen') execute_hooks(merge_request, 'reopen')
merge_request.reload_diff(current_user) merge_request.reload_diff(current_user)
merge_request.mark_as_unchecked merge_request.mark_as_unchecked
invalidate_cache_counts(merge_request.assignees, merge_request) invalidate_cache_counts(merge_request, users: merge_request.assignees)
end end
merge_request merge_request
......
...@@ -31,6 +31,6 @@ class MetricsService ...@@ -31,6 +31,6 @@ class MetricsService
end end
def multiprocess_metrics_path def multiprocess_metrics_path
@multiprocess_metrics_path ||= Rails.root.join(ENV['prometheus_multiproc_dir']).freeze ::Prometheus::Client.configuration.multiprocess_files_dir
end end
end end
...@@ -50,10 +50,12 @@ module Users ...@@ -50,10 +50,12 @@ module Users
def migrate_issues def migrate_issues
user.issues.update_all(author_id: ghost_user.id) 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 end
def migrate_merge_requests def migrate_merge_requests
user.merge_requests.update_all(author_id: ghost_user.id) 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 end
def migrate_notes def migrate_notes
......
...@@ -16,7 +16,7 @@ class GitlabUploader < CarrierWave::Uploader::Base ...@@ -16,7 +16,7 @@ class GitlabUploader < CarrierWave::Uploader::Base
def self.base_dir def self.base_dir
return root_dir unless file_storage? return root_dir unless file_storage?
File.join(root_dir, 'system') File.join(root_dir, '-', 'system')
end end
def self.file_storage? def self.file_storage?
......
...@@ -3,6 +3,10 @@ class PersonalFileUploader < FileUploader ...@@ -3,6 +3,10 @@ class PersonalFileUploader < FileUploader
File.join(CarrierWave.root, model_path(model)) File.join(CarrierWave.root, model_path(model))
end end
def self.base_dir
File.join(root_dir, 'system')
end
private private
def secure_url def secure_url
......
- page_title "Edit", @application.name, "Applications" - page_title "Edit", @application.name, "Applications"
%h3.page-title Edit application %h3.page-title Edit application
- @url = admin_application_path(@application) - @url = admin_application_path(@application)
= render 'form', application: @application = render 'form', application: @application
- breadcrumb_title "Applications"
- page_title "New Application" - page_title "New Application"
%h3.page-title New application %h3.page-title New application
- @url = admin_applications_path - @url = admin_applications_path
= render 'form', application: @application = render 'form', application: @application
- breadcrumb_title "Messages"
- page_title "Broadcast Messages" - page_title "Broadcast Messages"
= render 'form' = render 'form'
- breadcrumb_title "Messages"
- page_title "Broadcast Messages" - page_title "Broadcast Messages"
%h3.page-title %h3.page-title
......
- 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 .top-area
%ul.nav-links %ul.nav-links
= nav_link(page: dashboard_groups_path) do = nav_link(page: dashboard_groups_path) do
...@@ -6,9 +10,8 @@ ...@@ -6,9 +10,8 @@
= nav_link(page: explore_groups_path) do = nav_link(page: explore_groups_path) do
= link_to explore_groups_path, title: 'Explore public groups' do = link_to explore_groups_path, title: 'Explore public groups' do
Explore public groups Explore public groups
.nav-controls .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) }
= render 'shared/groups/search_form' = render 'shared/groups/search_form'
= render 'shared/groups/dropdown' = render 'shared/groups/dropdown'
- if current_user.can_create_group? - if current_user.can_create_group?
= link_to new_group_path, class: "btn btn-new" do = link_to "New group", new_group_path, class: "btn btn-new #{("visible-xs" if show_new_nav?)}"
New group
= content_for :flash_message do = content_for :flash_message do
= render 'shared/project_limit' = 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 .top-area.scrolling-tabs-container.inner-page-scroll-tabs
.fade-left= icon('angle-left') .fade-left= icon('angle-left')
.fade-right= icon('angle-right') .fade-right= icon('angle-right')
...@@ -14,9 +19,8 @@ ...@@ -14,9 +19,8 @@
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do = link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore projects Explore projects
.nav-controls .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) }
= render 'shared/projects/search_form' = render 'shared/projects/search_form'
= render 'shared/projects/dropdown' = render 'shared/projects/dropdown'
- if current_user.can_create_project? - if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do = link_to "New project", new_project_path, class: "btn btn-new #{("visible-xs" if show_new_nav?)}"
New project
- 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 .top-area
%ul.nav-links %ul.nav-links
= nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do = nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do
...@@ -8,6 +12,5 @@ ...@@ -8,6 +12,5 @@
Explore Snippets Explore Snippets
- if current_user - if current_user
.nav-controls.hidden-xs .nav-controls.hidden-xs{ class: ("hidden-sm hidden-md hidden-lg" if show_new_nav?) }
= link_to new_snippet_path, class: "btn btn-new", title: "New snippet" do = link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet"
New snippet
- @hide_top_links = true
- page_title "Issues" - page_title "Issues"
- header_title "Issues", issues_dashboard_path(assignee_id: current_user.id) - header_title "Issues", issues_dashboard_path(assignee_id: current_user.id)
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{current_user.name} issues") = 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 .top-area
= render 'shared/issuable/nav', type: :issues = 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 = link_to params.merge(rss_url_options), class: 'btn has-tooltip', title: 'Subscribe' do
= icon('rss') = icon('rss')
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues' = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues'
......
- @hide_top_links = true
- page_title "Merge Requests" - page_title "Merge Requests"
- header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id) - 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 .top-area
= render 'shared/issuable/nav', type: :merge_requests = 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/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests'
= render 'shared/issuable/filter', type: :merge_requests = render 'shared/issuable/filter', type: :merge_requests
......
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
- page_title 'Milestones' - page_title 'Milestones'
- header_title 'Milestones', dashboard_milestones_path - 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 .top-area
= render 'shared/milestones_filter', counts: @milestone_states = 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 = render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true
.milestones .milestones
......
- @no_container = true - @no_container = true
- @hide_top_links = true - @hide_top_links = true
- @breadcrumb_title = "Projects"
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity") = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
......
- @hide_top_links = true
- @no_container = true - @no_container = true
- breadcrumb_title "Projects"
- page_title "Starred Projects" - page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
......
- @hide_top_links = true
- page_title "Todos" - page_title "Todos"
- header_title "Todos", dashboard_todos_path - header_title "Todos", dashboard_todos_path
......
- @hide_top_links = true
- page_title "Groups" - page_title "Groups"
- header_title "Groups", dashboard_groups_path - header_title "Groups", dashboard_groups_path
......
- @hide_top_links = true
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
......
- @hide_top_links = true
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
......
- @hide_top_links = true
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
......
- @hide_top_links = true
- page_title "Snippets" - page_title "Snippets"
- header_title "Snippets", snippets_path - header_title "Snippets", snippets_path
......
- page_title "Issues" - page_title "Issues"
- group_issues_exists = group_issues(@group).exists?
= render "head_issues" = render "head_issues"
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues") = 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 .top-area
= render 'shared/issuable/nav', type: :issues = 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 = link_to params.merge(rss_url_options), class: 'btn' do
= icon('rss') = icon('rss')
%span.icon-label %span.icon-label
......
- page_title 'Labels' - 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" = render "groups/head_issues"
.top-area.adjust .top-area.adjust
.nav-text .nav-text
Labels can be applied to issues and merge requests. Group labels are available for any project within the group. 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) - if can?(current_user, :admin_label, @group)
= link_to new_group_label_path(@group), class: "btn btn-new" do = link_to "New label", new_group_label_path(@group), class: "btn btn-new"
New label
.labels .labels
.other-labels .other-labels
......
- breadcrumb_title "Labels"
- page_title 'New Label' - page_title 'New Label'
- header_title group_title(@group, 'Labels', group_labels_path(@group)) - header_title group_title(@group, 'Labels', group_labels_path(@group))
......
- page_title "Merge Requests" - 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? - if @group_merge_requests.empty?
= render 'shared/empty_states/merge_requests', project_select_button: true = render 'shared/empty_states/merge_requests', project_select_button: true
- else - else
.top-area .top-area
= render 'shared/issuable/nav', type: :merge_requests = render 'shared/issuable/nav', type: :merge_requests
- if current_user - 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/new_project_item_select', path: 'merge_requests/new', label: "New merge request"
= render 'shared/issuable/filter', type: :merge_requests = render 'shared/issuable/filter', type: :merge_requests
......
- page_title "Milestones" - 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" = render "groups/head_issues"
.top-area .top-area
= render 'shared/milestones_filter', counts: @milestone_states = 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) - if can?(current_user, :admin_milestones, @group)
= link_to new_group_milestone_path(@group), class: "btn btn-new" do = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new"
New milestone
.milestones .milestones
%ul.content-list %ul.content-list
......
- breadcrumb_title "Milestones"
- page_title "Milestones" - page_title "Milestones"
- header_title group_title(@group, "Milestones", group_milestones_path(@group)) - header_title group_title(@group, "Milestones", group_milestones_path(@group))
......
- @breadcrumb_link = dashboard_groups_path
- breadcrumb_title "Groups"
- @hide_top_links = true
- page_title 'New Group' - page_title 'New Group'
- header_title "Groups", dashboard_groups_path - header_title "Groups", dashboard_groups_path
......
- @no_container = true - @no_container = true
- breadcrumb_title "Group"
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity") = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
......
...@@ -10,12 +10,15 @@ ...@@ -10,12 +10,15 @@
- if content_for?(:sub_nav) - if content_for?(:sub_nav)
= yield :sub_nav = yield :sub_nav
.content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" } .content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" }
- if show_new_nav?
.mobile-overlay
.alert-wrapper .alert-wrapper
= render "layouts/broadcast" = render "layouts/broadcast"
- if show_new_nav? - if show_new_nav?
- if content_for?(:new_global_flash) - if content_for?(:new_global_flash)
= yield :new_global_flash = yield :new_global_flash
= render "layouts/nav/breadcrumbs" - unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs"
= render "layouts/flash" = render "layouts/flash"
= yield :flash_message = yield :flash_message
%div{ class: "#{(container_class unless @no_container)} #{@content_class}" } %div{ class: "#{(container_class unless @no_container)} #{@content_class}" }
......
- breadcrumb_title = @breadcrumb_title || controller.controller_name.humanize - breadcrumb_link = breadcrumb_title_link
- hide_top_links = @hide_top_links || false - hide_top_links = @hide_top_links || false
%nav.breadcrumbs{ role: "navigation" } %nav.breadcrumbs{ role: "navigation" }
.breadcrumbs-container{ class: [container_class, @content_class] } .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 .breadcrumbs-links.js-title-container
- unless hide_top_links - unless hide_top_links
.title .title
= link_to "GitLab", root_path = link_to "GitLab", root_path
\/ \/
- if content_for?(:header_title_before)
= yield :header_title_before
\/
= header_title = header_title
%h2.breadcrumbs-sub-title %h2.breadcrumbs-sub-title
%ul.list-unstyled %ul.list-unstyled
- if content_for?(:sub_title_before) - if @breadcrumbs_extra_links
= yield :sub_title_before - @breadcrumbs_extra_links.each do |extra|
%li= link_to breadcrumb_title, request.path %li= link_to extra[:text], extra[:link]
%li= link_to @breadcrumb_title, breadcrumb_link
- if content_for?(:breadcrumbs_extra) - if content_for?(:breadcrumbs_extra)
.breadcrumbs-extra.hidden-xs= yield :breadcrumbs_extra .breadcrumbs-extra.hidden-xs= yield :breadcrumbs_extra
= yield :header_content = yield :header_content
...@@ -42,18 +42,18 @@ ...@@ -42,18 +42,18 @@
.key .key
= icon('arrow-up', 'aria-label' => 'hidden') = icon('arrow-up', 'aria-label' => 'hidden')
I I
%span.badge.pull-right= number_with_delimiter(assigned_issuables_count(:issues))
%span %span
Issues Issues
.badge= number_with_delimiter(assigned_issuables_count(:issues))
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
.shortcut-mappings .shortcut-mappings
.key .key
= icon('arrow-up', 'aria-label' => 'hidden') = icon('arrow-up', 'aria-label' => 'hidden')
M M
%span.badge.pull-right= number_with_delimiter(assigned_issuables_count(:merge_requests))
%span %span
Merge Requests Merge Requests
.badge= number_with_delimiter(assigned_issuables_count(:merge_requests))
= nav_link(controller: 'dashboard/snippets') do = nav_link(controller: 'dashboard/snippets') do
= link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
.shortcut-mappings .shortcut-mappings
......
.nav-sidebar .nav-sidebar
= link_to admin_root_path, title: 'Admin Overview', class: 'context-header' do .context-header
.avatar-container.s40.settings-avatar = link_to admin_root_path, title: 'Admin Overview' do
= icon('wrench') .avatar-container.s40.settings-avatar
.project-title Admin Area = 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 %ul.sidebar-top-level-items
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
...@@ -13,7 +17,7 @@ ...@@ -13,7 +17,7 @@
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview' do = link_to admin_root_path, title: 'Overview' do
%span %span
Overview Dashboard
= nav_link(controller: [:admin, :projects]) do = nav_link(controller: [:admin, :projects]) do
= link_to admin_projects_path, title: 'Projects' do = link_to admin_projects_path, title: 'Projects' do
%span %span
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
Projects 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 = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do
Groups Groups
......
.nav-sidebar .nav-sidebar
= link_to group_path(@group), title: @group.name, class: 'context-header' do .context-header
.avatar-container.s40.group-avatar = link_to group_path(@group), title: @group.name do
= image_tag group_icon(@group), class: "avatar s40 avatar-tile" .avatar-container.s40.group-avatar
.group-title = image_tag group_icon(@group), class: "avatar s40 avatar-tile"
= @group.name .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 %ul.sidebar-top-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: 'Home' do = link_to group_path(@group), title: 'About group' do
%span %span
Group About
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do = nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: 'Group Home' do = link_to group_path(@group), title: 'Group details' do
%span %span
Home Details
= nav_link(path: 'groups#activity') do = nav_link(path: 'groups#activity') do
= link_to activity_group_path(@group), title: 'Activity' do = link_to activity_group_path(@group), title: 'Activity' do
......
.nav-sidebar .nav-sidebar
= link_to profile_path, title: 'Profile Settings', class: 'context-header' do .context-header
.avatar-container.s40.settings-avatar = link_to profile_path, title: 'Profile Settings' do
= icon('user') .avatar-container.s40.settings-avatar
.project-title User Settings = 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 %ul.sidebar-top-level-items
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do = link_to profile_path, title: 'Profile Settings' do
......
.nav-sidebar .nav-sidebar
- can_edit = can?(current_user, :admin_project, @project) - can_edit = can?(current_user, :admin_project, @project)
= link_to project_path(@project), title: @project.name, class: 'context-header' do .context-header
.avatar-container.s40.project-avatar = link_to project_path(@project), title: @project.name do
= project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile') .avatar-container.s40.project-avatar
.project-title = project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile')
= @project.name .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 %ul.sidebar-top-level-items
= nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do = nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do = link_to project_path(@project), title: 'About project', class: 'shortcuts-project' do
%span %span
Project About
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: 'projects#show') do = nav_link(path: 'projects#show') do
= link_to project_path(@project), title: _('Project home'), class: 'shortcuts-project' do = link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do
%span= _('Home') %span= _('Details')
= nav_link(path: 'projects#activity') do = nav_link(path: 'projects#activity') do
= link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do
......
...@@ -24,6 +24,12 @@ ...@@ -24,6 +24,12 @@
%p %p
This setting allows you to turn on or off the new upcoming navigation concept. This setting allows you to turn on or off the new upcoming navigation concept.
.col-lg-8.syntax-theme .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 = label_tag do
.preview= image_tag "old_nav.png" .preview= image_tag "old_nav.png"
%input.js-experiment-feature-toggle{ type: "radio", value: "false", name: "new_nav", checked: !show_new_nav? } %input.js-experiment-feature-toggle{ type: "radio", value: "false", name: "new_nav", checked: !show_new_nav? }
......
- breadcrumb_title "Profile"
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
= render 'profiles/head' = render 'profiles/head'
......
- page_title 'Two-Factor Authentication', 'Account' - 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 - @content_class = "limit-container-width" unless fluid_layout
= render 'profiles/head' = render 'profiles/head'
- if inject_u2f_api? - if inject_u2f_api?
......
- @no_container = true - @no_container = true
- if show_new_nav?
- add_to_breadcrumbs("Project", project_path(@project))
- page_title "Activity" - page_title "Activity"
= render "projects/head" = render "projects/head"
......
...@@ -5,12 +5,6 @@ ...@@ -5,12 +5,6 @@
.tree-holder .tree-holder
.nav-block .nav-block
.tree-controls
= link_to download_project_job_artifacts_path(@project, @build),
rel: 'nofollow', download: '', class: 'btn btn-default download' do
= icon('download')
Download artifacts archive
%ul.breadcrumb.repo-breadcrumb %ul.breadcrumb.repo-breadcrumb
%li %li
= link_to 'Artifacts', browse_project_job_artifacts_path(@project, @build) = link_to 'Artifacts', browse_project_job_artifacts_path(@project, @build)
...@@ -18,6 +12,12 @@ ...@@ -18,6 +12,12 @@
%li %li
= link_to truncate(title, length: 40), browse_project_job_artifacts_path(@project, @build, path) = link_to truncate(title, length: 40), browse_project_job_artifacts_path(@project, @build, path)
.tree-controls
= link_to download_project_job_artifacts_path(@project, @build),
rel: 'nofollow', download: '', class: 'btn btn-default download' do
= icon('download')
Download artifacts archive
.tree-content-holder .tree-content-holder
%table.table.tree-table %table.table.tree-table
%thead %thead
......
- breadcrumb_title "Repository"
- @no_container = true - @no_container = true
- page_title "Edit", @blob.path, @ref - page_title "Edit", @blob.path, @ref
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
......
- breadcrumb_title "Repository"
- page_title "New File", @path.presence, @ref - page_title "New File", @path.presence, @ref
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_tag('lib/ace.js')
......
- breadcrumb_title "Repository"
- @no_container = true - @no_container = true
- page_title @blob.path, @ref - page_title @blob.path, @ref
......
...@@ -3,8 +3,7 @@ ...@@ -3,8 +3,7 @@
- page_title "Boards" - page_title "Boards"
- if show_new_nav? - if show_new_nav?
- content_for :sub_title_before do - add_to_breadcrumbs("Issues", project_issues_path(@project))
%li= link_to "Issues", project_issues_path(@project)
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'common_vue'
......
...@@ -2,11 +2,15 @@ ...@@ -2,11 +2,15 @@
- page_title "Branches" - page_title "Branches"
= render "projects/commits/head" = render "projects/commits/head"
- if show_new_nav?
- add_to_breadcrumbs("Repository", project_tree_path(@project))
%div{ class: container_class } %div{ class: container_class }
.top-area.adjust .top-area.adjust
.nav-text - if can?(current_user, :admin_project, @project)
Protected branches can be managed in .nav-text
= link_to 'project settings', project_protected_branches_path(@project) Protected branches can be managed in
= link_to 'project settings', project_protected_branches_path(@project)
.nav-controls .nav-controls
= form_tag(filter_branches_path, method: :get) do = form_tag(filter_branches_path, method: :get) do
......
- @no_container = true - @no_container = true
- breadcrumb_title _("Commits")
- page_title _("Commits"), @ref - page_title _("Commits"), @ref
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
- if show_new_nav?
- add_to_breadcrumbs("Repository", project_tree_path(@project))
= content_for :sub_nav do = content_for :sub_nav do
= render "head" = render "head"
......
- @no_container = true - @no_container = true
- page_title "Compare" - page_title "Compare"
- if show_new_nav?
- add_to_breadcrumbs("Repository", project_tree_path(@project))
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: container_class } %div{ class: container_class }
......
- @no_container = true - @no_container = true
- breadcrumb_title "Compare"
- page_title "#{params[:from]}...#{params[:to]}" - page_title "#{params[:from]}...#{params[:to]}"
- if show_new_nav?
- add_to_breadcrumbs("Repository", project_tree_path(@project))
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: container_class } %div{ class: container_class }
......
- @no_container = true - @no_container = true
- page_title "Cycle Analytics" - page_title "Cycle Analytics"
- if show_new_nav?
- add_to_breadcrumbs("Project", project_path(@project))
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('cycle_analytics') = page_specific_javascript_bundle_tag('cycle_analytics')
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
- page_title "Environments" - page_title "Environments"
= render "projects/pipelines/head" = render "projects/pipelines/head"
- if show_new_nav?
- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag("environments") = page_specific_javascript_bundle_tag("environments")
......
- @no_container = true - @no_container = true
- breadcrumb_title "Environments"
- page_title 'New Environment' - page_title 'New Environment'
= render "projects/pipelines/head" = render "projects/pipelines/head"
......
- @no_container = true - @no_container = true
- page_title "Charts" - page_title "Charts"
- if show_new_nav?
- add_to_breadcrumbs("Repository", project_tree_path(@project))
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3') = page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs') = page_specific_javascript_bundle_tag('graphs')
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment