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
This commit is contained in:
commit
00ca7adca2
|
@ -1,3 +1,4 @@
|
|||
/coverage/
|
||||
/coverage-javascript/
|
||||
/public/
|
||||
/tmp/
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
{
|
||||
"env": {
|
||||
"jquery": true,
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": "airbnb",
|
||||
"extends": "airbnb-base",
|
||||
"globals": {
|
||||
"$": false,
|
||||
"_": false,
|
||||
"gl": false,
|
||||
"gon": false,
|
||||
"jQuery": false
|
||||
"gon": false
|
||||
},
|
||||
"plugins": [
|
||||
"filenames"
|
||||
|
|
29
CHANGELOG.md
29
CHANGELOG.md
|
@ -2,6 +2,26 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
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)
|
||||
|
||||
- Use separate email-token for incoming email and revert back the inactive feature. !5914
|
||||
|
@ -202,6 +222,15 @@ entry.
|
|||
- Fix "Without projects" filter. !6611 (Ben Bodenmiller)
|
||||
- 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)
|
||||
|
||||
- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.0.0
|
||||
1.0.1
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -133,7 +133,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
|
|||
|
||||
# Background jobs
|
||||
gem 'sidekiq', '~> 4.2'
|
||||
gem 'sidekiq-cron', '~> 0.4.0'
|
||||
gem 'sidekiq-cron', '~> 0.4.4'
|
||||
gem 'redis-namespace', '~> 1.5.2'
|
||||
gem 'sidekiq-limit_fetch', '~> 3.4'
|
||||
|
||||
|
@ -309,6 +309,8 @@ group :development, :test do
|
|||
gem 'knapsack', '~> 1.11.0'
|
||||
|
||||
gem 'activerecord_sane_schema_dumper', '0.2'
|
||||
|
||||
gem 'stackprof', '~> 0.2.10'
|
||||
end
|
||||
|
||||
group :test do
|
||||
|
|
11
Gemfile.lock
11
Gemfile.lock
|
@ -614,7 +614,8 @@ GEM
|
|||
rubyntlm (0.5.2)
|
||||
rubypants (0.2.0)
|
||||
rubyzip (1.2.0)
|
||||
rufus-scheduler (3.1.10)
|
||||
rufus-scheduler (3.3.0)
|
||||
tzinfo
|
||||
rugged (0.24.0)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (2.1.0)
|
||||
|
@ -650,10 +651,10 @@ GEM
|
|||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
rack-protection (~> 1.5)
|
||||
redis (~> 3.2, >= 3.2.1)
|
||||
sidekiq-cron (0.4.0)
|
||||
sidekiq-cron (0.4.4)
|
||||
redis-namespace (>= 1.5.2)
|
||||
rufus-scheduler (>= 2.0.24)
|
||||
sidekiq (>= 4.0.0)
|
||||
sidekiq (>= 4.2.1)
|
||||
sidekiq-limit_fetch (3.4.0)
|
||||
sidekiq (>= 4)
|
||||
simplecov (0.12.0)
|
||||
|
@ -691,6 +692,7 @@ GEM
|
|||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
stackprof (0.2.10)
|
||||
state_machines (0.4.0)
|
||||
state_machines-activemodel (0.4.0)
|
||||
activemodel (>= 4.1, < 5.1)
|
||||
|
@ -925,7 +927,7 @@ DEPENDENCIES
|
|||
sham_rack (~> 1.3.6)
|
||||
shoulda-matchers (~> 2.8.0)
|
||||
sidekiq (~> 4.2)
|
||||
sidekiq-cron (~> 0.4.0)
|
||||
sidekiq-cron (~> 0.4.4)
|
||||
sidekiq-limit_fetch (~> 3.4)
|
||||
simplecov (= 0.12.0)
|
||||
slack-notifier (~> 1.2.0)
|
||||
|
@ -937,6 +939,7 @@ DEPENDENCIES
|
|||
spring-commands-teaspoon (~> 0.0.2)
|
||||
sprockets (~> 3.7.0)
|
||||
sprockets-es6 (~> 0.9.2)
|
||||
stackprof (~> 0.2.10)
|
||||
state_machines-activerecord (~> 0.4.0)
|
||||
sys-filesystem (~> 1.1.6)
|
||||
teaspoon (~> 1.1.0)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# GitLab
|
||||
|
||||
[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
|
||||
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](http://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
|
||||
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
|
||||
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
|
||||
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
|
||||
|
||||
|
@ -84,7 +84,7 @@ For more information please see the [architecture documentation](https://docs.gi
|
|||
|
||||
## UX design
|
||||
|
||||
Please adhere to the [UX Guide](doc/development/ux_guide/readme.md) when creating designs and implementing code.
|
||||
Please adhere to the [UX Guide](doc/development/ux_guide/index.md) when creating designs and implementing code.
|
||||
|
||||
## Third-party applications
|
||||
|
||||
|
|
|
@ -10,10 +10,15 @@
|
|||
},
|
||||
template: `
|
||||
<span class="total-time">
|
||||
<template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
|
||||
<template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
|
||||
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
|
||||
<template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
|
||||
<template v-if="Object.keys(time).length">
|
||||
<template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
|
||||
<template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
|
||||
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
|
||||
<template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
|
||||
</template>
|
||||
<template v-else>
|
||||
--
|
||||
</template>
|
||||
</span>
|
||||
`,
|
||||
});
|
||||
|
|
|
@ -208,6 +208,9 @@
|
|||
new gl.ProtectedBranchCreate();
|
||||
new gl.ProtectedBranchEditList();
|
||||
break;
|
||||
case 'projects:variables:index':
|
||||
new gl.ProjectVariables();
|
||||
break;
|
||||
}
|
||||
switch (path.first()) {
|
||||
case 'admin':
|
||||
|
|
|
@ -181,7 +181,7 @@
|
|||
|
||||
<div class="environments-container">
|
||||
<div class="environments-list-loading text-center" v-if="isLoading">
|
||||
<i class="fa fa-spinner spin"></i>
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
|
||||
<div class="blank-state blank-state-no-icon"
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */
|
||||
Array.prototype.first = function() {
|
||||
return this[0];
|
||||
}
|
||||
|
||||
Array.prototype.last = function() {
|
||||
return this[this.length-1];
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */
|
||||
Array.prototype.first = function() {
|
||||
return this[0];
|
||||
}
|
||||
|
||||
Array.prototype.last = function() {
|
||||
return this[this.length-1];
|
||||
}
|
||||
|
||||
Array.prototype.find = Array.prototype.find || function(predicate, ...args) {
|
||||
if (!this) throw new TypeError('Array.prototype.find called on null or undefined');
|
||||
if (typeof predicate !== 'function') throw new TypeError('predicate must be a function');
|
||||
|
||||
const list = Object(this);
|
||||
const thisArg = args[1];
|
||||
let value = {};
|
||||
|
||||
for (let i = 0; i < list.length; i += 1) {
|
||||
value = list[i];
|
||||
if (predicate.call(thisArg, value, i, list)) return value;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
|
||||
/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
|
||||
(function() {
|
||||
this.LabelsSelect = (function() {
|
||||
function LabelsSelect() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
|
||||
/* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
|
||||
|
||||
/*= require autosave */
|
||||
/*= require autosize */
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
(() => {
|
||||
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 @@
|
|||
border-radius: 0;
|
||||
border: none;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
align-self: center;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
@include btn-default;
|
||||
}
|
||||
|
||||
@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border) {
|
||||
@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border, $active-background, $active-border) {
|
||||
background-color: $background;
|
||||
color: $text;
|
||||
border-color: $border;
|
||||
|
@ -23,8 +23,14 @@
|
|||
&:hover,
|
||||
&:focus {
|
||||
background-color: $hover-background;
|
||||
color: $hover-text;
|
||||
border-color: $hover-border;
|
||||
color: $hover-text;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $active-background;
|
||||
border-color: $active-border;
|
||||
color: $hover-text;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,11 +88,11 @@
|
|||
}
|
||||
|
||||
@mixin btn-gray {
|
||||
@include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, $gl-gray-dark);
|
||||
@include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, $gl-gray-dark);
|
||||
}
|
||||
|
||||
@mixin btn-white {
|
||||
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
|
||||
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $gl-text-color);
|
||||
}
|
||||
|
||||
@mixin btn-with-margin {
|
||||
|
@ -139,11 +145,11 @@
|
|||
&.btn-new,
|
||||
&.btn-create,
|
||||
&.btn-save {
|
||||
@include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light);
|
||||
@include btn-outline($white-light, $border-green-light, $border-green-light, $green-light, $white-light, $border-green-light, $green-normal, $border-green-normal);
|
||||
}
|
||||
|
||||
&.btn-remove {
|
||||
@include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
|
||||
@include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,11 +171,11 @@
|
|||
}
|
||||
|
||||
&.btn-close {
|
||||
@include btn-outline($white-light, $orange-normal, $orange-normal, $orange-light, $white-light, $orange-light);
|
||||
@include btn-outline($white-light, $border-orange-light, $border-orange-light, $orange-light, $white-light, $border-orange-light, $orange-normal, $border-orange-normal);
|
||||
}
|
||||
|
||||
&.btn-spam {
|
||||
@include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
|
||||
@include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
|
||||
}
|
||||
|
||||
&.btn-danger,
|
||||
|
@ -199,7 +205,7 @@
|
|||
}
|
||||
|
||||
.fa-caret-down,
|
||||
.fa-caret-up {
|
||||
.fa-chevron-down {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
|
@ -351,7 +357,7 @@
|
|||
|
||||
.btn-inverted {
|
||||
&-secondary {
|
||||
@include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light);
|
||||
@include btn-outline($white-light, $border-blue-light, $border-blue-light, $blue-light, $white-light, $border-blue-light, $blue-normal, $border-blue-normal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
@mixin chevron-active {
|
||||
.fa-chevron-down {
|
||||
color: $dropdown-toggle-hover-icon-color;
|
||||
}
|
||||
}
|
||||
|
||||
.open {
|
||||
.dropdown-menu,
|
||||
.dropdown-menu-nav {
|
||||
|
@ -19,53 +25,27 @@
|
|||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle,
|
||||
.dropdown-menu-toggle {
|
||||
@include chevron-active;
|
||||
border-color: $dropdown-toggle-hover-border-color;
|
||||
|
||||
.fa {
|
||||
color: $dropdown-toggle-hover-icon-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-toggle {
|
||||
position: relative;
|
||||
width: 160px;
|
||||
padding: 6px 20px 6px 10px;
|
||||
.dropdown-toggle {
|
||||
padding: 6px 8px 6px 10px;
|
||||
background-color: $dropdown-toggle-bg;
|
||||
color: $dropdown-toggle-color;
|
||||
font-size: 15px;
|
||||
text-align: left;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: $border-radius-base;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
.fa {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 8px;
|
||||
color: $dropdown-toggle-icon-color;
|
||||
|
||||
&.fa-spinner {
|
||||
font-size: 16px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-outline {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&:hover, {
|
||||
border-color: $dropdown-toggle-hover-border-color;
|
||||
|
||||
.fa {
|
||||
color: $dropdown-toggle-hover-icon-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
width: 200px;
|
||||
}
|
||||
|
@ -86,6 +66,51 @@
|
|||
max-width: 100%;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.fa {
|
||||
color: $dropdown-toggle-icon-color;
|
||||
}
|
||||
|
||||
.fa-chevron-down {
|
||||
font-size: $dropdown-chevron-size;
|
||||
position: relative;
|
||||
top: -3px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include chevron-active;
|
||||
border-color: $dropdown-toggle-hover-border-color;
|
||||
}
|
||||
|
||||
&:focus:active {
|
||||
@include chevron-active;
|
||||
border-color: $dropdown-toggle-active-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-toggle {
|
||||
@extend .dropdown-toggle;
|
||||
padding-right: 20px;
|
||||
position: relative;
|
||||
width: 160px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
.fa {
|
||||
position: absolute;
|
||||
|
||||
&.fa-spinner {
|
||||
font-size: 16px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-chevron-down {
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu,
|
||||
|
|
|
@ -51,19 +51,26 @@
|
|||
margin-bottom: -1px;
|
||||
font-size: 15px;
|
||||
line-height: 28px;
|
||||
color: #959494;
|
||||
color: $note-toolbar-color;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
border-bottom: 2px solid $gray-darkest;
|
||||
color: $black;
|
||||
|
||||
.badge {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active a {
|
||||
border-bottom: 2px solid $link-underline-blue;
|
||||
color: $black;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge {
|
||||
|
@ -85,15 +92,21 @@
|
|||
|
||||
li {
|
||||
|
||||
a {
|
||||
margin: 0;
|
||||
padding: 11px 10px 9px;
|
||||
}
|
||||
|
||||
&.active a {
|
||||
border-bottom: none;
|
||||
color: $link-underline-blue;
|
||||
}
|
||||
|
||||
a {
|
||||
margin: 0;
|
||||
padding: 11px 10px 9px;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,37 +323,9 @@
|
|||
height: 51px;
|
||||
|
||||
li {
|
||||
|
||||
a {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
a,
|
||||
i {
|
||||
color: $layout-link-gray;
|
||||
}
|
||||
|
||||
&.active {
|
||||
|
||||
a,
|
||||
i {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
svg {
|
||||
path,
|
||||
polygon {
|
||||
fill: $black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
a,
|
||||
i {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,67 +12,71 @@ $sidebar-breakpoint: 1024px;
|
|||
/*
|
||||
* Color schema
|
||||
*/
|
||||
$darken-normal-factor: 7%;
|
||||
$darken-dark-factor: 10%;
|
||||
$darken-border-factor: 5%;
|
||||
|
||||
$white-light: #fff;
|
||||
$white-normal: #ededed;
|
||||
$white-dark: #ececec;
|
||||
$white-normal: darken($white-light, $darken-normal-factor);
|
||||
$white-dark: darken($white-light, $darken-dark-factor);
|
||||
|
||||
$gray-lightest: #fdfdfd;
|
||||
$gray-light: #fafafa;
|
||||
$gray-lighter: #f9f9f9;
|
||||
$gray-normal: #f5f5f5;
|
||||
$gray-dark: #ededed;
|
||||
$gray-normal: darken($gray-light, $darken-normal-factor);
|
||||
$gray-dark: darken($gray-light, $darken-dark-factor);
|
||||
$gray-darker: #eee;
|
||||
$gray-darkest: #c9c9c9;
|
||||
|
||||
$green-light: #38ae67;
|
||||
$green-normal: #2faa60;
|
||||
$green-dark: #2ca05b;
|
||||
$green-light: #3cbd70;
|
||||
$green-normal: darken($green-light, $darken-normal-factor);
|
||||
$green-dark: darken($green-light, $darken-dark-factor);
|
||||
|
||||
$blue-light: #2ea8e5;
|
||||
$blue-normal: #2d9fd8;
|
||||
$blue-dark: #2897ce;
|
||||
$blue-normal: darken($blue-light, $darken-normal-factor);
|
||||
$blue-dark: darken($blue-light, $darken-dark-factor);
|
||||
|
||||
$blue-medium-light: #3498cb;
|
||||
$blue-medium: #2f8ebf;
|
||||
$blue-medium-dark: #2d86b4;
|
||||
$blue-medium: darken($blue-medium-light, $darken-normal-factor);
|
||||
$blue-medium-dark: darken($blue-medium-light, $darken-dark-factor);
|
||||
|
||||
$blue-light-transparent: rgba(44, 159, 216, 0.05);
|
||||
|
||||
$orange-light: #fc8a51;
|
||||
$orange-normal: #e75e40;
|
||||
$orange-dark: #ce5237;
|
||||
$orange-normal: darken($orange-light, $darken-normal-factor);
|
||||
$orange-dark: darken($orange-light, $darken-dark-factor);
|
||||
|
||||
$red-light: #e52c5a;
|
||||
$red-normal: #d22852;
|
||||
$red-dark: darken($red-normal, 5%);
|
||||
$red-normal: darken($red-light, $darken-normal-factor);
|
||||
$red-dark: darken($red-light, $darken-dark-factor);
|
||||
|
||||
$black: #000;
|
||||
$black-transparent: rgba(0, 0, 0, 0.3);
|
||||
|
||||
$border-white-light: #f1f2f4;
|
||||
$border-white-normal: #d6dae2;
|
||||
$border-white-dark: #c6cacf;
|
||||
$border-white-light: darken($white-light, $darken-border-factor);
|
||||
$border-white-normal: darken($white-normal, $darken-border-factor);
|
||||
$border-white-dark: darken($white-dark, $darken-border-factor);
|
||||
|
||||
$border-gray-light: #dcdcdc;
|
||||
$border-gray-normal: #d7d7d7;
|
||||
$border-gray-dark: #c6cacf;
|
||||
$border-gray-light: darken($gray-light, $darken-border-factor);
|
||||
$border-gray-normal: darken($gray-normal, $darken-border-factor);
|
||||
$border-gray-dark: darken($gray-dark, $darken-border-factor);
|
||||
|
||||
$border-green-extra-light: #9adb84;
|
||||
$border-green-light: #2faa60;
|
||||
$border-green-normal: #2ca05b;
|
||||
$border-green-dark: #279654;
|
||||
$border-green-light: darken($green-light, $darken-border-factor);
|
||||
$border-green-normal: darken($green-normal, $darken-border-factor);
|
||||
$border-green-dark: darken($green-dark, $darken-border-factor);
|
||||
|
||||
$border-blue-light: #2d9fd8;
|
||||
$border-blue-normal: #2897ce;
|
||||
$border-blue-dark: #258dc1;
|
||||
$border-blue-light: darken($blue-light, $darken-border-factor);
|
||||
$border-blue-normal: darken($blue-normal, $darken-border-factor);
|
||||
$border-blue-dark: darken($blue-dark, $darken-border-factor);
|
||||
|
||||
$border-orange-light: #fc6d26;
|
||||
$border-orange-normal: #ce5237;
|
||||
$border-orange-dark: #c14e35;
|
||||
$border-orange-light: darken($orange-light, $darken-border-factor);
|
||||
$border-orange-normal: darken($orange-normal, $darken-border-factor);
|
||||
$border-orange-dark: darken($orange-dark, $darken-border-factor);
|
||||
|
||||
$border-red-light: #d22852;
|
||||
$border-red-normal: #ca264f;
|
||||
$border-red-dark: darken($border-red-normal, 5%);
|
||||
$border-red-light: darken($red-light, $darken-border-factor);
|
||||
$border-red-normal: darken($red-normal, $darken-border-factor);
|
||||
$border-red-dark: darken($red-dark, $darken-border-factor);
|
||||
|
||||
$help-well-bg: $gray-light;
|
||||
$help-well-border: #e5e5e5;
|
||||
|
@ -216,7 +220,7 @@ $dropdown-bg: #fff;
|
|||
$dropdown-link-color: #555;
|
||||
$dropdown-link-hover-bg: $row-hover;
|
||||
$dropdown-empty-row-bg: rgba(#000, .04);
|
||||
$dropdown-border-color: rgba(#000, .1);
|
||||
$dropdown-border-color: $border-color;
|
||||
$dropdown-shadow-color: rgba(#000, .1);
|
||||
$dropdown-divider-color: rgba(#000, .1);
|
||||
$dropdown-header-color: #959494;
|
||||
|
@ -225,13 +229,15 @@ $dropdown-input-color: #555;
|
|||
$dropdown-input-focus-border: $focus-border-color;
|
||||
$dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4);
|
||||
$dropdown-loading-bg: rgba(#fff, .6);
|
||||
$dropdown-chevron-size: 10px;
|
||||
|
||||
$dropdown-toggle-bg: #fff;
|
||||
$dropdown-toggle-color: #626262;
|
||||
$dropdown-toggle-border-color: #eaeaea;
|
||||
$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 15%);
|
||||
$dropdown-toggle-color: #5c5c5c;
|
||||
$dropdown-toggle-border-color: #e5e5e5;
|
||||
$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 13%);
|
||||
$dropdown-toggle-active-border-color: darken($dropdown-toggle-border-color, 14%);
|
||||
$dropdown-toggle-icon-color: #c4c4c4;
|
||||
$dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color;
|
||||
$dropdown-toggle-hover-icon-color: darken($dropdown-toggle-icon-color, 7%);
|
||||
|
||||
/*
|
||||
* Buttons
|
||||
|
@ -255,7 +261,7 @@ $search-input-border-color: rgba(#4688f1, .8);
|
|||
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
|
||||
$search-input-width: 220px;
|
||||
$location-badge-color: #aaa;
|
||||
$location-badge-bg: $gray-normal;
|
||||
$location-badge-bg: $dark-background-color;
|
||||
$location-badge-active-bg: #4f91f8;
|
||||
$location-icon-color: #e7e9ed;
|
||||
$location-icon-active-color: #807e7e;
|
||||
|
|
|
@ -132,7 +132,7 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.btn-clipboard {
|
||||
.btn-clipboard:hover {
|
||||
color: $gl-gray;
|
||||
}
|
||||
}
|
||||
|
@ -235,6 +235,10 @@
|
|||
padding-bottom: 10px;
|
||||
color: #999;
|
||||
|
||||
&:hover {
|
||||
color: $gl-gray;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
|
@ -244,15 +248,17 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.avatar:hover {
|
||||
border-color: #999;
|
||||
}
|
||||
|
||||
.btn-clipboard {
|
||||
border: none;
|
||||
color: #999;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
i {
|
||||
color: #999;
|
||||
color: $gl-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -876,3 +876,11 @@ pre.light-well {
|
|||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.variables-table {
|
||||
table-layout: fixed;
|
||||
|
||||
.variable-key {
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,6 +112,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
:koding_enabled,
|
||||
:koding_url,
|
||||
:email_author_in_body,
|
||||
:html_emails_enabled,
|
||||
:repository_checks_enabled,
|
||||
:metrics_packet_size,
|
||||
:send_user_confirmation_email,
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
module LfsHelper
|
||||
include Gitlab::Routing.url_helpers
|
||||
# This concern assumes:
|
||||
# - a `#project` accessor
|
||||
# - a `#user` accessor
|
||||
# - a `#authentication_result` accessor
|
||||
# - a `#can?(object, action, subject)` method
|
||||
# - a `#ci?` method
|
||||
# - a `#download_request?` method
|
||||
# - a `#upload_request?` method
|
||||
# - a `#has_authentication_ability?(ability)` method
|
||||
module LfsRequest
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_action :require_lfs_enabled!
|
||||
before_action :lfs_check_access!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_lfs_enabled!
|
||||
return if Gitlab.config.lfs.enabled
|
||||
|
@ -17,35 +33,15 @@ module LfsHelper
|
|||
return if download_request? && lfs_download_access?
|
||||
return if upload_request? && lfs_upload_access?
|
||||
|
||||
if project.public? || (user && user.can?(:read_project, project))
|
||||
render_lfs_forbidden
|
||||
if project.public? || can?(user, :read_project, project)
|
||||
lfs_forbidden!
|
||||
else
|
||||
render_lfs_not_found
|
||||
end
|
||||
end
|
||||
|
||||
def lfs_download_access?
|
||||
return false unless project.lfs_enabled?
|
||||
|
||||
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
|
||||
end
|
||||
|
||||
def objects
|
||||
@objects ||= (params[:objects] || []).to_a
|
||||
end
|
||||
|
||||
def user_can_download_code?
|
||||
has_authentication_ability?(:download_code) && can?(user, :download_code, project)
|
||||
end
|
||||
|
||||
def build_can_download_code?
|
||||
has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
|
||||
end
|
||||
|
||||
def lfs_upload_access?
|
||||
return false unless project.lfs_enabled?
|
||||
|
||||
has_authentication_ability?(:push_code) && can?(user, :push_code, project)
|
||||
def lfs_forbidden!
|
||||
render_lfs_forbidden
|
||||
end
|
||||
|
||||
def render_lfs_forbidden
|
||||
|
@ -70,6 +66,30 @@ module LfsHelper
|
|||
)
|
||||
end
|
||||
|
||||
def lfs_download_access?
|
||||
return false unless project.lfs_enabled?
|
||||
|
||||
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
|
||||
end
|
||||
|
||||
def lfs_upload_access?
|
||||
return false unless project.lfs_enabled?
|
||||
|
||||
has_authentication_ability?(:push_code) && can?(user, :push_code, project)
|
||||
end
|
||||
|
||||
def lfs_deploy_token?
|
||||
authentication_result.lfs_deploy_token?(project)
|
||||
end
|
||||
|
||||
def user_can_download_code?
|
||||
has_authentication_ability?(:download_code) && can?(user, :download_code, project)
|
||||
end
|
||||
|
||||
def build_can_download_code?
|
||||
has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
|
||||
end
|
||||
|
||||
def storage_project
|
||||
@storage_project ||= begin
|
||||
result = project
|
||||
|
@ -82,4 +102,8 @@ module LfsHelper
|
|||
result
|
||||
end
|
||||
end
|
||||
|
||||
def objects
|
||||
@objects ||= (params[:objects] || []).to_a
|
||||
end
|
||||
end
|
|
@ -1,11 +1,8 @@
|
|||
module ToggleAwardEmoji
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_action :authenticate_user!, only: [:toggle_award_emoji]
|
||||
end
|
||||
|
||||
def toggle_award_emoji
|
||||
authenticate_user!
|
||||
name = params.require(:name)
|
||||
|
||||
if awardable.user_can_award?(current_user, name)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
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
|
|||
def index
|
||||
@help_index = File.read(Rails.root.join('doc', 'README.md'))
|
||||
|
||||
# Prefix Markdown links with `help/` unless they already have been
|
||||
# See http://rubular.com/r/ie2MlpdUMq
|
||||
@help_index.gsub!(/(\]\()(\/?help\/)?([^\)\(]+\))/, '\1/help/\3')
|
||||
# Prefix Markdown links with `help/` unless they are external links
|
||||
# See http://rubular.com/r/MioSrVLK3S
|
||||
@help_index.gsub!(%r{(\]\()(?!.+://)([^\)\(]+\))}, '\1/help/\2')
|
||||
end
|
||||
|
||||
def show
|
||||
|
|
|
@ -4,7 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController
|
|||
@user.remove_avatar!
|
||||
|
||||
@user.save
|
||||
@user.reset_events_cache
|
||||
|
||||
redirect_to profile_path
|
||||
end
|
||||
|
|
|
@ -20,7 +20,6 @@ class Projects::AvatarsController < Projects::ApplicationController
|
|||
@project.remove_avatar!
|
||||
|
||||
@project.save
|
||||
@project.reset_events_cache
|
||||
|
||||
redirect_to edit_project_path(@project)
|
||||
end
|
||||
|
|
|
@ -13,7 +13,6 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
before_action :assign_blob_vars
|
||||
before_action :commit, except: [:new, :create]
|
||||
before_action :blob, except: [:new, :create]
|
||||
before_action :from_merge_request, only: [:edit, :update]
|
||||
before_action :require_branch_head, only: [:edit, :update]
|
||||
before_action :editor_variables, except: [:show, :preview, :diff]
|
||||
before_action :validate_diff_params, only: :diff
|
||||
|
@ -39,14 +38,6 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
def update
|
||||
@path = params[:file_path] if params[:file_path].present?
|
||||
after_edit_path =
|
||||
if from_merge_request && @target_branch == @ref
|
||||
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
|
||||
"##{hexdigest(@path)}"
|
||||
else
|
||||
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
|
||||
end
|
||||
|
||||
create_commit(Files::UpdateService, success_path: after_edit_path,
|
||||
failure_view: :edit,
|
||||
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
|
||||
|
@ -124,9 +115,14 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
render_404
|
||||
end
|
||||
|
||||
def from_merge_request
|
||||
# If blob edit was initiated from merge request page
|
||||
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
|
||||
def after_edit_path
|
||||
from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid])
|
||||
if from_merge_request && @target_branch == @ref
|
||||
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
|
||||
"##{hexdigest(@path)}"
|
||||
else
|
||||
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
|
||||
end
|
||||
end
|
||||
|
||||
def editor_variables
|
||||
|
|
|
@ -36,7 +36,7 @@ class Projects::BranchesController < Projects::ApplicationController
|
|||
execute(branch_name, ref)
|
||||
|
||||
if params[:issue_iid]
|
||||
issue = @project.issues.find_by(iid: params[:issue_iid])
|
||||
issue = IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:issue_iid])
|
||||
SystemNoteService.new_issue_branch(issue, @project, current_user, branch_name) if issue
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
|
|||
before_action :authorize_read_cycle_analytics!
|
||||
|
||||
def show
|
||||
@cycle_analytics = ::CycleAnalytics.new(@project, from: start_date(cycle_analytics_params))
|
||||
@cycle_analytics = ::CycleAnalytics.new(@project, current_user, from: start_date(cycle_analytics_params))
|
||||
|
||||
stats_values, cycle_analytics_json = generate_cycle_analytics_data
|
||||
|
||||
|
|
|
@ -18,6 +18,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def authenticate_user
|
||||
@authentication_result = Gitlab::Auth::Result.new
|
||||
|
||||
|
@ -130,10 +138,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController
|
|||
authentication_result.ci?(project)
|
||||
end
|
||||
|
||||
def lfs_deploy_token?
|
||||
authentication_result.lfs_deploy_token?(project)
|
||||
end
|
||||
|
||||
def authentication_has_download_access?
|
||||
has_authentication_ability?(:download_code) || has_authentication_ability?(:build_download_code)
|
||||
end
|
||||
|
@ -149,8 +153,4 @@ class Projects::GitHttpClientController < Projects::ApplicationController
|
|||
def authentication_project
|
||||
authentication_result.project
|
||||
end
|
||||
|
||||
def verify_workhorse_api!
|
||||
Gitlab::Workhorse.verify_api_request!(request.headers)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# This file should be identical in GitLab Community Edition and Enterprise Edition
|
||||
|
||||
class Projects::GitHttpController < Projects::GitHttpClientController
|
||||
before_action :verify_workhorse_api!
|
||||
include WorkhorseRequest
|
||||
|
||||
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
|
||||
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
|
||||
|
@ -67,14 +65,18 @@ class Projects::GitHttpController < Projects::GitHttpClientController
|
|||
end
|
||||
|
||||
def render_denied
|
||||
if user && user.can?(:read_project, project)
|
||||
render plain: 'Access denied', status: :forbidden
|
||||
if user && can?(user, :read_project, project)
|
||||
render plain: access_denied_message, status: :forbidden
|
||||
else
|
||||
# Do not leak information about project existence
|
||||
render_not_found
|
||||
end
|
||||
end
|
||||
|
||||
def access_denied_message
|
||||
'Access denied'
|
||||
end
|
||||
|
||||
def upload_pack_allowed?
|
||||
return false unless Gitlab.config.gitlab_shell.upload_pack
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
class Projects::LfsApiController < Projects::GitHttpClientController
|
||||
include LfsHelper
|
||||
include LfsRequest
|
||||
|
||||
before_action :require_lfs_enabled!
|
||||
before_action :lfs_check_access!, except: [:deprecated]
|
||||
skip_before_action :lfs_check_access!, only: [:deprecated]
|
||||
|
||||
def batch
|
||||
unless objects.present?
|
||||
|
@ -31,6 +30,14 @@ class Projects::LfsApiController < Projects::GitHttpClientController
|
|||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
params[:operation] == 'download'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
params[:operation] == 'upload'
|
||||
end
|
||||
|
||||
def existing_oids
|
||||
@existing_oids ||= begin
|
||||
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
|
||||
|
@ -79,12 +86,4 @@ class Projects::LfsApiController < Projects::GitHttpClientController
|
|||
}
|
||||
}
|
||||
end
|
||||
|
||||
def download_request?
|
||||
params[:operation] == 'download'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
params[:operation] == 'upload'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
class Projects::LfsStorageController < Projects::GitHttpClientController
|
||||
include LfsHelper
|
||||
include LfsRequest
|
||||
include WorkhorseRequest
|
||||
|
||||
before_action :require_lfs_enabled!
|
||||
before_action :lfs_check_access!
|
||||
before_action :verify_workhorse_api!, only: [:upload_authorize]
|
||||
skip_before_action :verify_workhorse_api!, only: [:download, :upload_finalize]
|
||||
|
||||
def download
|
||||
lfs_object = LfsObject.find_by_oid(oid)
|
||||
|
|
|
@ -329,17 +329,18 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
@merge_request.update(merge_error: nil)
|
||||
|
||||
if params[:merge_when_build_succeeds].present?
|
||||
unless @merge_request.pipeline
|
||||
unless @merge_request.head_pipeline
|
||||
@status = :failed
|
||||
return
|
||||
end
|
||||
|
||||
if @merge_request.pipeline.active?
|
||||
if @merge_request.head_pipeline.active?
|
||||
MergeRequests::MergeWhenPipelineSucceedsService
|
||||
.new(@project, current_user, merge_params)
|
||||
.execute(@merge_request)
|
||||
|
||||
@status = :merge_when_build_succeeds
|
||||
elsif @merge_request.pipeline.success?
|
||||
elsif @merge_request.head_pipeline.success?
|
||||
# This can be triggered when a user clicks the auto merge button while
|
||||
# the tests finish at about the same time
|
||||
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
|
||||
|
@ -403,7 +404,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def ci_status
|
||||
pipeline = @merge_request.pipeline
|
||||
pipeline = @merge_request.head_pipeline
|
||||
|
||||
if pipeline
|
||||
status = pipeline.status
|
||||
coverage = pipeline.try(:coverage)
|
||||
|
@ -539,7 +541,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def define_widget_vars
|
||||
@pipeline = @merge_request.pipeline
|
||||
@pipeline = @merge_request.head_pipeline
|
||||
end
|
||||
|
||||
def define_commit_vars
|
||||
|
@ -568,8 +570,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def define_pipelines_vars
|
||||
@pipelines = @merge_request.all_pipelines
|
||||
@pipeline = @merge_request.pipeline
|
||||
@statuses = @pipeline.statuses.relevant if @pipeline.present?
|
||||
@pipeline = @merge_request.head_pipeline
|
||||
@statuses_count = @pipeline.present? ? @pipeline.statuses.relevant.count : 0
|
||||
end
|
||||
|
||||
def define_new_vars
|
||||
|
@ -636,7 +638,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def merge_when_build_succeeds_active?
|
||||
params[:merge_when_build_succeeds].present? &&
|
||||
@merge_request.pipeline && @merge_request.pipeline.active?
|
||||
@merge_request.head_pipeline && @merge_request.head_pipeline.active?
|
||||
end
|
||||
|
||||
def build_merge_request
|
||||
|
|
|
@ -16,13 +16,7 @@ class Projects::TodosController < Projects::ApplicationController
|
|||
@issuable ||= begin
|
||||
case params[:issuable_type]
|
||||
when "issue"
|
||||
issue = @project.issues.find(params[:issuable_id])
|
||||
|
||||
if can?(current_user, :read_issue, issue)
|
||||
issue
|
||||
else
|
||||
render_404
|
||||
end
|
||||
IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
|
||||
when "merge_request"
|
||||
@project.merge_requests.find(params[:issuable_id])
|
||||
end
|
||||
|
|
|
@ -16,14 +16,12 @@
|
|||
# label_name: string
|
||||
# sort: string
|
||||
#
|
||||
require_relative 'projects_finder'
|
||||
|
||||
class IssuableFinder
|
||||
NONE = '0'
|
||||
|
||||
attr_accessor :current_user, :params
|
||||
|
||||
def initialize(current_user, params)
|
||||
def initialize(current_user, params = {})
|
||||
@current_user = current_user
|
||||
@params = params
|
||||
end
|
||||
|
@ -43,6 +41,14 @@ class IssuableFinder
|
|||
sort(items)
|
||||
end
|
||||
|
||||
def find(*params)
|
||||
execute.find(*params)
|
||||
end
|
||||
|
||||
def find_by(*params)
|
||||
execute.find_by(*params)
|
||||
end
|
||||
|
||||
def group
|
||||
return @group if defined?(@group)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ class NotesFinder
|
|||
when "commit"
|
||||
project.notes.for_commit_id(target_id).non_diff_notes
|
||||
when "issue"
|
||||
project.issues.visible_to_user(current_user).find(target_id).notes.inc_author
|
||||
IssuesFinder.new(current_user, project_id: project.id).find(target_id).notes.inc_author
|
||||
when "merge_request"
|
||||
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
|
||||
when "snippet", "project_snippet"
|
||||
|
|
|
@ -43,7 +43,7 @@ module DropdownsHelper
|
|||
default_label = data_attr[:default_label]
|
||||
content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
|
||||
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}")
|
||||
output << icon('caret-down')
|
||||
output << icon('chevron-down')
|
||||
output.html_safe
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,11 +8,7 @@ module GroupsHelper
|
|||
group = Group.find_by(path: group)
|
||||
end
|
||||
|
||||
if group && group.avatar.present?
|
||||
group.avatar.url
|
||||
else
|
||||
image_path('no_group_avatar.png')
|
||||
end
|
||||
group.try(:avatar_url) || image_path('no_group_avatar.png')
|
||||
end
|
||||
|
||||
def group_title(group, name = nil, url = nil)
|
||||
|
|
|
@ -143,6 +143,20 @@ module IssuablesHelper
|
|||
end
|
||||
end
|
||||
|
||||
def issuable_filter_params
|
||||
[
|
||||
:search,
|
||||
:author_id,
|
||||
:assignee_id,
|
||||
:milestone_title,
|
||||
:label_name
|
||||
]
|
||||
end
|
||||
|
||||
def issuable_filter_present?
|
||||
issuable_filter_params.any? { |k| params.key?(k) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assigned_issuables_count(assignee, issuable_type, state)
|
||||
|
@ -165,13 +179,9 @@ module IssuablesHelper
|
|||
end
|
||||
end
|
||||
|
||||
def issuable_filters_present
|
||||
params[:search] || params[:author_id] || params[:assignee_id] || params[:milestone_title] || params[:label_name]
|
||||
end
|
||||
|
||||
def issuables_count_for_state(issuable_type, state)
|
||||
issuables_finder = public_send("#{issuable_type}_finder")
|
||||
|
||||
|
||||
params = issuables_finder.params.merge(state: state)
|
||||
finder = issuables_finder.class.new(issuables_finder.current_user, params)
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ module Emails
|
|||
setup_note_mail(note_id, recipient_id)
|
||||
|
||||
@commit = @note.noteable
|
||||
@discussion = @note.to_discussion if @note.diff_note?
|
||||
@target_url = namespace_project_commit_url(*note_target_url_options)
|
||||
|
||||
mail_answer_thread(@commit,
|
||||
|
@ -24,6 +25,7 @@ module Emails
|
|||
setup_note_mail(note_id, recipient_id)
|
||||
|
||||
@merge_request = @note.noteable
|
||||
@discussion = @note.to_discussion if @note.diff_note?
|
||||
@target_url = namespace_project_merge_request_url(*note_target_url_options)
|
||||
mail_answer_thread(@merge_request, note_thread_options(recipient_id))
|
||||
end
|
||||
|
|
|
@ -317,7 +317,7 @@ module Ci
|
|||
def merge_requests
|
||||
@merge_requests ||= project.merge_requests
|
||||
.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
|
||||
|
||||
private
|
||||
|
|
|
@ -2,6 +2,9 @@ module ProtectedBranchAccess
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
belongs_to :protected_branch
|
||||
delegate :project, to: :protected_branch
|
||||
|
||||
scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
|
||||
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
|
||||
end
|
||||
|
@ -9,4 +12,10 @@ module ProtectedBranchAccess
|
|||
def humanize
|
||||
self.class.human_access_levels[self.access_level]
|
||||
end
|
||||
|
||||
def check_access(user)
|
||||
return true if user.is_admin?
|
||||
|
||||
project.team.max_member_access(user.id) >= access_level
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
class CycleAnalytics
|
||||
STAGES = %i[issue plan code test review staging production].freeze
|
||||
|
||||
def initialize(project, from:)
|
||||
def initialize(project, current_user, from:)
|
||||
@project = project
|
||||
@current_user = current_user
|
||||
@from = from
|
||||
@fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: from, branch: nil)
|
||||
end
|
||||
|
||||
def summary
|
||||
@summary ||= Summary.new(@project, from: @from)
|
||||
@summary ||= Summary.new(@project, @current_user, from: @from)
|
||||
end
|
||||
|
||||
def permissions(user:)
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
class CycleAnalytics
|
||||
class Summary
|
||||
def initialize(project, from:)
|
||||
def initialize(project, current_user, from:)
|
||||
@project = project
|
||||
@current_user = current_user
|
||||
@from = from
|
||||
end
|
||||
|
||||
def new_issues
|
||||
@project.issues.created_after(@from).count
|
||||
IssuesFinder.new(@current_user, project_id: @project.id).execute.created_after(@from).count
|
||||
end
|
||||
|
||||
def commits
|
||||
|
|
|
@ -25,7 +25,12 @@ class Discussion
|
|||
to: :last_resolved_note,
|
||||
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)
|
||||
notes.group_by(&:discussion_id).values.map { |notes| new(notes) }
|
||||
|
@ -159,10 +164,11 @@ class Discussion
|
|||
end
|
||||
|
||||
# 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 = []
|
||||
|
||||
highlighted_diff_lines.each do |line|
|
||||
lines.each do |line|
|
||||
if line.meta?
|
||||
prev_lines.clear
|
||||
else
|
||||
|
|
|
@ -43,12 +43,6 @@ class Event < ActiveRecord::Base
|
|||
scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
|
||||
|
||||
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
|
||||
def contributions
|
||||
where("action = ? OR (target_type in (?) AND action in (?))",
|
||||
|
@ -353,6 +347,10 @@ class Event < ActiveRecord::Base
|
|||
update_all(last_activity_at: created_at)
|
||||
end
|
||||
|
||||
def authored_by?(user)
|
||||
user ? author_id == user.id : false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def recent_update?
|
||||
|
|
|
@ -182,18 +182,6 @@ class Issue < ActiveRecord::Base
|
|||
branches_with_iid - branches_with_merge_request
|
||||
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.
|
||||
def source_project
|
||||
project
|
||||
|
|
|
@ -605,18 +605,6 @@ class MergeRequest < ActiveRecord::Base
|
|||
self.target_project.repository.branch_names.include?(self.target_branch)
|
||||
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
|
||||
message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n"
|
||||
message << "#{title}\n\n"
|
||||
|
@ -690,7 +678,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
def mergeable_ci_state?
|
||||
return true unless project.only_allow_merge_if_build_succeeds?
|
||||
|
||||
!pipeline || pipeline.success? || pipeline.skipped?
|
||||
!head_pipeline || head_pipeline.success? || head_pipeline.skipped?
|
||||
end
|
||||
|
||||
def environments
|
||||
|
@ -786,14 +774,14 @@ class MergeRequest < ActiveRecord::Base
|
|||
commits.map(&:sha)
|
||||
end
|
||||
|
||||
def pipeline
|
||||
def head_pipeline
|
||||
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
|
||||
|
||||
def all_pipelines
|
||||
return unless source_project
|
||||
return Ci::Pipeline.none unless source_project
|
||||
|
||||
@all_pipelines ||= source_project.pipelines
|
||||
.where(sha: all_commits_sha, ref: source_branch)
|
||||
|
|
|
@ -201,19 +201,6 @@ class Note < ActiveRecord::Base
|
|||
super(noteable_type.to_s.classify.constantize.base_class.to_s)
|
||||
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?
|
||||
!system?
|
||||
end
|
||||
|
|
|
@ -687,9 +687,9 @@ class Project < ActiveRecord::Base
|
|||
self.id
|
||||
end
|
||||
|
||||
def get_issue(issue_id)
|
||||
def get_issue(issue_id, current_user)
|
||||
if default_issues_tracker?
|
||||
issues.find_by(iid: issue_id)
|
||||
IssuesFinder.new(current_user, project_id: id).find_by(iid: issue_id)
|
||||
else
|
||||
ExternalIssue.new(issue_id, self)
|
||||
end
|
||||
|
@ -976,7 +976,6 @@ class Project < ActiveRecord::Base
|
|||
begin
|
||||
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
|
||||
send_move_instructions(old_path_with_namespace)
|
||||
reset_events_cache
|
||||
|
||||
@old_path_with_namespace = old_path_with_namespace
|
||||
|
||||
|
@ -1043,22 +1042,6 @@ class Project < ActiveRecord::Base
|
|||
attrs
|
||||
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)
|
||||
project_members.find_by(user_id: user)
|
||||
end
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
|
||||
include ProtectedBranchAccess
|
||||
|
||||
belongs_to :protected_branch
|
||||
delegate :project, to: :protected_branch
|
||||
|
||||
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
|
||||
Gitlab::Access::DEVELOPER] }
|
||||
|
||||
|
@ -13,10 +10,4 @@ class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
|
|||
Gitlab::Access::DEVELOPER => "Developers + Masters"
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
def check_access(user)
|
||||
return true if user.is_admin?
|
||||
|
||||
project.team.max_member_access(user.id) >= access_level
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
|
||||
include ProtectedBranchAccess
|
||||
|
||||
belongs_to :protected_branch
|
||||
delegate :project, to: :protected_branch
|
||||
|
||||
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
|
||||
Gitlab::Access::DEVELOPER,
|
||||
Gitlab::Access::NO_ACCESS] }
|
||||
|
@ -18,8 +15,7 @@ class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
|
|||
|
||||
def check_access(user)
|
||||
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
|
||||
|
|
|
@ -196,18 +196,12 @@ class Repository
|
|||
|
||||
options = { message: message, tagger: user_to_committer(user) } if message
|
||||
|
||||
rugged.tags.create(tag_name, target, options)
|
||||
tag = find_tag(tag_name)
|
||||
|
||||
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
|
||||
GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do |service|
|
||||
raw_tag = rugged.tags.create(tag_name, target, options)
|
||||
service.newrev = raw_tag.target_id
|
||||
end
|
||||
|
||||
tag
|
||||
rescue GitHooksService::PreReceiveError
|
||||
rugged.tags.delete(tag_name)
|
||||
raise
|
||||
find_tag(tag_name)
|
||||
end
|
||||
|
||||
def rm_branch(user, branch_name)
|
||||
|
|
|
@ -445,27 +445,21 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def refresh_authorized_projects
|
||||
loop do
|
||||
begin
|
||||
Gitlab::Database.serialized_transaction do
|
||||
project_authorizations.delete_all
|
||||
transaction do
|
||||
project_authorizations.delete_all
|
||||
|
||||
# project_authorizations_union can return multiple records for the same project/user with
|
||||
# different access_level so we take row with the maximum access_level
|
||||
project_authorizations.connection.execute <<-SQL
|
||||
INSERT INTO project_authorizations (user_id, project_id, access_level)
|
||||
SELECT user_id, project_id, MAX(access_level) AS access_level
|
||||
FROM (#{project_authorizations_union.to_sql}) sub
|
||||
GROUP BY user_id, project_id
|
||||
SQL
|
||||
# project_authorizations_union can return multiple records for the same
|
||||
# project/user with different access_level so we take row with the maximum
|
||||
# access_level
|
||||
project_authorizations.connection.execute <<-SQL
|
||||
INSERT INTO project_authorizations (user_id, project_id, access_level)
|
||||
SELECT user_id, project_id, MAX(access_level) AS access_level
|
||||
FROM (#{project_authorizations_union.to_sql}) sub
|
||||
GROUP BY user_id, project_id
|
||||
SQL
|
||||
|
||||
update_column(:authorized_projects_populated, true) unless authorized_projects_populated
|
||||
end
|
||||
|
||||
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
|
||||
unless authorized_projects_populated
|
||||
update_column(:authorized_projects_populated, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -708,20 +702,6 @@ class User < ActiveRecord::Base
|
|||
project.project_member(self)
|
||||
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
|
||||
return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\//
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
expose :branch do
|
||||
|
|
|
@ -16,6 +16,9 @@ class BuildEntity < Grape::Entity
|
|||
path_to(:play_namespace_project_build, build)
|
||||
end
|
||||
|
||||
expose :created_at
|
||||
expose :updated_at
|
||||
|
||||
private
|
||||
|
||||
def path_to(route, build)
|
||||
|
|
|
@ -2,6 +2,8 @@ module EntityDateHelper
|
|||
include ActionView::Helpers::DateHelper
|
||||
|
||||
def interval_in_words(diff)
|
||||
return 'Not started' unless diff
|
||||
|
||||
"#{distance_of_time_in_words(Time.now, diff)} ago"
|
||||
end
|
||||
|
||||
|
|
|
@ -45,9 +45,15 @@ module Ci
|
|||
return error('No builds for this pipeline.')
|
||||
end
|
||||
|
||||
pipeline.save
|
||||
pipeline.process!
|
||||
pipeline
|
||||
Ci::Pipeline.transaction do
|
||||
pipeline.save
|
||||
|
||||
Ci::CreatePipelineBuildsService
|
||||
.new(project, current_user)
|
||||
.execute(pipeline)
|
||||
end
|
||||
|
||||
pipeline.tap(&:process!)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -5,10 +5,7 @@ module Ci
|
|||
def execute(pipeline)
|
||||
@pipeline = pipeline
|
||||
|
||||
# This method will ensure that our pipeline does have all builds for all stages created
|
||||
if created_builds.empty?
|
||||
create_builds!
|
||||
end
|
||||
ensure_created_builds! # TODO, remove me in 9.0
|
||||
|
||||
new_builds =
|
||||
stage_indexes_of_created_builds.map do |index|
|
||||
|
@ -22,10 +19,6 @@ module Ci
|
|||
|
||||
private
|
||||
|
||||
def create_builds!
|
||||
Ci::CreatePipelineBuildsService.new(project, current_user).execute(pipeline)
|
||||
end
|
||||
|
||||
def process_stage(index)
|
||||
current_status = status_for_prior_stages(index)
|
||||
|
||||
|
@ -76,5 +69,18 @@ module Ci
|
|||
def created_builds
|
||||
pipeline.builds.created
|
||||
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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
class GitHooksService
|
||||
PreReceiveError = Class.new(StandardError)
|
||||
|
||||
attr_accessor :oldrev, :newrev, :ref
|
||||
|
||||
def execute(user, repo_path, oldrev, newrev, ref)
|
||||
@repo_path = repo_path
|
||||
@user = Gitlab::GlId.gl_id(user)
|
||||
|
@ -16,7 +18,7 @@ class GitHooksService
|
|||
end
|
||||
end
|
||||
|
||||
yield
|
||||
yield self
|
||||
|
||||
run_hook('post-receive')
|
||||
end
|
||||
|
@ -25,6 +27,6 @@ class GitHooksService
|
|||
|
||||
def run_hook(name)
|
||||
hook = Gitlab::Git::Hook.new(name, @repo_path)
|
||||
hook.trigger(@user, @oldrev, @newrev, @ref)
|
||||
hook.trigger(@user, oldrev, newrev, ref)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -85,14 +85,15 @@ class IssuableBaseService < BaseService
|
|||
|
||||
def find_or_create_label_ids
|
||||
labels = params.delete(: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)
|
||||
label = service.execute
|
||||
|
||||
label.id
|
||||
end
|
||||
label.try(:id)
|
||||
end.compact
|
||||
end
|
||||
|
||||
def process_label_ids(attributes, existing_label_ids: nil)
|
||||
|
@ -140,6 +141,7 @@ class IssuableBaseService < BaseService
|
|||
|
||||
params.delete(:state_event)
|
||||
params[:author] ||= current_user
|
||||
|
||||
label_ids = process_label_ids(params)
|
||||
|
||||
issuable.assign_attributes(params)
|
||||
|
@ -184,8 +186,6 @@ class IssuableBaseService < BaseService
|
|||
params[:label_ids] = process_label_ids(params, existing_label_ids: issuable.label_ids)
|
||||
|
||||
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
|
||||
ActiveRecord::Base.no_touching do
|
||||
handle_common_system_notes(issuable, old_labels: old_labels)
|
||||
|
|
|
@ -22,9 +22,14 @@ module Labels
|
|||
).execute(skip_authorization: skip_authorization)
|
||||
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
|
||||
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
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ module MergeRequests
|
|||
|
||||
def pipeline_merge_requests(pipeline)
|
||||
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
|
||||
end
|
||||
|
@ -63,7 +63,7 @@ module MergeRequests
|
|||
|
||||
def commit_status_merge_requests(commit_status)
|
||||
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.sha == commit_status.sha
|
||||
|
|
|
@ -81,7 +81,7 @@ module MergeRequests
|
|||
commit = commits.first
|
||||
merge_request.title = commit.title
|
||||
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
|
||||
when Issue
|
||||
merge_request.title = "Resolve \"#{issue.title}\""
|
||||
|
|
|
@ -2,7 +2,6 @@ module Notes
|
|||
class DeleteService < BaseService
|
||||
def execute(note)
|
||||
note.destroy
|
||||
note.reset_events_cache
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,6 @@ module Notes
|
|||
|
||||
note.update_attributes(params.merge(updated_by: current_user))
|
||||
note.create_new_cross_references!(current_user)
|
||||
note.reset_events_cache
|
||||
|
||||
if note.previous_changes.include?('note')
|
||||
TodoService.new.update_note(note, current_user)
|
||||
|
|
|
@ -61,9 +61,6 @@ module Projects
|
|||
# Move missing group labels to project
|
||||
Labels::TransferService.new(current_user, old_group, project).execute
|
||||
|
||||
# clear project cached events
|
||||
project.reset_events_cache
|
||||
|
||||
# Move uploads
|
||||
Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
|
||||
|
||||
|
|
|
@ -3,16 +3,10 @@ class AvatarUploader < CarrierWave::Uploader::Base
|
|||
|
||||
storage :file
|
||||
|
||||
after :store, :reset_events_cache
|
||||
|
||||
def store_dir
|
||||
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
||||
end
|
||||
|
||||
def reset_events_cache(file)
|
||||
model.reset_events_cache if model.is_a?(User)
|
||||
end
|
||||
|
||||
def exists?
|
||||
model.avatar.file && model.avatar.file.exists?
|
||||
end
|
||||
|
|
|
@ -443,7 +443,16 @@
|
|||
Some email servers do not support overriding the email sender name.
|
||||
Enable this option to include the name of the author of the issue,
|
||||
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
|
||||
%legend Automatic Git repository housekeeping
|
||||
.form-group
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
%ul.nav-links
|
||||
= nav_link(page: [dashboard_projects_path, root_path]) 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
|
||||
= 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
|
||||
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
|
||||
Explore Projects
|
||||
Explore projects
|
||||
|
||||
.nav-controls
|
||||
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
|
||||
|
|
|
@ -4,6 +4,18 @@
|
|||
Welcome to GitLab
|
||||
%p.blank-state-text
|
||||
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-icon
|
||||
= custom_icon("project", size: 50)
|
||||
|
@ -21,17 +33,6 @@
|
|||
= link_to new_project_path, class: "btn btn-new" do
|
||||
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
|
||||
.blank-state
|
||||
.blank-state-icon
|
||||
|
|
|
@ -50,13 +50,13 @@
|
|||
data: { data: todo_actions_options }})
|
||||
.pull-right
|
||||
.dropdown.inline.prepend-left-10
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- else
|
||||
= sort_title_recently_created
|
||||
= icon('caret-down')
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
|
||||
%li
|
||||
= link_to todos_filter_path(sort: sort_value_priority) do
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
.event-item-timestamp
|
||||
#{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?
|
||||
= render "events/event/created_project", event: event
|
||||
- elsif event.push?
|
||||
= render "events/event/push", event: event
|
||||
- elsif event.commented?
|
||||
= render "events/event/note", event: event
|
||||
- else
|
||||
= render "events/event/common", event: event
|
||||
- if event.created_project?
|
||||
= render "events/event/created_project", event: event
|
||||
- elsif event.push?
|
||||
= render "events/event/push", event: event
|
||||
- elsif event.commented?
|
||||
= render "events/event/note", event: event
|
||||
- else
|
||||
= render "events/event/common", event: event
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
- few_commits.each do |commit|
|
||||
= 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
|
||||
%li.commits-stat
|
||||
- if event.commits_count > 2
|
||||
|
@ -35,12 +35,12 @@
|
|||
Compare #{from_label}...#{truncate_sha(event.commit_to)}
|
||||
|
||||
- if create_mr
|
||||
%span{"data-user-is" => event.author_id, "data-display" => "inline"}
|
||||
%span
|
||||
or
|
||||
= link_to create_mr_path(project.default_branch, event.ref_name, project) do
|
||||
create a merge request
|
||||
- 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
|
||||
Create Merge Request
|
||||
- elsif event.rm_ref?
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
|
||||
.pull-right
|
||||
.dropdown.inline
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- else
|
||||
= sort_title_recently_created
|
||||
= icon('caret-down')
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
= link_to explore_groups_path(sort: sort_value_recently_created) do
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
- if current_user
|
||||
.dropdown
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
|
||||
= icon('globe')
|
||||
%span.light Visibility:
|
||||
- if params[:visibility_level].present?
|
||||
= visibility_level_label(params[:visibility_level].to_i)
|
||||
- else
|
||||
Any
|
||||
= icon('caret-down')
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
= link_to filter_projects_path(visibility_level: nil) do
|
||||
|
@ -20,14 +20,14 @@
|
|||
|
||||
- if @tags.present?
|
||||
.dropdown
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
|
||||
= icon('tags')
|
||||
%span.light Tags:
|
||||
- if params[:tag].present?
|
||||
= params[:tag]
|
||||
- else
|
||||
Any
|
||||
= icon('caret-down')
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
= link_to filter_projects_path(tag: nil) do
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
- if inviter = @member.created_by
|
||||
by
|
||||
= link_to inviter.name, user_url(inviter)
|
||||
to join
|
||||
to join
|
||||
- case @member.source
|
||||
- when Project
|
||||
- project = @member.source
|
||||
|
@ -20,11 +20,18 @@
|
|||
= link_to group.name, group_url(group)
|
||||
as #{@member.human_access}.
|
||||
|
||||
- if @member.source.users.include?(current_user)
|
||||
- is_member = @member.source.users.include?(current_user)
|
||||
|
||||
- if is_member
|
||||
%p
|
||||
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.
|
||||
- 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
|
||||
= 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"
|
||||
|
|
|
@ -56,5 +56,3 @@
|
|||
= 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/bootlint' if Rails.env.development?
|
||||
|
||||
= render 'layouts/user_styles'
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
: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 @@
|
|||
%span
|
||||
Issues
|
||||
- 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
|
||||
= nav_link(controller: :merge_requests) do
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<% if current_application_settings.email_author_in_body %>
|
||||
<%= @note.author_name %> wrote:
|
||||
<% end -%>
|
||||
|
||||
<%= @note.note %>
|
|
@ -0,0 +1,18 @@
|
|||
= 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'
|
|
@ -0,0 +1,8 @@
|
|||
<% if @discussion && @discussion.diff_file -%>
|
||||
on <%= @note.diff_file.file_path -%>
|
||||
<% end -%>:
|
||||
|
||||
<%= url %>
|
||||
|
||||
<%= render 'simple_diff' if @discussion -%>
|
||||
<%= render 'note_message' %>
|
|
@ -0,0 +1,3 @@
|
|||
<% @discussion.truncated_diff_lines(highlight: false).each do |line| %>
|
||||
> <%= line.text %>
|
||||
<% end %>
|
|
@ -1,2 +1,2 @@
|
|||
= render 'note_message'
|
||||
|
||||
%p.details
|
||||
= render 'note_mr_or_commit_email'
|
||||
|
|
|
@ -1,9 +1,2 @@
|
|||
New comment for Commit <%= @commit.short_id %>
|
||||
|
||||
<%= url_for(namespace_project_commit_url(@note.project.namespace, @note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
|
||||
|
||||
|
||||
Author: <%= @note.author_name %>
|
||||
|
||||
<%= @note.note %>
|
||||
|
||||
New comment for Commit <%= @commit.short_id -%>
|
||||
<%= render partial: 'note_mr_or_commit_email', locals: { url: @target_url } %>
|
||||
|
|
|
@ -1,7 +1,2 @@
|
|||
- if @note.diff_note? && @note.diff_file
|
||||
%p.details
|
||||
New comment on diff for
|
||||
= link_to @note.diff_file.file_path, @target_url
|
||||
\:
|
||||
|
||||
= render 'note_message'
|
||||
%p.details
|
||||
= render 'note_mr_or_commit_email'
|
||||
|
|
|
@ -1,9 +1,2 @@
|
|||
New comment for Merge Request <%= @merge_request.to_reference %>
|
||||
|
||||
<%= 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 %>
|
||||
|
||||
New comment for Merge Request <%= @merge_request.to_reference -%>
|
||||
<%= render partial: 'note_mr_or_commit_email', locals: { url: @target_url }%>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
= content_for :head do
|
||||
= stylesheet_link_tag 'mailers/repository_push_email'
|
||||
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
||||
|
||||
%h3
|
||||
#{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name}
|
||||
|
|
|
@ -27,5 +27,5 @@
|
|||
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
|
||||
= hidden_field_tag 'last_commit_sha', @last_commit_sha
|
||||
= 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)
|
||||
|
|
|
@ -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 }
|
||||
|
||||
.dropdown.inline
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light
|
||||
= projects_sort_options_hash[@sort]
|
||||
= icon('caret-down')
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
= link_to filter_branches_path(sort: sort_value_name) do
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
.title Stage
|
||||
%button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.stage-selection More
|
||||
= icon('caret-down')
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu
|
||||
- @build.pipeline.stages.each do |stage|
|
||||
%li
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
= icon('comment')
|
||||
\
|
||||
- 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,
|
||||
blob: blob, link_opts: link_opts)
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
%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) }<
|
||||
- if email
|
||||
%pre= diff_line_content(line.text)
|
||||
%pre= line.text
|
||||
- else
|
||||
= diff_line_content(line.text)
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
|
||||
|
||||
.dropdown
|
||||
%button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light sort:
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- else
|
||||
= sort_title_recently_created
|
||||
= icon('caret-down')
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
- excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue