Commit 71d731a2 authored by Regis's avatar Regis

merge master and fix application.rb conflict

parents 115536fb 21fb690c
This diff is collapsed.
...@@ -32,6 +32,7 @@ gem 'omniauth-saml', '~> 1.7.0' ...@@ -32,6 +32,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.2.0'
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt' gem 'jwt'
......
...@@ -428,6 +428,8 @@ GEM ...@@ -428,6 +428,8 @@ 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.2.2)
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)
omniauth (~> 1.0) omniauth (~> 1.0)
...@@ -897,6 +899,7 @@ DEPENDENCIES ...@@ -897,6 +899,7 @@ DEPENDENCIES
oj (~> 2.17.4) oj (~> 2.17.4)
omniauth (~> 1.3.1) omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.2.0)
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)
......
8.15.0-pre 8.16.0-pre
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
(function() { (function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
var AUTO_SCROLL_OFFSET = 75;
this.Build = (function() { this.Build = (function() {
Build.interval = null; Build.interval = null;
...@@ -19,6 +20,17 @@ ...@@ -19,6 +20,17 @@
this.buildStage = options.buildStage; this.buildStage = options.buildStage;
this.updateDropdown = bind(this.updateDropdown, this); this.updateDropdown = bind(this.updateDropdown, this);
this.$document = $(document); this.$document = $(document);
this.$body = $('body');
this.$buildTrace = $('#build-trace');
this.$autoScrollContainer = $('.autoscroll-container');
this.$autoScrollStatus = $('#autoscroll-status');
this.$autoScrollStatusText = this.$autoScrollStatus.find('.status-text');
this.$upBuildTrace = $('#up-build-trace');
this.$downBuildTrace = $('#down-build-trace');
this.$scrollTopBtn = $('#scroll-top');
this.$scrollBottomBtn = $('#scroll-bottom');
this.$buildRefreshAnimation = $('.js-build-refresh');
clearInterval(Build.interval); clearInterval(Build.interval);
// Init breakpoint checker // Init breakpoint checker
this.bp = Breakpoints.get(); this.bp = Breakpoints.get();
...@@ -32,6 +44,7 @@ ...@@ -32,6 +44,7 @@
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this)); this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this));
this.$document.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown); this.$document.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
this.$document.on('scroll', this.initScrollMonitor.bind(this));
$(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this)); $(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this));
$('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace); $('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace);
this.updateArtifactRemoveDate(); this.updateArtifactRemoveDate();
...@@ -40,18 +53,6 @@ ...@@ -40,18 +53,6 @@
this.initScrollButtonAffix(); this.initScrollButtonAffix();
} }
if (this.buildStatus === "running" || this.buildStatus === "pending") { if (this.buildStatus === "running" || this.buildStatus === "pending") {
// Bind autoscroll button to follow build output
$('#autoscroll-button').on('click', function() {
var state;
state = $(this).data("state");
if ("enabled" === state) {
$(this).data("state", "disabled");
return $(this).text("Enable autoscroll");
} else {
$(this).data("state", "enabled");
return $(this).text("Disable autoscroll");
}
});
Build.interval = setInterval((function(_this) { Build.interval = setInterval((function(_this) {
// Check for new build output if user still watching build page // Check for new build output if user still watching build page
// Only valid for runnig build when output changes during time // Only valid for runnig build when output changes during time
...@@ -91,9 +92,10 @@ ...@@ -91,9 +92,10 @@
success: function(buildData) { success: function(buildData) {
$('.js-build-output').html(buildData.trace_html); $('.js-build-output').html(buildData.trace_html);
if (removeRefreshStatuses.indexOf(buildData.status) >= 0) { if (removeRefreshStatuses.indexOf(buildData.status) >= 0) {
return $('.js-build-refresh').remove(); this.initScrollMonitor();
return this.$buildRefreshAnimation.remove();
} }
} }.bind(this)
}); });
}; };
...@@ -122,22 +124,95 @@ ...@@ -122,22 +124,95 @@
}; };
Build.prototype.checkAutoscroll = function() { Build.prototype.checkAutoscroll = function() {
if ("enabled" === $("#autoscroll-button").data("state")) { if (this.$autoScrollStatus.data("state") === "enabled") {
return $("html,body").scrollTop($("#build-trace").height()); return $("html,body").scrollTop(this.$buildTrace.height());
}
// Handle a situation where user started new build
// but never scrolled a page
if (!this.$scrollTopBtn.is(':visible') &&
!this.$scrollBottomBtn.is(':visible') &&
!gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
this.$scrollBottomBtn.show();
} }
}; };
Build.prototype.initScrollButtonAffix = function() { Build.prototype.initScrollButtonAffix = function() {
var $body, $buildTrace; // Hide everything initially
$body = $('body'); this.$scrollTopBtn.hide();
$buildTrace = $('#build-trace'); this.$scrollBottomBtn.hide();
return this.$buildScroll.affix({ this.$autoScrollContainer.hide();
offset: { }
bottom: function() {
return $body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top); // Page scroll listener to detect if user has scrolling page
} // and handle following cases
// 1) User is at Top of Build Log;
// - Hide Top Arrow button
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
// 2) User is at Bottom of Build Log;
// - Show Top Arrow button
// - Hide Bottom Arrow button
// - Enable Autoscroll and show indicator (when build is running)
// 3) User is somewhere in middle of Build Log;
// - Show Top Arrow button
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
Build.prototype.initScrollMonitor = function() {
if (!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
// User is somewhere in middle of Build Log
this.$scrollTopBtn.show();
if (this.buildStatus === 'success' || this.buildStatus === 'failed') { // Check if Build is completed
this.$scrollBottomBtn.show();
} else if (this.$buildRefreshAnimation.is(':visible') && !gl.utils.isInViewport(this.$buildRefreshAnimation.get(0))) {
this.$scrollBottomBtn.show();
} else {
this.$scrollBottomBtn.hide();
} }
});
// Hide Autoscroll Status Indicator
if (this.$scrollBottomBtn.is(':visible')) {
this.$autoScrollContainer.hide();
this.$autoScrollStatusText.removeClass('animate');
} else {
this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show();
this.$autoScrollStatusText.addClass('animate');
}
} else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
// User is at Top of Build Log
this.$scrollTopBtn.hide();
this.$scrollBottomBtn.show();
this.$autoScrollContainer.hide();
this.$autoScrollStatusText.removeClass('animate');
} else if ((!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) ||
(this.$buildRefreshAnimation.is(':visible') && gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)))) {
// User is at Bottom of Build Log
this.$scrollTopBtn.show();
this.$scrollBottomBtn.hide();
// Show and Reposition Autoscroll Status Indicator
this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show();
this.$autoScrollStatusText.addClass('animate');
} else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
// Build Log height is small
this.$scrollTopBtn.hide();
this.$scrollBottomBtn.hide();
// Hide Autoscroll Status Indicator
this.$autoScrollContainer.hide();
this.$autoScrollStatusText.removeClass('animate');
}
if (this.buildStatus === "running" || this.buildStatus === "pending") {
// Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise.
this.$autoScrollStatus.data("state", gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)) ? 'enabled' : 'disabled');
}
}; };
Build.prototype.shouldHideSidebarForViewport = function() { Build.prototype.shouldHideSidebarForViewport = function() {
......
...@@ -138,14 +138,8 @@ ...@@ -138,14 +138,8 @@
new MergedButtons(); new MergedButtons();
break; break;
case 'projects:merge_requests:commits': case 'projects:merge_requests:commits':
case 'projects:merge_requests:builds':
new MergedButtons(); new MergedButtons();
break; break;
case 'projects:merge_requests:pipelines':
new gl.MiniPipelineGraph({
container: '.js-pipeline-table',
});
break;
case "projects:merge_requests:diffs": case "projects:merge_requests:diffs":
new gl.Diff(); new gl.Diff();
new ZenMode(); new ZenMode();
...@@ -168,9 +162,6 @@ ...@@ -168,9 +162,6 @@
container: '.js-pipeline-table', container: '.js-pipeline-table',
}); });
break; break;
case 'projects:commit:builds':
new gl.Pipelines();
break;
case 'projects:commits:show': case 'projects:commits:show':
case 'projects:activity': case 'projects:activity':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
......
...@@ -74,15 +74,16 @@ ...@@ -74,15 +74,16 @@
// The below is taken from At.js source // The below is taken from At.js source
// Tweaked to commands to start without a space only if char before is a non-word character // Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js // https://github.com/ichord/At.js
var _a, _y, regexp, match, atSymbols; var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar;
atSymbols = Object.keys(this.app.controllers).join('|'); atSymbolsWithBar = Object.keys(this.app.controllers).join('|');
atSymbolsWithoutBar = Object.keys(this.app.controllers).join('');
subtext = subtext.split(' ').pop(); subtext = subtext.split(' ').pop();
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
_a = decodeURI("%C3%80"); _a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF"); _y = decodeURI("%C3%BF");
regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "(?![" + atSymbols + "])([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)$", 'gi'); regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)$", 'gi');
match = regexp.exec(subtext); match = regexp.exec(subtext);
...@@ -112,7 +113,6 @@ ...@@ -112,7 +113,6 @@
return value.path != null ? this.Emoji.template : this.Loading.template; return value.path != null ? this.Emoji.template : this.Loading.template;
}.bind(this), }.bind(this),
insertTpl: ':${name}:', insertTpl: ':${name}:',
startWithSpace: false,
skipSpecialCharacterTest: true, skipSpecialCharacterTest: true,
data: this.defaultLoadingData, data: this.defaultLoadingData,
callbacks: { callbacks: {
...@@ -129,7 +129,6 @@ ...@@ -129,7 +129,6 @@
}.bind(this), }.bind(this),
insertTpl: '${atwho-at}${username}', insertTpl: '${atwho-at}${username}',
searchKey: 'search', searchKey: 'search',
startWithSpace: false,
alwaysHighlightFirst: true, alwaysHighlightFirst: true,
skipSpecialCharacterTest: true, skipSpecialCharacterTest: true,
data: this.defaultLoadingData, data: this.defaultLoadingData,
...@@ -172,7 +171,6 @@ ...@@ -172,7 +171,6 @@
}.bind(this), }.bind(this),
data: this.defaultLoadingData, data: this.defaultLoadingData,
insertTpl: '${atwho-at}${id}', insertTpl: '${atwho-at}${id}',
startWithSpace: false,
callbacks: { callbacks: {
sorter: this.DefaultOptions.sorter, sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter, filter: this.DefaultOptions.filter,
...@@ -200,7 +198,6 @@ ...@@ -200,7 +198,6 @@
displayTpl: function(value) { displayTpl: function(value) {
return value.title != null ? this.Milestones.template : this.Loading.template; return value.title != null ? this.Milestones.template : this.Loading.template;
}.bind(this), }.bind(this),
startWithSpace: false,
data: this.defaultLoadingData, data: this.defaultLoadingData,
callbacks: { callbacks: {
matcher: this.DefaultOptions.matcher, matcher: this.DefaultOptions.matcher,
...@@ -225,7 +222,6 @@ ...@@ -225,7 +222,6 @@
at: '!', at: '!',
alias: 'mergerequests', alias: 'mergerequests',
searchKey: 'search', searchKey: 'search',
startWithSpace: false,
displayTpl: function(value) { displayTpl: function(value) {
return value.title != null ? this.Issues.template : this.Loading.template; return value.title != null ? this.Issues.template : this.Loading.template;
}.bind(this), }.bind(this),
...@@ -259,7 +255,6 @@ ...@@ -259,7 +255,6 @@
return this.isLoading(value) ? this.Loading.template : this.Labels.template; return this.isLoading(value) ? this.Loading.template : this.Labels.template;
}.bind(this), }.bind(this),
insertTpl: '${atwho-at}${title}', insertTpl: '${atwho-at}${title}',
startWithSpace: false,
callbacks: { callbacks: {
matcher: this.DefaultOptions.matcher, matcher: this.DefaultOptions.matcher,
sorter: this.DefaultOptions.sorter, sorter: this.DefaultOptions.sorter,
...@@ -379,14 +374,7 @@ ...@@ -379,14 +374,7 @@
togglePreventSelection(isPrevented = !!this.setting.tabSelectsMatch) { togglePreventSelection(isPrevented = !!this.setting.tabSelectsMatch) {
this.setting.tabSelectsMatch = !isPrevented; this.setting.tabSelectsMatch = !isPrevented;
this.setting.spaceSelectsMatch = !isPrevented; this.setting.spaceSelectsMatch = !isPrevented;
const eventListenerAction = `${isPrevented ? 'add' : 'remove'}EventListener`;
this.$inputor[0][eventListenerAction]('keydown', gl.GfmAutoComplete.preventSpaceTabEnter);
}, },
preventSpaceTabEnter(e) {
const key = e.which || e.keyCode;
const preventables = [9, 13, 32];
if (preventables.indexOf(key) > -1) e.preventDefault();
}
}; };
}).call(this); }).call(this);
/* eslint-disable func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, prefer-const, padded-blocks, wrap-iife, max-len */ /* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, prefer-const, padded-blocks, wrap-iife, max-len */
/* global Issuable */ /* global Issuable */
/* global Turbolinks */ /* global Turbolinks */
(function() { ((global) => {
var issuable_created; var issuable_created;
issuable_created = false; issuable_created = false;
this.Issuable = { global.Issuable = {
init: function() { init: function() {
Issuable.initTemplates(); Issuable.initTemplates();
Issuable.initSearch(); Issuable.initSearch();
...@@ -111,7 +111,11 @@ ...@@ -111,7 +111,11 @@
filterResults: (function(_this) { filterResults: (function(_this) {
return function(form) { return function(form) {
var formAction, formData, issuesUrl; var formAction, formData, issuesUrl;
formData = form.serialize(); formData = form.serializeArray();
formData = formData.filter(function(data) {
return data.value !== '';
});
formData = $.param(formData);
formAction = form.attr('action'); formAction = form.attr('action');
issuesUrl = formAction; issuesUrl = formAction;
issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&'); issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&');
...@@ -184,4 +188,4 @@ ...@@ -184,4 +188,4 @@
} }
}; };
}).call(this); })(window);
...@@ -93,6 +93,19 @@ ...@@ -93,6 +93,19 @@
} }
}; };
// Check if element scrolled into viewport from above or below
// Courtesy http://stackoverflow.com/a/7557433/414749
w.gl.utils.isInViewport = function(el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= window.innerHeight &&
rect.right <= window.innerWidth
);
};
gl.utils.getPagePath = function() { gl.utils.getPagePath = function() {
return $('body').data('page').split(':')[0]; return $('body').data('page').split(':')[0];
}; };
......
...@@ -59,16 +59,13 @@ ...@@ -59,16 +59,13 @@
class MergeRequestTabs { class MergeRequestTabs {
constructor({ action, setUrl, buildsLoaded, stubLocation } = {}) { constructor({ action, setUrl, stubLocation } = {}) {
this.diffsLoaded = false; this.diffsLoaded = false;
this.buildsLoaded = false;
this.pipelinesLoaded = false; this.pipelinesLoaded = false;
this.commitsLoaded = false; this.commitsLoaded = false;
this.fixedLayoutPref = null; this.fixedLayoutPref = null;
this.setUrl = setUrl !== undefined ? setUrl : true; this.setUrl = setUrl !== undefined ? setUrl : true;
this.buildsLoaded = buildsLoaded || false;
this.setCurrentAction = this.setCurrentAction.bind(this); this.setCurrentAction = this.setCurrentAction.bind(this);
this.tabShown = this.tabShown.bind(this); this.tabShown = this.tabShown.bind(this);
this.showTab = this.showTab.bind(this); this.showTab = this.showTab.bind(this);
...@@ -119,10 +116,6 @@ ...@@ -119,10 +116,6 @@
$.scrollTo('.merge-request-details .merge-request-tabs', { $.scrollTo('.merge-request-details .merge-request-tabs', {
offset: -navBarHeight, offset: -navBarHeight,
}); });
} else if (action === 'builds') {
this.loadBuilds($target.attr('href'));
this.expandView();
this.resetViewContainer();
} else if (action === 'pipelines') { } else if (action === 'pipelines') {
this.loadPipelines($target.attr('href')); this.loadPipelines($target.attr('href'));
this.expandView(); this.expandView();
...@@ -180,8 +173,8 @@ ...@@ -180,8 +173,8 @@
setCurrentAction(action) { setCurrentAction(action) {
this.currentAction = action === 'show' ? 'notes' : action; this.currentAction = action === 'show' ? 'notes' : action;
// Remove a trailing '/commits' '/diffs' '/builds' '/pipelines' '/new' '/new/diffs' // Remove a trailing '/commits' '/diffs' '/pipelines' '/new' '/new/diffs'
let newState = location.pathname.replace(/\/(commits|diffs|builds|pipelines|new|new\/diffs)(\.html)?\/?$/, ''); let newState = location.pathname.replace(/\/(commits|diffs|pipelines|new|new\/diffs)(\.html)?\/?$/, '');
// Append the new action if we're on a tab other than 'notes' // Append the new action if we're on a tab other than 'notes'
if (this.currentAction !== 'notes') { if (this.currentAction !== 'notes') {
...@@ -255,22 +248,6 @@ ...@@ -255,22 +248,6 @@
}); });
} }
loadBuilds(source) {
if (this.buildsLoaded) {
return;
}
this.ajaxGet({
url: `${source}.json`,
success: (data) => {
document.querySelector('div#builds').innerHTML = data.html;
gl.utils.localTimeAgo($('.js-timeago', 'div#builds'));
this.buildsLoaded = true;
new gl.Pipelines();
this.scrollToElement('#builds');
},
});
}
loadPipelines(source) { loadPipelines(source) {
if (this.pipelinesLoaded) { if (this.pipelinesLoaded) {
return; return;
...@@ -282,6 +259,10 @@ ...@@ -282,6 +259,10 @@
gl.utils.localTimeAgo($('.js-timeago', '#pipelines')); gl.utils.localTimeAgo($('.js-timeago', '#pipelines'));
this.pipelinesLoaded = true; this.pipelinesLoaded = true;
this.scrollToElement('#pipelines'); this.scrollToElement('#pipelines');
new gl.MiniPipelineGraph({
container: '.js-pipeline-table',
});
}, },
}); });
} }
......
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
MergeRequestWidget.prototype.addEventListeners = function() { MergeRequestWidget.prototype.addEventListeners = function() {
var allowedPages; var allowedPages;
allowedPages = ['show', 'commits', 'builds', 'pipelines', 'changes']; allowedPages = ['show', 'commits', 'pipelines', 'changes'];
$(document).on('page:change.merge_request', (function(_this) { $(document).on('page:change.merge_request', (function(_this) {
return function() { return function() {
var page; var page;
...@@ -173,7 +173,6 @@ ...@@ -173,7 +173,6 @@
message = message.replace('{{title}}', data.title); message = message.replace('{{title}}', data.title);
notify(title, message, _this.opts.gitlab_icon, function() { notify(title, message, _this.opts.gitlab_icon, function() {
this.close(); this.close();
return Turbolinks.visit(_this.opts.builds_path);
}); });
} }
} }
......
/* global merge_request_widget */
(() => {
$(() => {
/* TODO: This needs a better home, or should be refactored. It was previously contained
* in a script tag in app/views/projects/merge_requests/widget/open/_accept.html.haml,
* but Vue chokes on script tags and prevents their execution. So it was moved here
* temporarily.
* */
if ($('.accept-mr-form').length) {
$('.accept-mr-form').on('ajax:send', () => {
$('.accept-mr-form :input').disable();
});
$('.accept_merge_request').on('click', () => {
$('.js-merge-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress');
});
$('.merge_when_build_succeeds').on('click', () => {
$('#merge_when_build_succeeds').val('1');
});
$('.js-merge-dropdown a').on('click', (e) => {
e.preventDefault();
$(this).closest('form').submit();
});
} else if ($('.rebase-in-progress').length) {
merge_request_widget.rebaseInProgress();
} else if ($('.rebase-mr-form').length) {
$('.rebase-mr-form').on('ajax:send', () => {
$('.rebase-mr-form :input').disable();
});
$('.js-rebase-button').on('click', () => {
$('.js-rebase-button').html("<i class='fa fa-spinner fa-spin'></i> Rebase in progress");
});
} else {
merge_request_widget.getMergeStatus();
}
});
})();
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
@import "framework/asciidoctor.scss"; @import "framework/asciidoctor.scss";
@import "framework/blocks.scss"; @import "framework/blocks.scss";
@import "framework/buttons.scss"; @import "framework/buttons.scss";
@import "framework/badges.scss";
@import "framework/calendar.scss"; @import "framework/calendar.scss";
@import "framework/callout.scss"; @import "framework/callout.scss";
@import "framework/common.scss"; @import "framework/common.scss";
......
.badge {
font-weight: normal;
background-color: $badge-bg;
color: $badge-color;
vertical-align: baseline;
}
.badge-dark {
background-color: $badge-bg-dark;
color: $badge-color-dark;
}
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
padding: 20px; padding: 20px;
color: $gl-gray; color: $gl-gray;
font-weight: normal; font-weight: normal;
font-size: 16px; font-size: 14px;
line-height: 36px; line-height: 36px;
&.diff-collapsed { &.diff-collapsed {
......
...@@ -76,13 +76,6 @@ ...@@ -76,13 +76,6 @@
color: $black; color: $black;
} }
} }
.badge {
font-weight: normal;
background-color: $nav-badge-bg;
color: $gl-gray-light;
vertical-align: baseline;
}
} }
&.sub-nav { &.sub-nav {
...@@ -434,4 +427,4 @@ ...@@ -434,4 +427,4 @@
border-bottom: none; border-bottom: none;
} }
} }
} }
\ No newline at end of file
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
@import "bootstrap/labels"; @import "bootstrap/labels";
@import "bootstrap/badges"; @import "bootstrap/badges";
@import "bootstrap/alerts"; @import "bootstrap/alerts";
// @import "bootstrap/progress-bars"; @import "bootstrap/progress-bars";
@import "bootstrap/list-group"; @import "bootstrap/list-group";
@import "bootstrap/wells"; @import "bootstrap/wells";
@import "bootstrap/close"; @import "bootstrap/close";
......
...@@ -134,8 +134,8 @@ $md-area-border: #ddd; ...@@ -134,8 +134,8 @@ $md-area-border: #ddd;
/* /*
* Code * Code
*/ */
$code_font_size: 13px; $code_font_size: 12px;
$code_line_height: 1.5; $code_line_height: 1.6;
/* /*
* Padding * Padding
...@@ -279,6 +279,14 @@ $dropdown-toggle-active-border-color: darken($border-color, 14%); ...@@ -279,6 +279,14 @@ $dropdown-toggle-active-border-color: darken($border-color, 14%);
$btn-active-gray: #ececec; $btn-active-gray: #ececec;
$btn-active-gray-light: e4e7ed; $btn-active-gray-light: e4e7ed;
/*
* Badges
*/
$badge-bg: #f3f3f3;
$badge-bg-dark: #eee;
$badge-color: #929292;
$badge-color-dark: #8f8f8f;
/* /*
* Award emoji * Award emoji
*/ */
......
@keyframes fade-out-status {
0%, 50% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes blinking-dots {
0% {
background-color: rgba($white-light, 1);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 0 0 0 rgba($white-light,0.2);
}
25% {
background-color: rgba($white-light, 0.4);
box-shadow: 12px 0 0 0 rgba($white-light,2),
24px 0 0 0 rgba($white-light,0.2);
}
75% {
background-color: rgba($white-light, 0.4);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 0 0 0 rgba($white-light,1);
}
100% {
background-color: rgba($white-light, 1);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 0 0 0 rgba($white-light,0.2);
}
}
.build-page { .build-page {
pre.trace { pre.trace {
background: $builds-trace-bg; background: $builds-trace-bg;
...@@ -14,47 +45,101 @@ ...@@ -14,47 +45,101 @@
} }
} }
.scroll-controls { .environment-information {
.scroll-step { background-color: $gray-light;
width: 31px; border: 1px solid $border-color;
margin: 0 0 0 auto; padding: 12px $gl-padding;
border-radius: $border-radius-default;
svg {
position: relative;
top: 1px;
margin-right: 5px;
} }
}
}
.scroll-controls {
height: 100%;
.scroll-step {
width: 31px;
margin: 0 0 0 auto;
}
.scroll-link,
.autoscroll-container {
right: 25px;
z-index: 1;
}
.scroll-link {
position: fixed;
display: block;
margin-bottom: 10px;
&.affix-bottom { &.scroll-top .gitlab-icon-scroll-up-hover,
position: absolute; &.scroll-top:hover .gitlab-icon-scroll-up,
right: 25px; &.scroll-bottom .gitlab-icon-scroll-down-hover,
&.scroll-bottom:hover .gitlab-icon-scroll-down {
display: none;
} }
&.affix { &.scroll-top:hover .gitlab-icon-scroll-up-hover,
right: 25px; &.scroll-bottom:hover .gitlab-icon-scroll-down-hover {
bottom: 15px; display: inline-block;
z-index: 1;
} }
&.sidebar-expanded { &.scroll-top {
right: #{$gutter_width + ($gl-padding * 2)}; top: 110px;
} }
a { &.scroll-bottom {
display: block; bottom: -2px;
margin-bottom: 10px;
} }
} }
.environment-information { .autoscroll-container {
background-color: $gray-light; position: absolute;
border: 1px solid $border-color; }
padding: 12px $gl-padding;
border-radius: $border-radius-default;
svg { &.sidebar-expanded {
position: relative;
top: 1px; .scroll-link,
margin-right: 5px; .autoscroll-container {
right: ($gutter_width + ($gl-padding * 2));
} }
} }
} }
.status-message {
display: inline-block;
color: $white-light;
.status-icon {
display: inline-block;
width: 16px;
height: 33px;
}
.status-text {
float: left;
opacity: 0;
margin-right: 10px;
font-weight: normal;
line-height: 1.8;
transition: opacity 1s ease-out;
&.animate {
animation: fade-out-status 2s ease;
}
}
&:hover .status-text {
opacity: 1;
}
}
.build-header { .build-header {
position: relative; position: relative;
padding: 0; padding: 0;
...@@ -109,6 +194,15 @@ ...@@ -109,6 +194,15 @@
.bash { .bash {
display: block; display: block;
} }
.build-loader-animation {
position: relative;
width: 6px;
height: 6px;
margin: auto auto 12px 2px;
border-radius: 50%;
animation: blinking-dots 1s linear infinite;
}
} }
.right-sidebar.build-sidebar { .right-sidebar.build-sidebar {
...@@ -248,6 +342,12 @@ ...@@ -248,6 +342,12 @@
} }
} }
.build-sidebar {
.container-fluid.container-limited {
max-width: 100%;
}
}
.build-detail-row { .build-detail-row {
margin-bottom: 5px; margin-bottom: 5px;
......
...@@ -121,13 +121,6 @@ ...@@ -121,13 +121,6 @@
.folder-name { .folder-name {
cursor: pointer; cursor: pointer;
.badge {
font-weight: normal;
background-color: $gray-darker;
color: $gl-gray-light;
vertical-align: baseline;
}
} }
} }
...@@ -142,4 +135,4 @@ ...@@ -142,4 +135,4 @@
margin-right: 0; margin-right: 0;
} }
} }
} }
\ No newline at end of file
...@@ -434,6 +434,7 @@ ...@@ -434,6 +434,7 @@
.issuable-meta { .issuable-meta {
display: inline-block; display: inline-block;
line-height: 18px; line-height: 18px;
font-size: 14px;
} }
.js-issuable-selector-wrap { .js-issuable-selector-wrap {
......
...@@ -98,7 +98,7 @@ ...@@ -98,7 +98,7 @@
} }
.label { .label {
padding: 8px 9px 9px; padding: 8px 9px 9px $gl-padding;
font-size: 14px; font-size: 14px;
} }
} }
......
...@@ -21,6 +21,14 @@ ...@@ -21,6 +21,14 @@
display: inline-block; display: inline-block;
float: left; float: left;
.btn-success.dropdown-toggle .fa {
color: inherit;
}
.btn-success.dropdown-toggle:disabled {
background-color: $gl-success;
}
.accept_merge_request { .accept_merge_request {
&.ci-pending, &.ci-pending,
&.ci-running { &.ci-running {
...@@ -96,7 +104,7 @@ ...@@ -96,7 +104,7 @@
.mr-widget-body { .mr-widget-body {
h4 { h4 {
font-weight: 600; font-weight: 600;
font-size: 17px; font-size: 16px;
margin: 5px 0; margin: 5px 0;
color: $gl-gray-dark; color: $gl-gray-dark;
......
...@@ -25,12 +25,6 @@ ...@@ -25,12 +25,6 @@
} }
.issuable-row { .issuable-row {
.color-label {
border-radius: 2px;
padding: 3px !important;
margin-right: 7px;
}
span a { span a {
color: $gl-text-color; color: $gl-text-color;
word-wrap: break-word; word-wrap: break-word;
......
...@@ -448,11 +448,6 @@ ul.notes { ...@@ -448,11 +448,6 @@ ul.notes {
border-radius: $border-radius-base; border-radius: $border-radius-base;
} }
.diff-file .note .note-actions {
right: 0;
top: 0;
}
/** /**
* Line note button on the side of diffs * Line note button on the side of diffs
...@@ -586,3 +581,19 @@ ul.notes { ...@@ -586,3 +581,19 @@ ul.notes {
} }
} }
} }
// Merge request notes in diffs
.diff-file {
// Diff is side by side
.notes_content.parallel .note-header .note-headline-light {
display: block;
position: relative;
}
// Diff is inline
.notes_content .note-header .note-headline-light {
display: inline-block;
position: relative;
}
}
...@@ -63,6 +63,10 @@ ...@@ -63,6 +63,10 @@
} }
} }
.content-list.pipelines .table-holder {
min-height: 300px;
}
.pipeline-holder { .pipeline-holder {
width: 100%; width: 100%;
overflow: auto; overflow: auto;
...@@ -91,6 +95,10 @@ ...@@ -91,6 +95,10 @@
td { td {
padding: 10px 8px; padding: 10px 8px;
} }
.commit-link {
padding: 9px 8px 10px;
}
} }
tbody { tbody {
...@@ -203,7 +211,7 @@ ...@@ -203,7 +211,7 @@
width: 8px; width: 8px;
position: absolute; position: absolute;
right: -7px; right: -7px;
bottom: 9px; top: 10px;
border-bottom: 2px solid $border-color; border-bottom: 2px solid $border-color;
} }
} }
...@@ -509,15 +517,10 @@ ...@@ -509,15 +517,10 @@
> .ci-action-icon-container { > .ci-action-icon-container {
position: absolute; position: absolute;
right: 4px; right: 5px;
top: 5px; top: 5px;
} }
.ci-status-icon {
position: relative;
top: 1px;
}
.ci-status-icon svg { .ci-status-icon svg {
height: 20px; height: 20px;
width: 20px; width: 20px;
...@@ -604,6 +607,27 @@ ...@@ -604,6 +607,27 @@
} }
} }
} }
.grouped-pipeline-dropdown {
.dropdown-build {
.build-content {
width: 100%;
&:hover {
background-color: $stage-hover-bg;
color: $gl-text-color;
}
}
.ci-action-icon-container {
padding: 0;
font-size: 11px;
position: absolute;
top: 1px;
right: 8px;
}
}
}
} }
.dropdown-counter-badge { .dropdown-counter-badge {
...@@ -614,9 +638,11 @@ ...@@ -614,9 +638,11 @@
margin-right: 2px; margin-right: 2px;
} }
.grouped-pipeline-dropdown { .grouped-pipeline-dropdown {
padding: 0; padding: 0;
width: 191px; width: 191px;
min-width: 191px;
left: auto; left: auto;
right: -195px; right: -195px;
top: -4px; top: -4px;
...@@ -624,28 +650,55 @@ ...@@ -624,28 +650,55 @@
a { a {
display: inline-block; display: inline-block;
}
&:hover { .dropdown-build {
background-color: $stage-hover-bg; .build-content {
width: 100%;
&:hover {
background-color: $stage-hover-bg;
color: $gl-text-color;
}
}
.ci-action-icon-container {
padding: 0;
font-size: 11px;
position: absolute;
margin-top: 3px;
right: 7px;
} }
} }
ul { ul {
max-height: 245px; max-height: 245px;
overflow: auto; overflow: auto;
margin: 5px 0; margin: 3px 0;
li { li {
padding-top: 2px; margin: 4px 8px 4px 9px;
margin: 0 5px; padding: 0;
padding-left: 0; line-height: 1.1;
padding-bottom: 0; position: relative;
margin-bottom: 0;
line-height: 1.2; .ci-action-icon-container:hover {
background-color: transparent;
}
.ci-status-icon {
position: relative;
top: 2px;
}
} }
} }
} }
.pipeline-graph .dropdown-build .ci-status-icon svg {
width: 18px;
height: 18px;
}
.ci-status-text { .ci-status-text {
max-width: 110px; max-width: 110px;
white-space: nowrap; white-space: nowrap;
...@@ -654,7 +707,7 @@ ...@@ -654,7 +707,7 @@
vertical-align: bottom; vertical-align: bottom;
display: inline-block; display: inline-block;
position: relative; position: relative;
font-weight: 100; font-weight: 200;
} }
// Action Icons // Action Icons
...@@ -690,11 +743,15 @@ ...@@ -690,11 +743,15 @@
.dropdown-build { .dropdown-build {
color: $gl-text-color-light; color: $gl-text-color-light;
.build-content {
padding: 4px 7px 8px;
}
.ci-action-icon-container { .ci-action-icon-container {
padding: 0; padding: 0;
font-size: 11px; font-size: 11px;
float: right; float: right;
margin-top: 4px; margin-top: 3px;
display: inline-block; display: inline-block;
position: relative; position: relative;
...@@ -704,16 +761,10 @@ ...@@ -704,16 +761,10 @@
} }
} }
&:hover {
background-color: $stage-hover-bg;
border-radius: 3px;
color: $gl-text-color;
}
.ci-action-icon-container { .ci-action-icon-container {
i { i {
width: 25px; width: 24px;
height: 25px; height: 24px;
&::before { &::before {
top: 1px; top: 1px;
...@@ -743,11 +794,76 @@ ...@@ -743,11 +794,76 @@
.mini-pipeline-graph { .mini-pipeline-graph {
.builds-dropdown { .builds-dropdown {
background-color: transparent; background-color: transparent;
border: none;
padding: 0; padding: 0;
color: $gl-text-color-light; color: $gl-text-color-light;
border: none; border: none;
margin: 0; margin: 0;
&:focus,
&:hover {
outline: none;
margin-right: -8px;
.ci-status-icon {
width: 32px;
padding: 0 8px 0 0;
transition: width 0.1s cubic-bezier(0.25, 0, 1, 1);
+ .dropdown-caret {
visibility: visible;
opacity: 1;
}
}
}
&:focus,
&:active {
.ci-status-icon-success {
background-color: rgba($gl-success, .1);
}
.ci-status-icon-failed {
background-color: rgba($gl-danger, .1);
}
.ci-status-icon-pending,
.ci-status-icon-success_with_warnings {
background-color: rgba($gl-warning, .1);
}
.ci-status-icon-running {
background-color: rgba($blue-normal, .1);
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found {
background-color: rgba($gl-gray, .1);
}
.ci-status-icon-created,
.ci-status-icon-skipped {
background-color: rgba($gray-darkest, .1);
}
}
.mini-pipeline-graph-icon-container {
.dropdown-caret {
font-size: 11px;
position: absolute;
top: 6px;
left: 20px;
margin-right: -6px;
z-index: 2;
visibility: hidden;
opacity: 0;
transition: visibility 0.1s, opacity 0.1s linear;
}
}
}
.dropdown-build .build-content {
padding: 3px 7px 7px;
} }
.builds-dropdown-loading { .builds-dropdown-loading {
...@@ -798,89 +914,26 @@ ...@@ -798,89 +914,26 @@
.mini-pipeline-graph-icon-container .ci-status-icon { .mini-pipeline-graph-icon-container .ci-status-icon {
display: inline-block; display: inline-block;
border: 1px solid; border: 1px solid;
border-radius: 20px; border-radius: 22px;
margin-right: 1px; margin-right: 1px;
width: 20px; width: 22px;
height: 20px; height: 22px;
position: relative; position: relative;
z-index: 2; z-index: 2;
transition: all 0.2s cubic-bezier(0.25, 0, 1, 1); transition: all 0.1s cubic-bezier(0.25, 0, 1, 1);
svg { svg {
top: -1px; top: -1px;
left: -1px;
} }
} }
.builds-dropdown { .stage-cell .mini-pipeline-graph-icon-container .ci-status-icon svg {
&:focus { width: 22px;
outline: none; height: 22px;
margin-right: -8px;
.ci-status-icon {
width: 28px;
padding: 0 8px 0 0;
transition: width 0.2s cubic-bezier(0.25, 0, 1, 1);
+ .dropdown-caret {
display: inline-block;
}
}
}
&:focus,
&:active {
.ci-status-icon-success {
background-color: rgba($gl-success, .1);
}
.ci-status-icon-failed {
background-color: rgba($gl-danger, .1);
}
.ci-status-icon-pending,
.ci-status-icon-success_with_warnings {
background-color: rgba($gl-warning, .1);
}
.ci-status-icon-running {
background-color: rgba($blue-normal, .1);
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found {
background-color: rgba($gl-gray, .1);
}
.ci-status-icon-created,
.ci-status-icon-skipped {
background-color: rgba($gray-darkest, .1);
}
}
.mini-pipeline-graph-icon-container {
.ci-status-icon:hover,
.ci-status-icon:focus {
width: 28px;
padding: 0 8px 0 0;
+ .dropdown-caret {
display: inline-block;
}
}
.dropdown-caret {
font-size: 11px;
position: relative;
top: 3px;
left: -11px;
margin-right: -6px;
display: none;
z-index: 2;
}
}
} }
.terminal-icon { .terminal-icon {
margin-left: 3px; margin-left: 3px;
} }
......
...@@ -880,3 +880,23 @@ pre.light-well { ...@@ -880,3 +880,23 @@ pre.light-well {
width: 30%; width: 30%;
} }
} }
.services-installation-info .row {
margin-bottom: 10px;
}
.service-installation {
padding: 32px;
margin: 32px;
border-radius: 3px;
background-color: $white-light;
h3 {
margin-top: 0;
}
hr {
margin: 32px 0;
border-color: $border-color;
}
}
.container-fluid { .container-fluid {
.ci-status { .ci-status {
padding: 2px 7px; padding: 2px 7px 4px;
margin-right: 10px; margin-right: 10px;
border: 1px solid $gray-darker; border: 1px solid $gray-darker;
white-space: nowrap; white-space: nowrap;
......
...@@ -82,6 +82,8 @@ class GroupsController < Groups::ApplicationController ...@@ -82,6 +82,8 @@ class GroupsController < Groups::ApplicationController
if Groups::UpdateService.new(@group, current_user, group_params).execute if Groups::UpdateService.new(@group, current_user, group_params).execute
redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated." redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
else else
@group.reset_path!
render action: "edit" render action: "edit"
end end
end end
......
...@@ -8,13 +8,11 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -8,13 +8,11 @@ class Projects::CommitController < Projects::ApplicationController
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds] before_action :authorize_download_code!
before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_read_pipeline!, only: [:pipelines] before_action :authorize_read_pipeline!, only: [:pipelines]
before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit before_action :commit
before_action :define_commit_vars, only: [:show, :diff_for_path, :builds, :pipelines] before_action :define_commit_vars, only: [:show, :diff_for_path, :pipelines]
before_action :define_status_vars, only: [:show, :builds, :pipelines] before_action :define_status_vars, only: [:show, :pipelines]
before_action :define_note_vars, only: [:show, :diff_for_path] before_action :define_note_vars, only: [:show, :diff_for_path]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick] before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
...@@ -35,25 +33,6 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -35,25 +33,6 @@ class Projects::CommitController < Projects::ApplicationController
def pipelines def pipelines
end end
def builds
end
def cancel_builds
ci_builds.running_or_pending.each(&:cancel)
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def retry_builds
ci_builds.latest.failed.each do |build|
if build.retryable?
Ci::Build.retry(build, current_user)
end
end
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def branches def branches
@branches = @project.repository.branch_names_contains(commit.id) @branches = @project.repository.branch_names_contains(commit.id)
@tags = @project.repository.tag_names_contains(commit.id) @tags = @project.repository.tag_names_contains(commit.id)
...@@ -98,10 +77,6 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -98,10 +77,6 @@ class Projects::CommitController < Projects::ApplicationController
@noteable = @commit ||= @project.commit(params[:id]) @noteable = @commit ||= @project.commit(params[:id])
end end
def ci_builds
@ci_builds ||= Ci::Build.where(pipeline: pipelines)
end
def define_commit_vars def define_commit_vars
return git_not_found! unless commit return git_not_found! unless commit
...@@ -133,8 +108,6 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -133,8 +108,6 @@ class Projects::CommitController < Projects::ApplicationController
def define_status_vars def define_status_vars
@ci_pipelines = project.pipelines.where(sha: commit.sha) @ci_pipelines = project.pipelines.where(sha: commit.sha)
@statuses = CommitStatus.where(pipeline: @ci_pipelines).relevant
@builds = Ci::Build.where(pipeline: @ci_pipelines).relevant
end end
def assign_change_commit_vars(mr_source_branch) def assign_change_commit_vars(mr_source_branch)
......
class Projects::MattermostsController < Projects::ApplicationController
include TriggersHelper
include ActionView::Helpers::AssetUrlHelper
layout 'project_settings'
before_action :authorize_admin_project!
before_action :service
before_action :teams, only: [:new]
def new
end
def create
result, message = @service.configure(current_user, configure_params)
if result
flash[:notice] = 'This service is now configured'
redirect_to edit_namespace_project_service_path(
@project.namespace, @project, service)
else
flash[:alert] = message || 'Failed to configure service'
redirect_to new_namespace_project_mattermost_path(
@project.namespace, @project)
end
end
private
def configure_params
params.require(:mattermost).permit(:trigger, :team_id).merge(
url: service_trigger_url(@service),
icon_url: asset_url('gitlab_logo.png'))
end
def teams
@teams ||= @service.list_teams(current_user)
end
def service
@service ||= @project.find_or_initialize_service('mattermost_slash_commands')
end
end
...@@ -9,10 +9,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -9,10 +9,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled before_action :module_enabled
before_action :merge_request, only: [ before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines, :merge, :merge_check, :edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge, :merge_check,
:ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues :ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
] ]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds, :pipelines] before_action :validates_merge_request, only: [:show, :diffs, :commits, :pipelines]
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines] before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check] before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs] before_action :define_commit_vars, only: [:diffs]
...@@ -201,17 +201,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -201,17 +201,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
end end
def builds
respond_to do |format|
format.html do
define_discussion_vars
render 'show'
end
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
end
end
def pipelines def pipelines
@pipelines = @merge_request.all_pipelines @pipelines = @merge_request.all_pipelines
......
module AuthHelper module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2).freeze PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
def ldap_enabled? def ldap_enabled?
......
...@@ -188,7 +188,7 @@ module BlobHelper ...@@ -188,7 +188,7 @@ module BlobHelper
end end
def gitlab_ci_ymls def gitlab_ci_ymls
@gitlab_ci_ymls ||= Gitlab::Template::GitlabCiYmlTemplate.dropdown_names @gitlab_ci_ymls ||= Gitlab::Template::GitlabCiYmlTemplate.dropdown_names(params[:context])
end end
def dockerfile_names def dockerfile_names
......
module CiStatusHelper module CiStatusHelper
def ci_status_path(pipeline) def ci_status_path(pipeline)
project = pipeline.project project = pipeline.project
builds_namespace_project_commit_path(project.namespace, project, pipeline.sha) namespace_project_pipeline_path(project.namespace, project, pipeline)
end end
# Is used by Commit and Merge Request Widget # Is used by Commit and Merge Request Widget
......
...@@ -96,8 +96,8 @@ module IssuablesHelper ...@@ -96,8 +96,8 @@ module IssuablesHelper
if issuable.tasks? if issuable.tasks?
output << "&ensp;".html_safe output << "&ensp;".html_safe
output << content_tag(:span, issuable.task_status, id: "task_status", class: "hidden-xs") output << content_tag(:span, issuable.task_status, id: "task_status", class: "hidden-xs hidden-sm")
output << content_tag(:span, issuable.task_status_short, id: "task_status_short", class: "hidden-sm hidden-md hidden-lg") output << content_tag(:span, issuable.task_status_short, id: "task_status_short", class: "hidden-md hidden-lg")
end end
output output
......
module MattermostHelper
def mattermost_teams_options(teams)
teams_options = teams.map do |id, options|
[options['display_name'] || options['name'], id]
end
teams_options.compact.unshift(['Select team...', '0'])
end
end
...@@ -280,13 +280,15 @@ module ProjectsHelper ...@@ -280,13 +280,15 @@ module ProjectsHelper
end end
end end
def add_special_file_path(project, file_name:, commit_message: nil) def add_special_file_path(project, file_name:, commit_message: nil, target_branch: nil, context: nil)
namespace_project_new_blob_path( namespace_project_new_blob_path(
project.namespace, project.namespace,
project, project,
project.default_branch || 'master', project.default_branch || 'master',
file_name: file_name, file_name: file_name,
commit_message: commit_message || "Add #{file_name.downcase}" commit_message: commit_message || "Add #{file_name.downcase}",
target_branch: target_branch,
context: context
) )
end end
......
...@@ -35,7 +35,7 @@ module TodosHelper ...@@ -35,7 +35,7 @@ module TodosHelper
else else
path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target] path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target]
path.unshift(:builds) if todo.build_failed? path.unshift(:pipelines) if todo.build_failed?
polymorphic_path(path, anchor: anchor) polymorphic_path(path, anchor: anchor)
end end
......
...@@ -93,11 +93,8 @@ module Ci ...@@ -93,11 +93,8 @@ module Ci
.select("max(#{quoted_table_name}.id)") .select("max(#{quoted_table_name}.id)")
.group(:ref, :sha) .group(:ref, :sha)
if ref relation = ref ? where(ref: ref) : self
where(id: max_id, ref: ref) relation.where(id: max_id).order(id: :desc)
else
where(id: max_id)
end
end end
def self.latest_status(ref = nil) def self.latest_status(ref = nil)
......
...@@ -98,7 +98,7 @@ class Namespace < ActiveRecord::Base ...@@ -98,7 +98,7 @@ class Namespace < ActiveRecord::Base
def move_dir def move_dir
if any_project_has_container_registry_tags? if any_project_has_container_registry_tags?
raise Exception.new('Namespace cannot be moved, because at least one project has tags in container registry') raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
end end
# Move the namespace directory in all storages paths used by member projects # Move the namespace directory in all storages paths used by member projects
...@@ -111,7 +111,7 @@ class Namespace < ActiveRecord::Base ...@@ -111,7 +111,7 @@ class Namespace < ActiveRecord::Base
# if we cannot move namespace directory we should rollback # if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs # db changes in order to prevent out of sync between db and fs
raise Exception.new('namespace directory cannot be moved') raise Gitlab::UpdatePathError.new('namespace directory cannot be moved')
end end
end end
......
...@@ -418,7 +418,7 @@ class Project < ActiveRecord::Base ...@@ -418,7 +418,7 @@ class Project < ActiveRecord::Base
repository.commit(ref) repository.commit(ref)
end end
# ref can't be HEAD, can only be branch/tag name or SHA # ref can't be HEAD or SHA, can only be branch/tag name
def latest_successful_builds_for(ref = default_branch) def latest_successful_builds_for(ref = default_branch)
latest_pipeline = pipelines.latest_successful_for(ref) latest_pipeline = pipelines.latest_successful_for(ref)
......
...@@ -49,11 +49,13 @@ class ChatNotificationService < Service ...@@ -49,11 +49,13 @@ class ChatNotificationService < Service
return false unless message return false unless message
opt = {} channel_name = get_channel_field(object_kind).presence || channel
opt[:channel] = get_channel_field(object_kind).presence || channel || default_channel opts = {}
opt[:username] = username if username opts[:channel] = channel_name if channel_name
notifier = Slack::Notifier.new(webhook, opt) opts[:username] = username if username
notifier = Slack::Notifier.new(webhook, opts)
notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback)
true true
...@@ -71,7 +73,7 @@ class ChatNotificationService < Service ...@@ -71,7 +73,7 @@ class ChatNotificationService < Service
fields.reject { |field| field[:name].end_with?('channel') } fields.reject { |field| field[:name].end_with?('channel') }
end end
def default_channel def default_channel_placeholder
raise NotImplementedError raise NotImplementedError
end end
...@@ -103,7 +105,7 @@ class ChatNotificationService < Service ...@@ -103,7 +105,7 @@ class ChatNotificationService < Service
def build_event_channels def build_event_channels
supported_events.reduce([]) do |channels, event| supported_events.reduce([]) do |channels, event|
channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel } channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel_placeholder }
end end
end end
......
...@@ -35,7 +35,7 @@ class MattermostService < ChatNotificationService ...@@ -35,7 +35,7 @@ class MattermostService < ChatNotificationService
] ]
end end
def default_channel def default_channel_placeholder
"#town-square" "#town-square"
end end
end end
...@@ -18,4 +18,34 @@ class MattermostSlashCommandsService < ChatSlashCommandsService ...@@ -18,4 +18,34 @@ class MattermostSlashCommandsService < ChatSlashCommandsService
def to_param def to_param
'mattermost_slash_commands' 'mattermost_slash_commands'
end end
def configure(user, params)
token = Mattermost::Command.new(user).
create(command(params))
update(active: true, token: token) if token
rescue Mattermost::Error => e
[false, e.message]
end
def list_teams(user)
Mattermost::Team.new(user).all
rescue Mattermost::Error => e
[[], e.message]
end
private
def command(params)
pretty_project_name = project.name_with_namespace
params.merge(
auto_complete: true,
auto_complete_desc: "Perform common operations on: #{pretty_project_name}",
auto_complete_hint: '[help]',
description: "Perform common operations on: #{pretty_project_name}",
display_name: "GitLab / #{pretty_project_name}",
method: 'P',
username: 'GitLab')
end
end end
...@@ -34,7 +34,7 @@ class SlackService < ChatNotificationService ...@@ -34,7 +34,7 @@ class SlackService < ChatNotificationService
] ]
end end
def default_channel def default_channel_placeholder
"#general" "#general"
end end
end end
...@@ -16,7 +16,7 @@ class SlackSlashCommandsService < ChatSlashCommandsService ...@@ -16,7 +16,7 @@ class SlackSlashCommandsService < ChatSlashCommandsService
def trigger(params) def trigger(params)
# Format messages to be Slack-compatible # Format messages to be Slack-compatible
super.tap do |result| super.tap do |result|
result[:text] = format(result[:text]) result[:text] = format(result[:text]) if result.is_a?(Hash)
end end
end end
......
...@@ -654,11 +654,19 @@ class Repository ...@@ -654,11 +654,19 @@ class Repository
end end
def last_commit_for_path(sha, path) def last_commit_for_path(sha, path)
args = %W(#{Gitlab.config.git.bin_path} rev-list --max-count=1 #{sha} -- #{path}) sha = last_commit_id_for_path(sha, path)
sha = Gitlab::Popen.popen(args, path_to_repo).first.strip
commit(sha) commit(sha)
end end
def last_commit_id_for_path(sha, path)
key = path.blank? ? "last_commit_id_for_path:#{sha}" : "last_commit_id_for_path:#{sha}:#{Digest::SHA1.hexdigest(path)}"
cache.fetch(key) do
args = %W(#{Gitlab.config.git.bin_path} rev-list --max-count=1 #{sha} -- #{path})
Gitlab::Popen.popen(args, path_to_repo).first.strip
end
end
def next_branch(name, opts = {}) def next_branch(name, opts = {})
branch_ids = self.branch_names.map do |n| branch_ids = self.branch_names.map do |n|
next 1 if n == name next 1 if n == name
......
...@@ -3,6 +3,9 @@ class GitPushService < BaseService ...@@ -3,6 +3,9 @@ class GitPushService < BaseService
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
include Gitlab::Access include Gitlab::Access
# The N most recent commits to process in a single push payload.
PROCESS_COMMIT_LIMIT = 100
# This method will be called after each git update # This method will be called after each git update
# and only if the provided user and project are present in GitLab. # and only if the provided user and project are present in GitLab.
# #
...@@ -77,6 +80,16 @@ class GitPushService < BaseService ...@@ -77,6 +80,16 @@ class GitPushService < BaseService
ProjectCacheWorker.perform_async(@project.id, types) ProjectCacheWorker.perform_async(@project.id, types)
end end
# Schedules processing of commit messages.
def process_commit_messages
default = is_default_branch?
push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
ProcessCommitWorker.
perform_async(project.id, current_user.id, commit.to_hash, default)
end
end
protected protected
def execute_related_hooks def execute_related_hooks
...@@ -128,17 +141,6 @@ class GitPushService < BaseService ...@@ -128,17 +141,6 @@ class GitPushService < BaseService
end end
end end
# Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched,
# close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables.
def process_commit_messages
default = is_default_branch?
@push_commits.each do |commit|
ProcessCommitWorker.
perform_async(project.id, current_user.id, commit.to_hash, default)
end
end
def build_push_data def build_push_data
@push_data ||= Gitlab::DataBuilder::Push.build( @push_data ||= Gitlab::DataBuilder::Push.build(
@project, @project,
......
...@@ -14,7 +14,13 @@ module Groups ...@@ -14,7 +14,13 @@ module Groups
group.assign_attributes(params) group.assign_attributes(params)
group.save begin
group.save
rescue Gitlab::UpdatePathError => e
group.errors.add(:base, e.message)
false
end
end end
end end
end end
...@@ -14,7 +14,8 @@ class ProjectPathValidator < ActiveModel::EachValidator ...@@ -14,7 +14,8 @@ class ProjectPathValidator < ActiveModel::EachValidator
# without tree as reserved name routing can match 'group/project' as group name, # without tree as reserved name routing can match 'group/project' as group name,
# 'tree' as project name and 'deploy_keys' as route. # 'tree' as project name and 'deploy_keys' as route.
# #
RESERVED = (NamespaceValidator::RESERVED + RESERVED = (NamespaceValidator::RESERVED -
%w[dashboard help ci admin search notes services] +
%w[tree commits wikis new edit create update logs_tree %w[tree commits wikis new edit create update logs_tree
preview blob blame raw files create_dir find_file]).freeze preview blob blame raw files create_dir find_file]).freeze
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
= link_to admin_abuse_reports_path, title: "Abuse Reports" do = link_to admin_abuse_reports_path, title: "Abuse Reports" do
%span %span
Abuse Reports Abuse Reports
%span.badge.count= number_with_delimiter(AbuseReport.count(:all)) %span.badge.badge-dark.count= number_with_delimiter(AbuseReport.count(:all))
- if askimet_enabled? - if askimet_enabled?
= nav_link(controller: :spam_logs) do = nav_link(controller: :spam_logs) do
......
...@@ -26,13 +26,13 @@ ...@@ -26,13 +26,13 @@
%span %span
Issues Issues
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.badge.count= number_with_delimiter(issues.count) %span.badge.badge-dark.count= number_with_delimiter(issues.count)
= nav_link(path: 'groups#merge_requests') do = nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
%span %span
Merge Requests Merge Requests
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
%span.badge.count= number_with_delimiter(merge_requests.count) %span.badge.badge-dark.count= number_with_delimiter(merge_requests.count)
= nav_link(controller: [:group_members]) do = nav_link(controller: [:group_members]) do
= link_to group_group_members_path(@group), title: 'Members' do = link_to group_group_members_path(@group), title: 'Members' do
%span %span
......
...@@ -61,14 +61,14 @@ ...@@ -61,14 +61,14 @@
%span %span
Issues Issues
- if @project.default_issues_tracker? - if @project.default_issues_tracker?
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count) %span.badge.badge-dark.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do = nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%span %span
Merge Requests Merge Requests
%span.badge.count.merge_counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count) %span.badge.badge-dark.count.merge_counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :wiki - if project_nav_tab? :wiki
= nav_link(controller: :wikis) do = nav_link(controller: :wikis) do
......
- ref = local_assigns.fetch(:ref) - ref = local_assigns.fetch(:ref)
- status = commit.status(ref) - status = commit.status(ref)
- if status - if status
= link_to builds_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{status}" do = link_to pipelines_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{status}" do
= ci_icon_for_status(status) = ci_icon_for_status(status)
= ci_label_for_status(status) = ci_label_for_status(status)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= link_to pipeline_path(@build.pipeline) do = link_to pipeline_path(@build.pipeline) do
%strong ##{@build.pipeline.id} %strong ##{@build.pipeline.id}
for commit for commit
= link_to ci_status_path(@build.pipeline) do = link_to namespace_project_commit_path(@project.namespace, @project, @build.pipeline.sha) do
%strong= @build.pipeline.short_sha %strong= @build.pipeline.short_sha
from from
= link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do = link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
......
...@@ -56,17 +56,22 @@ ...@@ -56,17 +56,22 @@
- else - else
#js-build-scroll.scroll-controls #js-build-scroll.scroll-controls
.scroll-step .scroll-step
= link_to '#build-trace', class: 'btn' do %a{ href: '#up-build-trace', id: 'scroll-top', class: 'scroll-link scroll-top', title: 'Scroll to top' }
%i.fa.fa-angle-up = custom_icon('scroll_up')
= link_to '#down-build-trace', class: 'btn' do = custom_icon('scroll_up_hover_active')
%i.fa.fa-angle-down %a{ href: '#down-build-trace', id: 'scroll-bottom', class: 'scroll-link scroll-bottom', title: 'Scroll to bottom' }
= custom_icon('scroll_down')
= custom_icon('scroll_down_hover_active')
- if @build.active? - if @build.active?
.autoscroll-container .autoscroll-container
%button.btn.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} %span.status-message#autoscroll-status{ data: { state: 'disabled' } }
Enable autoscroll %span.status-text Autoscroll active
%i.status-icon
= custom_icon('scroll_down_hover_active')
#up-build-trace
%pre.build-trace#build-trace %pre.build-trace#build-trace
%code.bash.js-build-output %code.bash.js-build-output
= icon("refresh spin", class: "js-build-refresh") .build-loader-animation.js-build-refresh
#down-build-trace #down-build-trace
......
- @ci_pipelines.each do |pipeline|
= render "pipeline", pipeline: pipeline, pipeline_details: true
...@@ -8,7 +8,3 @@ ...@@ -8,7 +8,3 @@
= link_to pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id) do = link_to pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Pipelines Pipelines
%span.badge= @ci_pipelines.count %span.badge= @ci_pipelines.count
= nav_link(path: 'commit#builds') do
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Builds
%span.badge= @statuses.count
- @no_container = true
- page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "projects/commits/head"
%div{ class: container_class }
= render "commit_box"
= render "ci_menu"
= render "builds"
%p
You aren’t a member of any team on the Mattermost instance at
%strong= Gitlab.config.mattermost.host
%p
To install this service,
= link_to "#{Gitlab.config.mattermost.host}/select_team", target: '__blank' do
join a team
= icon('external-link')
and try again.
%hr
.clearfix
= link_to 'Go back', edit_namespace_project_service_path(@project.namespace, @project, @service), class: 'btn btn-lg pull-right'
%p
This service will be installed on the Mattermost instance at
%strong= link_to Gitlab.config.mattermost.host, Gitlab.config.mattermost.host
%hr
= form_for(:mattermost, method: :post, url: namespace_project_mattermost_path(@project.namespace, @project)) do |f|
%h4 Team
%p
= @teams.one? ? 'The team' : 'Select the team'
where the slash commands will be used in
- selected_id = @teams.keys.first if @teams.one?
= f.select(:team_id, mattermost_teams_options(@teams), {}, { class: 'form-control', selected: "#{selected_id}", disabled: @teams.one? })
.help-block
- if @teams.one?
This is the only team where you are an administrator.
- else
The list shows teams where you are administrator
To create a team, ask your Mattermost system administrator.
To create a team,
= link_to "#{Gitlab.config.mattermost.host}/create_team" do
use Mattermost's interface
= icon('external-link')
%hr
%h4 Command trigger word
%p Choose the word that will trigger commands
= f.text_field(:trigger, value: @project.path, class: 'form-control')
.help-block
%p
Trigger word must be unique, and can't begin with a slash or contain any spaces.
Use the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.path_with_namespace
%p
Reserved:
= link_to 'https://docs.mattermost.com/help/messaging/executing-commands.html#built-in-commands', target: '__blank' do
see list of built-in slash commands
= icon('external-link')
%hr
.clearfix
.pull-right
= link_to 'Cancel', edit_namespace_project_service_path(@project.namespace, @project, @service), class: 'btn btn-lg'
= f.submit 'Install', class: 'btn btn-save btn-lg'
.service-installation
.inline.pull-right
= custom_icon('mattermost_logo', size: 48)
%h3 Install Mattermost Command
- if @teams.empty?
= render 'no_teams'
- else
= render 'team_selection'
...@@ -34,11 +34,6 @@ ...@@ -34,11 +34,6 @@
= link_to url_for(params), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tab'} do = link_to url_for(params), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tab'} do
Pipelines Pipelines
%span.badge= @pipelines.size %span.badge= @pipelines.size
- if @pipeline.present?
%li.builds-tab
= link_to url_for(params), data: {target: 'div#builds', action: 'builds', toggle: 'tab'} do
Builds
%span.badge= @statuses_count
%li.diffs-tab %li.diffs-tab
= link_to url_for(params.merge(action: 'new_diffs')), data: {target: 'div#diffs', action: 'new/diffs', toggle: 'tab'} do = link_to url_for(params.merge(action: 'new_diffs')), data: {target: 'div#diffs', action: 'new/diffs', toggle: 'tab'} do
Changes Changes
...@@ -49,9 +44,6 @@ ...@@ -49,9 +44,6 @@
= render "projects/merge_requests/show/commits" = render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane #diffs.diffs.tab-pane
- # This tab is always loaded via AJAX - # This tab is always loaded via AJAX
- if @pipeline.present?
#builds.builds.tab-pane
= render "projects/merge_requests/show/builds"
- if @pipelines.any? - if @pipelines.any?
#pipelines.pipelines.tab-pane #pipelines.pipelines.tab-pane
= render "projects/merge_requests/show/pipelines" = render "projects/merge_requests/show/pipelines"
...@@ -66,6 +58,5 @@ ...@@ -66,6 +58,5 @@
}); });
:javascript :javascript
var merge_request = new MergeRequest({ var merge_request = new MergeRequest({
action: "#{(@show_changes_tab ? 'new/diffs' : 'new')}", action: "#{(@show_changes_tab ? 'new/diffs' : 'new')}"
buildsLoaded: "#{@pipeline.present? ? 'true' : 'false'}"
}); });
...@@ -65,11 +65,6 @@ ...@@ -65,11 +65,6 @@
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines Pipelines
%span.badge= @pipelines.size %span.badge= @pipelines.size
- if @pipeline.present?
%li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do
Builds
%span.badge= @statuses_count
%li.diffs-tab %li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes Changes
...@@ -98,8 +93,6 @@ ...@@ -98,8 +93,6 @@
#commits.commits.tab-pane #commits.commits.tab-pane
- # This tab is always loaded via AJAX - # This tab is always loaded via AJAX
#builds.builds.tab-pane
- # This tab is always loaded via AJAX
#pipelines.pipelines.tab-pane #pipelines.pipelines.tab-pane
- # This tab is always loaded via AJAX - # This tab is always loaded via AJAX
#diffs.diffs.tab-pane #diffs.diffs.tab-pane
......
= render "projects/commit/pipeline", pipeline: @pipeline, link_to_commit: true
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
- # TODO, remove in later versions when services like Jenkins will set CI status via Commit status API - # TODO, remove in later versions when services like Jenkins will set CI status via Commit status API
.mr-widget-heading .mr-widget-heading
- %w[success skipped canceled failed running pending].each do |status| - %w[success skipped canceled failed running pending].each do |status|
.ci_widget{class: "ci-#{status}", style: "display:none"} .ci_widget{class: "ci-#{status} ci-status-icon-#{status}", style: "display:none"}
= ci_icon_for_status(status) = ci_icon_for_status(status)
%span %span
CI build CI build
......
...@@ -24,12 +24,10 @@ ...@@ -24,12 +24,10 @@
preparing: "{{status}} build", preparing: "{{status}} build",
normal: "Build {{status}}" normal: "Build {{status}}"
}, },
builds_path: "#{builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
pipelines_path: "#{pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}" pipelines_path: "#{pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}"
}; };
if (typeof merge_request_widget !== 'undefined') { if (typeof merge_request_widget !== 'undefined') {
clearInterval(merge_request_widget.fetchBuildStatusInterval);
merge_request_widget.cancelPolling(); merge_request_widget.cancelPolling();
merge_request_widget.clearEventListeners(); merge_request_widget.clearEventListeners();
} }
......
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('merge_request_widget/ci_bundle.js')
- status_class = @pipeline ? " ci-#{@pipeline.status}" : nil - status_class = @pipeline ? " ci-#{@pipeline.status}" : nil
= form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-quick-submit js-requires-input' } do |f| = form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-quick-submit js-requires-input' } do |f|
...@@ -47,21 +50,3 @@ ...@@ -47,21 +50,3 @@
rows: 14, hint: true rows: 14, hint: true
= hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off" = hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
:javascript
$('.accept-mr-form').on('ajax:send', function() {
$(".accept-mr-form :input").disable();
});
$('.accept_merge_request').on('click', function() {
$('.js-merge-button').html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
});
$('.merge_when_build_succeeds').on('click', function() {
$("#merge_when_build_succeeds").val("1");
});
$('.js-merge-dropdown a').on('click', function(e) {
e.preventDefault();
$(this).closest("form").submit();
});
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('merge_request_widget/ci_bundle.js')
%strong %strong
= icon("spinner spin") = icon("spinner spin")
Checking ability to merge automatically&hellip; Checking ability to merge automatically&hellip;
:javascript
$(function() {
merge_request_widget.getMergeStatus();
});
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.timeline-content .timeline-content
.note-header .note-header
= link_to_member(note.project, note.author, avatar: false) = link_to_member(note.project, note.author, avatar: false)
.inline.note-headline-light .note-headline-light
= note.author.to_reference = note.author.to_reference
- unless note.system - unless note.system
commented commented
......
%ul %ul
- @stage.statuses.each do |status| - @stage.statuses.latest.each do |status|
%li.dropdown-build %li.dropdown-build
= render 'ci/status/graph_badge', subject: status = render 'ci/status/graph_badge', subject: status
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
.col-lg-9 .col-lg-9
= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form|
= render 'shared/service_settings', form: form, subject: @service = render 'shared/service_settings', form: form, subject: @service
.footer-block.row-content-block .footer-block.row-content-block
= form.submit 'Save changes', class: 'btn btn-save' = form.submit 'Save changes', class: 'btn btn-save'
&nbsp; &nbsp;
......
- run_actions_text = "Perform common operations on this project: #{@project.name_with_namespace}"
To setup this service:
%ul.list-unstyled
%li
1.
= link_to 'Enable custom slash commands', 'https://docs.mattermost.com/developer/slash-commands.html#enabling-custom-commands'
on your Mattermost installation
%li
2.
= link_to 'Add a slash command', 'https://docs.mattermost.com/developer/slash-commands.html#set-up-a-custom-command'
in Mattermost with these options:
%hr
.help-form
.form-group
= label_tag :display_name, 'Display name', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#display_name')
.form-group
= label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#description')
.form-group
= label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block
%p Fill in the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.path_with_namespace
.form-group
= label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#request_url')
.form-group
= label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block POST
.form-group
= label_tag :response_username, 'Response username', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#response_username')
.form-group
= label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#response_icon')
.form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block Yes
.form-group
= label_tag :autocomplete_hint, 'Autocomplete hint', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_hint')
.form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_description')
%hr
%ul.list-unstyled
%li
3. After adding the slash command, paste the
%strong token
into the field below
- run_actions_text = "Perform common operations on this project: #{@project.name_with_namespace}" - enabled = Gitlab.config.mattermost.enabled
.well .well
This service allows GitLab users to perform common operations on this This service allows GitLab users to perform common operations on this
...@@ -7,93 +7,9 @@ ...@@ -7,93 +7,9 @@
See list of available commands in Mattermost after setting up this service, See list of available commands in Mattermost after setting up this service,
by entering by entering
%code /&lt;command_trigger_word&gt; help %code /&lt;command_trigger_word&gt; help
%br
%br
To setup this service:
%ul.list-unstyled
%li
1.
= link_to 'Enable custom slash commands', 'https://docs.mattermost.com/developer/slash-commands.html#enabling-custom-commands'
on your Mattermost installation
%li
2.
= link_to 'Add a slash command', 'https://docs.mattermost.com/developer/slash-commands.html#set-up-a-custom-command'
in Mattermost with these options:
%hr
.help-form
.form-group
= label_tag :display_name, 'Display name', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#display_name')
.form-group
= label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#description')
.form-group
= label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block
%p Fill in the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.path_with_namespace
.form-group
= label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#request_url')
.form-group
= label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block POST
.form-group
= label_tag :response_username, 'Response username', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#response_username')
.form-group
= label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#response_icon')
.form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block Yes
.form-group
= label_tag :autocomplete_hint, 'Autocomplete hint', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_hint')
.form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_description')
%hr - unless enabled
= render 'projects/services/mattermost_slash_commands/detailed_help', subject: @service
%ul.list-unstyled - if enabled
%li = render 'projects/services/mattermost_slash_commands/installation_info', subject: @service
3. After adding the slash command, paste the
%strong token
into the field below
.services-installation-info
- unless @service.activated?
.row
.col-sm-9.col-sm-offset-3
= link_to new_namespace_project_mattermost_path(@project.namespace, @project), class: 'btn btn-lg' do
= custom_icon('mattermost_logo', size: 15)
= 'Add to Mattermost'
...@@ -68,6 +68,10 @@ ...@@ -68,6 +68,10 @@
- if koding_enabled? && @repository.koding_yml.blank? - if koding_enabled? && @repository.koding_yml.blank?
%li.missing %li.missing
= link_to 'Set up Koding', add_koding_stack_path(@project) = link_to 'Set up Koding', add_koding_stack_path(@project)
- if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present?
%li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up autodeploy', target_branch: 'autodeploy', context: 'autodeploy') do
Set up autodeploy
- if @repository.commit - if @repository.commit
.project-last-commit{ class: container_class } .project-last-commit{ class: container_class }
......
- group_status = CommitStatus.where(id: subject).status - group_status = CommitStatus.where(id: subject).status
%button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown'} } %button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } }
%span{class: "ci-status-icon ci-status-icon-#{group_status}"} %span{class: "ci-status-icon ci-status-icon-#{group_status}"}
= ci_icon_for_status(group_status) = ci_icon_for_status(group_status)
%span.ci-status-text{ 'data-toggle' => 'tooltip', 'data-title' => "#{name} - #{group_status}" } %span.ci-status-text
= name = name
%span.dropdown-counter-badge= subject.size %span.dropdown-counter-badge= subject.size
.dropdown-menu.grouped-pipeline-dropdown .dropdown-menu.grouped-pipeline-dropdown
......
...@@ -10,10 +10,10 @@ ...@@ -10,10 +10,10 @@
.text-content .text-content
- if has_button && current_user - if has_button && current_user
%h4 %h4
The Issue Tracker is a good place to add things that need to be improved or solved in a project! The Issue Tracker is the place to add things that need to be improved or solved in a project
%p %p
An issue can be a bug, a todo or a feature request that needs to be discussed in a project. Issues can be bugs, tasks or ideas to be discussed.
Besides, issues are searchable and filterable. Also, issues are searchable and filterable.
- if project_select_button - if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue' = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue'
- else - else
......
<svg xmlns="http://www.w3.org/2000/svg" width="<%= size %>" height="<%= size %>" version="1" viewBox="0 0 501 501"><path d="M236 .7C137.7 7.5 54 68.2 18.2 158.5c-32 81-19.6 172.8 33 242.5 39.8 53 97.2 87 164.3 97 16.5 2.7 48 3.2 63.5 1.2 48.7-6.3 92.2-24.6 129-54.2 13-10.5 33-31.2 42.2-43.7 26.4-35.5 42.8-75.8 49-120.3 1.6-12.3 1.6-48.7 0-61-4-28.3-12-54.8-24.2-79.5-12.8-26-26.5-45.3-46.8-65.8C417.8 64 400.2 49 398.4 49c-.6 0-.4 10.5.3 26l1.3 26 7 8.7c19 23.7 32.8 53.5 38.2 83 2.5 14 3 43 1 55.8-4.5 27.8-15.2 54-31 76.5-8.6 12.2-28 31.6-40.2 40.2-24 17-50 27.6-80 33-10 1.8-49 1.8-59 0-43-7.7-78.8-26-107.2-54.8-29.3-29.7-46.5-64-52.4-104.4-2-14-1.5-42 1-55C90 121.4 132 72 192 49.7c8-3 18.4-5.8 29.5-8.2 1.7-.4 34.4-38 35.3-40.6.3-1-10.2-1-20.8-.4z"/><path d="M322.2 24.6c-1.3.8-8.4 9.3-16 18.7-7.4 9.5-22.4 28-33.2 41.2-51 62.2-66 81.6-70.6 91-6 12-8.4 21-9 33-1.2 19.8 5 36 19 50C222 268 230 273 243 277.2c9 3 10.4 3.2 24 3.2 13.8 0 15 0 22.6-3 23.2-9 39-28.4 45-55.7 2-8.2 2-28.7.4-79.7l-2-72c-1-36.8-1.4-41.8-3-44-2-3-4.8-3.6-7.8-1.4z"/></svg>
<svg width="16" height="33" class="gitlab-icon-scroll-down" viewBox="0 0 16 33" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M1.385 5.534v12.47a4.145 4.145 0 0 0 4.144 4.15h4.942a4.151 4.151 0 0 0 4.144-4.15V5.535a4.145 4.145 0 0 0-4.144-4.15H5.53a4.151 4.151 0 0 0-4.144 4.15zM8.88 30.27v-4.351a.688.688 0 0 0-.69-.688.687.687 0 0 0-.69.688v4.334l-1.345-1.346a.69.69 0 0 0-.976.976l2.526 2.526a.685.685 0 0 0 .494.2.685.685 0 0 0 .493-.2l2.526-2.526a.69.69 0 1 0-.976-.976L8.88 30.27zM0 5.534A5.536 5.536 0 0 1 5.529 0h4.942A5.53 5.53 0 0 1 16 5.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 18.005V5.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0V6.544z" fill-rule="evenodd"/>
</svg>
<svg width="16" height="33" class="gitlab-icon-scroll-down-hover" viewBox="0 0 16 33" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M8.88 30.27v-4.351a.688.688 0 0 0-.69-.688.687.687 0 0 0-.69.688v4.334l-1.345-1.346a.69.69 0 0 0-.976.976l2.526 2.526a.685.685 0 0 0 .494.2.685.685 0 0 0 .493-.2l2.526-2.526a.69.69 0 1 0-.976-.976L8.88 30.27zM0 5.534A5.536 5.536 0 0 1 5.529 0h4.942A5.53 5.53 0 0 1 16 5.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 18.005V5.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0V6.544z" fill-rule="evenodd"/>
</svg>
<svg width="16" height="33" class="gitlab-icon-scroll-up" viewBox="0 0 16 33" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M1.385 14.534v12.47a4.145 4.145 0 0 0 4.144 4.15h4.942a4.151 4.151 0 0 0 4.144-4.15v-12.47a4.145 4.145 0 0 0-4.144-4.15H5.53a4.151 4.151 0 0 0-4.144 4.15zM8.88 2.609V6.96a.688.688 0 0 1-.69.688.687.687 0 0 1-.69-.688V2.627L6.155 3.972a.69.69 0 0 1-.976-.976L7.705.47a.685.685 0 0 1 .494-.2.685.685 0 0 1 .493.2l2.526 2.526a.69.69 0 1 1-.976.976L8.88 2.609zM0 14.534A5.536 5.536 0 0 1 5.529 9h4.942A5.53 5.53 0 0 1 16 14.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 27.005V14.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0v-2.143z" fill-rule="evenodd"/>
</svg>
<svg width="16" height="33" class="gitlab-icon-scroll-up-hover" viewBox="0 0 16 33" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M8.88 2.646l1.362 1.362a.69.69 0 0 0 .976-.976L8.692.507A.685.685 0 0 0 8.2.306a.685.685 0 0 0-.494.2L5.179 3.033a.69.69 0 1 0 .976.976L7.5 2.663v4.179c0 .38.306.688.69.688.381 0 .69-.306.69-.688V2.646zM0 14.534A5.536 5.536 0 0 1 5.529 9h4.942A5.53 5.53 0 0 1 16 14.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 27.005V14.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0v-2.143z" fill-rule="evenodd"/>
</svg>
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- show_menu_above = show_menu_above || false - show_menu_above = show_menu_above || false
- selected_text = selected.try(:title) || params[:milestone_title] - selected_text = selected.try(:title) || params[:milestone_title]
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone") - dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
- if selected.present? - if selected.present? || params[:milestone_title].present?
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id) = hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", = dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected.try(:title), project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected.try(:title), project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
......
---
title: Fix wrong tab selected when loggin fails and multiple login tabs exists
merge_request: 7314
author: Jacopo Beschi @jacopo-beschi
---
title: Made comment autocomplete more performant and removed some loading bugs
merge_request: 6856
author:
---
title: Update wiki page design
merge_request: 7429
author:
---
title: group authors in contribution graph with case insensitive email handle comparison
merge_request: 8021
author:
---
title: Adds Direct link from pipeline list to builds
merge_request: 8097
author:
---
title: Prevent overflow with vertical scroll when we have space to show content
merge_request: 8061
author:
---
title: Add scopes for personal access tokens and OAuth tokens
merge_request: 5951
author:
---
title: New Gitea importer
merge_request: 8116
author:
---
title: 'Make API::Helpers find a project with only one query'
merge_request: 7714
author:
---
title: Resolve "Manual actions on pipeline graph"
merge_request: 7931
author:
---
title: Prevent DOM ID collisions resulting from user-generated content anchors
merge_request: 7631
author:
---
title: Introduce $CI_BUILD_REF_SLUG
merge_request: 8072
author:
---
title: Add a slug to environments
merge_request: 7983
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.
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