Merge branch 'master' into reference-pipeline-and-caching
This commit is contained in:
commit
f5a630111f
571 changed files with 9843 additions and 3255 deletions
1
.flayignore
Normal file
1
.flayignore
Normal file
|
@ -0,0 +1 @@
|
|||
*.erb
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -37,8 +37,10 @@ nohup.out
|
|||
public/assets/
|
||||
public/uploads.*
|
||||
public/uploads/
|
||||
shared/artifacts/
|
||||
rails_best_practices_output.html
|
||||
/tags
|
||||
tmp/
|
||||
vendor/bundle/*
|
||||
builds/*
|
||||
shared/*
|
||||
|
|
|
@ -73,3 +73,17 @@ brakeman:
|
|||
tags:
|
||||
- ruby
|
||||
- mysql
|
||||
|
||||
flog:
|
||||
script:
|
||||
- bundle exec rake flog
|
||||
tags:
|
||||
- ruby
|
||||
- mysql
|
||||
|
||||
flay:
|
||||
script:
|
||||
- bundle exec rake flay
|
||||
tags:
|
||||
- ruby
|
||||
- mysql
|
||||
|
|
64
CHANGELOG
64
CHANGELOG
|
@ -1,6 +1,21 @@
|
|||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
v 8.2.0 (unreleased)
|
||||
v 8.3.0 (unreleased)
|
||||
|
||||
v 8.2.0
|
||||
- Improved performance of finding projects and groups in various places
|
||||
- Improved performance of rendering user profile pages and Atom feeds
|
||||
- Fix grouping of contributors by email in graph.
|
||||
- Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
|
||||
- Fix Drone CI service template not saving properly (Stan Hu)
|
||||
- Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu)
|
||||
- Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749)
|
||||
- Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu)
|
||||
- Improved performance of finding users by one of their Email addresses
|
||||
- Add allow_failure field to commit status API (Stan Hu)
|
||||
- Commits without .gitlab-ci.yml are marked as skipped
|
||||
- Save detailed error when YAML syntax is invalid
|
||||
- Added build artifacts
|
||||
- Improved performance of replacing references in comments
|
||||
- Show last project commit to default branch on project home page
|
||||
- Highlight comment based on anchor in URL
|
||||
|
@ -11,9 +26,49 @@ v 8.2.0 (unreleased)
|
|||
- Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork
|
||||
- Use git follow flag for commits page when retrieve history for file or directory
|
||||
- Show merge request CI status on merge requests index page
|
||||
- Send build name and stage in CI notification e-mail
|
||||
- Extend yml syntax for only and except to support specifying repository path
|
||||
- Enable shared runners to all new projects
|
||||
- Bump GitLab-Workhorse to 0.4.1
|
||||
- Allow to define cache in `.gitlab-ci.yml`
|
||||
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
|
||||
- Remove deprecated CI events from project settings page
|
||||
- [API] Add ability to fetch the commit ID of the last commit that actually touched a file
|
||||
- Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
|
||||
- Add "New file" link to dropdown on project page
|
||||
- Include commit logs in project search
|
||||
- Add "added", "modified" and "removed" properties to commit object in webhook
|
||||
- Rename "Back to" links to "Go to" because its not always a case it point to place user come from
|
||||
- Allow groups to appear in the search results if the group owner allows it
|
||||
- Add email notification to former assignee upon unassignment (Adam Lieskovský)
|
||||
- New design for project graphs page
|
||||
- Remove deprecated dumped yaml file generated from previous job definitions
|
||||
- Fix incoming email config defaults
|
||||
- Show specific runners from projects where user is master or owner
|
||||
- MR target branch is now visible on a list view when it is different from project's default one
|
||||
- Improve Continuous Integration graphs page
|
||||
- Make color of "Accept Merge Request" button consistent with current build status
|
||||
- Add ignore white space option in merge request diff and commit and compare view
|
||||
- Ability to add release notes (markdown text and attachments) to git tags (aka Releases)
|
||||
- Relative links from a repositories README.md now link to the default branch
|
||||
- Fix trailing whitespace issue in merge request/issue title
|
||||
- Fix bug when milestone/label filter was empty for dashboard issues page
|
||||
- Add ability to create milestone in group projects from single form
|
||||
- Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez)
|
||||
|
||||
v 8.1.1
|
||||
v 8.1.4
|
||||
- Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
|
||||
- Prevent redirect loop when home_page_url is set to the root URL
|
||||
- Fix incoming email config defaults
|
||||
- Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
|
||||
|
||||
v 8.1.3
|
||||
- Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
|
||||
- Spread out runner contacted_at updates
|
||||
- Use issue editor as cross reference comment author when issue is edited with a new mention
|
||||
- Add Facebook authentication
|
||||
|
||||
v 8.1.2
|
||||
- Fix cloning Wiki repositories via HTTP (Stan Hu)
|
||||
- Add migration to remove satellites directory
|
||||
- Fix specific runners visibility
|
||||
|
@ -23,10 +78,15 @@ v 8.1.1
|
|||
- Fix CI badge
|
||||
- Allow developer to manage builds
|
||||
|
||||
v 8.1.1
|
||||
- Removed, see 8.1.2
|
||||
|
||||
v 8.1.0
|
||||
- Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
|
||||
- Fix duplicate repositories in GitHub import page (Stan Hu)
|
||||
- Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
|
||||
- Adds ability to create directories using the web editor (Ben Ford)
|
||||
- Cleanup stuck CI builds
|
||||
- Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
|
||||
- Show notifications button when user is member of group rather than project (Grzegorz Bizon)
|
||||
- Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
|
||||
|
|
|
@ -10,7 +10,7 @@ By submitting code as an individual you agree to the [individual contributor lic
|
|||
|
||||
## Security vulnerability disclosure
|
||||
|
||||
Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
|
||||
Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
|
||||
|
||||
## Closing policy for issues and merge requests
|
||||
|
||||
|
@ -35,7 +35,7 @@ The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab
|
|||
|
||||
Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple.
|
||||
|
||||
Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
|
||||
Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
|
||||
|
||||
### Issue tracker guidelines
|
||||
|
||||
|
@ -72,7 +72,7 @@ If you can, please submit a merge request with the fix or improvements including
|
|||
1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
|
||||
1. Add your changes to the [CHANGELOG](CHANGELOG)
|
||||
1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message
|
||||
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
|
||||
1. If you have multiple commits please combine them into one commit by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
|
||||
1. Push the commit to your fork
|
||||
1. Submit a merge request (MR) to the master branch
|
||||
1. The MR title should describe the change you want to make
|
||||
|
@ -83,6 +83,7 @@ If you can, please submit a merge request with the fix or improvements including
|
|||
1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submission
|
||||
1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
|
||||
1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk.
|
||||
1. If your code creates new files on disk please read the [shared files guidelines](doc/development/shared_files.md).
|
||||
|
||||
The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast.
|
||||
Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as regressions requiring patch releases.
|
||||
|
@ -180,4 +181,4 @@ This code of conduct applies both within project spaces and in public spaces whe
|
|||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
0.3.0
|
|
@ -1 +1 @@
|
|||
2.6.5
|
||||
2.6.7
|
||||
|
|
1
GITLAB_WORKHORSE
Normal file
1
GITLAB_WORKHORSE
Normal file
|
@ -0,0 +1 @@
|
|||
0.4.1
|
1
GITLAB_WORKHORSE_VERSION
Normal file
1
GITLAB_WORKHORSE_VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
0.3.1
|
25
Gemfile
25
Gemfile
|
@ -19,6 +19,7 @@ gem 'devise-async', '~> 0.9.0'
|
|||
gem 'doorkeeper', '~> 2.1.3'
|
||||
gem 'omniauth', '~> 1.2.2'
|
||||
gem 'omniauth-bitbucket', '~> 0.0.2'
|
||||
gem 'omniauth-facebook', '~> 3.0.0'
|
||||
gem 'omniauth-github', '~> 1.1.1'
|
||||
gem 'omniauth-gitlab', '~> 1.0.0'
|
||||
gem 'omniauth-google-oauth2', '~> 0.2.0'
|
||||
|
@ -39,7 +40,7 @@ gem "browser", '~> 1.0.0'
|
|||
|
||||
# Extracting information from a git repository
|
||||
# Provide access to Gitlab::Git library
|
||||
gem "gitlab_git", '~> 7.2.19'
|
||||
gem "gitlab_git", '~> 7.2.20'
|
||||
|
||||
# LDAP Auth
|
||||
# GitLab fork with several improvements to original library. For full list of changes
|
||||
|
@ -50,20 +51,16 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap"
|
|||
gem 'gollum-lib', '~> 4.0.2'
|
||||
|
||||
# Language detection
|
||||
# GitLab fork of linguist does not require pygments/python dependency.
|
||||
# New version of original gem also dropped pygments support but it has strict
|
||||
# dependency to unstable rugged version. We have internal issue for replacing
|
||||
# fork with original gem when we meet on same rugged version - https://dev.gitlab.org/gitlab/gitlabhq/issues/2052.
|
||||
gem "gitlab-linguist", "~> 3.0.1", require: "linguist"
|
||||
gem "github-linguist", "~> 4.7.0", require: "linguist"
|
||||
|
||||
# API
|
||||
gem 'grape', '~> 0.6.1'
|
||||
gem 'grape', '~> 0.13.0'
|
||||
gem 'grape-entity', '~> 0.4.2'
|
||||
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
|
||||
|
||||
# Format dates and times
|
||||
# based on human-friendly examples
|
||||
gem "stamp", '~> 0.5.0'
|
||||
gem "stamp", '~> 0.6.0'
|
||||
|
||||
# Enumeration fields
|
||||
gem 'enumerize', '~> 0.7.0'
|
||||
|
@ -112,7 +109,7 @@ group :unicorn do
|
|||
end
|
||||
|
||||
# State machine
|
||||
gem "state_machine", '~> 1.2.0'
|
||||
gem "state_machines-activerecord", '~> 0.3.0'
|
||||
# Run events after state machine commits
|
||||
gem 'after_commit_queue'
|
||||
|
||||
|
@ -184,7 +181,7 @@ gem 'ace-rails-ap', '~> 2.0.1'
|
|||
gem 'mousetrap-rails', '~> 1.4.6'
|
||||
|
||||
# Detect and convert string character encoding
|
||||
gem 'charlock_holmes', '~> 0.6.9.4'
|
||||
gem 'charlock_holmes', '~> 0.7.3'
|
||||
|
||||
gem "sass-rails", '~> 4.0.5'
|
||||
gem "coffee-rails", '~> 4.1.0'
|
||||
|
@ -201,7 +198,7 @@ gem 'jquery-atwho-rails', '~> 1.3.2'
|
|||
gem 'jquery-rails', '~> 3.1.3'
|
||||
gem 'jquery-scrollto-rails', '~> 1.4.3'
|
||||
gem 'jquery-ui-rails', '~> 4.2.1'
|
||||
gem 'nprogress-rails', '~> 0.1.2.3'
|
||||
gem 'nprogress-rails', '~> 0.1.6.7'
|
||||
gem 'raphael-rails', '~> 2.1.2'
|
||||
gem 'request_store', '~> 1.2.0'
|
||||
gem 'select2-rails', '~> 3.5.9'
|
||||
|
@ -214,11 +211,9 @@ group :development do
|
|||
gem "annotate", "~> 2.6.0"
|
||||
gem "letter_opener", '~> 1.1.2'
|
||||
gem 'quiet_assets', '~> 1.0.2'
|
||||
gem 'rack-mini-profiler', '~> 0.9.0', require: false
|
||||
gem 'rerun', '~> 0.10.0'
|
||||
gem 'bullet', require: false
|
||||
gem 'active_record_query_trace', require: false
|
||||
gem 'rack-lineprof', platform: :mri
|
||||
gem 'rblineprof', platform: :mri, require: false
|
||||
|
||||
# Better errors handler
|
||||
gem 'better_errors', '~> 1.0.1'
|
||||
|
@ -264,6 +259,8 @@ group :development, :test do
|
|||
gem 'rubocop', '~> 0.28.0', require: false
|
||||
gem 'coveralls', '~> 0.8.2', require: false
|
||||
gem 'simplecov', '~> 0.10.0', require: false
|
||||
gem 'flog', require: false
|
||||
gem 'flay', require: false
|
||||
|
||||
gem 'benchmark-ips', require: false
|
||||
end
|
||||
|
|
75
Gemfile.lock
75
Gemfile.lock
|
@ -17,7 +17,6 @@ GEM
|
|||
activesupport (= 4.1.12)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
active_record_query_trace (1.5)
|
||||
activemodel (4.1.12)
|
||||
activesupport (= 4.1.12)
|
||||
builder (~> 3.1)
|
||||
|
@ -108,7 +107,7 @@ GEM
|
|||
json (>= 1.7)
|
||||
celluloid (0.16.0)
|
||||
timers (~> 4.0.0)
|
||||
charlock_holmes (0.6.9.4)
|
||||
charlock_holmes (0.7.3)
|
||||
chunky_png (1.3.4)
|
||||
cliver (0.3.2)
|
||||
coderay (1.1.0)
|
||||
|
@ -176,7 +175,7 @@ GEM
|
|||
activesupport (>= 3.2)
|
||||
equalizer (0.0.11)
|
||||
erubis (2.7.0)
|
||||
escape_utils (0.2.4)
|
||||
escape_utils (1.1.0)
|
||||
eventmachine (1.0.8)
|
||||
excon (0.45.4)
|
||||
execjs (2.6.0)
|
||||
|
@ -195,6 +194,12 @@ GEM
|
|||
ffi (1.9.10)
|
||||
fission (0.5.0)
|
||||
CFPropertyList (~> 2.2)
|
||||
flay (2.6.1)
|
||||
ruby_parser (~> 3.0)
|
||||
sexp_processor (~> 4.0)
|
||||
flog (4.3.2)
|
||||
ruby_parser (~> 3.1, > 3.1.0)
|
||||
sexp_processor (~> 4.4)
|
||||
flowdock (0.7.0)
|
||||
httparty (~> 0.7)
|
||||
multi_json
|
||||
|
@ -267,6 +272,11 @@ GEM
|
|||
json
|
||||
get_process_mem (0.2.0)
|
||||
gherkin-ruby (0.3.2)
|
||||
github-linguist (4.7.0)
|
||||
charlock_holmes (~> 0.7.3)
|
||||
escape_utils (~> 1.1.0)
|
||||
mime-types (>= 1.19)
|
||||
rugged (>= 0.23.0b)
|
||||
github-markup (1.3.3)
|
||||
gitlab-flowdock-git-hook (1.0.1)
|
||||
flowdock (~> 0.7)
|
||||
|
@ -277,17 +287,13 @@ GEM
|
|||
diff-lcs (~> 1.1)
|
||||
mime-types (~> 1.15)
|
||||
posix-spawn (~> 0.3)
|
||||
gitlab-linguist (3.0.1)
|
||||
charlock_holmes (~> 0.6.6)
|
||||
escape_utils (~> 0.2.4)
|
||||
mime-types (~> 1.19)
|
||||
gitlab_emoji (0.1.1)
|
||||
gemojione (~> 2.0)
|
||||
gitlab_git (7.2.19)
|
||||
gitlab_git (7.2.20)
|
||||
activesupport (~> 4.0)
|
||||
charlock_holmes (~> 0.6)
|
||||
gitlab-linguist (~> 3.0)
|
||||
rugged (~> 0.22.2)
|
||||
charlock_holmes (~> 0.7.3)
|
||||
github-linguist (~> 4.7.0)
|
||||
rugged (~> 0.23.3)
|
||||
gitlab_meta (7.0)
|
||||
gitlab_omniauth-ldap (1.2.1)
|
||||
net-ldap (~> 0.9)
|
||||
|
@ -306,10 +312,10 @@ GEM
|
|||
gon (5.0.4)
|
||||
actionpack (>= 2.3.0)
|
||||
json
|
||||
grape (0.6.1)
|
||||
grape (0.13.0)
|
||||
activesupport
|
||||
builder
|
||||
hashie (>= 1.2.0)
|
||||
hashie (>= 2.1.0)
|
||||
multi_json (>= 1.3.2)
|
||||
multi_xml (>= 0.5.2)
|
||||
rack (>= 1.3.0)
|
||||
|
@ -406,7 +412,7 @@ GEM
|
|||
newrelic_rpm (3.9.4.245)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.0)
|
||||
nprogress-rails (0.1.2.3)
|
||||
nprogress-rails (0.1.6.7)
|
||||
oauth (0.4.7)
|
||||
oauth2 (1.0.0)
|
||||
faraday (>= 0.8, < 0.10)
|
||||
|
@ -423,6 +429,8 @@ GEM
|
|||
multi_json (~> 1.7)
|
||||
omniauth (~> 1.1)
|
||||
omniauth-oauth (~> 1.0)
|
||||
omniauth-facebook (3.0.0)
|
||||
omniauth-oauth2 (~> 1.2)
|
||||
omniauth-github (1.1.2)
|
||||
omniauth (~> 1.0)
|
||||
omniauth-oauth2 (~> 1.1)
|
||||
|
@ -489,12 +497,6 @@ GEM
|
|||
rack-attack (4.3.0)
|
||||
rack
|
||||
rack-cors (0.4.0)
|
||||
rack-lineprof (0.0.3)
|
||||
rack (~> 1.5)
|
||||
rblineprof (~> 0.3.6)
|
||||
term-ansicolor (~> 1.3)
|
||||
rack-mini-profiler (0.9.7)
|
||||
rack (>= 1.1.3)
|
||||
rack-mount (0.8.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-oauth2 (1.0.10)
|
||||
|
@ -615,7 +617,7 @@ GEM
|
|||
sexp_processor (~> 4.1)
|
||||
rubyntlm (0.5.2)
|
||||
rubypants (0.2.0)
|
||||
rugged (0.22.2)
|
||||
rugged (0.23.3)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (2.1.0)
|
||||
nokogiri (>= 1.4.4)
|
||||
|
@ -689,8 +691,14 @@ GEM
|
|||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
stamp (0.5.0)
|
||||
state_machine (1.2.0)
|
||||
stamp (0.6.0)
|
||||
state_machines (0.4.0)
|
||||
state_machines-activemodel (0.3.0)
|
||||
activemodel (~> 4.1)
|
||||
state_machines (>= 0.4.0)
|
||||
state_machines-activerecord (0.3.0)
|
||||
activerecord (~> 4.1)
|
||||
state_machines-activemodel (>= 0.3.0)
|
||||
stringex (2.5.2)
|
||||
systemu (2.6.5)
|
||||
task_list (1.0.2)
|
||||
|
@ -777,7 +785,6 @@ PLATFORMS
|
|||
DEPENDENCIES
|
||||
RedCloth (~> 4.2.9)
|
||||
ace-rails-ap (~> 2.0.1)
|
||||
active_record_query_trace
|
||||
activerecord-deprecated_finders (~> 1.0.3)
|
||||
activerecord-session_store (~> 0.1.0)
|
||||
acts-as-taggable-on (~> 3.4)
|
||||
|
@ -800,7 +807,7 @@ DEPENDENCIES
|
|||
capybara (~> 2.4.0)
|
||||
capybara-screenshot (~> 1.0.0)
|
||||
carrierwave (~> 0.9.0)
|
||||
charlock_holmes (~> 0.6.9.4)
|
||||
charlock_holmes (~> 0.7.3)
|
||||
coffee-rails (~> 4.1.0)
|
||||
colored (~> 1.2)
|
||||
colorize (~> 0.5.8)
|
||||
|
@ -820,21 +827,23 @@ DEPENDENCIES
|
|||
enumerize (~> 0.7.0)
|
||||
factory_girl_rails (~> 4.3.0)
|
||||
ffaker (~> 2.0.0)
|
||||
flay
|
||||
flog
|
||||
fog (~> 1.25.0)
|
||||
font-awesome-rails (~> 4.2)
|
||||
foreman
|
||||
fuubar (~> 2.0.0)
|
||||
gemnasium-gitlab-service (~> 0.2)
|
||||
github-linguist (~> 4.7.0)
|
||||
github-markup (~> 1.3.1)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab-linguist (~> 3.0.1)
|
||||
gitlab_emoji (~> 0.1)
|
||||
gitlab_git (~> 7.2.19)
|
||||
gitlab_git (~> 7.2.20)
|
||||
gitlab_meta (= 7.0)
|
||||
gitlab_omniauth-ldap (~> 1.2.1)
|
||||
gollum-lib (~> 4.0.2)
|
||||
gon (~> 5.0.0)
|
||||
grape (~> 0.6.1)
|
||||
grape (~> 0.13.0)
|
||||
grape-entity (~> 0.4.2)
|
||||
haml-rails (~> 0.9.0)
|
||||
hipchat (~> 1.5.0)
|
||||
|
@ -854,11 +863,12 @@ DEPENDENCIES
|
|||
nested_form (~> 0.3.2)
|
||||
newrelic-grape
|
||||
newrelic_rpm (~> 3.9.4.245)
|
||||
nprogress-rails (~> 0.1.2.3)
|
||||
nprogress-rails (~> 0.1.6.7)
|
||||
oauth2 (~> 1.0.0)
|
||||
octokit (~> 3.7.0)
|
||||
omniauth (~> 1.2.2)
|
||||
omniauth-bitbucket (~> 0.0.2)
|
||||
omniauth-facebook (~> 3.0.0)
|
||||
omniauth-github (~> 1.1.1)
|
||||
omniauth-gitlab (~> 1.0.0)
|
||||
omniauth-google-oauth2 (~> 0.2.0)
|
||||
|
@ -875,11 +885,10 @@ DEPENDENCIES
|
|||
quiet_assets (~> 1.0.2)
|
||||
rack-attack (~> 4.3.0)
|
||||
rack-cors (~> 0.4.0)
|
||||
rack-lineprof
|
||||
rack-mini-profiler (~> 0.9.0)
|
||||
rack-oauth2 (~> 1.0.5)
|
||||
rails (= 4.1.12)
|
||||
raphael-rails (~> 2.1.2)
|
||||
rblineprof
|
||||
rdoc (~> 3.6)
|
||||
redcarpet (~> 3.3.3)
|
||||
redis-rails (~> 4.0.0)
|
||||
|
@ -909,8 +918,8 @@ DEPENDENCIES
|
|||
spring-commands-spinach (~> 1.0.0)
|
||||
spring-commands-teaspoon (~> 0.0.2)
|
||||
sprockets (~> 2.12.3)
|
||||
stamp (~> 0.5.0)
|
||||
state_machine (~> 1.2.0)
|
||||
stamp (~> 0.6.0)
|
||||
state_machines-activerecord (~> 0.3.0)
|
||||
task_list (~> 1.0.2)
|
||||
teaspoon (~> 1.0.0)
|
||||
teaspoon-jasmine (~> 2.2.0)
|
||||
|
|
16
PROCESS.md
16
PROCESS.md
|
@ -34,13 +34,18 @@ The most important thing is making sure valid issues receive feedback from the d
|
|||
|
||||
## Workflow labels
|
||||
|
||||
Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue.
|
||||
Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue.
|
||||
|
||||
- *Awaiting feedback*: Feedback pending from the reporter
|
||||
- *Awaiting confirmation of fix*: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away)
|
||||
- *Attached MR*: There is a MR attached and the discussion should happen there
|
||||
- We need to let issues stay in sync with the MR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the MR. We can't close the issue when there is a merge request because sometimes a MR is not good and we just close the MR, then the issue must stay.
|
||||
- *Awaiting developer action/feedback*: Issue needs to be fixed or clarified by a developer
|
||||
- *Developer*: needs help from a developer
|
||||
- *UX* needs needs help from a UX designer
|
||||
- *Frontend* needs help from a Front-end engineer
|
||||
- *Graphics* needs help from a Graphics designer
|
||||
|
||||
Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label.
|
||||
|
||||
## Functional labels
|
||||
|
||||
|
@ -115,3 +120,10 @@ We can only accept a merge request if all the tests are green. I've just
|
|||
restarted the build. When the tests are still not passing after this restart and
|
||||
you're sure that is does not have anything to do with your code changes, please
|
||||
rebase with master to see if that solves the issue.
|
||||
|
||||
### Closing down the issue tracker on GitHub
|
||||
|
||||
We are currently in the process of closing down the issue tracker on GitHub, to
|
||||
prevent duplication with the GitLab.com issue tracker.
|
||||
Since this is an older issue I'll be closing this for now. If you think this is
|
||||
still an issue I encourage you to open it on the \[GitLab.com issue tracker\](https://gitlab.com/gitlab-org/gitlab-ce/issues).
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
8.1.0.pre
|
||||
8.3.0.pre
|
||||
|
|
BIN
app/assets/images/auth_buttons/facebook_64.png
Normal file
BIN
app/assets/images/auth_buttons/facebook_64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -11,10 +11,10 @@ class @EditBlob
|
|||
if ace_mode
|
||||
editor.getSession().setMode "ace/mode/" + ace_mode
|
||||
|
||||
$(".js-commit-button").click ->
|
||||
$("#file-content").val editor.getValue()
|
||||
$(".file-editor form").submit()
|
||||
return false
|
||||
# Before a form submission, move the content from the Ace editor into the
|
||||
# submitted textarea
|
||||
$('form').submit ->
|
||||
$("#file-content").val(editor.getValue())
|
||||
|
||||
editModePanes = $(".js-edit-mode-pane")
|
||||
editModeLinks = $(".js-edit-mode a")
|
||||
|
|
|
@ -11,10 +11,10 @@ class @NewBlob
|
|||
if ace_mode
|
||||
editor.getSession().setMode "ace/mode/" + ace_mode
|
||||
|
||||
$(".js-commit-button").click ->
|
||||
$("#file-content").val editor.getValue()
|
||||
$(".file-editor form").submit()
|
||||
return false
|
||||
# Before a form submission, move the content from the Ace editor into the
|
||||
# submitted textarea
|
||||
$('form').submit ->
|
||||
$("#file-content").val(editor.getValue())
|
||||
|
||||
editor: ->
|
||||
return @editor
|
||||
|
|
|
@ -25,7 +25,7 @@ class @Calendar
|
|||
30
|
||||
]
|
||||
legendCellPadding: 3
|
||||
cellSize: $('.user-calendar').width() / 80
|
||||
cellSize: $('.user-calendar').width() / 73
|
||||
onClick: (date, count) ->
|
||||
formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
|
||||
$.ajax
|
||||
|
|
|
@ -28,6 +28,8 @@ class Dispatcher
|
|||
when 'projects:milestones:new', 'projects:milestones:edit'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.milestone-form'))
|
||||
when 'groups:milestones:new'
|
||||
new ZenMode()
|
||||
when 'projects:compare:show'
|
||||
new Diff()
|
||||
when 'projects:issues:new','projects:issues:edit'
|
||||
|
@ -39,6 +41,12 @@ class Dispatcher
|
|||
shortcut_handler = new ShortcutsNavigation()
|
||||
new DropzoneInput($('.merge-request-form'))
|
||||
new IssuableForm($('.merge-request-form'))
|
||||
when 'projects:tags:new'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.tag-form'))
|
||||
when 'projects:releases:edit'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.release-form'))
|
||||
when 'projects:merge_requests:show'
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsIssuable()
|
||||
|
|
|
@ -6,7 +6,7 @@ window.ContributorsStatGraphUtil =
|
|||
for entry in log
|
||||
@add_date(entry.date, total) unless total[entry.date]?
|
||||
|
||||
data = by_author[entry.author_name] #|| by_email[entry.author_email]
|
||||
data = by_author[entry.author_name] || by_email[entry.author_email]
|
||||
data ?= @add_author(entry, by_author, by_email)
|
||||
|
||||
@add_date(entry.date, data) unless data[entry.date]
|
||||
|
@ -95,5 +95,4 @@ window.ContributorsStatGraphUtil =
|
|||
if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
false
|
|
@ -28,6 +28,10 @@
|
|||
border-bottom: 1px solid $border-color;
|
||||
color: $gl-gray;
|
||||
|
||||
&.oneline-block {
|
||||
line-height: 42px;
|
||||
}
|
||||
|
||||
&.white {
|
||||
background-color: white;
|
||||
}
|
||||
|
@ -100,7 +104,7 @@
|
|||
}
|
||||
|
||||
.cover-desc {
|
||||
padding: 0 $gl-padding;
|
||||
padding: 0 $gl-padding 3px;
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
|
|
|
@ -180,3 +180,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-clipboard {
|
||||
border: none;
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
.bs-callout {
|
||||
margin: 20px 0;
|
||||
padding: 20px;
|
||||
border-left: 3px solid #eee;
|
||||
color: #666;
|
||||
background: #f9f9f9;
|
||||
border-left: 3px solid $border-color;
|
||||
color: $text-color;
|
||||
background: $background-color;
|
||||
}
|
||||
.bs-callout h4 {
|
||||
margin-top: 0;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
.append-bottom-10 { margin-bottom:10px }
|
||||
.append-bottom-15 { margin-bottom:15px }
|
||||
.append-bottom-20 { margin-bottom:20px }
|
||||
.append-bottom-default { margin-bottom: $gl-padding; }
|
||||
.inline { display: inline-block }
|
||||
.center { text-align: center }
|
||||
|
||||
|
@ -327,6 +328,10 @@ table {
|
|||
}
|
||||
}
|
||||
|
||||
.well {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.search_box {
|
||||
@extend .well;
|
||||
text-align: center;
|
||||
|
@ -387,6 +392,36 @@ table {
|
|||
}
|
||||
}
|
||||
|
||||
.center-middle-menu {
|
||||
@include nav-menu;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
margin: -$gl-padding;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
height: 58px;
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
li {
|
||||
&:after {
|
||||
content: "|";
|
||||
color: $border-gray-light;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
&:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
> a {
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-progress {
|
||||
border-color: $border-color !important;
|
||||
}
|
||||
|
|
|
@ -118,6 +118,10 @@ header {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.impersonation i {
|
||||
color: $red-normal;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin collapsed-header {
|
||||
|
|
|
@ -5,7 +5,6 @@ html {
|
|||
|
||||
body {
|
||||
padding-top: $header-height;
|
||||
text-rendering: geometricPrecision;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ ul.content-list {
|
|||
}
|
||||
|
||||
.controls {
|
||||
padding-top: 4px;
|
||||
padding-top: 1px;
|
||||
float: right;
|
||||
|
||||
.btn {
|
||||
|
|
|
@ -106,6 +106,7 @@
|
|||
}
|
||||
|
||||
.markdown-area {
|
||||
@include border-radius(0);
|
||||
background: #FFF;
|
||||
border: 1px solid #ddd;
|
||||
min-height: 140px;
|
||||
|
|
|
@ -72,9 +72,10 @@
|
|||
list-style: none;
|
||||
|
||||
> li {
|
||||
@include clearfix;
|
||||
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #EEE;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
margin: 0px;
|
||||
|
||||
|
@ -137,6 +138,7 @@
|
|||
|
||||
&:hover, &:active, &:focus {
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
text-decoration: none;
|
||||
padding-left: 22px;
|
||||
font-weight: normal;
|
||||
outline: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
|
@ -176,6 +177,7 @@
|
|||
text-align: center;
|
||||
line-height: 40px;
|
||||
transition-duration: .3s;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.collapse-nav a:hover {
|
||||
|
@ -238,6 +240,7 @@
|
|||
width: 100%;
|
||||
padding: 10px 22px;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
|
||||
img {
|
||||
width: 36px;
|
||||
|
|
|
@ -172,7 +172,7 @@
|
|||
}
|
||||
|
||||
.panel-body {
|
||||
form {
|
||||
form, pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -173,7 +173,6 @@
|
|||
*
|
||||
*/
|
||||
body {
|
||||
text-rendering:optimizeLegibility;
|
||||
-webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
.autoscroll-container {
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
|||
|
||||
a {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
|
||||
li {
|
||||
padding: 3px 0px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.new-file {
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
}
|
||||
|
||||
li.commit {
|
||||
list-style: none;
|
||||
|
||||
.commit-row-title {
|
||||
font-size: $list-font-size;
|
||||
line-height: 20px;
|
||||
|
@ -113,3 +115,10 @@ li.commit {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.branch-commit {
|
||||
color: $gl-gray;
|
||||
.commit-id, .commit-row-message {
|
||||
color: $gl-gray;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -367,7 +367,6 @@
|
|||
|
||||
.inline-parallel-buttons {
|
||||
float: right;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
// Mobile
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
.editor-file-name {
|
||||
.new-file-name {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
.event-item {
|
||||
font-size: $gl-font-size;
|
||||
padding: $gl-padding;
|
||||
padding: $gl-padding $gl-padding $gl-padding ($gl-padding + $gl-avatar-size + 15px);
|
||||
margin-left: -$gl-padding;
|
||||
margin-right: -$gl-padding;
|
||||
border-bottom: 1px solid $table-border-color;
|
||||
|
@ -16,10 +16,7 @@
|
|||
top: -2px;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.event-title,
|
||||
.event-item-timestamp {
|
||||
line-height: 44px;
|
||||
}
|
||||
|
@ -30,7 +27,7 @@
|
|||
}
|
||||
|
||||
.avatar {
|
||||
margin-right: 15px;
|
||||
margin-left: -($gl-avatar-size + 15px);
|
||||
}
|
||||
|
||||
.event-title {
|
||||
|
@ -43,8 +40,7 @@
|
|||
}
|
||||
|
||||
.event-body {
|
||||
margin-left: 63px;
|
||||
margin-right: 80px;
|
||||
margin-right: 174px;
|
||||
|
||||
.event-note {
|
||||
margin-top: 5px;
|
||||
|
@ -155,6 +151,8 @@
|
|||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.event-item {
|
||||
padding-left: $gl-padding;
|
||||
|
||||
.event-title {
|
||||
white-space: normal;
|
||||
overflow: visible;
|
||||
|
|
|
@ -19,6 +19,20 @@
|
|||
.accept-merge-holder {
|
||||
.accept-action {
|
||||
display: inline-block;
|
||||
|
||||
.accept_merge_request {
|
||||
&.ci-pending,
|
||||
&.ci-running {
|
||||
@include btn-orange;
|
||||
}
|
||||
|
||||
&.ci-skipped,
|
||||
&.ci-failed,
|
||||
&.ci-canceled,
|
||||
&.ci-error {
|
||||
@include btn-red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accept-control {
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
.note_text {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.comment-hints {
|
||||
margin-top: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* loading indicator */
|
||||
|
@ -168,7 +172,7 @@
|
|||
color: #999;
|
||||
background: #FFF;
|
||||
padding: 7px;
|
||||
margin-top: -11px;
|
||||
margin-top: -7px;
|
||||
border: 1px solid $border-color;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
|
|
@ -53,3 +53,25 @@
|
|||
float: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.profile-link-holder {
|
||||
display: inline;
|
||||
|
||||
&:after {
|
||||
content: "\00B7";
|
||||
padding: 0px 6px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
&:after {
|
||||
content: "";
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $blue-dark;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -552,4 +552,4 @@ pre.light-well {
|
|||
z-index: 100;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
33
app/assets/stylesheets/pages/sherlock.scss
Normal file
33
app/assets/stylesheets/pages/sherlock.scss
Normal file
|
@ -0,0 +1,33 @@
|
|||
table .sherlock-code {
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.sherlock-code {
|
||||
pre {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
pre code {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
.sherlock-line-samples-table {
|
||||
margin-bottom: 0px !important;
|
||||
|
||||
thead tr th,
|
||||
tbody tr td {
|
||||
font-size: 13px !important;
|
||||
text-align: right;
|
||||
padding: 0px 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.sherlock-file-sample pre {
|
||||
padding-top: 28px !important;
|
||||
}
|
||||
|
||||
.sherlock-line-samples-table .slow {
|
||||
color: $red-light;
|
||||
font-weight: bold;
|
||||
}
|
|
@ -8,4 +8,10 @@ class Admin::ApplicationController < ApplicationController
|
|||
def authenticate_admin!
|
||||
return render_404 unless current_user.is_admin?
|
||||
end
|
||||
|
||||
def authorize_impersonator!
|
||||
if session[:impersonator_id]
|
||||
User.find_by!(username: session[:impersonator_id]).admin?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -57,6 +57,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
:version_check_enabled,
|
||||
:admin_notification_email,
|
||||
:user_oauth_applications,
|
||||
:shared_runners_enabled,
|
||||
:max_artifacts_size,
|
||||
restricted_visibility_levels: [],
|
||||
import_sources: []
|
||||
)
|
||||
|
|
32
app/controllers/admin/impersonation_controller.rb
Normal file
32
app/controllers/admin/impersonation_controller.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
class Admin::ImpersonationController < Admin::ApplicationController
|
||||
skip_before_action :authenticate_admin!, only: :destroy
|
||||
|
||||
before_action :user
|
||||
before_action :authorize_impersonator!
|
||||
|
||||
def create
|
||||
session[:impersonator_id] = current_user.username
|
||||
session[:impersonator_return_to] = request.env['HTTP_REFERER']
|
||||
|
||||
warden.set_user(user, scope: 'user')
|
||||
|
||||
flash[:alert] = "You are impersonating #{user.username}."
|
||||
|
||||
redirect_to root_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
redirect = session[:impersonator_return_to]
|
||||
|
||||
warden.set_user(user, scope: 'user')
|
||||
|
||||
session[:impersonator_return_to] = nil
|
||||
session[:impersonator_id] = nil
|
||||
|
||||
redirect_to redirect || root_path
|
||||
end
|
||||
|
||||
def user
|
||||
@user ||= User.find_by!(username: params[:id] || session[:impersonator_id])
|
||||
end
|
||||
end
|
|
@ -63,12 +63,6 @@ class Admin::UsersController < Admin::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def login_as
|
||||
sign_in(user)
|
||||
flash[:alert] = "Logged in as #{user.username}"
|
||||
redirect_to root_path
|
||||
end
|
||||
|
||||
def disable_two_factor
|
||||
user.disable_two_factor!
|
||||
redirect_to admin_user_path(user),
|
||||
|
|
|
@ -59,13 +59,8 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def authenticate_user!(*args)
|
||||
# If user is not signed-in and tries to access root_path - redirect him to landing page
|
||||
# Don't redirect to the default URL to prevent endless redirections
|
||||
if current_application_settings.home_page_url.present? &&
|
||||
current_application_settings.home_page_url.chomp('/') != Gitlab.config.gitlab['url'].chomp('/')
|
||||
if current_user.nil? && root_path == request.path
|
||||
redirect_to current_application_settings.home_page_url and return
|
||||
end
|
||||
if redirect_to_home_page_url?
|
||||
redirect_to current_application_settings.home_page_url and return
|
||||
end
|
||||
|
||||
super(*args)
|
||||
|
@ -346,4 +341,17 @@ class ApplicationController < ActionController::Base
|
|||
def git_import_enabled?
|
||||
current_application_settings.import_sources.include?('git')
|
||||
end
|
||||
|
||||
def redirect_to_home_page_url?
|
||||
# If user is not signed-in and tries to access root_path - redirect him to landing page
|
||||
# Don't redirect to the default URL to prevent endless redirections
|
||||
return false unless current_application_settings.home_page_url.present?
|
||||
|
||||
home_page_url = current_application_settings.home_page_url.chomp('/')
|
||||
root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
|
||||
|
||||
return false if root_urls.include?(home_page_url)
|
||||
|
||||
current_user.nil? && root_path == request.path
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
module Ci
|
||||
class EventsController < Ci::ApplicationController
|
||||
EVENTS_PER_PAGE = 50
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :project
|
||||
before_action :authorize_manage_project!
|
||||
|
||||
layout 'ci/project'
|
||||
|
||||
def index
|
||||
@events = project.events.order("created_at DESC").page(params[:page]).per(EVENTS_PER_PAGE)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def project
|
||||
@project ||= Ci::Project.find(params[:project_id])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -26,10 +26,6 @@ module Ci
|
|||
redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project)
|
||||
end
|
||||
|
||||
def dumped_yaml
|
||||
send_data @project.generated_yaml_config, filename: '.gitlab-ci.yml'
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def project
|
||||
|
|
|
@ -4,8 +4,6 @@ module Ci
|
|||
before_action :project
|
||||
before_action :authorize_manage_project!
|
||||
|
||||
layout 'ci/project'
|
||||
|
||||
def create
|
||||
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
||||
|
||||
|
|
19
app/controllers/concerns/global_milestones.rb
Normal file
19
app/controllers/concerns/global_milestones.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
module GlobalMilestones
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def milestones
|
||||
@milestones = MilestonesFinder.new.execute(@projects, params)
|
||||
@milestones = GlobalMilestone.build_collection(@milestones)
|
||||
@milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE)
|
||||
end
|
||||
|
||||
def milestone
|
||||
milestones = Milestone.of_projects(@projects).where(title: params[:title])
|
||||
|
||||
if milestones.present?
|
||||
@milestone = GlobalMilestone.new(params[:title], milestones)
|
||||
else
|
||||
render_404
|
||||
end
|
||||
end
|
||||
end
|
14
app/controllers/concerns/issues_action.rb
Normal file
14
app/controllers/concerns/issues_action.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
module IssuesAction
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def issues
|
||||
@issues = get_issues_collection
|
||||
@issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE)
|
||||
@issues = @issues.preload(:author, :project)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.atom { render layout: false }
|
||||
end
|
||||
end
|
||||
end
|
9
app/controllers/concerns/merge_requests_action.rb
Normal file
9
app/controllers/concerns/merge_requests_action.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
module MergeRequestsAction
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def merge_requests
|
||||
@merge_requests = get_merge_requests_collection
|
||||
@merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE)
|
||||
@merge_requests = @merge_requests.preload(:author, :target_project)
|
||||
end
|
||||
end
|
|
@ -1,34 +1,19 @@
|
|||
class Dashboard::MilestonesController < Dashboard::ApplicationController
|
||||
before_action :load_projects
|
||||
include GlobalMilestones
|
||||
|
||||
before_action :projects
|
||||
before_action :milestones, only: [:index]
|
||||
before_action :milestone, only: [:show]
|
||||
|
||||
def index
|
||||
project_milestones = case params[:state]
|
||||
when 'all'; state
|
||||
when 'closed'; state('closed')
|
||||
else state('active')
|
||||
end
|
||||
@dashboard_milestones = Milestones::GroupService.new(project_milestones).execute
|
||||
@dashboard_milestones = Kaminari.paginate_array(@dashboard_milestones).page(params[:page]).per(PER_PAGE)
|
||||
end
|
||||
|
||||
def show
|
||||
project_milestones = Milestone.where(project_id: @projects).order("due_date ASC")
|
||||
@dashboard_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_projects
|
||||
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
|
||||
end
|
||||
|
||||
def title
|
||||
params[:title]
|
||||
end
|
||||
|
||||
def state(state = nil)
|
||||
conditions = { project_id: @projects }
|
||||
conditions.reverse_merge!(state: state) if state
|
||||
Milestone.where(conditions).order("title ASC")
|
||||
def projects
|
||||
@projects ||= current_user.authorized_projects.sorted_by_activity.non_archived
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,25 +1,12 @@
|
|||
class DashboardController < Dashboard::ApplicationController
|
||||
include IssuesAction
|
||||
include MergeRequestsAction
|
||||
|
||||
before_action :event_filter, only: :activity
|
||||
before_action :projects, only: [:issues, :merge_requests]
|
||||
|
||||
respond_to :html
|
||||
|
||||
def merge_requests
|
||||
@merge_requests = get_merge_requests_collection
|
||||
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
|
||||
@merge_requests = @merge_requests.preload(:author, :target_project)
|
||||
end
|
||||
|
||||
def issues
|
||||
@issues = get_issues_collection
|
||||
@issues = @issues.page(params[:page]).per(PER_PAGE)
|
||||
@issues = @issues.preload(:author, :project)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.atom { render layout: false }
|
||||
end
|
||||
end
|
||||
|
||||
def activity
|
||||
@last_push = current_user.recent_push
|
||||
|
||||
|
@ -47,4 +34,8 @@ class DashboardController < Dashboard::ApplicationController
|
|||
@events = @event_filter.apply_filter(@events).with_associations
|
||||
@events = @events.limit(20).offset(params[:offset] || 0)
|
||||
end
|
||||
|
||||
def projects
|
||||
@projects ||= current_user.authorized_projects.sorted_by_activity.non_archived
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
class Groups::ApplicationController < ApplicationController
|
||||
layout 'group'
|
||||
before_action :group
|
||||
|
||||
private
|
||||
|
||||
|
||||
def group
|
||||
@group ||= Group.find_by(path: params[:group_id])
|
||||
end
|
||||
|
||||
def authorize_read_group!
|
||||
unless @group and can?(current_user, :read_group, @group)
|
||||
if current_user.nil?
|
||||
|
@ -12,13 +17,13 @@ class Groups::ApplicationController < ApplicationController
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def authorize_admin_group!
|
||||
unless can?(current_user, :admin_group, group)
|
||||
return render_404
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def authorize_admin_group_member!
|
||||
unless can?(current_user, :admin_group_member, group)
|
||||
return render_403
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
class Groups::AvatarsController < ApplicationController
|
||||
class Groups::AvatarsController < Groups::ApplicationController
|
||||
def destroy
|
||||
@group = Group.find_by(path: params[:group_id])
|
||||
@group.remove_avatar!
|
||||
|
||||
@group.save
|
||||
|
||||
redirect_to edit_group_path(@group)
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
class Groups::GroupMembersController < Groups::ApplicationController
|
||||
skip_before_action :authenticate_user!, only: [:index]
|
||||
before_action :group
|
||||
|
||||
# Authorize
|
||||
before_action :authorize_read_group!
|
||||
before_action :authorize_admin_group!, except: [:index, :leave]
|
||||
before_action :authorize_admin_group_member!, only: [:create, :resend_invite]
|
||||
before_action :authorize_admin_group_member!, except: [:index, :leave]
|
||||
|
||||
def index
|
||||
@project = @group.projects.find(params[:project_id]) if params[:project_id]
|
||||
|
@ -18,7 +16,8 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
@members = @members.order('access_level DESC').page(params[:page]).per(50)
|
||||
@group_member = GroupMember.new
|
||||
|
||||
@group_member = @group.group_members.new
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -28,24 +27,23 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
@member = @group.group_members.find(params[:id])
|
||||
@group_member = @group.group_members.find(params[:id])
|
||||
|
||||
return render_403 unless can?(current_user, :update_group_member, @member)
|
||||
return render_403 unless can?(current_user, :update_group_member, @group_member)
|
||||
|
||||
@member.update_attributes(member_params)
|
||||
@group_member.update_attributes(member_params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@group_member = @group.group_members.find(params[:id])
|
||||
|
||||
if can?(current_user, :destroy_group_member, @group_member) # May fail if last owner.
|
||||
@group_member.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
|
||||
format.js { render nothing: true }
|
||||
end
|
||||
else
|
||||
return render_403
|
||||
return render_403 unless can?(current_user, :destroy_group_member, @group_member)
|
||||
|
||||
@group_member.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
|
||||
format.js { render nothing: true }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -64,10 +62,11 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def leave
|
||||
@group_member = @group.group_members.where(user_id: current_user.id).first
|
||||
@group_member = @group.group_members.find_by(user_id: current_user)
|
||||
|
||||
if can?(current_user, :destroy_group_member, @group_member)
|
||||
@group_member.destroy
|
||||
|
||||
redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.")
|
||||
else
|
||||
if @group.last_owner?(current_user)
|
||||
|
@ -80,10 +79,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
|
||||
protected
|
||||
|
||||
def group
|
||||
@group ||= Group.find_by(path: params[:group_id])
|
||||
end
|
||||
|
||||
def member_params
|
||||
params.require(:group_member).permit(:access_level, :user_id)
|
||||
end
|
||||
|
|
|
@ -1,54 +1,55 @@
|
|||
class Groups::MilestonesController < Groups::ApplicationController
|
||||
before_action :authorize_group_milestone!, only: :update
|
||||
include GlobalMilestones
|
||||
|
||||
before_action :projects
|
||||
before_action :milestones, only: [:index]
|
||||
before_action :milestone, only: [:show, :update]
|
||||
before_action :authorize_group_milestone!, only: [:create, :update]
|
||||
|
||||
def index
|
||||
project_milestones = case params[:state]
|
||||
when 'all'; state
|
||||
when 'closed'; state('closed')
|
||||
else state('active')
|
||||
end
|
||||
@group_milestones = Milestones::GroupService.new(project_milestones).execute
|
||||
@group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(PER_PAGE)
|
||||
end
|
||||
|
||||
def new
|
||||
@milestone = Milestone.new
|
||||
end
|
||||
|
||||
def create
|
||||
project_ids = params[:milestone][:project_ids]
|
||||
title = milestone_params[:title]
|
||||
|
||||
@group.projects.where(id: project_ids).each do |project|
|
||||
Milestones::CreateService.new(project, current_user, milestone_params).execute
|
||||
end
|
||||
|
||||
redirect_to milestone_path(title)
|
||||
end
|
||||
|
||||
def show
|
||||
project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
|
||||
@group_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
|
||||
end
|
||||
|
||||
def update
|
||||
project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
|
||||
@group_milestones = Milestones::GroupService.new(project_milestones).milestone(title)
|
||||
|
||||
@group_milestones.milestones.each do |milestone|
|
||||
Milestones::UpdateService.new(milestone.project, current_user, params[:milestone]).execute(milestone)
|
||||
@milestone.milestones.each do |milestone|
|
||||
Milestones::UpdateService.new(milestone.project, current_user, milestone_params).execute(milestone)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.html do
|
||||
redirect_to group_milestones_path(group)
|
||||
end
|
||||
end
|
||||
redirect_back_or_default(default: milestone_path(@milestone.title))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def group
|
||||
@group ||= Group.find_by(path: params[:group_id])
|
||||
end
|
||||
|
||||
def title
|
||||
params[:title]
|
||||
end
|
||||
|
||||
def state(state = nil)
|
||||
conditions = { project_id: group.projects }
|
||||
conditions.reverse_merge!(state: state) if state
|
||||
Milestone.where(conditions).order("title ASC")
|
||||
end
|
||||
|
||||
def authorize_group_milestone!
|
||||
return render_404 unless can?(current_user, :admin_group, group)
|
||||
return render_404 unless can?(current_user, :admin_milestones, group)
|
||||
end
|
||||
|
||||
def milestone_params
|
||||
params.require(:milestone).permit(:title, :description, :due_date, :state_event)
|
||||
end
|
||||
|
||||
def milestone_path(title)
|
||||
group_milestone_path(@group, title.parameterize, title: title)
|
||||
end
|
||||
|
||||
def projects
|
||||
@projects ||= @group.projects
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
class GroupsController < Groups::ApplicationController
|
||||
include IssuesAction
|
||||
include MergeRequestsAction
|
||||
|
||||
skip_before_action :authenticate_user!, only: [:show, :issues, :merge_requests]
|
||||
respond_to :html
|
||||
before_action :group, except: [:new, :create]
|
||||
|
||||
# Authorize
|
||||
before_action :authorize_read_group!, except: [:show, :new, :create]
|
||||
before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete]
|
||||
before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects]
|
||||
before_action :authorize_create_group!, only: [:new, :create]
|
||||
|
||||
# Load group projects
|
||||
before_action :load_projects, except: [:new, :create, :projects, :edit, :update]
|
||||
before_action :load_projects, except: [:new, :create, :projects, :edit, :update, :autocomplete]
|
||||
before_action :event_filter, only: :show
|
||||
|
||||
layout :determine_layout
|
||||
|
@ -53,23 +56,6 @@ class GroupsController < Groups::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def merge_requests
|
||||
@merge_requests = get_merge_requests_collection
|
||||
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
|
||||
@merge_requests = @merge_requests.preload(:author, :target_project)
|
||||
end
|
||||
|
||||
def issues
|
||||
@issues = get_issues_collection
|
||||
@issues = @issues.page(params[:page]).per(PER_PAGE)
|
||||
@issues = @issues.preload(:author, :project)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.atom { render layout: false }
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
|
@ -133,7 +119,7 @@ class GroupsController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def group_params
|
||||
params.require(:group).permit(:name, :description, :path, :avatar)
|
||||
params.require(:group).permit(:name, :description, :path, :avatar, :public)
|
||||
end
|
||||
|
||||
def load_events
|
||||
|
|
|
@ -29,7 +29,7 @@ class Projects::ApplicationController < ApplicationController
|
|||
private
|
||||
|
||||
def ci_enabled
|
||||
return render_404 unless @project.gitlab_ci?
|
||||
return render_404 unless @project.builds_enabled?
|
||||
end
|
||||
|
||||
def ci_project
|
||||
|
|
|
@ -161,7 +161,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
if params[:file].present?
|
||||
params[:file_name] = params[:file].original_filename
|
||||
end
|
||||
File.join(@path, File.basename(params[:file_name]))
|
||||
File.join(@path, params[:file_name])
|
||||
else
|
||||
@path
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@ class Projects::BuildsController < Projects::ApplicationController
|
|||
before_action :build, except: [:index, :cancel_all]
|
||||
|
||||
before_action :authorize_manage_builds!, except: [:index, :show, :status]
|
||||
before_action :authorize_download_build_artifacts!, only: [:download]
|
||||
|
||||
layout "project"
|
||||
|
||||
|
@ -30,7 +31,7 @@ class Projects::BuildsController < Projects::ApplicationController
|
|||
|
||||
def show
|
||||
@builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC')
|
||||
@builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20)
|
||||
@builds = @builds.where("id not in (?)", @build.id)
|
||||
@commit = @build.commit
|
||||
|
||||
respond_to do |format|
|
||||
|
@ -42,17 +43,25 @@ class Projects::BuildsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def retry
|
||||
if @build.commands.blank?
|
||||
unless @build.retryable?
|
||||
return page_404
|
||||
end
|
||||
|
||||
build = Ci::Build.retry(@build)
|
||||
|
||||
if params[:return_to]
|
||||
redirect_to URI.parse(params[:return_to]).path
|
||||
else
|
||||
redirect_to build_path(build)
|
||||
redirect_to build_path(build)
|
||||
end
|
||||
|
||||
def download
|
||||
unless artifacts_file.file_storage?
|
||||
return redirect_to artifacts_file.url
|
||||
end
|
||||
|
||||
unless artifacts_file.exists?
|
||||
return not_found!
|
||||
end
|
||||
|
||||
send_file artifacts_file.path, disposition: 'attachment'
|
||||
end
|
||||
|
||||
def status
|
||||
|
@ -71,6 +80,10 @@ class Projects::BuildsController < Projects::ApplicationController
|
|||
@build ||= ci_project.builds.unscoped.find_by!(id: params[:id])
|
||||
end
|
||||
|
||||
def artifacts_file
|
||||
build.artifacts_file
|
||||
end
|
||||
|
||||
def build_path(build)
|
||||
namespace_project_build_path(build.gl_project.namespace, build.gl_project, build)
|
||||
end
|
||||
|
@ -80,4 +93,14 @@ class Projects::BuildsController < Projects::ApplicationController
|
|||
return page_404
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_download_build_artifacts!
|
||||
unless can?(current_user, :download_build_artifacts, @project)
|
||||
if current_user.nil?
|
||||
return authenticate_user!
|
||||
else
|
||||
return render_404
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,14 +7,14 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
before_action :authorize_download_code!, except: [:cancel_builds]
|
||||
before_action :authorize_manage_builds!, only: [:cancel_builds]
|
||||
before_action :commit
|
||||
before_action :authorize_manage_builds!, only: [:cancel_builds, :retry_builds]
|
||||
before_action :define_show_vars, only: [:show, :builds]
|
||||
|
||||
def show
|
||||
return git_not_found! unless @commit
|
||||
|
||||
@line_notes = commit.notes.inline
|
||||
@diffs = @commit.diffs
|
||||
@note = @project.build_commit_note(commit)
|
||||
@notes_count = commit.notes.count
|
||||
@notes = commit.notes.not_inline.fresh
|
||||
@noteable = @commit
|
||||
@comments_allowed = @reply_allowed = true
|
||||
|
@ -23,8 +23,6 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
commit_id: @commit.id
|
||||
}
|
||||
|
||||
@ci_commit = project.ci_commit(commit.sha)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.diff { render text: @commit.to_diff }
|
||||
|
@ -32,20 +30,25 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def ci
|
||||
@ci_commit = @project.ci_commit(@commit.sha)
|
||||
@builds = @ci_commit.builds if @ci_commit
|
||||
@notes_count = @commit.notes.count
|
||||
def builds
|
||||
@ci_project = @project.gitlab_ci_project
|
||||
end
|
||||
|
||||
def cancel_builds
|
||||
@ci_commit = @project.ci_commit(@commit.sha)
|
||||
@ci_commit.builds.running_or_pending.each(&:cancel)
|
||||
ci_commit.builds.running_or_pending.each(&:cancel)
|
||||
|
||||
redirect_to ci_namespace_project_commit_path(project.namespace, project, commit.sha)
|
||||
redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
|
||||
end
|
||||
|
||||
def retry_builds
|
||||
ci_commit.builds.latest.failed.each do |build|
|
||||
if build.retryable?
|
||||
Ci::Build.retry(build)
|
||||
end
|
||||
end
|
||||
|
||||
redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
|
||||
end
|
||||
|
||||
def branches
|
||||
@branches = @project.repository.branch_names_contains(commit.id)
|
||||
|
@ -53,11 +56,22 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
render layout: false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def commit
|
||||
@commit ||= @project.commit(params[:id])
|
||||
end
|
||||
|
||||
private
|
||||
def ci_commit
|
||||
@ci_commit ||= project.ci_commit(commit.sha)
|
||||
end
|
||||
|
||||
def define_show_vars
|
||||
@diffs = commit.diffs
|
||||
@notes_count = commit.notes.count
|
||||
|
||||
@builds = ci_commit.builds if ci_commit
|
||||
end
|
||||
|
||||
def authorize_manage_builds!
|
||||
unless can?(current_user, :manage_builds, project)
|
||||
|
|
|
@ -12,15 +12,16 @@ class Projects::CompareController < Projects::ApplicationController
|
|||
def show
|
||||
base_ref = Addressable::URI.unescape(params[:from])
|
||||
@ref = head_ref = Addressable::URI.unescape(params[:to])
|
||||
diff_options = { ignore_whitespace_change: true } if params[:w] == '1'
|
||||
|
||||
compare_result = CompareService.new.
|
||||
execute(@project, head_ref, @project, base_ref)
|
||||
execute(@project, head_ref, @project, base_ref, diff_options)
|
||||
|
||||
if compare_result
|
||||
@commits = Commit.decorate(compare_result.commits, @project)
|
||||
@diffs = compare_result.diffs
|
||||
@commit = @commits.last
|
||||
@first_commit = @commits.first
|
||||
@commit = @project.commit(head_ref)
|
||||
@first_commit = @project.commit(base_ref)
|
||||
@line_notes = []
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,8 +28,8 @@ class Projects::ImportsController < Projects::ApplicationController
|
|||
if @project.import_finished?
|
||||
redirect_to(project_path(@project)) and return
|
||||
else
|
||||
redirect_to new_namespace_project_import_path(@project.namespace,
|
||||
@project) && return
|
||||
redirect_to(new_namespace_project_import_path(@project.namespace,
|
||||
@project)) and return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -158,10 +158,12 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def issue_params
|
||||
params.require(:issue).permit(
|
||||
permitted = params.require(:issue).permit(
|
||||
:title, :assignee_id, :position, :description,
|
||||
:milestone_id, :state_event, :task_num, label_ids: []
|
||||
)
|
||||
params[:issue][:title].strip! if params[:issue][:title]
|
||||
permitted
|
||||
end
|
||||
|
||||
def bulk_update_params
|
||||
|
|
|
@ -31,6 +31,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
|
||||
@merge_requests = @merge_requests.preload(:target_project)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
|
@ -275,11 +276,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def merge_request_params
|
||||
params.require(:merge_request).permit(
|
||||
permitted = params.require(:merge_request).permit(
|
||||
:title, :assignee_id, :source_project_id, :source_branch,
|
||||
:target_project_id, :target_branch, :milestone_id,
|
||||
:state_event, :description, :task_num, label_ids: []
|
||||
)
|
||||
params[:merge_request][:title].strip! if params[:merge_request][:title]
|
||||
permitted
|
||||
end
|
||||
|
||||
# Make sure merge requests created before 8.0
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class Projects::ProjectMembersController < Projects::ApplicationController
|
||||
# Authorize
|
||||
before_action :authorize_admin_project!, except: :leave
|
||||
before_action :authorize_admin_project_member!, except: :leave
|
||||
|
||||
def index
|
||||
@project_members = @project.project_members
|
||||
|
@ -29,10 +29,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
|||
@project_member = @project.project_members.new
|
||||
end
|
||||
|
||||
def new
|
||||
@project_member = @project.project_members.new
|
||||
end
|
||||
|
||||
def create
|
||||
@project.team.add_users(params[:user_ids].split(','), params[:access_level], current_user)
|
||||
|
||||
|
@ -41,11 +37,17 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
|||
|
||||
def update
|
||||
@project_member = @project.project_members.find(params[:id])
|
||||
|
||||
return render_403 unless can?(current_user, :update_project_member, @project_member)
|
||||
|
||||
@project_member.update_attributes(member_params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@project_member = @project.project_members.find(params[:id])
|
||||
|
||||
return render_403 unless can?(current_user, :destroy_project_member, @project_member)
|
||||
|
||||
@project_member.destroy
|
||||
|
||||
respond_to do |format|
|
||||
|
@ -71,16 +73,22 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def leave
|
||||
if @project.namespace == current_user.namespace
|
||||
message = 'You can not leave your own project. Transfer or delete the project.'
|
||||
return redirect_back_or_default(default: { action: 'index' }, options: { alert: message })
|
||||
end
|
||||
@project_member = @project.project_members.find_by(user_id: current_user)
|
||||
|
||||
@project.project_members.find_by(user_id: current_user).destroy
|
||||
if can?(current_user, :destroy_project_member, @project_member)
|
||||
@project_member.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to dashboard_projects_path }
|
||||
format.js { render nothing: true }
|
||||
respond_to do |format|
|
||||
format.html { redirect_to dashboard_projects_path, notice: "You left the project." }
|
||||
format.js { render nothing: true }
|
||||
end
|
||||
else
|
||||
if current_user == @project.owner
|
||||
message = 'You can not leave your own project. Transfer or delete the project.'
|
||||
redirect_back_or_default(default: { action: 'index' }, options: { alert: message })
|
||||
else
|
||||
render_403
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
31
app/controllers/projects/releases_controller.rb
Normal file
31
app/controllers/projects/releases_controller.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
class Projects::ReleasesController < Projects::ApplicationController
|
||||
# Authorize
|
||||
before_action :require_non_empty_project
|
||||
before_action :authorize_download_code!
|
||||
before_action :authorize_push_code!
|
||||
before_action :tag
|
||||
before_action :release
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def update
|
||||
release.update_attributes(release_params)
|
||||
|
||||
redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag
|
||||
@tag ||= @repository.find_tag(params[:tag_id])
|
||||
end
|
||||
|
||||
def release
|
||||
@release ||= @project.releases.find_or_initialize_by(tag: @tag.name)
|
||||
end
|
||||
|
||||
def release_params
|
||||
params.require(:release).permit(:description)
|
||||
end
|
||||
end
|
|
@ -8,15 +8,23 @@ class Projects::TagsController < Projects::ApplicationController
|
|||
def index
|
||||
sorted = VersionSorter.rsort(@repository.tag_names)
|
||||
@tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE)
|
||||
@releases = project.releases.where(tag: @tags)
|
||||
end
|
||||
|
||||
def show
|
||||
@tag = @repository.find_tag(params[:id])
|
||||
@release = @project.releases.find_or_initialize_by(tag: @tag.name)
|
||||
@commit = @repository.commit(@tag.target)
|
||||
end
|
||||
|
||||
def create
|
||||
result = CreateTagService.new(@project, current_user).
|
||||
execute(params[:tag_name], params[:ref], params[:message])
|
||||
execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
|
||||
|
||||
if result[:status] == :success
|
||||
@tag = result[:tag]
|
||||
redirect_to namespace_project_tags_path(@project.namespace, @project)
|
||||
|
||||
redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name)
|
||||
else
|
||||
@error = result[:message]
|
||||
render action: 'new'
|
||||
|
@ -26,12 +34,6 @@ class Projects::TagsController < Projects::ApplicationController
|
|||
def destroy
|
||||
DeleteTagService.new(project, current_user).execute(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to namespace_project_tags_path(@project.namespace,
|
||||
@project)
|
||||
end
|
||||
format.js
|
||||
end
|
||||
redirect_to namespace_project_tags_path(@project.namespace, @project)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class ProjectsController < ApplicationController
|
||||
include ExtractsPath
|
||||
|
||||
prepend_before_filter :render_go_import, only: [:show]
|
||||
prepend_before_action :render_go_import, only: [:show]
|
||||
skip_before_action :authenticate_user!, only: [:show, :activity]
|
||||
before_action :project, except: [:new, :create]
|
||||
before_action :repository, except: [:new, :create]
|
||||
|
@ -72,8 +72,7 @@ class ProjectsController < ApplicationController
|
|||
def remove_fork
|
||||
return access_denied! unless can?(current_user, :remove_fork_project, @project)
|
||||
|
||||
if @project.forked?
|
||||
@project.forked_project_link.destroy
|
||||
if @project.unlink_fork
|
||||
flash[:notice] = 'The fork relationship has been removed.'
|
||||
end
|
||||
end
|
||||
|
@ -213,7 +212,8 @@ class ProjectsController < ApplicationController
|
|||
params.require(:project).permit(
|
||||
:name, :path, :description, :issues_tracker, :tag_list,
|
||||
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
|
||||
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar
|
||||
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
|
||||
:builds_enabled
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -242,7 +242,7 @@ class ProjectsController < ApplicationController
|
|||
project.repository_exists? && !project.empty_repo?
|
||||
end
|
||||
|
||||
# Override get_id from ExtractsPath, which returns the branch and file path
|
||||
# Override get_id from ExtractsPath, which returns the branch and file path
|
||||
# for the blob/tree, which in this case is just the root of the default branch.
|
||||
def get_id
|
||||
project.repository.root_ref
|
||||
|
|
|
@ -23,8 +23,8 @@ class SearchController < ApplicationController
|
|||
|
||||
@search_results =
|
||||
if @project
|
||||
unless %w(blobs notes issues merge_requests milestones wiki_blobs).
|
||||
include?(@scope)
|
||||
unless %w(blobs notes issues merge_requests milestones wiki_blobs
|
||||
commits).include?(@scope)
|
||||
@scope = 'blobs'
|
||||
end
|
||||
|
||||
|
|
12
app/controllers/sherlock/application_controller.rb
Normal file
12
app/controllers/sherlock/application_controller.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
module Sherlock
|
||||
class ApplicationController < ::ApplicationController
|
||||
before_action :find_transaction
|
||||
|
||||
def find_transaction
|
||||
if params[:transaction_id]
|
||||
@transaction = Gitlab::Sherlock.collection.
|
||||
find_transaction(params[:transaction_id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
7
app/controllers/sherlock/file_samples_controller.rb
Normal file
7
app/controllers/sherlock/file_samples_controller.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
module Sherlock
|
||||
class FileSamplesController < Sherlock::ApplicationController
|
||||
def show
|
||||
@file_sample = @transaction.find_file_sample(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
7
app/controllers/sherlock/queries_controller.rb
Normal file
7
app/controllers/sherlock/queries_controller.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
module Sherlock
|
||||
class QueriesController < Sherlock::ApplicationController
|
||||
def show
|
||||
@query = @transaction.find_query(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
19
app/controllers/sherlock/transactions_controller.rb
Normal file
19
app/controllers/sherlock/transactions_controller.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
module Sherlock
|
||||
class TransactionsController < Sherlock::ApplicationController
|
||||
def index
|
||||
@transactions = Gitlab::Sherlock.collection.newest_first
|
||||
end
|
||||
|
||||
def show
|
||||
@transaction = Gitlab::Sherlock.collection.find_transaction(params[:id])
|
||||
|
||||
render_404 unless @transaction
|
||||
end
|
||||
|
||||
def destroy_all
|
||||
Gitlab::Sherlock.collection.clear
|
||||
|
||||
redirect_to(:back)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,14 +3,11 @@ class UsersController < ApplicationController
|
|||
before_action :set_user
|
||||
|
||||
def show
|
||||
@contributed_projects = contributed_projects.joined(@user).
|
||||
reject(&:forked?)
|
||||
@contributed_projects = contributed_projects.joined(@user).reject(&:forked?)
|
||||
|
||||
@projects = @user.personal_projects.
|
||||
where(id: authorized_projects_ids).includes(:namespace)
|
||||
@projects = PersonalProjectsFinder.new(@user).execute(current_user)
|
||||
|
||||
# Collect only groups common for both users
|
||||
@groups = @user.groups & GroupsFinder.new.execute(current_user)
|
||||
@groups = JoinedGroupsFinder.new(@user).execute(current_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
|
@ -53,16 +50,8 @@ class UsersController < ApplicationController
|
|||
@user = User.find_by_username!(params[:username])
|
||||
end
|
||||
|
||||
def authorized_projects_ids
|
||||
# Projects user can view
|
||||
@authorized_projects_ids ||=
|
||||
ProjectsFinder.new.execute(current_user).pluck(:id)
|
||||
end
|
||||
|
||||
def contributed_projects
|
||||
@contributed_projects = Project.
|
||||
where(id: authorized_projects_ids & @user.contributed_projects_ids).
|
||||
includes(:namespace)
|
||||
ContributedProjectsFinder.new(@user).execute(current_user)
|
||||
end
|
||||
|
||||
def contributions_calendar
|
||||
|
@ -73,9 +62,13 @@ class UsersController < ApplicationController
|
|||
def load_events
|
||||
# Get user activity feed for projects common for both users
|
||||
@events = @user.recent_events.
|
||||
where(project_id: authorized_projects_ids).
|
||||
with_associations
|
||||
merge(projects_for_current_user).
|
||||
references(:project).
|
||||
with_associations.
|
||||
limit_recent(20, params[:offset])
|
||||
end
|
||||
|
||||
@events = @events.limit(20).offset(params[:offset] || 0)
|
||||
def projects_for_current_user
|
||||
ProjectsFinder.new.execute(current_user)
|
||||
end
|
||||
end
|
||||
|
|
37
app/finders/contributed_projects_finder.rb
Normal file
37
app/finders/contributed_projects_finder.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
class ContributedProjectsFinder
|
||||
def initialize(user)
|
||||
@user = user
|
||||
end
|
||||
|
||||
# Finds the projects "@user" contributed to, limited to either public projects
|
||||
# or projects visible to the given user.
|
||||
#
|
||||
# current_user - When given the list of the projects is limited to those only
|
||||
# visible by this user.
|
||||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def execute(current_user = nil)
|
||||
if current_user
|
||||
relation = projects_visible_to_user(current_user)
|
||||
else
|
||||
relation = public_projects
|
||||
end
|
||||
|
||||
relation.includes(:namespace).order_id_desc
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def projects_visible_to_user(current_user)
|
||||
authorized = @user.contributed_projects.visible_to_user(current_user)
|
||||
|
||||
union = Gitlab::SQL::Union.
|
||||
new([authorized.select(:id), public_projects.select(:id)])
|
||||
|
||||
Project.where("projects.id IN (#{union.to_sql})")
|
||||
end
|
||||
|
||||
def public_projects
|
||||
@user.contributed_projects.public_only
|
||||
end
|
||||
end
|
|
@ -1,38 +1,44 @@
|
|||
class GroupsFinder
|
||||
def execute(current_user, options = {})
|
||||
all_groups(current_user)
|
||||
# Finds the groups available to the given user.
|
||||
#
|
||||
# current_user - The user to find the groups for.
|
||||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def execute(current_user = nil)
|
||||
if current_user
|
||||
relation = groups_visible_to_user(current_user)
|
||||
else
|
||||
relation = public_groups
|
||||
end
|
||||
|
||||
relation.order_id_desc
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def all_groups(current_user)
|
||||
if current_user
|
||||
if current_user.authorized_groups.any?
|
||||
# User has access to groups
|
||||
#
|
||||
# Return only:
|
||||
# groups with public projects
|
||||
# groups with internal projects
|
||||
# groups with joined projects
|
||||
#
|
||||
group_ids = Project.public_and_internal_only.pluck(:namespace_id) +
|
||||
current_user.authorized_groups.pluck(:id)
|
||||
Group.where(id: group_ids)
|
||||
else
|
||||
# User has no group membership
|
||||
#
|
||||
# Return only:
|
||||
# groups with public projects
|
||||
# groups with internal projects
|
||||
#
|
||||
Group.where(id: Project.public_and_internal_only.pluck(:namespace_id))
|
||||
end
|
||||
else
|
||||
# Not authenticated
|
||||
#
|
||||
# Return only:
|
||||
# groups with public projects
|
||||
Group.where(id: Project.public_only.pluck(:namespace_id))
|
||||
end
|
||||
# This method returns the groups "current_user" can see.
|
||||
def groups_visible_to_user(current_user)
|
||||
base = groups_for_projects(public_and_internal_projects)
|
||||
|
||||
union = Gitlab::SQL::Union.
|
||||
new([base.select(:id), current_user.authorized_groups.select(:id)])
|
||||
|
||||
Group.where("namespaces.id IN (#{union.to_sql})")
|
||||
end
|
||||
|
||||
def public_groups
|
||||
groups_for_projects(public_projects)
|
||||
end
|
||||
|
||||
def groups_for_projects(projects)
|
||||
Group.public_and_given_groups(projects.select(:namespace_id))
|
||||
end
|
||||
|
||||
def public_projects
|
||||
Project.unscoped.public_only
|
||||
end
|
||||
|
||||
def public_and_internal_projects
|
||||
Project.unscoped.public_and_internal_only
|
||||
end
|
||||
end
|
||||
|
|
49
app/finders/joined_groups_finder.rb
Normal file
49
app/finders/joined_groups_finder.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Class for finding the groups a user is a member of.
|
||||
class JoinedGroupsFinder
|
||||
def initialize(user = nil)
|
||||
@user = user
|
||||
end
|
||||
|
||||
# Finds the groups of the source user, optionally limited to those visible to
|
||||
# the current user.
|
||||
#
|
||||
# current_user - If given the groups of "@user" will only include the groups
|
||||
# "current_user" can also see.
|
||||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def execute(current_user = nil)
|
||||
if current_user
|
||||
relation = groups_visible_to_user(current_user)
|
||||
else
|
||||
relation = public_groups
|
||||
end
|
||||
|
||||
relation.order_id_desc
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns the groups the user in "current_user" can see.
|
||||
#
|
||||
# This list includes all public/internal projects as well as the projects of
|
||||
# "@user" that "current_user" also has access to.
|
||||
def groups_visible_to_user(current_user)
|
||||
base = @user.authorized_groups.visible_to_user(current_user)
|
||||
extra = public_and_internal_groups
|
||||
union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)])
|
||||
|
||||
Group.where("namespaces.id IN (#{union.to_sql})")
|
||||
end
|
||||
|
||||
def public_groups
|
||||
groups_for_projects(@user.authorized_projects.public_only)
|
||||
end
|
||||
|
||||
def public_and_internal_groups
|
||||
groups_for_projects(@user.authorized_projects.public_and_internal_only)
|
||||
end
|
||||
|
||||
def groups_for_projects(projects)
|
||||
@user.groups.public_and_given_groups(projects.select(:namespace_id))
|
||||
end
|
||||
end
|
12
app/finders/milestones_finder.rb
Normal file
12
app/finders/milestones_finder.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class MilestonesFinder
|
||||
def execute(projects, params)
|
||||
milestones = Milestone.of_projects(projects)
|
||||
milestones = milestones.order("due_date ASC")
|
||||
|
||||
case params[:state]
|
||||
when 'closed' then milestones.closed
|
||||
when 'all' then milestones
|
||||
else milestones.active
|
||||
end
|
||||
end
|
||||
end
|
41
app/finders/personal_projects_finder.rb
Normal file
41
app/finders/personal_projects_finder.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
class PersonalProjectsFinder
|
||||
def initialize(user)
|
||||
@user = user
|
||||
end
|
||||
|
||||
# Finds the projects belonging to the user in "@user", limited to either
|
||||
# public projects or projects visible to the given user.
|
||||
#
|
||||
# current_user - When given the list of projects is limited to those only
|
||||
# visible by this user.
|
||||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def execute(current_user = nil)
|
||||
if current_user
|
||||
relation = projects_visible_to_user(current_user)
|
||||
else
|
||||
relation = public_projects
|
||||
end
|
||||
|
||||
relation.includes(:namespace).order_id_desc
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def projects_visible_to_user(current_user)
|
||||
authorized = @user.personal_projects.visible_to_user(current_user)
|
||||
|
||||
union = Gitlab::SQL::Union.
|
||||
new([authorized.select(:id), public_and_internal_projects.select(:id)])
|
||||
|
||||
Project.where("projects.id IN (#{union.to_sql})")
|
||||
end
|
||||
|
||||
def public_projects
|
||||
@user.personal_projects.public_only
|
||||
end
|
||||
|
||||
def public_and_internal_projects
|
||||
@user.personal_projects.public_and_internal_only
|
||||
end
|
||||
end
|
|
@ -1,11 +1,39 @@
|
|||
class ProjectsFinder
|
||||
def execute(current_user, options = {})
|
||||
# Returns all projects, optionally including group projects a user has access
|
||||
# to.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# Retrieving all public projects:
|
||||
#
|
||||
# ProjectsFinder.new.execute
|
||||
#
|
||||
# Retrieving all public/internal projects and those the given user has access
|
||||
# to:
|
||||
#
|
||||
# ProjectsFinder.new.execute(some_user)
|
||||
#
|
||||
# Retrieving all public/internal projects as well as the group's projects the
|
||||
# user has access to:
|
||||
#
|
||||
# ProjectsFinder.new.execute(some_user, group: some_group)
|
||||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def execute(current_user = nil, options = {})
|
||||
group = options[:group]
|
||||
|
||||
if group
|
||||
group_projects(current_user, group)
|
||||
base, extra = group_projects(current_user, group)
|
||||
else
|
||||
all_projects(current_user)
|
||||
base, extra = all_projects(current_user)
|
||||
end
|
||||
|
||||
if base and extra
|
||||
union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)])
|
||||
|
||||
Project.where("projects.id IN (#{union.to_sql})")
|
||||
else
|
||||
base
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -13,77 +41,36 @@ class ProjectsFinder
|
|||
|
||||
def group_projects(current_user, group)
|
||||
if current_user
|
||||
if group.users.include?(current_user)
|
||||
# User is group member
|
||||
#
|
||||
# Return ALL group projects
|
||||
group.projects
|
||||
else
|
||||
projects_members = ProjectMember.in_projects(group.projects).
|
||||
with_user(current_user)
|
||||
|
||||
if projects_members.any?
|
||||
# User is a project member
|
||||
#
|
||||
# Return only:
|
||||
# public projects
|
||||
# internal projects
|
||||
# joined projects
|
||||
#
|
||||
group.projects.where(
|
||||
"projects.id IN (?) OR projects.visibility_level IN (?)",
|
||||
projects_members.pluck(:source_id),
|
||||
Project.public_and_internal_levels
|
||||
)
|
||||
else
|
||||
# User has no access to group or group projects
|
||||
#
|
||||
# Return only:
|
||||
# public projects
|
||||
# internal projects
|
||||
#
|
||||
group.projects.public_and_internal_only
|
||||
end
|
||||
end
|
||||
[
|
||||
group_projects_for_user(current_user, group),
|
||||
group.projects.public_and_internal_only
|
||||
]
|
||||
else
|
||||
# Not authenticated
|
||||
#
|
||||
# Return only:
|
||||
# public projects
|
||||
group.projects.public_only
|
||||
[group.projects.public_only]
|
||||
end
|
||||
end
|
||||
|
||||
def all_projects(current_user)
|
||||
if current_user
|
||||
if current_user.authorized_projects.any?
|
||||
# User has access to private projects
|
||||
#
|
||||
# Return only:
|
||||
# public projects
|
||||
# internal projects
|
||||
# joined projects
|
||||
#
|
||||
Project.where(
|
||||
"projects.id IN (?) OR projects.visibility_level IN (?)",
|
||||
current_user.authorized_projects.pluck(:id),
|
||||
Project.public_and_internal_levels
|
||||
)
|
||||
else
|
||||
# User has no access to private projects
|
||||
#
|
||||
# Return only:
|
||||
# public projects
|
||||
# internal projects
|
||||
#
|
||||
Project.public_and_internal_only
|
||||
end
|
||||
[current_user.authorized_projects, public_and_internal_projects]
|
||||
else
|
||||
# Not authenticated
|
||||
#
|
||||
# Return only:
|
||||
# public projects
|
||||
Project.public_only
|
||||
[Project.public_only]
|
||||
end
|
||||
end
|
||||
|
||||
def group_projects_for_user(current_user, group)
|
||||
if group.users.include?(current_user)
|
||||
group.projects
|
||||
else
|
||||
group.projects.visible_to_user(current_user)
|
||||
end
|
||||
end
|
||||
|
||||
def public_projects
|
||||
Project.unscoped.public_only
|
||||
end
|
||||
|
||||
def public_and_internal_projects
|
||||
Project.unscoped.public_and_internal_only
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module AuthHelper
|
||||
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze
|
||||
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook).freeze
|
||||
FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
|
||||
|
||||
def ldap_enabled?
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
module BuildsHelper
|
||||
def build_ref_link build
|
||||
gitlab_ref_link build.project, build.ref
|
||||
end
|
||||
|
||||
def build_commit_link build
|
||||
gitlab_commit_link build.project, build.short_sha
|
||||
end
|
||||
|
||||
def build_url(build)
|
||||
namespace_project_build_path(build.gl_project, build.project, build)
|
||||
end
|
||||
end
|
|
@ -4,25 +4,6 @@ module Ci
|
|||
{ :"data-no-turbolink" => "data-no-turbolink" }
|
||||
end
|
||||
|
||||
def gitlab_ref_link project, ref
|
||||
gitlab_url = project.gitlab_url.dup
|
||||
gitlab_url << "/commits/#{ref}"
|
||||
link_to ref, gitlab_url, no_turbolink
|
||||
end
|
||||
|
||||
def gitlab_compare_link project, before, after
|
||||
gitlab_url = project.gitlab_url.dup
|
||||
gitlab_url << "/compare/#{before}...#{after}"
|
||||
|
||||
link_to "#{before}...#{after}", gitlab_url, no_turbolink
|
||||
end
|
||||
|
||||
def gitlab_commit_link project, sha
|
||||
gitlab_url = project.gitlab_url.dup
|
||||
gitlab_url << "/commit/#{sha}"
|
||||
link_to Ci::Commit.truncate_sha(sha), gitlab_url, no_turbolink
|
||||
end
|
||||
|
||||
def yaml_web_editor_link(project)
|
||||
commits = project.commits
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module CiStatusHelper
|
||||
def ci_status_path(ci_commit)
|
||||
project = ci_commit.gl_project
|
||||
ci_namespace_project_commit_path(project.namespace, project, ci_commit.sha)
|
||||
builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha)
|
||||
end
|
||||
|
||||
def ci_status_icon(ci_commit)
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
module DiffHelper
|
||||
def diff_view
|
||||
params[:view] == 'parallel' ? 'parallel' : 'inline'
|
||||
end
|
||||
|
||||
def allowed_diff_size
|
||||
if diff_hard_limit_enabled?
|
||||
Commit::DIFF_HARD_LIMIT_FILES
|
||||
|
@ -132,25 +136,11 @@ module DiffHelper
|
|||
end
|
||||
|
||||
def inline_diff_btn
|
||||
params_copy = params.dup
|
||||
params_copy[:view] = 'inline'
|
||||
# Always use HTML to handle case where JSON diff rendered this button
|
||||
params_copy.delete(:format)
|
||||
|
||||
link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do
|
||||
'Inline'
|
||||
end
|
||||
diff_btn('Inline', 'inline', diff_view == 'inline')
|
||||
end
|
||||
|
||||
def parallel_diff_btn
|
||||
params_copy = params.dup
|
||||
params_copy[:view] = 'parallel'
|
||||
# Always use HTML to handle case where JSON diff rendered this button
|
||||
params_copy.delete(:format)
|
||||
|
||||
link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do
|
||||
'Side-by-side'
|
||||
end
|
||||
diff_btn('Side-by-side', 'parallel', diff_view == 'parallel')
|
||||
end
|
||||
|
||||
def submodule_link(blob, ref, repository = @repository)
|
||||
|
@ -171,7 +161,7 @@ module DiffHelper
|
|||
def commit_for_diff(diff)
|
||||
if diff.deleted_file
|
||||
first_commit = @first_commit || @commit
|
||||
first_commit.parent
|
||||
first_commit.parent || @first_commit
|
||||
else
|
||||
@commit
|
||||
end
|
||||
|
@ -187,4 +177,18 @@ module DiffHelper
|
|||
def editable_diff?(diff)
|
||||
!diff.deleted_file && @merge_request && @merge_request.source_project
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def diff_btn(title, name, selected)
|
||||
params_copy = params.dup
|
||||
params_copy[:view] = name
|
||||
|
||||
# Always use HTML to handle case where JSON diff rendered this button
|
||||
params_copy.delete(:format)
|
||||
|
||||
link_to url_for(params_copy), id: "#{name}-diff-btn", class: (selected ? 'btn active' : 'btn') do
|
||||
title
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -108,19 +108,23 @@ module EventsHelper
|
|||
end
|
||||
end
|
||||
elsif event.push?
|
||||
if event.push_with_commits? && event.md_ref?
|
||||
if event.commits_count > 1
|
||||
namespace_project_compare_url(event.project.namespace, event.project,
|
||||
from: event.commit_from, to:
|
||||
event.commit_to)
|
||||
else
|
||||
namespace_project_commit_url(event.project.namespace, event.project,
|
||||
id: event.commit_to)
|
||||
end
|
||||
push_event_feed_url(event)
|
||||
end
|
||||
end
|
||||
|
||||
def push_event_feed_url(event)
|
||||
if event.push_with_commits? && event.md_ref?
|
||||
if event.commits_count > 1
|
||||
namespace_project_compare_url(event.project.namespace, event.project,
|
||||
from: event.commit_from, to:
|
||||
event.commit_to)
|
||||
else
|
||||
namespace_project_commits_url(event.project.namespace, event.project,
|
||||
event.ref_name)
|
||||
namespace_project_commit_url(event.project.namespace, event.project,
|
||||
id: event.commit_to)
|
||||
end
|
||||
else
|
||||
namespace_project_commits_url(event.project.namespace, event.project,
|
||||
event.ref_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -198,7 +202,7 @@ module EventsHelper
|
|||
xml.link href: event_link
|
||||
xml.title truncate(event_title, length: 80)
|
||||
xml.updated event.created_at.xmlschema
|
||||
xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email)
|
||||
xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email))
|
||||
xml.author do |author|
|
||||
xml.name event.author_name
|
||||
xml.email event.author_email
|
||||
|
|
|
@ -49,7 +49,7 @@ module GitlabMarkdownHelper
|
|||
return "" unless text.present?
|
||||
|
||||
context[:project] ||= @project
|
||||
|
||||
|
||||
html = Gitlab::Markdown.render(text, context)
|
||||
|
||||
context.merge!(
|
||||
|
|
|
@ -74,7 +74,7 @@ module IssuesHelper
|
|||
issue.project, issue)
|
||||
xml.title truncate(issue.title, length: 80)
|
||||
xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email)
|
||||
xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email))
|
||||
xml.author do |author|
|
||||
xml.name issue.author_name
|
||||
xml.email issue.author_email
|
||||
|
|
|
@ -100,7 +100,7 @@ module LabelsHelper
|
|||
Label.where(project_id: @projects)
|
||||
end
|
||||
|
||||
grouped_labels = Labels::GroupService.new(labels).execute
|
||||
grouped_labels = GlobalLabel.build_collection(labels)
|
||||
grouped_labels.unshift(Label::None)
|
||||
grouped_labels.unshift(Label::Any)
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ module MilestonesHelper
|
|||
Milestone.where(project_id: @projects)
|
||||
end.active
|
||||
|
||||
grouped_milestones = Milestones::GroupService.new(milestones).execute
|
||||
grouped_milestones = GlobalMilestone.build_collection(milestones)
|
||||
grouped_milestones.unshift(Milestone::None)
|
||||
grouped_milestones.unshift(Milestone::Any)
|
||||
|
||||
|
|
|
@ -17,15 +17,6 @@ module NamespacesHelper
|
|||
grouped_options_for_select(options, selected)
|
||||
end
|
||||
|
||||
def namespace_select_tag(id, opts = {})
|
||||
css_class = "ajax-namespace-select "
|
||||
css_class << "multiselect " if opts[:multiple]
|
||||
css_class << (opts[:class] || '')
|
||||
value = opts[:selected] || ''
|
||||
|
||||
hidden_field_tag(id, value, class: css_class)
|
||||
end
|
||||
|
||||
def namespace_icon(namespace, size = 40)
|
||||
if namespace.kind_of?(Group)
|
||||
group_icon(namespace)
|
||||
|
|
|
@ -16,40 +16,28 @@ module NotificationsHelper
|
|||
def notification_list_item(notification_level, user_membership)
|
||||
case notification_level
|
||||
when Notification::N_DISABLED
|
||||
content_tag(:li, class: active_level_for(user_membership, Notification::N_DISABLED)) do
|
||||
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do
|
||||
icon('microphone-slash fw', text: 'Disabled')
|
||||
end
|
||||
end
|
||||
update_notification_link(Notification::N_DISABLED, user_membership, 'Disabled', 'microphone-slash')
|
||||
when Notification::N_PARTICIPATING
|
||||
content_tag(:li, class: active_level_for(user_membership, Notification::N_PARTICIPATING)) do
|
||||
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do
|
||||
icon('volume-up fw', text: 'Participate')
|
||||
end
|
||||
end
|
||||
update_notification_link(Notification::N_PARTICIPATING, user_membership, 'Participate', 'volume-up')
|
||||
when Notification::N_WATCH
|
||||
content_tag(:li, class: active_level_for(user_membership, Notification::N_WATCH)) do
|
||||
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do
|
||||
icon('eye fw', text: 'Watch')
|
||||
end
|
||||
end
|
||||
update_notification_link(Notification::N_WATCH, user_membership, 'Watch', 'eye')
|
||||
when Notification::N_MENTION
|
||||
content_tag(:li, class: active_level_for(user_membership, Notification::N_MENTION)) do
|
||||
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do
|
||||
icon('at fw', text: 'On mention')
|
||||
end
|
||||
end
|
||||
update_notification_link(Notification::N_MENTION, user_membership, 'On mention', 'at')
|
||||
when Notification::N_GLOBAL
|
||||
content_tag(:li, class: active_level_for(user_membership, Notification::N_GLOBAL)) do
|
||||
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_GLOBAL } do
|
||||
icon('globe fw', text: 'Global')
|
||||
end
|
||||
end
|
||||
update_notification_link(Notification::N_GLOBAL, user_membership, 'Global', 'globe')
|
||||
else
|
||||
# do nothing
|
||||
end
|
||||
end
|
||||
|
||||
def update_notification_link(notification_level, user_membership, title, icon)
|
||||
content_tag(:li, class: active_level_for(user_membership, notification_level)) do
|
||||
link_to '#', class: 'update-notification', data: { notification_level: notification_level } do
|
||||
icon("#{icon} fw", text: title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def notification_label(user_membership)
|
||||
Notification.new(user_membership).to_s
|
||||
end
|
||||
|
|
|
@ -117,7 +117,7 @@ module ProjectsHelper
|
|||
nav_tabs << :merge_requests
|
||||
end
|
||||
|
||||
if project.gitlab_ci? && can?(current_user, :read_build, project)
|
||||
if project.builds_enabled? && can?(current_user, :read_build, project)
|
||||
nav_tabs << :builds
|
||||
end
|
||||
|
||||
|
@ -253,14 +253,6 @@ module ProjectsHelper
|
|||
filename_path(project, :version)
|
||||
end
|
||||
|
||||
def hidden_pass_url(original_url)
|
||||
result = URI(original_url)
|
||||
result.password = '*****' unless result.password.nil?
|
||||
result
|
||||
rescue
|
||||
original_url
|
||||
end
|
||||
|
||||
def project_wiki_path_with_version(proj, page, version, is_newest)
|
||||
url_params = is_newest ? {} : { version_id: version }
|
||||
namespace_project_wiki_path(proj.namespace, proj, page, url_params)
|
||||
|
|
|
@ -70,7 +70,7 @@ module SearchHelper
|
|||
|
||||
# Autocomplete results for the current user's groups
|
||||
def groups_autocomplete(term, limit = 5)
|
||||
current_user.authorized_groups.search(term).limit(limit).map do |group|
|
||||
GroupsFinder.new.execute(current_user).search(term).limit(limit).map do |group|
|
||||
{
|
||||
label: "group: #{search_result_sanitize(group.name)}",
|
||||
url: group_path(group)
|
||||
|
|
|
@ -35,8 +35,20 @@ module SelectsHelper
|
|||
end
|
||||
|
||||
def groups_select_tag(id, opts = {})
|
||||
css_class = "ajax-groups-select "
|
||||
css_class << "multiselect " if opts[:multiple]
|
||||
opts[:class] ||= ''
|
||||
opts[:class] << ' ajax-groups-select'
|
||||
select2_tag(id, opts)
|
||||
end
|
||||
|
||||
def namespace_select_tag(id, opts = {})
|
||||
opts[:class] ||= ''
|
||||
opts[:class] << ' ajax-namespace-select'
|
||||
select2_tag(id, opts)
|
||||
end
|
||||
|
||||
def select2_tag(id, opts = {})
|
||||
css_class = ''
|
||||
css_class << 'multiselect ' if opts[:multiple]
|
||||
css_class << (opts[:class] || '')
|
||||
value = opts[:selected] || ''
|
||||
|
||||
|
|
|
@ -1,53 +1,49 @@
|
|||
module Emails
|
||||
module Issues
|
||||
def new_issue_email(recipient_id, issue_id)
|
||||
@issue = Issue.find(issue_id)
|
||||
@project = @issue.project
|
||||
@target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
|
||||
mail_new_thread(@issue,
|
||||
from: sender(@issue.author_id),
|
||||
to: recipient(recipient_id),
|
||||
subject: subject("#{@issue.title} (##{@issue.iid})"))
|
||||
|
||||
SentNotification.record(@issue, recipient_id, reply_key)
|
||||
issue_mail_with_notification(issue_id, recipient_id) do
|
||||
mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id))
|
||||
end
|
||||
end
|
||||
|
||||
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id)
|
||||
@issue = Issue.find(issue_id)
|
||||
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
|
||||
@project = @issue.project
|
||||
@target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
|
||||
mail_answer_thread(@issue,
|
||||
from: sender(updated_by_user_id),
|
||||
to: recipient(recipient_id),
|
||||
subject: subject("#{@issue.title} (##{@issue.iid})"))
|
||||
|
||||
SentNotification.record(@issue, recipient_id, reply_key)
|
||||
issue_mail_with_notification(issue_id, recipient_id) do
|
||||
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
|
||||
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
|
||||
end
|
||||
end
|
||||
|
||||
def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
|
||||
@issue = Issue.find issue_id
|
||||
@project = @issue.project
|
||||
@updated_by = User.find updated_by_user_id
|
||||
@target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
|
||||
mail_answer_thread(@issue,
|
||||
from: sender(updated_by_user_id),
|
||||
to: recipient(recipient_id),
|
||||
subject: subject("#{@issue.title} (##{@issue.iid})"))
|
||||
|
||||
SentNotification.record(@issue, recipient_id, reply_key)
|
||||
issue_mail_with_notification(issue_id, recipient_id) do
|
||||
@updated_by = User.find updated_by_user_id
|
||||
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
|
||||
end
|
||||
end
|
||||
|
||||
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
|
||||
@issue = Issue.find issue_id
|
||||
@issue_status = status
|
||||
issue_mail_with_notification(issue_id, recipient_id) do
|
||||
@issue_status = status
|
||||
@updated_by = User.find updated_by_user_id
|
||||
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def issue_thread_options(sender_id, recipient_id)
|
||||
{
|
||||
from: sender(sender_id),
|
||||
to: recipient(recipient_id),
|
||||
subject: subject("#{@issue.title} (##{@issue.iid})")
|
||||
}
|
||||
end
|
||||
|
||||
def issue_mail_with_notification(issue_id, recipient_id)
|
||||
@issue = Issue.find(issue_id)
|
||||
@project = @issue.project
|
||||
@updated_by = User.find updated_by_user_id
|
||||
@target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
|
||||
mail_answer_thread(@issue,
|
||||
from: sender(updated_by_user_id),
|
||||
to: recipient(recipient_id),
|
||||
subject: subject("#{@issue.title} (##{@issue.iid})"))
|
||||
|
||||
yield
|
||||
|
||||
SentNotification.record(@issue, recipient_id, reply_key)
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue