Commit 32c6c9c4 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into bitbucket-oauth2

parents 7a155137 43c8788e

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

/coverage/
/coverage-javascript/
/public/
/tmp/
......
{
"extends": "airbnb",
"env": {
"jquery": true,
"browser": true,
"es6": true
},
"extends": "airbnb-base",
"globals": {
"_": false,
"gl": false,
"gon": false
},
"plugins": [
"filenames"
],
"rules": {
"filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"]
},
"globals": {
"$": false,
"_": false,
"beforeEach": false,
"d3": false,
"define": false,
"describe": false,
"document": false,
"expect": false,
"fixture": false,
"gl": false,
"it": false,
"jQuery": false,
"Mousetrap": false,
"spyOn": false,
"spyOnEvent": false,
"Turbolinks": false,
"window": false,
"Vue": false,
"Flash": false,
"Cookies": false
}
}
......@@ -5,6 +5,7 @@
.chef
.directory
/.envrc
eslint-report.html
/.gitlab_shell_secret
.idea
/.rbenv-version
......
......@@ -20,7 +20,7 @@ before_script:
- source ./scripts/prepare_build.sh
- cp config/gitlab.yml.example config/gitlab.yml
- bundle --version
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"'
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) $FLAGS'
- retry gem install knapsack
- '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql'
......@@ -271,12 +271,17 @@ rake db:seed_fu:
- log/development.log
teaspoon:
cache:
paths:
- vendor/ruby
- node_modules/
stage: test
<<: *use-db
script:
- curl --silent --location https://deb.nodesource.com/setup_6.x | bash -
- apt-get install --assume-yes nodejs
- npm install --global istanbul
- npm install
- npm link istanbul
- rake teaspoon
artifacts:
name: coverage-javascript
......@@ -323,7 +328,7 @@ migration paths:
- git checkout -f FETCH_HEAD
- cp config/resque.yml.example config/resque.yml
- sed -i 's/localhost/redis/g' config/resque.yml
- bundle install --without postgres production --jobs $(nproc) ${FLAGS[@]} --retry=3
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3
- rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_BUILD_REF
- source scripts/prepare_build.sh
......@@ -344,13 +349,33 @@ coverage:
- coverage/index.html
- coverage/assets/
lint-javascript:
lint:javascript:
cache:
paths:
- node_modules/
stage: test
image: "node:latest"
image: "node:7.1"
before_script:
- npm install
script:
- npm --silent run eslint
lint:javascript:report:
cache:
paths:
- node_modules/
stage: post-test
image: "node:7.1"
before_script:
- npm install
script:
- npm run eslint
- find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
- npm --silent run eslint-report || true # ignore exit code
artifacts:
name: eslint-report
expire_in: 31d
paths:
- eslint-report.html
# Trigger docs build
# https://gitlab.com/gitlab-com/doc-gitlab-com/blob/master/README.md#deployment-process
......@@ -376,7 +401,7 @@ notify:slack:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
- ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
when: on_failure
only:
- master@gitlab-org/gitlab-ce
......@@ -390,11 +415,13 @@ pages:
dependencies:
- coverage
- teaspoon
- lint:javascript:report
script:
- mv public/ .public/
- mkdir public/
- mv coverage public/coverage-ruby
- mv coverage-javascript/default/ public/coverage-javascript/
- mv eslint-report.html public/
artifacts:
paths:
- public
......
This diff is collapsed.
......@@ -67,7 +67,7 @@ gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API
gem 'grape', '~> 0.15.0'
gem 'grape-entity', '~> 0.4.2'
gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination
......@@ -84,10 +84,8 @@ gem 'dropzonejs-rails', '~> 0.7.1'
# for backups
gem 'fog-aws', '~> 0.9'
gem 'fog-azure', '~> 0.0'
gem 'fog-core', '~> 1.40'
gem 'fog-local', '~> 0.3'
gem 'fog-google', '~> 0.3'
gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1'
......@@ -134,7 +132,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
# Background jobs
gem 'sidekiq', '~> 4.2'
gem 'sidekiq-cron', '~> 0.4.0'
gem 'sidekiq-cron', '~> 0.4.4'
gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4'
......@@ -310,6 +308,8 @@ group :development, :test do
gem 'knapsack', '~> 1.11.0'
gem 'activerecord_sane_schema_dumper', '0.2'
gem 'stackprof', '~> 0.2.10'
end
group :test do
......
......@@ -66,21 +66,6 @@ GEM
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
azure (0.7.5)
addressable (~> 2.3)
azure-core (~> 0.1)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
json (~> 1.8)
mime-types (>= 1, < 3.0)
nokogiri (~> 1.6)
systemu (~> 2.6)
thor (~> 0.19)
uuid (~> 2.0)
azure-core (0.1.2)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
nokogiri (~> 1.6)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
......@@ -217,19 +202,10 @@ GEM
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
fog-azure (0.0.2)
azure (~> 0.6)
fog-core (~> 1.27)
fog-json (~> 1.0)
fog-xml (~> 0.1)
fog-core (1.42.0)
builder
excon (~> 0.49)
formatador (~> 0.2)
fog-google (0.3.2)
fog-core
fog-json
fog-xml
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
......@@ -316,7 +292,7 @@ GEM
rack-accept
rack-mount
virtus (>= 1.0.0)
grape-entity (0.4.8)
grape-entity (0.6.0)
activesupport
multi_json (>= 1.3.2)
haml (4.0.7)
......@@ -397,8 +373,6 @@ GEM
rb-inotify (>= 0.9)
loofah (2.0.3)
nokogiri (>= 1.5.9)
macaddr (1.7.1)
systemu (~> 2.6.2)
mail (2.6.4)
mime-types (>= 1.16, < 4)
mail_room (0.9.0)
......@@ -636,7 +610,8 @@ GEM
rubyntlm (0.5.2)
rubypants (0.2.0)
rubyzip (1.2.0)
rufus-scheduler (3.1.10)
rufus-scheduler (3.3.0)
tzinfo
rugged (0.24.0)
safe_yaml (1.0.4)
sanitize (2.1.0)
......@@ -672,10 +647,10 @@ GEM
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (~> 1.5)
redis (~> 3.2, >= 3.2.1)
sidekiq-cron (0.4.0)
sidekiq-cron (0.4.4)
redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.0.0)
sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4)
simplecov (0.12.0)
......@@ -713,6 +688,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
activemodel (>= 4.1, < 5.1)
......@@ -724,7 +700,6 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
systemu (2.6.5)
teaspoon (1.1.5)
railties (>= 3.2.5, < 6)
teaspoon-jasmine (2.2.0)
......@@ -764,8 +739,6 @@ GEM
get_process_mem (~> 0)
unicorn (>= 4, < 6)
uniform_notifier (1.10.0)
uuid (2.3.8)
macaddr (~> 1.0)
version_sorter (2.1.0)
virtus (1.0.5)
axiom-types (~> 0.1)
......@@ -845,9 +818,7 @@ DEPENDENCIES
ffaker (~> 2.0.0)
flay (~> 2.6.1)
fog-aws (~> 0.9)
fog-azure (~> 0.0)
fog-core (~> 1.40)
fog-google (~> 0.3)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1)
......@@ -865,7 +836,7 @@ DEPENDENCIES
gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.1.0)
grape (~> 0.15.0)
grape-entity (~> 0.4.2)
grape-entity (~> 0.6.0)
haml_lint (~> 0.18.2)
hamlit (~> 2.6.1)
health_check (~> 2.2.0)
......@@ -951,7 +922,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.2)
sidekiq-cron (~> 0.4.0)
sidekiq-cron (~> 0.4.4)
sidekiq-limit_fetch (~> 3.4)
simplecov (= 0.12.0)
slack-notifier (~> 1.2.0)
......@@ -963,6 +934,7 @@ DEPENDENCIES
spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.7.0)
sprockets-es6 (~> 0.9.2)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6)
teaspoon (~> 1.1.0)
......
# GitLab
[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](http://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
......@@ -84,7 +84,7 @@ For more information please see the [architecture documentation](https://docs.gi
## UX design
Please adhere to the [UX Guide](doc/development/ux_guide/readme.md) when creating designs and implementing code.
Please adhere to the [UX Guide](doc/development/ux_guide/index.md) when creating designs and implementing code.
## Third-party applications
......
8.14.0-pre
8.15.0-pre
/* eslint-disable no-param-reassign, class-methods-use-this */
/* global Pager, Cookies */
/* global Pager */
/* global Cookies */
((global) => {
class Activities {
......
......@@ -54,6 +54,9 @@
mouseDown () {
this.showDetail = true;
},
mouseMove() {
this.showDetail = false;
},
showIssue (e) {
const targetTagName = e.target.tagName.toLowerCase();
......
......@@ -80,6 +80,7 @@
},
mounted () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({
scroll: document.querySelectorAll('.boards-list')[0],
group: 'issues',
sort: false,
disabled: this.disabled,
......@@ -88,18 +89,16 @@
const card = this.$refs.issue[e.oldIndex];
card.showDetail = false;
Store.moving.issue = card.issue;
Store.moving.list = card.list;
Store.moving.issue = Store.moving.list.findIssue(+e.item.dataset.issueId);
gl.issueBoards.onStart();
},
onAdd: (e) => {
// Add the element back to original list to allow Vue to handle DOM updates
e.from.appendChild(e.item);
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue, e.newIndex);
this.$nextTick(() => {
// Update the issues once we know the element has been moved
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue);
e.item.remove();
});
},
});
......
......@@ -106,9 +106,13 @@ class List {
});
}
addIssue (issue, listFrom) {
addIssue (issue, listFrom, newIndex) {
if (!this.findIssue(issue.id)) {
this.issues.push(issue);
if (newIndex !== undefined) {
this.issues.splice(newIndex, 0, issue);
} else {
this.issues.push(issue);
}
if (this.label) {
issue.addLabel(this.label);
......
......@@ -89,14 +89,14 @@
});
listFrom.update();
},
moveIssueToList (listFrom, listTo, issue) {
moveIssueToList (listFrom, listTo, issue, newIndex) {
const issueTo = listTo.findIssue(issue.id),
issueLists = issue.getLists(),
listLabels = issueLists.map( listIssue => listIssue.label );
// Add to new lists issues if it doesn't already exist
if (!issueTo) {
listTo.addIssue(issue, listFrom);
listTo.addIssue(issue, listFrom, newIndex);
}
if (listTo.type === 'done' && listFrom.type !== 'backlog') {
......
......@@ -12,7 +12,7 @@
this.pageUrl = options.pageUrl;
this.buildUrl = options.buildUrl;
this.buildStatus = options.buildStatus;
this.state = options.state1;
this.state = options.logState;
this.buildStage = options.buildStage;
this.updateDropdown = bind(this.updateDropdown, this);
this.$document = $(document);
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-undef, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, padded-blocks, max-len */
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-undef, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, padded-blocks, max-len, prefer-arrow-callback */
(function() {
this.CommitsList = (function() {
function CommitsList() {}
......@@ -13,7 +13,9 @@
return false;
}
});
Pager.init(limit, false);
Pager.init(limit, false, false, function() {
gl.utils.localTimeAgo($('.js-timeago'));
});
this.content = $("#commits-list");
this.searchField = $("#commits-search");
return this.initSearch();
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......@@ -8,10 +10,15 @@
},
template: `
<span class="total-time">
<template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
<template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
<template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
<template v-if="Object.keys(time).length">
<template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
<template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
<template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
</template>
<template v-else>
--
</template>
</span>
`,
});
......
/* global Vue */
/* global Cookies */
/* global Flash */
//= require vue
//= require_tree ./svg
//= require_tree .
......
......@@ -62,9 +62,11 @@
this.state.events = this.decorateEvents(events);
},
decorateEvents(events) {
const newEvents = events;
const newEvents = [];
events.forEach((item) => {
if (!item) return;
newEvents.forEach((item) => {
item.totalTime = item.total_time;
item.author.webUrl = item.author.web_url;
item.author.avatarUrl = item.author.avatar_url;
......@@ -79,6 +81,8 @@
delete item.created_at;
delete item.short_sha;
delete item.commit_url;
newEvents.push(item);
});
return newEvents;
......
......@@ -57,14 +57,17 @@ class DiscussionModel {
}
updateHeadline (data) {
const $discussionHeadline = $(`.discussion[data-discussion-id="${this.id}"] .js-discussion-headline`);
const discussionSelector = `.discussion[data-discussion-id="${this.id}"]`;
const $discussionHeadline = $(`${discussionSelector} .js-discussion-headline`);
if (data.discussion_headline_html) {
if ($discussionHeadline.length) {
$discussionHeadline.replaceWith(data.discussion_headline_html);
} else {
$(`.discussion[data-discussion-id="${this.id}"] .discussion-header`).append(data.discussion_headline_html);
$(`${discussionSelector} .discussion-header`).append(data.discussion_headline_html);
}
gl.utils.localTimeAgo($('.js-timeago', `${discussionSelector}`));
} else {
$discussionHeadline.remove();
}
......@@ -74,7 +77,7 @@ class DiscussionModel {
if (!this.canResolve) {
return false;
}
for (const noteId in this.notes) {
const note = this.notes[noteId];
......
......@@ -208,6 +208,9 @@
new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList();
break;
case 'projects:variables:index':
new gl.ProjectVariables();
break;
}
switch (path.first()) {
case 'admin':
......
......@@ -145,25 +145,19 @@
class DueDateSelectors {
constructor() {
this.initMilestoneDueDate();
this.initMilestoneDatePicker();
this.initIssuableSelect();
}
initMilestoneDueDate() {
const $datePicker = $('.datepicker');
initMilestoneDatePicker() {
$('.datepicker').datepicker({
dateFormat: 'yy-mm-dd'
});
if ($datePicker.length) {
const $dueDate = $('#milestone_due_date');
$datePicker.datepicker({
dateFormat: 'yy-mm-dd',
onSelect: (dateText, inst) => {
$dueDate.val(dateText);
}
}).datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $dueDate.val()));
}
$('.js-clear-due-date').on('click', (e) => {
$('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
e.preventDefault();
$.datepicker._clearDate($datePicker);
const datepicker = $(e.target).siblings('.datepicker');
$.datepicker._clearDate(datepicker);
});
}
......
/* eslint-disable no-param-reassign */
/* global Vue */
/* global EnvironmentsService */
//= require vue
//= require vue-resource
//= require_tree ../services/
//= require ./environment_item
/* globals Vue, EnvironmentsService */
/* eslint-disable no-param-reassign */
(() => { // eslint-disable-line
(() => {
window.gl = window.gl || {};
/**
......@@ -180,7 +181,7 @@
<div class="environments-container">
<div class="environments-list-loading text-center" v-if="isLoading">
<i class="fa fa-spinner spin"></i>
<i class="fa fa-spinner fa-spin"></i>
</div>
<div class="blank-state blank-state-no-icon"
......@@ -209,12 +210,12 @@
<table class="table ci-table environments">
<thead>
<tr>
<th>Environment</th>
<th>Last deployment</th>
<th>Build</th>
<th>Commit</th>
<th></th>
<th class="hidden-xs"></th>
<th class="environments-name">Environment</th>
<th class="environments-deploy">Last deployment</th>
<th class="environments-build">Build</th>
<th class="environments-commit">Commit</th>
<th class="environments-date"></th>
<th class="hidden-xs environments-actions"></th>
</tr>
</thead>
<tbody>
......
/*= require lib/utils/timeago */
/* global Vue */
/* global timeago */
/*= require timeago */
/*= require lib/utils/text_utility */
/*= require vue_common_component/commit */
/*= require ./environment_actions */
......@@ -6,8 +9,6 @@
/*= require ./environment_stop */
/*= require ./environment_rollback */
/* globals Vue, timeago */
(() => {
/**
* Envrionment Item Component
......
......@@ -6,3 +6,19 @@ Array.prototype.first = function() {
Array.prototype.last = function() {
return this[this.length-1];
}
Array.prototype.find = Array.prototype.find || function(predicate, ...args) {
if (!this) throw new TypeError('Array.prototype.find called on null or undefined');
if (typeof predicate !== 'function') throw new TypeError('predicate must be a function');
const list = Object(this);
const thisArg = args[1];
let value = {};
for (let i = 0; i < list.length; i += 1) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) return value;
}
return undefined;
};
......@@ -3,7 +3,7 @@
Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
Element.prototype.closest = function closest(selector, selectedElement = this) {
Element.prototype.closest = Element.prototype.closest || function closest(selector, selectedElement = this) {
if (!selectedElement) return;
return selectedElement.matches(selector) ? selectedElement : Element.prototype.closest(selector, selectedElement.parentElement);
};
......@@ -53,6 +53,26 @@
} else {
return value;
}
},
matcher: function (flag, subtext) {
// The below is taken from At.js source
// Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js
var _a, _y, regexp, match;
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
_a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF");
regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)|([^\\x00-\\xff]*)$", 'gi');
match = regexp.exec(subtext);
if (match) {
return match[2] || match[1];
} else {
return null;
}
}
},
setup: _.debounce(function(input) {
......@@ -91,10 +111,12 @@
})(this),
insertTpl: ':${name}:',
data: ['loading'],
startWithSpace: false,
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
beforeInsert: this.DefaultOptions.beforeInsert
beforeInsert: this.DefaultOptions.beforeInsert,
matcher: this.DefaultOptions.matcher
}
});
// Team Members
......@@ -112,11 +134,13 @@
insertTpl: '${atwho-at}${username}',
searchKey: 'search',
data: ['loading'],
startWithSpace: false,
alwaysHighlightFirst: true,
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
beforeInsert: this.DefaultOptions.beforeInsert,
matcher: this.DefaultOptions.matcher,
beforeSave: function(members) {
return $.map(members, function(m) {
let title = '';
......@@ -157,10 +181,12 @@
})(this),
data: ['loading'],
insertTpl: '${atwho-at}${id}',
startWithSpace: false,
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
beforeInsert: this.DefaultOptions.beforeInsert,
matcher: this.DefaultOptions.matcher,
beforeSave: function(issues) {
return $.map(issues, function(i) {
if (i.title == null) {
......@@ -190,7 +216,9 @@
})(this),
insertTpl: '${atwho-at}"${title}"',
data: ['loading'],
startWithSpace: false,
callbacks: {
matcher: this.DefaultOptions.matcher,
sorter: this.DefaultOptions.sorter,
beforeSave: function(milestones) {
return $.map(milestones, function(m) {
......@@ -220,11 +248,13 @@
};
})(this),
data: ['loading'],
startWithSpace: false,
insertTpl: '${atwho-at}${id}',
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
beforeInsert: this.DefaultOptions.beforeInsert,
matcher: this.DefaultOptions.matcher,
beforeSave: function(merges) {
return $.map(merges, function(m) {
if (m.title == null) {
......@@ -245,7 +275,9 @@
searchKey: 'search',
displayTpl: this.Labels.template,
insertTpl: '${atwho-at}${title}',
startWithSpace: false,
callbacks: {
matcher: this.DefaultOptions.matcher,
sorter: this.DefaultOptions.sorter,
beforeSave: function(merges) {
var sanitizeLabelTitle;
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
(function() {
this.LabelsSelect = (function() {
function LabelsSelect() {
......
/**
* CustomEvent support for IE
*/
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function CustomEvent(e, params) {
const options = params || { bubbles: false, cancelable: false, detail: undefined };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(e, options.bubbles, options.cancelable, options.detail);
return evt;
};
window.CustomEvent.prototype = window.Event.prototype;
}
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-undef, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */
/* global timeago */
/* global dateFormat */
/*= require timeago */
/*= require date.format */
(function() {
(function(w) {
var base;
......
(() => {
/*
* TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints,
* stringifyTime condensed or non-condensed, abbreviateTimelengths)
* */
class PrettyTime {
/*
* Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # }
* Seconds can be negative or positive, zero or non-zero.
*/
static parseSeconds(seconds) {
const DAYS_PER_WEEK = 5;
const HOURS_PER_DAY = 8;
const MINUTES_PER_HOUR = 60;
const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR;
const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR;
const timePeriodConstraints = {
weeks: MINUTES_PER_WEEK,
days: MINUTES_PER_DAY,
hours: MINUTES_PER_HOUR,
minutes: 1,
};
let unorderedMinutes = PrettyTime.secondsToMinutes(seconds);
return _.mapObject(timePeriodConstraints, (minutesPerPeriod) => {
const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod);
unorderedMinutes -= (periodCount * minutesPerPeriod);
return periodCount;
});
}
/*
* Accepts a timeObject and returns a condensed string representation of it
* (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included.
*/
static stringifyTime(timeObject) {
const reducedTime = _.reduce(timeObject, (memo, unitValue, unitName) => {
const isNonZero = !!unitValue;
return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo;
}, '').trim();
return reducedTime.length ? reducedTime : '0m';
}
/*
* Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns
* the first non-zero unit/value pair.
*/
static abbreviateTime(timeStr) {
return timeStr.split(' ')
.filter(unitStr => unitStr.charAt(0) !== '0')[0];
}
static secondsToMinutes(seconds) {
return Math.abs(seconds / 60);
}
}
gl.PrettyTime = PrettyTime;
})(window.gl || (window.gl = {}));
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, padded-blocks */
/* global Turbolinks */
(function() {
Turbolinks.enableProgressBar();
......
......@@ -67,7 +67,7 @@
MergeRequestWidget.prototype.addEventListeners = function() {
var allowedPages;
allowedPages = ['show', 'commits', 'builds', 'pipelines', 'changes'];
return $(document).on('page:change.merge_request', (function(_this) {
$(document).on('page:change.merge_request', (function(_this) {
return function() {
var page;
page = $('body').data('page').split(':').last();
......@@ -245,7 +245,7 @@
case "not_found":
return this.setMergeButtonClass('btn-danger');
case "running":
return this.setMergeButtonClass('btn-warning');
return this.setMergeButtonClass('btn-info');
case "success":
case "success_with_warnings":
return this.setMergeButtonClass('btn-create');
......@@ -263,7 +263,7 @@
};
MergeRequestWidget.prototype.setMergeButtonClass = function(css_class) {
return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-warning btn-create').addClass(css_class);
return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class);
};
return MergeRequestWidget;
......
/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
/* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
/*= require autosave */
/*= require autosize */
......@@ -113,6 +113,7 @@
$(document).off("click", ".js-note-discard");
$(document).off("keydown", ".js-note-text");
$(document).off('click', '.js-comment-resolve-button');
$(document).off("click", '.system-note-commit-list-toggler');
$('.note .js-task-list-container').taskList('disable');
return $(document).off('tasklist:changed', '.note .js-task-list-container');
};
......@@ -332,7 +333,7 @@
gl.diffNotesCompileComponents();
}
gl.utils.localTimeAgo($('.js-timeago', note_html), false);
gl.utils.localTimeAgo($('.js-timeago'), false);
return this.updateNotesCount(1);
};
......
......@@ -35,7 +35,6 @@
}
onSubmitForm(e) {
e.preventDefault();
return this.saveForm();
}
......
(() => {
const HIDDEN_VALUE_TEXT = '******';
class ProjectVariables {
constructor() {
this.$revealBtn = $('.js-btn-toggle-reveal-values');
this.$revealBtn.on('click', this.toggleRevealState.bind(this));
}
toggleRevealState(e) {
e.preventDefault();
const oldStatus = this.$revealBtn.attr('data-status');
let newStatus = 'hidden';
let newAction = 'Reveal Values';
if (oldStatus === 'hidden') {
newStatus = 'revealed';
newAction = 'Hide Values';
}
this.$revealBtn.attr('data-status', newStatus);
const $variables = $('.variable-value');
$variables.each((_, variable) => {
const $variable = $(variable);
let newText = HIDDEN_VALUE_TEXT;
if (newStatus === 'revealed') {
newText = $variable.attr('data-value');
}
$variable.text(newText);
});
this.$revealBtn.text(newAction);
}
}
window.gl = window.gl || {};
window.gl.ProjectVariables = ProjectVariables;
})();
/*
* Instances of SmartInterval extend the functionality of `setInterval`, make it configurable
* and controllable by a public API.
*
* */
(() => {
class SmartInterval {
/**
* @param { function } callback Function to be called on each iteration (required)
* @param { milliseconds } startingInterval `currentInterval` is set to this initially
* @param { milliseconds } maxInterval `currentInterval` will be incremented to this
* @param { integer } incrementByFactorOf `currentInterval` is incremented by this factor
* @param { boolean } lazyStart Configure if timer is initialized on instantiation or lazily
*/
constructor({ callback, startingInterval, maxInterval, incrementByFactorOf, lazyStart }) {
this.cfg = {
callback,
startingInterval,
maxInterval,
incrementByFactorOf,
lazyStart,
};
this.state = {
intervalId: null,
currentInterval: startingInterval,
pageVisibility: 'visible',
};
this.initInterval();
}
/* public */
start() {
const cfg = this.cfg;
const state = this.state;
state.intervalId = window.setInterval(() => {
cfg.callback();
if (this.getCurrentInterval() === cfg.maxInterval) {
return;
}
this.incrementInterval();
this.resume();
}, this.getCurrentInterval());
}
// cancel the existing timer, setting the currentInterval back to startingInterval
cancel() {
this.setCurrentInterval(this.cfg.startingInterval);
this.stopTimer();
}
// start a timer, using the existing interval
resume() {
this.stopTimer(); // stop exsiting timer, in case timer was not previously stopped
this.start();
}
destroy() {
this.cancel();
$(document).off('visibilitychange').off('page:before-unload');
}
/* private */
initInterval() {
const cfg = this.cfg;
if (!cfg.lazyStart) {
this.start();
}
this.initVisibilityChangeHandling();
this.initPageUnloadHandling();
}
initVisibilityChangeHandling() {
// cancel interval when tab no longer shown (prevents cached pages from polling)
$(document)
.off('visibilitychange').on('visibilitychange', (e) => {
this.state.pageVisibility = e.target.visibilityState;
this.handleVisibilityChange();
});
}
initPageUnloadHandling() {
// prevent interval continuing after page change, when kept in cache by Turbolinks
$(document).on('page:before-unload', () => this.cancel());
}
handleVisibilityChange() {
const state = this.state;
const intervalAction = state.pageVisibility === 'hidden' ? this.cancel : this.resume;
intervalAction.apply(this);
}
getCurrentInterval() {
return this.state.currentInterval;
}
setCurrentInterval(newInterval) {
this.state.currentInterval = newInterval;
}
incrementInterval() {
const cfg = this.cfg;
const currentInterval = this.getCurrentInterval();
let nextInterval = currentInterval * cfg.incrementByFactorOf;
if (nextInterval > cfg.maxInterval) {
nextInterval = cfg.maxInterval;
}
this.setCurrentInterval(nextInterval);
}
stopTimer() {
const state = this.state;
state.intervalId = window.clearInterval(state.intervalId);
}
}
gl.SmartInterval = SmartInterval;
})(window.gl || (window.gl = {}));
//= require vue
//= require vue-resource
(() => {
/*
* SubbableResource can be extended to provide a pubsub-style service for one-off REST
* calls. Subscribe by passing a callback or render method you will use to handle responses.
*
* */
class SubbableResource {
constructor(resourcePath) {
this.endpoint = resourcePath;
// TODO: Switch to axios.create
this.resource = $.ajax;
this.subscribers = [];
}
subscribe(callback) {
this.subscribers.push(callback);
}
publish(newResponse) {
const responseCopy = _.extend({}, newResponse);
this.subscribers.forEach((fn) => {
fn(responseCopy);
});
return newResponse;
}
get(payload) {
return this.resource(payload)
.then(data => this.publish(data));
}
post(payload) {
return this.resource(payload)
.then(data => this.publish(data));
}
put(payload) {
return this.resource(payload)
.then(data => this.publish(data));
}
delete(payload) {
return this.resource(payload)
.then(data => this.publish(data));
}
}
gl.SubbableResource = SubbableResource;
})(window.gl || (window.gl = {}));
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
/* global Turbolinks */
(function() {
this.TreeView = (function() {
function TreeView() {
......
......@@ -206,6 +206,7 @@
}
});
} else {
this.currentSelectedDate = '';
return $('.user-calendar-activities').html('');
}
};
......
......@@ -80,6 +80,7 @@
border-radius: 0;
border: none;
height: auto;
width: 100%;
margin: 0;
align-self: center;
}
......
......@@ -254,3 +254,32 @@
.content-block-small {
padding: 10px 0;
}
.empty-state {
margin: 100px 0 0;
.text-content {
max-width: 460px;
margin: 0 auto;
padding: $gl-padding;
}
.svg-content {
text-align: center;
svg {
max-width: 425px;
width: 100%;
padding: $gl-padding;
}
}
@media(max-width: $screen-xs-max) {
margin-top: 50px;
text-align: center;
.btn {
width: 100%;
}
}
}
......@@ -15,7 +15,7 @@
@include btn-default;
}
@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border) {
@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border, $active-background, $active-border) {
background-color: $background;
color: $text;
border-color: $border;
......@@ -23,8 +23,14 @@
&:hover,
&:focus {
background-color: $hover-background;
color: $hover-text;
border-color: $hover-border;
color: $hover-text;
}
&:active {
background-color: $active-background;
border-color: $active-border;
color: $hover-text;
}
}
......@@ -82,11 +88,11 @@
}
@mixin btn-gray {
@include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, $gl-gray-dark);
@include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, $gl-gray-dark);
}
@mixin btn-white {
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $gl-text-color);
}
@mixin btn-with-margin {
......@@ -139,11 +145,11 @@
&.btn-new,
&.btn-create,
&.btn-save {
@include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light);
@include btn-outline($white-light, $border-green-light, $border-green-light, $green-light, $white-light, $border-green-light, $green-normal, $border-green-normal);
}
&.btn-remove {
@include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
@include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
}
}
......@@ -165,11 +171,11 @@
}
&.btn-close {
@include btn-outline($white-light, $orange-normal, $orange-normal, $orange-light, $white-light, $orange-light);
@include btn-outline($white-light, $border-orange-light, $border-orange-light, $orange-light, $white-light, $border-orange-light, $orange-normal, $border-orange-normal);
}
&.btn-spam {
@include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
@include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
}
&.btn-danger,
......@@ -199,7 +205,7 @@
}
.fa-caret-down,
.fa-caret-up {
.fa-chevron-down {
margin-left: 5px;
}
......@@ -351,7 +357,7 @@
.btn-inverted {
&-secondary {
@include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light);
@include btn-outline($white-light, $border-blue-light, $border-blue-light, $blue-light, $white-light, $border-blue-light, $blue-normal, $border-blue-normal);
}
}
......
......@@ -299,6 +299,10 @@ table {
.well {
margin-bottom: $gl-padding;
hr {
border-color: $gray-darker;
}
}
.search_box {
......
......@@ -8,6 +8,12 @@
}
}
@mixin chevron-active {
.fa-chevron-down {
color: $dropdown-toggle-hover-icon-color;
}
}
.open {
.dropdown-menu,
.dropdown-menu-nav {
......@@ -19,53 +25,27 @@
}
}
.dropdown-toggle,
.dropdown-menu-toggle {
@include chevron-active;
border-color: $dropdown-toggle-hover-border-color;
.fa {
color: $dropdown-toggle-hover-icon-color;
}
}
}
.dropdown-menu-toggle {
position: relative;
width: 160px;
padding: 6px 20px 6px 10px;
.dropdown-toggle {
padding: 6px 8px 6px 10px;
background-color: $dropdown-toggle-bg;
color: $dropdown-toggle-color;
font-size: 15px;
text-align: left;
border: 1px solid $border-color;
border-radius: $border-radius-base;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
.fa {
position: absolute;
top: 10px;
right: 8px;
color: $dropdown-toggle-icon-color;
&.fa-spinner {
font-size: 16px;
margin-top: -8px;
}
}
&.no-outline {
outline: 0;
}
&:hover, {
border-color: $dropdown-toggle-hover-border-color;
.fa {
color: $dropdown-toggle-hover-icon-color;
}
}
&.large {
width: 200px;
}
......@@ -86,6 +66,51 @@
max-width: 100%;
padding-right: 25px;
}
.fa {
color: $dropdown-toggle-icon-color;
}
.fa-chevron-down {
font-size: $dropdown-chevron-size;
position: relative;
top: -3px;
margin-left: 5px;
}
&:hover {
@include chevron-active;
border-color: $dropdown-toggle-hover-border-color;
}
&:focus:active {
@include chevron-active;
border-color: $dropdown-toggle-active-border-color;
}
}
.dropdown-menu-toggle {
@extend .dropdown-toggle;
padding-right: 20px;
position: relative;
width: 160px;
text-overflow: ellipsis;
overflow: hidden;
.fa {
position: absolute;
&.fa-spinner {
font-size: 16px;
margin-top: -8px;
}
}
.fa-chevron-down {
position: absolute;
top: 11px;
right: 8px;
}
}
.dropdown-menu,
......
......@@ -68,6 +68,46 @@ label {
}
}
.help-form .form-group {
margin-left: 0;
margin-right: 0;
.control-label {
font-weight: bold;
padding-top: 4px;
}
.form-control {
height: 29px;
background: $white-light;
font-family: $monospace_font;
}
.input-group-btn .btn {
padding: 3px $gl-btn-padding;
background-color: $gray-light;
border: 1px solid $border-color;
}
.text-block {
line-height: 0.8;
padding-top: 9px;
code {
line-height: 1.8;
}
}
@media(max-width: $screen-sm-min) {
padding: 0 $gl-padding;
.control-label,
.text-block {
padding-left: 0;
}
}
}
.fieldset-form fieldset {
margin-bottom: 20px;
}
......@@ -167,4 +207,3 @@ label {
color: $gl-text-color;
}
}
......@@ -39,4 +39,8 @@
&.status-box-expired {
background: #cea61b;
}
&.status-box-upcoming {
background: #8f8f8f;
}
}
......@@ -51,19 +51,26 @@
margin-bottom: -1px;
font-size: 15px;
line-height: 28px;
color: #959494;
color: $note-toolbar-color;
border-bottom: 2px solid transparent;
&:hover,
&:active,
&:focus {
text-decoration: none;
border-bottom: 2px solid $gray-darkest;
color: $black;
.badge {
color: $black;
}
}
}
&.active a {
border-bottom: 2px solid $link-underline-blue;
color: $black;
font-weight: 600;
}
.badge {
......@@ -85,14 +92,20 @@
li {
&.active a {
border-bottom: none;
color: $link-underline-blue;
}
a {
margin: 0;
padding: 11px 10px 9px;
}
&.active a {
border-bottom: none;
color: $link-underline-blue;
&:hover,
&:active,
&:focus {
border-bottom: none;
}
}
}
}
......@@ -310,37 +323,9 @@
height: 51px;
li {
a {
padding-top: 10px;
}
a,
i {
color: $layout-link-gray;
}
&.active {
a,
i {
color: $black;
}
svg {
path,
polygon {
fill: $black;
}
}
}
&:hover {
a,
i {
color: $black;
}
}
}
}
}
......
......@@ -12,67 +12,71 @@ $sidebar-breakpoint: 1024px;
/*
* Color schema
*/
$darken-normal-factor: 7%;
$darken-dark-factor: 10%;
$darken-border-factor: 5%;
$white-light: #fff;
$white-normal: #ededed;
$white-dark: #ececec;
$white-normal: darken($white-light, $darken-normal-factor);
$white-dark: darken($white-light, $darken-dark-factor);
$gray-lightest: #fdfdfd;
$gray-light: #fafafa;
$gray-lighter: #f9f9f9;
$gray-normal: #f5f5f5;
$gray-dark: #ededed;
$gray-normal: darken($gray-light, $darken-normal-factor);
$gray-dark: darken($gray-light, $darken-dark-factor);
$gray-darker: #eee;
$gray-darkest: #c9c9c9;
$green-light: #38ae67;
$green-normal: #2faa60;
$green-dark: #2ca05b;
$green-light: #3cbd70;
$green-normal: darken($green-light, $darken-normal-factor);
$green-dark: darken($green-light, $darken-dark-factor);
$blue-light: #2ea8e5;
$blue-normal: #2d9fd8;
$blue-dark: #2897ce;
$blue-normal: darken($blue-light, $darken-normal-factor);
$blue-dark: darken($blue-light, $darken-dark-factor);
$blue-medium-light: #3498cb;
$blue-medium: #2f8ebf;
$blue-medium-dark: #2d86b4;
$blue-medium: darken($blue-medium-light, $darken-normal-factor);
$blue-medium-dark: darken($blue-medium-light, $darken-dark-factor);
$blue-light-transparent: rgba(44, 159, 216, 0.05);
$orange-light: #fc8a51;
$orange-normal: #e75e40;
$orange-dark: #ce5237;
$orange-normal: darken($orange-light, $darken-normal-factor);
$orange-dark: darken($orange-light, $darken-dark-factor);
$red-light: #e52c5a;
$red-normal: #d22852;
$red-dark: darken($red-normal, 5%);
$red-normal: darken($red-light, $darken-normal-factor);
$red-dark: darken($red-light, $darken-dark-factor);
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
$border-white-light: #f1f2f4;
$border-white-normal: #d6dae2;
$border-white-dark: #c6cacf;
$border-white-light: darken($white-light, $darken-border-factor);
$border-white-normal: darken($white-normal, $darken-border-factor);
$border-white-dark: darken($white-dark, $darken-border-factor);
$border-gray-light: #dcdcdc;
$border-gray-normal: #d7d7d7;
$border-gray-dark: #c6cacf;
$border-gray-light: darken($gray-light, $darken-border-factor);
$border-gray-normal: darken($gray-normal, $darken-border-factor);
$border-gray-dark: darken($gray-dark, $darken-border-factor);
$border-green-extra-light: #9adb84;
$border-green-light: #2faa60;
$border-green-normal: #2ca05b;
$border-green-dark: #279654;
$border-green-light: darken($green-light, $darken-border-factor);
$border-green-normal: darken($green-normal, $darken-border-factor);
$border-green-dark: darken($green-dark, $darken-border-factor);
$border-blue-light: #2d9fd8;
$border-blue-normal: #2897ce;
$border-blue-dark: #258dc1;
$border-blue-light: darken($blue-light, $darken-border-factor);
$border-blue-normal: darken($blue-normal, $darken-border-factor);
$border-blue-dark: darken($blue-dark, $darken-border-factor);
$border-orange-light: #fc6d26;
$border-orange-normal: #ce5237;
$border-orange-dark: #c14e35;
$border-orange-light: darken($orange-light, $darken-border-factor);
$border-orange-normal: darken($orange-normal, $darken-border-factor);
$border-orange-dark: darken($orange-dark, $darken-border-factor);
$border-red-light: #d22852;
$border-red-normal: #ca264f;
$border-red-dark: darken($border-red-normal, 5%);
$border-red-light: darken($red-light, $darken-border-factor);
$border-red-normal: darken($red-normal, $darken-border-factor);
$border-red-dark: darken($red-dark, $darken-border-factor);
$help-well-bg: $gray-light;
$help-well-border: #e5e5e5;
......@@ -216,7 +220,7 @@ $dropdown-bg: #fff;
$dropdown-link-color: #555;
$dropdown-link-hover-bg: $row-hover;
$dropdown-empty-row-bg: rgba(#000, .04);
$dropdown-border-color: rgba(#000, .1);
$dropdown-border-color: $border-color;
$dropdown-shadow-color: rgba(#000, .1);
$dropdown-divider-color: rgba(#000, .1);
$dropdown-header-color: #959494;
......@@ -225,13 +229,15 @@ $dropdown-input-color: #555;
$dropdown-input-focus-border: $focus-border-color;
$dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4);
$dropdown-loading-bg: rgba(#fff, .6);
$dropdown-chevron-size: 10px;
$dropdown-toggle-bg: #fff;
$dropdown-toggle-color: #626262;
$dropdown-toggle-border-color: #eaeaea;
$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 15%);
$dropdown-toggle-color: #5c5c5c;
$dropdown-toggle-border-color: #e5e5e5;
$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 13%);
$dropdown-toggle-active-border-color: darken($dropdown-toggle-border-color, 14%);
$dropdown-toggle-icon-color: #c4c4c4;
$dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color;
$dropdown-toggle-hover-icon-color: darken($dropdown-toggle-icon-color, 7%);
/*
* Buttons
......@@ -255,7 +261,7 @@ $search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 220px;
$location-badge-color: #aaa;
$location-badge-bg: $gray-normal;
$location-badge-bg: $dark-background-color;
$location-badge-active-bg: #4f91f8;
$location-icon-color: #e7e9ed;
$location-icon-active-color: #807e7e;
......
......@@ -243,7 +243,7 @@
}
.issue-boards-search {
width: 335px;
width: 290px;
.form-control {
display: inline-block;
......
......@@ -18,6 +18,31 @@
.environments {
table-layout: fixed;
.environments-commit,
.environments-actions,
.environments-deploy,
.environments-build,
.environments-date {
position: static;
float: none;
display: table-cell;
}
.environments-commit,
.environments-actions {
width: 20%;
}
.environments-deploy,
.environments-build,
.environments-date {
width: 10%;
}
.environments-name {
width: 30%;
}
.deployment-column {
.avatar {
float: none;
......
// CI icon colors
.ci-status-icon-success {
color: $gl-success;
.ci-status-icon {
&-created {
fill: $gray-darkest;
svg {
fill: $gl-success;
}
}
.ci-status-icon-failed {
color: $gl-danger;
svg {
fill: $gl-danger;
}
}
.ci-status-icon-pending,
.ci-status-icon-success_with_warnings {
color: $gl-warning;
svg {
fill: $gl-warning;
}
}
.ci-status-icon-running {
color: $blue-normal;
svg {
fill: $blue-normal;
}
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found {
color: $gl-gray;
svg {
fill: $gl-gray;
}
}
&-skipped,
&-canceled {
fill: $gl-text-color;
.ci-status-icon-created,
.ci-status-icon-skipped {
color: $gray-darkest;
svg {
fill: $gray-darkest;
}
}
......@@ -132,7 +132,7 @@
display: none;
}
.btn-clipboard {
.btn-clipboard:hover {
color: $gl-gray;
}
}
......@@ -235,6 +235,10 @@
padding-bottom: 10px;
color: #999;
&:hover {
color: $gl-gray;
}
span {
display: block;
margin-top: 0;
......@@ -244,15 +248,17 @@
display: none;
}
.avatar:hover {
border-color: #999;
}
.btn-clipboard {
border: none;
color: #999;
&:hover {
background: transparent;
}
i {
color: #999;
color: $gl-gray;
}
}
}
......
......@@ -24,7 +24,7 @@
.accept_merge_request {
&.ci-pending,
&.ci-running {
@include btn-orange;
@include btn-blue;
}
&.ci-skipped,
......@@ -62,6 +62,7 @@
.ci_widget {
border-bottom: 1px solid $well-inner-border;
color: $gl-gray;
svg {
margin-right: 4px;
......@@ -70,48 +71,12 @@
overflow: visible;
}
&.ci-success {
color: $gl-success;
a.environment,
a.pipeline {
color: inherit;
}
}
&.ci-success_with_warnings {
color: $gl-success;
i {
color: $gl-warning;
}
}
&.ci-skipped {
background-color: #eee;
color: #888;
}
&.ci-pending {
color: $gl-warning;
}
&.ci-running {
color: $blue-normal;
}
&.ci-failed,
&.ci-error {
color: $gl-danger;
}
&.ci-canceled {
color: $gl-gray;
}
a.monospace {
color: inherit;
}
}
.mr-widget-body,
......
......@@ -43,12 +43,25 @@ ul.notes {
}
.system-note-message {
text-transform: lowercase;
display: inline-block;
&::first-letter {
text-transform: lowercase;
}
a {
color: $gl-link-color;
text-decoration: none;
}
p {
display: inline-block;
margin: 0;
&::first-letter {
text-transform: lowercase;
}
}
}
.timeline-content {
......@@ -62,6 +75,13 @@ ul.notes {
display: none;
padding: 10px 0 0;
cursor: pointer;
position: relative;
z-index: 2;
&:hover {
color: $gl-link-color;
text-decoration: underline;
}
}
.note-text {
......@@ -87,14 +107,24 @@ ul.notes {
display: none;
}
p:last-child {
a {
color: $gl-text-color;
&:hover {
color: $gl-link-color;
}
}
}
&::after {
content: '';
width: 100%;
height: 20px;
height: 67px;
position: absolute;
left: 0;
bottom: 50px;
background: linear-gradient(rgba($gray-light, .3) 0, $white-light);
bottom: 0;
background: linear-gradient(rgba($gray-light, 0.1) -100px, $white-light 100%);
}
&.hide-shade {
......@@ -188,11 +218,6 @@ ul.notes {
padding-bottom: 3px;
padding-right: 20px;
p {
display: inline;
margin: 0;
}
@media (min-width: $screen-sm-min) {
padding-right: 0;
}
......
......@@ -91,14 +91,6 @@
}
}
.ci-status {
svg {
top: 1px;
margin-right: 0;
}
}
a:hover {
text-decoration: none;
}
......@@ -195,7 +187,7 @@
width: 8px;
position: absolute;
right: -7px;
bottom: 8px;
bottom: 9px;
border-bottom: 2px solid $border-color;
}
}
......@@ -691,10 +683,3 @@
}
}
}
.ci-status-icon-created {
svg {
fill: $gray-darkest;
}
}
......@@ -876,3 +876,11 @@ pre.light-well {
pointer-events: none;
}
}
.variables-table {
table-layout: fixed;
.variable-key {
width: 30%;
}
}
......@@ -20,3 +20,7 @@
.danger-title {
color: $gl-danger;
}
.service-settings .control-label {
padding-top: 0;
}
......@@ -11,80 +11,108 @@
text-decoration: none;
}
svg {
height: 13px;
width: 13px;
position: relative;
top: 1px;
margin-right: 3px;
overflow: visible;
}
&.ci-failed {
color: $gl-danger;
border-color: $gl-danger;
&:not(span):hover {
background-color: rgba( $gl-danger, .07);
}
svg {
fill: $gl-danger;
}
}
&.ci-success,
&.ci-success_with_warnings {
color: $gl-success;
border-color: $gl-success;
&:not(span):hover {
background-color: rgba( $gl-success, .07);
}
svg {
fill: $gl-success;
}
}
&.ci-info {
color: $gl-info;
border-color: $gl-info;
&:not(span):hover {
background-color: rgba( $gl-info, .07);
}
svg {
fill: $gl-info;
}
}
&.ci-canceled,
&.ci-skipped,
&.ci-disabled {
color: $gl-gray;
border-color: $gl-gray;
&:not(span):hover {
background-color: rgba( $gl-gray, .07);
}
svg {
fill: $gl-gray;
}
}
&.ci-pending {
color: $gl-warning;
border-color: $gl-warning;
&:not(span):hover {
background-color: rgba( $gl-warning, .07);
}
svg {
fill: $gl-warning;
}
}
&.ci-running {
color: $blue-normal;
border-color: $blue-normal;
&:not(span):hover {
background-color: rgba( $blue-normal, .07);
}
svg {
fill: $blue-normal;
}
}
&.ci-created {
&.ci-created,
&.ci-skipped {
color: $table-text-gray;
border-color: $table-text-gray;
&:not(span):hover {
background-color: rgba( $table-text-gray, .07);
}
svg {
fill: $table-text-gray;
}
}
svg {
height: 13px;
width: 13px;
position: relative;
top: 1px;
margin: 0 3px;
overflow: visible;
}
}
.ci-status-icon-success {
color: $gl-success;
}
.ci-status-icon-failed {
color: $gl-danger;
}
.ci-status-icon-pending,
.ci-status-icon-success_with_warning {
color: $gl-warning;
}
.ci-status-icon-running {
color: $blue-normal;
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found,
.ci-status-icon-skipped {
color: $gl-gray;
}
}
......
......@@ -112,6 +112,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:koding_enabled,
:koding_url,
:email_author_in_body,
:html_emails_enabled,
:repository_checks_enabled,
:metrics_packet_size,
:send_user_confirmation_email,
......
......@@ -49,6 +49,14 @@ class ApplicationController < ActionController::Base
render_404
end
def route_not_found
if current_user
not_found
else
redirect_to new_user_session_path
end
end
protected
# This filter handles both private tokens and personal access tokens
......@@ -224,7 +232,7 @@ class ApplicationController < ActionController::Base
end
def require_email
if current_user && current_user.temp_oauth_email?
if current_user && current_user.temp_oauth_email? && session[:impersonator_id].nil?
redirect_to profile_path, notice: 'Please complete your profile with email address' and return
end
end
......
......@@ -11,7 +11,7 @@ class AutocompleteController < ApplicationController
@users = @users.reorder(:name)
@users = @users.page(params[:page])
if params[:todo_filter].present?
if params[:todo_filter].present? && current_user
@users = @users.todo_authors(current_user.id, params[:todo_state_filter])
end
......
module LfsHelper
include Gitlab::Routing.url_helpers
# This concern assumes:
# - a `#project` accessor
# - a `#user` accessor
# - a `#authentication_result` accessor
# - a `#can?(object, action, subject)` method
# - a `#ci?` method
# - a `#download_request?` method
# - a `#upload_request?` method
# - a `#has_authentication_ability?(ability)` method
module LfsRequest
extend ActiveSupport::Concern
included do
before_action :require_lfs_enabled!
before_action :lfs_check_access!
end
private
def require_lfs_enabled!
return if Gitlab.config.lfs.enabled
......@@ -17,35 +33,15 @@ module LfsHelper
return if download_request? && lfs_download_access?
return if upload_request? && lfs_upload_access?
if project.public? || (user && user.can?(:read_project, project))
render_lfs_forbidden
if project.public? || can?(user, :read_project, project)
lfs_forbidden!
else
render_lfs_not_found
end
end
def lfs_download_access?
return false unless project.lfs_enabled?
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
end
def objects
@objects ||= (params[:objects] || []).to_a
end
def user_can_download_code?
has_authentication_ability?(:download_code) && can?(user, :download_code, project)
end
def build_can_download_code?
has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
end
def lfs_upload_access?
return false unless project.lfs_enabled?
has_authentication_ability?(:push_code) && can?(user, :push_code, project)
def lfs_forbidden!
render_lfs_forbidden
end
def render_lfs_forbidden
......@@ -70,6 +66,30 @@ module LfsHelper
)
end
def lfs_download_access?
return false unless project.lfs_enabled?
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
end
def lfs_upload_access?
return false unless project.lfs_enabled?
has_authentication_ability?(:push_code) && can?(user, :push_code, project)
end
def lfs_deploy_token?
authentication_result.lfs_deploy_token?(project)
end
def user_can_download_code?
has_authentication_ability?(:download_code) && can?(user, :download_code, project)
end
def build_can_download_code?
has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
end
def storage_project
@storage_project ||= begin
result = project
......@@ -82,4 +102,8 @@ module LfsHelper
result
end
end
def objects
@objects ||= (params[:objects] || []).to_a
end
end
module ToggleAwardEmoji
extend ActiveSupport::Concern
included do
before_action :authenticate_user!, only: [:toggle_award_emoji]
end
def toggle_award_emoji
authenticate_user!
name = params.require(:name)
if awardable.user_can_award?(current_user, name)
......
module WorkhorseRequest
extend ActiveSupport::Concern
included do
before_action :verify_workhorse_api!
end
private
def verify_workhorse_api!
Gitlab::Workhorse.verify_api_request!(request.headers)
end
end
......@@ -67,7 +67,7 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def milestone_params
params.require(:milestone).permit(:title, :description, :due_date, :state_event)
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
def milestone_path(title)
......
......@@ -6,9 +6,9 @@ class HelpController < ApplicationController
def index
@help_index = File.read(Rails.root.join('doc', 'README.md'))
# Prefix Markdown links with `help/` unless they already have been
# See http://rubular.com/r/ie2MlpdUMq
@help_index.gsub!(/(\]\()(\/?help\/)?([^\)\(]+\))/, '\1/help/\3')
# Prefix Markdown links with `help/` unless they are external links
# See http://rubular.com/r/MioSrVLK3S
@help_index.gsub!(%r{(\]\()(?!.+://)([^\)\(]+\))}, '\1/help/\2')
end
def show
......
......@@ -4,7 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController
@user.remove_avatar!
@user.save
@user.reset_events_cache
redirect_to profile_path
end
......
......@@ -20,7 +20,6 @@ class Projects::AvatarsController < Projects::ApplicationController
@project.remove_avatar!
@project.save
@project.reset_events_cache
redirect_to edit_project_path(@project)
end
......
......@@ -13,7 +13,6 @@ class Projects::BlobController < Projects::ApplicationController
before_action :assign_blob_vars
before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create]
before_action :from_merge_request, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
before_action :validate_diff_params, only: :diff
......@@ -39,14 +38,6 @@ class Projects::BlobController < Projects::ApplicationController
def update
@path = params[:file_path] if params[:file_path].present?
after_edit_path =
if from_merge_request && @target_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"##{hexdigest(@path)}"
else
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
end
create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
......@@ -124,9 +115,14 @@ class Projects::BlobController < Projects::ApplicationController
render_404
end
def from_merge_request
# If blob edit was initiated from merge request page
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
def after_edit_path
from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid])
if from_merge_request && @target_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"##{hexdigest(@path)}"
else
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
end
end
def editor_variables
......
......@@ -36,7 +36,7 @@ class Projects::BranchesController < Projects::ApplicationController
execute(branch_name, ref)
if params[:issue_iid]
issue = @project.issues.find_by(iid: params[:issue_iid])
issue = IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:issue_iid])
SystemNoteService.new_issue_branch(issue, @project, current_user, branch_name) if issue
end
......
......@@ -6,7 +6,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
before_action :authorize_read_cycle_analytics!
def show
@cycle_analytics = ::CycleAnalytics.new(@project, from: start_date(cycle_analytics_params))
@cycle_analytics = ::CycleAnalytics.new(@project, current_user, from: start_date(cycle_analytics_params))
stats_values, cycle_analytics_json = generate_cycle_analytics_data
......
......@@ -18,6 +18,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController
private
def download_request?
raise NotImplementedError
end
def upload_request?
raise NotImplementedError
end
def authenticate_user
@authentication_result = Gitlab::Auth::Result.new
......@@ -130,10 +138,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController
authentication_result.ci?(project)
end
def lfs_deploy_token?
authentication_result.lfs_deploy_token?(project)
end
def authentication_has_download_access?
has_authentication_ability?(:download_code) || has_authentication_ability?(:build_download_code)
end
......@@ -149,8 +153,4 @@ class Projects::GitHttpClientController < Projects::ApplicationController
def authentication_project
authentication_result.project
end
def verify_workhorse_api!
Gitlab::Workhorse.verify_api_request!(request.headers)
end
end
# This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpController < Projects::GitHttpClientController
before_action :verify_workhorse_api!
include WorkhorseRequest
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
......@@ -67,14 +65,18 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end
def render_denied
if user && user.can?(:read_project, project)
render plain: 'Access denied', status: :forbidden
if user && can?(user, :read_project, project)
render plain: access_denied_message, status: :forbidden
else
# Do not leak information about project existence
render_not_found
end
end
def access_denied_message
'Access denied'
end
def upload_pack_allowed?
return false unless Gitlab.config.gitlab_shell.upload_pack
......
class Projects::LfsApiController < Projects::GitHttpClientController
include LfsHelper
include LfsRequest
before_action :require_lfs_enabled!
before_action :lfs_check_access!, except: [:deprecated]
skip_before_action :lfs_check_access!, only: [:deprecated]
def batch
unless objects.present?
......@@ -31,6 +30,14 @@ class Projects::LfsApiController < Projects::GitHttpClientController
private
def download_request?
params[:operation] == 'download'
end
def upload_request?
params[:operation] == 'upload'
end
def existing_oids
@existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
......@@ -79,12 +86,4 @@ class Projects::LfsApiController < Projects::GitHttpClientController
}
}
end
def download_request?
params[:operation] == 'download'
end
def upload_request?
params[:operation] == 'upload'
end
end
class Projects::LfsStorageController < Projects::GitHttpClientController
include LfsHelper
include LfsRequest
include WorkhorseRequest
before_action :require_lfs_enabled!
before_action :lfs_check_access!
before_action :verify_workhorse_api!, only: [:upload_authorize]
skip_before_action :verify_workhorse_api!, only: [:download, :upload_finalize]
def download
lfs_object = LfsObject.find_by_oid(oid)
......
......@@ -325,16 +325,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present?
unless @merge_request.pipeline
unless @merge_request.head_pipeline
@status = :failed
return
end
if @merge_request.pipeline.active?
if @merge_request.head_pipeline.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
.execute(@merge_request)
@status = :merge_when_build_succeeds
elsif @merge_request.pipeline.success?
elsif @merge_request.head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
......@@ -398,7 +398,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def ci_status
pipeline = @merge_request.pipeline
pipeline = @merge_request.head_pipeline
if pipeline
status = pipeline.status
coverage = pipeline.try(:coverage)
......@@ -534,7 +535,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def define_widget_vars
@pipeline = @merge_request.pipeline
@pipeline = @merge_request.head_pipeline
end
def define_commit_vars
......@@ -563,11 +564,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_pipelines_vars
@pipelines = @merge_request.all_pipelines
if @pipelines.present? && @merge_request.commits.present?
@pipeline = @pipelines.first
@statuses = @pipeline.statuses.relevant
end
@pipeline = @merge_request.head_pipeline
@statuses_count = @pipeline.present? ? @pipeline.statuses.relevant.count : 0
end
def define_new_vars
......@@ -634,7 +632,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_when_build_succeeds_active?
params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active?
@merge_request.head_pipeline && @merge_request.head_pipeline.active?
end
def build_merge_request
......
......@@ -112,6 +112,6 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def milestone_params
params.require(:milestone).permit(:title, :description, :due_date, :state_event)
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
end
......@@ -197,6 +197,7 @@ class Projects::NotesController < Projects::ApplicationController
)
end
attrs[:commands_changes] = note.commands_changes unless attrs[:award]
attrs
end
......
......@@ -17,7 +17,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
flash[:notice] = "CI/CD Pipelines settings for '#{@project.name}' were successfully updated."
redirect_to namespace_project_pipelines_settings_path(@project.namespace, @project)
else
render 'index'
render 'show'
end
end
......
......@@ -16,13 +16,7 @@ class Projects::TodosController < Projects::ApplicationController
@issuable ||= begin
case params[:issuable_type]
when "issue"
issue = @project.issues.find(params[:issuable_id])
if can?(current_user, :read_issue, issue)
issue
else
render_404
end
IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
when "merge_request"
@project.merge_requests.find(params[:issuable_id])
end
......
......@@ -16,14 +16,12 @@
# label_name: string
# sort: string
#
require_relative 'projects_finder'
class IssuableFinder
NONE = '0'
attr_accessor :current_user, :params
def initialize(current_user, params)
def initialize(current_user, params = {})
@current_user = current_user
@params = params
end
......@@ -43,6 +41,14 @@ class IssuableFinder
sort(items)
end
def find(*params)
execute.find(*params)
end
def find_by(*params)
execute.find_by(*params)
end
def group
return @group if defined?(@group)
......
......@@ -12,7 +12,7 @@ class NotesFinder
when "commit"
project.notes.for_commit_id(target_id).non_diff_notes
when "issue"
project.issues.visible_to_user(current_user).find(target_id).notes.inc_author
IssuesFinder.new(current_user, project_id: project.id).find(target_id).notes.inc_author
when "merge_request"
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
when "snippet", "project_snippet"
......
......@@ -50,14 +50,14 @@ module ApplicationSettingsHelper
def restricted_level_checkboxes(help_block_id)
Gitlab::VisibilityLevel.options.map do |name, level|
checked = restricted_visibility_levels(true).include?(level)
css_class = 'btn'
css_class += ' active' if checked
checkbox_name = 'application_setting[restricted_visibility_levels][]'
css_class = checked ? 'active' : ''
checkbox_name = "application_setting[restricted_visibility_levels][]"
label_tag(checkbox_name, class: css_class) do
label_tag(name, class: css_class) do
check_box_tag(checkbox_name, level, checked,
autocomplete: 'off',
'aria-describedby' => help_block_id) + name
'aria-describedby' => help_block_id,
id: name) + visibility_level_icon(level) + name
end
end
end
......@@ -67,14 +67,14 @@ module ApplicationSettingsHelper
def import_sources_checkboxes(help_block_id)
Gitlab::ImportSources.options.map do |name, source|
checked = current_application_settings.import_sources.include?(source)
css_class = 'btn'
css_class += ' active' if checked
css_class = checked ? 'active' : ''
checkbox_name = 'application_setting[import_sources][]'
label_tag(checkbox_name, class: css_class) do
label_tag(name, class: css_class) do
check_box_tag(checkbox_name, source, checked,
autocomplete: 'off',
'aria-describedby' => help_block_id) + name
'aria-describedby' => help_block_id,
id: name.tr(' ', '_')) + name
end
end
end
......
......@@ -12,7 +12,7 @@ module BuildsHelper
build_url: namespace_project_build_url(@project.namespace, @project, @build, :json),
build_status: @build.status,
build_stage: @build.stage,
state1: @build.trace_with_state[:state]
log_state: @build.trace_with_state[:state].to_s
}
end
end
......@@ -5,7 +5,7 @@ module CiStatusHelper
end
def ci_status_with_icon(status, target = nil)
content = ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status)
content = ci_icon_for_status(status) + ci_label_for_status(status)
klass = "ci-status ci-#{status}"
if target
link_to content, target, class: klass
......
......@@ -43,7 +43,7 @@ module DropdownsHelper
default_label = data_attr[:default_label]
content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}")
output << icon('caret-down')
output << icon('chevron-down')
output.html_safe
end
end
......
......@@ -8,11 +8,7 @@ module GroupsHelper
group = Group.find_by(path: group)
end
if group && group.avatar.present?
group.avatar.url
else
image_path('no_group_avatar.png')
end
group.try(:avatar_url) || image_path('no_group_avatar.png')
end
def group_title(group, name = nil, url = nil)
......@@ -48,4 +44,8 @@ module GroupsHelper
"#{status.humanize} #{projects_lfs_status(group)}"
end
end
def group_issues(group)
IssuesFinder.new(current_user, group_id: group.id).execute
end
end
......@@ -143,6 +143,20 @@ module IssuablesHelper
end
end
def issuable_filter_params
[
:search,
:author_id,
:assignee_id,
:milestone_title,
:label_name
]
end
def issuable_filter_present?
issuable_filter_params.any? { |k| params.key?(k) }
end
private
def assigned_issuables_count(assignee, issuable_type, state)
......@@ -165,13 +179,9 @@ module IssuablesHelper
end
end
def issuable_filters_present
params[:search] || params[:author_id] || params[:assignee_id] || params[:milestone_title] || params[:label_name]
end
def issuables_count_for_state(issuable_type, state)
issuables_finder = public_send("#{issuable_type}_finder")
params = issuables_finder.params.merge(state: state)
finder = issuables_finder.class.new(issuables_finder.current_user, params)
......
......@@ -64,6 +64,8 @@ module IssuesHelper
'status-box-merged'
elsif item.closed?
'status-box-closed'
elsif item.respond_to?(:upcoming?) && item.upcoming?
'status-box-upcoming'
else
'status-box-open'
end
......
......@@ -86,6 +86,30 @@ module MilestonesHelper
days = milestone.remaining_days
content = content_tag(:strong, days)
content << " #{'day'.pluralize(days)} remaining"
elsif milestone.upcoming?
content_tag(:strong, 'Upcoming')
elsif milestone.start_date && milestone.start_date.past?
days = milestone.elapsed_days
content = content_tag(:strong, days)
content << " #{'day'.pluralize(days)} elapsed"
end
end
def milestone_date_range(milestone)
if milestone.start_date && milestone.due_date
"#{milestone.start_date.to_s(:medium)} - #{milestone.due_date.to_s(:medium)}"
elsif milestone.due_date
if milestone.due_date.past?
"expired on #{milestone.due_date.to_s(:medium)}"
else
"expires on #{milestone.due_date.to_s(:medium)}"
end
elsif milestone.start_date
if milestone.start_date.past?
"started on #{milestone.start_date.to_s(:medium)}"
else
"starts on #{milestone.start_date.to_s(:medium)}"
end
end
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.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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