Commit becf6e4c authored by Valery Sizov's avatar Valery Sizov

Merge branch 'rc/ce-to-ee-monday' into 'master'

CE Upstream - Monday

Closes gitlab-com/organization#22 and gitlab-ce#28204

See merge request !1265
parents 5c572335 e1f0f5ba
......@@ -222,7 +222,7 @@ rake db:seed_fu:
paths:
- log/development.log
karma:
rake karma:
cache:
paths:
- vendor/ruby
......@@ -369,7 +369,7 @@ pages:
<<: *dedicated-runner
dependencies:
- coverage
- karma
- rake karma
- lint:javascript:report
script:
- mv public/ .public/
......
......@@ -6,6 +6,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Move RepositoryUpdateRemoteMirrorWorker jobs to project_mirror Sidekiq queue. !1234
- Fixed merge request environment link not displaying.
- Reduce queries needed to check if node is a primary or secondary Geo node.
- Read true-up info from license and validate it. !1159
## 8.16.5 (2017-02-14)
......
......@@ -5,7 +5,6 @@ entry.
## 8.16.6 (2017-02-17)
- API: Fix file downloading. !0 (8267)
- Read true-up info from license and validate it. !1159
- Reduce hits to LDAP on Git HTTP auth by reordering auth mechanisms. !8752
- Fix filtered search user autocomplete for gitlab instances that are hosted on a subdirectory. !8891
- Fix wrong call to ProjectCacheWorker.perform. !8910
......
......@@ -35,7 +35,7 @@ gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0'
gem 'gssapi', group: :kerberos
gem 'omniauth-authentiq', '~> 0.2.0'
gem 'omniauth-authentiq', '~> 0.3.0'
gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt', '~> 1.5.6'
......
......@@ -472,7 +472,7 @@ GEM
rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1)
omniauth-oauth2 (~> 1.1)
omniauth-authentiq (0.2.2)
omniauth-authentiq (0.3.0)
omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0)
......@@ -955,7 +955,7 @@ DEPENDENCIES
oj (~> 2.17.4)
omniauth (~> 1.3.2)
omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.2.0)
omniauth-authentiq (~> 0.3.0)
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 4.0.0)
......
......@@ -97,7 +97,7 @@ $(() => {
}
this.isLoadingStage = true;
cycleAnalyticsStore.setStageEvents([]);
cycleAnalyticsStore.setStageEvents([], stage);
cycleAnalyticsStore.setActiveStage(stage);
cycleAnalyticsService
......@@ -107,7 +107,7 @@ $(() => {
})
.done((response) => {
this.isEmptyStage = !response.events.length;
cycleAnalyticsStore.setStageEvents(response.events);
cycleAnalyticsStore.setStageEvents(response.events, stage);
})
.error(() => {
this.isEmptyStage = true;
......
/* eslint-disable no-param-reassign */
require('../lib/utils/text_utility');
const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......@@ -34,11 +38,12 @@
});
newData.stages.forEach((item) => {
const stageName = item.title.toLowerCase();
const stageSlug = gl.text.dasherize(item.title.toLowerCase());
item.active = false;
item.isUserAllowed = data.permissions[stageName];
item.emptyStageText = EMPTY_STAGE_TEXTS[stageName];
item.component = `stage-${stageName}-component`;
item.isUserAllowed = data.permissions[stageSlug];
item.emptyStageText = EMPTY_STAGE_TEXTS[stageSlug];
item.component = `stage-${stageSlug}-component`;
item.slug = stageSlug;
});
newData.analytics = data;
return newData;
......@@ -58,31 +63,33 @@
this.deactivateAllStages();
stage.active = true;
},
setStageEvents(events) {
this.state.events = this.decorateEvents(events);
setStageEvents(events, stage) {
this.state.events = this.decorateEvents(events, stage);
},
decorateEvents(events) {
decorateEvents(events, stage) {
const newEvents = [];
events.forEach((item) => {
if (!item) return;
item.totalTime = item.total_time;
item.author.webUrl = item.author.web_url;
item.author.avatarUrl = item.author.avatar_url;
const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
eventItem.totalTime = eventItem.total_time;
eventItem.author.webUrl = eventItem.author.web_url;
eventItem.author.avatarUrl = eventItem.author.avatar_url;
if (item.created_at) item.createdAt = item.created_at;
if (item.short_sha) item.shortSha = item.short_sha;
if (item.commit_url) item.commitUrl = item.commit_url;
if (eventItem.created_at) eventItem.createdAt = eventItem.created_at;
if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha;
if (eventItem.commit_url) eventItem.commitUrl = eventItem.commit_url;
delete item.author.web_url;
delete item.author.avatar_url;
delete item.total_time;
delete item.created_at;
delete item.short_sha;
delete item.commit_url;
delete eventItem.author.web_url;
delete eventItem.author.avatar_url;
delete eventItem.total_time;
delete eventItem.created_at;
delete eventItem.short_sha;
delete eventItem.commit_url;
newEvents.push(item);
newEvents.push(eventItem);
});
return newEvents;
......
module.exports = {
issue: {
created_at: '',
url: '',
iid: '',
title: '',
total_time: {},
author: {
avatar_url: '',
id: '',
name: '',
web_url: '',
},
},
plan: {
title: '',
commit_url: '',
short_sha: '',
total_time: {},
author: {
name: '',
id: '',
avatar_url: '',
web_url: '',
},
},
code: {
title: '',
iid: '',
created_at: '',
url: '',
total_time: {},
author: {
name: '',
id: '',
avatar_url: '',
web_url: '',
},
},
test: {
name: '',
id: '',
date: '',
url: '',
short_sha: '',
commit_url: '',
total_time: {},
branch: {
name: '',
url: '',
},
},
review: {
title: '',
iid: '',
created_at: '',
url: '',
state: '',
total_time: {},
author: {
name: '',
id: '',
avatar_url: '',
web_url: '',
},
},
staging: {
id: '',
short_sha: '',
date: '',
url: '',
commit_url: '',
total_time: {},
author: {
name: '',
id: '',
avatar_url: '',
web_url: '',
},
branch: {
name: '',
url: '',
},
},
production: {
title: '',
created_at: '',
url: '',
iid: '',
total_time: {},
author: {
name: '',
id: '',
avatar_url: '',
web_url: '',
},
},
};
......@@ -173,7 +173,7 @@
tokens.forEach((token) => {
const condition = gl.FilteredSearchTokenKeys
.searchByConditionKeyValue(token.key, token.value.toLowerCase());
const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key);
const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key) || {};
const keyParam = param ? `${token.key}_${param}` : token.key;
let tokenPath = '';
......
require('./filtered_search_token_keys');
(() => {
class FilteredSearchTokenizer {
static processTokens(input) {
const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
// Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single)
const tokenRegex = /(\w+):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\S+))/g;
const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokens = [];
let lastToken = null;
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
......
......@@ -83,12 +83,12 @@
_a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF");
regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])(([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi');
regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?!" + atSymbolsWithBar + ")((?:[A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi');
match = regexp.exec(subtext);
if (match) {
return (match[1] || match[1] === "") ? match[1] : match[2];
return match[1];
} else {
return null;
}
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len */
require('vendor/latinise');
(function() {
(function(w) {
var base;
......@@ -164,8 +166,14 @@
gl.text.pluralize = function(str, count) {
return str + (count > 1 || count === 0 ? 's' : '');
};
return gl.text.truncate = function(string, maxLength) {
gl.text.truncate = function(string, maxLength) {
return string.substr(0, (maxLength - 3)) + '...';
};
gl.text.dasherize = function(str) {
return str.replace(/[_\s]+/g, '-');
};
gl.text.slugify = function(str) {
return str.trim().toLowerCase().latinise();
};
})(window);
}).call(window);
......@@ -269,7 +269,6 @@ require('./smart_interval');
$('.ci_widget.ci-error').show();
this.setMergeButtonClass('btn-danger');
}
this.initMiniPipelineGraph();
};
MergeRequestWidget.prototype.showCICoverage = function(coverage) {
......
......@@ -28,7 +28,7 @@
* All dropdown events are fired at the .dropdown-menu's parent element.
*/
bindEvents() {
$(document).on('shown.bs.dropdown', this.container, this.getBuildsList);
$(document).off('shown.bs.dropdown', this.container).on('shown.bs.dropdown', this.container, this.getBuildsList);
}
/**
......
/* eslint-disable class-methods-use-this, no-new, func-names, prefer-template, no-unneeded-ternary, object-shorthand, space-before-function-paren, comma-dangle, quote-props, consistent-return, no-else-return, no-param-reassign, max-len */
/* eslint-disable class-methods-use-this, no-new, func-names, no-unneeded-ternary, object-shorthand, quote-props, no-param-reassign, max-len */
/* global UsersSelect */
((global) => {
class Todos {
constructor({ el } = {}) {
this.allDoneClicked = this.allDoneClicked.bind(this);
this.doneClicked = this.doneClicked.bind(this);
this.el = el || $('.js-todos-options');
this.perPage = this.el.data('perPage');
this.clearListeners();
this.initBtnListeners();
constructor() {
this.initFilters();
this.bindEvents();
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('beforeunload', this.cleanupWrapper);
}
clearListeners() {
$('.done-todo').off('click');
$('.js-todos-mark-all').off('click');
return $('.todo').off('click');
cleanup() {
this.unbindEvents();
document.removeEventListener('beforeunload', this.cleanupWrapper);
}
initBtnListeners() {
$('.done-todo').on('click', this.doneClicked);
$('.js-todos-mark-all').on('click', this.allDoneClicked);
return $('.todo').on('click', this.goToTodoUrl);
unbindEvents() {
$('.js-done-todo, .js-undo-todo').off('click', this.updateStateClickedWrapper);
$('.js-todos-mark-all').off('click', this.allDoneClickedWrapper);
$('.todo').off('click', this.goToTodoUrl);
}
bindEvents() {
this.updateStateClickedWrapper = this.updateStateClicked.bind(this);
this.allDoneClickedWrapper = this.allDoneClicked.bind(this);
$('.js-done-todo, .js-undo-todo').on('click', this.updateStateClickedWrapper);
$('.js-todos-mark-all').on('click', this.allDoneClickedWrapper);
$('.todo').on('click', this.goToTodoUrl);
}
initFilters() {
......@@ -33,7 +39,7 @@
$('form.filter-form').on('submit', function (event) {
event.preventDefault();
gl.utils.visitUrl(this.action + '&' + $(this).serialize());
gl.utils.visitUrl(`${this.action}&${$(this).serialize()}`);
});
}
......@@ -44,105 +50,72 @@
filterable: searchFields ? true : false,
search: { fields: searchFields },
data: $dropdown.data('data'),
clicked: function() {
clicked: function () {
return $dropdown.closest('form.filter-form').submit();
}
},
});
}
doneClicked(e) {
updateStateClicked(e) {
e.preventDefault();
e.stopImmediatePropagation();
const $target = $(e.currentTarget);
$target.disable();
return $.ajax({
const target = e.target;
target.setAttribute('disabled', '');
target.classList.add('disabled');
$.ajax({
type: 'POST',
url: $target.attr('href'),
url: target.getAttribute('href'),
dataType: 'json',
data: {
'_method': 'delete'
'_method': target.getAttribute('data-method'),
},
success: (data) => {
this.redirectIfNeeded(data.count);
this.clearDone($target.closest('li'));
return this.updateBadges(data);
}
this.updateState(target);
this.updateBadges(data);
},
});
}
allDoneClicked(e) {
e.preventDefault();
e.stopImmediatePropagation();
const $target = $(e.currentTarget);
$target.disable();
return $.ajax({
$.ajax({
type: 'POST',
url: $target.attr('href'),
dataType: 'json',
data: {
'_method': 'delete'
'_method': 'delete',
},
success: (data) => {
$target.remove();
$('.js-todos-all').html('<div class="nothing-here-block">You\'re all done!</div>');
return this.updateBadges(data);
}
this.updateBadges(data);
},
});
}
clearDone($row) {
const $ul = $row.closest('ul');
$row.remove();
if (!$ul.find('li').length) {
return $ul.parents('.panel').remove();
updateState(target) {
const row = target.closest('li');
const restoreBtn = row.querySelector('.js-undo-todo');
const doneBtn = row.querySelector('.js-done-todo');
target.removeAttribute('disabled');
target.classList.remove('disabled');
target.classList.add('hidden');
if (target === doneBtn) {
row.classList.add('done-reversible');
restoreBtn.classList.remove('hidden');
} else {
row.classList.remove('done-reversible');
doneBtn.classList.remove('hidden');
}
}
updateBadges(data) {
$(document).trigger('todo:toggle', data.count);
$('.todos-pending .badge').text(data.count);
return $('.todos-done .badge').text(data.done_count);
}
getTotalPages() {
return this.el.data('totalPages');
}
getCurrentPage() {
return this.el.data('currentPage');
}
getTodosPerPage() {
return this.el.data('perPage');
}
redirectIfNeeded(total) {
const currPages = this.getTotalPages();
const currPage = this.getCurrentPage();
// Refresh if no remaining Todos
if (!total) {
window.location.reload();
return;
}
// Do nothing if no pagination
if (!currPages) {
return;
}
const newPages = Math.ceil(total / this.getTodosPerPage());
let url = location.href;
if (newPages !== currPages) {
// Redirect to previous page if there's one available
if (currPages > 1 && currPage === currPages) {
const pageParams = {
page: currPages - 1
};
url = gl.utils.mergeUrlParams(pageParams, url);
}
return gl.utils.visitUrl(url);
}
$('.todos-done .badge').text(data.done_count);
}
goToTodoUrl(e) {
......@@ -159,12 +132,12 @@
if (selected.tagName === 'IMG') {
const avatarUrl = selected.parentElement.getAttribute('href');
return window.open(avatarUrl, windowTarget);
window.open(avatarUrl, windowTarget);
} else {
return window.open(todoLink, windowTarget);
window.open(todoLink, windowTarget);
}
} else {
return gl.utils.visitUrl(todoLink);
gl.utils.visitUrl(todoLink);
}
}
}
......
/* eslint-disable no-param-reassign */
/* global Breakpoints */
require('vendor/latinise');
require('./breakpoints');
require('vendor/jquery.nicescroll');
((global) => {
const dasherize = str => str.replace(/[_\s]+/g, '-');
const slugify = str => dasherize(str.trim().toLowerCase().latinise());
class Wikis {
constructor() {
this.bp = Breakpoints.get();
......@@ -34,7 +30,7 @@ require('vendor/jquery.nicescroll');
if (!this.newWikiForm) return;
const slugInput = this.newWikiForm.querySelector('#new_wiki_path');
const slug = slugify(slugInput.value);
const slug = gl.text.slugify(slugInput.value);
if (slug.length > 0) {
const wikisPath = slugInput.getAttribute('data-wikis-path');
......
......@@ -111,15 +111,9 @@ header {
}
li {
.active a {
&.active a {
font-weight: bold;
}
&:hover {
.badge {
background-color: $white-light;
}
}
}
}
......@@ -132,7 +126,11 @@ header {
&:hover {
background-color: $white-normal;
color: $gl-header-nav-hover-color;
}
&:focus {
outline: none;
background-color: $white-normal;
}
}
......@@ -150,16 +148,11 @@ header {
}
.header-logo {
position: absolute;
left: 50%;
display: inline-block;
margin: 0 8px 0 3px;
position: relative;
top: 7px;
transition-duration: .3s;
z-index: 999;
#logo {
position: relative;
left: -50%;
}
svg,
img {
......@@ -169,15 +162,6 @@ header {
&:hover {
cursor: pointer;
}
@media (max-width: $screen-xs-max) {
right: 20px;
left: auto;
#logo {
left: auto;
}
}
}
.title {
......@@ -185,7 +169,7 @@ header {
padding-right: 20px;
margin: 0;
font-size: 18px;
max-width: 385px;
max-width: 450px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
......@@ -195,10 +179,6 @@ header {
vertical-align: top;
white-space: nowrap;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
max-width: 300px;
}
@media (max-width: $screen-xs-max) {
max-width: 190px;
}
......
@mixin fade($gradient-direction, $gradient-color) {
visibility: hidden;
opacity: 0;
z-index: 2;
z-index: 1;
position: absolute;
bottom: 12px;
width: 43px;
......@@ -18,7 +18,7 @@
.fa {
position: relative;
top: 5px;
top: 6px;
font-size: 18px;
}
}
......@@ -79,7 +79,6 @@
}
&.sub-nav {
text-align: center;
background-color: $gray-normal;
.container-fluid {
......@@ -287,7 +286,6 @@
background: $gray-light;
border-bottom: 1px solid $border-color;
transition: padding $sidebar-transition-duration;
text-align: center;
.container-fluid {
position: relative;
......@@ -353,7 +351,7 @@
right: -5px;
.fa {
right: -7px;
right: -28px;
}
}
......@@ -383,7 +381,7 @@
left: 0;
.fa {
left: 10px;
left: -4px;
}
}
}
......
......@@ -30,6 +30,26 @@
word-wrap: break-word;
}
}
.panel-heading {
line-height: $line-height-base;
padding: 14px 16px;
display: -webkit-flex;
display: flex;
.title {
-webkit-flex: 1;
-webkit-flex-grow: 1;
flex: 1;
flex-grow: 2;
}
.counter {
-webkit-flex: 1;
flex: 0;
padding-left: 16px;
}
}
}
.milestone-summary {
......
......@@ -222,6 +222,11 @@
}
}
.dropdown-menu {
max-height: 250px;
overflow-y: auto;
}
.dropdown-toggle,
.dropdown-menu {
color: $gl-text-color-secondary;
......
......@@ -268,6 +268,13 @@
}
}
.project-repo-buttons {
.project-action-button .dropdown-menu {
max-height: 250px;
overflow-y: auto;
}
}
.split-one {
display: inline-table;
margin-right: 12px;
......@@ -645,29 +652,23 @@ pre.light-well {
}
}
.project-last-commit {
@media (min-width: $screen-sm-min) {
margin-top: $gl-padding;
.container-fluid.project-stats-container {
@media (max-width: $screen-xs-max) {
padding: 12px 0;
}
}
&.container-fluid {
padding-top: 12px;
padding-bottom: 12px;
background-color: $gray-light;
border: 1px solid $border-color;
border-right-width: 0;
border-left-width: 0;
.project-last-commit {
background-color: $gray-light;
padding: 12px $gl-padding;
border: 1px solid $border-color;
@media (min-width: $screen-sm-min) {
border-right-width: 1px;
border-left-width: 1px;
}
@media (min-width: $screen-sm-min) {
margin-top: $gl-padding;
}
&.container-limited {
@media (min-width: 1281px) {
border-radius: $border-radius-base;
}
@media (min-width: $screen-sm-min) {
border-radius: $border-radius-base;
}
.ci-status {
......
......@@ -60,6 +60,18 @@
}
}
.todos-list > .todo.todo-pending.done-reversible {
background-color: $gray-light;
&:hover {
border-color: $border-color;
}
.title {
font-weight: normal;
}
}
.todo-item {
.todo-title {
@include str-truncated(calc(100% - 174px));
......
......@@ -2,5 +2,6 @@ class Admin::BackgroundJobsController < Admin::ApplicationController
def show
ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Gitlab.config.gitlab.user} -o pid,pcpu,pmem,stat,start,command))
@sidekiq_processes = ps_output.split("\n").grep(/sidekiq/)
@concurrency = Sidekiq.options[:concurrency]
end
end
......@@ -21,6 +21,7 @@ class Admin::SystemInfoController < Admin::ApplicationController
'mqueue',
'proc',
'pstore',
'rpc_pipefs',
'securityfs',
'sysfs',
'tmpfs',
......
......@@ -104,23 +104,15 @@ module CreatesCommit
if can?(current_user, :push_code, @project)
# Edit file in this project
@mr_source_project = @project
if @project.forked?
# Merge request from this project to fork origin
@mr_target_project = @project.forked_from_project
@mr_target_branch = @mr_target_project.repository.root_ref
else
# Merge request to this project
@mr_target_project = @project
@mr_target_branch = @ref || @target_branch
end
else
# Merge request from fork to this project
@mr_source_project = current_user.fork_of(@project)
@mr_target_project = @project
@mr_target_branch = @ref || @target_branch
end
# Merge request to this project
@mr_target_project = @project
@mr_target_branch = @ref || @target_branch
@mr_source_branch = guess_mr_source_branch
end
......
module SnippetsActions
extend ActiveSupport::Concern
def edit
end
def raw
send_data(
convert_line_endings(@snippet.content),
type: 'text/plain; charset=utf-8',
disposition: 'inline',
filename: @snippet.sanitized_file_name
)
end
private
def convert_line_endings(content)
params[:line_ending] == 'raw' ? content : content.gsub(/\r\n/, "\n")
end
end
......@@ -29,6 +29,12 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
end
def restore
TodoService.new.mark_todos_as_pending_by_ids([params[:id]], current_user)
render json: todos_counts
end
private
def find_todos
......
......@@ -88,6 +88,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
handle_omniauth
end
def authentiq
if params['sid']
handle_service_ticket oauth['provider'], params['sid']
end
handle_omniauth
end
private
def handle_omniauth
......
class Projects::SnippetsController < Projects::ApplicationController
include ToggleAwardEmoji
include SpammableActions
include SnippetsActions
before_action :module_enabled
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
......@@ -49,9 +50,6 @@ class Projects::SnippetsController < Projects::ApplicationController
end
end
def edit
end
def update
UpdateSnippetService.new(project, current_user, @snippet,
snippet_params).execute
......@@ -74,15 +72,6 @@ class Projects::SnippetsController < Projects::ApplicationController
redirect_to namespace_project_snippets_path(@project.namespace, @project)
end
def raw
send_data(
@snippet.content,
type: 'text/plain; charset=utf-8',
disposition: 'inline',
filename: @snippet.sanitized_file_name
)
end
protected
def snippet
......
class SnippetsController < ApplicationController
include ToggleAwardEmoji
include SpammableActions
include SnippetsActions
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download]
......@@ -47,9 +48,6 @@ class SnippetsController < ApplicationController
respond_with @snippet.becomes(Snippet)
end
def edit
end
def update
UpdateSnippetService.new(nil, current_user, @snippet,
snippet_params).execute
......@@ -67,18 +65,9 @@ class SnippetsController < ApplicationController
redirect_to snippets_path
end
def raw
send_data(
@snippet.content,
type: 'text/plain; charset=utf-8',
disposition: 'inline',
filename: @snippet.sanitized_file_name
)
end
def download
send_data(
@snippet.content,
convert_line_endings(@snippet.content),
type: 'text/plain; charset=utf-8',
filename: @snippet.sanitized_file_name
)
......
......@@ -16,6 +16,7 @@
# label_name: string
# sort: string
# non_archived: boolean
# iids: integer[]
#
class IssuableFinder
NONE = '0'
......@@ -41,6 +42,7 @@ class IssuableFinder
items = by_weight(items)
items = by_due_date(items)
items = by_non_archived(items)
items = by_iids(items)
sort(items)
end
......@@ -267,16 +269,11 @@ class IssuableFinder
end
def by_search(items)
if search
items =
if search =~ iid_pattern
items.where(iid: $~[:iid])
else
items.full_search(search)
end
end
search ? items.full_search(search) : items
end
items
def by_iids(items)
params[:iids].present? ? items.where(iid: params[:iids]) : items
end
def sort(items)
......
......@@ -26,10 +26,6 @@ class IssuesFinder < IssuableFinder
IssuesFinder.not_restricted_by_confidentiality(current_user)
end
def iid_pattern
@iid_pattern ||= %r{\A#{Regexp.escape(Issue.reference_prefix)}(?<iid>\d+)\z}
end
def self.not_restricted_by_confidentiality(user)
return Issue.where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
......
......@@ -20,14 +20,4 @@ class MergeRequestsFinder < IssuableFinder
def klass
MergeRequest
end
private
def iid_pattern
@iid_pattern ||= %r{\A[
#{Regexp.escape(MergeRequest.reference_prefix)}
#{Regexp.escape(Issue.reference_prefix)}
](?<iid>\d+)\z
}x
end
end
......@@ -22,6 +22,10 @@ module Spammable
delegate :ip_address, :user_agent, to: :user_agent_detail, allow_nil: true
end
def submittable_as_spam_by?(current_user)
current_user && current_user.admin? && submittable_as_spam?
end
def submittable_as_spam?
if user_agent_detail
user_agent_detail.submittable? && current_application_settings.akismet_enabled
......
......@@ -2,6 +2,7 @@ class AnalyticsStageEntity < Grape::Entity
include EntityDateHelper
expose :title
expose :legend
expose :description
expose :median, as: :value do |stage|
......
......@@ -17,7 +17,8 @@ module Projects
def execute
return false unless can?(current_user, :remove_project, project)
project.team.truncate
repo_path = project.path_with_namespace
wiki_path = repo_path + '.wiki'
# Flush the cache for both repositories. This has to be done _before_
# removing the physical repositories as some expiration code depends on
......@@ -27,6 +28,7 @@ module Projects
Projects::UnlinkForkService.new(project, current_user).execute
Project.transaction do
project.team.truncate
project.destroy!
trash_repositories!
......
......@@ -178,16 +178,20 @@ class TodoService
# When user marks some todos as done
def mark_todos_as_done(todos, current_user)
mark_todos_as_done_by_ids(todos.select(&:id), current_user)
update_todos_state_by_ids(todos.select(&:id), current_user, :done)
end
def mark_todos_as_done_by_ids(ids, current_user)
todos = current_user.todos.where(id: ids)
update_todos_state_by_ids(ids, current_user, :done)
end
# Only return those that are not really on that state
marked_todos = todos.where.not(state: :done).update_all(state: :done)
current_user.update_todos_count_cache
marked_todos
# When user marks some todos as pending
def mark_todos_as_pending(todos, current_user)
update_todos_state_by_ids(todos.select(&:id), current_user, :pending)
end
def mark_todos_as_pending_by_ids(ids, current_user)
update_todos_state_by_ids(ids, current_user, :pending)
end
# When user marks an issue as todo
......@@ -202,6 +206,15 @@ class TodoService
private
def update_todos_state_by_ids(ids, current_user, state)
todos = current_user.todos.where(id: ids)
# Only return those that are not really on that state
marked_todos = todos.where.not(state: state).update_all(state: state)
current_user.update_todos_count_cache
marked_todos
end
def create_todos(users, attributes)
Array(users).map do |user|
next if pending_todos(user, attributes).exists?
......
......@@ -35,7 +35,7 @@
.clearfix
%p
%i.fa.fa-exclamation-circle
If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
If '[#{@concurrency} of #{@concurrency} busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
%p
%i.fa.fa-exclamation-circle
If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{gitlab_config.user} -f sidekiq) and restart GitLab.
......
......@@ -56,7 +56,7 @@
= submit_tag 'Search', class: 'btn'
.pull-right.light
Runners with last contact less than a minute ago: #{@active_runners_cnt}
Runners with last contact more than a minute ago: #{@active_runners_cnt}
%br
......
......@@ -31,6 +31,9 @@
- if todo.pending?
.todo-actions
= link_to [:dashboard, todo], method: :delete, class: 'btn btn-loading done-todo' do
= link_to [:dashboard, todo], method: :delete, class: 'btn btn-loading js-done-todo' do
Done
= icon('spinner spin')
= link_to restore_dashboard_todo_path(todo), method: :patch, class: 'btn btn-loading js-undo-todo hidden' do
Undo
= icon('spinner spin')
......@@ -3,11 +3,9 @@
.event-title
%span.author_name= link_to_author event
%span.pushed #{event.action_name} #{event.ref_type}
- if event.rm_ref?
%strong= event.ref_name
- else
%strong
= link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title)
%strong
- commits_link = namespace_project_commits_path(project.namespace, project, event.ref_name)
= link_to_if project.repository.branch_exists?(event.ref_name), event.ref_name, commits_link
= render "events/event_scope", event: event
......
.page-with-sidebar{ class: page_gutter_class }
- if defined?(nav) && nav
.layout-nav
.container-fluid
%div{ class: container_class }
= render "layouts/nav/#{nav}"
.content-wrapper{ class: "#{layout_nav_class}" }
= yield :sub_nav
......
......@@ -65,12 +65,12 @@
%div
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
%h1.title= title
.header-logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
= brand_header_logo
%h1.title= title
= yield :header_content
= render 'shared/outdated_browser'
......
......@@ -24,12 +24,12 @@
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
%span
Issues
%span.badge= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))
(#{number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))})
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
%span
Merge Requests
%span.badge= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))
(#{number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))})
= nav_link(controller: 'dashboard/snippets') do
= link_to dashboard_snippets_path, title: 'Snippets' do
%span
......
......@@ -2,13 +2,6 @@
= render 'profiles/head'
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
Application theme
%p
This setting allows you to customize the appearance of the site, e.g. the sidebar.
.col-sm-12
%hr
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
Syntax highlighting theme
......
......@@ -40,7 +40,7 @@
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
%li
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
- if @issue.submittable_as_spam? && current_user.admin?
- if @issue.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
......@@ -50,7 +50,7 @@
- if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
- if @issue.submittable_as_spam? && current_user.admin?
- if @issue.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam'
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
......
......@@ -16,69 +16,70 @@
= render "home_panel"
- if current_user && can?(current_user, :download_code, @project)
%nav.project-stats{ class: container_class }
%ul.nav
%li
= link_to project_files_path(@project) do
Files (#{storage_counter(@project.statistics.total_repository_size)})
%li
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
#{'Commit'.pluralize(@project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)})
%li
= link_to namespace_project_branches_path(@project.namespace, @project) do
#{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
%li
= link_to namespace_project_tags_path(@project.namespace, @project) do
#{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
- if default_project_view != 'readme' && @repository.readme
.project-stats-container{ class: container_class }
%nav.project-stats
%ul.nav
%li
= link_to 'Readme', readme_path(@project)
- if @repository.changelog
= link_to project_files_path(@project) do
Files (#{storage_counter(@project.statistics.total_repository_size)})
%li
= link_to 'Changelog', changelog_path(@project)
- if @repository.license_blob
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
#{'Commit'.pluralize(@project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)})
%li
= link_to license_short_name(@project), license_path(@project)
- if @repository.contribution_guide
= link_to namespace_project_branches_path(@project.namespace, @project) do
#{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
%li
= link_to 'Contribution guide', contribution_guide_path(@project)
= link_to namespace_project_tags_path(@project.namespace, @project) do
#{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
- if @repository.gitlab_ci_yml
%li
= link_to 'CI configuration', ci_configuration_path(@project)
- if default_project_view != 'readme' && @repository.readme
%li
= link_to 'Readme', readme_path(@project)
- if @repository.changelog
%li
= link_to 'Changelog', changelog_path(@project)
- if @repository.license_blob
%li
= link_to license_short_name(@project), license_path(@project)
- if @repository.contribution_guide
%li
= link_to 'Contribution guide', contribution_guide_path(@project)
- if @repository.gitlab_ci_yml
%li
= link_to 'CI configuration', ci_configuration_path(@project)
- if current_user && can_push_branch?(@project, @project.default_branch)
- unless @repository.changelog
%li.missing
= link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
Add Changelog
- unless @repository.license_blob
%li.missing
= link_to add_special_file_path(@project, file_name: 'LICENSE') do
Add License
- unless @repository.contribution_guide
%li.missing
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
Add Contribution guide
- unless @repository.gitlab_ci_yml
%li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
Set up CI
- if koding_enabled? && @repository.koding_yml.blank?
%li.missing
= 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 auto deploy', target_branch: 'auto-deploy', context: 'autodeploy') do
Set up auto deploy
- if current_user && can_push_branch?(@project, @project.default_branch)
- unless @repository.changelog
%li.missing
= link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
Add Changelog
- unless @repository.license_blob
%li.missing
= link_to add_special_file_path(@project, file_name: 'LICENSE') do
Add License
- unless @repository.contribution_guide
%li.missing
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
Add Contribution guide
- unless @repository.gitlab_ci_yml
%li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
Set up CI
- if koding_enabled? && @repository.koding_yml.blank?
%li.missing
= 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 auto deploy', target_branch: 'auto-deploy', context: 'autodeploy') do
Set up auto deploy
- if @repository.commit
.project-last-commit{ class: container_class }
= render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project
- if @repository.commit
.project-last-commit
= render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project
%div{ class: container_class }
- if @project.archived?
......
......@@ -10,7 +10,7 @@
- if can?(current_user, :create_project_snippet, @project)
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-inverted btn-create', title: "New snippet" do
New snippet
- if @snippet.submittable_as_spam? && current_user.admin?
- if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
- if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet)
.visible-xs-block.dropdown
......@@ -31,6 +31,6 @@
%li
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
Edit
- if @snippet.submittable_as_spam? && current_user.admin?
- if @snippet.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post
- label_css_id = dom_id(label)
- open_issues_count = label.open_issues_count(current_user)
- open_merge_requests_count = label.open_merge_requests_count(current_user)
- status = label_subscription_status(label, @project).inquiry if current_user
- subject = local_assigns[:subject]
......@@ -15,10 +13,10 @@
%ul
%li
= link_to_label(label, subject: subject, type: :merge_request) do
= pluralize open_merge_requests_count, 'merge request'
view merge requests
%li
= link_to_label(label, subject: subject) do
= pluralize open_issues_count, 'open issue'
view open issues
- if current_user && defined?(@project)
%li.label-subscription
- if label.is_a?(ProjectLabel)
......@@ -40,9 +38,9 @@
.pull-right.hidden-xs.hidden-sm.hidden-md
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
= pluralize open_merge_requests_count, 'merge request'
view merge requests
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do
= pluralize open_issues_count, 'open issue'
view open issues
- if current_user && defined?(@project)
.label-subscription.inline
......
......@@ -3,11 +3,11 @@
- panel_class = primary ? 'panel-primary' : 'panel-default'
.panel{ class: panel_class }
.panel-heading.split
.left
.panel-heading
.title
= title
- if show_counter
.right
.counter
= number_with_delimiter(issuables.size)
- class_prefix = dom_class(issuables).pluralize
......
......@@ -9,7 +9,7 @@
Delete
= link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: "New snippet" do
New snippet
- if @snippet.submittable_as_spam? && current_user.admin?
- if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
.visible-xs-block.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
......@@ -28,6 +28,6 @@
%li
= link_to edit_snippet_path(@snippet) do
Edit
- if @snippet.submittable_as_spam? && current_user.admin?
- if @snippet.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post
---
title: Download snippets with LF line-endings by default
merge_request: 8999
author:
---
title: Fix position of counter in milestone panels
merge_request: 7842
author: Andrew Smith (EspadaV8)
---
title: Todo done clicking is kind of unusable
merge_request: 8691
author: Jacopo Beschi @jacopo-beschi
---
title: Set dropdown height fixed to 250px and make it scrollable
merge_request: 9063
author:
---
title: Unify issues search behavior by always filtering when ALL labels matches
merge_request: 8849
author:
---
title: add :iids param to IssuableFinder (resolve technical dept)
merge_request: 9222
author: mhasbini
---
title: Left align navigation
merge_request:
author:
---
title: Stop linking to deleted Branches in Activity tabs
merge_request: 9203
author: Jan Christophersen
---
title: Pick up option from GDK to disable webpack dev server livereload
merge_request:
author:
---
title: Fix grammer issue in admin/runners
merge_request:
author:
---
title: Allow searching issues for strings containing colons
merge_request:
author:
---
title: Adds remote logout functionality to the Authentiq OAuth provider
merge_request: 9381
author: Alexandros Keramidas
---
title: add rake tasks to handle yarn dependencies and update documentation
merge_request: 9316
author:
---
title: 'API: Use POST to (un)block a user'
merge_request: 9371
author: Robert Schilling
---
title: 'API: Remove `DELETE projects/:id/deploy_keys/:key_id/disable`'
merge_request: 9365
author: Robert Schilling
---
title: 'API: Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar`'
merge_request: 9328
author: Robert Schilling
---
title: Added documentation for permalinks to most recent build artifacts.
merge_request: 8934
author: Christian Godenschwager
---
title: Increase process_commit queue weight from 2 to 3
merge_request: 9326
author: blackst0ne
---
title: GitHub Importer - Find users based on GitHub email address
merge_request: 8958
author:
---
title: fix incorrect sidekiq concurrency count in admin background page
merge_request:
author: wendy0402
---
title: Fix errors in slash commands matcher, add simple test coverage
merge_request:
author: YarNayar
---
title: Remove issue and MR counts from labels index
merge_request:
author:
---
title: Update GitLab Pages to v0.3.1
merge_request:
author:
......@@ -240,6 +240,17 @@ Devise.setup do |config|
true
end
end
if provider['name'] == 'authentiq'
provider['args'][:remote_sign_out_handler] = lambda do |request|
authentiq_session = request.params['sid']
if Gitlab::OAuth::Session.valid?(:authentiq, authentiq_session)
Gitlab::OAuth::Session.destroy(:authentiq, authentiq_session)
true
else
false
end
end
end
if provider['name'] == 'shibboleth'
provider['args'][:fail_with_empty_uid] = true
......
......@@ -14,6 +14,9 @@ resource :dashboard, controller: 'dashboard', only: [] do
collection do
delete :destroy_all
end
member do
patch :restore
end
end
resources :projects, only: [:index] do
......
......@@ -21,7 +21,7 @@
- [post_receive, 5]
- [merge, 5]
- [update_merge_requests, 3]
- [process_commit, 2]
- [process_commit, 3]
- [new_note, 2]
- [build, 2]
- [pipeline, 2]
......
......@@ -10,6 +10,7 @@ var ROOT_PATH = path.resolve(__dirname, '..');
var IS_PRODUCTION = process.env.NODE_ENV === 'production';
var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1;
var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
var config = {
context: path.join(ROOT_PATH, 'app/assets/javascripts'),
......@@ -85,8 +86,7 @@ var config = {
'bootstrap/js': 'bootstrap-sass/assets/javascripts/bootstrap',
'emoji-aliases$': path.join(ROOT_PATH, 'fixtures/emojis/aliases.json'),
'vendor': path.join(ROOT_PATH, 'vendor/assets/javascripts'),
'vue$': 'vue/dist/vue.js',
'vue-resource$': 'vue-resource/dist/vue-resource.js'
'vue$': IS_PRODUCTION ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js',
}
}
}
......@@ -116,6 +116,7 @@ if (IS_DEV_SERVER) {
port: DEV_SERVER_PORT,
headers: { 'Access-Control-Allow-Origin': '*' },
stats: 'errors-only',
inline: DEV_SERVER_LIVERELOAD
};
config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath;
}
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexToUserAgentDetail < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def change
add_concurrent_index(:user_agent_details, [:subject_id, :subject_type])
end
end
......@@ -1402,6 +1402,8 @@ ActiveRecord::Schema.define(version: 20170215200045) do
t.datetime "updated_at", null: false
end
add_index "user_agent_details", ["subject_id", "subject_type"], name: "index_user_agent_details_on_subject_id_and_subject_type", using: :btree
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
......
......@@ -23,6 +23,7 @@
- [Project Services](user/project/integrations//project_services.md) Integrate a project with external services, such as CI and chat.
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
- [Analytics](analytics/README.md)
- [Snippets](user/snippets.md) Snippets allow you to create little bits of code.
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Webhooks](user/project/integrations/webhooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
......
......@@ -54,7 +54,7 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
5. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits.
See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq#scopes-and-redirect-uri-configuration) for more information on scopes and modifiers.
6. Change 'YOUR_CLIENT_ID' and 'YOUR_CLIENT_SECRET' to the Client credentials you received in step 1.
6. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in step 1.
7. Save the configuration file.
......
......@@ -12,8 +12,8 @@ GET /projects/:id/repository/commits
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
| `ref_name` | string | no | The name of a repository branch or tag or if not given the default branch |
| `since` | string | no | Only commits after or in this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
| `until` | string | no | Only commits before or in this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
| `since` | string | no | Only commits after or on this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
| `until` | string | no | Only commits before or on this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/commits"
......
......@@ -137,7 +137,7 @@ Example response:
## Delete deploy key
Delete a deploy key from a project
Removes a deploy key from the project. If the deploy key is used only for this project, it will be deleted from the system.
```
DELETE /projects/:id/deploy_keys/:key_id
......@@ -156,14 +156,11 @@ Example response:
```json
{
"updated_at" : "2015-08-29T12:50:57.259Z",
"key" : "ssh-rsa AAAA...",
"public" : false,
"title" : "My deploy key",
"user_id" : null,
"created_at" : "2015-08-29T12:50:57.259Z",
"fingerprint" : "6a:33:1f:74:51:c0:39:81:79:ec:7a:31:f8:40:20:43",
"id" : 13
"id": 6,
"deploy_key_id": 14,
"project_id": 1,
"created_at" : "2015-08-29T12:50:57.259Z",
"updated_at" : "2015-08-29T12:50:57.259Z"
}
```
......@@ -190,27 +187,3 @@ Example response:
"created_at" : "2015-08-29T12:44:31.550Z"
}
```
## Disable a deploy key
Disable a deploy key for a project. Returns the disabled key.
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/deploy_keys/13/disable
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of the project |
| `key_id` | integer | yes | The ID of the deploy key |
Example response:
```json
{
"key" : "ssh-rsa AAAA...",
"id" : 12,
"title" : "My deploy key",
"created_at" : "2015-08-29T12:44:31.550Z"
}
```
......@@ -30,7 +30,7 @@ GET /issues?milestone=1.0.0&state=opened
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `state` | string | no | Return all issues or just those that are `opened` or `closed`|
| `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned |
| `milestone` | string| no | The milestone title |
| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
......@@ -190,7 +190,7 @@ GET /projects/:id/issues?milestone=1.0.0&state=opened
| `id` | integer | yes | The ID of a project |
| `iid` | integer | no | Return the issue having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed`|
| `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned |
| `milestone` | string| no | The milestone title |
| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
......
......@@ -616,7 +616,7 @@ Example response:
Unstars a given project. Returns status code `304` if the project is not starred.
```
DELETE /projects/:id/star
POST /projects/:id/unstar
```
| Attribute | Type | Required | Description |
......@@ -624,7 +624,7 @@ DELETE /projects/:id/star
| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/star"
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/unstar"
```
Example response:
......@@ -1203,6 +1203,7 @@ Parameters:
| `order_by` | string | no | Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order |
## Push Rules (EE only)
### Show project push rules
......
......@@ -661,14 +661,14 @@ Will return `200 OK` on success, or `404 Not found` if either user or email cann
Blocks the specified user. Available only for admin.
```
PUT /users/:id/block
POST /users/:id/block
```
Parameters:
- `id` (required) - id of specified user
Will return `200 OK` on success, `404 User Not Found` is user cannot be found or
Will return `201 OK` on success, `404 User Not Found` is user cannot be found or
`403 Forbidden` when trying to block an already blocked user by LDAP synchronization.
## Unblock user
......@@ -676,14 +676,14 @@ Will return `200 OK` on success, `404 User Not Found` is user cannot be found or
Unblocks the specified user. Available only for admin.
```
PUT /users/:id/unblock
POST /users/:id/unblock
```
Parameters:
- `id` (required) - id of specified user
Will return `200 OK` on success, `404 User Not Found` is user cannot be found or
Will return `201 OK` on success, `404 User Not Found` is user cannot be found or
`403 Forbidden` when trying to unblock a user blocked by LDAP synchronization.
### Get user contribution events
......
......@@ -13,6 +13,7 @@ changes are in V4:
- Project snippets do not return deprecated field `expires_at`
- Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`)
- Status 409 returned for POST `project/:id/members` when a member already exists
- Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar`
- Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix)
- `/licences`
- `/licences/:key`
......@@ -25,5 +26,7 @@ changes are in V4:
- Moved `/projects/fork/:id` to `/projects/:id/fork`
- Endpoints `/projects/owned`, `/projects/visible`, `/projects/starred` & `/projects/all` are consolidated into `/projects` using query parameters
- Return pagination headers for all endpoints that return an array
- Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead
- Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)`
- Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR)
......@@ -91,7 +91,7 @@ total running time should be:
## Badges
Job status and test coverage report badges are available. You can find their
Pipeline status and test coverage report badges are available. You can find their
respective link in the [Pipelines settings] page.
[jobs]: #jobs
......
......@@ -64,6 +64,10 @@ Libraries with the following licenses are unacceptable for use:
- [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects.
- [Open Software License (OSL)][OSL]: is a copyleft license. In addition, the FSF [recommend against its use][OSL-GNU].
## Requesting Approval for Licenses
Libraries that are not listed in the [Acceptable Licenses][Acceptable-Licenses] or [Unacceptable Licenses][Unacceptable-Licenses] list can be submitted to the legal team for review. Please create an issue in the [Organization Repository][Org-Repo] and cc `@gl-legal`. After a decision has been made, the original requestor is responsible for updating this document.
## Notes
Decisions regarding the GNU GPL licenses are based on information provided by [The GNU Project][GNU-GPL-FAQ], as well as [the Open Source Initiative][OSI-GPL], which both state that linking GPL libraries makes the program itself GPL.
......@@ -96,3 +100,6 @@ Gems which are included only in the "development" or "test" groups by Bundler ar
[OSI-GPL]: https://opensource.org/faq#linking-proprietary-code
[OSL]: https://opensource.org/licenses/OSL-3.0
[OSL-GNU]: https://www.gnu.org/licenses/license-list.en.html#OSL
[Org-Repo]: https://gitlab.com/gitlab-com/organization
[Acceptable-Licenses]: #acceptable-licenses
[Unacceptable-Licenses]: #unacceptable-licenses
......@@ -95,6 +95,25 @@ so we need to set some guidelines for their use going forward:
[lets-not]: https://robots.thoughtbot.com/lets-not
### Time-sensitive tests
[Timecop](https://github.com/travisjeffery/timecop) is available in our
Ruby-based tests for verifying things that are time-sensitive. Any test that
exercises or verifies something time-sensitive should make use of Timecop to
prevent transient test failures.
Example:
```ruby
it 'is overdue' do
issue = build(:issue, due_date: Date.tomorrow)
Timecop.freeze(3.days.from_now) do
expect(issue).to be_overdue
end
end
```
### Test speed
GitLab has a massive test suite that, without parallelization, can take more
......@@ -115,6 +134,10 @@ Here are some things to keep in mind regarding test performance:
### Features / Integration
GitLab uses [rspec-rails feature specs] to test features in a browser
environment. These are [capybara] specs running on the headless [poltergeist]
driver.
- Feature specs live in `spec/features/` and should be named
`ROLE_ACTION_spec.rb`, such as `user_changes_password_spec.rb`.
- Use only one `feature` block per feature spec file.
......@@ -122,6 +145,10 @@ Here are some things to keep in mind regarding test performance:
- Avoid scenario titles that add no information, such as "successfully."
- Avoid scenario titles that repeat the feature title.
[rspec-rails feature specs]: https://github.com/rspec/rspec-rails#feature-specs
[capybara]: https://github.com/teamcapybara/capybara
[poltergeist]: https://github.com/teampoltergeist/poltergeist
## Spinach (feature) tests
GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426)
......
......@@ -176,4 +176,4 @@ Portions of this page are modifications based on work created and shared by the
[products]: https://about.gitlab.com/products/ "GitLab products page"
[serial comma]: https://en.wikipedia.org/wiki/Serial_comma "“Serial comma” in Wikipedia"
[android project]: http://source.android.com/
[creative commons]: http://creativecommons.org/licenses/by/2.5/
\ No newline at end of file
[creative commons]: http://creativecommons.org/licenses/by/2.5/
This diff is collapsed.
......@@ -155,15 +155,19 @@ page](https://golang.org/dl).
## 4. Node
Since GitLab 8.17, GitLab requires the use of node >= v4.3.0 to compile
javascript assets. In many distros the version provided by the official package
repositories is out of date, so we'll need to install through the following
commands:
javascript assets, and starting in GitLab 9.0, yarn >= v0.17.0 is required to
manage javascript dependencies. In many distros the versions provided by the
official package repositories are out of date, so we'll need to install through
the following commands:
# install node v7.x
curl --location https://deb.nodesource.com/setup_7.x | bash -
sudo apt-get install -y nodejs
Visit the official website for [node](https://nodejs.org/en/download/package-manager/) if you have any trouble with this step.
# install yarn
curl --location https://yarnpkg.com/install.sh | bash -
Visit the official websites for [node](https://nodejs.org/en/download/package-manager/) and [yarn](https://yarnpkg.com/en/docs/install/) if you have any trouble with these steps.
## 5. System Users
......@@ -465,7 +469,7 @@ Check if GitLab and its environment are configured correctly:
### Compile Assets
sudo -u git -H npm install --production
sudo -u git -H yarn install --production --pure-lockfile
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
### Start Your GitLab Instance
......
......@@ -3,13 +3,6 @@
Settings in the **Profile > Preferences** page allow the user to customize
various aspects of the site to their liking.
## Application theme
Changing this setting allows the user to customize the color scheme used for the
navigation bar on the left side of the screen.
The default is **Charcoal**.
## Syntax highlighting theme
_GitLab uses the [rouge ruby library][rouge] for syntax highlighting. For a
......
......@@ -90,18 +90,43 @@ inside GitLab that make that possible.
It is possible to download the latest artifacts of a job via a well known URL
so you can use it for scripting purposes.
The structure of the URL is the following:
The structure of the URL to download the whole artifacts archive is the following:
```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/download?job=<job_name>
```
For example, to download the latest artifacts of the job named `rspec 6 20` of
To download a single file from the artifacts use the following URL:
```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/file/<path_to_file>?job=<job_name>
```
For example, to download the latest artifacts of the job named `coverage` of
the `master` branch of the `gitlab-ce` project that belongs to the `gitlab-org`
namespace, the URL would be:
```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/download?job=rspec+6+20
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/download?job=coverage
```
To download the file `coverage/index.html` from the same
artifacts use the following URL:
```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/file/coverage/index.html?job=coverage
```
There is also a URL to browse the latest job artifacts:
```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/browse?job=<job_name>
```
For example:
```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/browse?job=coverage
```
The latest builds are also exposed in the UI in various places. Specifically,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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