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: ...@@ -15,10 +15,15 @@ variables:
GIT_DEPTH: "20" GIT_DEPTH: "20"
PHANTOMJS_VERSION: "2.1.1" PHANTOMJS_VERSION: "2.1.1"
GET_SOURCES_ATTEMPTS: "3" GET_SOURCES_ATTEMPTS: "3"
<<<<<<< HEAD
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-${CI_COMMIT_REF_SLUG}.json 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 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 # This hack is needed to make ES not that memory hungry
ES_JAVA_OPTS: "-Xms600m -Xmx600m" 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: before_script:
- source ./scripts/prepare_build.sh - source ./scripts/prepare_build.sh
...@@ -278,14 +283,35 @@ rake karma: ...@@ -278,14 +283,35 @@ rake karma:
paths: paths:
- coverage-javascript/ - coverage-javascript/
lint-doc: docs:check:apilint:
image: "phusion/baseimage"
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
image: "phusion/baseimage:latest" variables:
GIT_DEPTH: "3"
cache: {}
dependencies: []
before_script: [] before_script: []
script: script:
- scripts/lint-doc.sh - 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: bundler:check:
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
......
...@@ -314,9 +314,12 @@ request is as follows: ...@@ -314,9 +314,12 @@ request is as follows:
organized commits by [squashing them][git-squash] organized commits by [squashing them][git-squash]
1. Push the commit(s) to your fork 1. Push the commit(s) to your fork
1. Submit a merge request (MR) to the `master` branch 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 but feel free to require more.
1. Your merge request needs at least 1 approval For instance if you're touching backend and frontend code, it's a good idea
1. You don't have to select any approvers 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 title should describe the change you want to make
1. The MR description should give a motive for your change and the method you 1. The MR description should give a motive for your change and the method you
used to achieve it. used to achieve it.
...@@ -376,7 +379,7 @@ There are a few rules to get your merge request accepted: ...@@ -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 1. If your merge request includes only frontend changes [^1], it must be
**approved by a [frontend maintainer][team]**. **approved by a [frontend maintainer][team]**.
1. If your merge request includes frontend and backend changes [^1], it must 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 1. To lower the amount of merge requests maintainers need to review, you can
ask or assign any [reviewers][team] for a first review. 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 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 ...@@ -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 [GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
[polling-etag]: https://docs.gitlab.com/ce/development/polling.html [polling-etag]: https://docs.gitlab.com/ce/development/polling.html
[^1]: Specs other than JavaScript specs are considered backend code. Haml [^1]: Please note that specs other than JavaScript specs are considered backend
changes are considered backend code if they include Ruby code other than just code.
pure HTML.
...@@ -15,7 +15,7 @@ gem 'default_value_for', '~> 3.0.0' ...@@ -15,7 +15,7 @@ gem 'default_value_for', '~> 3.0.0'
gem 'mysql2', '~> 0.3.16', group: :mysql gem 'mysql2', '~> 0.3.16', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.24.0' gem 'rugged', '~> 0.25.1.1'
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.2' gem 'devise', '~> 4.2'
...@@ -65,7 +65,7 @@ gem 'net-ldap' ...@@ -65,7 +65,7 @@ gem 'net-ldap'
# Git Wiki # Git Wiki
# Required manually in config/initializers/gollum.rb to control load order # Required manually in config/initializers/gollum.rb to control load order
gem 'gollum-lib', '~> 4.2', require: false 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 # Language detection
gem 'github-linguist', '~> 4.7.0', require: 'linguist' gem 'github-linguist', '~> 4.7.0', require: 'linguist'
......
...@@ -321,9 +321,9 @@ GEM ...@@ -321,9 +321,9 @@ GEM
rouge (~> 2.0) rouge (~> 2.0)
sanitize (~> 2.1.0) sanitize (~> 2.1.0)
stringex (~> 2.5.1) stringex (~> 2.5.1)
gollum-rugged_adapter (0.4.2) gollum-rugged_adapter (0.4.4)
mime-types (>= 1.15) mime-types (>= 1.15)
rugged (~> 0.24.0, >= 0.21.3) rugged (~> 0.25)
gon (6.1.0) gon (6.1.0)
actionpack (>= 3.0) actionpack (>= 3.0)
json json
...@@ -718,7 +718,7 @@ GEM ...@@ -718,7 +718,7 @@ GEM
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.1) rubyzip (1.2.1)
rufus-scheduler (3.1.10) rufus-scheduler (3.1.10)
rugged (0.24.0) rugged (0.25.1.1)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -948,7 +948,7 @@ DEPENDENCIES ...@@ -948,7 +948,7 @@ DEPENDENCIES
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.5.1)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2) gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6) google-api-client (~> 0.8.6)
grape (~> 0.19.0) grape (~> 0.19.0)
...@@ -1032,7 +1032,7 @@ DEPENDENCIES ...@@ -1032,7 +1032,7 @@ DEPENDENCIES
rubocop-rspec (~> 1.12.0) rubocop-rspec (~> 1.12.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2) ruby-prof (~> 0.16.2)
rugged (~> 0.24.0) rugged (~> 0.25.1.1)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 5.0.6) sass-rails (~> 5.0.6)
scss_lint (~> 0.47.0) scss_lint (~> 0.47.0)
......
<<<<<<< HEAD
8.18.0-ee-pre 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/ // On Windows, flags render as two-letter country codes, see http://emojipedia.org/flags/
const flagACodePoint = 127462; // parseInt('1F1E6', 16) const flagACodePoint = 127462; // parseInt('1F1E6', 16)
const flagZCodePoint = 127487; // parseInt('1F1FF', 16) const flagZCodePoint = 127487; // parseInt('1F1FF', 16)
...@@ -20,7 +18,7 @@ function isKeycapEmoji(emojiUnicode) { ...@@ -20,7 +18,7 @@ function isKeycapEmoji(emojiUnicode) {
const tone1 = 127995;// parseInt('1F3FB', 16) const tone1 = 127995;// parseInt('1F3FB', 16)
const tone5 = 127999;// parseInt('1F3FF', 16) const tone5 = 127999;// parseInt('1F3FF', 16)
function isSkinToneComboEmoji(emojiUnicode) { 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); const cp = char.codePointAt(0);
return cp >= tone1 && cp <= tone5; return cp >= tone1 && cp <= tone5;
}); });
...@@ -30,7 +28,7 @@ function isSkinToneComboEmoji(emojiUnicode) { ...@@ -30,7 +28,7 @@ function isSkinToneComboEmoji(emojiUnicode) {
// doesn't support the skin tone versions of horse racing // doesn't support the skin tone versions of horse racing
const horseRacingCodePoint = 127943;// parseInt('1F3C7', 16) const horseRacingCodePoint = 127943;// parseInt('1F3C7', 16)
function isHorceRacingSkinToneComboEmoji(emojiUnicode) { function isHorceRacingSkinToneComboEmoji(emojiUnicode) {
return spreadString(emojiUnicode)[0].codePointAt(0) === horseRacingCodePoint && return Array.from(emojiUnicode)[0].codePointAt(0) === horseRacingCodePoint &&
isSkinToneComboEmoji(emojiUnicode); isSkinToneComboEmoji(emojiUnicode);
} }
...@@ -42,7 +40,7 @@ const personEndCodePoint = 128105; // parseInt('1F469', 16) ...@@ -42,7 +40,7 @@ const personEndCodePoint = 128105; // parseInt('1F469', 16)
function isPersonZwjEmoji(emojiUnicode) { function isPersonZwjEmoji(emojiUnicode) {
let hasPersonEmoji = false; let hasPersonEmoji = false;
let hasZwj = false; let hasZwj = false;
spreadString(emojiUnicode).forEach((character) => { Array.from(emojiUnicode).forEach((character) => {
const cp = character.codePointAt(0); const cp = character.codePointAt(0);
if (cp === zwj) { if (cp === zwj) {
hasZwj = true; 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 @@ ...@@ -18,7 +18,7 @@
// Button does not change visibility. If button has icon - it changes chevron style. // Button does not change visibility. If button has icon - it changes chevron style.
// //
// %div.js-toggle-container // %div.js-toggle-container
// %a.js-toggle-button // %button.js-toggle-button
// %div.js-toggle-content // %div.js-toggle-content
// //
$('body').on('click', '.js-toggle-button', function(e) { $('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 { ...@@ -50,9 +50,7 @@ export default {
this.showDetail = false; this.showDetail = false;
}, },
showIssue(e) { showIssue(e) {
const targetTagName = e.target.tagName.toLowerCase(); if (e.target.classList.contains('js-no-trigger')) return;
if (targetTagName === 'a' || targetTagName === 'button') return;
if (this.showDetail) { if (this.showDetail) {
this.showDetail = false; this.showDetail = false;
......
...@@ -84,20 +84,20 @@ import eventHub from '../eventhub'; ...@@ -84,20 +84,20 @@ import eventHub from '../eventhub';
#{{ issue.id }} #{{ issue.id }}
</span> </span>
<a <a
class="card-assignee has-tooltip" class="card-assignee has-tooltip js-no-trigger"
:href="rootPath + issue.assignee.username" :href="rootPath + issue.assignee.username"
:title="'Assigned to ' + issue.assignee.name" :title="'Assigned to ' + issue.assignee.name"
v-if="issue.assignee" v-if="issue.assignee"
data-container="body"> data-container="body">
<img <img
class="avatar avatar-inline s20" class="avatar avatar-inline s20 js-no-trigger"
:src="issue.assignee.avatar" :src="issue.assignee.avatar"
width="20" width="20"
height="20" height="20"
:alt="'Avatar for ' + issue.assignee.name" /> :alt="'Avatar for ' + issue.assignee.name" />
</a> </a>
<button <button
class="label color-label has-tooltip" class="label color-label has-tooltip js-no-trigger"
v-for="label in issue.labels" v-for="label in issue.labels"
type="button" type="button"
v-if="showLabel(label)" v-if="showLabel(label)"
......
...@@ -33,12 +33,11 @@ export default Vue.component('pipelines-table', { ...@@ -33,12 +33,11 @@ export default Vue.component('pipelines-table', {
* @return {Object} * @return {Object}
*/ */
data() { data() {
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
const store = new PipelineStore(); const store = new PipelineStore();
return { return {
endpoint: pipelinesTableData.endpoint, endpoint: null,
helpPagePath: pipelinesTableData.helpPagePath, helpPagePath: null,
store, store,
state: store.state, state: store.state,
isLoading: false, isLoading: false,
...@@ -65,6 +64,8 @@ export default Vue.component('pipelines-table', { ...@@ -65,6 +64,8 @@ export default Vue.component('pipelines-table', {
* *
*/ */
beforeMount() { beforeMount() {
this.endpoint = this.$el.dataset.endpoint;
this.helpPagePath = this.$el.dataset.helpPagePath;
this.service = new PipelinesService(this.endpoint); this.service = new PipelinesService(this.endpoint);
this.fetchPipelines(); this.fetchPipelines();
......
...@@ -34,8 +34,11 @@ ...@@ -34,8 +34,11 @@
/* global Labels */ /* global Labels */
/* global Shortcuts */ /* global Shortcuts */
/* global Sidebar */ /* global Sidebar */
<<<<<<< HEAD
/* global WeightSelect */ /* global WeightSelect */
/* global AdminEmailSelect */ /* global AdminEmailSelect */
=======
>>>>>>> ce/master
import Issue from './issue'; import Issue from './issue';
......
...@@ -8,21 +8,31 @@ require('./filtered_search_token_keys'); ...@@ -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) // 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 tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokens = []; const tokens = [];
const tokenIndexes = []; // stores key+value for simple search
let lastToken = null; let lastToken = null;
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => { const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
let tokenValue = v1 || v2 || v3; let tokenValue = v1 || v2 || v3;
let tokenSymbol = symbol; let tokenSymbol = symbol;
let tokenIndex = '';
if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') { if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
tokenSymbol = tokenValue; tokenSymbol = tokenValue;
tokenValue = ''; tokenValue = '';
} }
tokenIndex = `${key}:${tokenValue}`;
// Prevent adding duplicates
if (tokenIndexes.indexOf(tokenIndex) === -1) {
tokenIndexes.push(tokenIndex);
tokens.push({ tokens.push({
key, key,
value: tokenValue || '', value: tokenValue || '',
symbol: tokenSymbol || '', symbol: tokenSymbol || '',
}); });
}
return ''; return '';
}).replace(/\s{2,}/g, ' ').trim() || ''; }).replace(/\s{2,}/g, ' ').trim() || '';
......
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var */ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var */
$(document).on('todo:toggle', function(e, count) { $(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count'); var $todoPendingCount = $('.todos-count');
$todoPendingCount.text(gl.text.highCountTrim(count)); $todoPendingCount.text(gl.text.highCountTrim(count));
$todoPendingCount.toggleClass('hidden', count === 0); $todoPendingCount.toggleClass('hidden', count === 0);
}); });
...@@ -5,23 +5,37 @@ import httpStatusCodes from './http_status'; ...@@ -5,23 +5,37 @@ import httpStatusCodes from './http_status';
* Service for vue resouce and method need to be provided as props * Service for vue resouce and method need to be provided as props
* *
* @example * @example
* new poll({ * new Poll({
* resource: resource, * resource: resource,
* method: 'name', * method: 'name',
* data: {page: 1, scope: 'all'}, * data: {page: 1, scope: 'all'}, // optional
* successCallback: () => {}, * successCallback: () => {},
* errorCallback: () => {}, * errorCallback: () => {},
* notificationCallback: () => {}, // optional
* }).makeRequest(); * }).makeRequest();
* *
* this.service = new BoardsService(endpoint); * Usage in pipelines table with visibility lib:
* new poll({ *
* const poll = new Poll({
* resource: this.service, * resource: this.service,
* method: 'get', * method: 'getPipelines',
* data: {page: 1, scope: 'all'}, * data: { page: pageNumber, scope },
* successCallback: () => {}, * successCallback: this.successCallback,
* errorCallback: () => {}, * errorCallback: this.errorCallback,
* }).makeRequest(); * 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 * 1. Checks for response and headers before start polling
* 2. Interval is provided by `Poll-Interval` header. * 2. Interval is provided by `Poll-Interval` header.
...@@ -34,6 +48,8 @@ export default class Poll { ...@@ -34,6 +48,8 @@ export default class Poll {
constructor(options = {}) { constructor(options = {}) {
this.options = options; this.options = options;
this.options.data = options.data || {}; this.options.data = options.data || {};
this.options.notificationCallback = options.notificationCallback ||
function notificationCallback() {};
this.intervalHeader = 'POLL-INTERVAL'; this.intervalHeader = 'POLL-INTERVAL';
this.timeoutID = null; this.timeoutID = null;
...@@ -42,7 +58,7 @@ export default class Poll { ...@@ -42,7 +58,7 @@ export default class Poll {
checkConditions(response) { checkConditions(response) {
const headers = gl.utils.normalizeHeaders(response.headers); 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) { if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
this.timeoutID = setTimeout(() => { this.timeoutID = setTimeout(() => {
...@@ -54,7 +70,10 @@ export default class Poll { ...@@ -54,7 +70,10 @@ export default class Poll {
} }
makeRequest() { 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) return resource[method](data)
.then(response => this.checkConditions(response)) .then(response => this.checkConditions(response))
...@@ -70,4 +89,12 @@ export default class Poll { ...@@ -70,4 +89,12 @@ export default class Poll {
this.canPoll = false; this.canPoll = false;
clearTimeout(this.timeoutID); clearTimeout(this.timeoutID);
} }
/**
* Restarts polling after it has been stoped
*/
restart() {
this.canPoll = true;
this.makeRequest();
}
} }
...@@ -4,8 +4,10 @@ ...@@ -4,8 +4,10 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
require('./breakpoints'); import CommitPipelinesTable from './commit/pipelines/pipelines_table';
require('./flash');
import './breakpoints';
import './flash';
/* eslint-disable max-len */ /* eslint-disable max-len */
// MergeRequestTabs // MergeRequestTabs
...@@ -97,6 +99,13 @@ require('./flash'); ...@@ -97,6 +99,13 @@ require('./flash');
.off('click', this.clickTab); .off('click', this.clickTab);
} }
destroy() {
this.unbindEvents();
if (this.commitPipelinesTable) {
this.commitPipelinesTable.$destroy();
}
}
showTab(e) { showTab(e) {
e.preventDefault(); e.preventDefault();
this.activateTab($(e.target).data('action')); this.activateTab($(e.target).data('action'));
...@@ -128,12 +137,8 @@ require('./flash'); ...@@ -128,12 +137,8 @@ require('./flash');
this.expandViewContainer(); this.expandViewContainer();
} }
} else if (action === 'pipelines') { } else if (action === 'pipelines') {
if (this.pipelinesLoaded) { this.resetViewContainer();
return; this.loadPipelines();
}
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl);
this.pipelinesLoaded = true;
} else { } else {
this.expandView(); this.expandView();
this.resetViewContainer(); this.resetViewContainer();
...@@ -222,6 +227,18 @@ require('./flash'); ...@@ -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) { loadDiff(source) {
if (this.diffsLoaded) { if (this.diffsLoaded) {
return; return;
......
...@@ -56,14 +56,15 @@ import Cookies from 'js-cookie'; ...@@ -56,14 +56,15 @@ import Cookies from 'js-cookie';
Sidebar.prototype.toggleTodo = function(e) { Sidebar.prototype.toggleTodo = function(e) {
var $btnText, $this, $todoLoading, ajaxType, url; var $btnText, $this, $todoLoading, ajaxType, url;
$this = $(e.currentTarget); $this = $(e.currentTarget);
$todoLoading = $('.js-issuable-todo-loading');
$btnText = $('.js-issuable-todo-text', $this);
ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST'; ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST';
if ($this.attr('data-delete-path')) { if ($this.attr('data-delete-path')) {
url = "" + ($this.attr('data-delete-path')); url = "" + ($this.attr('data-delete-path'));
} else { } else {
url = "" + ($this.data('url')); url = "" + ($this.data('url'));
} }
$this.tooltip('hide');
return $.ajax({ return $.ajax({
url: url, url: url,
type: ajaxType, type: ajaxType,
...@@ -74,34 +75,44 @@ import Cookies from 'js-cookie'; ...@@ -74,34 +75,44 @@ import Cookies from 'js-cookie';
}, },
beforeSend: (function(_this) { beforeSend: (function(_this) {
return function() { return function() {
return _this.beforeTodoSend($this, $todoLoading); $('.js-issuable-todo').disable()
.addClass('is-loading');
}; };
})(this) })(this)
}).done((function(_this) { }).done((function(_this) {
return function(data) { return function(data) {
return _this.todoUpdateDone(data, $this, $btnText, $todoLoading); return _this.todoUpdateDone(data);
}; };
})(this)); })(this));
}; };
Sidebar.prototype.beforeTodoSend = function($btn, $todoLoading) { Sidebar.prototype.todoUpdateDone = function(data) {
$btn.disable(); const deletePath = data.delete_path ? data.delete_path : null;
return $todoLoading.removeClass('hidden'); const attrPrefix = deletePath ? 'mark' : 'todo';
}; const $todoBtns = $('.js-issuable-todo');
Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
$(document).trigger('todo:toggle', data.count); $(document).trigger('todo:toggle', data.count);
$btn.enable(); $todoBtns.each((i, el) => {
$todoLoading.addClass('hidden'); const $el = $(el);
const $elText = $el.find('.js-issuable-todo-inner');
$el.removeClass('is-loading')
.enable()
.attr('aria-label', $el.data(`${attrPrefix}-text`))
.attr('data-delete-path', deletePath)
.attr('title', $el.data(`${attrPrefix}-text`));
if (data.delete_path != null) { if ($el.hasClass('has-tooltip')) {
$btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path); $el.tooltip('fixTitle');
return $btnText.text($btn.data('mark-text')); }
if ($el.data(`${attrPrefix}-icon`)) {
$elText.html($el.data(`${attrPrefix}-icon`));
} else { } else {
$btn.attr('aria-label', $btn.data('todo-text')).removeAttr('data-delete-path'); $elText.text($el.data(`${attrPrefix}-text`));
return $btnText.text($btn.data('todo-text'));
} }
});
}; };
Sidebar.prototype.sidebarDropdownLoading = function(e) { Sidebar.prototype.sidebarDropdownLoading = function(e) {
......
import Cookies from 'js-cookie'; 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_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 { export default class UserCallout {
constructor() { constructor() {
this.isCalloutDismissed = Cookies.get(USER_CALLOUT_COOKIE); this.isCalloutDismissed = Cookies.get(USER_CALLOUT_COOKIE);
this.userCalloutBody = $(userCalloutElementName); this.userCalloutBody = $('.user-callout');
this.userCalloutSvg = $(userCalloutElementName).attr(userCalloutSvgAttrName);
$(userCalloutElementName).removeAttr(userCalloutSvgAttrName);
this.init(); this.init();
} }
init() { init() {
const $template = $(USER_CALLOUT_TEMPLATE);
if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') { if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') {
$template.find('.svg-container').append(this.userCalloutSvg); $('.js-close-callout').on('click', e => this.dismissCallout(e));
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();
} }
} }
dismissCallout(e) { dismissCallout(e) {
Cookies.set(USER_CALLOUT_COOKIE, 'true');
const $currentTarget = $(e.currentTarget); const $currentTarget = $(e.currentTarget);
if ($currentTarget.hasClass('close-user-callout')) {
Cookies.set(USER_CALLOUT_COOKIE, 'true');
if ($currentTarget.hasClass('close')) {
this.userCalloutBody.remove(); this.userCalloutBody.remove();
} }
} }
......
...@@ -3,8 +3,8 @@ const UI_LIMIT = 6; ...@@ -3,8 +3,8 @@ const UI_LIMIT = 6;
const SPREAD = '...'; const SPREAD = '...';
const PREV = 'Prev'; const PREV = 'Prev';
const NEXT = 'Next'; const NEXT = 'Next';
const FIRST = '<< First'; const FIRST = '« First';
const LAST = 'Last >>'; const LAST = 'Last »';
export default { export default {
props: { props: {
......
...@@ -362,3 +362,13 @@ ...@@ -362,3 +362,13 @@
width: 100%; width: 100%;
} }
} }
.btn-blank {
padding: 0;
background: transparent;
border: 0;
&:focus {
outline: 0;
}
}
...@@ -48,10 +48,10 @@ header { ...@@ -48,10 +48,10 @@ header {
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
font-size: 18px; font-size: 18px;
padding: 0; padding: 0;
margin: ($header-height - 28) / 2 0; margin: (($header-height - 28) / 2) 3px;
margin-left: 8px; margin-left: 8px;
height: 28px; height: 28px;
min-width: 28px; min-width: 32px;
line-height: 28px; line-height: 28px;
text-align: center; text-align: center;
...@@ -73,21 +73,29 @@ header { ...@@ -73,21 +73,29 @@ header {
background-color: $gray-light; background-color: $gray-light;
color: $gl-text-color; color: $gl-text-color;
.todos-pending-count { svg {
background: darken($todo-alert-blue, 10%); fill: $gl-text-color;
} }
} }
.fa-caret-down { .fa-caret-down {
font-size: 14px; 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 { .navbar-toggle {
color: $nav-toggle-gray; color: $nav-toggle-gray;
margin: 7px 0; margin: 5px 0;
border-radius: 0; border-radius: 0;
position: absolute;
right: -10px; right: -10px;
padding: 6px 10px; padding: 6px 10px;
...@@ -141,10 +149,6 @@ header { ...@@ -141,10 +149,6 @@ header {
min-height: $header-height; min-height: $header-height;
padding-left: 30px; padding-left: 30px;
@media (max-width: $screen-sm-max) {
padding-right: 20px;
}
.dropdown-menu { .dropdown-menu {
margin-top: -5px; margin-top: -5px;
} }
...@@ -243,10 +247,7 @@ header { ...@@ -243,10 +247,7 @@ header {
.navbar-collapse { .navbar-collapse {
flex: 0 0 auto; flex: 0 0 auto;
border-top: none; border-top: none;
@media (min-width: $screen-md-min) {
padding: 0; padding: 0;
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
flex: 1 1 auto; flex: 1 1 auto;
...@@ -263,6 +264,34 @@ header { ...@@ -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) { @media (max-width: $screen-xs-max) {
header .container-fluid { header .container-fluid {
font-size: 18px; font-size: 18px;
......
...@@ -52,6 +52,18 @@ ...@@ -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 { @mixin bulleted-list {
> ul { > ul {
list-style-type: disc; list-style-type: disc;
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
padding-right: 0; padding-right: 0;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
.content-wrapper { &:not(.wiki-sidebar):not(.build-sidebar) .content-wrapper {
padding-right: $gutter_collapsed_width; padding-right: $gutter_collapsed_width;
} }
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
padding-right: 0; padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { @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; padding-right: $gutter_collapsed_width;
} }
} }
......
...@@ -269,8 +269,13 @@ ...@@ -269,8 +269,13 @@
font-size: (14px / $issue-boards-font-size) * 1em; font-size: (14px / $issue-boards-font-size) * 1em;
} }
.card-assignee {
margin-right: 5px;
}
.avatar { .avatar {
margin-left: 0; margin-left: 0;
margin-right: 0;
} }
} }
...@@ -325,6 +330,7 @@ ...@@ -325,6 +330,7 @@
} }
} }
<<<<<<< HEAD
.boards-title-holder { .boards-title-holder {
padding: 25px 13px $gl-padding; padding: 25px 13px $gl-padding;
...@@ -346,6 +352,9 @@ ...@@ -346,6 +352,9 @@
} }
.issue-boards-sidebar { .issue-boards-sidebar {
=======
.page-with-layout-nav.page-with-sub-nav .issue-boards-sidebar {
>>>>>>> ce/master
&.right-sidebar { &.right-sidebar {
top: 0; top: 0;
bottom: 0; bottom: 0;
......
...@@ -431,6 +431,21 @@ ...@@ -431,6 +431,21 @@
border-bottom: none; 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 // Mobile
@media (max-width: 480px) { @media (max-width: 480px) {
.diff-title { .diff-title {
......
...@@ -22,15 +22,7 @@ ...@@ -22,15 +22,7 @@
} }
.group-row { .group-row {
.stats { @include basic-list-stats;
float: right;
line-height: $list-text-height;
color: $gl-text-color;
span {
margin-right: 15px;
}
}
} }
.ldap-group-links { .ldap-group-links {
......
...@@ -243,6 +243,10 @@ ...@@ -243,6 +243,10 @@
font-size: 13px; font-size: 13px;
font-weight: normal; font-weight: normal;
} }
.hide-expanded {
display: none;
}
} }
&.right-sidebar-collapsed { &.right-sidebar-collapsed {
...@@ -282,10 +286,11 @@ ...@@ -282,10 +286,11 @@
display: block; display: block;
width: 100%; width: 100%;
text-align: center; text-align: center;
padding-bottom: 10px; margin-bottom: 10px;
color: $issuable-sidebar-color; color: $issuable-sidebar-color;
&:hover { &:hover,
&:hover .todo-undone {
color: $gl-text-color; color: $gl-text-color;
} }
...@@ -294,6 +299,10 @@ ...@@ -294,6 +299,10 @@
margin-top: 0; margin-top: 0;
} }
.todo-undone {
color: $gl-link-color;
}
.author { .author {
display: none; display: none;
} }
...@@ -582,3 +591,21 @@ ...@@ -582,3 +591,21 @@
opacity: 0; 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 @@ ...@@ -60,7 +60,17 @@
} }
.modify-merge-commit-link { .modify-merge-commit-link {
padding: 0;
background-color: transparent;
border: 0;
color: $gl-text-color; color: $gl-text-color;
&:hover,
&:focus {
text-decoration: underline;
}
} }
.merge-param-checkbox { .merge-param-checkbox {
......
...@@ -410,8 +410,22 @@ ul.notes { ...@@ -410,8 +410,22 @@ ul.notes {
} }
.discussion-toggle-button { .discussion-toggle-button {
padding: 0;
background-color: transparent;
border: 0;
line-height: 20px; line-height: 20px;
font-size: 13px; font-size: 13px;
transition: color 0.1s linear;
&:hover {
color: $gl-link-color;
}
&:focus {
text-decoration: underline;
outline: none;
color: $gl-link-color;
}
.fa { .fa {
margin-right: 3px; margin-right: 3px;
......
...@@ -573,9 +573,19 @@ pre.light-well { ...@@ -573,9 +573,19 @@ pre.light-well {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
// Disable Flexbox for admin page
&.admin-projects {
display: block;
.project-row {
display: block;
}
}
.project-row { .project-row {
display: flex; display: flex;
align-items: center; align-items: center;
@include basic-list-stats;
} }
h3 { h3 {
...@@ -732,6 +742,7 @@ pre.light-well { ...@@ -732,6 +742,7 @@ pre.light-well {
} }
} }
<<<<<<< HEAD
a.allowed-to-merge, a.allowed-to-merge,
a.allowed-to-push { a.allowed-to-push {
cursor: pointer; cursor: pointer;
...@@ -745,6 +756,8 @@ a.allowed-to-push { ...@@ -745,6 +756,8 @@ a.allowed-to-push {
} }
} }
=======
>>>>>>> ce/master
.create-new-protected-branch-button { .create-new-protected-branch-button {
@include dropdown-link; @include dropdown-link;
......
...@@ -3,25 +3,6 @@ ...@@ -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 { .todos-list > .todo {
// workaround because we cannot use border-colapse // workaround because we cannot use border-colapse
border-top: 1px solid transparent; border-top: 1px solid transparent;
......
class Admin::BackgroundJobsController < Admin::ApplicationController class Admin::BackgroundJobsController < Admin::ApplicationController
def show 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/) @sidekiq_processes = ps_output.split("\n").grep(/sidekiq/)
@concurrency = Sidekiq.options[:concurrency] @concurrency = Sidekiq.options[:concurrency]
end end
......
...@@ -16,10 +16,9 @@ class Admin::LabelsController < Admin::ApplicationController ...@@ -16,10 +16,9 @@ class Admin::LabelsController < Admin::ApplicationController
end end
def create def create
@label = Label.new(label_params) @label = Labels::CreateService.new(label_params).execute(template: true)
@label.template = true
if @label.save if @label.persisted?
redirect_to admin_labels_url, notice: "Label was created" redirect_to admin_labels_url, notice: "Label was created"
else else
render :new render :new
...@@ -27,7 +26,9 @@ class Admin::LabelsController < Admin::ApplicationController ...@@ -27,7 +26,9 @@ class Admin::LabelsController < Admin::ApplicationController
end end
def update 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.' redirect_to admin_labels_path, notice: 'label was successfully updated.'
else else
render :edit render :edit
......
...@@ -26,7 +26,7 @@ class Groups::LabelsController < Groups::ApplicationController ...@@ -26,7 +26,7 @@ class Groups::LabelsController < Groups::ApplicationController
end end
def create def create
@label = @group.labels.create(label_params) @label = Labels::CreateService.new(label_params).execute(group: group)
if @label.valid? if @label.valid?
redirect_to group_labels_path(@group) redirect_to group_labels_path(@group)
...@@ -40,7 +40,9 @@ class Groups::LabelsController < Groups::ApplicationController ...@@ -40,7 +40,9 @@ class Groups::LabelsController < Groups::ApplicationController
end end
def update 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 redirect_back_or_group_labels_path
else else
render :edit render :edit
......
...@@ -29,7 +29,7 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -29,7 +29,7 @@ class Projects::LabelsController < Projects::ApplicationController
end end
def create def create
@label = @project.labels.create(label_params) @label = Labels::CreateService.new(label_params).execute(project: @project)
if @label.valid? if @label.valid?
respond_to do |format| respond_to do |format|
...@@ -48,7 +48,9 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -48,7 +48,9 @@ class Projects::LabelsController < Projects::ApplicationController
end end
def update 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) redirect_to namespace_project_labels_path(@project.namespace, @project)
else else
render :edit 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 ...@@ -310,4 +310,8 @@ module ApplicationHelper
def active_when(condition) def active_when(condition)
'active' if condition 'active' if condition
end end
def show_user_callout?
cookies[:user_callout_dismissed] == 'true'
end
end end
...@@ -257,4 +257,19 @@ module IssuablesHelper ...@@ -257,4 +257,19 @@ module IssuablesHelper
def selected_template(issuable) def selected_template(issuable)
params[:issuable_template] if issuable_templates(issuable).include?(params[:issuable_template]) params[:issuable_template] if issuable_templates(issuable).include?(params[:issuable_template])
end 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 end
...@@ -46,6 +46,10 @@ class Blob < SimpleDelegator ...@@ -46,6 +46,10 @@ class Blob < SimpleDelegator
text? && language && language.name == 'SVG' text? && language && language.name == 'SVG'
end end
def ipython_notebook?
text? && language && language.name == 'Jupyter Notebook'
end
def size_within_svg_limits? def size_within_svg_limits?
size <= MAXIMUM_SVG_SIZE size <= MAXIMUM_SVG_SIZE
end end
...@@ -63,6 +67,8 @@ class Blob < SimpleDelegator ...@@ -63,6 +67,8 @@ class Blob < SimpleDelegator
end end
elsif image? || svg? elsif image? || svg?
'image' 'image'
elsif ipython_notebook?
'notebook'
elsif text? elsif text?
'text' 'text'
else else
......
...@@ -105,6 +105,10 @@ class CommitStatus < ActiveRecord::Base ...@@ -105,6 +105,10 @@ class CommitStatus < ActiveRecord::Base
end end
end end
def locking_enabled?
status_changed?
end
def before_sha def before_sha
pipeline.before_sha || Gitlab::Git::BLANK_SHA pipeline.before_sha || Gitlab::Git::BLANK_SHA
end end
......
...@@ -121,10 +121,10 @@ class Namespace < ActiveRecord::Base ...@@ -121,10 +121,10 @@ class Namespace < ActiveRecord::Base
# Move the namespace directory in all storages paths used by member projects # Move the namespace directory in all storages paths used by member projects
repository_storage_paths.each do |repository_storage_path| repository_storage_paths.each do |repository_storage_path|
# Ensure old directory exists before moving it # 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) unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path)
Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{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 # if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs # db changes in order to prevent out of sync between db and fs
...@@ -132,8 +132,8 @@ class Namespace < ActiveRecord::Base ...@@ -132,8 +132,8 @@ class Namespace < ActiveRecord::Base
end end
end end
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path) Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path)
Gitlab::PagesTransfer.new.rename_namespace(path_was, path) Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
remove_exports! remove_exports!
...@@ -156,7 +156,7 @@ class Namespace < ActiveRecord::Base ...@@ -156,7 +156,7 @@ class Namespace < ActiveRecord::Base
def send_update_instructions def send_update_instructions
projects.each do |project| projects.each do |project|
project.send_move_instructions("#{path_was}/#{project.path}") project.send_move_instructions("#{full_path_was}/#{project.path}")
end end
end end
...@@ -235,10 +235,10 @@ class Namespace < ActiveRecord::Base ...@@ -235,10 +235,10 @@ class Namespace < ActiveRecord::Base
old_repository_storage_paths.each do |repository_storage_path| old_repository_storage_paths.each do |repository_storage_path|
# Move namespace directory into trash. # Move namespace directory into trash.
# We will remove it later async # 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) if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path)
message = "Namespace directory \"#{path}\" moved to \"#{new_path}\"" message = "Namespace directory \"#{full_path}\" moved to \"#{new_path}\""
Gitlab::AppLogger.info message Gitlab::AppLogger.info message
# Remove namespace directroy async with delay so # Remove namespace directroy async with delay so
......
...@@ -74,8 +74,8 @@ class PrometheusService < MonitoringService ...@@ -74,8 +74,8 @@ class PrometheusService < MonitoringService
def calculate_reactive_cache(environment_slug) def calculate_reactive_cache(environment_slug)
return unless active? && project && !project.pending_delete? 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} 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="app",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}) * 100} 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, success: true,
......
class SystemNoteMetadata < ActiveRecord::Base class SystemNoteMetadata < ActiveRecord::Base
ICON_TYPES = %w[ ICON_TYPES = %w[
commit merge confidentiality status label assignee cross_reference 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 approvals
=======
title time_tracking branch milestone discussion task moved
>>>>>>> ce/master
].freeze ].freeze
validates :note, presence: true validates :note, presence: true
......
module Groups module Groups
class UpdateService < Groups::BaseService class UpdateService < Groups::BaseService
def execute def execute
reject_parent_id!
# check that user is allowed to set specified visibility_level # check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level] new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != group.visibility_level if new_visibility && new_visibility.to_i != group.visibility_level
...@@ -26,5 +28,11 @@ module Groups ...@@ -26,5 +28,11 @@ module Groups
false false
end end
end end
private
def reject_parent_id!
params.except!(:parent_id)
end
end 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 ...@@ -3,7 +3,7 @@ module Labels
def initialize(current_user, project, params = {}) def initialize(current_user, project, params = {})
@current_user = current_user @current_user = current_user
@project = project @project = project
@params = params.dup @params = params.dup.with_indifferent_access
end end
def execute(skip_authorization: false) def execute(skip_authorization: false)
...@@ -28,7 +28,7 @@ module Labels ...@@ -28,7 +28,7 @@ module Labels
new_label = available_labels.find_by(title: title) new_label = available_labels.find_by(title: title)
if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, project)) 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 end
new_label 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 ...@@ -467,6 +467,7 @@ module SystemNoteService
body = "moved #{direction} #{cross_reference}" body = "moved #{direction} #{cross_reference}"
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved')) create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
<<<<<<< HEAD
end end
# Called when the merge request is approved by user # Called when the merge request is approved by user
...@@ -489,6 +490,8 @@ module SystemNoteService ...@@ -489,6 +490,8 @@ module SystemNoteService
body = "unapproved this merge request" body = "unapproved this merge request"
create_note(NoteSummary.new(noteable, noteable.project, user, body, action: 'approvals')) create_note(NoteSummary.new(noteable, noteable.project, user, body, action: 'approvals'))
=======
>>>>>>> ce/master
end end
private private
......
.js-projects-list-holder .js-projects-list-holder
- if @projects.any? - if @projects.any?
%ul.projects-list.content-list %ul.projects-list.content-list.admin-projects
- @projects.each_with_index do |project| - @projects.each_with_index do |project|
%li.project-row %li.project-row{ class: ('no-description' if project.description.blank?) }
.controls .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 '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" = 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 .title
= link_to [:admin, project.namespace.becomes(Namespace), project] do = link_to [:admin, project.namespace.becomes(Namespace), project] do
.dash-project-avatar .dash-project-avatar
...@@ -20,7 +21,7 @@ ...@@ -20,7 +21,7 @@
- if project.namespace - if project.namespace
= project.namespace.human_name = project.namespace.human_name
\/ \/
%span.project-name.filter-title %span.project-name
= project.name = project.name
- if project.description.present? - if project.description.present?
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - 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] - if @projects.any? || params[:name]
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.discussion.js-toggle-container{ class: discussion.id, data: { discussion_id: discussion.id } } .discussion.js-toggle-container{ class: discussion.id, data: { discussion_id: discussion.id } }
.discussion-header .discussion-header
.discussion-actions .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 - if expanded
= icon("chevron-up") = icon("chevron-up")
- else - else
......
...@@ -11,9 +11,6 @@ ...@@ -11,9 +11,6 @@
= render 'layouts/nav/dashboard' = render 'layouts/nav/dashboard'
- else - else
= render 'layouts/nav/explore' = render 'layouts/nav/explore'
%button.navbar-toggle{ type: 'button' }
%span.sr-only Toggle navigation
= icon('ellipsis-v')
.header-logo .header-logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
...@@ -38,10 +35,20 @@ ...@@ -38,10 +35,20 @@
%li %li
= link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw') = 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 %li
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw') = icon('check-circle fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) } %span.badge.todos-count
= todos_count_format(todos_pending_count) = todos_count_format(todos_pending_count)
- if current_user.can_create_project? - if current_user.can_create_project?
%li %li
...@@ -76,6 +83,10 @@ ...@@ -76,6 +83,10 @@
%div %div
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' = 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 = yield :header_content
= render 'shared/outdated_browser' = 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 @@ ...@@ -24,7 +24,7 @@
.visible-xs-inline .visible-xs-inline
= render_commit_status(commit, ref: ref) = render_commit_status(commit, ref: ref)
- if commit.description? - if commit.description?
%a.text-expander.hidden-xs.js-toggle-button ... %button.text-expander.hidden-xs.js-toggle-button{ type: "button" } ...
- if commit.description? - if commit.description?
%pre.commit-row-description.js-toggle-content %pre.commit-row-description.js-toggle-content
......
.js-toggle-container .js-toggle-container
.commit-stat-summary .commit-stat-summary
Showing 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") %strong= pluralize(diff_files.size, "changed file")
with with
%strong.cgreen #{diff_files.sum(&:added_lines)} additions %strong.cgreen #{diff_files.sum(&:added_lines)} additions
......
...@@ -45,6 +45,7 @@ ...@@ -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'} = 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 .accept-control
<<<<<<< HEAD
- if @project.merge_requests_ff_only_enabled - if @project.merge_requests_ff_only_enabled
Fast-forward merge without a merge commit Fast-forward merge without a merge commit
- else - else
...@@ -58,5 +59,16 @@ ...@@ -58,5 +59,16 @@
message_without_description: @merge_request.merge_commit_message, message_without_description: @merge_request.merge_commit_message,
text: @merge_request.merge_commit_message, text: @merge_request.merge_commit_message,
rows: 14, hint: true 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" = hidden_field_tag :merge_when_pipeline_succeeds, "", autocomplete: "off"
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
Gitea Gitea
%div %div
- if git_import_enabled? - 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') = icon('git', text: 'Repo by URL')
.import_gitlab_project .import_gitlab_project
- if gitlab_project_import_enabled? - if gitlab_project_import_enabled?
......
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
Milestones Milestones
%span.badge %span.badge
= @search_results.milestones_count = @search_results.milestones_count
<<<<<<< HEAD
- if current_application_settings.elasticsearch_search? - if current_application_settings.elasticsearch_search?
%li{ class: ("active" if @scope == 'blobs') } %li{ class: ("active" if @scope == 'blobs') }
= link_to search_filter_path(scope: 'blobs') do = link_to search_filter_path(scope: 'blobs') do
...@@ -83,3 +84,5 @@ ...@@ -83,3 +84,5 @@
Commits Commits
%span.badge %span.badge
= @search_results.commits_count = @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 = root_url
- group_path << parent.full_path + '/' if parent - group_path << parent.full_path + '/' if parent
- if @group.persisted? - 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 @@ ...@@ -29,7 +29,7 @@
%button.btn.btn-link %button.btn.btn-link
= icon('search') = icon('search')
%span %span
Keep typing and press Enter Press Enter or click to search
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link %button.btn.btn-link
......
...@@ -13,15 +13,12 @@ ...@@ -13,15 +13,12 @@
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" } %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
= sidebar_gutter_toggle_icon = sidebar_gutter_toggle_icon
- if current_user - 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) } } = render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable
%span.js-issuable-todo-text
- if todo
Mark done
- else
Add todo
= icon('spin spinner', class: 'hidden js-issuable-todo-loading', 'aria-hidden': 'true')
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f| = 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 .block.assignee
.sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) } .sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) }
- if issuable.assignee - if issuable.assignee
...@@ -121,7 +118,7 @@ ...@@ -121,7 +118,7 @@
- selected_labels = issuable.labels - selected_labels = issuable.labels
.block.labels .block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } } .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 %span
= selected_labels.size = selected_labels.size
.title.hide-collapsed .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 @@ ...@@ -85,6 +85,7 @@
Closed: Closed:
= milestone.issues_visible_to_user(current_user).closed.count = milestone.issues_visible_to_user(current_user).closed.count
<<<<<<< HEAD
- total_weight = milestone.issues_visible_to_user(current_user).sum(:weight) - total_weight = milestone.issues_visible_to_user(current_user).sum(:weight)
.block.weight .block.weight
.sidebar-collapsed-icon .sidebar-collapsed-icon
...@@ -102,6 +103,8 @@ ...@@ -102,6 +103,8 @@
- else - else
.no-value None .no-value None
=======
>>>>>>> ce/master
.block .block
.sidebar-collapsed-icon .sidebar-collapsed-icon
%strong %strong
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
- if project.namespace && !skip_namespace - if project.namespace && !skip_namespace
= project.namespace.human_name = project.namespace.human_name
\/ \/
%span.project-name.filter-title %span.project-name
= project.name = project.name
- if show_last_commit_as_description - if show_last_commit_as_description
......
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
= event_action_name(event) = event_action_name(event)
%strong %strong
- if event.note? - 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 - 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 at
%strong %strong
......
...@@ -100,8 +100,8 @@ ...@@ -100,8 +100,8 @@
Snippets Snippets
%div{ class: container_class } %div{ class: container_class }
- if @user == current_user - if @user == current_user && !show_user_callout?
.user-callout{ 'callout-svg' => custom_icon('icon_customization') } = render 'shared/user_callout'
.tab-content .tab-content
#activity.tab-pane #activity.tab-pane
.row-content-block.calender-block.white.second-block.hidden-xs .row-content-block.calender-block.white.second-block.hidden-xs
......
...@@ -4,20 +4,16 @@ class PostReceive ...@@ -4,20 +4,16 @@ class PostReceive
extend Gitlab::CurrentSettings extend Gitlab::CurrentSettings
def perform(repo_path, identifier, changes) 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_relative_path = Gitlab::RepoPath.strip_storage_path(repo_path)
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
changes = Base64.decode64(changes) unless changes.include?(' ') changes = Base64.decode64(changes) unless changes.include?(' ')
# Use Sidekiq.logger so arguments can be correlated with execution # Use Sidekiq.logger so arguments can be correlated with execution
# time and thread ID's. # time and thread ID's.
Sidekiq.logger.info "changes: #{changes.inspect}" if ENV['SIDEKIQ_LOG_ARGUMENTS'] 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? 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 return false
end end
...@@ -41,7 +37,7 @@ class PostReceive ...@@ -41,7 +37,7 @@ class PostReceive
process_project_changes(post_received) process_project_changes(post_received)
else 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 false
end end
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 title: Add metadata to system notes
<<<<<<< HEAD
merge_request: 1526 merge_request: 1526
=======
merge_request: 9964
>>>>>>> ce/master
author: 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 ...@@ -530,14 +530,10 @@ production: &base
# Gitaly settings # Gitaly settings
gitaly: gitaly:
# The socket_path setting is optional and obsolete. When this is set # This setting controls whether GitLab uses Gitaly (new component
# GitLab assumes it can reach a Gitaly services via a Unix socket at # introduced in 9.0). Eventually Gitaly use will become mandatory and
# this path. When this is commented out GitLab will not use Gitaly. # this option will disappear.
# enabled: false
# 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
# #
# 4. Advanced settings # 4. Advanced settings
...@@ -552,6 +548,7 @@ production: &base ...@@ -552,6 +548,7 @@ production: &base
storages: # You must have at least a `default` storage path. storages: # You must have at least a `default` storage path.
default: default:
path: /home/git/repositories/ path: /home/git/repositories/
gitaly_address: unix:/home/git/gitlab/tmp/sockets/private/gitaly.socket
## Backup settings ## Backup settings
backup: backup:
...@@ -660,10 +657,15 @@ test: ...@@ -660,10 +657,15 @@ test:
# In order to setup it correctly you need to specify # In order to setup it correctly you need to specify
# your system username you use to run GitLab # your system username you use to run GitLab
# user: YOUR_USERNAME # user: YOUR_USERNAME
pages:
path: tmp/tests/pages
repositories: repositories:
storages: storages:
default: default:
path: tmp/tests/repositories/ path: tmp/tests/repositories/
gitaly_address: unix:<%= Rails.root.join('tmp/sockets/private/gitaly.socket') %>
gitaly:
enabled: false
backup: backup:
path: tmp/tests/backups path: tmp/tests/backups
gitlab_shell: gitlab_shell:
......
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
# end # end
# #
ActiveSupport::Inflector.inflections do |inflect| ActiveSupport::Inflector.inflections do |inflect|
<<<<<<< HEAD
inflect.uncountable %w(award_emoji project_statistics project_registry file_registry system_note_metadata) inflect.uncountable %w(award_emoji project_statistics project_registry file_registry system_note_metadata)
inflect.acronym 'EE' inflect.acronym 'EE'
=======
inflect.uncountable %w(award_emoji project_statistics system_note_metadata)
>>>>>>> ce/master
end 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