Commit 00ca7adc authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into fix/rename-mwbs-to-merge-when-pipeline-succeeds

* master: (110 commits)
  Rewrite an HTTP link to use HTTPS
  Edit /spec/features/profiles/preferences_spec.rb to match changes in 084d90ac
  Add blue back to sub nav active
  Remove JSX/React eslint plugins.
  Fix a transient spec failure
  Adds hoverstates for collapsed Issue/Merge Request sidebar
  Moved groups above projects
  Add StackProf to the Gemfile, along with a utility to get a profile for a spec
  Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1
  Add a CHANGELOG entry
  Alert user when logged in user email is not the same as the invitation
  Expose timestamp in build entity used by serializer
  Rename `MergeRequest#pipeline` to `head_pipeline`
  Remove unnecessary database indexes
  CE-specific changes gitlab-org/gitlab-ee#1137
  Fixing typo & Clarifying Key name
  fix started_at check
  fix blob controller spec failure - updated not to use file-path-
  fix blob controller spec failure
  Merge branch 'jej-use-issuable-finder-instead-of-access-check' into 'security'
  ...

Conflicts:
	app/controllers/projects/merge_requests_controller.rb
	lib/api/merge_requests.rb
	spec/requests/api/merge_requests_spec.rb
parents adb3f3d4 7e5fa10b
/coverage/
/coverage-javascript/ /coverage-javascript/
/public/ /public/
/tmp/ /tmp/
......
{ {
"env": { "env": {
"jquery": true,
"browser": true, "browser": true,
"es6": true "es6": true
}, },
"extends": "airbnb", "extends": "airbnb-base",
"globals": { "globals": {
"$": false,
"_": false, "_": false,
"gl": false, "gl": false,
"gon": false, "gon": false
"jQuery": false
}, },
"plugins": [ "plugins": [
"filenames" "filenames"
......
...@@ -2,6 +2,26 @@ ...@@ -2,6 +2,26 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 8.14.1 (2016-11-28)
- Fix deselecting calendar days on contribution graph. !6453 (ClemMakesApps)
- Update grape entity to 0.6.0. !7491
- If Build running change accept merge request when build succeeds button from orange to blue. !7577
- Changed import sources buttons to checkboxes. !7598 (Luke "Jared" Bennett)
- Last minute CI Style tweaks for 8.14. !7643
- Fix exceptions when loading build trace. !7658
- Fix wrong template rendered when CI/CD settings aren't update successfully. !7665
- fixes last_deployment call environment is nil. !7671
- Sort builds by name within pipeline graph. !7681
- Correctly determine mergeability of MR with no discussions.
- Sidekiq stats in the admin area will now show correctly on different platforms. (blackst0ne)
- Fixed issue boards dragging card removing random issues.
- Fix information disclosure in `Projects::BlobController#update`.
- Fix missing access checks on issue lookup using IssuableFinder.
- Replace issue access checks with use of IssuableFinder.
- Non members cannot create labels through the API.
- Fix cycle analytics plan stage when commits are missing.
## 8.14.0 (2016-11-22) ## 8.14.0 (2016-11-22)
- Use separate email-token for incoming email and revert back the inactive feature. !5914 - Use separate email-token for incoming email and revert back the inactive feature. !5914
...@@ -202,6 +222,15 @@ entry. ...@@ -202,6 +222,15 @@ entry.
- Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix "Without projects" filter. !6611 (Ben Bodenmiller)
- Fix 404 when visit /projects page - Fix 404 when visit /projects page
## 8.13.7 (2016-11-28)
- fixes 500 error on project show when user is not logged in and project is still empty. !7376
- Update grape entity to 0.6.0. !7491
- Fix information disclosure in `Projects::BlobController#update`.
- Fix missing access checks on issue lookup using IssuableFinder.
- Replace issue access checks with use of IssuableFinder.
- Non members cannot create labels through the API.
## 8.13.6 (2016-11-17) ## 8.13.6 (2016-11-17)
- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002 - Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
......
...@@ -133,7 +133,7 @@ gem 'acts-as-taggable-on', '~> 4.0' ...@@ -133,7 +133,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
# Background jobs # Background jobs
gem 'sidekiq', '~> 4.2' gem 'sidekiq', '~> 4.2'
gem 'sidekiq-cron', '~> 0.4.0' gem 'sidekiq-cron', '~> 0.4.4'
gem 'redis-namespace', '~> 1.5.2' gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4' gem 'sidekiq-limit_fetch', '~> 3.4'
...@@ -309,6 +309,8 @@ group :development, :test do ...@@ -309,6 +309,8 @@ group :development, :test do
gem 'knapsack', '~> 1.11.0' gem 'knapsack', '~> 1.11.0'
gem 'activerecord_sane_schema_dumper', '0.2' gem 'activerecord_sane_schema_dumper', '0.2'
gem 'stackprof', '~> 0.2.10'
end end
group :test do group :test do
......
...@@ -614,7 +614,8 @@ GEM ...@@ -614,7 +614,8 @@ GEM
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.0) rubyzip (1.2.0)
rufus-scheduler (3.1.10) rufus-scheduler (3.3.0)
tzinfo
rugged (0.24.0) rugged (0.24.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
...@@ -650,10 +651,10 @@ GEM ...@@ -650,10 +651,10 @@ GEM
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (~> 1.5) rack-protection (~> 1.5)
redis (~> 3.2, >= 3.2.1) redis (~> 3.2, >= 3.2.1)
sidekiq-cron (0.4.0) sidekiq-cron (0.4.4)
redis-namespace (>= 1.5.2) redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24) rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.0.0) sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0) sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4) sidekiq (>= 4)
simplecov (0.12.0) simplecov (0.12.0)
...@@ -691,6 +692,7 @@ GEM ...@@ -691,6 +692,7 @@ GEM
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
stackprof (0.2.10)
state_machines (0.4.0) state_machines (0.4.0)
state_machines-activemodel (0.4.0) state_machines-activemodel (0.4.0)
activemodel (>= 4.1, < 5.1) activemodel (>= 4.1, < 5.1)
...@@ -925,7 +927,7 @@ DEPENDENCIES ...@@ -925,7 +927,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6) sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0) shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.2) sidekiq (~> 4.2)
sidekiq-cron (~> 0.4.0) sidekiq-cron (~> 0.4.4)
sidekiq-limit_fetch (~> 3.4) sidekiq-limit_fetch (~> 3.4)
simplecov (= 0.12.0) simplecov (= 0.12.0)
slack-notifier (~> 1.2.0) slack-notifier (~> 1.2.0)
...@@ -937,6 +939,7 @@ DEPENDENCIES ...@@ -937,6 +939,7 @@ DEPENDENCIES
spring-commands-teaspoon (~> 0.0.2) spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.7.0) sprockets (~> 3.7.0)
sprockets-es6 (~> 0.9.2) sprockets-es6 (~> 0.9.2)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0) state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6) sys-filesystem (~> 1.1.6)
teaspoon (~> 1.1.0) teaspoon (~> 1.1.0)
......
# GitLab # GitLab
[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) [![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) [![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) [![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 ...@@ -84,7 +84,7 @@ For more information please see the [architecture documentation](https://docs.gi
## UX design ## 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 ## Third-party applications
......
...@@ -10,10 +10,15 @@ ...@@ -10,10 +10,15 @@
}, },
template: ` template: `
<span class="total-time"> <span class="total-time">
<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.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.hours">{{ time.hours }} <span>hr</span></template>
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</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="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
</template>
<template v-else>
--
</template>
</span> </span>
`, `,
}); });
......
...@@ -208,6 +208,9 @@ ...@@ -208,6 +208,9 @@
new gl.ProtectedBranchCreate(); new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList(); new gl.ProtectedBranchEditList();
break; break;
case 'projects:variables:index':
new gl.ProjectVariables();
break;
} }
switch (path.first()) { switch (path.first()) {
case 'admin': case 'admin':
......
...@@ -181,7 +181,7 @@ ...@@ -181,7 +181,7 @@
<div class="environments-container"> <div class="environments-container">
<div class="environments-list-loading text-center" v-if="isLoading"> <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>
<div class="blank-state blank-state-no-icon" <div class="blank-state blank-state-no-icon"
......
...@@ -6,3 +6,19 @@ Array.prototype.first = function() { ...@@ -6,3 +6,19 @@ Array.prototype.first = function() {
Array.prototype.last = function() { Array.prototype.last = function() {
return this[this.length-1]; 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;
};
/* 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() { (function() {
this.LabelsSelect = (function() { this.LabelsSelect = (function() {
function LabelsSelect() { function LabelsSelect() {
......
/* 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 autosave */
/*= require autosize */ /*= require autosize */
......
(() => {
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;
})();
...@@ -80,6 +80,7 @@ ...@@ -80,6 +80,7 @@
border-radius: 0; border-radius: 0;
border: none; border: none;
height: auto; height: auto;
width: 100%;
margin: 0; margin: 0;
align-self: center; align-self: center;
} }
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
@include btn-default; @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; background-color: $background;
color: $text; color: $text;
border-color: $border; border-color: $border;
...@@ -23,8 +23,14 @@ ...@@ -23,8 +23,14 @@
&:hover, &:hover,
&:focus { &:focus {
background-color: $hover-background; background-color: $hover-background;
color: $hover-text;
border-color: $hover-border; border-color: $hover-border;
color: $hover-text;
}
&:active {
background-color: $active-background;
border-color: $active-border;
color: $hover-text;
} }
} }
...@@ -82,11 +88,11 @@ ...@@ -82,11 +88,11 @@
} }
@mixin btn-gray { @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 { @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 { @mixin btn-with-margin {
...@@ -139,11 +145,11 @@ ...@@ -139,11 +145,11 @@
&.btn-new, &.btn-new,
&.btn-create, &.btn-create,
&.btn-save { &.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 { &.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 @@ ...@@ -165,11 +171,11 @@
} }
&.btn-close { &.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 { &.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, &.btn-danger,
...@@ -199,7 +205,7 @@ ...@@ -199,7 +205,7 @@
} }
.fa-caret-down, .fa-caret-down,
.fa-caret-up { .fa-chevron-down {
margin-left: 5px; margin-left: 5px;
} }
...@@ -351,7 +357,7 @@ ...@@ -351,7 +357,7 @@
.btn-inverted { .btn-inverted {
&-secondary { &-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);
} }
} }
......
...@@ -8,6 +8,12 @@ ...@@ -8,6 +8,12 @@
} }
} }
@mixin chevron-active {
.fa-chevron-down {
color: $dropdown-toggle-hover-icon-color;
}
}
.open { .open {
.dropdown-menu, .dropdown-menu,
.dropdown-menu-nav { .dropdown-menu-nav {
...@@ -19,53 +25,27 @@ ...@@ -19,53 +25,27 @@
} }
} }
.dropdown-toggle,
.dropdown-menu-toggle { .dropdown-menu-toggle {
@include chevron-active;
border-color: $dropdown-toggle-hover-border-color; border-color: $dropdown-toggle-hover-border-color;
.fa {
color: $dropdown-toggle-hover-icon-color;
}
} }
} }
.dropdown-menu-toggle { .dropdown-toggle {
position: relative; padding: 6px 8px 6px 10px;
width: 160px;
padding: 6px 20px 6px 10px;
background-color: $dropdown-toggle-bg; background-color: $dropdown-toggle-bg;
color: $dropdown-toggle-color; color: $dropdown-toggle-color;
font-size: 15px; font-size: 15px;
text-align: left; text-align: left;
border: 1px solid $border-color; border: 1px solid $border-color;
border-radius: $border-radius-base; border-radius: $border-radius-base;
text-overflow: ellipsis;
white-space: nowrap; 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 { &.no-outline {
outline: 0; outline: 0;
} }
&:hover, {
border-color: $dropdown-toggle-hover-border-color;
.fa {
color: $dropdown-toggle-hover-icon-color;
}
}
&.large { &.large {
width: 200px; width: 200px;
} }
...@@ -86,6 +66,51 @@ ...@@ -86,6 +66,51 @@
max-width: 100%; max-width: 100%;
padding-right: 25px; 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, .dropdown-menu,
......
...@@ -51,19 +51,26 @@ ...@@ -51,19 +51,26 @@
margin-bottom: -1px; margin-bottom: -1px;
font-size: 15px; font-size: 15px;
line-height: 28px; line-height: 28px;
color: #959494; color: $note-toolbar-color;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
&:hover, &:hover,
&:active, &:active,
&:focus { &:focus {
text-decoration: none; text-decoration: none;
border-bottom: 2px solid $gray-darkest;
color: $black;
.badge {
color: $black;
}
} }
} }
&.active a { &.active a {
border-bottom: 2px solid $link-underline-blue; border-bottom: 2px solid $link-underline-blue;
color: $black; color: $black;
font-weight: 600;
} }
.badge { .badge {
...@@ -85,14 +92,20 @@ ...@@ -85,14 +92,20 @@
li { li {
&.active a {
border-bottom: none;
color: $link-underline-blue;
}
a { a {
margin: 0; margin: 0;
padding: 11px 10px 9px; padding: 11px 10px 9px;
}
&.active a { &:hover,
&:active,
&:focus {
border-bottom: none; border-bottom: none;
color: $link-underline-blue; }
} }
} }
} }
...@@ -310,37 +323,9 @@ ...@@ -310,37 +323,9 @@
height: 51px; height: 51px;
li { li {
a { a {
padding-top: 10px; 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; ...@@ -12,67 +12,71 @@ $sidebar-breakpoint: 1024px;
/* /*
* Color schema * Color schema
*/ */
$darken-normal-factor: 7%;
$darken-dark-factor: 10%;
$darken-border-factor: 5%;
$white-light: #fff; $white-light: #fff;
$white-normal: #ededed; $white-normal: darken($white-light, $darken-normal-factor);
$white-dark: #ececec; $white-dark: darken($white-light, $darken-dark-factor);
$gray-lightest: #fdfdfd; $gray-lightest: #fdfdfd;
$gray-light: #fafafa; $gray-light: #fafafa;
$gray-lighter: #f9f9f9; $gray-lighter: #f9f9f9;
$gray-normal: #f5f5f5; $gray-normal: darken($gray-light, $darken-normal-factor);
$gray-dark: #ededed; $gray-dark: darken($gray-light, $darken-dark-factor);
$gray-darker: #eee; $gray-darker: #eee;
$gray-darkest: #c9c9c9; $gray-darkest: #c9c9c9;
$green-light: #38ae67; $green-light: #3cbd70;
$green-normal: #2faa60; $green-normal: darken($green-light, $darken-normal-factor);
$green-dark: #2ca05b; $green-dark: darken($green-light, $darken-dark-factor);
$blue-light: #2ea8e5; $blue-light: #2ea8e5;
$blue-normal: #2d9fd8; $blue-normal: darken($blue-light, $darken-normal-factor);
$blue-dark: #2897ce; $blue-dark: darken($blue-light, $darken-dark-factor);
$blue-medium-light: #3498cb; $blue-medium-light: #3498cb;
$blue-medium: #2f8ebf; $blue-medium: darken($blue-medium-light, $darken-normal-factor);
$blue-medium-dark: #2d86b4; $blue-medium-dark: darken($blue-medium-light, $darken-dark-factor);
$blue-light-transparent: rgba(44, 159, 216, 0.05); $blue-light-transparent: rgba(44, 159, 216, 0.05);
$orange-light: #fc8a51; $orange-light: #fc8a51;
$orange-normal: #e75e40; $orange-normal: darken($orange-light, $darken-normal-factor);
$orange-dark: #ce5237; $orange-dark: darken($orange-light, $darken-dark-factor);
$red-light: #e52c5a; $red-light: #e52c5a;
$red-normal: #d22852; $red-normal: darken($red-light, $darken-normal-factor);
$red-dark: darken($red-normal, 5%); $red-dark: darken($red-light, $darken-dark-factor);
$black: #000; $black: #000;
$black-transparent: rgba(0, 0, 0, 0.3); $black-transparent: rgba(0, 0, 0, 0.3);
$border-white-light: #f1f2f4; $border-white-light: darken($white-light, $darken-border-factor);
$border-white-normal: #d6dae2; $border-white-normal: darken($white-normal, $darken-border-factor);
$border-white-dark: #c6cacf; $border-white-dark: darken($white-dark, $darken-border-factor);
$border-gray-light: #dcdcdc; $border-gray-light: darken($gray-light, $darken-border-factor);
$border-gray-normal: #d7d7d7; $border-gray-normal: darken($gray-normal, $darken-border-factor);
$border-gray-dark: #c6cacf; $border-gray-dark: darken($gray-dark, $darken-border-factor);
$border-green-extra-light: #9adb84; $border-green-extra-light: #9adb84;
$border-green-light: #2faa60; $border-green-light: darken($green-light, $darken-border-factor);
$border-green-normal: #2ca05b; $border-green-normal: darken($green-normal, $darken-border-factor);
$border-green-dark: #279654; $border-green-dark: darken($green-dark, $darken-border-factor);
$border-blue-light: #2d9fd8; $border-blue-light: darken($blue-light, $darken-border-factor);
$border-blue-normal: #2897ce; $border-blue-normal: darken($blue-normal, $darken-border-factor);
$border-blue-dark: #258dc1; $border-blue-dark: darken($blue-dark, $darken-border-factor);
$border-orange-light: #fc6d26; $border-orange-light: darken($orange-light, $darken-border-factor);
$border-orange-normal: #ce5237; $border-orange-normal: darken($orange-normal, $darken-border-factor);
$border-orange-dark: #c14e35; $border-orange-dark: darken($orange-dark, $darken-border-factor);
$border-red-light: #d22852; $border-red-light: darken($red-light, $darken-border-factor);
$border-red-normal: #ca264f; $border-red-normal: darken($red-normal, $darken-border-factor);
$border-red-dark: darken($border-red-normal, 5%); $border-red-dark: darken($red-dark, $darken-border-factor);
$help-well-bg: $gray-light; $help-well-bg: $gray-light;
$help-well-border: #e5e5e5; $help-well-border: #e5e5e5;
...@@ -216,7 +220,7 @@ $dropdown-bg: #fff; ...@@ -216,7 +220,7 @@ $dropdown-bg: #fff;
$dropdown-link-color: #555; $dropdown-link-color: #555;
$dropdown-link-hover-bg: $row-hover; $dropdown-link-hover-bg: $row-hover;
$dropdown-empty-row-bg: rgba(#000, .04); $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-shadow-color: rgba(#000, .1);
$dropdown-divider-color: rgba(#000, .1); $dropdown-divider-color: rgba(#000, .1);
$dropdown-header-color: #959494; $dropdown-header-color: #959494;
...@@ -225,13 +229,15 @@ $dropdown-input-color: #555; ...@@ -225,13 +229,15 @@ $dropdown-input-color: #555;
$dropdown-input-focus-border: $focus-border-color; $dropdown-input-focus-border: $focus-border-color;
$dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4); $dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4);
$dropdown-loading-bg: rgba(#fff, .6); $dropdown-loading-bg: rgba(#fff, .6);
$dropdown-chevron-size: 10px;
$dropdown-toggle-bg: #fff; $dropdown-toggle-bg: #fff;
$dropdown-toggle-color: #626262; $dropdown-toggle-color: #5c5c5c;
$dropdown-toggle-border-color: #eaeaea; $dropdown-toggle-border-color: #e5e5e5;
$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 15%); $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-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 * Buttons
...@@ -255,7 +261,7 @@ $search-input-border-color: rgba(#4688f1, .8); ...@@ -255,7 +261,7 @@ $search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow; $search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 220px; $search-input-width: 220px;
$location-badge-color: #aaa; $location-badge-color: #aaa;
$location-badge-bg: $gray-normal; $location-badge-bg: $dark-background-color;
$location-badge-active-bg: #4f91f8; $location-badge-active-bg: #4f91f8;
$location-icon-color: #e7e9ed; $location-icon-color: #e7e9ed;
$location-icon-active-color: #807e7e; $location-icon-active-color: #807e7e;
......
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
display: none; display: none;
} }
.btn-clipboard { .btn-clipboard:hover {
color: $gl-gray; color: $gl-gray;
} }
} }
...@@ -235,6 +235,10 @@ ...@@ -235,6 +235,10 @@
padding-bottom: 10px; padding-bottom: 10px;
color: #999; color: #999;
&:hover {
color: $gl-gray;
}
span { span {
display: block; display: block;
margin-top: 0; margin-top: 0;
...@@ -244,15 +248,17 @@ ...@@ -244,15 +248,17 @@
display: none; display: none;
} }
.avatar:hover {
border-color: #999;
}
.btn-clipboard { .btn-clipboard {
border: none; border: none;
color: #999;
&:hover { &:hover {
background: transparent; background: transparent;
} color: $gl-gray;
i {
color: #999;
} }
} }
} }
......
...@@ -876,3 +876,11 @@ pre.light-well { ...@@ -876,3 +876,11 @@ pre.light-well {
pointer-events: none; pointer-events: none;
} }
} }
.variables-table {
table-layout: fixed;
.variable-key {
width: 30%;
}
}
...@@ -112,6 +112,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -112,6 +112,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:koding_enabled, :koding_enabled,
:koding_url, :koding_url,
:email_author_in_body, :email_author_in_body,
:html_emails_enabled,
:repository_checks_enabled, :repository_checks_enabled,
:metrics_packet_size, :metrics_packet_size,
:send_user_confirmation_email, :send_user_confirmation_email,
......
module LfsHelper # This concern assumes:
include Gitlab::Routing.url_helpers # - 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! def require_lfs_enabled!
return if Gitlab.config.lfs.enabled return if Gitlab.config.lfs.enabled
...@@ -17,35 +33,15 @@ module LfsHelper ...@@ -17,35 +33,15 @@ module LfsHelper
return if download_request? && lfs_download_access? return if download_request? && lfs_download_access?
return if upload_request? && lfs_upload_access? return if upload_request? && lfs_upload_access?
if project.public? || (user && user.can?(:read_project, project)) if project.public? || can?(user, :read_project, project)
render_lfs_forbidden lfs_forbidden!
else else
render_lfs_not_found render_lfs_not_found
end end
end end
def lfs_download_access? def lfs_forbidden!
return false unless project.lfs_enabled? render_lfs_forbidden
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)
end end
def render_lfs_forbidden def render_lfs_forbidden
...@@ -70,6 +66,30 @@ module LfsHelper ...@@ -70,6 +66,30 @@ module LfsHelper
) )
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 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 def storage_project
@storage_project ||= begin @storage_project ||= begin
result = project result = project
...@@ -82,4 +102,8 @@ module LfsHelper ...@@ -82,4 +102,8 @@ module LfsHelper
result result
end end
end end
def objects
@objects ||= (params[:objects] || []).to_a
end
end end
module ToggleAwardEmoji module ToggleAwardEmoji
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do
before_action :authenticate_user!, only: [:toggle_award_emoji]
end
def toggle_award_emoji def toggle_award_emoji
authenticate_user!
name = params.require(:name) name = params.require(:name)
if awardable.user_can_award?(current_user, 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
...@@ -6,9 +6,9 @@ class HelpController < ApplicationController ...@@ -6,9 +6,9 @@ class HelpController < ApplicationController
def index def index
@help_index = File.read(Rails.root.join('doc', 'README.md')) @help_index = File.read(Rails.root.join('doc', 'README.md'))
# Prefix Markdown links with `help/` unless they already have been # Prefix Markdown links with `help/` unless they are external links
# See http://rubular.com/r/ie2MlpdUMq # See http://rubular.com/r/MioSrVLK3S
@help_index.gsub!(/(\]\()(\/?help\/)?([^\)\(]+\))/, '\1/help/\3') @help_index.gsub!(%r{(\]\()(?!.+://)([^\)\(]+\))}, '\1/help/\2')
end end
def show def show
......
...@@ -4,7 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController ...@@ -4,7 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController
@user.remove_avatar! @user.remove_avatar!
@user.save @user.save
@user.reset_events_cache
redirect_to profile_path redirect_to profile_path
end end
......
...@@ -20,7 +20,6 @@ class Projects::AvatarsController < Projects::ApplicationController ...@@ -20,7 +20,6 @@ class Projects::AvatarsController < Projects::ApplicationController
@project.remove_avatar! @project.remove_avatar!
@project.save @project.save
@project.reset_events_cache
redirect_to edit_project_path(@project) redirect_to edit_project_path(@project)
end end
......
...@@ -13,7 +13,6 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -13,7 +13,6 @@ class Projects::BlobController < Projects::ApplicationController
before_action :assign_blob_vars before_action :assign_blob_vars
before_action :commit, except: [:new, :create] before_action :commit, except: [:new, :create]
before_action :blob, 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 :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff] before_action :editor_variables, except: [:show, :preview, :diff]
before_action :validate_diff_params, only: :diff before_action :validate_diff_params, only: :diff
...@@ -39,14 +38,6 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -39,14 +38,6 @@ class Projects::BlobController < Projects::ApplicationController
def update def update
@path = params[:file_path] if params[:file_path].present? @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, create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit, failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
...@@ -124,9 +115,14 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -124,9 +115,14 @@ class Projects::BlobController < Projects::ApplicationController
render_404 render_404
end end
def from_merge_request def after_edit_path
# If blob edit was initiated from merge request page from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid])
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id]) 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 end
def editor_variables def editor_variables
......
...@@ -36,7 +36,7 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -36,7 +36,7 @@ class Projects::BranchesController < Projects::ApplicationController
execute(branch_name, ref) execute(branch_name, ref)
if params[:issue_iid] 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 SystemNoteService.new_issue_branch(issue, @project, current_user, branch_name) if issue
end end
......
...@@ -6,7 +6,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController ...@@ -6,7 +6,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
before_action :authorize_read_cycle_analytics! before_action :authorize_read_cycle_analytics!
def show 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 stats_values, cycle_analytics_json = generate_cycle_analytics_data
......
...@@ -18,6 +18,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController ...@@ -18,6 +18,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController
private private
def download_request?
raise NotImplementedError
end
def upload_request?
raise NotImplementedError
end
def authenticate_user def authenticate_user
@authentication_result = Gitlab::Auth::Result.new @authentication_result = Gitlab::Auth::Result.new
...@@ -130,10 +138,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController ...@@ -130,10 +138,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController
authentication_result.ci?(project) authentication_result.ci?(project)
end end
def lfs_deploy_token?
authentication_result.lfs_deploy_token?(project)
end
def authentication_has_download_access? def authentication_has_download_access?
has_authentication_ability?(:download_code) || has_authentication_ability?(:build_download_code) has_authentication_ability?(:download_code) || has_authentication_ability?(:build_download_code)
end end
...@@ -149,8 +153,4 @@ class Projects::GitHttpClientController < Projects::ApplicationController ...@@ -149,8 +153,4 @@ class Projects::GitHttpClientController < Projects::ApplicationController
def authentication_project def authentication_project
authentication_result.project authentication_result.project
end end
def verify_workhorse_api!
Gitlab::Workhorse.verify_api_request!(request.headers)
end
end end
# This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpController < Projects::GitHttpClientController 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-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push) # GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
...@@ -67,14 +65,18 @@ class Projects::GitHttpController < Projects::GitHttpClientController ...@@ -67,14 +65,18 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end end
def render_denied def render_denied
if user && user.can?(:read_project, project) if user && can?(user, :read_project, project)
render plain: 'Access denied', status: :forbidden render plain: access_denied_message, status: :forbidden
else else
# Do not leak information about project existence # Do not leak information about project existence
render_not_found render_not_found
end end
end end
def access_denied_message
'Access denied'
end
def upload_pack_allowed? def upload_pack_allowed?
return false unless Gitlab.config.gitlab_shell.upload_pack return false unless Gitlab.config.gitlab_shell.upload_pack
......
class Projects::LfsApiController < Projects::GitHttpClientController class Projects::LfsApiController < Projects::GitHttpClientController
include LfsHelper include LfsRequest
before_action :require_lfs_enabled! skip_before_action :lfs_check_access!, only: [:deprecated]
before_action :lfs_check_access!, except: [:deprecated]
def batch def batch
unless objects.present? unless objects.present?
...@@ -31,6 +30,14 @@ class Projects::LfsApiController < Projects::GitHttpClientController ...@@ -31,6 +30,14 @@ class Projects::LfsApiController < Projects::GitHttpClientController
private private
def download_request?
params[:operation] == 'download'
end
def upload_request?
params[:operation] == 'upload'
end
def existing_oids def existing_oids
@existing_oids ||= begin @existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid) storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
...@@ -79,12 +86,4 @@ class Projects::LfsApiController < Projects::GitHttpClientController ...@@ -79,12 +86,4 @@ class Projects::LfsApiController < Projects::GitHttpClientController
} }
} }
end end
def download_request?
params[:operation] == 'download'
end
def upload_request?
params[:operation] == 'upload'
end
end end
class Projects::LfsStorageController < Projects::GitHttpClientController class Projects::LfsStorageController < Projects::GitHttpClientController
include LfsHelper include LfsRequest
include WorkhorseRequest
before_action :require_lfs_enabled! skip_before_action :verify_workhorse_api!, only: [:download, :upload_finalize]
before_action :lfs_check_access!
before_action :verify_workhorse_api!, only: [:upload_authorize]
def download def download
lfs_object = LfsObject.find_by_oid(oid) lfs_object = LfsObject.find_by_oid(oid)
......
...@@ -329,17 +329,18 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -329,17 +329,18 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present? if params[:merge_when_build_succeeds].present?
unless @merge_request.pipeline unless @merge_request.head_pipeline
@status = :failed @status = :failed
return return
end end
if @merge_request.pipeline.active? if @merge_request.head_pipeline.active?
MergeRequests::MergeWhenPipelineSucceedsService MergeRequests::MergeWhenPipelineSucceedsService
.new(@project, current_user, merge_params) .new(@project, current_user, merge_params)
.execute(@merge_request) .execute(@merge_request)
@status = :merge_when_build_succeeds @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 # This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time # the tests finish at about the same time
MergeWorker.perform_async(@merge_request.id, current_user.id, params) MergeWorker.perform_async(@merge_request.id, current_user.id, params)
...@@ -403,7 +404,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -403,7 +404,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def ci_status def ci_status
pipeline = @merge_request.pipeline pipeline = @merge_request.head_pipeline
if pipeline if pipeline
status = pipeline.status status = pipeline.status
coverage = pipeline.try(:coverage) coverage = pipeline.try(:coverage)
...@@ -539,7 +541,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -539,7 +541,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def define_widget_vars def define_widget_vars
@pipeline = @merge_request.pipeline @pipeline = @merge_request.head_pipeline
end end
def define_commit_vars def define_commit_vars
...@@ -568,8 +570,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -568,8 +570,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_pipelines_vars def define_pipelines_vars
@pipelines = @merge_request.all_pipelines @pipelines = @merge_request.all_pipelines
@pipeline = @merge_request.pipeline @pipeline = @merge_request.head_pipeline
@statuses = @pipeline.statuses.relevant if @pipeline.present? @statuses_count = @pipeline.present? ? @pipeline.statuses.relevant.count : 0
end end
def define_new_vars def define_new_vars
...@@ -636,7 +638,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -636,7 +638,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_when_build_succeeds_active? def merge_when_build_succeeds_active?
params[:merge_when_build_succeeds].present? && params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active? @merge_request.head_pipeline && @merge_request.head_pipeline.active?
end end
def build_merge_request def build_merge_request
......
...@@ -16,13 +16,7 @@ class Projects::TodosController < Projects::ApplicationController ...@@ -16,13 +16,7 @@ class Projects::TodosController < Projects::ApplicationController
@issuable ||= begin @issuable ||= begin
case params[:issuable_type] case params[:issuable_type]
when "issue" when "issue"
issue = @project.issues.find(params[:issuable_id]) IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
if can?(current_user, :read_issue, issue)
issue
else
render_404
end
when "merge_request" when "merge_request"
@project.merge_requests.find(params[:issuable_id]) @project.merge_requests.find(params[:issuable_id])
end end
......
...@@ -16,14 +16,12 @@ ...@@ -16,14 +16,12 @@
# label_name: string # label_name: string
# sort: string # sort: string
# #
require_relative 'projects_finder'
class IssuableFinder class IssuableFinder
NONE = '0' NONE = '0'
attr_accessor :current_user, :params attr_accessor :current_user, :params
def initialize(current_user, params) def initialize(current_user, params = {})
@current_user = current_user @current_user = current_user
@params = params @params = params
end end
...@@ -43,6 +41,14 @@ class IssuableFinder ...@@ -43,6 +41,14 @@ class IssuableFinder
sort(items) sort(items)
end end
def find(*params)
execute.find(*params)
end
def find_by(*params)
execute.find_by(*params)
end
def group def group
return @group if defined?(@group) return @group if defined?(@group)
......
...@@ -12,7 +12,7 @@ class NotesFinder ...@@ -12,7 +12,7 @@ class NotesFinder
when "commit" when "commit"
project.notes.for_commit_id(target_id).non_diff_notes project.notes.for_commit_id(target_id).non_diff_notes
when "issue" 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" when "merge_request"
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
when "snippet", "project_snippet" when "snippet", "project_snippet"
......
...@@ -43,7 +43,7 @@ module DropdownsHelper ...@@ -43,7 +43,7 @@ module DropdownsHelper
default_label = data_attr[:default_label] 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 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 = 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 output.html_safe
end end
end end
......
...@@ -8,11 +8,7 @@ module GroupsHelper ...@@ -8,11 +8,7 @@ module GroupsHelper
group = Group.find_by(path: group) group = Group.find_by(path: group)
end end
if group && group.avatar.present? group.try(:avatar_url) || image_path('no_group_avatar.png')
group.avatar.url
else
image_path('no_group_avatar.png')
end
end end
def group_title(group, name = nil, url = nil) def group_title(group, name = nil, url = nil)
......
...@@ -143,6 +143,20 @@ module IssuablesHelper ...@@ -143,6 +143,20 @@ module IssuablesHelper
end end
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 private
def assigned_issuables_count(assignee, issuable_type, state) def assigned_issuables_count(assignee, issuable_type, state)
...@@ -165,10 +179,6 @@ module IssuablesHelper ...@@ -165,10 +179,6 @@ module IssuablesHelper
end end
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) def issuables_count_for_state(issuable_type, state)
issuables_finder = public_send("#{issuable_type}_finder") issuables_finder = public_send("#{issuable_type}_finder")
......
...@@ -4,6 +4,7 @@ module Emails ...@@ -4,6 +4,7 @@ module Emails
setup_note_mail(note_id, recipient_id) setup_note_mail(note_id, recipient_id)
@commit = @note.noteable @commit = @note.noteable
@discussion = @note.to_discussion if @note.diff_note?
@target_url = namespace_project_commit_url(*note_target_url_options) @target_url = namespace_project_commit_url(*note_target_url_options)
mail_answer_thread(@commit, mail_answer_thread(@commit,
...@@ -24,6 +25,7 @@ module Emails ...@@ -24,6 +25,7 @@ module Emails
setup_note_mail(note_id, recipient_id) setup_note_mail(note_id, recipient_id)
@merge_request = @note.noteable @merge_request = @note.noteable
@discussion = @note.to_discussion if @note.diff_note?
@target_url = namespace_project_merge_request_url(*note_target_url_options) @target_url = namespace_project_merge_request_url(*note_target_url_options)
mail_answer_thread(@merge_request, note_thread_options(recipient_id)) mail_answer_thread(@merge_request, note_thread_options(recipient_id))
end end
......
...@@ -317,7 +317,7 @@ module Ci ...@@ -317,7 +317,7 @@ module Ci
def merge_requests def merge_requests
@merge_requests ||= project.merge_requests @merge_requests ||= project.merge_requests
.where(source_branch: self.ref) .where(source_branch: self.ref)
.select { |merge_request| merge_request.pipeline.try(:id) == self.id } .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end end
private private
......
...@@ -2,6 +2,9 @@ module ProtectedBranchAccess ...@@ -2,6 +2,9 @@ module ProtectedBranchAccess
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
belongs_to :protected_branch
delegate :project, to: :protected_branch
scope :master, -> { where(access_level: Gitlab::Access::MASTER) } scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) } scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
end end
...@@ -9,4 +12,10 @@ module ProtectedBranchAccess ...@@ -9,4 +12,10 @@ module ProtectedBranchAccess
def humanize def humanize
self.class.human_access_levels[self.access_level] self.class.human_access_levels[self.access_level]
end end
def check_access(user)
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level
end
end end
class CycleAnalytics class CycleAnalytics
STAGES = %i[issue plan code test review staging production].freeze STAGES = %i[issue plan code test review staging production].freeze
def initialize(project, from:) def initialize(project, current_user, from:)
@project = project @project = project
@current_user = current_user
@from = from @from = from
@fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: from, branch: nil) @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: from, branch: nil)
end end
def summary def summary
@summary ||= Summary.new(@project, from: @from) @summary ||= Summary.new(@project, @current_user, from: @from)
end end
def permissions(user:) def permissions(user:)
......
class CycleAnalytics class CycleAnalytics
class Summary class Summary
def initialize(project, from:) def initialize(project, current_user, from:)
@project = project @project = project
@current_user = current_user
@from = from @from = from
end end
def new_issues def new_issues
@project.issues.created_after(@from).count IssuesFinder.new(@current_user, project_id: @project.id).execute.created_after(@from).count
end end
def commits def commits
......
...@@ -25,7 +25,12 @@ class Discussion ...@@ -25,7 +25,12 @@ class Discussion
to: :last_resolved_note, to: :last_resolved_note,
allow_nil: true allow_nil: true
delegate :blob, :highlighted_diff_lines, to: :diff_file, allow_nil: true delegate :blob,
:highlighted_diff_lines,
:diff_lines,
to: :diff_file,
allow_nil: true
def self.for_notes(notes) def self.for_notes(notes)
notes.group_by(&:discussion_id).values.map { |notes| new(notes) } notes.group_by(&:discussion_id).values.map { |notes| new(notes) }
...@@ -159,10 +164,11 @@ class Discussion ...@@ -159,10 +164,11 @@ class Discussion
end end
# Returns an array of at most 16 highlighted lines above a diff note # Returns an array of at most 16 highlighted lines above a diff note
def truncated_diff_lines def truncated_diff_lines(highlight: true)
lines = highlight ? highlighted_diff_lines : diff_lines
prev_lines = [] prev_lines = []
highlighted_diff_lines.each do |line| lines.each do |line|
if line.meta? if line.meta?
prev_lines.clear prev_lines.clear
else else
......
...@@ -43,12 +43,6 @@ class Event < ActiveRecord::Base ...@@ -43,12 +43,6 @@ class Event < ActiveRecord::Base
scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) } scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
class << self class << self
def reset_event_cache_for(target)
Event.where(target_id: target.id, target_type: target.class.to_s).
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end
# Update Gitlab::ContributionsCalendar#activity_dates if this changes # Update Gitlab::ContributionsCalendar#activity_dates if this changes
def contributions def contributions
where("action = ? OR (target_type in (?) AND action in (?))", where("action = ? OR (target_type in (?) AND action in (?))",
...@@ -353,6 +347,10 @@ class Event < ActiveRecord::Base ...@@ -353,6 +347,10 @@ class Event < ActiveRecord::Base
update_all(last_activity_at: created_at) update_all(last_activity_at: created_at)
end end
def authored_by?(user)
user ? author_id == user.id : false
end
private private
def recent_update? def recent_update?
......
...@@ -182,18 +182,6 @@ class Issue < ActiveRecord::Base ...@@ -182,18 +182,6 @@ class Issue < ActiveRecord::Base
branches_with_iid - branches_with_merge_request branches_with_iid - branches_with_merge_request
end end
# Reset issue events cache
#
# Since we do cache @event we need to reset cache in special cases:
# * when an issue is updated
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
# when the event is updated because the key changes.
def reset_events_cache
Event.reset_event_cache_for(self)
end
# To allow polymorphism with MergeRequest. # To allow polymorphism with MergeRequest.
def source_project def source_project
project project
......
...@@ -605,18 +605,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -605,18 +605,6 @@ class MergeRequest < ActiveRecord::Base
self.target_project.repository.branch_names.include?(self.target_branch) self.target_project.repository.branch_names.include?(self.target_branch)
end end
# Reset merge request events cache
#
# Since we do cache @event we need to reset cache in special cases:
# * when a merge request is updated
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
# when the event is updated because the key changes.
def reset_events_cache
Event.reset_event_cache_for(self)
end
def merge_commit_message def merge_commit_message
message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n" message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n"
message << "#{title}\n\n" message << "#{title}\n\n"
...@@ -690,7 +678,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -690,7 +678,7 @@ class MergeRequest < ActiveRecord::Base
def mergeable_ci_state? def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds? return true unless project.only_allow_merge_if_build_succeeds?
!pipeline || pipeline.success? || pipeline.skipped? !head_pipeline || head_pipeline.success? || head_pipeline.skipped?
end end
def environments def environments
...@@ -786,14 +774,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -786,14 +774,14 @@ class MergeRequest < ActiveRecord::Base
commits.map(&:sha) commits.map(&:sha)
end end
def pipeline def head_pipeline
return unless diff_head_sha && source_project return unless diff_head_sha && source_project
@pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha) @head_pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
end end
def all_pipelines def all_pipelines
return unless source_project return Ci::Pipeline.none unless source_project
@all_pipelines ||= source_project.pipelines @all_pipelines ||= source_project.pipelines
.where(sha: all_commits_sha, ref: source_branch) .where(sha: all_commits_sha, ref: source_branch)
......
...@@ -201,19 +201,6 @@ class Note < ActiveRecord::Base ...@@ -201,19 +201,6 @@ class Note < ActiveRecord::Base
super(noteable_type.to_s.classify.constantize.base_class.to_s) super(noteable_type.to_s.classify.constantize.base_class.to_s)
end end
# Reset notes events cache
#
# Since we do cache @event we need to reset cache in special cases:
# * when a note is updated
# * when a note is removed
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
# when the event is updated because the key changes.
def reset_events_cache
Event.reset_event_cache_for(self)
end
def editable? def editable?
!system? !system?
end end
......
...@@ -687,9 +687,9 @@ class Project < ActiveRecord::Base ...@@ -687,9 +687,9 @@ class Project < ActiveRecord::Base
self.id self.id
end end
def get_issue(issue_id) def get_issue(issue_id, current_user)
if default_issues_tracker? if default_issues_tracker?
issues.find_by(iid: issue_id) IssuesFinder.new(current_user, project_id: id).find_by(iid: issue_id)
else else
ExternalIssue.new(issue_id, self) ExternalIssue.new(issue_id, self)
end end
...@@ -976,7 +976,6 @@ class Project < ActiveRecord::Base ...@@ -976,7 +976,6 @@ class Project < ActiveRecord::Base
begin begin
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace) send_move_instructions(old_path_with_namespace)
reset_events_cache
@old_path_with_namespace = old_path_with_namespace @old_path_with_namespace = old_path_with_namespace
...@@ -1043,22 +1042,6 @@ class Project < ActiveRecord::Base ...@@ -1043,22 +1042,6 @@ class Project < ActiveRecord::Base
attrs attrs
end end
# Reset events cache related to this project
#
# Since we do cache @event we need to reset cache in special cases:
# * when project was moved
# * when project was renamed
# * when the project avatar changes
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
# when the event is updated because the key changes.
def reset_events_cache
Event.where(project_id: self.id).
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end
def project_member(user) def project_member(user)
project_members.find_by(user_id: user) project_members.find_by(user_id: user)
end end
......
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess include ProtectedBranchAccess
belongs_to :protected_branch
delegate :project, to: :protected_branch
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER, validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER] } Gitlab::Access::DEVELOPER] }
...@@ -13,10 +10,4 @@ class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base ...@@ -13,10 +10,4 @@ class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
Gitlab::Access::DEVELOPER => "Developers + Masters" Gitlab::Access::DEVELOPER => "Developers + Masters"
}.with_indifferent_access }.with_indifferent_access
end end
def check_access(user)
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level
end
end end
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess include ProtectedBranchAccess
belongs_to :protected_branch
delegate :project, to: :protected_branch
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER, validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER, Gitlab::Access::DEVELOPER,
Gitlab::Access::NO_ACCESS] } Gitlab::Access::NO_ACCESS] }
...@@ -18,8 +15,7 @@ class ProtectedBranch::PushAccessLevel < ActiveRecord::Base ...@@ -18,8 +15,7 @@ class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
def check_access(user) def check_access(user)
return false if access_level == Gitlab::Access::NO_ACCESS return false if access_level == Gitlab::Access::NO_ACCESS
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level super
end end
end end
...@@ -196,18 +196,12 @@ class Repository ...@@ -196,18 +196,12 @@ class Repository
options = { message: message, tagger: user_to_committer(user) } if message options = { message: message, tagger: user_to_committer(user) } if message
rugged.tags.create(tag_name, target, options) GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do |service|
tag = find_tag(tag_name) raw_tag = rugged.tags.create(tag_name, target, options)
service.newrev = raw_tag.target_id
GitHooksService.new.execute(user, path_to_repo, oldrev, tag.target, ref) do
# we already created a tag, because we need tag SHA to pass correct
# values to hooks
end end
tag find_tag(tag_name)
rescue GitHooksService::PreReceiveError
rugged.tags.delete(tag_name)
raise
end end
def rm_branch(user, branch_name) def rm_branch(user, branch_name)
......
...@@ -445,13 +445,12 @@ class User < ActiveRecord::Base ...@@ -445,13 +445,12 @@ class User < ActiveRecord::Base
end end
def refresh_authorized_projects def refresh_authorized_projects
loop do transaction do
begin
Gitlab::Database.serialized_transaction do
project_authorizations.delete_all project_authorizations.delete_all
# project_authorizations_union can return multiple records for the same project/user with # project_authorizations_union can return multiple records for the same
# different access_level so we take row with the maximum access_level # project/user with different access_level so we take row with the maximum
# access_level
project_authorizations.connection.execute <<-SQL project_authorizations.connection.execute <<-SQL
INSERT INTO project_authorizations (user_id, project_id, access_level) INSERT INTO project_authorizations (user_id, project_id, access_level)
SELECT user_id, project_id, MAX(access_level) AS access_level SELECT user_id, project_id, MAX(access_level) AS access_level
...@@ -459,13 +458,8 @@ class User < ActiveRecord::Base ...@@ -459,13 +458,8 @@ class User < ActiveRecord::Base
GROUP BY user_id, project_id GROUP BY user_id, project_id
SQL SQL
update_column(:authorized_projects_populated, true) unless authorized_projects_populated unless authorized_projects_populated
end update_column(:authorized_projects_populated, true)
break
# In the event of a concurrent modification Rails raises StatementInvalid.
# In this case we want to keep retrying until the transaction succeeds
rescue ActiveRecord::StatementInvalid
end end
end end
end end
...@@ -708,20 +702,6 @@ class User < ActiveRecord::Base ...@@ -708,20 +702,6 @@ class User < ActiveRecord::Base
project.project_member(self) project.project_member(self)
end end
# Reset project events cache related to this user
#
# Since we do cache @event we need to reset cache in special cases:
# * when the user changes their avatar
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
# when the event is updated because the key changes.
def reset_events_cache
Event.where(author_id: id).
order('id DESC').limit(1000).
update_all(updated_at: Time.now)
end
def full_website_url def full_website_url
return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\// return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\//
......
...@@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity ...@@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity
end end
expose :duration, as: :total_time do |build| expose :duration, as: :total_time do |build|
distance_of_time_as_hash(build.duration.to_f) build.duration ? distance_of_time_as_hash(build.duration.to_f) : {}
end end
expose :branch do expose :branch do
......
...@@ -16,6 +16,9 @@ class BuildEntity < Grape::Entity ...@@ -16,6 +16,9 @@ class BuildEntity < Grape::Entity
path_to(:play_namespace_project_build, build) path_to(:play_namespace_project_build, build)
end end
expose :created_at
expose :updated_at
private private
def path_to(route, build) def path_to(route, build)
......
...@@ -2,6 +2,8 @@ module EntityDateHelper ...@@ -2,6 +2,8 @@ module EntityDateHelper
include ActionView::Helpers::DateHelper include ActionView::Helpers::DateHelper
def interval_in_words(diff) def interval_in_words(diff)
return 'Not started' unless diff
"#{distance_of_time_in_words(Time.now, diff)} ago" "#{distance_of_time_in_words(Time.now, diff)} ago"
end end
......
...@@ -45,9 +45,15 @@ module Ci ...@@ -45,9 +45,15 @@ module Ci
return error('No builds for this pipeline.') return error('No builds for this pipeline.')
end end
Ci::Pipeline.transaction do
pipeline.save pipeline.save
pipeline.process!
pipeline Ci::CreatePipelineBuildsService
.new(project, current_user)
.execute(pipeline)
end
pipeline.tap(&:process!)
end end
private private
......
...@@ -5,10 +5,7 @@ module Ci ...@@ -5,10 +5,7 @@ module Ci
def execute(pipeline) def execute(pipeline)
@pipeline = pipeline @pipeline = pipeline
# This method will ensure that our pipeline does have all builds for all stages created ensure_created_builds! # TODO, remove me in 9.0
if created_builds.empty?
create_builds!
end
new_builds = new_builds =
stage_indexes_of_created_builds.map do |index| stage_indexes_of_created_builds.map do |index|
...@@ -22,10 +19,6 @@ module Ci ...@@ -22,10 +19,6 @@ module Ci
private private
def create_builds!
Ci::CreatePipelineBuildsService.new(project, current_user).execute(pipeline)
end
def process_stage(index) def process_stage(index)
current_status = status_for_prior_stages(index) current_status = status_for_prior_stages(index)
...@@ -76,5 +69,18 @@ module Ci ...@@ -76,5 +69,18 @@ module Ci
def created_builds def created_builds
pipeline.builds.created pipeline.builds.created
end end
# This method is DEPRECATED and should be removed in 9.0.
#
# We need it to maintain backwards compatibility with previous versions
# when builds were not created within one transaction with the pipeline.
#
def ensure_created_builds!
return if created_builds.any?
Ci::CreatePipelineBuildsService
.new(project, current_user)
.execute(pipeline)
end
end end
end end
class GitHooksService class GitHooksService
PreReceiveError = Class.new(StandardError) PreReceiveError = Class.new(StandardError)
attr_accessor :oldrev, :newrev, :ref
def execute(user, repo_path, oldrev, newrev, ref) def execute(user, repo_path, oldrev, newrev, ref)
@repo_path = repo_path @repo_path = repo_path
@user = Gitlab::GlId.gl_id(user) @user = Gitlab::GlId.gl_id(user)
...@@ -16,7 +18,7 @@ class GitHooksService ...@@ -16,7 +18,7 @@ class GitHooksService
end end
end end
yield yield self
run_hook('post-receive') run_hook('post-receive')
end end
...@@ -25,6 +27,6 @@ class GitHooksService ...@@ -25,6 +27,6 @@ class GitHooksService
def run_hook(name) def run_hook(name)
hook = Gitlab::Git::Hook.new(name, @repo_path) hook = Gitlab::Git::Hook.new(name, @repo_path)
hook.trigger(@user, @oldrev, @newrev, @ref) hook.trigger(@user, oldrev, newrev, ref)
end end
end end
...@@ -85,14 +85,15 @@ class IssuableBaseService < BaseService ...@@ -85,14 +85,15 @@ class IssuableBaseService < BaseService
def find_or_create_label_ids def find_or_create_label_ids
labels = params.delete(:labels) labels = params.delete(:labels)
return unless labels return unless labels
params[:label_ids] = labels.split(',').map do |label_name| params[:label_ids] = labels.split(",").map do |label_name|
service = Labels::FindOrCreateService.new(current_user, project, title: label_name.strip) service = Labels::FindOrCreateService.new(current_user, project, title: label_name.strip)
label = service.execute label = service.execute
label.id label.try(:id)
end end.compact
end end
def process_label_ids(attributes, existing_label_ids: nil) def process_label_ids(attributes, existing_label_ids: nil)
...@@ -140,6 +141,7 @@ class IssuableBaseService < BaseService ...@@ -140,6 +141,7 @@ class IssuableBaseService < BaseService
params.delete(:state_event) params.delete(:state_event)
params[:author] ||= current_user params[:author] ||= current_user
label_ids = process_label_ids(params) label_ids = process_label_ids(params)
issuable.assign_attributes(params) issuable.assign_attributes(params)
...@@ -184,8 +186,6 @@ class IssuableBaseService < BaseService ...@@ -184,8 +186,6 @@ class IssuableBaseService < BaseService
params[:label_ids] = process_label_ids(params, existing_label_ids: issuable.label_ids) params[:label_ids] = process_label_ids(params, existing_label_ids: issuable.label_ids)
if params.present? && update_issuable(issuable, params) if params.present? && update_issuable(issuable, params)
issuable.reset_events_cache
# We do not touch as it will affect a update on updated_at field # We do not touch as it will affect a update on updated_at field
ActiveRecord::Base.no_touching do ActiveRecord::Base.no_touching do
handle_common_system_notes(issuable, old_labels: old_labels) handle_common_system_notes(issuable, old_labels: old_labels)
......
...@@ -22,9 +22,14 @@ module Labels ...@@ -22,9 +22,14 @@ module Labels
).execute(skip_authorization: skip_authorization) ).execute(skip_authorization: skip_authorization)
end end
# Only creates the label if current_user can do so, if the label does not exist
# and the user can not create the label, nil is returned
def find_or_create_label def find_or_create_label
new_label = available_labels.find_by(title: title) new_label = available_labels.find_by(title: title)
new_label ||= project.labels.create(params)
if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, project))
new_label = project.labels.create(params)
end
new_label new_label
end end
......
...@@ -55,7 +55,7 @@ module MergeRequests ...@@ -55,7 +55,7 @@ module MergeRequests
def pipeline_merge_requests(pipeline) def pipeline_merge_requests(pipeline)
merge_requests_for(pipeline.ref).each do |merge_request| merge_requests_for(pipeline.ref).each do |merge_request|
next unless pipeline == merge_request.pipeline next unless pipeline == merge_request.head_pipeline
yield merge_request yield merge_request
end end
...@@ -63,7 +63,7 @@ module MergeRequests ...@@ -63,7 +63,7 @@ module MergeRequests
def commit_status_merge_requests(commit_status) def commit_status_merge_requests(commit_status)
merge_requests_for(commit_status.ref).each do |merge_request| merge_requests_for(commit_status.ref).each do |merge_request|
pipeline = merge_request.pipeline pipeline = merge_request.head_pipeline
next unless pipeline next unless pipeline
next unless pipeline.sha == commit_status.sha next unless pipeline.sha == commit_status.sha
......
...@@ -81,7 +81,7 @@ module MergeRequests ...@@ -81,7 +81,7 @@ module MergeRequests
commit = commits.first commit = commits.first
merge_request.title = commit.title merge_request.title = commit.title
merge_request.description ||= commit.description.try(:strip) merge_request.description ||= commit.description.try(:strip)
elsif iid && (issue = merge_request.target_project.get_issue(iid)) && !issue.try(:confidential?) elsif iid && issue = merge_request.target_project.get_issue(iid, current_user)
case issue case issue
when Issue when Issue
merge_request.title = "Resolve \"#{issue.title}\"" merge_request.title = "Resolve \"#{issue.title}\""
......
...@@ -2,7 +2,6 @@ module Notes ...@@ -2,7 +2,6 @@ module Notes
class DeleteService < BaseService class DeleteService < BaseService
def execute(note) def execute(note)
note.destroy note.destroy
note.reset_events_cache
end end
end end
end end
...@@ -5,7 +5,6 @@ module Notes ...@@ -5,7 +5,6 @@ module Notes
note.update_attributes(params.merge(updated_by: current_user)) note.update_attributes(params.merge(updated_by: current_user))
note.create_new_cross_references!(current_user) note.create_new_cross_references!(current_user)
note.reset_events_cache
if note.previous_changes.include?('note') if note.previous_changes.include?('note')
TodoService.new.update_note(note, current_user) TodoService.new.update_note(note, current_user)
......
...@@ -61,9 +61,6 @@ module Projects ...@@ -61,9 +61,6 @@ module Projects
# Move missing group labels to project # Move missing group labels to project
Labels::TransferService.new(current_user, old_group, project).execute Labels::TransferService.new(current_user, old_group, project).execute
# clear project cached events
project.reset_events_cache
# Move uploads # Move uploads
Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
......
...@@ -3,16 +3,10 @@ class AvatarUploader < CarrierWave::Uploader::Base ...@@ -3,16 +3,10 @@ class AvatarUploader < CarrierWave::Uploader::Base
storage :file storage :file
after :store, :reset_events_cache
def store_dir def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end end
def reset_events_cache(file)
model.reset_events_cache if model.is_a?(User)
end
def exists? def exists?
model.avatar.file && model.avatar.file.exists? model.avatar.file && model.avatar.file.exists?
end end
......
...@@ -443,7 +443,16 @@ ...@@ -443,7 +443,16 @@
Some email servers do not support overriding the email sender name. Some email servers do not support overriding the email sender name.
Enable this option to include the name of the author of the issue, Enable this option to include the name of the author of the issue,
merge request or comment in the email body instead. merge request or comment in the email body instead.
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :html_emails_enabled do
= f.check_box :html_emails_enabled
Enable HTML emails
.help-block
By default GitLab sends emails in HTML and plain text formats so mail
clients can choose what format to use. Disable this option if you only
want to send emails in plain text format.
%fieldset %fieldset
%legend Automatic Git repository housekeeping %legend Automatic Git repository housekeeping
.form-group .form-group
......
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
%ul.nav-links %ul.nav-links
= nav_link(page: [dashboard_projects_path, root_path]) do = nav_link(page: [dashboard_projects_path, root_path]) do
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do = link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
Your Projects Your projects
= nav_link(page: starred_dashboard_projects_path) do = nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do = link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
Starred Projects Starred projects
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do = nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do = link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore Projects Explore projects
.nav-controls .nav-controls
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
......
...@@ -4,6 +4,18 @@ ...@@ -4,6 +4,18 @@
Welcome to GitLab Welcome to GitLab
%p.blank-state-text %p.blank-state-text
Code, test, and deploy together Code, test, and deploy together
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("group", size: 50)
%h3.blank-state-title
You can create a group for several dependent projects.
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "btn btn-new" do
New group
.blank-state .blank-state
.blank-state-icon .blank-state-icon
= custom_icon("project", size: 50) = custom_icon("project", size: 50)
...@@ -21,17 +33,6 @@ ...@@ -21,17 +33,6 @@
= link_to new_project_path, class: "btn btn-new" do = link_to new_project_path, class: "btn btn-new" do
New project New project
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("group", size: 50)
%h3.blank-state-title
You can create a group for several dependent projects.
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "btn btn-new" do
New group
-if publicish_project_count > 0 -if publicish_project_count > 0
.blank-state .blank-state
.blank-state-icon .blank-state-icon
......
...@@ -50,13 +50,13 @@ ...@@ -50,13 +50,13 @@
data: { data: todo_actions_options }}) data: { data: todo_actions_options }})
.pull-right .pull-right
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light %span.light
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
- else - else
= sort_title_recently_created = sort_title_recently_created
= icon('caret-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
%li %li
= link_to todos_filter_path(sort: sort_value_priority) do = link_to todos_filter_path(sort: sort_value_priority) do
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
.event-item-timestamp .event-item-timestamp
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
= cache [event, current_application_settings, "v2.2"] do
= author_avatar(event, size: 40) = author_avatar(event, size: 40)
- if event.created_project? - if event.created_project?
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
- few_commits.each do |commit| - few_commits.each do |commit|
= render "events/commit", commit: commit, project: project, event: event = render "events/commit", commit: commit, project: project, event: event
- create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) && event.authored_by?(current_user)
- if event.commits_count > 1 - if event.commits_count > 1
%li.commits-stat %li.commits-stat
- if event.commits_count > 2 - if event.commits_count > 2
...@@ -35,12 +35,12 @@ ...@@ -35,12 +35,12 @@
Compare #{from_label}...#{truncate_sha(event.commit_to)} Compare #{from_label}...#{truncate_sha(event.commit_to)}
- if create_mr - if create_mr
%span{"data-user-is" => event.author_id, "data-display" => "inline"} %span
or or
= link_to create_mr_path(project.default_branch, event.ref_name, project) do = link_to create_mr_path(project.default_branch, event.ref_name, project) do
create a merge request create a merge request
- elsif create_mr - elsif create_mr
%li.commits-stat{"data-user-is" => event.author_id} %li.commits-stat
= link_to create_mr_path(project.default_branch, event.ref_name, project) do = link_to create_mr_path(project.default_branch, event.ref_name, project) do
Create Merge Request Create Merge Request
- elsif event.rm_ref? - elsif event.rm_ref?
......
...@@ -17,13 +17,13 @@ ...@@ -17,13 +17,13 @@
.pull-right .pull-right
.dropdown.inline .dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light %span.light
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
- else - else
= sort_title_recently_created = sort_title_recently_created
= icon('caret-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right
%li %li
= link_to explore_groups_path(sort: sort_value_recently_created) do = link_to explore_groups_path(sort: sort_value_recently_created) do
......
- if current_user - if current_user
.dropdown .dropdown
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('globe') = icon('globe')
%span.light Visibility: %span.light Visibility:
- if params[:visibility_level].present? - if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i) = visibility_level_label(params[:visibility_level].to_i)
- else - else
Any Any
= icon('caret-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right
%li %li
= link_to filter_projects_path(visibility_level: nil) do = link_to filter_projects_path(visibility_level: nil) do
...@@ -20,14 +20,14 @@ ...@@ -20,14 +20,14 @@
- if @tags.present? - if @tags.present?
.dropdown .dropdown
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('tags') = icon('tags')
%span.light Tags: %span.light Tags:
- if params[:tag].present? - if params[:tag].present?
= params[:tag] = params[:tag]
- else - else
Any Any
= icon('caret-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right
%li %li
= link_to filter_projects_path(tag: nil) do = link_to filter_projects_path(tag: nil) do
......
...@@ -20,11 +20,18 @@ ...@@ -20,11 +20,18 @@
= link_to group.name, group_url(group) = link_to group.name, group_url(group)
as #{@member.human_access}. as #{@member.human_access}.
- if @member.source.users.include?(current_user) - is_member = @member.source.users.include?(current_user)
- if is_member
%p %p
However, you are already a member of this #{@member.source.is_a?(Group) ? "group" : "project"}. However, you are already a member of this #{@member.source.is_a?(Group) ? "group" : "project"}.
Sign in using a different account to accept the invitation. Sign in using a different account to accept the invitation.
- else
- if @member.invite_email != current_user.email
%p
Note that this invitation was sent to #{mail_to @member.invite_email}, but you are signed in as #{link_to current_user.to_reference, user_url(current_user)} with email #{mail_to current_user.email}.
- unless is_member
.actions .actions
= link_to "Accept invitation", accept_invite_url(@token), method: :post, class: "btn btn-success" = link_to "Accept invitation", accept_invite_url(@token), method: :post, class: "btn btn-success"
= link_to "Decline", decline_invite_url(@token), method: :post, class: "btn btn-danger prepend-left-10" = link_to "Decline", decline_invite_url(@token), method: :post, class: "btn btn-danger prepend-left-10"
...@@ -56,5 +56,3 @@ ...@@ -56,5 +56,3 @@
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id') = render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id') = render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
= render 'layouts/bootlint' if Rails.env.development? = render 'layouts/bootlint' if Rails.env.development?
= render 'layouts/user_styles'
:css
[data-user-is] {
display: none !important;
}
[data-user-is="#{current_user.try(:id)}"] {
display: block !important;
}
[data-user-is="#{current_user.try(:id)}"][data-display="inline"] {
display: inline !important;
}
[data-user-is-not] {
display: block !important;
}
[data-user-is-not][data-display="inline"] {
display: inline !important;
}
[data-user-is-not="#{current_user.try(:id)}"] {
display: none !important;
}
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
%span %span
Issues Issues
- if @project.default_issues_tracker? - if @project.default_issues_tracker?
%span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count) %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do = nav_link(controller: :merge_requests) do
......
<% if current_application_settings.email_author_in_body %>
<%= @note.author_name %> wrote:
<% end -%>
<%= @note.note %>
= content_for :head do
= stylesheet_link_tag 'mailers/highlighted_diff_email'
New comment
- if @discussion && @discussion.diff_file
on
= link_to @note.diff_file.file_path, @target_url, class: 'details'
\:
%table
= render partial: "projects/diffs/line",
collection: @discussion.truncated_diff_lines,
as: :line,
locals: { diff_file: @note.diff_file,
plain: true,
email: true }
= render 'note_message'
<% if @discussion && @discussion.diff_file -%>
on <%= @note.diff_file.file_path -%>
<% end -%>:
<%= url %>
<%= render 'simple_diff' if @discussion -%>
<%= render 'note_message' %>
<% @discussion.truncated_diff_lines(highlight: false).each do |line| %>
> <%= line.text %>
<% end %>
= render 'note_message' %p.details
= render 'note_mr_or_commit_email'
New comment for Commit <%= @commit.short_id %> New comment for Commit <%= @commit.short_id -%>
<%= render partial: 'note_mr_or_commit_email', locals: { url: @target_url } %>
<%= url_for(namespace_project_commit_url(@note.project.namespace, @note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
Author: <%= @note.author_name %>
<%= @note.note %>
- if @note.diff_note? && @note.diff_file %p.details
%p.details = render 'note_mr_or_commit_email'
New comment on diff for
= link_to @note.diff_file.file_path, @target_url
\:
= render 'note_message'
New comment for Merge Request <%= @merge_request.to_reference %> New comment for Merge Request <%= @merge_request.to_reference -%>
<%= render partial: 'note_mr_or_commit_email', locals: { url: @target_url }%>
<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %>
<%= @note.author_name %>
<%= @note.note %>
= content_for :head do = content_for :head do
= stylesheet_link_tag 'mailers/repository_push_email' = stylesheet_link_tag 'mailers/highlighted_diff_email'
%h3 %h3
#{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name} #{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name}
......
...@@ -27,5 +27,5 @@ ...@@ -27,5 +27,5 @@
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}" = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
= hidden_field_tag 'last_commit_sha', @last_commit_sha = hidden_field_tag 'last_commit_sha', @last_commit_sha
= hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] = hidden_field_tag 'from_merge_request_iid', params[:from_merge_request_iid]
= render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id) = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
...@@ -12,10 +12,10 @@ ...@@ -12,10 +12,10 @@
= search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false } = search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline .dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light %span.light
= projects_sort_options_hash[@sort] = projects_sort_options_hash[@sort]
= icon('caret-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right
%li %li
= link_to filter_branches_path(sort: sort_value_name) do = link_to filter_branches_path(sort: sort_value_name) do
......
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
.title Stage .title Stage
%button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.stage-selection More %span.stage-selection More
= icon('caret-down') = icon('chevron-down')
%ul.dropdown-menu %ul.dropdown-menu
- @build.pipeline.stages.each do |stage| - @build.pipeline.stages.each do |stage|
%li %li
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= icon('comment') = icon('comment')
\ \
- if editable_diff?(diff_file) - if editable_diff?(diff_file)
- link_opts = @merge_request.id ? { from_merge_request_id: @merge_request.id } : {} - link_opts = @merge_request.persisted? ? { from_merge_request_iid: @merge_request.iid } : {}
= edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path, = edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path,
blob: blob, link_opts: link_opts) blob: blob, link_opts: link_opts)
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
%a{href: "##{line_code}", data: { linenumber: link_text }} %a{href: "##{line_code}", data: { linenumber: link_text }}
%td.line_content.noteable_line{ class: type, data: (diff_view_line_data(line_code, diff_file.position(line), type) unless plain) }< %td.line_content.noteable_line{ class: type, data: (diff_view_line_data(line_code, diff_file.position(line), type) unless plain) }<
- if email - if email
%pre= diff_line_content(line.text) %pre= line.text
- else - else
= diff_line_content(line.text) = diff_line_content(line.text)
......
...@@ -9,13 +9,13 @@ ...@@ -9,13 +9,13 @@
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' } spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown .dropdown
%button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'} %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort: %span.light sort:
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
- else - else
= sort_title_recently_created = sort_title_recently_created
= icon('caret-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right
%li %li
- excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id] - excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
%h2.merge-requests-title %h2.merge-requests-title
= pluralize(@merge_requests.count, 'Related Merge Request') = pluralize(@merge_requests.count, 'Related Merge Request')
%ul.unstyled-list.related-merge-requests %ul.unstyled-list.related-merge-requests
- has_any_ci = @merge_requests.any?(&:pipeline) - has_any_ci = @merge_requests.any?(&:head_pipeline)
- @merge_requests.each do |merge_request| - @merge_requests.each do |merge_request|
%li %li
%span.merge-request-ci-status %span.merge-request-ci-status
- if merge_request.pipeline - if merge_request.head_pipeline
= render_pipeline_status(merge_request.pipeline) = render_pipeline_status(merge_request.head_pipeline)
- elsif has_any_ci - elsif has_any_ci
= icon('blank fw') = icon('blank fw')
%span.merge-request-id %span.merge-request-id
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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