Commit bd01d79f authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into fe-paginated-environments-api

* master: (97 commits)
  convert js-cookie dependency to an npm module
  convert timeago.js dependency to an npm module
  remove vue from vendor since it is now in node_modules
  Add CHANGELOG file
  Stylize blockquote in notification emails
  Add js prefix to right sidebar
  Replace accidentally deleted metaclick
  Remove right padding from navbar-collapse on large screens
  Add changelog
  common_utils merge conflicts
  Set sidebar height to 100% if at top of page
  Set height of fixed sidebars with js
  Add sticky sidebar on wiki page
  Fix comment button test for slash commands
  Fix time tracking spec
  Fix issue boards sidebar alignment and sidebar toggle spec
  Fix failing conflicts test
  Fix build sidebar scrolling
  Refactor JS
  Fix pinned sidebar alignment
  ...
parents d0d94e4f edb8ed36
......@@ -163,64 +163,7 @@ spinach 7 10: *spinach-knapsack
spinach 8 10: *spinach-knapsack
spinach 9 10: *spinach-knapsack
# Execute all testing suites against Ruby 2.1
.ruby-21: &ruby-21
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1"
<<: *use-db
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
cache:
key: "ruby21"
paths:
- vendor/ruby
.rspec-knapsack-ruby21: &rspec-knapsack-ruby21
<<: *rspec-knapsack
<<: *dedicated-runner
<<: *ruby-21
.spinach-knapsack-ruby21: &spinach-knapsack-ruby21
<<: *spinach-knapsack
<<: *dedicated-runner
<<: *ruby-21
rspec 0 20 ruby21: *rspec-knapsack-ruby21
rspec 1 20 ruby21: *rspec-knapsack-ruby21
rspec 2 20 ruby21: *rspec-knapsack-ruby21
rspec 3 20 ruby21: *rspec-knapsack-ruby21
rspec 4 20 ruby21: *rspec-knapsack-ruby21
rspec 5 20 ruby21: *rspec-knapsack-ruby21
rspec 6 20 ruby21: *rspec-knapsack-ruby21
rspec 7 20 ruby21: *rspec-knapsack-ruby21
rspec 8 20 ruby21: *rspec-knapsack-ruby21
rspec 9 20 ruby21: *rspec-knapsack-ruby21
rspec 10 20 ruby21: *rspec-knapsack-ruby21
rspec 11 20 ruby21: *rspec-knapsack-ruby21
rspec 12 20 ruby21: *rspec-knapsack-ruby21
rspec 13 20 ruby21: *rspec-knapsack-ruby21
rspec 14 20 ruby21: *rspec-knapsack-ruby21
rspec 15 20 ruby21: *rspec-knapsack-ruby21
rspec 16 20 ruby21: *rspec-knapsack-ruby21
rspec 17 20 ruby21: *rspec-knapsack-ruby21
rspec 18 20 ruby21: *rspec-knapsack-ruby21
rspec 19 20 ruby21: *rspec-knapsack-ruby21
spinach 0 10 ruby21: *spinach-knapsack-ruby21
spinach 1 10 ruby21: *spinach-knapsack-ruby21
spinach 2 10 ruby21: *spinach-knapsack-ruby21
spinach 3 10 ruby21: *spinach-knapsack-ruby21
spinach 4 10 ruby21: *spinach-knapsack-ruby21
spinach 5 10 ruby21: *spinach-knapsack-ruby21
spinach 6 10 ruby21: *spinach-knapsack-ruby21
spinach 7 10 ruby21: *spinach-knapsack-ruby21
spinach 8 10 ruby21: *spinach-knapsack-ruby21
spinach 9 10 ruby21: *spinach-knapsack-ruby21
# Other generic tests
.ruby-static-analysis: &ruby-static-analysis
variables:
SIMPLECOV: "false"
......
......@@ -6,13 +6,13 @@
(How one can reproduce the issue - this is very important)
### Expected behavior
### What is the current *bug* behavior?
(What you should see instead)
(What actually happens)
### Actual behavior
### What is the expected *correct* behavior?
(What actually happens)
(What you should see instead)
### Relevant logs and/or screenshots
......@@ -23,23 +23,23 @@ logs, and code as it's very hard to read otherwise.)
(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com)
#### Results of GitLab application Check
#### Results of GitLab environment info
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:check SANITIZE=true`)
`sudo gitlab-rake gitlab:env:info`)
(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`)
(we will only investigate if the tests are passing)
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
#### Results of GitLab environment info
#### Results of GitLab application Check
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)
`sudo gitlab-rake gitlab:check SANITIZE=true`)
(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`)
(we will only investigate if the tests are passing)
### Possible fixes
......
......@@ -31,8 +31,7 @@ AllCops:
- 'lib/gitlab/seeder.rb'
- 'generator_templates/**/*'
##################### Style ##################################
# Style #######################################################################
# Check indentation of private/protected visibility modifiers.
Style/AccessModifierIndentation:
......@@ -471,7 +470,7 @@ Style/WhileUntilModifier:
Style/WordArray:
Enabled: false
#################### Metrics ################################
# Metrics #####################################################################
# A calculated magnitude based on number of assignments,
# branches, and conditions.
......@@ -516,8 +515,7 @@ Metrics/PerceivedComplexity:
Enabled: true
Max: 18
#################### Lint ################################
# Lint ########################################################################
# Checks for useless access modifiers.
Lint/UselessAccessModifier:
......@@ -679,8 +677,7 @@ Lint/UselessSetterCall:
Lint/Void:
Enabled: true
##################### Performance ############################
# Performance #################################################################
# Use `casecmp` rather than `downcase ==`.
Performance/Casecmp:
......@@ -718,8 +715,7 @@ Performance/StringReplacement:
Performance/TimesMap:
Enabled: true
##################### Rails ##################################
# Rails #######################################################################
# Enables Rails cops.
Rails:
......@@ -767,7 +763,7 @@ Rails/ReadWriteAttribute:
Rails/ScopeArgs:
Enabled: true
##################### RSpec ##################################
# RSpec #######################################################################
# Check that instances are not being stubbed globally.
RSpec/AnyInstance:
......@@ -828,3 +824,9 @@ RSpec/NotToNot:
# Prefer using verifying doubles over normal doubles.
RSpec/VerifiedDoubles:
Enabled: false
# Custom ######################################################################
# Disallow the `git` and `github` arguments in the Gemfile.
GemFetcher:
Enabled: true
......@@ -15,7 +15,7 @@
- [Issue weight](#issue-weight)
- [Regression issues](#regression-issues)
- [Technical debt](#technical-debt)
- [Stewardship][#stewardship]
- [Stewardship](#stewardship)
- [Merge requests](#merge-requests)
- [Merge request guidelines](#merge-request-guidelines)
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
......
......@@ -284,7 +284,7 @@ group :development, :test do
gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2'
gem 'rspec_profiling'
gem 'rspec_profiling', '~> 0.0.5'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.7.0'
......
......@@ -638,7 +638,7 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
rspec_profiling (0.0.4)
rspec_profiling (0.0.5)
activerecord
pg
rails
......@@ -738,7 +738,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.11)
sqlite3 (1.3.13)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
......@@ -962,7 +962,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rspec_profiling
rspec_profiling (~> 0.0.5)
rubocop (~> 0.46.0)
rubocop-rspec (~> 1.9.1)
ruby-fogbugz (~> 0.2.1)
......@@ -1011,4 +1011,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.14.2
1.14.3
Copyright (c) 2011-2016 GitLab B.V.
Copyright (c) 2011-2017 GitLab B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
......@@ -59,7 +59,7 @@ star, smile, etc.). Some good tips about code reviews can be found in our
## Feature Freeze
On the 7th of each month, RC1 of the upcoming release is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it.
After the 7th (Pacific Standard Time Zone) of each month, RC1 of the upcoming release is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it.
Merge requests may still be merged into master during this period,
but they will go into the _next_ release, unless they are manually cherry-picked into the stable branch.
By freezing the stable branches 2 weeks prior to a release, we reduce the risk of a last minute merge request potentially breaking things.
......
......@@ -20,7 +20,7 @@ require('vendor/jquery.waitforimages');
require('vendor/jquery.caret');
require('vendor/jquery.atwho');
require('vendor/jquery.scrollTo');
window.Cookies = require('vendor/js.cookie');
window.Cookies = require('js-cookie');
require('./autosave');
require('bootstrap/js/affix');
require('bootstrap/js/alert');
......
......@@ -67,16 +67,8 @@
Build.prototype.initSidebar = function() {
this.$sidebar = $('.js-build-sidebar');
this.sidebarTranslationLimits = {
min: $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
};
this.sidebarTranslationLimits.max = this.sidebarTranslationLimits.min + $('.scrolling-tabs-container').outerHeight();
this.$sidebar.css({
top: this.sidebarTranslationLimits.max
});
this.$sidebar.niceScroll();
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
this.$document.off('scroll.translateSidebar').on('scroll.translateSidebar', this.translateSidebar.bind(this));
};
Build.prototype.location = function() {
......@@ -231,14 +223,6 @@
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
};
Build.prototype.translateSidebar = function(e) {
var newPosition = this.sidebarTranslationLimits.max - (document.body.scrollTop || document.documentElement.scrollTop);
if (newPosition < this.sidebarTranslationLimits.min) newPosition = this.sidebarTranslationLimits.min;
this.$sidebar.css({
top: newPosition
});
};
Build.prototype.toggleSidebar = function(shouldHide) {
var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
this.$buildScroll.toggleClass('sidebar-expanded', shouldShow)
......@@ -285,7 +269,7 @@
e.preventDefault();
$currentTarget = $(e.currentTarget);
$.scrollTo($currentTarget.attr('href'), {
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
offset: 0
});
};
......
......@@ -91,6 +91,9 @@ require('./lib/utils/common_utils');
},
},
SanitizationFilter: {
'a[name]:not([href]):empty'(el, text) {
return el.outerHTML;
},
'dl'(el, text) {
let lines = text.trim().split('\n');
// Add two spaces to the front of subsequent list items lines,
......
......@@ -3,7 +3,7 @@
/* global Flash */
window.Vue = require('vue');
window.Cookies = require('vendor/js.cookie');
window.Cookies = require('js-cookie');
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/));
......
......@@ -76,7 +76,7 @@ require('./lib/utils/url_utility');
const diffFile = diffTitle.closest('.diff-file');
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
if (nothingHereBlock.length) {
const clickTarget = $('.file-title, .click-to-expand', diffFile);
const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
this.highlighSelectedLine();
if (cb) cb();
......
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */
/* global Vue */
/* global CommentsStore */
const Vue = require('vue');
(() => {
const CommentAndResolveBtn = Vue.extend({
......@@ -9,13 +9,11 @@
},
data() {
return {
textareaIsEmpty: true
textareaIsEmpty: true,
discussion: {},
};
},
computed: {
discussion: function () {
return CommentsStore.state[this.discussionId];
},
showButton: function () {
if (this.discussion) {
return this.discussion.isResolvable();
......@@ -42,6 +40,9 @@
}
}
},
created() {
this.discussion = CommentsStore.state[this.discussionId];
},
mounted: function () {
const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`);
this.textareaIsEmpty = $textarea.val() === '';
......
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, one-var, space-before-function-paren, no-lonely-if, no-continue, brace-style, max-len, quotes */
/* global Vue */
/* global DiscussionMixins */
/* global CommentsStore */
const Vue = require('vue');
(() => {
const JumpToDiscussion = Vue.extend({
......@@ -12,12 +12,10 @@
data: function () {
return {
discussions: CommentsStore.state,
discussion: {},
};
},
computed: {
discussion: function () {
return this.discussions[this.discussionId];
},
allResolved: function () {
return this.unresolvedDiscussionCount === 0;
},
......@@ -183,10 +181,13 @@
}
$.scrollTo($target, {
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
offset: 0
});
}
}
},
created() {
this.discussion = this.discussions[this.discussionId];
},
});
Vue.component('jump-to-discussion', JumpToDiscussion);
......
/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */
/* global Vue */
/* global CommentsStore */
/* global ResolveService */
/* global Flash */
const Vue = require('vue');
(() => {
const ResolveBtn = Vue.extend({
......@@ -10,14 +10,14 @@
noteId: Number,
discussionId: String,
resolved: Boolean,
projectPath: String,
canResolve: Boolean,
resolvedBy: String
},
data: function () {
return {
discussions: CommentsStore.state,
loading: false
loading: false,
note: {},
};
},
watch: {
......@@ -30,13 +30,6 @@
discussion: function () {
return this.discussions[this.discussionId];
},
note: function () {
if (this.discussion) {
return this.discussion.getNote(this.noteId);
} else {
return undefined;
}
},
buttonText: function () {
if (this.isResolved) {
return `Resolved by ${this.resolvedByName}`;
......@@ -73,10 +66,10 @@
if (this.isResolved) {
promise = ResolveService
.unresolve(this.projectPath, this.noteId);
.unresolve(this.noteId);
} else {
promise = ResolveService
.resolve(this.projectPath, this.noteId);
.resolve(this.noteId);
}
promise.then((response) => {
......@@ -106,6 +99,8 @@
},
created: function () {
CommentsStore.create(this.discussionId, this.noteId, this.canResolve, this.resolved, this.resolvedBy);
this.note = this.discussion.getNote(this.noteId);
}
});
......
/* eslint-disable comma-dangle, object-shorthand, func-names, no-param-reassign */
/* global Vue */
/* global DiscussionMixins */
/* global CommentsStore */
const Vue = require('vue');
((w) => {
w.ResolveCount = Vue.extend({
......
/* eslint-disable object-shorthand, func-names, space-before-function-paren, comma-dangle, no-else-return, quotes, max-len */
/* global Vue */
/* global CommentsStore */
/* global ResolveService */
const Vue = require('vue');
(() => {
const ResolveDiscussionBtn = Vue.extend({
props: {
discussionId: String,
mergeRequestId: Number,
projectPath: String,
canResolve: Boolean,
},
data: function() {
return {
discussions: CommentsStore.state
discussion: {},
};
},
computed: {
discussion: function () {
return this.discussions[this.discussionId];
},
showButton: function () {
if (this.discussion) {
return this.discussion.isResolvable();
......@@ -51,11 +48,13 @@
},
methods: {
resolve: function () {
ResolveService.toggleResolveForDiscussion(this.projectPath, this.mergeRequestId, this.discussionId);
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
}
},
created: function () {
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
this.discussion = CommentsStore.state[this.discussionId];
}
});
......
......@@ -3,6 +3,7 @@
/* global ResolveCount */
function requireAll(context) { return context.keys().map(context); }
const Vue = require('vue');
requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./services', false, /^\.\/.*\.(js|es6)$/));
......@@ -10,11 +11,14 @@ requireAll(require.context('./mixins', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./components', false, /^\.\/.*\.(js|es6)$/));
$(() => {
const projectPath = document.querySelector('.merge-request').dataset.projectPath;
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn';
window.gl = window.gl || {};
window.gl.diffNoteApps = {};
window.ResolveService = new gl.DiffNotesResolveServiceClass(projectPath);
gl.diffNotesCompileComponents = () => {
const $components = $(COMPONENT_SELECTOR).filter(function () {
return $(this).closest('resolve-count').length !== 1;
......
/* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */
/* global Vue */
/* global Flash */
/* global CommentsStore */
((w) => {
class ResolveServiceClass {
constructor() {
this.noteResource = Vue.resource('notes{/noteId}/resolve');
this.discussionResource = Vue.resource('merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve');
}
const Vue = window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
require('../../vue_shared/vue_resource_interceptor');
setCSRF() {
Vue.http.headers.common['X-CSRF-Token'] = $.rails.csrfToken();
}
(() => {
window.gl = window.gl || {};
prepareRequest(root) {
this.setCSRF();
Vue.http.options.root = root;
class ResolveServiceClass {
constructor(root) {
this.noteResource = Vue.resource(`${root}/notes{/noteId}/resolve`);
this.discussionResource = Vue.resource(`${root}/merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve`);
}
resolve(projectPath, noteId) {
this.prepareRequest(projectPath);
resolve(noteId) {
return this.noteResource.save({ noteId }, {});
}
unresolve(projectPath, noteId) {
this.prepareRequest(projectPath);
unresolve(noteId) {
return this.noteResource.delete({ noteId }, {});
}
toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId) {
toggleResolveForDiscussion(mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId];
const isResolved = discussion.isResolved();
let promise;
if (isResolved) {
promise = this.unResolveAll(projectPath, mergeRequestId, discussionId);
promise = this.unResolveAll(mergeRequestId, discussionId);
} else {
promise = this.resolveAll(projectPath, mergeRequestId, discussionId);
promise = this.resolveAll(mergeRequestId, discussionId);
}
promise.then((response) => {
......@@ -62,11 +54,9 @@
});
}
resolveAll(projectPath, mergeRequestId, discussionId) {
resolveAll(mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId];
this.prepareRequest(projectPath);
discussion.loading = true;
return this.discussionResource.save({
......@@ -75,11 +65,9 @@
}, {});
}
unResolveAll(projectPath, mergeRequestId, discussionId) {
unResolveAll(mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId];
this.prepareRequest(projectPath);
discussion.loading = true;
return this.discussionResource.delete({
......@@ -89,5 +77,5 @@
}
}
w.ResolveService = new ResolveServiceClass();
})(window);
gl.DiffNotesResolveServiceClass = ResolveServiceClass;
})();
const Vue = require('vue');
const Timeago = require('vendor/timeago');
const Timeago = require('timeago.j');
require('../../lib/utils/text_utility');
require('../../vue_shared/components/commit');
const ActionsComponent = require('./environment_actions');
......
......@@ -4,7 +4,7 @@
class FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter) {
this.droplab = droplab;
this.hookId = input.getAttribute('data-id');
this.hookId = input && input.getAttribute('data-id');
this.input = input;
this.filter = filter;
this.dropdown = dropdown;
......
......@@ -72,27 +72,18 @@
// This is required to handle non-unicode characters in hash
hash = decodeURIComponent(hash);
var navbar = document.querySelector('.navbar-gitlab');
var subnav = document.querySelector('.layout-nav');
var fixedTabs = document.querySelector('.js-tabs-affix');
var adjustment = 0;
if (navbar) adjustment -= navbar.offsetHeight;
if (subnav) adjustment -= subnav.offsetHeight;
// scroll to user-generated markdown anchor if we cannot find a match
if (document.getElementById(hash) === null) {
var target = document.getElementById('user-content-' + hash);
if (target && target.scrollIntoView) {
target.scrollIntoView(true);
window.scrollBy(0, adjustment);
}
} else {
// only adjust for fixedTabs when not targeting user-generated content
var fixedTabs = document.querySelector('.js-tabs-affix');
if (fixedTabs) {
adjustment -= fixedTabs.offsetHeight;
window.scrollBy(0, -fixedTabs.offsetHeight);
}
window.scrollBy(0, adjustment);
}
};
......@@ -147,12 +138,10 @@
gl.utils.scrollToElement = function($el) {
var top = $el.offset().top;
gl.navBarHeight = gl.navBarHeight || $('.navbar-gitlab').height();
gl.navLinksHeight = gl.navLinksHeight || $('.nav-links').height();
gl.mrTabsHeight = gl.mrTabsHeight || $('.merge-request-tabs').height();
return $('body, html').animate({
scrollTop: top - (gl.navBarHeight + gl.navLinksHeight + gl.mrTabsHeight)
scrollTop: top - (gl.mrTabsHeight)
}, 200);
};
......
......@@ -2,7 +2,7 @@
/* global timeago */
/* global dateFormat */
window.timeago = require('vendor/timeago');
window.timeago = require('timeago.js');
window.dateFormat = require('vendor/date.format');
(function() {
......
......@@ -115,8 +115,8 @@ require('./merge_request_tabs');
e.preventDefault();
textarea.val(textarea.data('messageWithDescription'));
$('p.js-with-description-hint').hide();
$('p.js-without-description-hint').show();
$('.js-with-description-hint').hide();
$('.js-without-description-hint').show();
});
$(document).on('click', 'a.js-without-description-link', function(e) {
......@@ -124,8 +124,8 @@ require('./merge_request_tabs');
e.preventDefault();
textarea.val(textarea.data('messageWithoutDescription'));
$('p.js-with-description-hint').show();
$('p.js-without-description-hint').hide();
$('.js-with-description-hint').show();
$('.js-without-description-hint').hide();
});
};
......
......@@ -4,7 +4,7 @@
/* global Flash */
require('./breakpoints');
window.Cookies = require('vendor/js.cookie');
window.Cookies = require('js-cookie');
require('./flash');
/* eslint-disable max-len */
......@@ -125,9 +125,8 @@ require('./flash');
if (this.diffViewType() === 'parallel') {
this.expandViewContainer();
}
const navBarHeight = $('.navbar-gitlab').outerHeight();
$.scrollTo('.merge-request-details .merge-request-tabs', {
offset: -navBarHeight,
offset: 0,
});
} else {
this.expandView();
......@@ -140,11 +139,7 @@ require('./flash');
scrollToElement(container) {
if (location.hash) {
const offset = 0 - (
$('.navbar-gitlab').outerHeight() +
$('.layout-nav').outerHeight() +
$('.js-tabs-affix').outerHeight()
);
const offset = -$('.js-tabs-affix').outerHeight();
const $el = $(`${container} ${location.hash}:not(.match)`);
if ($el.length) {
$.scrollTo($el[0], { offset });
......@@ -330,14 +325,12 @@ require('./flash');
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
const $diffTabs = $('#diff-notes-app');
const $fixedNav = $('.navbar-fixed-top');
const $layoutNav = $('.layout-nav');
$tabs.off('affix.bs.affix affix-top.bs.affix')
.affix({
offset: {
top: () => (
$diffTabs.offset().top - $tabs.height() - $fixedNav.height() - $layoutNav.height()
$diffTabs.offset().top - $tabs.height()
),
},
})
......
......@@ -154,7 +154,7 @@ require('./smart_interval');
return $.getJSON(this.opts.ci_status_url, (function(_this) {
return function(data) {
var message, status, title;
if (data.status === '') {
if (!data.status) {
return;
}
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
......
......@@ -455,7 +455,7 @@ require('vendor/task_list');
var mergeRequestId = $form.data('noteable-iid');
if (ResolveService != null) {
ResolveService.toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId);
ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId);
}
}
......
......@@ -6,7 +6,7 @@
const sidebarBreakpoint = 1024;
const pageSelector = '.page-with-sidebar';
const navbarSelector = '.navbar-fixed-top';
const navbarSelector = '.navbar-gitlab';
const sidebarWrapperSelector = '.sidebar-wrapper';
const sidebarContentSelector = '.nav-sidebar';
......@@ -35,13 +35,16 @@
window.innerWidth >= sidebarBreakpoint &&
$(pageSelector).hasClass(expandedPageClass)
);
$(window).on('resize', () => this.setSidebarHeight());
$(document)
.on('click', sidebarToggleSelector, () => this.toggleSidebar())
.on('click', pinnedToggleSelector, () => this.togglePinnedState())
.on('click', 'html, body, a, button', (e) => this.handleClickEvent(e))
.on('DOMContentLoaded', () => this.renderState())
.on('scroll', () => this.setSidebarHeight())
.on('todo:toggle', (e, count) => this.updateTodoCount(count));
this.renderState();
this.setSidebarHeight();
}
handleClickEvent(e) {
......@@ -64,6 +67,16 @@
this.renderState();
}
setSidebarHeight() {
const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
const diff = $navHeight - $('body').scrollTop();
if (diff > 0) {
$('.js-right-sidebar').outerHeight($(window).height() - diff);
} else {
$('.js-right-sidebar').outerHeight('100%');
}
}
togglePinnedState() {
this.isPinned = !this.isPinned;
if (!this.isPinned) {
......
......@@ -33,13 +33,13 @@
this.$toggleIcon.addClass('fa-caret-down');
}
$('.file-title, .click-to-expand', this.file).on('click', (function (e) {
$('.js-file-title, .click-to-expand', this.file).on('click', (function (e) {
this.toggleDiff($(e.target));
}).bind(this));
}
SingleFileDiff.prototype.toggleDiff = function($target, cb) {
if (!$target.hasClass('file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
this.isOpen = !this.isOpen;
if (!this.isOpen && !this.hasError) {
this.content.hide();
......
......@@ -9,6 +9,8 @@
}
.user-calendar-activities {
direction: ltr;
.str-truncated {
max-width: 70%;
}
......
......@@ -125,7 +125,6 @@
top: 100%;
left: 0;
z-index: 9;
max-width: 280px;
min-width: 240px;
margin-top: 2px;
margin-bottom: 0;
......@@ -137,6 +136,10 @@
border-radius: $border-radius-base;
box-shadow: 0 2px 4px $dropdown-shadow-color;
.filtered-search-input-container & {
max-width: 280px;
}
&.is-loading {
.dropdown-content {
display: none;
......
......@@ -231,3 +231,46 @@ span.idiff {
}
}
}
.file-title-flex-parent {
display: flex;
align-items: center;
justify-content: space-between;
background-color: $gray-light;
border-bottom: 1px solid $border-color;
padding: 5px $gl-padding;
margin: 0;
border-radius: 3px 3px 0 0;
.file-header-content {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 30px;
position: relative;
}
.btn-clipboard {
position: absolute;
right: 0;
}
a {
color: $gl-text-color;
}
small {
margin: 0 10px 0 0;
}
.file-actions {
white-space: nowrap;
.btn {
padding: 0 10px;
font-size: 13px;
line-height: 28px;
display: inline-block;
}
}
}
......@@ -222,6 +222,10 @@ header {
float: right;
border-top: none;
@media (min-width: $screen-md-min) {
padding: 0;
}
@media (max-width: $screen-xs-max) {
float: none;
}
......@@ -272,7 +276,7 @@ header {
.header-user {
.dropdown-menu-nav {
width: 140px;
min-width: 140px;
margin-top: -5px;
}
}
......
......@@ -283,10 +283,7 @@
}
.layout-nav {
position: fixed;
top: $header-height;
width: 100%;
z-index: 11;
background: $gray-light;
border-bottom: 1px solid $border-color;
transition: padding $sidebar-transition-duration;
......@@ -419,15 +416,20 @@
}
.page-with-layout-nav {
margin-top: $header-height + 2;
.right-sidebar {
top: ($header-height * 2) + 2;
}
.build-sidebar {
top: ($header-height * 3) + 3;
&.affix {
top: 0;
}
}
}
.activities {
.nav-block {
border-bottom: 1px solid $border-color;
......
.page-with-sidebar {
padding: $header-height 0 25px;
padding-bottom: 25px;
transition: padding $sidebar-transition-duration;
&.page-sidebar-pinned {
......@@ -208,7 +208,9 @@ header.header-sidebar-pinned {
padding-right: 0;
@media (min-width: $screen-sm-min) {
padding-right: $sidebar_collapsed_width;
.content-wrapper {
padding-right: $sidebar_collapsed_width;
}
.merge-request-tabs-holder.affix {
right: $sidebar_collapsed_width;
......@@ -234,7 +236,9 @@ header.header-sidebar-pinned {
}
@media (min-width: $screen-md-min) {
padding-right: $gutter_width;
.content-wrapper {
padding-right: $gutter_width;
}
&:not(.with-overlay) .merge-request-tabs-holder.affix {
right: $gutter_width;
......@@ -252,4 +256,9 @@ header.header-sidebar-pinned {
.right-sidebar {
border-left: 1px solid $border-color;
&.affix {
position: fixed;
top: 0;
}
}
......@@ -138,6 +138,13 @@ pre {
margin: 0;
}
blockquote {
color: $gl-grayish-blue;
padding: 0 0 0 15px;
margin: 0;
border-left: 3px solid $white-dark;
}
span.highlight_word {
background-color: $highlighted-highlight-word !important;
}
......
......@@ -298,12 +298,8 @@
.issue-boards-sidebar {
&.right-sidebar {
top: 153px;
top: 0;
bottom: 0;
@media (min-width: $screen-sm-min) {
top: 220px;
}
}
.issuable-sidebar-header {
......
......@@ -34,9 +34,14 @@
}
}
.file-title {
.file-title,
.file-title-flex-parent {
cursor: pointer;
a:hover {
text-decoration: none;
}
&:hover {
background-color: $gray-normal;
}
......
......@@ -189,7 +189,7 @@
}
.right-sidebar {
position: fixed;
position: absolute;
top: $header-height;
bottom: 0;
right: 0;
......@@ -461,8 +461,19 @@
.issuable-list {
li {
.issue-box {
display: -webkit-flex;
display: flex;
}
.issue-info-container {
-webkit-flex: 1;
flex: 1;
padding-right: $gl-padding;
}
.issue-check {
float: left;
padding-right: $gl-padding;
margin-bottom: 10px;
min-width: 15px;
......
.issues-list {
.issue {
padding: 10px $gl-padding;
padding: 10px 0 10px $gl-padding;
position: relative;
.title {
......
......@@ -96,13 +96,6 @@
padding-right: 4px;
}
&.ci-success_with_warnings {
i {
color: $gl-warning;
}
}
@media (max-width: $screen-xs-max) {
flex-wrap: wrap;
}
......@@ -272,8 +265,15 @@
.mr-list {
.merge-request {
padding: 10px 15px;
padding: 10px 0 10px 15px;
position: relative;
display: -webkit-flex;
display: flex;
.issue-info-container {
-webkit-flex: 1;
flex: 1;
}
.merge-request-title {
margin-bottom: 2px;
......@@ -479,7 +479,7 @@
background-color: $white-light;
&.affix {
top: 100px;
top: 0;
left: 0;
z-index: 10;
transition: right .15s;
......
......@@ -94,6 +94,10 @@
padding: 10px 8px;
}
td.stage-cell {
padding: 10px 0;
}
.commit-link {
padding: 9px 8px 10px;
}
......@@ -291,12 +295,14 @@
height: 22px;
margin: 3px 6px 3px 0;
.tooltip {
white-space: nowrap;
// Hack to show a button tooltip inline
button.has-tooltip + .tooltip {
min-width: 105px;
}
.tooltip-inner {
padding: 3px 4px;
// Bootstrap way of showing the content inline for anchors.
a.has-tooltip {
white-space: nowrap;
}
&:not(:last-child) {
......
......@@ -171,8 +171,6 @@
.tree-controls {
float: right;
margin-top: 11px;
position: relative;
z-index: 2;
.project-action-button {
margin-left: $btn-side-margin;
......
......@@ -12,7 +12,6 @@ class ApplicationController < ActionController::Base
before_action :authenticate_user_from_private_token!
before_action :authenticate_user!
before_action :validate_user_service_ticket!
before_action :reject_blocked!
before_action :check_password_expiration
before_action :check_2fa_requirement
before_action :ldap_security_check
......@@ -87,22 +86,8 @@ class ApplicationController < ActionController::Base
logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}"
end
def reject_blocked!
if current_user && current_user.blocked?
sign_out current_user
flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it."
redirect_to new_user_session_path
end
end
def after_sign_in_path_for(resource)
if resource.is_a?(User) && resource.respond_to?(:blocked?) && resource.blocked?
sign_out resource
flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it."
new_user_session_path
else
stored_location_for(:redirect) || stored_location_for(resource) || root_path
end
stored_location_for(:redirect) || stored_location_for(resource) || root_path
end
def after_sign_out_path_for(resource)
......
class Explore::ApplicationController < ApplicationController
skip_before_action :authenticate_user!, :reject_blocked!
skip_before_action :authenticate_user!
layout 'explore'
end
......@@ -9,7 +9,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
@sort = params[:sort].presence || sort_value_name
@project = @group.projects.find(params[:project_id]) if params[:project_id]
@members = @group.group_members
@members = GroupMembersFinder.new(@group).execute
@members = @members.non_invite unless can?(current_user, :admin_group, @group)
@members = @members.search(params[:search]) if params[:search].present?
@members = @members.sort(@sort)
......
class HelpController < ApplicationController
skip_before_action :authenticate_user!, :reject_blocked!
skip_before_action :authenticate_user!
layout 'help'
......
class KodingController < ApplicationController
before_action :check_integration!, :authenticate_user!, :reject_blocked!
before_action :check_integration!
layout 'koding'
def index
......
......@@ -68,8 +68,12 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
def access_levels_options
{
push_access_levels: ProtectedBranch::PushAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } },
merge_access_levels: ProtectedBranch::MergeAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } }
push_access_levels: {
"Roles" => ProtectedBranch::PushAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } },
},
merge_access_levels: {
"Roles" => ProtectedBranch::MergeAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } }
}
}
end
......
class Projects::UploadsController < Projects::ApplicationController
skip_before_action :reject_blocked!, :project,
:repository, if: -> { action_name == 'show' && image_or_video? }
skip_before_action :project, :repository,
if: -> { action_name == 'show' && image_or_video? }
before_action :authorize_upload_file!, only: [:create]
......
class SearchController < ApplicationController
skip_before_action :authenticate_user!, :reject_blocked!
skip_before_action :authenticate_user!
include SearchHelper
......
class GroupMembersFinder < Projects::ApplicationController
def initialize(group)
@group = group
end
def execute
group_members = @group.members
return group_members unless @group.parent
parents_members = GroupMember.non_request.
where(source_id: @group.ancestors.select(:id)).
where.not(user_id: @group.users.select(:id))
wheres = ["members.id IN (#{group_members.select(:id).to_sql})"]
wheres << "members.id IN (#{parents_members.select(:id).to_sql})"
GroupMember.where(wheres.join(' OR '))
end
end
......@@ -206,7 +206,7 @@ class Group < Namespace
end
def members_with_parents
GroupMember.where(requested_at: nil, source_id: ancestors.map(&:id).push(id))
GroupMember.non_request.where(source_id: ancestors.map(&:id).push(id))
end
def users_with_parents
......
......@@ -47,6 +47,7 @@ class Member < ActiveRecord::Base
scope :invite, -> { where.not(invite_token: nil) }
scope :non_invite, -> { where(invite_token: nil) }
scope :request, -> { where.not(requested_at: nil) }
scope :non_request, -> { where(requested_at: nil) }
scope :has_access, -> { active.where('access_level > 0') }
......
......@@ -250,21 +250,11 @@ class JiraService < IssueTrackerService
end
end
# Build remote link on JIRA properties
# Icons here must be available on WEB so JIRA can read the URL
# We are using a open word graphics icon which have LGPL license
def build_remote_link_props(url:, title:, resolved: false)
status = {
resolved: resolved
}
if resolved
status[:icon] = {
title: 'Closed',
url16x16: 'http://www.openwebgraphics.com/resources/data/1768/16x16_apply.png'
}
end
{
GlobalID: 'GitLab',
object: {
......
......@@ -167,6 +167,15 @@ class User < ActiveRecord::Base
def blocked?
true
end
def active_for_authentication?
false
end
def inactive_message
"Your account has been blocked. Please contact your GitLab " \
"administrator if you think this is an error."
end
end
end
......
......@@ -18,7 +18,7 @@
.tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''),
id: klass::file_name_noext }
.file-holder#README
.file-title
.js-file-title.file-title
%i.fa.fa-file
= klass::file_name
.pull-right
......
......@@ -11,7 +11,7 @@
.form-group
.col-sm-12
.file-holder
.file-title.clearfix
.js-file-title.file-title.clearfix
Content of .gitlab-ci.yml
#ci-editor.ci-editor= @content
= text_area_tag(:content, @content, class: 'hidden form-control span1', rows: 7, require: true)
......
......@@ -2,7 +2,7 @@
- blob = discussion.blob
.diff-file.file-holder
.file-title
.js-file-title.file-title
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_file.content_commit, project: discussion.project, url: discussion_diff_path(discussion)
.diff-content.code.js-syntax-highlight
......
- if discussion.for_merge_request?
%resolve-discussion-btn{ ":project-path" => "'#{project_path(discussion.project)}'",
":discussion-id" => "'#{discussion.id}'",
%resolve-discussion-btn{ ":discussion-id" => "'#{discussion.id}'",
":merge-request-id" => discussion.noteable.iid,
":can-resolve" => discussion.can_resolve?(current_user),
"inline-template" => true }
......
......@@ -528,7 +528,7 @@
- blob = Snippet.new(content: "Wow\nSuch\nFile")
.example
.file-holder
.file-title
.js-file-title.file-title
Awesome file
.file-actions
.btn-group
......
%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
%header.navbar.navbar-gitlab{ class: nav_header_class }
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
.container-fluid
.header-content
......
......@@ -10,6 +10,7 @@
- if @project && event.project != @project
%span at
%strong= link_to_project event.project
= clipboard_button(clipboard_text: event.ref_name, class: 'btn-clipboard btn-transparent', title: 'Copy branch to clipboard')
#{time_ago_with_tooltip(event.created_at)}
.pull-right
......
......@@ -7,7 +7,7 @@
#blob-content-holder.tree-holder
.file-holder
.file-title
.js-file-title.file-title
= blob_icon @blob.mode, @blob.name
%strong
= @path
......
......@@ -24,7 +24,7 @@
#blob-content-holder.blob-content-holder
%article.file-holder
.file-title
.js-file-title.file-title
= blob_icon blob.mode, blob.name
%strong
= blob.name
......
.file-holder.file.append-bottom-default
.file-title.clearfix
.js-file-title.file-title.clearfix
.editor-ref
= icon('code-fork')
= ref
......
- builds = @build.pipeline.builds.to_a
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "151", "spy" => "affix" } }
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
Job
%strong ##{@build.id}
......
......@@ -15,7 +15,7 @@
- else
%span.api.monospace API
- if pipeline.latest?
%span.label.label-success.has-tooltip{ title: 'Latest job for this branch' } latest
%span.label.label-success.has-tooltip{ title: 'Latest pipeline for this branch' } latest
- if pipeline.triggered?
%span.label.label-primary triggered
- if pipeline.yaml_errors.present?
......@@ -61,7 +61,7 @@
.btn-group.inline
- if actions.any?
.btn-group
%button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual job', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual job' }
%button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual pipeline', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual pipeline' }
= custom_icon('icon_play')
= icon('caret-down', 'aria-hidden' => 'true')
%ul.dropdown-menu.dropdown-menu-align-right
......
- environment = local_assigns.fetch(:environment, nil)
.diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) }
.file-title
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}"
.js-file-title.file-title-flex-parent
.file-header-content
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}"
- unless diff_file.submodule?
.file-actions.hidden-xs
......
......@@ -10,13 +10,13 @@
- if diff_file.renamed_file
- old_path, new_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path)
%strong
%strong.file-title-name.has-tooltip{ data: { title: old_path } }
= old_path
&rarr;
%strong
%strong.file-title-name.has-tooltip{ data: { title: new_path } }
= new_path
- else
%strong
%strong.file-title-name.has-tooltip{ data: { title: diff_file.new_path } }
= diff_file.new_path
- if diff_file.deleted_file
deleted
......
......@@ -16,6 +16,8 @@
.col-sm-6
.nav-controls
= link_to @environment.external_url, class: 'btn btn-default' do
= icon('external-link')
= render 'projects/deployments/actions', deployment: @environment.last_deployment
.terminal-container{ class: container_class }
......
%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue), data: { labels: issue.label_ids, id: issue.id } }
- if @bulk_edit
.issue-check
= check_box_tag dom_id(issue, "selected"), nil, false, 'data-id' => issue.id, class: "selected_issue"
.issue-box
- if @bulk_edit
.issue-check
= check_box_tag dom_id(issue, "selected"), nil, false, 'data-id' => issue.id, class: "selected_issue"
.issue-info-container
.issue-title.title
%span.issue-title-text
= confidential_icon(issue)
= link_to issue.title, issue_path(issue)
%ul.controls
- if issue.closed?
%li
CLOSED
.issue-title.title
%span.issue-title-text
= confidential_icon(issue)
= link_to issue.title, issue_path(issue)
%ul.controls
- if issue.closed?
%li
CLOSED
- if issue.assignee
%li
= link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
- if issue.assignee
%li
= link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
- upvotes, downvotes = issue.upvotes, issue.downvotes
- if upvotes > 0
%li
= icon('thumbs-up')
= upvotes
- upvotes, downvotes = issue.upvotes, issue.downvotes
- if upvotes > 0
%li
= icon('thumbs-up')
= upvotes
- if downvotes > 0
%li
= icon('thumbs-down')
= downvotes
- if downvotes > 0
%li
= icon('thumbs-down')
= downvotes
- note_count = issue.notes.user.count
%li
= link_to issue_path(issue, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
= icon('comments')
= note_count
- note_count = issue.notes.user.count
%li
= link_to issue_path(issue, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
= icon('comments')
= note_count
.issue-info
#{issuable_reference(issue)} &middot;
opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')}
by #{link_to_member(@project, issue.author, avatar: false)}
- if issue.milestone
&nbsp;
= link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do
= icon('clock-o')
= issue.milestone.title
- if issue.due_date
%span{ class: "#{'cred' if issue.overdue?}" }
&nbsp;
= icon('calendar')
= issue.due_date.to_s(:medium)
- if issue.labels.any?
&nbsp;
- issue.labels.each do |label|
= link_to_label(label, subject: issue.project, css_class: 'label-link')
- if issue.tasks?
&nbsp;
%span.task-status
= issue.task_status
.issue-info
#{issuable_reference(issue)} &middot;
opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')}
by #{link_to_member(@project, issue.author, avatar: false)}
- if issue.milestone
&nbsp;
= link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do
= icon('clock-o')
= issue.milestone.title
- if issue.due_date
%span{ class: "#{'cred' if issue.overdue?}" }
&nbsp;
= icon('calendar')
= issue.due_date.to_s(:medium)
- if issue.labels.any?
&nbsp;
- issue.labels.each do |label|
= link_to_label(label, subject: issue.project, css_class: 'label-link')
- if issue.tasks?
&nbsp;
%span.task-status
= issue.task_status
.pull-right.issue-updated-at
%span updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')}
.pull-right.issue-updated-at
%span updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')}
......@@ -3,73 +3,74 @@
.issue-check
= check_box_tag dom_id(merge_request, "selected"), nil, false, 'data-id' => merge_request.id, class: "selected_issue"
.merge-request-title.title
%span.merge-request-title-text
= link_to merge_request.title, merge_request_path(merge_request)
%ul.controls
- if merge_request.merged?
%li
MERGED
- elsif merge_request.closed?
%li
= icon('ban')
CLOSED
.issue-info-container
.merge-request-title.title
%span.merge-request-title-text
= link_to merge_request.title, merge_request_path(merge_request)
%ul.controls
- if merge_request.merged?
%li
MERGED
- elsif merge_request.closed?
%li
= icon('ban')
CLOSED
- if merge_request.head_pipeline
%li
= render_pipeline_status(merge_request.head_pipeline)
- if merge_request.head_pipeline
%li
= render_pipeline_status(merge_request.head_pipeline)
- if merge_request.open? && merge_request.broken?
%li
= link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
= icon('exclamation-triangle')
- if merge_request.open? && merge_request.broken?
%li
= link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
= icon('exclamation-triangle')
- if merge_request.assignee
%li
= link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
- if merge_request.assignee
%li
= link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
- upvotes, downvotes = merge_request.upvotes, merge_request.downvotes
- if upvotes > 0
%li
= icon('thumbs-up')
= upvotes
- upvotes, downvotes = merge_request.upvotes, merge_request.downvotes
- if upvotes > 0
%li
= icon('thumbs-up')
= upvotes
- if downvotes > 0
%li
= icon('thumbs-down')
= downvotes
- if downvotes > 0
%li
= icon('thumbs-down')
= downvotes
- note_count = merge_request.related_notes.user.count
%li
= link_to merge_request_path(merge_request, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
= icon('comments')
= note_count
- note_count = merge_request.related_notes.user.count
%li
= link_to merge_request_path(merge_request, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
= icon('comments')
= note_count
.merge-request-info
#{issuable_reference(merge_request)} &middot;
opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')}
by #{link_to_member(@project, merge_request.author, avatar: false)}
- if merge_request.target_project.default_branch != merge_request.target_branch
&nbsp;
= link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do
= icon('code-fork')
= merge_request.target_branch
.merge-request-info
#{issuable_reference(merge_request)} &middot;
opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')}
by #{link_to_member(@project, merge_request.author, avatar: false)}
- if merge_request.target_project.default_branch != merge_request.target_branch
&nbsp;
= link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do
= icon('code-fork')
= merge_request.target_branch
- if merge_request.milestone
&nbsp;
= link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do
= icon('clock-o')
= merge_request.milestone.title
- if merge_request.milestone
&nbsp;
= link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do
= icon('clock-o')
= merge_request.milestone.title
- if merge_request.labels.any?
&nbsp;
- merge_request.labels.each do |label|
= link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link')
- if merge_request.labels.any?
&nbsp;
- merge_request.labels.each do |label|
= link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link')
- if merge_request.tasks?
&nbsp;
%span.task-status
= merge_request.task_status
- if merge_request.tasks?
&nbsp;
%span.task-status
= merge_request.task_status
.pull-right.hidden-xs
%span updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')}
.pull-right.hidden-xs
%span updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')}
......@@ -3,10 +3,9 @@
- page_description @merge_request.description
- page_card_attributes @merge_request.card_attributes
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('lib_vue')
= page_specific_javascript_bundle_tag('diff_notes')
.merge-request{ 'data-url' => merge_request_path(@merge_request) }
.merge-request{ 'data-url' => merge_request_path(@merge_request), 'data-project-path' => project_path(@merge_request.project) }
= render "projects/merge_requests/show/mr_title"
.merge-request-details.issuable-details{ data: { id: @merge_request.project.id } }
......
......@@ -23,7 +23,7 @@
.files-wrapper{ "v-if" => "!isLoading && !hasError" }
.files
.diff-file.file-holder.conflict{ "v-for" => "file in conflictsData.files" }
.file-title
.js-file-title.file-title
%i.fa.fa-fw{ ":class" => "file.iconClass" }
%strong {{file.filePath}}
= render partial: 'projects/merge_requests/conflicts/file_actions'
......
......@@ -9,8 +9,9 @@
Pipeline
= link_to "##{@pipeline.id}", namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'pipeline'
= ci_label_for_status(status)
.mr-widget-pipeline-graph
= render 'shared/mini_pipeline_graph', pipeline: @pipeline, klass: 'js-pipeline-inline-mr-widget-graph'
- if @pipeline.stages.any?
.mr-widget-pipeline-graph
= render 'shared/mini_pipeline_graph', pipeline: @pipeline, klass: 'js-pipeline-inline-mr-widget-graph'
%span
for
= succeed "." do
......
......@@ -16,13 +16,13 @@
gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}",
ci_message: {
normal: "Job {{status}} for \"{{title}}\"",
preparing: "{{status}} job for \"{{title}}\""
normal: "Pipeline {{status}} for \"{{title}}\"",
preparing: "{{status}} pipeline for \"{{title}}\""
},
ci_enable: #{@project.ci_service ? "true" : "false"},
ci_title: {
preparing: "{{status}} job",
normal: "Job {{status}}"
preparing: "{{status}} pipeline",
normal: "Pipeline {{status}}"
},
ci_sha: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.short_sha : ''}",
ci_pipeline: #{@merge_request.head_pipeline.try(:id).to_json},
......
%h4
= icon('exclamation-triangle')
The job for this merge request failed
The pipeline for this merge request failed
%p
Please retry the job or push a new commit to fix the failure.
......@@ -94,9 +94,8 @@
.form-group.project-visibility-level-holder
= f.label :visibility_level, class: 'label-light' do
Visibility Level
= link_to "(?)", help_page_path("public_access/public_access")
= render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
= link_to icon('question-circle'), help_page_path("public_access/public_access")
= render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project, with_label: false
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
......
......@@ -30,8 +30,7 @@
- if note.resolvable?
- can_resolve = can?(current_user, :resolve_note, note)
%resolve-btn{ "project-path" => "#{project_path(note.project)}",
"discussion-id" => "#{note.discussion_id}",
%resolve-btn{ "discussion-id" => "#{note.discussion_id}",
":note-id" => note.id,
":resolved" => note.resolved?,
":can-resolve" => can_resolve,
......
......@@ -4,7 +4,7 @@
.project-snippets
%article.file-holder.snippet-file-content
.file-title
.js-file-title.file-title
= blob_icon 0, @snippet.file_name
= @snippet.file_name
.file-actions
......
%article.file-holder.readme-holder
.file-title
.js-file-title.file-title
= blob_icon readme.mode, readme.name
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, @path, readme.name)) do
%strong
......
......@@ -65,7 +65,7 @@
In the
%code .gitlab-ci.yml
of another project, include the following snippet.
The project will be rebuilt at the end of the job.
The project will be rebuilt at the end of the pipeline.
%pre
:plain
......@@ -89,7 +89,7 @@
%p.light
Add
%code variables[VARIABLE]=VALUE
to an API request. Variable values can be used to distinguish between triggered jobs and normal jobs.
to an API request. Variable values can be used to distinguish between triggered pipelines and normal pipelines.
With cURL:
......
%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar
%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" } }
.block.wiki-sidebar-header.append-bottom-default
%a.gutter-toggle.pull-right.visible-xs-block.visible-sm-block.js-sidebar-wiki-toggle{ href: "#" }
= icon('angle-double-right')
......
- file_name, blob = blob
.blob-result
.file-holder
.file-title
.js-file-title.file-title
- ref = @search_results.repository_ref
- blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(ref, file_name))
= link_to blob_link do
......
......@@ -14,7 +14,7 @@
- snippet_path = reliable_snippet_path(snippet)
= link_to snippet_path do
.file-holder
.file-title
.js-file-title.file-title
%i.fa.fa-file
%strong= snippet.file_name
- if markup?(snippet.file_name)
......
- wiki_blob = parse_search_result(wiki_blob)
.blob-result
.file-holder
.file-title
.js-file-title.file-title
= link_to namespace_project_wiki_path(@project.namespace, @project, wiki_blob.basename) do
%i.fa.fa-file
%strong
......
......@@ -17,9 +17,9 @@
Try to keep the first line under 52 characters
and the others under 72.
- if descriptions.present?
%p.hint.js-with-description-hint
.hint.js-with-description-hint
= link_to "#", class: "js-with-description-link" do
Include description in commit message
%p.hint.js-without-description-hint.hide
.hint.js-without-description-hint.hide
= link_to "#", class: "js-without-description-link" do
Don't include description in commit message
- with_label = local_assigns.fetch(:with_label, true)
.form-group.project-visibility-level-holder
= f.label :visibility_level, class: 'control-label' do
Visibility Level
= link_to icon('question-circle'), help_page_path("public_access/public_access")
.col-sm-10
- if with_label
= f.label :visibility_level, class: 'control-label' do
Visibility Level
= link_to icon('question-circle'), help_page_path("public_access/public_access")
%div{ :class => ("col-sm-10" if with_label) }
- if can_change_visibility_level
= render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: visibility_level, form_model: form_model)
- else
......
......@@ -2,7 +2,7 @@
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('issuable')
%aside.right-sidebar{ class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar
- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.block.issuable-sidebar-header
......
......@@ -19,9 +19,9 @@
%label.label.label-danger
%strong Blocked
- if source.instance_of?(Group) && !@group
- if source.instance_of?(Group) && source != @group
&middot;
= link_to source.name, source, class: "member-group-link"
= link_to source.full_name, source, class: "member-group-link"
.hidden-xs.cgray
- if member.request?
......@@ -44,8 +44,9 @@
= link_to member.created_by.name, user_path(member.created_by)
= time_ago_with_tooltip(member.created_at)
- if show_roles
- current_resource = @project || @group
.controls.member-controls
- if show_controls && (member.respond_to?(:group) && @group) || (member.respond_to?(:project) && @project)
- if show_controls && member.source == current_resource
- if user != current_user
= form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f|
= f.hidden_field :access_level
......
......@@ -18,7 +18,7 @@
= f.label :file_name, "File", class: 'control-label'
.col-sm-10
.file-holder.snippet
.file-title
.js-file-title.file-title
= f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name'
.file-content.code
%pre#editor= @snippet.content
......
......@@ -26,7 +26,7 @@
= @file_sample.events
%article.file-holder
.file-title
.js-file-title.file-title
%i.fa.fa-file-text-o.fa-fw
%strong
= @file_sample.file
......
......@@ -3,7 +3,7 @@
= render 'shared/snippets/header'
%article.file-holder.snippet-file-content
.file-title
.js-file-title.file-title
= blob_icon 0, @snippet.file_name
= @snippet.file_name
.file-actions
......
---
title: Optionally make users created via the API set their password
merge_request: 8957
author: Joost Rijneveld
---
title: Responsive title in diffs inline, side by side, with and without sidebar
merge_request: 8475
author:
---
title: Added external environment link to web terminal view
merge_request: 8303
author:
---
title: fixes issue number alignment problem in MR and issue list
merge_request: 9020
author:
---
title: Fixes dropdown width in admin project page
merge_request: 9002
author:
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