Commit f071e3d3 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into fix-git-hooks-when-creating-file

* upstream/master: (116 commits)
  adds impersonator variable and makes sudo usage overall more clear
  Reenables /user API request to return private-token if user is admin and requested with sudo
  Fix Backup::Manager#remove_old
  Allow public access to some Tag API endpoints
  Update outdated visible content spec descriptions
  Grapify the issues API
  new DevOps report, 404s, typos
  Remove dashboard.scss
  Update custom_hooks.md for global custom hooks and chained hook info
  Move admin hooks spinach to rspec
  Move admin logs spinach test to rspec
  Fix 404 error when visit group label edit page
  A simpler implementation of finding a merge request
  Encourage bug reporters to mention if they use GitLab.com [ci skip]
  Bump gitlab-shell version to 4.0.3
  Remove confirmation.scss
  Explain "js: true" in "deleted_source_branch_spec.rb" [ci skip]
  Move award emojis to framwork
  Move image styles to framework
  Remove tags.scss
  ...
parents 9c6563f6 50a78448
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3-git-2.7-phantomjs-2.1" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-git-2.7-phantomjs-2.1-node-7.1"
cache: cache:
key: "ruby-231" key: "ruby-233"
paths: paths:
- vendor/ruby - vendor/ruby
...@@ -30,7 +30,12 @@ stages: ...@@ -30,7 +30,12 @@ stages:
- post-test - post-test
- pages - pages
# Prepare and merge knapsack tests # Predefined scopes
.dedicated-runner: &dedicated-runner
tags:
- gitlab-org
- 2gb
.knapsack-state: &knapsack-state .knapsack-state: &knapsack-state
services: [] services: []
variables: variables:
...@@ -45,47 +50,14 @@ stages: ...@@ -45,47 +50,14 @@ stages:
paths: paths:
- knapsack/ - knapsack/
knapsack:
<<: *knapsack-state
stage: prepare
script:
- mkdir -p knapsack/
- '[[ -f knapsack/rspec_report.json ]] || echo "{}" > knapsack/rspec_report.json'
- '[[ -f knapsack/spinach_report.json ]] || echo "{}" > knapsack/spinach_report.json'
update-knapsack:
<<: *knapsack-state
stage: post-test
script:
- scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
.use-db: &use-db .use-db: &use-db
services: services:
- mysql:latest - mysql:latest
- redis:alpine - redis:alpine
setup-test-env:
<<: *use-db
stage: prepare
script:
- bundle exec rake assets:precompile 2>/dev/null
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts:
expire_in: 7d
paths:
- public/assets
- tmp/tests
.rspec-knapsack: &rspec-knapsack .rspec-knapsack: &rspec-knapsack
stage: test stage: test
<<: *dedicated-runner
<<: *use-db <<: *use-db
script: script:
- JOB_NAME=( $CI_BUILD_NAME ) - JOB_NAME=( $CI_BUILD_NAME )
...@@ -103,6 +75,7 @@ setup-test-env: ...@@ -103,6 +75,7 @@ setup-test-env:
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
stage: test stage: test
<<: *dedicated-runner
<<: *use-db <<: *use-db
script: script:
- JOB_NAME=( $CI_BUILD_NAME ) - JOB_NAME=( $CI_BUILD_NAME )
...@@ -118,6 +91,44 @@ setup-test-env: ...@@ -118,6 +91,44 @@ setup-test-env:
- knapsack/ - knapsack/
- coverage/ - coverage/
# Prepare and merge knapsack tests
knapsack:
<<: *knapsack-state
<<: *dedicated-runner
stage: prepare
script:
- mkdir -p knapsack/
- '[[ -f knapsack/rspec_report.json ]] || echo "{}" > knapsack/rspec_report.json'
- '[[ -f knapsack/spinach_report.json ]] || echo "{}" > knapsack/spinach_report.json'
setup-test-env:
<<: *use-db
<<: *dedicated-runner
stage: prepare
script:
- bundle exec rake assets:precompile 2>/dev/null
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts:
expire_in: 7d
paths:
- public/assets
- tmp/tests
update-knapsack:
<<: *knapsack-state
<<: *dedicated-runner
stage: post-test
script:
- scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
rspec 0 20: *rspec-knapsack rspec 0 20: *rspec-knapsack
rspec 1 20: *rspec-knapsack rspec 1 20: *rspec-knapsack
rspec 2 20: *rspec-knapsack rspec 2 20: *rspec-knapsack
...@@ -166,10 +177,12 @@ spinach 9 10: *spinach-knapsack ...@@ -166,10 +177,12 @@ spinach 9 10: *spinach-knapsack
.rspec-knapsack-ruby21: &rspec-knapsack-ruby21 .rspec-knapsack-ruby21: &rspec-knapsack-ruby21
<<: *rspec-knapsack <<: *rspec-knapsack
<<: *dedicated-runner
<<: *ruby-21 <<: *ruby-21
.spinach-knapsack-ruby21: &spinach-knapsack-ruby21 .spinach-knapsack-ruby21: &spinach-knapsack-ruby21
<<: *spinach-knapsack <<: *spinach-knapsack
<<: *dedicated-runner
<<: *ruby-21 <<: *ruby-21
rspec 0 20 ruby21: *rspec-knapsack-ruby21 rspec 0 20 ruby21: *rspec-knapsack-ruby21
...@@ -214,6 +227,7 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21 ...@@ -214,6 +227,7 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21
.exec: &exec .exec: &exec
<<: *ruby-static-analysis <<: *ruby-static-analysis
<<: *dedicated-runner
stage: test stage: test
script: script:
- bundle exec $CI_BUILD_NAME - bundle exec $CI_BUILD_NAME
...@@ -235,7 +249,7 @@ rake ee_compat_check: ...@@ -235,7 +249,7 @@ rake ee_compat_check:
- /^[\d-]+-stable(-ee)?$/ - /^[\d-]+-stable(-ee)?$/
allow_failure: yes allow_failure: yes
cache: cache:
key: "ruby231-ee_compat_check_repo" key: "ruby233-ee_compat_check_repo"
paths: paths:
- ee_compat_check/repo/ - ee_compat_check/repo/
- vendor/ruby - vendor/ruby
...@@ -249,12 +263,14 @@ rake ee_compat_check: ...@@ -249,12 +263,14 @@ rake ee_compat_check:
rake db:migrate:reset: rake db:migrate:reset:
stage: test stage: test
<<: *use-db <<: *use-db
<<: *dedicated-runner
script: script:
- rake db:migrate:reset - rake db:migrate:reset
rake db:seed_fu: rake db:seed_fu:
stage: test stage: test
<<: *use-db <<: *use-db
<<: *dedicated-runner
variables: variables:
SIZE: "1" SIZE: "1"
SETUP_DB: "false" SETUP_DB: "false"
...@@ -276,9 +292,8 @@ teaspoon: ...@@ -276,9 +292,8 @@ teaspoon:
- node_modules/ - node_modules/
stage: test stage: test
<<: *use-db <<: *use-db
<<: *dedicated-runner
script: script:
- curl --silent --location https://deb.nodesource.com/setup_6.x | bash -
- apt-get install --assume-yes nodejs
- npm install - npm install
- npm link istanbul - npm link istanbul
- rake teaspoon - rake teaspoon
...@@ -290,6 +305,7 @@ teaspoon: ...@@ -290,6 +305,7 @@ teaspoon:
lint-doc: lint-doc:
stage: test stage: test
<<: *dedicated-runner
image: "phusion/baseimage:latest" image: "phusion/baseimage:latest"
before_script: [] before_script: []
script: script:
...@@ -297,6 +313,7 @@ lint-doc: ...@@ -297,6 +313,7 @@ lint-doc:
bundler:check: bundler:check:
stage: test stage: test
<<: *dedicated-runner
<<: *ruby-static-analysis <<: *ruby-static-analysis
script: script:
- bundle check - bundle check
...@@ -304,6 +321,7 @@ bundler:check: ...@@ -304,6 +321,7 @@ bundler:check:
bundler:audit: bundler:audit:
stage: test stage: test
<<: *ruby-static-analysis <<: *ruby-static-analysis
<<: *dedicated-runner
only: only:
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee - master@gitlab-org/gitlab-ee
...@@ -315,6 +333,7 @@ bundler:audit: ...@@ -315,6 +333,7 @@ bundler:audit:
migration paths: migration paths:
stage: test stage: test
<<: *use-db <<: *use-db
<<: *dedicated-runner
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
only: only:
...@@ -336,6 +355,7 @@ migration paths: ...@@ -336,6 +355,7 @@ migration paths:
coverage: coverage:
stage: post-test stage: post-test
services: [] services: []
<<: *dedicated-runner
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "true" USE_BUNDLE_INSTALL: "true"
...@@ -349,6 +369,7 @@ coverage: ...@@ -349,6 +369,7 @@ coverage:
- coverage/assets/ - coverage/assets/
lint:javascript: lint:javascript:
<<: *dedicated-runner
cache: cache:
paths: paths:
- node_modules/ - node_modules/
...@@ -360,6 +381,7 @@ lint:javascript: ...@@ -360,6 +381,7 @@ lint:javascript:
- npm --silent run eslint - npm --silent run eslint
lint:javascript:report: lint:javascript:report:
<<: *dedicated-runner
cache: cache:
paths: paths:
- node_modules/ - node_modules/
...@@ -381,6 +403,7 @@ lint:javascript:report: ...@@ -381,6 +403,7 @@ lint:javascript:report:
trigger_docs: trigger_docs:
stage: post-test stage: post-test
image: "alpine" image: "alpine"
<<: *dedicated-runner
before_script: before_script:
- apk update && apk add curl - apk update && apk add curl
variables: variables:
...@@ -396,6 +419,7 @@ trigger_docs: ...@@ -396,6 +419,7 @@ trigger_docs:
notify:slack: notify:slack:
stage: post-test stage: post-test
<<: *dedicated-runner
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
...@@ -411,6 +435,7 @@ notify:slack: ...@@ -411,6 +435,7 @@ notify:slack:
pages: pages:
before_script: [] before_script: []
stage: pages stage: pages
<<: *dedicated-runner
dependencies: dependencies:
- coverage - coverage
- teaspoon - teaspoon
...@@ -425,11 +450,12 @@ pages: ...@@ -425,11 +450,12 @@ pages:
paths: paths:
- public - public
only: only:
- master - master@gitlab-org/gitlab-ce
# Insurance in case a gem needed by one of our releases gets yanked from # Insurance in case a gem needed by one of our releases gets yanked from
# rubygems.org in the future. # rubygems.org in the future.
cache gems: cache gems:
<<: *dedicated-runner
only: only:
- tags - tags
variables: variables:
...@@ -439,3 +465,5 @@ cache gems: ...@@ -439,3 +465,5 @@ cache gems:
artifacts: artifacts:
paths: paths:
- vendor/cache - vendor/cache
only:
- master@gitlab-org/gitlab-ce
...@@ -21,6 +21,8 @@ logs, and code as it's very hard to read otherwise.) ...@@ -21,6 +21,8 @@ logs, and code as it's very hard to read otherwise.)
### Output of checks ### Output of checks
(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com)
#### Results of GitLab application Check #### Results of GitLab application Check
(For installations with omnibus-gitlab package run and paste the output of: (For installations with omnibus-gitlab package run and paste the output of:
......
...@@ -2,6 +2,15 @@ ...@@ -2,6 +2,15 @@
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.3 (2016-12-02)
- Pass commit data to ProcessCommitWorker to reduce Git overhead. !7744
- Speed up issuable dashboards.
- Don't change relative URLs to absolute URLs in the Help page.
- Fixes "ActionView::Template::Error: undefined method `text?` for nil:NilClass" on MR pages.
- Fix branch validation for GitHub PR where repo/fork was renamed/deleted.
- Validate state param when filtering issuables.
## 8.14.2 (2016-12-01) ## 8.14.2 (2016-12-01)
- Remove caching of events data. !6578 - Remove caching of events data. !6578
...@@ -242,6 +251,11 @@ entry. ...@@ -242,6 +251,11 @@ 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.8 (2016-12-02)
- Pass tag SHA to post-receive hook when tag is created via UI. !7700
- Validate state param when filtering issuables.
## 8.13.7 (2016-11-28) ## 8.13.7 (2016-11-28)
- fixes 500 error on project show when user is not logged in and project is still empty. !7376 - fixes 500 error on project show when user is not logged in and project is still empty. !7376
......
...@@ -264,7 +264,7 @@ group :development do ...@@ -264,7 +264,7 @@ group :development do
end end
group :development, :test do group :development, :test do
gem 'byebug', '~> 8.2.1', platform: :mri gem 'pry-byebug', '~> 3.4.1', platform: :mri
gem 'pry-rails', '~> 0.3.4' gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false gem 'awesome_print', '~> 1.2.0', require: false
...@@ -338,7 +338,7 @@ gem 'ruby-prof', '~> 0.16.2' ...@@ -338,7 +338,7 @@ gem 'ruby-prof', '~> 0.16.2'
gem 'oauth2', '~> 1.2.0' gem 'oauth2', '~> 1.2.0'
# Soft deletion # Soft deletion
gem 'paranoia', '~> 2.0' gem 'paranoia', '~> 2.2'
# Health check # Health check
gem 'health_check', '~> 2.2.0' gem 'health_check', '~> 2.2.0'
......
...@@ -91,7 +91,7 @@ GEM ...@@ -91,7 +91,7 @@ GEM
bundler-audit (0.5.0) bundler-audit (0.5.0)
bundler (~> 1.2) bundler (~> 1.2)
thor (~> 0.18) thor (~> 0.18)
byebug (8.2.1) byebug (9.0.6)
capybara (2.6.2) capybara (2.6.2)
addressable addressable
mime-types (>= 1.16) mime-types (>= 1.16)
...@@ -460,8 +460,8 @@ GEM ...@@ -460,8 +460,8 @@ GEM
org-ruby (0.9.12) org-ruby (0.9.12)
rubypants (~> 0.2) rubypants (~> 0.2)
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.1.4) paranoia (2.2.0)
activerecord (~> 4.0) activerecord (>= 4.0, < 5.1)
parser (2.3.1.4) parser (2.3.1.4)
ast (~> 2.2) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
...@@ -483,6 +483,9 @@ GEM ...@@ -483,6 +483,9 @@ GEM
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
slop (~> 3.4) slop (~> 3.4)
pry-byebug (3.4.1)
byebug (~> 9.0)
pry (~> 0.10)
pry-rails (0.3.4) pry-rails (0.3.4)
pry (>= 0.9.10) pry (>= 0.9.10)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
...@@ -796,7 +799,6 @@ DEPENDENCIES ...@@ -796,7 +799,6 @@ DEPENDENCIES
browser (~> 2.2) browser (~> 2.2)
bullet (~> 5.2.0) bullet (~> 5.2.0)
bundler-audit (~> 0.5.0) bundler-audit (~> 0.5.0)
byebug (~> 8.2.1)
capybara (~> 2.6.2) capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave (~> 0.10.0) carrierwave (~> 0.10.0)
...@@ -887,10 +889,11 @@ DEPENDENCIES ...@@ -887,10 +889,11 @@ DEPENDENCIES
omniauth-twitter (~> 1.2.0) omniauth-twitter (~> 1.2.0)
omniauth_crowd (~> 2.2.0) omniauth_crowd (~> 2.2.0)
org-ruby (~> 0.9.12) org-ruby (~> 0.9.12)
paranoia (~> 2.0) paranoia (~> 2.2)
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.9.0) poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.0) premailer-rails (~> 1.9.0)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1) rack-attack (~> 4.4.1)
rack-cors (~> 0.4.0) rack-cors (~> 0.4.0)
......
...@@ -76,7 +76,7 @@ GitLab is a Ruby on Rails application that runs on the following software: ...@@ -76,7 +76,7 @@ GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL - Ubuntu/Debian/CentOS/RHEL
- Ruby (MRI) 2.3 - Ruby (MRI) 2.3
- Git 2.7.4+ - Git 2.8.4+
- Redis 2.8+ - Redis 2.8+
- MySQL or PostgreSQL - MySQL or PostgreSQL
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, max-len, one-var, camelcase, one-var-declaration-per-line, no-unused-vars, no-unused-expressions, no-sequences, object-shorthand, comma-dangle, prefer-arrow-callback, semi, radix, padded-blocks, max-len */
(function() {
this.Diff = (function() {
var UNFOLD_COUNT;
UNFOLD_COUNT = 20;
function Diff() {
$('.files .diff-file').singleFileDiff();
this.filesCommentButton = $('.files .diff-file').filesCommentButton();
if (this.diffViewType() === 'parallel') {
$('.content-wrapper .container-fluid').removeClass('container-limited');
}
$(document).off('click', '.js-unfold');
$(document).on('click', '.js-unfold', (function(_this) {
return function(event) {
var line_number, link, file, offset, old_line, params, prev_new_line, prev_old_line, ref, ref1, since, target, to, unfold, unfoldBottom;
target = $(event.target);
unfoldBottom = target.hasClass('js-unfold-bottom');
unfold = true;
ref = _this.lineNumbers(target.parent()), old_line = ref[0], line_number = ref[1];
offset = line_number - old_line;
if (unfoldBottom) {
line_number += 1;
since = line_number;
to = line_number + UNFOLD_COUNT;
} else {
ref1 = _this.lineNumbers(target.parent().prev()), prev_old_line = ref1[0], prev_new_line = ref1[1];
line_number -= 1;
to = line_number;
if (line_number - UNFOLD_COUNT > prev_new_line + 1) {
since = line_number - UNFOLD_COUNT;
} else {
since = prev_new_line + 1;
unfold = false;
}
}
file = target.parents('.diff-file');
link = file.data('blob-diff-path');
params = {
since: since,
to: to,
bottom: unfoldBottom,
offset: offset,
unfold: unfold,
view: file.data('view')
};
return $.get(link, params, function(response) {
return target.parent().replaceWith(response);
});
};
})(this));
}
Diff.prototype.diffViewType = function() {
return $('.inline-parallel-buttons a.active').data('view-type');
}
Diff.prototype.lineNumbers = function(line) {
if (!line.children().length) {
return [0, 0];
}
return line.find('.diff-line-num').map(function() {
return parseInt($(this).data('linenumber'));
});
};
return Diff;
})();
}).call(this);
/* eslint-disable class-methods-use-this */
(() => {
const UNFOLD_COUNT = 20;
class Diff {
constructor() {
$('.files .diff-file').singleFileDiff();
$('.files .diff-file').filesCommentButton();
if (this.diffViewType() === 'parallel') {
$('.content-wrapper .container-fluid').removeClass('container-limited');
}
$(document)
.off('click', '.js-unfold, .diff-line-num a')
.on('click', '.js-unfold', this.handleClickUnfold.bind(this))
.on('click', '.diff-line-num a', this.handleClickLineNum.bind(this));
this.highlighSelectedLine();
}
handleClickUnfold(e) {
const $target = $(e.target);
// current babel config relies on iterators implementation, so we cannot simply do:
// const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent());
const ref = this.lineNumbers($target.parent());
const oldLineNumber = ref[0];
const newLineNumber = ref[1];
const offset = newLineNumber - oldLineNumber;
const bottom = $target.hasClass('js-unfold-bottom');
let since;
let to;
let unfold = true;
if (bottom) {
const lineNumber = newLineNumber + 1;
since = lineNumber;
to = lineNumber + UNFOLD_COUNT;
} else {
const lineNumber = newLineNumber - 1;
since = lineNumber - UNFOLD_COUNT;
to = lineNumber;
// make sure we aren't loading more than we need
const prevNewLine = this.lineNumbers($target.parent().prev())[1];
if (since <= prevNewLine + 1) {
since = prevNewLine + 1;
unfold = false;
}
}
const file = $target.parents('.diff-file');
const link = file.data('blob-diff-path');
const view = file.data('view');
const params = { since, to, bottom, offset, unfold, view };
$.get(link, params, response => $target.parent().replaceWith(response));
}
openAnchoredDiff(anchoredDiff, cb) {
const diffTitle = $(`#file-path-${anchoredDiff}`);
const diffFile = diffTitle.closest('.diff-file');
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
if (nothingHereBlock.length) {
diffFile.singleFileDiff(true, cb);
} else {
cb();
}
}
handleClickLineNum(e) {
const hash = $(e.currentTarget).attr('href');
e.preventDefault();
if (window.history.pushState) {
window.history.pushState(null, null, hash);
} else {
window.location.hash = hash;
}
this.highlighSelectedLine();
}
diffViewType() {
return $('.inline-parallel-buttons a.active').data('view-type');
}
lineNumbers(line) {
if (!line.children().length) {
return [0, 0];
}
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10));
}
highlighSelectedLine() {
const $diffFiles = $('.diff-file');
$diffFiles.find('.hll').removeClass('hll');
if (window.location.hash !== '') {
const hash = window.location.hash.replace('#', '');
$diffFiles
.find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`)
.addClass('hll');
}
}
}
window.gl = window.gl || {};
window.gl.Diff = Diff;
})();
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
new ZenMode(); new ZenMode();
break; break;
case 'projects:compare:show': case 'projects:compare:show':
new Diff(); new gl.Diff();
break; break;
case 'projects:issues:new': case 'projects:issues:new':
case 'projects:issues:edit': case 'projects:issues:edit':
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
break; break;
case 'projects:merge_requests:new': case 'projects:merge_requests:new':
case 'projects:merge_requests:edit': case 'projects:merge_requests:edit':
new Diff(); new gl.Diff();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new GLForm($('.merge-request-form')); new GLForm($('.merge-request-form'));
new IssuableForm($('.merge-request-form')); new IssuableForm($('.merge-request-form'));
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
new GLForm($('.release-form')); new GLForm($('.release-form'));
break; break;
case 'projects:merge_requests:show': case 'projects:merge_requests:show':
new Diff(); new gl.Diff();
shortcut_handler = new ShortcutsIssuable(true); shortcut_handler = new ShortcutsIssuable(true);
new ZenMode(); new ZenMode();
new MergedButtons(); new MergedButtons();
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
new MergedButtons(); new MergedButtons();
break; break;
case "projects:merge_requests:diffs": case "projects:merge_requests:diffs":
new Diff(); new gl.Diff();
new ZenMode(); new ZenMode();
new MergedButtons(); new MergedButtons();
break; break;
...@@ -117,7 +117,7 @@ ...@@ -117,7 +117,7 @@
break; break;
case 'projects:commit:show': case 'projects:commit:show':
new Commit(); new Commit();
new Diff(); new gl.Diff();
new ZenMode(); new ZenMode();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
break; break;
......
...@@ -97,6 +97,19 @@ ...@@ -97,6 +97,19 @@
return $('body').data('page').split(':')[0]; return $('body').data('page').split(':')[0];
}; };
gl.utils.parseUrl = function (url) {
var parser = document.createElement('a');
parser.href = url;
return parser;
};
gl.utils.parseUrlPathname = function (url) {
var parsedUrl = gl.utils.parseUrl(url);
// parsedUrl.pathname will return an absolute path for Firefox and a relative path for IE11
// We have to make sure we always have an absolute path.
return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : '/' + parsedUrl.pathname;
};
gl.utils.isMetaKey = function(e) { gl.utils.isMetaKey = function(e) {
return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey; return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
}; };
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
if (window.mrTabs) { if (window.mrTabs) {
window.mrTabs.unbindEvents(); window.mrTabs.unbindEvents();
} }
window.mrTabs = new MergeRequestTabs(this.opts); window.mrTabs = new gl.MergeRequestTabs(this.opts);
}; };
MergeRequest.prototype.showAllCommits = function() { MergeRequest.prototype.showAllCommits = function() {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>'; COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
function SingleFileDiff(file, forceLoad, cb) { function SingleFileDiff(file, forceLoad, cb) {
var clickTarget;
this.file = file; this.file = file;
this.toggleDiff = bind(this.toggleDiff, this); this.toggleDiff = bind(this.toggleDiff, this);
this.content = $('.diff-content', this.file); this.content = $('.diff-content', this.file);
...@@ -31,9 +32,9 @@ ...@@ -31,9 +32,9 @@
this.content.after(this.collapsedContent); this.content.after(this.collapsedContent);
this.$toggleIcon.addClass('fa-caret-down'); this.$toggleIcon.addClass('fa-caret-down');
} }
$('.file-title, .click-to-expand', this.file).on('click', this.toggleDiff); clickTarget = $('.file-title, .click-to-expand', this.file).on('click', this.toggleDiff);
if (forceLoad) { if (forceLoad) {
this.toggleDiff(null, cb); this.toggleDiff({ target: clickTarget }, cb);
} }
} }
......
...@@ -40,3 +40,6 @@ ...@@ -40,3 +40,6 @@
@import "framework/blank"; @import "framework/blank";
@import "framework/wells.scss"; @import "framework/wells.scss";
@import "framework/page-header.scss"; @import "framework/page-header.scss";
@import "framework/awards.scss";
@import "framework/images.scss";
@import "framework/broadcast-messages";
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
.award-control-icon { .award-control-icon {
float: left; float: left;
margin-right: 5px; margin-right: 5px;
font-size: 19px; font-size: 18px;
} }
.award-control-icon-loading { .award-control-icon-loading {
......
...@@ -32,14 +32,14 @@ ...@@ -32,14 +32,14 @@
.blank-state-title { .blank-state-title {
margin-top: 0; margin-top: 0;
margin-bottom: 5px; margin-bottom: 5px;
font-size: 19px; font-size: 18px;
font-weight: normal; font-weight: normal;
} }
.blank-state-text { .blank-state-text {
margin-top: 0; margin-top: 0;
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
font-size: 15px; font-size: 14px;
> strong { > strong {
font-weight: 600; font-weight: 600;
......
.light-well {
background-color: $background-color;
padding: 15px;
}
.centered-light-block { .centered-light-block {
text-align: center; text-align: center;
color: $gl-gray; color: $gl-gray;
...@@ -274,6 +269,10 @@ ...@@ -274,6 +269,10 @@
} }
} }
.emoji-icon {
display: inline-block;
}
@media(max-width: $screen-xs-max) { @media(max-width: $screen-xs-max) {
margin-top: 50px; margin-top: 50px;
text-align: center; text-align: center;
......
.broadcast-message {
@extend .alert-warning;
padding: 10px;
text-align: center;
div,
p {
display: inline;
margin: 0;
a {
color: inherit;
text-decoration: underline;
}
}
}
.broadcast-message-preview {
@extend .broadcast-message;
margin-bottom: 20px;
}
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
.slead { .slead {
color: $common-gray; color: $common-gray;
font-size: 15px; font-size: 14px;
margin-bottom: 12px; margin-bottom: 12px;
font-weight: normal; font-weight: normal;
line-height: 24px; line-height: 24px;
...@@ -379,7 +379,9 @@ table { ...@@ -379,7 +379,9 @@ table {
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
} }
.hide-bottom-border { border-bottom: none !important; } .hide-bottom-border {
border-bottom: none !important;
}
.gl-accessibility { .gl-accessibility {
&:focus { &:focus {
...@@ -396,3 +398,13 @@ table { ...@@ -396,3 +398,13 @@ table {
z-index: 1; z-index: 1;
} }
} }
.str-truncated {
&-60 {
@include str-truncated(60%);
}
&-100 {
@include str-truncated(100%);
}
}
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
padding: 6px 8px 6px 10px; padding: 6px 8px 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: 14px;
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;
...@@ -123,7 +123,7 @@ ...@@ -123,7 +123,7 @@
width: 240px; width: 240px;
margin-top: 2px; margin-top: 2px;
margin-bottom: 0; margin-bottom: 0;
font-size: 15px; font-size: 14px;
font-weight: normal; font-weight: normal;
padding: 8px 0; padding: 8px 0;
background-color: $dropdown-bg; background-color: $dropdown-bg;
...@@ -589,7 +589,7 @@ ...@@ -589,7 +589,7 @@
.ui-datepicker-title { .ui-datepicker-title {
color: $gl-gray; color: $gl-gray;
font-size: 15px; font-size: 14px;
line-height: 1; line-height: 1;
font-weight: normal; font-weight: normal;
} }
......
...@@ -71,7 +71,7 @@ header { ...@@ -71,7 +71,7 @@ header {
} }
.fa-caret-down { .fa-caret-down {
font-size: 15px; font-size: 14px;
} }
} }
...@@ -156,7 +156,7 @@ header { ...@@ -156,7 +156,7 @@ header {
position: relative; position: relative;
padding-right: 20px; padding-right: 20px;
margin: 0; margin: 0;
font-size: 19px; font-size: 18px;
max-width: 385px; max-width: 385px;
display: inline-block; display: inline-block;
line-height: $header-height; line-height: $header-height;
......
...@@ -106,13 +106,13 @@ ul.task-list { ...@@ -106,13 +106,13 @@ ul.task-list {
} }
} }
// Generic content list
ul.content-list { ul.content-list {
@include basic-list; @include basic-list;
margin: 0; margin: 0;
padding: 0; padding: 0;
> li { li {
border-color: $table-border-color; border-color: $table-border-color;
font-size: $list-font-size; font-size: $list-font-size;
color: $list-text-color; color: $list-text-color;
...@@ -193,6 +193,41 @@ ul.content-list { ...@@ -193,6 +193,41 @@ ul.content-list {
} }
} }
// Content list using flexbox
.flex-list {
.flex-row {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
white-space: nowrap;
}
.row-main-content {
flex: 1 1 auto;
overflow: hidden;
padding-right: 8px;
}
.row-title {
font-weight: 600;
}
.row-second-line {
display: block;
}
.dropdown {
.btn-block {
margin-bottom: 0;
line-height: inherit;
}
}
.label-default {
color: $btn-transparent-color;
}
}
.panel > .content-list > li { .panel > .content-list > li {
padding: $gl-padding-top $gl-padding; padding: $gl-padding-top $gl-padding;
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
padding: $gl-btn-padding; padding: $gl-btn-padding;
padding-bottom: 11px; padding-bottom: 11px;
margin-bottom: -1px; margin-bottom: -1px;
font-size: 15px; font-size: 14px;
line-height: 28px; line-height: 28px;
color: $note-toolbar-color; color: $note-toolbar-color;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
...@@ -268,6 +268,16 @@ ...@@ -268,6 +268,16 @@
width: auto; width: auto;
} }
} }
&.multi-line {
.nav-text {
line-height: 20px;
}
.nav-controls {
padding: 17px 0;
}
}
} }
.layout-nav { .layout-nav {
......
...@@ -34,6 +34,10 @@ table { ...@@ -34,6 +34,10 @@ table {
background-color: $background-color; background-color: $background-color;
font-weight: normal; font-weight: normal;
border-bottom: none; border-bottom: none;
&.wide {
width: 55%;
}
} }
td { td {
...@@ -42,3 +46,16 @@ table { ...@@ -42,3 +46,16 @@ table {
} }
} }
} }
.responsive-table {
@media (max-width: $screen-sm-max) {
th {
width: 100%;
}
td {
width: 100%;
float: left;
}
}
}
...@@ -102,7 +102,7 @@ $well-light-text-color: #5b6169; ...@@ -102,7 +102,7 @@ $well-light-text-color: #5b6169;
/* /*
* Text * Text
*/ */
$gl-font-size: 15px; $gl-font-size: 14px;
$gl-title-color: #333; $gl-title-color: #333;
$gl-text-color: #5c5c5c; $gl-text-color: #5c5c5c;
$gl-text-color-dark: #5c5d5e; $gl-text-color-dark: #5c5d5e;
...@@ -380,7 +380,7 @@ $ci-skipped-color: #888; ...@@ -380,7 +380,7 @@ $ci-skipped-color: #888;
/* /*
* Boards * Boards
*/ */
$issue-boards-font-size: 15px; $issue-boards-font-size: 14px;
$issue-boards-card-shadow: rgba(186, 186, 186, 0.5); $issue-boards-card-shadow: rgba(186, 186, 186, 0.5);
/* /*
...@@ -427,12 +427,6 @@ $common-gray-dark: #444; ...@@ -427,12 +427,6 @@ $common-gray-dark: #444;
$common-red: $gl-text-red; $common-red: $gl-text-red;
$common-green: $gl-text-green; $common-green: $gl-text-green;
/*
* Dashboard
*/
$dashboard-project-access-icon-color: #888;
/* /*
* Editor * Editor
*/ */
......
...@@ -43,3 +43,16 @@ ...@@ -43,3 +43,16 @@
background-color: $well-expand-item; background-color: $well-expand-item;
} }
} }
.light-well {
background-color: $background-color;
padding: 15px;
}
.well-centered {
h1 {
font-weight: normal;
text-align: center;
font-size: 48px;
}
}
/**
* Admin area
*
*/
.admin-dashboard {
.data {
a {
h1 {
line-height: 48px;
font-size: 48px;
padding: 20px;
text-align: center;
font-weight: normal;
}
}
}
.str-truncated {
max-width: 60%;
}
}
.admin-filter form {
.select2-container {
width: 100%;
}
.controls {
margin-left: 130px;
}
.form-actions {
padding-left: 130px;
background: $white-light;
}
.visibility-levels {
.controls {
margin-bottom: 9px;
}
i {
color: inherit;
}
}
}
.broadcast-messages {
.message {
line-height: 2;
}
}
.broadcast-message {
@extend .alert-warning;
padding: 10px;
text-align: center;
> div,
p {
display: inline;
margin: 0;
a {
color: inherit;
text-decoration: underline;
}
}
}
.broadcast-message-preview {
@extend .broadcast-message;
margin-bottom: 20px;
}
// Users List
.users-list {
.user-row {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
white-space: nowrap;
}
.user-details {
flex: 1 1 auto;
overflow: hidden;
padding-right: 8px;
}
.user-name {
display: inline-block;
font-weight: 600;
}
.user-name,
.user-email {
overflow: hidden;
text-overflow: ellipsis;
}
.dropdown {
.btn-block {
margin-bottom: 0;
line-height: inherit;
}
}
.label-default {
color: $btn-transparent-color;
}
}
.abuse-reports {
.table {
table-layout: fixed;
}
.subheading {
padding-bottom: $gl-padding;
}
.message {
word-wrap: break-word;
}
.btn {
white-space: normal;
padding: $gl-btn-padding;
}
th {
width: 15%;
&.wide {
width: 55%;
}
}
@media (max-width: $screen-sm-max) {
th {
width: 100%;
}
td {
width: 100%;
float: left;
}
}
.no-reports {
.emoji-icon {
margin-left: $btn-side-margin;
margin-top: 3px;
}
span {
font-size: 19px;
}
}
}
.admin-builds-table {
.ci-table td:last-child {
min-width: 120px;
}
}
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
@media (max-width: $screen-sm-max) { @media (max-width: $screen-sm-max) {
padding-right: 40px; padding-right: 40px;
margin-top: 6px;
.btn-inverted { .btn-inverted {
display: none; display: none;
......
.well-confirmation {
margin-bottom: 20px;
border-bottom: 1px solid $gray-darker;
> h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 400;
}
.lead {
margin-bottom: 20px;
}
ul,
ol {
padding-left: 0;
}
li {
list-style-type: none;
}
}
.confirmation-content {
a {
color: $md-link-color;
}
}
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
&.title { &.title {
line-height: 19px; line-height: 19px;
font-size: 15px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: $gl-title-color; color: $gl-title-color;
} }
......
.dashboard {
.side {
.panel {
.panel-heading {
background: $background-color;
border-top-left-radius: 0;
}
border-top-left-radius: 0;
}
}
}
.dashboard-search-filter {
padding: 5px;
.search-text-input {
float: left;
@extend .col-md-2;
}
.btn {
margin-left: 5px;
float: left;
}
}
.project-access-icon {
margin-left: 10px;
float: left;
margin-right: 15px;
margin-bottom: 15px;
i {
color: $dashboard-project-access-icon-color;
}
}
.dash-project-access-icon {
float: left;
margin-right: 5px;
width: 16px;
}
.error-page {
max-width: 400px;
margin: 0 auto;
h1,
h2,
h3 {
text-align: center;
}
h1 {
font-size: 56px;
line-height: 100px;
font-weight: 300;
}
}
.ci-body { .ci-body {
.incorrect-syntax { .incorrect-syntax {
font-size: 19px; font-size: 18px;
color: $lint-incorrect-color; color: $lint-incorrect-color;
} }
.correct-syntax { .correct-syntax {
font-size: 19px; font-size: 18px;
color: $lint-correct-color; color: $lint-correct-color;
} }
} }
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
h4 { h4 {
font-size: 15px; font-size: 14px;
} }
p { p {
...@@ -129,6 +129,11 @@ ...@@ -129,6 +129,11 @@
margin-bottom: 4px; margin-bottom: 4px;
} }
.btn-grouped {
float: none;
margin-right: 0;
}
.accept-action { .accept-action {
width: 100%; width: 100%;
text-align: center; text-align: center;
......
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
.note-edit-form { .note-edit-form {
display: none; display: none;
font-size: 15px; font-size: 14px;
.md-area { .md-area {
background-color: $white-light; background-color: $white-light;
......
...@@ -280,6 +280,12 @@ ...@@ -280,6 +280,12 @@
} }
} }
.admin-builds-table {
.ci-table td:last-child {
min-width: 120px;
}
}
// Pipeline visualization // Pipeline visualization
.toggle-pipeline-btn { .toggle-pipeline-btn {
......
...@@ -521,7 +521,7 @@ a.deploy-project-label { ...@@ -521,7 +521,7 @@ a.deploy-project-label {
.nav > li > a { .nav > li > a {
padding: 0; padding: 0;
background-color: transparent; background-color: transparent;
font-size: 15px; font-size: 14px;
line-height: 29px; line-height: 29px;
color: $notes-light-color; color: $notes-light-color;
......
.tag-buttons {
line-height: 40px;
.btn:not(.dropdown-toggle) {
margin-left: 10px;
}
}
.votes-inline {
display: inline-block;
margin: 0 8px;
}
...@@ -6,7 +6,12 @@ module MergeRequestsAction ...@@ -6,7 +6,12 @@ module MergeRequestsAction
@label = merge_requests_finder.labels.first @label = merge_requests_finder.labels.first
@merge_requests = merge_requests_collection @merge_requests = merge_requests_collection
.non_archived
.page(params[:page]) .page(params[:page])
end end
private
def filter_params
super.merge(non_archived: true)
end
end end
...@@ -5,9 +5,7 @@ class Projects::DiscussionsController < Projects::ApplicationController ...@@ -5,9 +5,7 @@ class Projects::DiscussionsController < Projects::ApplicationController
before_action :authorize_resolve_discussion! before_action :authorize_resolve_discussion!
def resolve def resolve
discussion.resolve!(current_user) Discussions::ResolveService.new(project, current_user, merge_request: merge_request).execute(discussion)
MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(merge_request)
render json: { render json: {
resolved_by: discussion.resolved_by.try(:name), resolved_by: discussion.resolved_by.try(:name),
......
...@@ -46,8 +46,9 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -46,8 +46,9 @@ class Projects::IssuesController < Projects::ApplicationController
params[:issue] ||= ActionController::Parameters.new( params[:issue] ||= ActionController::Parameters.new(
assignee_id: "" assignee_id: ""
) )
build_params = issue_params.merge(merge_request_for_resolving_discussions: merge_request_for_resolving_discussions)
@issue = @noteable = Issues::BuildService.new(project, current_user, build_params).execute
@issue = @noteable = @project.issues.new(issue_params)
respond_with(@issue) respond_with(@issue)
end end
...@@ -75,7 +76,9 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -75,7 +76,9 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def create def create
@issue = Issues::CreateService.new(project, current_user, issue_params.merge(request: request)).execute extra_params = { request: request,
merge_request_for_resolving_discussions: merge_request_for_resolving_discussions }
@issue = Issues::CreateService.new(project, current_user, issue_params.merge(extra_params)).execute
respond_to do |format| respond_to do |format|
format.html do format.html do
...@@ -169,6 +172,14 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -169,6 +172,14 @@ class Projects::IssuesController < Projects::ApplicationController
alias_method :awardable, :issue alias_method :awardable, :issue
alias_method :spammable, :issue alias_method :spammable, :issue
def merge_request_for_resolving_discussions
return unless merge_request_iid = params[:merge_request_for_resolving_discussions]
@merge_request_for_resolving_discussions ||= MergeRequestsFinder.new(current_user, project_id: project.id).
execute.
find_by(iid: merge_request_iid)
end
def authorize_read_issue! def authorize_read_issue!
return render_404 unless can?(current_user, :read_issue, @issue) return render_404 unless can?(current_user, :read_issue, @issue)
end end
......
...@@ -302,9 +302,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -302,9 +302,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def cancel_merge_when_build_succeeds def cancel_merge_when_build_succeeds
return access_denied! unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user) unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
return access_denied!
end
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user).cancel(@merge_request) MergeRequests::MergeWhenPipelineSucceedsService
.new(@project, current_user)
.cancel(@merge_request)
end end
def merge def merge
...@@ -331,8 +335,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -331,8 +335,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
if @merge_request.head_pipeline.active? if @merge_request.head_pipeline.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) MergeRequests::MergeWhenPipelineSucceedsService
.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.head_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
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
# current_user - which user use # current_user - which user use
# params: # params:
# scope: 'created-by-me' or 'assigned-to-me' or 'all' # scope: 'created-by-me' or 'assigned-to-me' or 'all'
# state: 'open' or 'closed' or 'all' # state: 'opened' or 'closed' or 'all'
# group_id: integer # group_id: integer
# project_id: integer # project_id: integer
# milestone_title: string # milestone_title: string
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# search: string # search: string
# label_name: string # label_name: string
# sort: string # sort: string
# non_archived: boolean
# #
class IssuableFinder class IssuableFinder
NONE = '0' NONE = '0'
...@@ -38,6 +39,7 @@ class IssuableFinder ...@@ -38,6 +39,7 @@ class IssuableFinder
items = by_author(items) items = by_author(items)
items = by_label(items) items = by_label(items)
items = by_due_date(items) items = by_due_date(items)
items = by_non_archived(items)
sort(items) sort(items)
end end
...@@ -207,10 +209,13 @@ class IssuableFinder ...@@ -207,10 +209,13 @@ class IssuableFinder
end end
def by_state(items) def by_state(items)
params[:state] ||= 'all' case params[:state].to_s
when 'closed'
if items.respond_to?(params[:state]) items.closed
items.public_send(params[:state]) when 'merged'
items.respond_to?(:merged) ? items.merged : items.closed
when 'opened'
items.opened
else else
items items
end end
...@@ -353,6 +358,10 @@ class IssuableFinder ...@@ -353,6 +358,10 @@ class IssuableFinder
end end
end end
def by_non_archived(items)
params[:non_archived].present? ? items.non_archived : items
end
def current_user_related? def current_user_related?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end end
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
# search: string # search: string
# label_name: string # label_name: string
# sort: string # sort: string
# non_archived: boolean
# #
class MergeRequestsFinder < IssuableFinder class MergeRequestsFinder < IssuableFinder
def klass def klass
......
...@@ -5,8 +5,9 @@ module CiStatusHelper ...@@ -5,8 +5,9 @@ module CiStatusHelper
end end
def ci_status_with_icon(status, target = nil) def ci_status_with_icon(status, target = nil)
content = ci_icon_for_status(status) + ci_label_for_status(status) content = ci_icon_for_status(status) + ci_text_for_status(status)
klass = "ci-status ci-#{status}" klass = "ci-status ci-#{status}"
if target if target
link_to content, target, class: klass link_to content, target, class: klass
else else
...@@ -14,7 +15,19 @@ module CiStatusHelper ...@@ -14,7 +15,19 @@ module CiStatusHelper
end end
end end
def ci_text_for_status(status)
if detailed_status?(status)
status.text
else
status
end
end
def ci_label_for_status(status) def ci_label_for_status(status)
if detailed_status?(status)
return status.label
end
case status case status
when 'success' when 'success'
'passed' 'passed'
...@@ -31,6 +44,10 @@ module CiStatusHelper ...@@ -31,6 +44,10 @@ module CiStatusHelper
end end
def ci_icon_for_status(status) def ci_icon_for_status(status)
if detailed_status?(status)
return custom_icon(status.icon)
end
icon_name = icon_name =
case status case status
when 'success' when 'success'
...@@ -94,4 +111,10 @@ module CiStatusHelper ...@@ -94,4 +111,10 @@ module CiStatusHelper
class: klass, title: title, data: data class: klass, title: title, data: data
end end
end end
def detailed_status?(status)
status.respond_to?(:text) &&
status.respond_to?(:label) &&
status.respond_to?(:icon)
end
end end
...@@ -174,7 +174,7 @@ module GitlabMarkdownHelper ...@@ -174,7 +174,7 @@ module GitlabMarkdownHelper
# Returns a String # Returns a String
def cross_project_reference(project, entity) def cross_project_reference(project, entity)
if entity.respond_to?(:to_reference) if entity.respond_to?(:to_reference)
"#{project.to_reference}#{entity.to_reference}" entity.to_reference(project)
else else
'' ''
end end
......
...@@ -82,12 +82,6 @@ module LabelsHelper ...@@ -82,12 +82,6 @@ module LabelsHelper
span.html_safe span.html_safe
end end
def render_colored_cross_project_label(label, source_project = nil, tooltip: true)
label_suffix = source_project ? source_project.name_with_namespace : label.project.name_with_namespace
label_suffix = " <i>in #{escape_once(label_suffix)}</i>"
render_colored_label(label, label_suffix, tooltip: tooltip)
end
def suggested_colors def suggested_colors
[ [
'#0033CC', '#0033CC',
...@@ -166,6 +160,5 @@ module LabelsHelper ...@@ -166,6 +160,5 @@ module LabelsHelper
end end
# Required for Banzai::Filter::LabelReferenceFilter # Required for Banzai::Filter::LabelReferenceFilter
module_function :render_colored_label, :render_colored_cross_project_label, module_function :render_colored_label, :text_color_for_bg, :escape_once
:text_color_for_bg, :escape_once
end end
...@@ -320,6 +320,10 @@ module Ci ...@@ -320,6 +320,10 @@ module Ci
.select { |merge_request| merge_request.head_pipeline.try(:id) == self.id } .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end end
def detailed_status
Gitlab::Ci::Status::Pipeline::Factory.new(self).fabricate!
end
private private
def pipeline_data def pipeline_data
......
...@@ -4,10 +4,10 @@ module Ci ...@@ -4,10 +4,10 @@ module Ci
belongs_to :project, foreign_key: :gl_project_id belongs_to :project, foreign_key: :gl_project_id
validates_uniqueness_of :key, scope: :gl_project_id
validates :key, validates :key,
presence: true, presence: true,
length: { within: 0..255 }, uniqueness: { scope: :gl_project_id },
length: { maximum: 255 },
format: { with: /\A[a-zA-Z0-9_]+\z/, format: { with: /\A[a-zA-Z0-9_]+\z/,
message: "can contain only letters, digits and '_'." } message: "can contain only letters, digits and '_'." }
......
...@@ -92,19 +92,11 @@ class Commit ...@@ -92,19 +92,11 @@ class Commit
end end
def to_reference(from_project = nil) def to_reference(from_project = nil)
if cross_project_reference?(from_project) commit_reference(from_project, id)
project.to_reference + self.class.reference_prefix + self.id
else
self.id
end
end end
def reference_link_text(from_project = nil) def reference_link_text(from_project = nil)
if cross_project_reference?(from_project) commit_reference(from_project, short_id)
project.to_reference + self.class.reference_prefix + self.short_id
else
self.short_id
end
end end
def diff_line_count def diff_line_count
...@@ -329,6 +321,16 @@ class Commit ...@@ -329,6 +321,16 @@ class Commit
private private
def commit_reference(from_project, referable_commit_id)
reference = project.to_reference(from_project)
if reference.present?
"#{reference}#{self.class.reference_prefix}#{referable_commit_id}"
else
referable_commit_id
end
end
def find_author_by_any_email def find_author_by_any_email
User.find_by_any_email(author_email.downcase) User.find_by_any_email(author_email.downcase)
end end
......
...@@ -90,22 +90,25 @@ class CommitRange ...@@ -90,22 +90,25 @@ class CommitRange
alias_method :id, :to_s alias_method :id, :to_s
def to_reference(from_project = nil) def to_reference(from_project = nil)
if cross_project_reference?(from_project) project_reference = project.to_reference(from_project)
project.to_reference + self.class.reference_prefix + self.id
if project_reference.present?
project_reference + self.class.reference_prefix + self.id
else else
self.id self.id
end end
end end
def reference_link_text(from_project = nil) def reference_link_text(from_project = nil)
project_reference = project.to_reference(from_project)
reference = ref_from + notation + ref_to reference = ref_from + notation + ref_to
if cross_project_reference?(from_project) if project_reference.present?
reference = project.to_reference + self.class.reference_prefix + reference project_reference + self.class.reference_prefix + reference
end else
reference reference
end end
end
# Return a Hash of parameters for passing to a URL helper # Return a Hash of parameters for passing to a URL helper
# #
......
...@@ -41,7 +41,7 @@ module Issuable ...@@ -41,7 +41,7 @@ module Issuable
has_one :metrics has_one :metrics
validates :author, presence: true validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 } validates :title, presence: true, length: { maximum: 255 }
scope :authored, ->(user) { where(author_id: user) } scope :authored, ->(user) { where(author_id: user) }
scope :assigned_to, ->(u) { where(assignee_id: u.id)} scope :assigned_to, ->(u) { where(assignee_id: u.id)}
......
...@@ -72,17 +72,4 @@ module Referable ...@@ -72,17 +72,4 @@ module Referable
}x }x
end end
end end
private
# Check if a reference is being done cross-project
#
# from_project - Refering Project object
def cross_project_reference?(from_project)
if self.is_a?(Project)
self != from_project
else
from_project && self.project && self.project != from_project
end
end
end end
...@@ -88,6 +88,10 @@ class Discussion ...@@ -88,6 +88,10 @@ class Discussion
@first_note ||= @notes.first @first_note ||= @notes.first
end end
def first_note_to_resolve
@first_note_to_resolve ||= notes.detect(&:to_be_resolved?)
end
def last_note def last_note
@last_note ||= @notes.last @last_note ||= @notes.last
end end
......
...@@ -9,7 +9,7 @@ class Environment < ActiveRecord::Base ...@@ -9,7 +9,7 @@ class Environment < ActiveRecord::Base
validates :name, validates :name,
presence: true, presence: true,
uniqueness: { scope: :project_id }, uniqueness: { scope: :project_id },
length: { within: 0..255 }, length: { maximum: 255 },
format: { with: Gitlab::Regex.environment_name_regex, format: { with: Gitlab::Regex.environment_name_regex,
message: Gitlab::Regex.environment_name_regex_message } message: Gitlab::Regex.environment_name_regex_message }
......
...@@ -153,11 +153,7 @@ class Issue < ActiveRecord::Base ...@@ -153,11 +153,7 @@ class Issue < ActiveRecord::Base
def to_reference(from_project = nil) def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}" reference = "#{self.class.reference_prefix}#{iid}"
if cross_project_reference?(from_project) "#{project.to_reference(from_project)}#{reference}"
reference = project.to_reference + reference
end
reference
end end
def referenced_merge_requests(current_user = nil) def referenced_merge_requests(current_user = nil)
......
...@@ -8,10 +8,18 @@ class Key < ActiveRecord::Base ...@@ -8,10 +8,18 @@ class Key < ActiveRecord::Base
before_validation :generate_fingerprint before_validation :generate_fingerprint
validates :title, presence: true, length: { within: 0..255 } validates :title,
validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ } presence: true,
validates :key, format: { without: /\n|\r/, message: 'should be a single line' } length: { maximum: 255 }
validates :fingerprint, uniqueness: true, presence: { message: 'cannot be generated' } validates :key,
presence: true,
length: { maximum: 5000 },
format: { with: /\A(ssh|ecdsa)-.*\Z/ }
validates :key,
format: { without: /\n|\r/, message: 'should be a single line' }
validates :fingerprint,
uniqueness: true,
presence: { message: 'cannot be generated' }
delegate :name, :email, to: :user, prefix: true delegate :name, :email, to: :user, prefix: true
......
...@@ -146,7 +146,8 @@ class Label < ActiveRecord::Base ...@@ -146,7 +146,8 @@ class Label < ActiveRecord::Base
# #
# Label.first.to_reference # => "~1" # Label.first.to_reference # => "~1"
# Label.first.to_reference(format: :name) # => "~\"bug\"" # Label.first.to_reference(format: :name) # => "~\"bug\""
# Label.first.to_reference(project1, project2) # => "gitlab-org/gitlab-ce~1" # Label.first.to_reference(project, same_namespace_project) # => "gitlab-ce~1"
# Label.first.to_reference(project, another_namespace_project) # => "gitlab-org/gitlab-ce~1"
# #
# Returns a String # Returns a String
# #
...@@ -154,8 +155,8 @@ class Label < ActiveRecord::Base ...@@ -154,8 +155,8 @@ class Label < ActiveRecord::Base
format_reference = label_format_reference(format) format_reference = label_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}" reference = "#{self.class.reference_prefix}#{format_reference}"
if cross_project_reference?(source_project, target_project) if source_project
source_project.to_reference + reference "#{source_project.to_reference(target_project)}#{reference}"
else else
reference reference
end end
...@@ -169,10 +170,6 @@ class Label < ActiveRecord::Base ...@@ -169,10 +170,6 @@ class Label < ActiveRecord::Base
private private
def cross_project_reference?(source_project, target_project)
source_project && target_project && source_project != target_project
end
def issues_count(user, params = {}) def issues_count(user, params = {})
params.merge!(subject_foreign_key => subject.id, label_name: title, scope: 'all') params.merge!(subject_foreign_key => subject.id, label_name: title, scope: 'all')
IssuesFinder.new(user, params.with_indifferent_access).execute.count IssuesFinder.new(user, params.with_indifferent_access).execute.count
......
...@@ -63,6 +63,7 @@ class Member < ActiveRecord::Base ...@@ -63,6 +63,7 @@ class Member < ActiveRecord::Base
after_create :send_request, if: :request?, unless: :importing? after_create :send_request, if: :request?, unless: :importing?
after_create :create_notification_setting, unless: [:pending?, :importing?] after_create :create_notification_setting, unless: [:pending?, :importing?]
after_create :post_create_hook, unless: [:pending?, :importing?] after_create :post_create_hook, unless: [:pending?, :importing?]
after_create :refresh_member_authorized_projects, if: :importing?
after_update :post_update_hook, unless: [:pending?, :importing?] after_update :post_update_hook, unless: [:pending?, :importing?]
after_destroy :post_destroy_hook, unless: :pending? after_destroy :post_destroy_hook, unless: :pending?
......
...@@ -176,11 +176,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -176,11 +176,7 @@ class MergeRequest < ActiveRecord::Base
def to_reference(from_project = nil) def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}" reference = "#{self.class.reference_prefix}#{iid}"
if cross_project_reference?(from_project) "#{project.to_reference(from_project)}#{reference}"
reference = project.to_reference + reference
end
reference
end end
def first_commit def first_commit
...@@ -480,6 +476,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -480,6 +476,14 @@ class MergeRequest < ActiveRecord::Base
@diff_discussions ||= self.notes.diff_notes.discussions @diff_discussions ||= self.notes.diff_notes.discussions
end end
def resolvable_discussions
@resolvable_discussions ||= diff_discussions.select(&:to_be_resolved?)
end
def discussions_can_be_resolved_by?(user)
resolvable_discussions.all? { |discussion| discussion.can_resolve?(user) }
end
def find_diff_discussion(discussion_id) def find_diff_discussion(discussion_id)
notes = self.notes.diff_notes.where(discussion_id: discussion_id).fresh.to_a notes = self.notes.diff_notes.where(discussion_id: discussion_id).fresh.to_a
return if notes.empty? return if notes.empty?
......
...@@ -115,17 +115,14 @@ class Milestone < ActiveRecord::Base ...@@ -115,17 +115,14 @@ class Milestone < ActiveRecord::Base
# #
# Milestone.first.to_reference # => "%1" # Milestone.first.to_reference # => "%1"
# Milestone.first.to_reference(format: :name) # => "%\"goal\"" # Milestone.first.to_reference(format: :name) # => "%\"goal\""
# Milestone.first.to_reference(project) # => "gitlab-org/gitlab-ce%1" # Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-ce%1"
# Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1"
# #
def to_reference(from_project = nil, format: :iid) def to_reference(from_project = nil, format: :iid)
format_reference = milestone_format_reference(format) format_reference = milestone_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}" reference = "#{self.class.reference_prefix}#{format_reference}"
if cross_project_reference?(from_project) "#{project.to_reference(from_project)}#{reference}"
project.to_reference + reference
else
reference
end
end end
def reference_link_text(from_project = nil) def reference_link_text(from_project = nil)
......
...@@ -12,17 +12,17 @@ class Namespace < ActiveRecord::Base ...@@ -12,17 +12,17 @@ class Namespace < ActiveRecord::Base
validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :owner, presence: true, unless: ->(n) { n.type == "Group" }
validates :name, validates :name,
length: { within: 0..255 },
namespace_name: true,
presence: true, presence: true,
uniqueness: true uniqueness: true,
length: { maximum: 255 },
namespace_name: true
validates :description, length: { within: 0..255 } validates :description, length: { maximum: 255 }
validates :path, validates :path,
length: { within: 1..255 },
namespace: true,
presence: true, presence: true,
uniqueness: { case_sensitive: false } uniqueness: { case_sensitive: false },
length: { maximum: 255 },
namespace: true
delegate :name, to: :owner, allow_nil: true, prefix: true delegate :name, to: :owner, allow_nil: true, prefix: true
......
...@@ -99,7 +99,7 @@ class Note < ActiveRecord::Base ...@@ -99,7 +99,7 @@ class Note < ActiveRecord::Base
end end
def discussions def discussions
Discussion.for_notes(all) Discussion.for_notes(fresh)
end end
def grouped_diff_discussions def grouped_diff_discussions
......
...@@ -172,13 +172,13 @@ class Project < ActiveRecord::Base ...@@ -172,13 +172,13 @@ class Project < ActiveRecord::Base
validates :description, length: { maximum: 2000 }, allow_blank: true validates :description, length: { maximum: 2000 }, allow_blank: true
validates :name, validates :name,
presence: true, presence: true,
length: { within: 0..255 }, length: { maximum: 255 },
format: { with: Gitlab::Regex.project_name_regex, format: { with: Gitlab::Regex.project_name_regex,
message: Gitlab::Regex.project_name_regex_message } message: Gitlab::Regex.project_name_regex_message }
validates :path, validates :path,
presence: true, presence: true,
project_path: true, project_path: true,
length: { within: 0..255 }, length: { maximum: 255 },
format: { with: Gitlab::Regex.project_path_regex, format: { with: Gitlab::Regex.project_path_regex,
message: Gitlab::Regex.project_path_regex_message } message: Gitlab::Regex.project_path_regex_message }
validates :namespace, presence: true validates :namespace, presence: true
...@@ -419,7 +419,11 @@ class Project < ActiveRecord::Base ...@@ -419,7 +419,11 @@ class Project < ActiveRecord::Base
def reference_pattern def reference_pattern
name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR
%r{(?<project>#{name_pattern}/#{name_pattern})}
%r{
((?<namespace>#{name_pattern})\/)?
(?<project>#{name_pattern})
}x
end end
def trending def trending
...@@ -650,8 +654,20 @@ class Project < ActiveRecord::Base ...@@ -650,8 +654,20 @@ class Project < ActiveRecord::Base
end end
end end
def to_reference(_from_project = nil) def to_reference(from_project = nil)
if cross_namespace_reference?(from_project)
path_with_namespace path_with_namespace
elsif cross_project_reference?(from_project)
path
end
end
def to_human_reference(from_project = nil)
if cross_namespace_reference?(from_project)
name_with_namespace
elsif cross_project_reference?(from_project)
name
end
end end
def web_url def web_url
...@@ -1327,10 +1343,21 @@ class Project < ActiveRecord::Base ...@@ -1327,10 +1343,21 @@ class Project < ActiveRecord::Base
private private
# Check if a reference is being done cross-project
#
# from_project - Refering Project object
def cross_project_reference?(from_project)
from_project && self != from_project
end
def pushes_since_gc_redis_key def pushes_since_gc_redis_key
"projects/#{id}/pushes_since_gc" "projects/#{id}/pushes_since_gc"
end end
def cross_namespace_reference?(from_project)
from_project && namespace != from_project.namespace
end
def default_branch_protected? def default_branch_protected?
current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL || current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
......
...@@ -81,12 +81,8 @@ class Repository ...@@ -81,12 +81,8 @@ class Repository
# This method return true if repository contains some content visible in project page. # This method return true if repository contains some content visible in project page.
# #
def has_visible_content? def has_visible_content?
return @has_visible_content unless @has_visible_content.nil?
@has_visible_content = cache.fetch(:has_visible_content?) do
branch_count > 0 branch_count > 0
end end
end
def commit(ref = 'HEAD') def commit(ref = 'HEAD')
return nil unless exists? return nil unless exists?
...@@ -340,12 +336,6 @@ class Repository ...@@ -340,12 +336,6 @@ class Repository
return unless empty? return unless empty?
expire_method_caches(%i(empty?)) expire_method_caches(%i(empty?))
expire_has_visible_content_cache
end
def expire_has_visible_content_cache
cache.expire(:has_visible_content?)
@has_visible_content = nil
end end
def lookup_cache def lookup_cache
...@@ -433,7 +423,6 @@ class Repository ...@@ -433,7 +423,6 @@ class Repository
# Runs code after a new branch has been created. # Runs code after a new branch has been created.
def after_create_branch def after_create_branch
expire_branches_cache expire_branches_cache
expire_has_visible_content_cache
repository_event(:push_branch) repository_event(:push_branch)
end end
...@@ -447,7 +436,6 @@ class Repository ...@@ -447,7 +436,6 @@ class Repository
# Runs code after an existing branch has been removed. # Runs code after an existing branch has been removed.
def after_remove_branch def after_remove_branch
expire_has_visible_content_cache
expire_branches_cache expire_branches_cache
end end
......
...@@ -27,9 +27,9 @@ class Snippet < ActiveRecord::Base ...@@ -27,9 +27,9 @@ class Snippet < ActiveRecord::Base
delegate :name, :email, to: :author, prefix: true, allow_nil: true delegate :name, :email, to: :author, prefix: true, allow_nil: true
validates :author, presence: true validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 } validates :title, presence: true, length: { maximum: 255 }
validates :file_name, validates :file_name,
length: { within: 0..255 }, length: { maximum: 255 },
format: { with: Gitlab::Regex.file_name_regex, format: { with: Gitlab::Regex.file_name_regex,
message: Gitlab::Regex.file_name_regex_message } message: Gitlab::Regex.file_name_regex_message }
...@@ -67,12 +67,12 @@ class Snippet < ActiveRecord::Base ...@@ -67,12 +67,12 @@ class Snippet < ActiveRecord::Base
def to_reference(from_project = nil) def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{id}" reference = "#{self.class.reference_prefix}#{id}"
if cross_project_reference?(from_project) if project.present?
reference = project.to_reference + reference "#{project.to_reference(from_project)}#{reference}"
end else
reference reference
end end
end
def self.content_types def self.content_types
[ [
...@@ -94,6 +94,10 @@ class Snippet < ActiveRecord::Base ...@@ -94,6 +94,10 @@ class Snippet < ActiveRecord::Base
0 0
end end
def file_name
super.to_s
end
# alias for compatibility with blobs and highlighting # alias for compatibility with blobs and highlighting
def path def path
file_name file_name
......
module Ci module Ci
class BuildPolicy < CommitStatusPolicy class BuildPolicy < CommitStatusPolicy
def rules def rules
can! :read_build if @subject.project.public_builds?
super super
# If we can't read build we should also not have that # If we can't read build we should also not have that
......
...@@ -12,9 +12,6 @@ class ProjectPolicy < BasePolicy ...@@ -12,9 +12,6 @@ class ProjectPolicy < BasePolicy
guest_access! guest_access!
public_access! public_access!
# Allow to read builds for internal projects
can! :read_build if project.public_builds?
if project.request_access_enabled && if project.request_access_enabled &&
!(owner || user.admin? || project.team.member?(user) || project_group_member?(user)) !(owner || user.admin? || project.team.member?(user) || project_group_member?(user))
can! :request_access can! :request_access
...@@ -46,6 +43,11 @@ class ProjectPolicy < BasePolicy ...@@ -46,6 +43,11 @@ class ProjectPolicy < BasePolicy
can! :create_note can! :create_note
can! :upload_file can! :upload_file
can! :read_cycle_analytics can! :read_cycle_analytics
if project.public_builds?
can! :read_pipeline
can! :read_build
end
end end
def reporter_access! def reporter_access!
......
module Discussions
class BaseService < ::BaseService
end
end
module Discussions
class ResolveService < Discussions::BaseService
def execute(one_or_more_discussions)
Array(one_or_more_discussions).each { |discussion| resolve_discussion(discussion) }
end
def resolve_discussion(discussion)
return unless discussion.can_resolve?(current_user)
discussion.resolve!(current_user)
MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(merge_request)
SystemNoteService.discussion_continued_in_issue(discussion, project, current_user, follow_up_issue) if follow_up_issue
end
def merge_request
params[:merge_request]
end
def follow_up_issue
params[:follow_up_issue]
end
end
end
...@@ -122,7 +122,8 @@ class IssuableBaseService < BaseService ...@@ -122,7 +122,8 @@ class IssuableBaseService < BaseService
SlashCommands::InterpretService.new(project, current_user). SlashCommands::InterpretService.new(project, current_user).
execute(params[:description], issuable) execute(params[:description], issuable)
params[:description] = description # Avoid a description already set on an issuable to be overwritten by a nil
params[:description] = description if params.has_key?(:description)
params.merge!(command_params) params.merge!(command_params)
end end
......
module Issues module Issues
class BaseService < ::IssuableBaseService class BaseService < ::IssuableBaseService
attr_reader :merge_request_for_resolving_discussions
def initialize(*args)
super
@merge_request_for_resolving_discussions ||= params.delete(:merge_request_for_resolving_discussions)
end
def hook_data(issue, action) def hook_data(issue, action)
issue_data = issue.to_hook_data(current_user) issue_data = issue.to_hook_data(current_user)
issue_url = Gitlab::UrlBuilder.build(issue) issue_url = Gitlab::UrlBuilder.build(issue)
......
module Issues
class BuildService < Issues::BaseService
def execute
@issue = project.issues.new(issue_params)
end
def issue_params_with_info_from_merge_request
return {} unless merge_request_for_resolving_discussions
{ title: title_from_merge_request, description: description_from_merge_request }
end
def title_from_merge_request
"Follow-up from \"#{merge_request_for_resolving_discussions.title}\""
end
def description_from_merge_request
if merge_request_for_resolving_discussions.resolvable_discussions.empty?
return "There are no unresolved discussions. "\
"Review the conversation in #{merge_request_for_resolving_discussions.to_reference}"
end
description = "The following discussions from #{merge_request_for_resolving_discussions.to_reference} should be addressed:"
[description, *items_for_discussions].join("\n\n")
end
def items_for_discussions
merge_request_for_resolving_discussions.resolvable_discussions.map { |discussion| item_for_discussion(discussion) }
end
def item_for_discussion(discussion)
first_note = discussion.first_note_to_resolve
other_note_count = discussion.notes.size - 1
creation_time = first_note.created_at.to_s(:medium)
note_url = Gitlab::UrlBuilder.build(first_note)
discussion_info = "- [ ] #{first_note.author.to_reference} commented in a discussion on [#{creation_time}](#{note_url}): "
discussion_info << " (+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
note_without_block_quotes = Banzai::Filter::BlockquoteFenceFilter.new(first_note.note).call
quote = ">>>\n#{note_without_block_quotes}\n>>>"
[discussion_info, quote].join("\n\n")
end
def issue_params
@issue_params ||= issue_params_with_info_from_merge_request.merge(params.slice(:title, :description))
end
end
end
...@@ -4,7 +4,8 @@ module Issues ...@@ -4,7 +4,8 @@ module Issues
@request = params.delete(:request) @request = params.delete(:request)
@api = params.delete(:api) @api = params.delete(:api)
@issue = project.issues.new issue_attributes = params.merge(merge_request_for_resolving_discussions: merge_request_for_resolving_discussions)
@issue = BuildService.new(project, current_user, issue_attributes).execute
create(@issue) create(@issue)
end end
...@@ -18,6 +19,17 @@ module Issues ...@@ -18,6 +19,17 @@ module Issues
notification_service.new_issue(issuable, current_user) notification_service.new_issue(issuable, current_user)
todo_service.new_issue(issuable, current_user) todo_service.new_issue(issuable, current_user)
user_agent_detail_service.create user_agent_detail_service.create
if merge_request_for_resolving_discussions.try(:discussions_can_be_resolved_by?, current_user)
resolve_discussions_in_merge_request(issuable)
end
end
def resolve_discussions_in_merge_request(issue)
Discussions::ResolveService.new(project, current_user,
merge_request: merge_request_for_resolving_discussions,
follow_up_issue: issue).
execute(merge_request_for_resolving_discussions.resolvable_discussions)
end end
private private
......
module MergeRequests module MergeRequests
class MergeWhenBuildSucceedsService < MergeRequests::BaseService class MergeWhenPipelineSucceedsService < MergeRequests::BaseService
# Marks the passed `merge_request` to be merged when the build succeeds or # Marks the passed `merge_request` to be merged when the build succeeds or
# updates the params for the automatic merge # updates the params for the automatic merge
def execute(merge_request) def execute(merge_request)
......
...@@ -131,14 +131,14 @@ module SystemNoteService ...@@ -131,14 +131,14 @@ module SystemNoteService
create_note(noteable: noteable, project: project, author: author, note: body) create_note(noteable: noteable, project: project, author: author, note: body)
end end
# Called when 'merge when build succeeds' is executed # Called when 'merge when pipeline succeeds' is executed
def merge_when_build_succeeds(noteable, project, author, last_commit) def merge_when_build_succeeds(noteable, project, author, last_commit)
body = "enabled an automatic merge when the build for #{last_commit.to_reference(project)} succeeds" body = "enabled an automatic merge when the pipeline for #{last_commit.to_reference(project)} succeeds"
create_note(noteable: noteable, project: project, author: author, note: body) create_note(noteable: noteable, project: project, author: author, note: body)
end end
# Called when 'merge when build succeeds' is canceled # Called when 'merge when pipeline succeeds' is canceled
def cancel_merge_when_build_succeeds(noteable, project, author) def cancel_merge_when_build_succeeds(noteable, project, author)
body = 'canceled the automatic merge' body = 'canceled the automatic merge'
...@@ -163,6 +163,14 @@ module SystemNoteService ...@@ -163,6 +163,14 @@ module SystemNoteService
create_note(noteable: merge_request, project: project, author: author, note: body) create_note(noteable: merge_request, project: project, author: author, note: body)
end end
def discussion_continued_in_issue(discussion, project, author, issue)
body = "Added #{issue.to_reference} to continue this discussion"
note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
note_attributes[:type] = note_attributes.delete(:note_type)
create_note(note_attributes)
end
# Called when the title of a Noteable is changed # Called when the title of a Noteable is changed
# #
# noteable - Noteable object that responds to `title` # noteable - Noteable object that responds to `title`
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.abuse-reports .abuse-reports
- if @abuse_reports.present? - if @abuse_reports.present?
.table-holder .table-holder
%table.table %table.table.responsive-table
%thead.hidden-sm.hidden-xs %thead.hidden-sm.hidden-xs
%tr %tr
%th User %th User
...@@ -13,8 +13,6 @@ ...@@ -13,8 +13,6 @@
%th Action %th Action
= render @abuse_reports = render @abuse_reports
- else - else
.no-reports .empty-state
%span.pull-left .text-center
There are no abuse reports! %h4 There are no abuse reports! #{emoji_icon 'tada'}
.pull-left
= emoji_icon 'tada'
...@@ -113,7 +113,7 @@ ...@@ -113,7 +113,7 @@
%hr %hr
.row .row
.col-sm-4 .col-sm-4
.light-well .light-well.well-centered
%h4 Projects %h4 Projects
.data .data
= link_to admin_namespaces_projects_path do = link_to admin_namespaces_projects_path do
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
%hr %hr
= link_to('New Project', new_project_path, class: "btn btn-new") = link_to('New Project', new_project_path, class: "btn btn-new")
.col-sm-4 .col-sm-4
.light-well .light-well.well-centered
%h4 Users %h4 Users
.data .data
= link_to admin_users_path do = link_to admin_users_path do
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
%hr %hr
= link_to 'New User', new_admin_user_path, class: "btn btn-new" = link_to 'New User', new_admin_user_path, class: "btn btn-new"
.col-sm-4 .col-sm-4
.light-well .light-well.well-centered
%h4 Groups %h4 Groups
.data .data
= link_to admin_groups_path do = link_to admin_groups_path do
...@@ -143,7 +143,7 @@ ...@@ -143,7 +143,7 @@
%hr %hr
- @projects.each do |project| - @projects.each do |project|
%p %p
= link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated' = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated-60'
%span.light.pull-right %span.light.pull-right
#{time_ago_with_tooltip(project.created_at)} #{time_ago_with_tooltip(project.created_at)}
...@@ -152,7 +152,7 @@ ...@@ -152,7 +152,7 @@
%hr %hr
- @users.each do |user| - @users.each do |user|
%p %p
= link_to [:admin, user], class: 'str-truncated' do = link_to [:admin, user], class: 'str-truncated-60' do
= user.name = user.name
%span.light.pull-right %span.light.pull-right
#{time_ago_with_tooltip(user.created_at)} #{time_ago_with_tooltip(user.created_at)}
...@@ -162,7 +162,7 @@ ...@@ -162,7 +162,7 @@
%hr %hr
- @groups.each do |group| - @groups.each do |group|
%p %p
= link_to [:admin, group], class: 'str-truncated' do = link_to [:admin, group], class: 'str-truncated-60' do
= group.name = group.name
%span.light.pull-right %span.light.pull-right
#{time_ago_with_tooltip(group.created_at)} #{time_ago_with_tooltip(group.created_at)}
%li.user-row %li.flex-row
.user-avatar .user-avatar
= image_tag avatar_icon(user), class: "avatar", alt: '' = image_tag avatar_icon(user), class: "avatar", alt: ''
.user-details .row-main-content
.user-name .user-name.row-title.str-truncated-100
= link_to user.name, [:admin, user] = link_to user.name, [:admin, user]
- if user.blocked? - if user.blocked?
%span.label.label-danger blocked %span.label.label-danger blocked
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
%span.label.label-default External %span.label.label-default External
- if user == current_user - if user == current_user
%span It's you! %span It's you!
.user-email .row-second-line.str-truncated-100
= mail_to user.email, user.email = mail_to user.email, user.email
.controls .controls
= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn' = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn'
......
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
%small.badge= number_with_delimiter(User.without_projects.count) %small.badge= number_with_delimiter(User.without_projects.count)
.fade-right .fade-right
%ul.users-list.content-list %ul.flex-list.content-list
- if @users.empty? - if @users.empty?
%li %li
.nothing-here-block No users found. .nothing-here-block No users found.
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
%ul.well-list %ul.well-list
- @user.groups.each do |group| - @user.groups.each do |group|
%li %li
%strong= group.name %strong= link_to group.name, admin_group_path(group)
&ndash; access to &ndash; access to
#{pluralize(group.projects.count, 'project')} #{pluralize(group.projects.count, 'project')}
......
.well-confirmation.text-center .well-confirmation.text-center.append-bottom-20
%h1.prepend-top-0 %h1.prepend-top-0
Almost there... Almost there...
%p.lead %p.lead.append-bottom-20
Please check your email to confirm your account Please check your email to confirm your account
%hr
- if current_application_settings.after_sign_up_text.present? - if current_application_settings.after_sign_up_text.present?
.well-confirmation.text-center .well-confirmation.text-center
= markdown_field(current_application_settings, :after_sign_up_text) = markdown_field(current_application_settings, :after_sign_up_text)
%p.confirmation-content.text-center %p.text-center
No confirmation email received? Please check your spam folder or No confirmation email received? Please check your spam folder or
.append-bottom-20.prepend-top-20.text-center .append-bottom-20.prepend-top-20.text-center
%a.btn.btn-lg.btn-success{ href: new_user_confirmation_path } %a.btn.btn-lg.btn-success{ href: new_user_confirmation_path }
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
%span %span
Merge Requests Merge Requests
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
%span.badge.count= number_with_delimiter(merge_requests.count) %span.badge.count= number_with_delimiter(merge_requests.count)
= nav_link(controller: [:group_members]) do = nav_link(controller: [:group_members]) do
= link_to group_group_members_path(@group), title: 'Members' do = link_to group_group_members_path(@group), title: 'Members' do
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
&nbsp; &nbsp;
.light .light
= commit_author_link(commit, avatar: false) = commit_author_link(commit, avatar: false)
authored committed
#{time_ago_with_tooltip(commit.committed_date)} #{time_ago_with_tooltip(commit.committed_date)}
%td.line-numbers %td.line-numbers
- line_count = blame_group[:lines].count - line_count = blame_group[:lines].count
......
- if !project.empty_repo? && can?(current_user, :download_code, project) - if !project.empty_repo? && can?(current_user, :download_code, project)
%span{class: 'download-button'} .dropdown.inline.download-button
.dropdown.inline
%button.btn{ 'data-toggle' => 'dropdown' } %button.btn{ 'data-toggle' => 'dropdown' }
= icon('download') = icon('download')
= icon("caret-down") = icon("caret-down")
......
- status = pipeline.status - status = pipeline.status
- detailed_status = pipeline.detailed_status
- show_commit = local_assigns.fetch(:show_commit, true) - show_commit = local_assigns.fetch(:show_commit, true)
- show_branch = local_assigns.fetch(:show_branch, true) - show_branch = local_assigns.fetch(:show_branch, true)
%tr.commit %tr.commit
%td.commit-link %td.commit-link
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{status}" do = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{detailed_status}" do
= ci_icon_for_status(status) = ci_icon_for_status(detailed_status)
= ci_label_for_status(status) = ci_text_for_status(detailed_status)
%td %td
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
......
...@@ -37,5 +37,5 @@ ...@@ -37,5 +37,5 @@
= preserve(markdown(commit.description, pipeline: :single_line, author: commit.author)) = preserve(markdown(commit.description, pipeline: :single_line, author: commit.author))
= commit_author_link(commit, avatar: false, size: 24) = commit_author_link(commit, avatar: false, size: 24)
authored committed
#{time_ago_with_tooltip(commit.committed_date)} #{time_ago_with_tooltip(commit.committed_date)}
...@@ -8,14 +8,13 @@ ...@@ -8,14 +8,13 @@
= link_to "##{@pipeline.id}", namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'pipeline' = link_to "##{@pipeline.id}", namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'pipeline'
= ci_label_for_status(status) = ci_label_for_status(status)
for for
- commit = @merge_request.diff_head_commit
= succeed "." do = succeed "." do
= link_to @pipeline.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @pipeline.sha), class: "monospace" = link_to @pipeline.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @pipeline.sha), class: "monospace"
%span.ci-coverage %span.ci-coverage
- elsif @merge_request.has_ci? - elsif @merge_request.has_ci?
- # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
- # Remove in later versions when services like Jenkins will set CI status via Commit status API - # TODO, remove in later versions when services like Jenkins will set CI status via Commit status API
.mr-widget-heading .mr-widget-heading
- %w[success skipped canceled failed running pending].each do |status| - %w[success skipped canceled failed running pending].each do |status|
.ci_widget{class: "ci-#{status}", style: "display:none"} .ci_widget{class: "ci-#{status}", style: "display:none"}
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- if @pipeline && @pipeline.active? - if @pipeline && @pipeline.active?
%span.btn-group %span.btn-group
= button_tag class: "btn btn-create js-merge-button merge_when_build_succeeds" do = button_tag class: "btn btn-create js-merge-button merge_when_build_succeeds" do
Merge When Build Succeeds Merge When Pipeline Succeeds
- unless @project.only_allow_merge_if_build_succeeds? - unless @project.only_allow_merge_if_build_succeeds?
= button_tag class: "btn btn-success dropdown-toggle", 'data-toggle' => 'dropdown' do = button_tag class: "btn btn-success dropdown-toggle", 'data-toggle' => 'dropdown' do
= icon('caret-down') = icon('caret-down')
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
%li %li
= link_to "#", class: "merge_when_build_succeeds" do = link_to "#", class: "merge_when_build_succeeds" do
= icon('check fw') = icon('check fw')
Merge When Build Succeeds Merge When Pipeline Succeeds
%li %li
= link_to "#", class: "accept_merge_request" do = link_to "#", class: "accept_merge_request" do
= icon('warning fw') = icon('warning fw')
......
%h4 %h4
Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)} Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)}
to be merged automatically when the build succeeds. to be merged automatically when the pipeline succeeds.
%div %div
%p %p
= succeed '.' do = succeed '.' do
......
...@@ -3,4 +3,8 @@ ...@@ -3,4 +3,8 @@
This merge request has unresolved discussions This merge request has unresolved discussions
%p %p
Please resolve these discussions to allow this merge request to be merged. Please resolve these discussions
\ No newline at end of file - if @project.issues_enabled? && can?(current_user, :create_issue, @project)
or
= link_to "open an issue to resolve them later", new_namespace_project_issue_path(@project.namespace, @project, merge_request_for_resolving_discussions: @merge_request.iid)
to allow this merge request to be merged.
.page-content-header .page-content-header
.header-main-content .header-main-content
= ci_status_with_icon(@pipeline.status) = ci_status_with_icon(@pipeline.detailed_status)
%strong Pipeline ##{@commit.pipelines.last.id} %strong Pipeline ##{@commit.pipelines.last.id}
triggered #{time_ago_with_tooltip(@commit.authored_date)} by triggered #{time_ago_with_tooltip(@commit.authored_date)} by
= author_avatar(@commit, size: 24) = author_avatar(@commit, size: 24)
......
...@@ -86,6 +86,9 @@ ...@@ -86,6 +86,9 @@
%li %li
tap --coverage-report=text-summary (NodeJS) - tap --coverage-report=text-summary (NodeJS) -
%code ^Statements\s*:\s*([^%]+) %code ^Statements\s*:\s*([^%]+)
%li
excoveralls (Elixir) -
%code \[TOTAL\]\s+(\d+\.\d+)%
= f.submit 'Save changes', class: "btn btn-save" = f.submit 'Save changes', class: "btn btn-save"
......
...@@ -3,8 +3,16 @@ ...@@ -3,8 +3,16 @@
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: container_class } %div{ class: container_class }
.sub-header-block .top-area.multi-line
.pull-right.tag-buttons .nav-text
.title
%span.item-title= @tag.name
- if @commit
= render 'projects/branches/commit', commit: @commit, project: @project
- else
Cant find HEAD commit for this tag
.nav-controls
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do
= icon("pencil") = icon("pencil")
...@@ -15,15 +23,8 @@ ...@@ -15,15 +23,8 @@
= render 'projects/buttons/download', project: @project, ref: @tag.name = render 'projects/buttons/download', project: @project, ref: @tag.name
- if can?(current_user, :admin_project, @project) - if can?(current_user, :admin_project, @project)
.pull-right .pull-right
= link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
%i.fa.fa-trash-o %i.fa.fa-trash-o
.tag-info.append-bottom-10
.title
%span.item-title= @tag.name
- if @commit
= render 'projects/branches/commit', commit: @commit, project: @project
- else
Cant find HEAD commit for this tag
- if @tag.message.present? - if @tag.message.present?
%pre.body %pre.body
= strip_gpg_signature(@tag.message) = strip_gpg_signature(@tag.message)
......
...@@ -42,6 +42,21 @@ ...@@ -42,6 +42,21 @@
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form = render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
- if @merge_request_for_resolving_discussions
.form-group
.col-sm-10.col-sm-offset-2
- if @merge_request_for_resolving_discussions.discussions_can_be_resolved_by?(current_user)
= icon('exclamation-triangle')
Creating this issue will mark all discussions in
= link_to @merge_request_for_resolving_discussions.to_reference, merge_request_path(@merge_request_for_resolving_discussions)
as resolved.
= hidden_field_tag 'merge_request_for_resolving_discussions', @merge_request_for_resolving_discussions.iid
- else
= icon('exclamation-triangle')
You can't automatically mark all discussions in
= link_to @merge_request_for_resolving_discussions.to_reference, merge_request_path(@merge_request_for_resolving_discussions)
as resolved. Ask someone with sufficient rights to resolve the them.
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) - is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
.row-content-block{class: (is_footer ? "footer-block" : "middle-block")} .row-content-block{class: (is_footer ? "footer-block" : "middle-block")}
- if issuable.new_record? - if issuable.new_record?
......
...@@ -4,22 +4,22 @@ ...@@ -4,22 +4,22 @@
%ul.nav-links.issues-state-filters %ul.nav-links.issues-state-filters
%li{class: ("active" if params[:state] == 'opened')} %li{class: ("active" if params[:state] == 'opened')}
= link_to page_filter_path(state: 'opened', label: true), title: "Filter by #{page_context_word} that are currently opened." do = link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened." do
#{issuables_state_counter_text(type, :opened)} #{issuables_state_counter_text(type, :opened)}
- if type == :merge_requests - if type == :merge_requests
%li{class: ("active" if params[:state] == 'merged')} %li{class: ("active" if params[:state] == 'merged')}
= link_to page_filter_path(state: 'merged', label: true), title: 'Filter by merge requests that are currently merged.' do = link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.' do
#{issuables_state_counter_text(type, :merged)} #{issuables_state_counter_text(type, :merged)}
%li{class: ("active" if params[:state] == 'closed')} %li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed', label: true), title: 'Filter by merge requests that are currently closed and unmerged.' do = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.' do
#{issuables_state_counter_text(type, :closed)} #{issuables_state_counter_text(type, :closed)}
- else - else
%li{class: ("active" if params[:state] == 'closed')} %li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed', label: true), title: 'Filter by issues that are currently closed.' do = link_to page_filter_path(state: 'closed', label: true), id: 'state-all', title: 'Filter by issues that are currently closed.' do
#{issuables_state_counter_text(type, :closed)} #{issuables_state_counter_text(type, :closed)}
%li{class: ("active" if params[:state] == 'all')} %li{class: ("active" if params[:state] == 'all')}
= link_to page_filter_path(state: 'all', label: true), title: "Show all #{page_context_word}." do = link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}." do
#{issuables_state_counter_text(type, :all)} #{issuables_state_counter_text(type, :all)}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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