Commit f6e6ff4b authored by Nick Thomas's avatar Nick Thomas

Merge remote-tracking branch 'ce/master' into nt/ce-to-ee-thursday

parents 7d2d4939 a33b376d
......@@ -15,10 +15,15 @@ variables:
GIT_DEPTH: "20"
PHANTOMJS_VERSION: "2.1.1"
GET_SOURCES_ATTEMPTS: "3"
<<<<<<< HEAD
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-${CI_COMMIT_REF_SLUG}.json
KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-${CI_COMMIT_REF_SLUG}.json
# This hack is needed to make ES not that memory hungry
ES_JAVA_OPTS: "-Xms600m -Xmx600m"
=======
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json
KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-master.json
>>>>>>> ce/master
before_script:
- source ./scripts/prepare_build.sh
......@@ -278,14 +283,35 @@ rake karma:
paths:
- coverage-javascript/
lint-doc:
docs:check:apilint:
image: "phusion/baseimage"
stage: test
<<: *dedicated-runner
image: "phusion/baseimage:latest"
variables:
GIT_DEPTH: "3"
cache: {}
dependencies: []
before_script: []
script:
- scripts/lint-doc.sh
docs:check:links:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
stage: test
<<: *dedicated-runner
variables:
GIT_DEPTH: "3"
cache: {}
dependencies: []
before_script: []
script:
- mv doc/ /nanoc/content/
- cd /nanoc
# Build HTML from Markdown
- bundle exec nanoc
# Check the internal links
- bundle exec nanoc check internal_links
bundler:check:
stage: test
<<: *dedicated-runner
......
......@@ -314,9 +314,12 @@ request is as follows:
organized commits by [squashing them][git-squash]
1. Push the commit(s) to your fork
1. Submit a merge request (MR) to the `master` branch
1. Leave the approvals settings as they are:
1. Your merge request needs at least 1 approval
1. You don't have to select any approvers
1. Your merge request needs at least 1 approval but feel free to require more.
For instance if you're touching backend and frontend code, it's a good idea
to require 2 approvals: 1 from a backend maintainer and 1 from a frontend
maintainer
1. You don't have to select any approvers, but you can if you really want
specific people to approve your merge request
1. The MR title should describe the change you want to make
1. The MR description should give a motive for your change and the method you
used to achieve it.
......@@ -376,7 +379,7 @@ There are a few rules to get your merge request accepted:
1. If your merge request includes only frontend changes [^1], it must be
**approved by a [frontend maintainer][team]**.
1. If your merge request includes frontend and backend changes [^1], it must
be approved by a frontend **and** a backend maintainer.
be **approved by a [frontend and a backend maintainer][team]**.
1. To lower the amount of merge requests maintainers need to review, you can
ask or assign any [reviewers][team] for a first review.
1. If you need some guidance (e.g. it's your first merge request), feel free
......@@ -556,6 +559,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
[^1]: Specs other than JavaScript specs are considered backend code. Haml
changes are considered backend code if they include Ruby code other than just
pure HTML.
[^1]: Please note that specs other than JavaScript specs are considered backend
code.
......@@ -15,7 +15,7 @@ gem 'default_value_for', '~> 3.0.0'
gem 'mysql2', '~> 0.3.16', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.24.0'
gem 'rugged', '~> 0.25.1.1'
# Authentication libraries
gem 'devise', '~> 4.2'
......@@ -65,7 +65,7 @@ gem 'net-ldap'
# Git Wiki
# Required manually in config/initializers/gollum.rb to control load order
gem 'gollum-lib', '~> 4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
# Language detection
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
......
......@@ -321,9 +321,9 @@ GEM
rouge (~> 2.0)
sanitize (~> 2.1.0)
stringex (~> 2.5.1)
gollum-rugged_adapter (0.4.2)
gollum-rugged_adapter (0.4.4)
mime-types (>= 1.15)
rugged (~> 0.24.0, >= 0.21.3)
rugged (~> 0.25)
gon (6.1.0)
actionpack (>= 3.0)
json
......@@ -718,7 +718,7 @@ GEM
rubypants (0.2.0)
rubyzip (1.2.1)
rufus-scheduler (3.1.10)
rugged (0.24.0)
rugged (0.25.1.1)
safe_yaml (1.0.4)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
......@@ -948,7 +948,7 @@ DEPENDENCIES
gitlab-markup (~> 1.5.1)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2)
gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0)
google-api-client (~> 0.8.6)
grape (~> 0.19.0)
......@@ -1032,7 +1032,7 @@ DEPENDENCIES
rubocop-rspec (~> 1.12.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2)
rugged (~> 0.24.0)
rugged (~> 0.25.1.1)
sanitize (~> 2.0)
sass-rails (~> 5.0.6)
scss_lint (~> 0.47.0)
......
<<<<<<< HEAD
8.18.0-ee-pre
=======
9.1.0-pre
>>>>>>> ce/master
import spreadString from './spread_string';
// On Windows, flags render as two-letter country codes, see http://emojipedia.org/flags/
const flagACodePoint = 127462; // parseInt('1F1E6', 16)
const flagZCodePoint = 127487; // parseInt('1F1FF', 16)
......@@ -20,7 +18,7 @@ function isKeycapEmoji(emojiUnicode) {
const tone1 = 127995;// parseInt('1F3FB', 16)
const tone5 = 127999;// parseInt('1F3FF', 16)
function isSkinToneComboEmoji(emojiUnicode) {
return emojiUnicode.length > 2 && spreadString(emojiUnicode).some((char) => {
return emojiUnicode.length > 2 && Array.from(emojiUnicode).some((char) => {
const cp = char.codePointAt(0);
return cp >= tone1 && cp <= tone5;
});
......@@ -30,7 +28,7 @@ function isSkinToneComboEmoji(emojiUnicode) {
// doesn't support the skin tone versions of horse racing
const horseRacingCodePoint = 127943;// parseInt('1F3C7', 16)
function isHorceRacingSkinToneComboEmoji(emojiUnicode) {
return spreadString(emojiUnicode)[0].codePointAt(0) === horseRacingCodePoint &&
return Array.from(emojiUnicode)[0].codePointAt(0) === horseRacingCodePoint &&
isSkinToneComboEmoji(emojiUnicode);
}
......@@ -42,7 +40,7 @@ const personEndCodePoint = 128105; // parseInt('1F469', 16)
function isPersonZwjEmoji(emojiUnicode) {
let hasPersonEmoji = false;
let hasZwj = false;
spreadString(emojiUnicode).forEach((character) => {
Array.from(emojiUnicode).forEach((character) => {
const cp = character.codePointAt(0);
if (cp === zwj) {
hasZwj = true;
......
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt#Fixing_charCodeAt()_to_handle_non-Basic-Multilingual-Plane_characters_if_their_presence_earlier_in_the_string_is_known
function knownCharCodeAt(givenString, index) {
const str = `${givenString}`;
const end = str.length;
const surrogatePairs = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
let idx = index;
while ((surrogatePairs.exec(str)) != null) {
const li = surrogatePairs.lastIndex;
if (li - 2 < idx) {
idx += 1;
} else {
break;
}
}
if (idx >= end || idx < 0) {
return NaN;
}
const code = str.charCodeAt(idx);
let high;
let low;
if (code >= 0xD800 && code <= 0xDBFF) {
high = code;
low = str.charCodeAt(idx + 1);
// Go one further, since one of the "characters" is part of a surrogate pair
return ((high - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
}
return code;
}
// See http://stackoverflow.com/a/38901550/796832
// ES5/PhantomJS compatible version of spreading a string
//
// [...'foo'] -> ['f', 'o', 'o']
// [...'🖐🏿'] -> ['🖐', '🏿']
function spreadString(str) {
const arr = [];
let i = 0;
while (!isNaN(knownCharCodeAt(str, i))) {
const codePoint = knownCharCodeAt(str, i);
arr.push(String.fromCodePoint(codePoint));
i += 1;
}
return arr;
}
export default spreadString;
......@@ -18,7 +18,7 @@
// Button does not change visibility. If button has icon - it changes chevron style.
//
// %div.js-toggle-container
// %a.js-toggle-button
// %button.js-toggle-button
// %div.js-toggle-content
//
$('body').on('click', '.js-toggle-button', function(e) {
......
/* eslint-disable no-new */
import Vue from 'vue';
import VueResource from 'vue-resource';
import NotebookLab from 'vendor/notebooklab';
Vue.use(VueResource);
Vue.use(NotebookLab);
export default () => {
const el = document.getElementById('js-notebook-viewer');
new Vue({
el,
data() {
return {
error: false,
loadError: false,
loading: true,
json: {},
};
},
template: `
<div class="container-fluid md prepend-top-default append-bottom-default">
<div
class="text-center loading"
v-if="loading && !error">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="iPython notebook loading">
</i>
</div>
<notebook-lab
v-if="!loading && !error"
:notebook="json"
code-css-class="code white" />
<p
class="text-center"
v-if="error">
<span v-if="loadError">
An error occured whilst loading the file. Please try again later.
</span>
<span v-else>
An error occured whilst parsing the file.
</span>
</p>
</div>
`,
methods: {
loadFile() {
this.$http.get(el.dataset.endpoint)
.then((res) => {
this.json = res.json();
this.loading = false;
})
.catch((e) => {
if (e.status) {
this.loadError = true;
}
this.error = true;
});
},
},
mounted() {
if (gon.katex_css_url) {
const katexStyles = document.createElement('link');
katexStyles.setAttribute('rel', 'stylesheet');
katexStyles.setAttribute('href', gon.katex_css_url);
document.head.appendChild(katexStyles);
}
if (gon.katex_js_url) {
const katexScript = document.createElement('script');
katexScript.addEventListener('load', () => {
this.loadFile();
});
katexScript.setAttribute('src', gon.katex_js_url);
document.head.appendChild(katexScript);
} else {
this.loadFile();
}
},
});
};
import renderNotebook from './notebook';
document.addEventListener('DOMContentLoaded', renderNotebook);
......@@ -50,9 +50,7 @@ export default {
this.showDetail = false;
},
showIssue(e) {
const targetTagName = e.target.tagName.toLowerCase();
if (targetTagName === 'a' || targetTagName === 'button') return;
if (e.target.classList.contains('js-no-trigger')) return;
if (this.showDetail) {
this.showDetail = false;
......
......@@ -84,20 +84,20 @@ import eventHub from '../eventhub';
#{{ issue.id }}
</span>
<a
class="card-assignee has-tooltip"
class="card-assignee has-tooltip js-no-trigger"
:href="rootPath + issue.assignee.username"
:title="'Assigned to ' + issue.assignee.name"
v-if="issue.assignee"
data-container="body">
<img
class="avatar avatar-inline s20"
class="avatar avatar-inline s20 js-no-trigger"
:src="issue.assignee.avatar"
width="20"
height="20"
:alt="'Avatar for ' + issue.assignee.name" />
</a>
<button
class="label color-label has-tooltip"
class="label color-label has-tooltip js-no-trigger"
v-for="label in issue.labels"
type="button"
v-if="showLabel(label)"
......
......@@ -33,12 +33,11 @@ export default Vue.component('pipelines-table', {
* @return {Object}
*/
data() {
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
const store = new PipelineStore();
return {
endpoint: pipelinesTableData.endpoint,
helpPagePath: pipelinesTableData.helpPagePath,
endpoint: null,
helpPagePath: null,
store,
state: store.state,
isLoading: false,
......@@ -65,6 +64,8 @@ export default Vue.component('pipelines-table', {
*
*/
beforeMount() {
this.endpoint = this.$el.dataset.endpoint;
this.helpPagePath = this.$el.dataset.helpPagePath;
this.service = new PipelinesService(this.endpoint);
this.fetchPipelines();
......
......@@ -34,8 +34,11 @@
/* global Labels */
/* global Shortcuts */
/* global Sidebar */
<<<<<<< HEAD
/* global WeightSelect */
/* global AdminEmailSelect */
=======
>>>>>>> ce/master
import Issue from './issue';
......
......@@ -8,21 +8,31 @@ require('./filtered_search_token_keys');
// Values that start with a double quote must end in a double quote (same for single)
const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokens = [];
const tokenIndexes = []; // stores key+value for simple search
let lastToken = null;
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
let tokenValue = v1 || v2 || v3;
let tokenSymbol = symbol;
let tokenIndex = '';
if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
tokenSymbol = tokenValue;
tokenValue = '';
}
tokens.push({
key,
value: tokenValue || '',
symbol: tokenSymbol || '',
});
tokenIndex = `${key}:${tokenValue}`;
// Prevent adding duplicates
if (tokenIndexes.indexOf(tokenIndex) === -1) {
tokenIndexes.push(tokenIndex);
tokens.push({
key,
value: tokenValue || '',
symbol: tokenSymbol || '',
});
}
return '';
}).replace(/\s{2,}/g, ' ').trim() || '';
......
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var */
$(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count');
var $todoPendingCount = $('.todos-count');
$todoPendingCount.text(gl.text.highCountTrim(count));
$todoPendingCount.toggleClass('hidden', count === 0);
});
......@@ -5,23 +5,37 @@ import httpStatusCodes from './http_status';
* Service for vue resouce and method need to be provided as props
*
* @example
* new poll({
* new Poll({
* resource: resource,
* method: 'name',
* data: {page: 1, scope: 'all'},
* data: {page: 1, scope: 'all'}, // optional
* successCallback: () => {},
* errorCallback: () => {},
* notificationCallback: () => {}, // optional
* }).makeRequest();
*
* this.service = new BoardsService(endpoint);
* new poll({
* resource: this.service,
* method: 'get',
* data: {page: 1, scope: 'all'},
* successCallback: () => {},
* errorCallback: () => {},
* }).makeRequest();
* Usage in pipelines table with visibility lib:
*
* const poll = new Poll({
* resource: this.service,
* method: 'getPipelines',
* data: { page: pageNumber, scope },
* successCallback: this.successCallback,
* errorCallback: this.errorCallback,
* notificationCallback: this.updateLoading,
* });
*
* if (!Visibility.hidden()) {
* poll.makeRequest();
* }
*
* Visibility.change(() => {
* if (!Visibility.hidden()) {
* poll.restart();
* } else {
* poll.stop();
* }
* });
*
* 1. Checks for response and headers before start polling
* 2. Interval is provided by `Poll-Interval` header.
......@@ -34,6 +48,8 @@ export default class Poll {
constructor(options = {}) {
this.options = options;
this.options.data = options.data || {};
this.options.notificationCallback = options.notificationCallback ||
function notificationCallback() {};
this.intervalHeader = 'POLL-INTERVAL';
this.timeoutID = null;
......@@ -42,7 +58,7 @@ export default class Poll {
checkConditions(response) {
const headers = gl.utils.normalizeHeaders(response.headers);
const pollInterval = headers[this.intervalHeader];
const pollInterval = parseInt(headers[this.intervalHeader], 10);
if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
this.timeoutID = setTimeout(() => {
......@@ -54,11 +70,14 @@ export default class Poll {
}
makeRequest() {
const { resource, method, data, errorCallback } = this.options;
const { resource, method, data, errorCallback, notificationCallback } = this.options;
// It's called everytime a new request is made. Useful to update the status.
notificationCallback(true);
return resource[method](data)
.then(response => this.checkConditions(response))
.catch(error => errorCallback(error));
.then(response => this.checkConditions(response))
.catch(error => errorCallback(error));
}
/**
......@@ -70,4 +89,12 @@ export default class Poll {
this.canPoll = false;
clearTimeout(this.timeoutID);
}
/**
* Restarts polling after it has been stoped
*/
restart() {
this.canPoll = true;
this.makeRequest();
}
}
......@@ -4,8 +4,10 @@
import Cookies from 'js-cookie';
require('./breakpoints');
require('./flash');
import CommitPipelinesTable from './commit/pipelines/pipelines_table';
import './breakpoints';
import './flash';
/* eslint-disable max-len */
// MergeRequestTabs
......@@ -97,6 +99,13 @@ require('./flash');
.off('click', this.clickTab);
}
destroy() {
this.unbindEvents();
if (this.commitPipelinesTable) {
this.commitPipelinesTable.$destroy();
}
}
showTab(e) {
e.preventDefault();
this.activateTab($(e.target).data('action'));
......@@ -128,12 +137,8 @@ require('./flash');
this.expandViewContainer();
}
} else if (action === 'pipelines') {
if (this.pipelinesLoaded) {
return;
}
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl);
this.pipelinesLoaded = true;
this.resetViewContainer();
this.loadPipelines();
} else {
this.expandView();
this.resetViewContainer();
......@@ -222,6 +227,18 @@ require('./flash');
});
}
loadPipelines() {
if (this.pipelinesLoaded) {
return;
}
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
// Could already be mounted from the `pipelines_bundle`
if (pipelineTableViewEl) {
this.commitPipelinesTable = new CommitPipelinesTable().$mount(pipelineTableViewEl);
}
this.pipelinesLoaded = true;
}
loadDiff(source) {
if (this.diffsLoaded) {
return;
......
......@@ -56,14 +56,15 @@ import Cookies from 'js-cookie';
Sidebar.prototype.toggleTodo = function(e) {
var $btnText, $this, $todoLoading, ajaxType, url;
$this = $(e.currentTarget);
$todoLoading = $('.js-issuable-todo-loading');
$btnText = $('.js-issuable-todo-text', $this);
ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST';
if ($this.attr('data-delete-path')) {
url = "" + ($this.attr('data-delete-path'));
} else {
url = "" + ($this.data('url'));
}
$this.tooltip('hide');
return $.ajax({
url: url,
type: ajaxType,
......@@ -74,34 +75,44 @@ import Cookies from 'js-cookie';
},
beforeSend: (function(_this) {
return function() {
return _this.beforeTodoSend($this, $todoLoading);
$('.js-issuable-todo').disable()
.addClass('is-loading');
};
})(this)
}).done((function(_this) {
return function(data) {
return _this.todoUpdateDone(data, $this, $btnText, $todoLoading);
return _this.todoUpdateDone(data);
};
})(this));
};
Sidebar.prototype.beforeTodoSend = function($btn, $todoLoading) {
$btn.disable();
return $todoLoading.removeClass('hidden');
};
Sidebar.prototype.todoUpdateDone = function(data) {
const deletePath = data.delete_path ? data.delete_path : null;
const attrPrefix = deletePath ? 'mark' : 'todo';
const $todoBtns = $('.js-issuable-todo');
Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
$(document).trigger('todo:toggle', data.count);
$btn.enable();
$todoLoading.addClass('hidden');
$todoBtns.each((i, el) => {
const $el = $(el);
const $elText = $el.find('.js-issuable-todo-inner');
if (data.delete_path != null) {
$btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
return $btnText.text($btn.data('mark-text'));
} else {
$btn.attr('aria-label', $btn.data('todo-text')).removeAttr('data-delete-path');
return $btnText.text($btn.data('todo-text'));
}
$el.removeClass('is-loading')
.enable()
.attr('aria-label', $el.data(`${attrPrefix}-text`))
.attr('data-delete-path', deletePath)
.attr('title', $el.data(`${attrPrefix}-text`));
if ($el.hasClass('has-tooltip')) {
$el.tooltip('fixTitle');
}
if ($el.data(`${attrPrefix}-icon`)) {
$elText.html($el.data(`${attrPrefix}-icon`));
} else {
$elText.text($el.data(`${attrPrefix}-text`));
}
});
};
Sidebar.prototype.sidebarDropdownLoading = function(e) {
......
import Cookies from 'js-cookie';
const userCalloutElementName = '.user-callout';
const closeButton = '.close-user-callout';
const userCalloutBtn = '.user-callout-btn';
const userCalloutSvgAttrName = 'callout-svg';
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
const USER_CALLOUT_TEMPLATE = `
<div class="bordered-box landing content-block">
<button class="btn btn-default close close-user-callout" type="button">
<i class="fa fa-times dismiss-icon"></i>
</button>
<div class="row">
<div class="col-sm-3 col-xs-12 svg-container">
</div>
<div class="col-sm-8 col-xs-12 inner-content">
<h4>
Customize your experience
</h4>
<p>
Change syntax themes, default project pages, and more in preferences.
</p>
<a class="btn user-callout-btn" href="/profile/preferences">Check it out</a>
</div>
</div>
</div>`;
export default class UserCallout {
constructor() {
this.isCalloutDismissed = Cookies.get(USER_CALLOUT_COOKIE);
this.userCalloutBody = $(userCalloutElementName);
this.userCalloutSvg = $(userCalloutElementName).attr(userCalloutSvgAttrName);
$(userCalloutElementName).removeAttr(userCalloutSvgAttrName);
this.userCalloutBody = $('.user-callout');
this.init();
}
init() {
const $template = $(USER_CALLOUT_TEMPLATE);
if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') {
$template.find('.svg-container').append(this.userCalloutSvg);
this.userCalloutBody.append($template);
$template.find(closeButton).on('click', e => this.dismissCallout(e));
$template.find(userCalloutBtn).on('click', e => this.dismissCallout(e));
} else {
this.userCalloutBody.remove();
$('.js-close-callout').on('click', e => this.dismissCallout(e));
}
}
dismissCallout(e) {
Cookies.set(USER_CALLOUT_COOKIE, 'true');
const $currentTarget = $(e.currentTarget);
if ($currentTarget.hasClass('close-user-callout')) {
Cookies.set(USER_CALLOUT_COOKIE, 'true');
if ($currentTarget.hasClass('close')) {
this.userCalloutBody.remove();
}
}
......
......@@ -3,8 +3,8 @@ const UI_LIMIT = 6;
const SPREAD = '...';
const PREV = 'Prev';
const NEXT = 'Next';
const FIRST = '<< First';
const LAST = 'Last >>';
const FIRST = '« First';
const LAST = 'Last »';
export default {
props: {
......
......@@ -362,3 +362,13 @@
width: 100%;
}
}
.btn-blank {
padding: 0;
background: transparent;
border: 0;
&:focus {
outline: 0;
}
}
......@@ -48,10 +48,10 @@ header {
color: $gl-text-color-secondary;
font-size: 18px;
padding: 0;
margin: ($header-height - 28) / 2 0;
margin: (($header-height - 28) / 2) 3px;
margin-left: 8px;
height: 28px;
min-width: 28px;
min-width: 32px;
line-height: 28px;
text-align: center;
......@@ -73,21 +73,29 @@ header {
background-color: $gray-light;
color: $gl-text-color;
.todos-pending-count {
background: darken($todo-alert-blue, 10%);
svg {
fill: $gl-text-color;
}
}
.fa-caret-down {
font-size: 14px;
}
svg {
position: relative;
top: 2px;
height: 17px;
// hack to get SVG to line up with FA icons
width: 23px;
fill: $gl-text-color-secondary;
}
}
.navbar-toggle {
color: $nav-toggle-gray;
margin: 7px 0;
margin: 5px 0;
border-radius: 0;
position: absolute;
right: -10px;
padding: 6px 10px;
......@@ -141,10 +149,6 @@ header {
min-height: $header-height;
padding-left: 30px;
@media (max-width: $screen-sm-max) {
padding-right: 20px;
}
.dropdown-menu {
margin-top: -5px;
}
......@@ -243,10 +247,7 @@ header {
.navbar-collapse {
flex: 0 0 auto;
border-top: none;
@media (min-width: $screen-md-min) {
padding: 0;
}
padding: 0;
@media (max-width: $screen-xs-max) {
flex: 1 1 auto;
......@@ -263,6 +264,34 @@ header {
}
}
.navbar-nav {
li {
.badge {
position: inherit;
top: -3px;
font-weight: normal;
margin-left: -12px;
font-size: 11px;
color: $white-light;
padding: 1px 5px 2px;
border-radius: 7px;
box-shadow: 0 1px 0 rgba($gl-header-color, .2);
&.issues-count {
background-color: $green-500;
}
&.merge-requests-count {
background-color: $orange-600;
}
&.todos-count {
background-color: $blue-500;
}
}
}
}
@media (max-width: $screen-xs-max) {
header .container-fluid {
font-size: 18px;
......
......@@ -52,6 +52,18 @@
}
}
@mixin basic-list-stats {
.stats {
float: right;
line-height: $list-text-height;
color: $gl-text-color;
span {
margin-right: 15px;
}
}
}
@mixin bulleted-list {
> ul {
list-style-type: disc;
......
......@@ -33,7 +33,7 @@
padding-right: 0;
@media (min-width: $screen-sm-min) {
.content-wrapper {
&:not(.wiki-sidebar):not(.build-sidebar) .content-wrapper {
padding-right: $gutter_collapsed_width;
}
......@@ -55,7 +55,7 @@
padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
.content-wrapper {
&:not(.wiki-sidebar):not(.build-sidebar) .content-wrapper {
padding-right: $gutter_collapsed_width;
}
}
......
......@@ -269,8 +269,13 @@
font-size: (14px / $issue-boards-font-size) * 1em;
}
.card-assignee {
margin-right: 5px;
}
.avatar {
margin-left: 0;
margin-right: 0;
}
}
......@@ -325,6 +330,7 @@
}
}
<<<<<<< HEAD
.boards-title-holder {
padding: 25px 13px $gl-padding;
......@@ -346,6 +352,9 @@
}
.issue-boards-sidebar {
=======
.page-with-layout-nav.page-with-sub-nav .issue-boards-sidebar {
>>>>>>> ce/master
&.right-sidebar {
top: 0;
bottom: 0;
......
......@@ -431,6 +431,21 @@
border-bottom: none;
}
.diff-stats-summary-toggler {
padding: 0;
background-color: transparent;
border: 0;
color: $gl-link-color;
transition: color 0.1s linear;
&:hover,
&:focus {
outline: none;
text-decoration: underline;
color: $gl-link-hover-color;
}
}
// Mobile
@media (max-width: 480px) {
.diff-title {
......
......@@ -22,15 +22,7 @@
}
.group-row {
.stats {
float: right;
line-height: $list-text-height;
color: $gl-text-color;
span {
margin-right: 15px;
}
}
@include basic-list-stats;
}
.ldap-group-links {
......
......@@ -243,6 +243,10 @@
font-size: 13px;
font-weight: normal;
}
.hide-expanded {
display: none;
}
}
&.right-sidebar-collapsed {
......@@ -282,10 +286,11 @@
display: block;
width: 100%;
text-align: center;
padding-bottom: 10px;
margin-bottom: 10px;
color: $issuable-sidebar-color;
&:hover {
&:hover,
&:hover .todo-undone {
color: $gl-text-color;
}
......@@ -294,6 +299,10 @@
margin-top: 0;
}
.todo-undone {
color: $gl-link-color;
}
.author {
display: none;
}
......@@ -582,3 +591,21 @@
opacity: 0;
}
}
.issuable-todo-btn {
.fa-spinner {
display: none;
}
&.is-loading {
.fa-spinner {
display: inline-block;
}
&.sidebar-collapsed-icon {
.issuable-todo-inner {
display: none;
}
}
}
}
......@@ -60,7 +60,17 @@
}
.modify-merge-commit-link {
padding: 0;
background-color: transparent;
border: 0;
color: $gl-text-color;
&:hover,
&:focus {
text-decoration: underline;
}
}
.merge-param-checkbox {
......
......@@ -410,8 +410,22 @@ ul.notes {
}
.discussion-toggle-button {
padding: 0;
background-color: transparent;
border: 0;
line-height: 20px;
font-size: 13px;
transition: color 0.1s linear;
&:hover {
color: $gl-link-color;
}
&:focus {
text-decoration: underline;
outline: none;
color: $gl-link-color;
}
.fa {
margin-right: 3px;
......
......@@ -573,9 +573,19 @@ pre.light-well {
display: flex;
flex-direction: column;
// Disable Flexbox for admin page
&.admin-projects {
display: block;
.project-row {
display: block;
}
}
.project-row {
display: flex;
align-items: center;
@include basic-list-stats;
}
h3 {
......@@ -732,6 +742,7 @@ pre.light-well {
}
}
<<<<<<< HEAD
a.allowed-to-merge,
a.allowed-to-push {
cursor: pointer;
......@@ -745,6 +756,8 @@ a.allowed-to-push {
}
}
=======
>>>>>>> ce/master
.create-new-protected-branch-button {
@include dropdown-link;
......
......@@ -3,25 +3,6 @@
*
*/
.navbar-nav {
li {
.badge.todos-pending-count {
position: inherit;
top: -6px;
margin-top: -5px;
font-weight: normal;
background: $todo-alert-blue;
margin-left: -17px;
font-size: 11px;
color: $white-light;
padding: 3px;
padding-top: 1px;
padding-bottom: 1px;
border-radius: 3px;
}
}
}
.todos-list > .todo {
// workaround because we cannot use border-colapse
border-top: 1px solid transparent;
......
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))
ps_output, _ = Gitlab::Popen.popen(%W(ps ww -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
......
......@@ -16,10 +16,9 @@ class Admin::LabelsController < Admin::ApplicationController
end
def create
@label = Label.new(label_params)
@label.template = true
@label = Labels::CreateService.new(label_params).execute(template: true)
if @label.save
if @label.persisted?
redirect_to admin_labels_url, notice: "Label was created"
else
render :new
......@@ -27,7 +26,9 @@ class Admin::LabelsController < Admin::ApplicationController
end
def update
if @label.update(label_params)
@label = Labels::UpdateService.new(label_params).execute(@label)
if @label.valid?
redirect_to admin_labels_path, notice: 'label was successfully updated.'
else
render :edit
......
......@@ -26,7 +26,7 @@ class Groups::LabelsController < Groups::ApplicationController
end
def create
@label = @group.labels.create(label_params)
@label = Labels::CreateService.new(label_params).execute(group: group)
if @label.valid?
redirect_to group_labels_path(@group)
......@@ -40,7 +40,9 @@ class Groups::LabelsController < Groups::ApplicationController
end
def update
if @label.update_attributes(label_params)
@label = Labels::UpdateService.new(label_params).execute(@label)
if @label.valid?
redirect_back_or_group_labels_path
else
render :edit
......
......@@ -29,7 +29,7 @@ class Projects::LabelsController < Projects::ApplicationController
end
def create
@label = @project.labels.create(label_params)
@label = Labels::CreateService.new(label_params).execute(project: @project)
if @label.valid?
respond_to do |format|
......@@ -48,7 +48,9 @@ class Projects::LabelsController < Projects::ApplicationController
end
def update
if @label.update_attributes(label_params)
@label = Labels::UpdateService.new(label_params).execute(@label)
if @label.valid?
redirect_to namespace_project_labels_path(@project.namespace, @project)
else
render :edit
......
class GroupFinder
include Gitlab::Allowable
def initialize(current_user)
@current_user = current_user
end
def execute(*params)
group = Group.find_by(*params)
if can?(@current_user, :read_group, group)
group
else
nil
end
end
end
......@@ -310,4 +310,8 @@ module ApplicationHelper
def active_when(condition)
'active' if condition
end
def show_user_callout?
cookies[:user_callout_dismissed] == 'true'
end
end
......@@ -257,4 +257,19 @@ module IssuablesHelper
def selected_template(issuable)
params[:issuable_template] if issuable_templates(issuable).include?(params[:issuable_template])
end
def issuable_todo_button_data(issuable, todo, is_collapsed)
{
todo_text: "Add todo",
mark_text: "Mark done",
todo_icon: (is_collapsed ? icon('plus-square') : nil),
mark_icon: (is_collapsed ? icon('check-square', class: 'todo-undone') : nil),
issuable_id: issuable.id,
issuable_type: issuable.class.name.underscore,
url: namespace_project_todos_path(@project.namespace, @project),
delete_path: (dashboard_todo_path(todo) if todo),
placement: (is_collapsed ? 'left' : nil),
container: (is_collapsed ? 'body' : nil)
}
end
end
......@@ -46,6 +46,10 @@ class Blob < SimpleDelegator
text? && language && language.name == 'SVG'
end
def ipython_notebook?
text? && language && language.name == 'Jupyter Notebook'
end
def size_within_svg_limits?
size <= MAXIMUM_SVG_SIZE
end
......@@ -63,6 +67,8 @@ class Blob < SimpleDelegator
end
elsif image? || svg?
'image'
elsif ipython_notebook?
'notebook'
elsif text?
'text'
else
......
......@@ -105,6 +105,10 @@ class CommitStatus < ActiveRecord::Base
end
end
def locking_enabled?
status_changed?
end
def before_sha
pipeline.before_sha || Gitlab::Git::BLANK_SHA
end
......
......@@ -121,10 +121,10 @@ class Namespace < ActiveRecord::Base
# Move the namespace directory in all storages paths used by member projects
repository_storage_paths.each do |repository_storage_path|
# Ensure old directory exists before moving it
gitlab_shell.add_namespace(repository_storage_path, path_was)
gitlab_shell.add_namespace(repository_storage_path, full_path_was)
unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path)
Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{path}"
unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path)
Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}"
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
......@@ -132,8 +132,8 @@ class Namespace < ActiveRecord::Base
end
end
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
Gitlab::PagesTransfer.new.rename_namespace(path_was, path)
Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path)
Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
remove_exports!
......@@ -156,7 +156,7 @@ class Namespace < ActiveRecord::Base
def send_update_instructions
projects.each do |project|
project.send_move_instructions("#{path_was}/#{project.path}")
project.send_move_instructions("#{full_path_was}/#{project.path}")
end
end
......@@ -235,10 +235,10 @@ class Namespace < ActiveRecord::Base
old_repository_storage_paths.each do |repository_storage_path|
# Move namespace directory into trash.
# We will remove it later async
new_path = "#{path}+#{id}+deleted"
new_path = "#{full_path}+#{id}+deleted"
if gitlab_shell.mv_namespace(repository_storage_path, path, new_path)
message = "Namespace directory \"#{path}\" moved to \"#{new_path}\""
if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path)
message = "Namespace directory \"#{full_path}\" moved to \"#{new_path}\""
Gitlab::AppLogger.info message
# Remove namespace directroy async with delay so
......
......@@ -31,7 +31,7 @@ class PrometheusService < MonitoringService
def help
<<-MD.strip_heredoc
Retrieves the Kubernetes node metrics `container_cpu_usage_seconds_total`
Retrieves the Kubernetes node metrics `container_cpu_usage_seconds_total`
and `container_memory_usage_bytes` from the configured Prometheus server.
If you are not using [Auto-Deploy](https://docs.gitlab.com/ee/ci/autodeploy/index.html)
......@@ -74,8 +74,8 @@ class PrometheusService < MonitoringService
def calculate_reactive_cache(environment_slug)
return unless active? && project && !project.pending_delete?
memory_query = %{(sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"}) / count(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})) /1024/1024}
cpu_query = %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}) * 100}
memory_query = %{(sum(container_memory_usage_bytes{container_name!="POD",environment="#{environment_slug}"}) / count(container_memory_usage_bytes{container_name!="POD",environment="#{environment_slug}"})) /1024/1024}
cpu_query = %{sum(rate(container_cpu_usage_seconds_total{container_name!="POD",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name!="POD",environment="#{environment_slug}"}) * 100}
{
success: true,
......
class SystemNoteMetadata < ActiveRecord::Base
ICON_TYPES = %w[
commit merge confidentiality status label assignee cross_reference
<<<<<<< HEAD
title time_tracking branch milestone discussion task moved approvals
=======
title time_tracking branch milestone discussion task moved
>>>>>>> ce/master
].freeze
validates :note, presence: true
......
module Groups
class UpdateService < Groups::BaseService
def execute
reject_parent_id!
# check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != group.visibility_level
......@@ -26,5 +28,11 @@ module Groups
false
end
end
private
def reject_parent_id!
params.except!(:parent_id)
end
end
end
module Labels
class BaseService < ::BaseService
COLOR_NAME_TO_HEX = {
black: '#000000',
silver: '#C0C0C0',
gray: '#808080',
white: '#FFFFFF',
maroon: '#800000',
red: '#FF0000',
purple: '#800080',
fuchsia: '#FF00FF',
green: '#008000',
lime: '#00FF00',
olive: '#808000',
yellow: '#FFFF00',
navy: '#000080',
blue: '#0000FF',
teal: '#008080',
aqua: '#00FFFF',
orange: '#FFA500',
aliceblue: '#F0F8FF',
antiquewhite: '#FAEBD7',
aquamarine: '#7FFFD4',
azure: '#F0FFFF',
beige: '#F5F5DC',
bisque: '#FFE4C4',
blanchedalmond: '#FFEBCD',
blueviolet: '#8A2BE2',
brown: '#A52A2A',
burlywood: '#DEB887',
cadetblue: '#5F9EA0',
chartreuse: '#7FFF00',
chocolate: '#D2691E',
coral: '#FF7F50',
cornflowerblue: '#6495ED',
cornsilk: '#FFF8DC',
crimson: '#DC143C',
darkblue: '#00008B',
darkcyan: '#008B8B',
darkgoldenrod: '#B8860B',
darkgray: '#A9A9A9',
darkgreen: '#006400',
darkgrey: '#A9A9A9',
darkkhaki: '#BDB76B',
darkmagenta: '#8B008B',
darkolivegreen: '#556B2F',
darkorange: '#FF8C00',
darkorchid: '#9932CC',
darkred: '#8B0000',
darksalmon: '#E9967A',
darkseagreen: '#8FBC8F',
darkslateblue: '#483D8B',
darkslategray: '#2F4F4F',
darkslategrey: '#2F4F4F',
darkturquoise: '#00CED1',
darkviolet: '#9400D3',
deeppink: '#FF1493',
deepskyblue: '#00BFFF',
dimgray: '#696969',
dimgrey: '#696969',
dodgerblue: '#1E90FF',
firebrick: '#B22222',
floralwhite: '#FFFAF0',
forestgreen: '#228B22',
gainsboro: '#DCDCDC',
ghostwhite: '#F8F8FF',
gold: '#FFD700',
goldenrod: '#DAA520',
greenyellow: '#ADFF2F',
grey: '#808080',
honeydew: '#F0FFF0',
hotpink: '#FF69B4',
indianred: '#CD5C5C',
indigo: '#4B0082',
ivory: '#FFFFF0',
khaki: '#F0E68C',
lavender: '#E6E6FA',
lavenderblush: '#FFF0F5',
lawngreen: '#7CFC00',
lemonchiffon: '#FFFACD',
lightblue: '#ADD8E6',
lightcoral: '#F08080',
lightcyan: '#E0FFFF',
lightgoldenrodyellow: '#FAFAD2',
lightgray: '#D3D3D3',
lightgreen: '#90EE90',
lightgrey: '#D3D3D3',
lightpink: '#FFB6C1',
lightsalmon: '#FFA07A',
lightseagreen: '#20B2AA',
lightskyblue: '#87CEFA',
lightslategray: '#778899',
lightslategrey: '#778899',
lightsteelblue: '#B0C4DE',
lightyellow: '#FFFFE0',
limegreen: '#32CD32',
linen: '#FAF0E6',
mediumaquamarine: '#66CDAA',
mediumblue: '#0000CD',
mediumorchid: '#BA55D3',
mediumpurple: '#9370DB',
mediumseagreen: '#3CB371',
mediumslateblue: '#7B68EE',
mediumspringgreen: '#00FA9A',
mediumturquoise: '#48D1CC',
mediumvioletred: '#C71585',
midnightblue: '#191970',
mintcream: '#F5FFFA',
mistyrose: '#FFE4E1',
moccasin: '#FFE4B5',
navajowhite: '#FFDEAD',
oldlace: '#FDF5E6',
olivedrab: '#6B8E23',
orangered: '#FF4500',
orchid: '#DA70D6',
palegoldenrod: '#EEE8AA',
palegreen: '#98FB98',
paleturquoise: '#AFEEEE',
palevioletred: '#DB7093',
papayawhip: '#FFEFD5',
peachpuff: '#FFDAB9',
peru: '#CD853F',
pink: '#FFC0CB',
plum: '#DDA0DD',
powderblue: '#B0E0E6',
rosybrown: '#BC8F8F',
royalblue: '#4169E1',
saddlebrown: '#8B4513',
salmon: '#FA8072',
sandybrown: '#F4A460',
seagreen: '#2E8B57',
seashell: '#FFF5EE',
sienna: '#A0522D',
skyblue: '#87CEEB',
slateblue: '#6A5ACD',
slategray: '#708090',
slategrey: '#708090',
snow: '#FFFAFA',
springgreen: '#00FF7F',
steelblue: '#4682B4',
tan: '#D2B48C',
thistle: '#D8BFD8',
tomato: '#FF6347',
turquoise: '#40E0D0',
violet: '#EE82EE',
wheat: '#F5DEB3',
whitesmoke: '#F5F5F5',
yellowgreen: '#9ACD32',
rebeccapurple: '#663399'
}.freeze
def convert_color_name_to_hex
color = params[:color]
color_name = color.strip.downcase
return color if color_name.start_with?('#')
COLOR_NAME_TO_HEX[color_name.to_sym] || color
end
end
end
module Labels
class CreateService < Labels::BaseService
def initialize(params = {})
@params = params.dup.with_indifferent_access
end
# returns the created label
def execute(target_params)
params[:color] = convert_color_name_to_hex if params[:color].present?
project_or_group = target_params[:project] || target_params[:group]
if project_or_group.present?
project_or_group.labels.create(params)
elsif target_params[:template]
label = Label.new(params)
label.template = true
label.save
label
else
Rails.logger.warn("target_params should contain :project or :group or :template, actual value: #{target_params}")
end
end
end
end
......@@ -3,7 +3,7 @@ module Labels
def initialize(current_user, project, params = {})
@current_user = current_user
@project = project
@params = params.dup
@params = params.dup.with_indifferent_access
end
def execute(skip_authorization: false)
......@@ -28,7 +28,7 @@ module Labels
new_label = available_labels.find_by(title: title)
if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, project))
new_label = project.labels.create(params)
new_label = Labels::CreateService.new(params).execute(project: project)
end
new_label
......
module Labels
class UpdateService < Labels::BaseService
def initialize(params = {})
@params = params.dup.with_indifferent_access
end
# returns the updated label
def execute(label)
params[:color] = convert_color_name_to_hex if params[:color].present?
label.update(params)
label
end
end
end
......@@ -467,6 +467,7 @@ module SystemNoteService
body = "moved #{direction} #{cross_reference}"
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
<<<<<<< HEAD
end
# Called when the merge request is approved by user
......@@ -489,6 +490,8 @@ module SystemNoteService
body = "unapproved this merge request"
create_note(NoteSummary.new(noteable, noteable.project, user, body, action: 'approvals'))
=======
>>>>>>> ce/master
end
private
......
.js-projects-list-holder
- if @projects.any?
%ul.projects-list.content-list
%ul.projects-list.content-list.admin-projects
- @projects.each_with_index do |project|
%li.project-row
%li.project-row{ class: ('no-description' if project.description.blank?) }
.controls
- if project.archived
%span.label.label-warning archived
%span.badge
= storage_counter(project.statistics.storage_size)
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn"
= link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove"
.stats
%span.badge
= storage_counter(project.statistics.storage_size)
- if project.archived
%span.label.label-warning archived
.title
= link_to [:admin, project.namespace.becomes(Namespace), project] do
.dash-project-avatar
......@@ -20,7 +21,7 @@
- if project.namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
%span.project-name
= project.name
- if project.description.present?
......
......@@ -4,7 +4,9 @@
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
.user-callout{ 'callout-svg' => custom_icon('icon_customization') }
- unless show_user_callout?
= render 'shared/user_callout'
- if @projects.any? || params[:name]
= render 'dashboard/projects_head'
......
......@@ -8,7 +8,7 @@
.discussion.js-toggle-container{ class: discussion.id, data: { discussion_id: discussion.id } }
.discussion-header
.discussion-actions
= link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do
%button.note-action-button.discussion-toggle-button.js-toggle-button{ type: "button" }
- if expanded
= icon("chevron-up")
- else
......
......@@ -11,9 +11,6 @@
= render 'layouts/nav/dashboard'
- else
= render 'layouts/nav/explore'
%button.navbar-toggle{ type: 'button' }
%span.sr-only Toggle navigation
= icon('ellipsis-v')
.header-logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
......@@ -38,10 +35,20 @@
%li
= link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw')
%li
= link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('hashtag fw')
%span.badge.issues-count
= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))
%li
= link_to assigned_mrs_dashboard_path, title: 'Merge requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= custom_icon('mr_bold')
%span.badge.merge-requests-count
= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))
%li
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
= icon('check-circle fw')
%span.badge.todos-count
= todos_count_format(todos_pending_count)
- if current_user.can_create_project?
%li
......@@ -76,6 +83,10 @@
%div
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
%button.navbar-toggle{ type: 'button' }
%span.sr-only Toggle navigation
= icon('ellipsis-v')
= yield :header_content
= render 'shared/outdated_browser'
......
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('notebook_viewer')
.file-content#js-notebook-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } }
......@@ -24,7 +24,7 @@
.visible-xs-inline
= render_commit_status(commit, ref: ref)
- if commit.description?
%a.text-expander.hidden-xs.js-toggle-button ...
%button.text-expander.hidden-xs.js-toggle-button{ type: "button" } ...
- if commit.description?
%pre.commit-row-description.js-toggle-content
......
.js-toggle-container
.commit-stat-summary
Showing
= link_to '#', class: 'js-toggle-button' do
%button.diff-stats-summary-toggler.js-toggle-button{ type: "button" }
%strong= pluralize(diff_files.size, "changed file")
with
%strong.cgreen #{diff_files.sum(&:added_lines)} additions
......
......@@ -45,6 +45,7 @@
= link_to icon('question-circle'), help_page_path('user/project/merge_requests/squash_and_merge'), title: 'About this feature', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'}
.accept-control
<<<<<<< HEAD
- if @project.merge_requests_ff_only_enabled
Fast-forward merge without a merge commit
- else
......@@ -58,5 +59,16 @@
message_without_description: @merge_request.merge_commit_message,
text: @merge_request.merge_commit_message,
rows: 14, hint: true
=======
%button.modify-merge-commit-link.js-toggle-button{ type: "button" }
= icon('edit')
Modify commit message
.js-toggle-content.hide.prepend-top-default
= render 'shared/commit_message_container', params: params,
message_with_description: @merge_request.merge_commit_message(include_description: true),
message_without_description: @merge_request.merge_commit_message,
text: @merge_request.merge_commit_message,
rows: 14, hint: true
>>>>>>> ce/master
= hidden_field_tag :merge_when_pipeline_succeeds, "", autocomplete: "off"
......@@ -76,7 +76,7 @@
Gitea
%div
- if git_import_enabled?
= link_to "#", class: 'btn js-toggle-button import_git' do
%button.btn.js-toggle-button.import_git{ type: "button" }
= icon('git', text: 'Repo by URL')
.import_gitlab_project
- if gitlab_project_import_enabled?
......
......@@ -72,6 +72,7 @@
Milestones
%span.badge
= @search_results.milestones_count
<<<<<<< HEAD
- if current_application_settings.elasticsearch_search?
%li{ class: ("active" if @scope == 'blobs') }
= link_to search_filter_path(scope: 'blobs') do
......@@ -83,3 +84,5 @@
Commits
%span.badge
= @search_results.commits_count
=======
>>>>>>> ce/master
- parent = Group.find_by(id: params[:parent_id] || @group.parent_id)
- parent = GroupFinder.new(current_user).execute(id: params[:parent_id] || @group.parent_id)
- group_path = root_url
- group_path << parent.full_path + '/' if parent
- if @group.persisted?
......
.user-callout
.bordered-box.landing.content-block
%button.btn.btn-default.close.js-close-callout{ type: 'button',
'aria-label' => 'Dismiss customize experience box' }
= icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true')
.row
.col-sm-3.col-xs-12.svg-container
= custom_icon('icon_customization')
.col-sm-8.col-xs-12.inner-content
%h4
Customize your experience
%p
Change syntax themes, default project pages, and more in preferences.
= link_to 'Check it out', profile_preferences_path, class: 'btn btn-default js-close-callout'
<svg width="15" height="20" viewBox="0 0 12 14" xmlns="http://www.w3.org/2000/svg"><path d="M1 4.967a2.15 2.15 0 1 1 2.3 0v5.066a2.15 2.15 0 1 1-2.3 0V4.967zm7.85 5.17V5.496c0-.745-.603-1.346-1.35-1.346V6l-3-3 3-3v1.85c2.016 0 3.65 1.63 3.65 3.646v4.45a2.15 2.15 0 1 1-2.3.191z" fill-rule="nonzero"/></svg>
......@@ -29,7 +29,7 @@
%button.btn.btn-link
= icon('search')
%span
Keep typing and press Enter
Press Enter or click to search
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link
......
......@@ -13,15 +13,12 @@
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
= sidebar_gutter_toggle_icon
- if current_user
%button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", "aria-label" => (todo.nil? ? "Add todo" : "Mark done"), data: { todo_text: "Add todo", mark_text: "Mark done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } }
%span.js-issuable-todo-text
- if todo
Mark done
- else
Add todo
= icon('spin spinner', class: 'hidden js-issuable-todo-loading', 'aria-hidden': 'true')
= render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
- if current_user
.block.todo.hide-expanded
= render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable, is_collapsed: true
.block.assignee
.sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) }
- if issuable.assignee
......@@ -121,7 +118,7 @@
- selected_labels = issuable.labels
.block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } }
= icon('tags', class: 'hidden', 'aria-hidden': 'true')
= icon('tags', 'aria-hidden': 'true')
%span
= selected_labels.size
.title.hide-collapsed
......
- is_collapsed = local_assigns.fetch(:is_collapsed, false)
- mark_content = is_collapsed ? icon('check-square', class: 'todo-undone') : 'Mark done'
- todo_content = is_collapsed ? icon('plus-square') : 'Add todo'
%button.issuable-todo-btn.js-issuable-todo{ type: 'button',
class: (is_collapsed ? 'btn-blank sidebar-collapsed-icon dont-change-state has-tooltip' : 'btn btn-default issuable-header-btn pull-right'),
title: (todo.nil? ? 'Add todo' : 'Mark done'),
'aria-label' => (todo.nil? ? 'Add todo' : 'Mark done'),
data: issuable_todo_button_data(issuable, todo, is_collapsed) }
%span.issuable-todo-inner.js-issuable-todo-inner<
- if todo
= mark_content
- else
= todo_content
= icon('spin spinner', 'aria-hidden': 'true')
......@@ -85,6 +85,7 @@
Closed:
= milestone.issues_visible_to_user(current_user).closed.count
<<<<<<< HEAD
- total_weight = milestone.issues_visible_to_user(current_user).sum(:weight)
.block.weight
.sidebar-collapsed-icon
......@@ -102,6 +103,8 @@
- else
.no-value None
=======
>>>>>>> ce/master
.block
.sidebar-collapsed-icon
%strong
......
......@@ -24,7 +24,7 @@
- if project.namespace && !skip_namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
%span.project-name
= project.name
- if show_last_commit_as_description
......
......@@ -18,9 +18,9 @@
= event_action_name(event)
%strong
- if event.note?
= link_to event.note_target.to_reference, event_note_target_path(event)
= link_to event.note_target.to_reference, event_note_target_path(event), class: 'has-tooltip', title: event.target_title
- elsif event.target
= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target]
= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
at
%strong
......
......@@ -100,8 +100,8 @@
Snippets
%div{ class: container_class }
- if @user == current_user
.user-callout{ 'callout-svg' => custom_icon('icon_customization') }
- if @user == current_user && !show_user_callout?
= render 'shared/user_callout'
.tab-content
#activity.tab-pane
.row-content-block.calender-block.white.second-block.hidden-xs
......
......@@ -4,20 +4,16 @@ class PostReceive
extend Gitlab::CurrentSettings
def perform(repo_path, identifier, changes)
if repository_storage = Gitlab.config.repositories.storages.find { |p| repo_path.start_with?(p[1]['path'].to_s) }
repo_path.gsub!(repository_storage[1]['path'].to_s, "")
else
log("Check gitlab.yml config for correct repositories.storages values. No repository storage path matches \"#{repo_path}\"")
end
repo_relative_path = Gitlab::RepoPath.strip_storage_path(repo_path)
changes = Base64.decode64(changes) unless changes.include?(' ')
# Use Sidekiq.logger so arguments can be correlated with execution
# time and thread ID's.
Sidekiq.logger.info "changes: #{changes.inspect}" if ENV['SIDEKIQ_LOG_ARGUMENTS']
post_received = Gitlab::GitPostReceive.new(repo_path, identifier, changes)
post_received = Gitlab::GitPostReceive.new(repo_relative_path, identifier, changes)
if post_received.project.nil?
log("Triggered hook for non-existing project with full path \"#{repo_path}\"")
log("Triggered hook for non-existing project with full path \"#{repo_relative_path}\"")
return false
end
......@@ -41,7 +37,7 @@ class PostReceive
process_project_changes(post_received)
else
log("Triggered hook for unidentifiable repository type with full path \"#{repo_path}\"")
log("Triggered hook for unidentifiable repository type with full path \"#{repo_relative_path}\"")
false
end
end
......
---
title: Update rugged to 0.25.1.1
merge_request: 10286
author: Elan Ruusamäe
---
title: Resolve "404 when requesting build trace"
merge_request: 9759
author: dosuken123
---
title: Fix API group/issues default state filter
merge_request:
author: Alexander Randa
---
title: Add metadata to system notes
<<<<<<< HEAD
merge_request: 1526
=======
merge_request: 9964
>>>>>>> ce/master
author:
---
title: Remove duplicated tokens in issuable search bar
merge_request:
author:
---
title: Labels support color names in backend
merge_request: 9725
author: Dongqing Hu
---
title: Update toggle buttons to be <button>
merge_request:
author:
---
title: Change hint on first row of filters dropdown to `Press Enter or click to search`
merge_request: 10138
author:
---
title: fix sidebar padding for build and wiki pages
merge_request:
author:
---
title: Correctly update paths when changing a child group
merge_request:
author:
---
title: Add shortcuts and counters to MRs and issues in navbar
merge_request:
author:
---
title: consistent icons in vue and kaminari pagers
merge_request:
author:
---
title: Fix escaped html appearing in milestone page
merge_request: 10224
author:
---
title: Improve Markdown rendering when a lot of merge requests are referenced
merge_request: 10252
author:
---
title: Add tooltip to user's calendar activities
merge_request: 10123
author: Alex Argunov
---
title: adds todo functionality to closed issuable sidebar and changes todo bell icon
to check-square
merge_request:
author:
---
title: Fix layout of projects page on admin area
merge_request:
author:
---
title: Fix after_script processing for Runners APIv4
merge_request: 10185
author:
---
title: Fix environment folder route when special chars present in environment name
merge_request: 10250
author:
---
title: Fix bug that caused jobs that already had been retried to be retried again
when retrying a pipeline
merge_request: 10249
author:
---
title: Force unlimited terminal size when checking processes via call to ps
merge_request: 10246
author: Sebastian Reitenbach
---
title: Fixed private group name disclosure via new/update forms
merge_request:
author:
---
title: Optimize labels finder query when searching for a project with a group
merge_request:
author: mhasbini
---
title: Make CI build to use optimistic locking only on status change
merge_request:
author:
---
title: Make user mentions case-insensitive
merge_request: 10285
author: blackst0ne
---
title: Simplify search queries for projects and merge requests
merge_request: 10053
author: mhasbini
......@@ -530,14 +530,10 @@ production: &base
# Gitaly settings
gitaly:
# The socket_path setting is optional and obsolete. When this is set
# GitLab assumes it can reach a Gitaly services via a Unix socket at
# this path. When this is commented out GitLab will not use Gitaly.
#
# This setting is obsolete because we expect it to be moved under
# repositories/storages in GitLab 9.1.
#
# socket_path: tmp/sockets/private/gitaly.socket
# This setting controls whether GitLab uses Gitaly (new component
# introduced in 9.0). Eventually Gitaly use will become mandatory and
# this option will disappear.
enabled: false
#
# 4. Advanced settings
......@@ -552,6 +548,7 @@ production: &base
storages: # You must have at least a `default` storage path.
default:
path: /home/git/repositories/
gitaly_address: unix:/home/git/gitlab/tmp/sockets/private/gitaly.socket
## Backup settings
backup:
......@@ -660,10 +657,15 @@ test:
# In order to setup it correctly you need to specify
# your system username you use to run GitLab
# user: YOUR_USERNAME
pages:
path: tmp/tests/pages
repositories:
storages:
default:
path: tmp/tests/repositories/
gitaly_address: unix:<%= Rails.root.join('tmp/sockets/private/gitaly.socket') %>
gitaly:
enabled: false
backup:
path: tmp/tests/backups
gitlab_shell:
......
......@@ -10,6 +10,10 @@
# end
#
ActiveSupport::Inflector.inflections do |inflect|
<<<<<<< HEAD
inflect.uncountable %w(award_emoji project_statistics project_registry file_registry system_note_metadata)
inflect.acronym 'EE'
=======
inflect.uncountable %w(award_emoji project_statistics system_note_metadata)
>>>>>>> ce/master
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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