Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/import-event-error

This commit is contained in:
James Lopez 2016-07-11 18:00:19 +02:00
commit feecb4afde
613 changed files with 13644 additions and 4841 deletions

View File

@ -193,7 +193,7 @@ Style/EmptyLineBetweenDefs:
# Don't use several empty lines in a row.
Style/EmptyLines:
Enabled: false
Enabled: true
# Keep blank lines around access modifiers.
Style/EmptyLinesAroundAccessModifier:
@ -284,7 +284,7 @@ Style/IfWithSemicolon:
# Checks that conditional statements do not have an identical line at the
# end of each branch, which can validly be moved out of the conditional.
Style/IdenticalConditionalBranches:
Enabled: false
Enabled: true
# Checks the indentation of the first line of the right-hand-side of a
# multi-line assignment.

View File

@ -8,27 +8,50 @@ v 8.10.0 (unreleased)
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell)
- Display last commit of deleted branch in push events !4699 (winniehell)
- Escape file extension when parsing search results !5141 (winniehell)
- Apply the trusted_proxies config to the rack request object for use with rack_attack
- Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs
- Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates.
- Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion
- Fix pagination when sorting by columns with lots of ties (like priority)
- The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020
- Updated project header design
- Exclude email check from the standard health check
- Updated layout for Projects, Groups, Users on Admin area !4424
- Fix changing issue state columns in milestone view
- Add notification settings dropdown for groups
- Wildcards for protected branches. !4665
- Allow importing from Github using Personal Access Tokens. (Eric K Idema)
- API: Todos !3188 (Robert Schilling)
- API: Expose shared groups for projects and shared projects for groups !5050 (Robert Schilling)
- Add "Enabled Git access protocols" to Application Settings
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data
- Throttle the update of `project.pushes_since_gc` to 1 minute.
- Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork
- Don't instantiate a git tree on Projects show default view
- Bump Rinku to 2.0.0
- Remove unused front-end variable -> default_issues_tracker
- Better caching of git calls on ProjectsController#show.
- Avoid to retrieve MR closes_issues as much as possible.
- Add API endpoint for a group issues !4520 (mahcsig)
- Add Bugzilla integration !4930 (iamtjg)
- Instrument Rinku usage
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
- RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
- Set import_url validation to be more strict
- Memoize MR merged/closed events retrieval
- Don't render discussion notes when requesting diff tab through AJAX
- Add basic system information like memory and disk usage to the admin panel
v 8.9.5 (unreleased)
@ -43,6 +66,8 @@ v 8.9.5 (unreleased)
v 8.9.6
- Fix importing of events under notes for GitLab projects
- Add min value for project limit field on user's form !3622 (jastkand)
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
v 8.9.5
- Add more debug info to import/export and memory killer. !5108
@ -58,9 +83,6 @@ v 8.9.5
- Admin should be able to turn shared runners into specific ones. !4961
- Update RedCloth to 4.3.2 for CVE-2012-6684. !4929 (Takuya Noguchi)
- Improve the request / withdraw access button. !4860
- Fix assigning shared runners as admins. !4961
- Show "locked" label for locked runners on runners admin. !4961
- Fixes issues importing events in Import/Export. Import/Export version bumped to 0.1.1
v 8.9.4
- Fix privilege escalation issue with OAuth external users.
@ -90,7 +112,7 @@ v 8.9.3
- Removed fade when filtering results. !4932
- Fix missing avatar on system notes. !4954
- Reduce overhead and optimize ProjectTeam#max_member_access performance. !4973
- Use update_columns to by_pass all the dirty code on active_record. !4985
- Use update_columns to bypass all the dirty code on active_record. !4985
- Fix restore Rake task warning message output !4980
v 8.9.2

View File

@ -1 +1 @@
3.1.0
3.2.0

115
Gemfile
View File

@ -1,4 +1,4 @@
source "https://rubygems.org"
source 'https://rubygems.org'
gem 'rails', '4.2.6'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
@ -11,15 +11,15 @@ gem 'responders', '~> 2.0'
gem 'sprockets', '~> 3.6.0'
# Default values for AR models
gem "default_value_for", "~> 3.0.0"
gem 'default_value_for', '~> 3.0.0'
# Supported DBs
gem "mysql2", '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres
gem 'mysql2', '~> 0.3.16', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres
# Authentication libraries
gem 'devise', '~> 4.0'
gem 'doorkeeper', '~> 3.1'
gem 'doorkeeper', '~> 4.0'
gem 'omniauth', '~> 1.3.1'
gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
@ -28,7 +28,7 @@ gem 'omniauth-cas3', '~> 1.1.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'
gem 'omniauth-google-oauth2', '~> 0.4.1'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.6.0'
gem 'omniauth-shibboleth', '~> 1.2.0'
@ -48,16 +48,16 @@ gem 'attr_encrypted', '~> 3.0.0'
gem 'u2f', '~> 0.2.1'
# Browser detection
gem "browser", '~> 2.2'
gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 10.2'
gem 'gitlab_git', '~> 10.2'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap"
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki
# Required manually in config/initializers/gollum.rb to control load order
@ -65,7 +65,7 @@ gem 'gollum-lib', '~> 4.1.0', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection
gem "github-linguist", "~> 4.7.0", require: "linguist"
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API
gem 'grape', '~> 0.13.0'
@ -73,13 +73,13 @@ gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination
gem "kaminari", "~> 0.17.0"
gem 'kaminari', '~> 0.17.0'
# HAML
gem 'hamlit', '~> 2.5'
# Files attachments
gem "carrierwave", '~> 0.10.0'
gem 'carrierwave', '~> 0.10.0'
# Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1'
@ -94,20 +94,20 @@ gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1'
# for aws storage
gem "unf", '~> 0.1.4'
gem 'unf', '~> 0.1.4'
# Authorization
gem "six", '~> 0.2.0'
gem 'six', '~> 0.2.0'
# Seed data
gem "seed-fu", '~> 2.3.5'
gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1'
gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.2.9'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
@ -124,29 +124,29 @@ gem 'diffy', '~> 3.0.3'
# Application server
group :unicorn do
gem "unicorn", '~> 4.9.0'
gem 'unicorn', '~> 4.9.0'
gem 'unicorn-worker-killer', '~> 0.4.2'
end
# State machine
gem "state_machines-activerecord", '~> 0.4.0'
gem 'state_machines-activerecord', '~> 0.4.0'
# Run events after state machine commits
gem 'after_commit_queue'
gem 'after_commit_queue', '~> 1.3.0'
# Issue tags
gem 'acts-as-taggable-on', '~> 3.4'
# Background jobs
gem 'sinatra', '~> 1.4.4', require: nil
gem 'sinatra', '~> 1.4.4', require: false
gem 'sidekiq', '~> 4.0'
gem 'sidekiq-cron', '~> 0.4.0'
gem 'redis-namespace'
gem 'redis-namespace', '~> 1.5.2'
# HTTP requests
gem "httparty", '~> 0.13.3'
gem 'httparty', '~> 0.13.3'
# Colored output to console
gem "rainbow", '~> 2.1.0'
gem 'rainbow', '~> 2.1.0'
# GitLab settings
gem 'settingslogic', '~> 2.0.9'
@ -156,7 +156,7 @@ gem 'settingslogic', '~> 2.0.9'
gem 'version_sorter', '~> 2.0.0'
# Cache
gem "redis-rails", '~> 4.0.0'
gem 'redis-rails', '~> 4.0.0'
# Redis
gem 'redis', '~> 3.2'
@ -169,13 +169,13 @@ gem 'tinder', '~> 1.10.0'
gem 'hipchat', '~> 1.5.0'
# Flowdock integration
gem "gitlab-flowdock-git-hook", "~> 1.0.1"
gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
# Gemnasium integration
gem "gemnasium-gitlab-service", "~> 0.2"
gem 'gemnasium-gitlab-service', '~> 0.2'
# Slack integration
gem "slack-notifier", "~> 1.2.0"
gem 'slack-notifier', '~> 1.2.0'
# Asana integration
gem 'asana', '~> 0.4.0'
@ -187,20 +187,20 @@ gem 'ruby-fogbugz', '~> 0.2.1'
gem 'd3_rails', '~> 3.5.0'
# underscore-rails
gem "underscore-rails", "~> 1.8.0"
gem 'underscore-rails', '~> 1.8.0'
# Sanitize user input
gem "sanitize", '~> 2.0'
gem 'sanitize', '~> 2.0'
gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input
gem "loofah", "~> 2.0.3"
gem 'loofah', '~> 2.0.3'
# Working with license
gem 'licensee', '~> 8.0.0'
# Protect against bruteforcing
gem "rack-attack", '~> 4.3.1'
gem 'rack-attack', '~> 4.3.1'
# Ace editor
gem 'ace-rails-ap', '~> 4.0.2'
@ -214,16 +214,16 @@ gem 'charlock_holmes', '~> 0.7.3'
# Parse duration
gem 'chronic_duration', '~> 0.10.6'
gem "sass-rails", '~> 5.0.0'
gem "coffee-rails", '~> 4.1.0'
gem "uglifier", '~> 2.7.2'
gem 'sass-rails', '~> 5.0.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'uglifier', '~> 2.7.2'
gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.6.1'
gem 'gitlab_emoji', '~> 0.3.0'
gem 'gemojione', '~> 2.6'
gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0'
@ -247,14 +247,13 @@ group :metrics do
end
group :development do
gem "foreman"
gem 'foreman', '~> 0.78.0'
gem 'brakeman', '~> 3.3.0', require: false
gem 'letter_opener_web', '~> 1.3.0'
gem 'quiet_assets', '~> 1.0.2'
gem 'rerun', '~> 0.11.0'
gem 'bullet', require: false
gem 'rblineprof', platform: :mri, require: false
gem 'bullet', '~> 5.0.0', require: false
gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
gem 'web-console', '~> 2.0'
# Better errors handler
@ -262,23 +261,23 @@ group :development do
gem 'binding_of_caller', '~> 0.7.2'
# Docs generator
gem "sdoc", '~> 0.3.20'
gem 'sdoc', '~> 0.3.20'
# thin instead webrick
gem 'thin', '~> 1.6.1'
gem 'thin', '~> 1.7.0'
end
group :development, :test do
gem 'byebug', platform: :mri
gem 'pry-rails'
gem 'byebug', '~> 8.2.1', platform: :mri
gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0'
gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.4.0'
gem 'rspec-retry'
gem 'rspec-rails', '~> 3.5.0'
gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2'
@ -304,14 +303,14 @@ group :development, :test do
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'simplecov', '~> 0.11.0', require: false
gem 'flog', require: false
gem 'flay', require: false
gem 'bundler-audit', require: false
gem 'flog', '~> 4.3.2', require: false
gem 'flay', '~> 2.6.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false
gem "license_finder", require: false
gem 'knapsack'
gem 'license_finder', '~> 2.1.0', require: false
gem 'knapsack', '~> 1.11.0'
end
group :test do
@ -319,30 +318,30 @@ group :test do
gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0'
gem 'test_after_commit', '~> 0.4.2'
gem 'sham_rack'
gem 'sham_rack', '~> 1.3.6'
end
group :production do
gem "gitlab_meta", '7.0'
gem 'gitlab_meta', '7.0'
end
gem "newrelic_rpm", '~> 3.14'
gem 'newrelic_rpm', '~> 3.14'
gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.8"
gem 'mail_room', '~> 0.8'
gem 'email_reply_parser', '~> 0.5.8'
## CI
gem 'activerecord-session_store', '~> 1.0.0'
gem "nested_form", '~> 0.3.2'
gem 'nested_form', '~> 0.3.2'
# OAuth
gem 'oauth2', '~> 1.0.0'
gem 'oauth2', '~> 1.2.0'
# Soft deletion
gem "paranoia", "~> 2.0"
gem 'paranoia', '~> 2.0'
# Health check
gem 'health_check', '~> 1.5.1'

View File

@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
RedCloth (4.2.9)
RedCloth (4.3.2)
ace-rails-ap (4.0.2)
actionmailer (4.2.6)
actionpack (= 4.2.6)
@ -171,8 +171,8 @@ GEM
diff-lcs (1.2.5)
diffy (3.0.7)
docile (1.1.5)
doorkeeper (3.1.0)
railties (>= 3.2)
doorkeeper (4.0.0)
railties (>= 4.2)
dropzonejs-rails (0.7.2)
rails (> 3.1)
email_reply_parser (0.5.8)
@ -255,7 +255,7 @@ GEM
ruby-progressbar (~> 1.4)
gemnasium-gitlab-service (0.2.6)
rugged (~> 0.21)
gemojione (2.2.1)
gemojione (2.6.1)
json
get_process_mem (0.2.0)
gherkin-ruby (0.3.2)
@ -274,8 +274,6 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab_emoji (0.3.1)
gemojione (~> 2.2, >= 2.2.1)
gitlab_git (10.2.3)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
@ -355,7 +353,7 @@ GEM
jquery-ui-rails (5.0.5)
railties (>= 3.2.16)
json (1.8.3)
jwt (1.5.2)
jwt (1.5.4)
kaminari (0.17.0)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
@ -395,7 +393,7 @@ GEM
mini_portile2 (2.1.0)
minitest (5.7.0)
mousetrap-rails (1.4.6)
multi_json (1.11.2)
multi_json (1.12.1)
multi_xml (0.5.5)
multipart-post (2.0.0)
mysql2 (0.3.20)
@ -408,12 +406,12 @@ GEM
pkg-config (~> 1.1.7)
numerizer (0.1.1)
oauth (0.4.7)
oauth2 (1.0.0)
oauth2 (1.2.0)
faraday (>= 0.8, < 0.10)
jwt (~> 1.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (~> 1.2)
rack (>= 1.2, < 3)
octokit (4.3.0)
sawyer (~> 0.7.0, >= 0.5.3)
omniauth (1.3.1)
@ -441,7 +439,7 @@ GEM
omniauth-gitlab (1.0.1)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.0)
omniauth-google-oauth2 (0.2.10)
omniauth-google-oauth2 (0.4.1)
addressable (~> 2.3)
jwt (~> 1.0)
multi_json (~> 1.3)
@ -499,8 +497,6 @@ GEM
pry-rails (0.3.4)
pry (>= 0.9.10)
pyu-ruby-sasl (0.0.3.3)
quiet_assets (1.0.3)
railties (>= 3.1, < 5.0)
rack (1.6.4)
rack-accept (0.4.5)
rack (>= 0.4)
@ -556,7 +552,7 @@ GEM
recaptcha (3.0.0)
json
redcarpet (3.3.3)
redis (3.3.0)
redis (3.2.2)
redis-actionpack (4.0.1)
actionpack (~> 4)
redis-rack (~> 1.5.0)
@ -580,36 +576,36 @@ GEM
listen (~> 3.0)
responders (2.1.1)
railties (>= 4.2.0, < 5.1)
rinku (1.7.3)
rinku (2.0.0)
rotp (2.1.2)
rouge (1.11.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
rspec (3.4.0)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-core (3.4.4)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.0)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-mocks (3.4.1)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-rails (3.4.2)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-support (~> 3.4.0)
rspec-support (~> 3.5.0)
rspec-rails (3.5.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-support (~> 3.5.0)
rspec-retry (0.4.5)
rspec-core
rspec-support (3.4.1)
rspec-support (3.5.0)
rubocop (0.40.0)
parser (>= 2.3.1.0, < 3.0)
powerpack (~> 0.1)
@ -634,8 +630,8 @@ GEM
sanitize (2.1.0)
nokogiri (>= 1.4.4)
sass (3.4.22)
sass-rails (5.0.4)
railties (>= 4.0.0, < 5.0)
sass-rails (5.0.5)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
@ -649,9 +645,9 @@ GEM
sdoc (0.3.20)
json (>= 1.1.3)
rdoc (~> 3.10)
seed-fu (2.3.5)
activerecord (>= 3.1, < 4.3)
activesupport (>= 3.1, < 4.3)
seed-fu (2.3.6)
activerecord (>= 3.1)
activesupport (>= 3.1)
select2-rails (3.5.9.3)
thor (~> 0.14)
sentry-raven (1.1.0)
@ -662,10 +658,11 @@ GEM
rack
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
sidekiq (4.1.2)
sidekiq (4.1.4)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
redis (~> 3.2, >= 3.2.1)
sinatra (>= 1.4.7)
sidekiq-cron (0.4.0)
redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24)
@ -676,8 +673,8 @@ GEM
json (~> 1.8)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
sinatra (1.4.6)
rack (~> 1.4)
sinatra (1.4.7)
rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
six (0.2.0)
@ -693,17 +690,17 @@ GEM
spinach (>= 0.4)
spinach-rerun-reporter (0.0.2)
spinach (~> 0.8)
spring (1.7.1)
spring (1.7.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
spring-commands-spinach (1.1.0)
spring (>= 0.9.1)
spring-commands-teaspoon (0.0.2)
spring (>= 0.9.1)
sprockets (3.6.0)
sprockets (3.6.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.0.4)
sprockets-rails (3.1.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
@ -727,10 +724,10 @@ GEM
temple (0.7.7)
test_after_commit (0.4.2)
activerecord (>= 3.2)
thin (1.6.4)
thin (1.7.0)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (~> 1.0)
rack (>= 1, < 3)
thor (0.19.1)
thread_safe (0.3.5)
tilt (2.0.5)
@ -804,12 +801,12 @@ PLATFORMS
ruby
DEPENDENCIES
RedCloth (~> 4.2.9)
RedCloth (~> 4.3.2)
ace-rails-ap (~> 4.0.2)
activerecord-session_store (~> 1.0.0)
acts-as-taggable-on (~> 3.4)
addressable (~> 2.3.8)
after_commit_queue
after_commit_queue (~> 1.3.0)
akismet (~> 2.0)
allocations (~> 1.0)
asana (~> 0.4.0)
@ -818,15 +815,15 @@ DEPENDENCIES
awesome_print (~> 1.2.0)
babosa (~> 1.0.2)
base32 (~> 0.3.0)
benchmark-ips
benchmark-ips (~> 2.3.0)
better_errors (~> 1.0.1)
binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0)
brakeman (~> 3.3.0)
browser (~> 2.2)
bullet
bundler-audit
byebug
bullet (~> 5.0.0)
bundler-audit (~> 0.5.0)
byebug (~> 8.2.1)
capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0)
carrierwave (~> 0.10.0)
@ -841,14 +838,14 @@ DEPENDENCIES
devise (~> 4.0)
devise-two-factor (~> 3.0.0)
diffy (~> 3.0.3)
doorkeeper (~> 3.1)
doorkeeper (~> 4.0)
dropzonejs-rails (~> 0.7.1)
email_reply_parser (~> 0.5.8)
email_spec (~> 1.6.0)
factory_girl_rails (~> 4.6.0)
ffaker (~> 2.0.0)
flay
flog
flay (~> 2.6.1)
flog (~> 4.3.2)
fog-aws (~> 0.9)
fog-azure (~> 0.0)
fog-core (~> 1.40)
@ -857,13 +854,13 @@ DEPENDENCIES
fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.6.1)
foreman
foreman (~> 0.78.0)
fuubar (~> 2.0.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 2.6)
github-linguist (~> 4.7.0)
github-markup (~> 1.3.1)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_emoji (~> 0.3.0)
gitlab_git (~> 10.2)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
@ -884,9 +881,9 @@ DEPENDENCIES
jquery-ui-rails (~> 5.0.0)
jwt
kaminari (~> 0.17.0)
knapsack
knapsack (~> 1.11.0)
letter_opener_web (~> 1.3.0)
license_finder
license_finder (~> 2.1.0)
licensee (~> 8.0.0)
loofah (~> 2.0.3)
mail_room (~> 0.8)
@ -898,7 +895,7 @@ DEPENDENCIES
net-ssh (~> 3.0.1)
newrelic_rpm (~> 3.14)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.0.0)
oauth2 (~> 1.2.0)
octokit (~> 4.3.0)
omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1)
@ -908,7 +905,7 @@ DEPENDENCIES
omniauth-facebook (~> 3.0.0)
omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0)
omniauth-google-oauth2 (~> 0.2.0)
omniauth-google-oauth2 (~> 0.4.1)
omniauth-kerberos (~> 0.3.0)
omniauth-saml (~> 1.6.0)
omniauth-shibboleth (~> 1.2.0)
@ -919,28 +916,27 @@ DEPENDENCIES
pg (~> 0.18.2)
poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.0)
pry-rails
quiet_assets (~> 1.0.2)
pry-rails (~> 0.3.4)
rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rails (= 4.2.6)
rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0)
rblineprof
rblineprof (~> 0.3.6)
rdoc (~> 3.6)
recaptcha (~> 3.0)
redcarpet (~> 3.3.3)
redis (~> 3.2)
redis-namespace
redis-namespace (~> 1.5.2)
redis-rails (~> 4.0.0)
request_store (~> 1.3.0)
rerun (~> 0.11.0)
responders (~> 2.0)
rouge (~> 1.11)
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.4.0)
rspec-retry
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rubocop (~> 0.40.0)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1)
@ -952,7 +948,7 @@ DEPENDENCIES
select2-rails (~> 3.5.9)
sentry-raven (~> 1.1.0)
settingslogic (~> 2.0.9)
sham_rack
sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.0)
sidekiq-cron (~> 0.4.0)
@ -973,7 +969,7 @@ DEPENDENCIES
teaspoon (~> 1.1.0)
teaspoon-jasmine (~> 2.2.0)
test_after_commit (~> 0.4.2)
thin (~> 1.6.1)
thin (~> 1.7.0)
tinder (~> 1.10.0)
turbolinks (~> 2.5.0)
u2f (~> 0.2.1)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 1002 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 KiB

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -54,7 +54,6 @@
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
#= require cropper
#= require u2f
window.slugify = (text) ->

View File

@ -2,7 +2,7 @@ class @CiBuild
@interval: null
@state: null
constructor: (@build_url, @build_status, @state) ->
constructor: (@page_url, @build_url, @build_status, @state) ->
clearInterval(CiBuild.interval)
# Init breakpoint checker
@ -41,7 +41,7 @@ class @CiBuild
# Only valid for runnig build when output changes during time
#
CiBuild.interval = setInterval =>
if window.location.href.split("#").first() is @build_url
if window.location.href.split("#").first() is @page_url
@getBuildTrace()
, 4000
@ -57,7 +57,7 @@ class @CiBuild
getBuildTrace: ->
$.ajax
url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}"
url: "#{@page_url}/trace.json?state=#{encodeURIComponent(@state)}"
dataType: "json"
success: (log) =>
if log.state
@ -70,7 +70,7 @@ class @CiBuild
$('.js-build-output').html log.html
@checkAutoscroll()
else if log.status isnt @build_status
Turbolinks.visit @build_url
Turbolinks.visit @page_url
checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")

View File

@ -127,11 +127,10 @@ class Dispatcher
when 'groups'
new UsersSelect()
when 'projects'
new NamespaceSelect()
new NamespaceSelects()
when 'dashboard', 'root'
shortcut_handler = new ShortcutsDashboardNavigation()
when 'profiles'
new Profile()
new NotificationsForm()
new NotificationsDropdown()
when 'projects'

View File

@ -70,12 +70,12 @@ class @DropzoneInput
pasteText response.link.markdown
return
error: (temp, errorMessage) ->
error: (temp) ->
errorAlert = $(form).find('.error-alert')
checkIfMsgExists = errorAlert.children().length
if checkIfMsgExists is 0
errorAlert.append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
$(".div-dropzone-alert").append "#{btnAlert}Attaching the file failed."
return
totaluploadprogress: (totalUploadProgress) ->

View File

@ -4,7 +4,7 @@ window.GitLab ?= {}
GitLab.GfmAutoComplete =
dataLoading: false
dataLoaded: false
cachedData: {}
dataSource: ''
# Emoji
@ -55,7 +55,7 @@ GitLab.GfmAutoComplete =
@setupAtWho()
if @dataSource
if !@dataLoading
if not @dataLoading and not @cachedData
@dataLoading = true
# We should wait until initializations are done
@ -70,6 +70,8 @@ GitLab.GfmAutoComplete =
@loadData(data)
, 1000)
if @cachedData?
@loadData(@cachedData)
setupAtWho: ->
# Emoji
@ -188,7 +190,7 @@ GitLab.GfmAutoComplete =
callbacks:
beforeSave: (merges) ->
sanitizeLabelTitle = (title)->
if /\w+\s+\w+/g.test(title)
if /[\w\?&]+\s+[\w\?&]+/g.test(title)
"\"#{sanitize(title)}\""
else
sanitize(title)
@ -205,6 +207,7 @@ GitLab.GfmAutoComplete =
$.getJSON(dataSource)
loadData: (data) ->
@cachedData = data
@dataLoaded = true
# load members

View File

@ -56,6 +56,7 @@ class GitLabDropdownFilter
return BLUR_KEYCODES.indexOf(keyCode) >= 0
filter: (search_text) ->
@options.onFilter(search_text) if @options.onFilter
data = @options.data()
if data? and not @options.filterByText
@ -195,6 +196,7 @@ class GitLabDropdown
@filter = new GitLabDropdownFilter @filterInput,
filterInputBlur: @filterInputBlur
filterByText: @options.filterByText
onFilter: @options.onFilter
remote: @options.filterRemote
query: @options.data
keys: searchFields
@ -530,7 +532,7 @@ class GitLabDropdown
if $el.length
e.preventDefault()
e.stopImmediatePropagation()
$(selector, @dropdown)[0].click()
$el.first().trigger('click')
addArrowKeyEvent: ->
ARROW_KEY_CODES = [38, 40]

View File

@ -7,13 +7,16 @@ class @ImporterStatus
$('.js-add-to-import')
.off 'click'
.on 'click', (e) =>
new_namespace = null
$btn = $(e.currentTarget)
$tr = $btn.closest('tr')
$target_field = $tr.find('.import-target')
$namespace_input = $target_field.find('input')
id = $tr.attr('id').replace('repo_', '')
if $tr.find('.import-target input').length > 0
new_namespace = $tr.find('.import-target input').prop('value')
$tr.find('.import-target').empty().append("#{new_namespace} / #{$tr.find('.import-target').data('project_name')}")
new_namespace = null
if $namespace_input.length > 0
new_namespace = $namespace_input.prop('value')
$target_field.empty().append("#{new_namespace}/#{$target_field.data('project_name')}")
$btn
.disable()

View File

@ -11,11 +11,11 @@ issuable_created = false
initTemplates: ->
Issuable.labelRow = _.template(
'<% _.each(labels, function(label){ %>
<span class="label-row btn-group" role="group" aria-label="<%= _.escape(label.title) %>" style="color: <%= label.text_color %>;">
<a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%= label.color %>;" title="<%= _.escape(label.description) %>" data-container="body">
<%= _.escape(label.title) %>
<span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;">
<a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body">
<%- label.title %>
</a>
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%= label.color %>;" data-label="<%= _.escape(label.title) %>">
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>">
<i class="fa fa-times"></i>
</button>
</span>

View File

@ -32,9 +32,9 @@ class @LabelsSelect
if issueUpdateURL
labelHTMLTemplate = _.template(
'<% _.each(labels, function(label){ %>
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= _.escape(label.title) %>">
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;">
<%= _.escape(label.title) %>
<a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>">
<span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">
<%- label.title %>
</span>
</a>
<% }); %>'
@ -261,7 +261,7 @@ class @LabelsSelect
$a.attr('data-label-id', label.id)
$a.addClass(selectedClass.join(' '))
.html("#{colorEl} #{_.escape(label.title)}")
.html("#{colorEl} #{label.title}")
# Return generated html
$li.html($a).prop('outerHTML')
@ -288,7 +288,7 @@ class @LabelsSelect
fieldName: $dropdown.data('field-name')
id: (label) ->
if $dropdown.hasClass("js-filter-submit") and not label.isAny?
_.escape label.title
label.title
else
label.id

View File

@ -0,0 +1 @@
#= require cropper

View File

@ -5,12 +5,12 @@
w.gl.utils.isInGroupsPage = ->
return $('body').data('page').split(':')[0] is 'groups'
return gl.utils.getPagePath() is 'groups'
w.gl.utils.isInProjectPage = ->
return $('body').data('page').split(':')[0] is 'projects'
return gl.utils.getPagePath() is 'projects'
w.gl.utils.getProjectSlug = ->
@ -40,6 +40,9 @@
e.stopImmediatePropagation()
return false
gl.utils.getPagePath = ->
return $('body').data('page').split(':')[0]
jQuery.timefor = (time, suffix, expiredLabel) ->

View File

@ -49,8 +49,9 @@
insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}"
if document.queryCommandSupported('insertText')
document.execCommand 'insertText', false, insertText
else
inserted = document.execCommand 'insertText', false, insertText
unless inserted
try
document.execCommand("ms-beginUndoUnit")

View File

@ -24,14 +24,14 @@ class @MilestoneSelect
if issueUpdateURL
milestoneLinkTemplate = _.template(
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>" class="bold has-tooltip" data-container="body" title="<%= remaining %>"><%= _.escape(title) %></a>'
'<a href="/<%- namespace %>/<%- path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'
)
milestoneLinkNoneTemplate = '<span class="no-value">None</span>'
collapsedSidebarLabelTemplate = _.template(
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
<%= _.escape(title) %>
'<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left">
<%- title %>
</span>'
)

View File

@ -1,25 +1,56 @@
class @NamespaceSelect
constructor: ->
namespaceFormatResult = (namespace) ->
markup = "<div class='namespace-result'>"
markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
markup += "<span class='namespace-path'>" + namespace.path + "</span>"
markup += "</div>"
markup
constructor: (opts) ->
{
@dropdown
} = opts
formatSelection = (namespace) ->
namespace.kind + ": " + namespace.path
showAny = true
fieldName = 'namespace_id'
$('.ajax-namespace-select').each (i, select) ->
$(select).select2
placeholder: "Search for namespace"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.namespaces query.term, (namespaces) ->
data = { results: namespaces }
query.callback(data)
if @dropdown.attr 'data-field-name'
fieldName = @dropdown.data 'fieldName'
dropdownCssClass: "ajax-namespace-dropdown"
formatResult: namespaceFormatResult
formatSelection: formatSelection
if @dropdown.attr 'data-show-any'
showAny = @dropdown.data 'showAny'
@dropdown.glDropdown(
filterable: true
selectable: true
filterRemote: true
search:
fields: ['path']
fieldName: fieldName
toggleLabel: (selected) ->
return if not selected.id? then selected.text else "#{selected.kind}: #{selected.path}"
data: (term, dataCallback) ->
Api.namespaces term, (namespaces) ->
if showAny
anyNamespace =
text: 'Any namespace'
id: null
namespaces.unshift(anyNamespace)
namespaces.splice 1, 0, 'divider'
dataCallback(namespaces)
text: (namespace) ->
return if not namespace.id? then namespace.text else "#{namespace.kind}: #{namespace.path}"
renderRow: @renderRow
clicked: @onSelectItem
)
onSelectItem: (item, el, e) =>
e.preventDefault()
class @NamespaceSelects
constructor: (opts = {}) ->
{
@$dropdowns = $('.js-namespace-select')
} = opts
@$dropdowns.each (i, dropdown) ->
$dropdown = $(dropdown)
new NamespaceSelect(
dropdown: $dropdown
)

View File

@ -100,13 +100,40 @@ class @Notes
$('.note .js-task-list-container').taskList('disable')
$(document).off 'tasklist:changed', '.note .js-task-list-container'
keydownNoteText: (e) ->
$this = $(this)
if $this.val() is '' and e.which is 38 and not isMetaKey e
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
if myLastNote.length
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
myLastNoteEditBtn.trigger('click', [true, myLastNote])
keydownNoteText: (e) =>
return if isMetaKey e
$textarea = $(e.target)
# Edit previous note when UP arrow is hit
switch e.which
when 38
return unless $textarea.val() is ''
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
if myLastNote.length
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
myLastNoteEditBtn.trigger('click', [true, myLastNote])
# Cancel creating diff note or editing any note when ESCAPE is hit
when 27
discussionNoteForm = $textarea.closest('.js-discussion-note-form')
if discussionNoteForm.length
if $textarea.val() isnt ''
return unless confirm('Are you sure you want to cancel creating this comment?')
@removeDiscussionNoteForm(discussionNoteForm)
return
editNote = $textarea.closest('.note')
if editNote.length
originalText = $textarea.closest('form').data('original-note')
newText = $textarea.val()
if originalText isnt newText
return unless confirm('Are you sure you want to cancel editing this comment?')
@removeNoteEditForm(editNote)
isMetaKey = (e) ->
(e.metaKey or e.ctrlKey or e.altKey or e.shiftKey)
@ -213,12 +240,16 @@ class @Notes
@note_ids.push(note.id)
form = $("#new-discussion-note-form-#{note.discussion_id}")
if note.original_discussion_id? and form.length is 0
form = $("#new-discussion-note-form-#{note.original_discussion_id}")
row = form.closest("tr")
note_html = $(note.html)
note_html.syntaxHighlight()
# is this the first note of discussion?
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']")
if note.original_discussion_id? and discussionContainer.length is 0
discussionContainer = $(".notes[data-discussion-id='" + note.original_discussion_id + "']")
if discussionContainer.length is 0
# insert the note and the reply button after the temp row
row.after note.discussion_html
@ -291,6 +322,7 @@ class @Notes
form.addClass "js-main-target-form"
form.find("#note_line_code").remove()
form.find("#note_position").remove()
form.find("#note_type").remove()
###
@ -308,10 +340,12 @@ class @Notes
new Autosave textarea, [
"Note"
form.find("#note_commit_id").val()
form.find("#note_line_code").val()
form.find("#note_noteable_type").val()
form.find("#note_noteable_id").val()
form.find("#note_commit_id").val()
form.find("#note_type").val()
form.find("#note_line_code").val()
form.find("#note_position").val()
]
###
@ -401,9 +435,12 @@ class @Notes
Hides edit form and restores the original note text to the editor textarea.
###
cancelEdit: (e) ->
cancelEdit: (e) =>
e.preventDefault()
note = $(this).closest(".note")
note = $(e.target).closest('.note')
@removeNoteEditForm(note)
removeNoteEditForm: (note) ->
form = note.find(".current-note-edit-form")
note.removeClass "is-editting"
form.removeClass("current-note-edit-form")
@ -482,10 +519,12 @@ class @Notes
setupDiscussionNoteForm: (dataHolder, form) =>
# setup note target
form.attr 'id', "new-discussion-note-form-#{dataHolder.data("discussionId")}"
form.attr "data-line-code", dataHolder.data("lineCode")
form.find("#note_type").val dataHolder.data("noteType")
form.find("#line_type").val dataHolder.data("lineType")
form.find("#note_commit_id").val dataHolder.data("commitId")
form.find("#note_line_code").val dataHolder.data("lineCode")
form.find("#note_position").val dataHolder.attr("data-position")
form.find("#note_noteable_type").val dataHolder.data("noteableType")
form.find("#note_noteable_id").val dataHolder.data("noteableId")
form.find('.js-note-discard')

View File

@ -0,0 +1,2 @@
#
#= require_tree .

View File

@ -78,3 +78,6 @@ $ ->
if comment && comment.length > 1 && $title.val() == ''
$title.val(comment[1]).change()
if gl.utils.getPagePath() == 'profiles'
new Profile()

View File

@ -0,0 +1,40 @@
class @ProtectedBranchSelect
constructor: (currentProject) ->
$('.dropdown-footer').hide();
@dropdown = $('.js-protected-branch-select').glDropdown(
data: @getProtectedBranches
filterable: true
remote: false
search:
fields: ['title']
selectable: true
toggleLabel: (selected) -> if (selected and 'id' of selected) then selected.title else 'Protected Branch'
fieldName: 'protected_branch[name]'
text: (protected_branch) -> _.escape(protected_branch.title)
id: (protected_branch) -> _.escape(protected_branch.id)
onFilter: @toggleCreateNewButton
clicked: () -> $('.protect-branch-btn').attr('disabled', false)
)
$('.create-new-protected-branch').on 'click', (event) =>
# Refresh the dropdown's data, which ends up calling `getProtectedBranches`
@dropdown.data('glDropdown').remote.execute()
@dropdown.data('glDropdown').selectRowAtIndex(event, 0)
getProtectedBranches: (term, callback) =>
if @selectedBranch
callback(gon.open_branches.concat(@selectedBranch))
else
callback(gon.open_branches)
toggleCreateNewButton: (branchName) =>
@selectedBranch = { title: branchName, id: branchName, text: branchName }
if branchName is ''
$('.protected-branch-select-footer-list').addClass('hidden')
$('.dropdown-footer').hide();
else
$('.create-new-protected-branch').text("Create Protected Branch: #{branchName}")
$('.protected-branch-select-footer-list').removeClass('hidden')
$('.dropdown-footer').show();

View File

@ -11,7 +11,8 @@ $ ->
dataType: "json"
data:
id: id
developers_can_push: checked
protected_branch:
developers_can_push: checked
success: ->
row = $(e.target)

View File

@ -61,8 +61,8 @@ class @UsersSelect
collapsedAssigneeTemplate = _.template(
'<% if( avatar ) { %>
<a class="author_link" href="/u/<%= username %>">
<img width="24" class="avatar avatar-inline s24" alt="" src="<%= avatar %>">
<a class="author_link" href="/u/<%- username %>">
<img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>">
<span class="author">Toni Boehm</span>
</a>
<% } else { %>
@ -72,13 +72,13 @@ class @UsersSelect
assigneeTemplate = _.template(
'<% if (username) { %>
<a class="author_link bold" href="/u/<%= username %>">
<a class="author_link bold" href="/u/<%- username %>">
<% if( avatar ) { %>
<img width="32" class="avatar avatar-inline s32" alt="" src="<%= avatar %>">
<img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>">
<% } %>
<span class="author"><%= name %></span>
<span class="author"><%- name %></span>
<span class="username">
@<%= username %>
@<%- username %>
</span>
</a>
<% } else { %>

View File

@ -1,3 +1,12 @@
.blank-state-welcome {
text-align: center;
border-bottom: 1px solid $border-color;
.blank-state-text {
margin-bottom: 0;
}
}
.blank-state {
padding-top: 20px;
padding-bottom: 20px;
@ -6,7 +15,15 @@
.blank-state-no-icon {
padding-top: 40px;
padding-bottom: 40px;
padding-bottom: 40px;
}
.blank-state-icon {
padding-bottom: 20px;
path {
fill: $gray-darkest;
}
}
.blank-state-title {
@ -21,3 +38,7 @@
margin-bottom: $gl-padding;
font-size: 15px;
}
.blank-state-welcome-title {
font-size: 24px;
}

View File

@ -281,3 +281,21 @@
color: $gl-icon-color;
}
}
.clone-dropdown-btn a {
color: $dropdown-link-color;
&:hover {
text-decoration: none;
}
}
.btn-static {
background-color: $background-color !important;
border: 1px solid lightgrey;
cursor: default;
&:active {
-moz-box-shadow: inset 0 0 0 white;
-webkit-box-shadow: inset 0 0 0 white;
box-shadow: inset 0 0 0 white;
}
}

View File

@ -20,8 +20,12 @@
}
.open {
.dropdown-menu {
.dropdown-menu,
.dropdown-menu-nav {
display: block;
@media (max-width: $screen-xs-max) {
width: 100%;
}
}
.dropdown-menu-toggle {
@ -64,9 +68,14 @@
color: $dropdown-toggle-hover-icon-color;
}
}
&.large {
width: 200px;
}
}
.dropdown-menu {
.dropdown-menu,
.dropdown-menu-nav {
display: none;
position: absolute;
top: 100%;
@ -77,7 +86,7 @@
margin-bottom: 0;
font-size: 15px;
font-weight: normal;
padding: 10px 0;
padding: 8px 0;
background-color: $dropdown-bg;
border: 1px solid $dropdown-border-color;
border-radius: $border-radius-base;
@ -101,12 +110,12 @@
li {
text-align: left;
list-style: none;
padding: 0 10px;
padding: 0 8px;
}
.divider {
height: 1px;
margin: 8px 10px;
margin: 8px;
padding: 0;
background-color: $dropdown-divider-color;
}
@ -122,7 +131,7 @@
a {
display: block;
position: relative;
padding: 5px 10px;
padding: 5px 8px;
color: $dropdown-link-color;
line-height: initial;
text-overflow: ellipsis;

View File

@ -50,7 +50,7 @@
}
a:not(.btn) {
color: $gl-dark-link-color;
color: $gl-text-color;
}
.left-options {

View File

@ -20,17 +20,6 @@
.sidebar-wrapper {
background: $color-darker;
.sidebar-user {
background: $color-darker;
color: $color-light;
&:hover {
background-color: $color-dark;
color: $white-light;
text-decoration: none;
}
}
}
.nav-sidebar li {

View File

@ -60,7 +60,7 @@ header {
margin: ($header-height - 28) / 2 0;
margin-left: 10px;
height: 28px;
width: 28px;
min-width: 28px;
line-height: 28px;
text-align: center;
@ -241,14 +241,23 @@ header {
.navbar-collapse {
padding-left: 5px;
li {
.nav > li {
display: table-cell;
width: 1%;
a {
margin-left: 8px !important;
}
}
}
}
}
.header-user {
.dropdown-menu-nav {
width: 140px;
margin-top: -5px;
}
}
.header-user-avatar {
float: left;
margin-right: 5px;
border-radius: 50%;
}

View File

@ -137,6 +137,15 @@ ul.content-list {
padding-top: 1px;
float: right;
> .control-text {
margin-right: $gl-padding-top;
line-height: 40px;
&:last-child {
margin-right: 0;
}
}
> .btn,
> .btn-group {
margin-right: $gl-padding-top;
@ -166,6 +175,12 @@ ul.content-list {
.panel > .content-list > li {
padding: $gl-padding-top $gl-padding;
&.commit {
@media (min-width: $screen-sm-min) {
padding-left: 46px + $gl-padding;
}
}
}
ul.controls {

View File

@ -77,10 +77,10 @@
&.sub-nav {
text-align: center;
background-color: $background-color;
background-color: $dark-background-color;
.container-fluid {
background-color: $background-color;
background-color: $dark-background-color;
margin-bottom: 0;
}
@ -134,6 +134,11 @@
margin-bottom: 0;
border-bottom: none;
&.wide {
width: 100%;
display: block;
}
li a {
padding: 16px 10px 11px;
}
@ -164,6 +169,7 @@
> .btn {
margin-right: $gl-padding-top;
display: inline-block;
vertical-align: top;
&:last-child {
margin-right: 0;

View File

@ -3,6 +3,12 @@
padding-bottom: 25px;
transition: padding $sidebar-transition-duration;
&.page-sidebar-pinned {
.sidebar-wrapper {
@include box-shadow(none);
}
}
.sidebar-wrapper {
position: fixed;
top: 0;
@ -11,6 +17,7 @@
height: 100%;
overflow: hidden;
transition: width $sidebar-transition-duration;
@include box-shadow(2px 0 16px 0 #bbb);
}
}
@ -40,32 +47,16 @@
}
}
.sidebar-user {
padding: 15px;
position: absolute;
left: 0;
bottom: 0;
width: $sidebar_width;
overflow: hidden;
font-size: 16px;
line-height: 36px;
transition: width $sidebar-transition-duration, padding $sidebar-transition-duration;
@media (min-width: $sidebar-breakpoint) {
bottom: 50px;
}
}
.nav-sidebar {
position: absolute;
top: 50px;
bottom: 65px;
bottom: 0;
width: $sidebar_width;
overflow-y: auto;
overflow-x: hidden;
@media (min-width: $sidebar-breakpoint) {
bottom: 115px;
bottom: 50px;
}
&.navbar-collapse {

View File

@ -12,10 +12,11 @@ $sidebar-breakpoint: 1024px;
/*
* UI elements
*/
$border-color: #e5e5e5;
$focus-border-color: #3aabf0;
$table-border-color: #f0f0f0;
$background-color: #fafafa;
$border-color: #e5e5e5;
$focus-border-color: #3aabf0;
$table-border-color: #f0f0f0;
$background-color: #fafafa;
$dark-background-color: #f7f7f7;
/*
* Text
@ -153,9 +154,6 @@ $warning-message-bg: #fbf2d9;
$warning-message-color: #9e8e60;
$warning-message-border: #f0e2bb;
/* header */
$light-grey-header: #faf9f9;
/* tanuki logo colors */
$tanuki-red: #e24329;
$tanuki-orange: #fc6d26;

View File

@ -71,3 +71,36 @@
@extend .broadcast-message;
margin-bottom: 20px;
}
// Users List
.users-list {
.user-row {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.user-details {
flex: 1 1 auto;
}
.user-name {
display: inline-block;
font-weight: bold;
}
.controls {
> .btn, > .dropdown {
margin-left: 5px;
}
}
.dropdown {
.btn-block {
margin-bottom: 0;
line-height: inherit;
}
}
}

View File

@ -434,13 +434,3 @@
}
}
}
.discussion {
.diff-content {
.diff-line-num {
&:before {
content: attr(data-linenumber);
}
}
}
}

View File

@ -38,6 +38,39 @@
margin-right: 15px;
}
}
&.group-admin {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
.group-avatar, .group-details, .group-controls {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.group-details {
flex: 1 1 auto;
flex-direction: column;
min-width: 0;
}
.group-controls {
align-items: center;
a {
margin-left: 5px;
}
}
}
}
.ldap-group-links {
.form-actions {
margin-bottom: $gl-padding;
}
}
.groups-cover-block {

View File

@ -63,8 +63,8 @@ form.edit-issue {
.merge-request,
.issue {
&.today {
background: #efe;
border-color: #cec;
background: #f8feef;
border-color: #e1e8d5;
}
&.closed {

View File

@ -167,7 +167,8 @@
.commit {
margin: 0;
padding: 2px 0;
padding-top: 2px;
padding-bottom: 2px;
list-style: none;
&:hover {
background: none;

View File

@ -64,86 +64,49 @@
}
.project-home-panel {
background: $white-light;
text-align: left;
padding: 24px 0;
padding-top: 24px;
padding-bottom: 24px;
.container-fluid {
position: relative;
@media (min-width: $screen-lg-min) {
.row {
display: flex;
-ms-flex-align: center;
-webkit-align-items: center;
-webkit-box-align: center;
}
}
@media (min-width: $screen-sm-min) {
border-bottom: 1px solid $border-color;
}
.cover-controls {
.project-settings-dropdown {
margin-left: 10px;
display: inline-block;
.dropdown-menu {
left: auto;
width: auto;
right: 0;
max-width: 240px;
}
}
}
.cover-title {
margin-bottom: 0;
}
.project-image-container {
@include make-sm-column(1);
max-width: 86px;
min-width: 86px;
padding-right: 0;
@media (max-width: $screen-md-max) {
padding-left: 0;
margin: 0 0 10px;
max-width: none;
min-width: none;
.avatar.s70 {
margin: auto;
}
}
}
.project-info {
@include make-sm-column(10);
h1 {
font-size: 24px;
font-weight: normal;
margin: 0;
}
.project-home-desc {
p {
margin: 0;
}
}
}
.identicon {
float: left;
@include border-radius(50%);
}
.avatar {
.project-avatar {
float: none;
margin-left: auto;
margin-right: auto;
&.identicon {
border-radius: 50%;
}
}
.project-title {
margin-top: 10px;
margin-bottom: 10px;
font-size: 24px;
font-weight: 400;
line-height: 1;
.fa {
margin-left: 2px;
font-size: 12px;
vertical-align: middle;
}
}
.project-home-desc {
margin-left: auto;
margin-right: auto;
margin-bottom: 15px;
max-width: 480px;
> p {
margin-bottom: 0;
}
}
.notifications-btn {
.fa-bell,
.fa-spinner {
margin-right: 6px;
@ -153,127 +116,106 @@
margin-left: 6px;
}
}
}
.project-repo-buttons {
font-size: 0;
.project-repo-buttons {
font-size: 0;
.btn {
@include btn-gray;
padding: 3px 10px;
text-transform: none;
background-color: $background-color;
.btn {
@include btn-gray;
padding: 3px 10px;
.fa {
color: $layout-link-gray;
}
.fa-caret-down {
margin-left: 3px;
}
.fa {
color: $layout-link-gray;
}
form {
margin-left: 10px;
.fa-caret-down {
margin-left: 3px;
}
}
.count-buttons {
display: inline-block;
vertical-align: top;
margin-top: 16px;
.project-repo-btn-group,
.notification-dropdown {
margin-left: 10px;
}
.count-buttons {
display: inline-block;
vertical-align: top;
}
.project-clone-holder {
display: inline-block;
input {
height: 29px;
}
}
.project-clone-holder {
display: inline-block;
margin-top: 16px;
.count-with-arrow {
display: inline-block;
position: relative;
margin-left: 4px;
input {
height: 29px;
}
}
.count-with-arrow {
display: inline-block;
position: relative;
margin-left: 4px;
.arrow {
&:before {
content: '';
display: inline-block;
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 0;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: #dce0e5;
}
&:after {
content: '';
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 1px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
border-right-color: #fff;
}
}
.count {
@include btn-gray;
.arrow {
&:before {
content: '';
display: inline-block;
background: white;
border-radius: 2px;
border-width: 1px;
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
font-size: 13px;
font-weight: 600;
line-height: 13px;
padding: $gl-vert-padding $gl-padding;
letter-spacing: .4px;
padding: 7px 14px;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
background-image: none;
white-space: nowrap;
margin: 0 10px 0 4px;
top: 50%;
left: 0;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: #dce0e5;
pointer-events: none;
}
a {
color: inherit;
}
&:hover {
background: #fff;
}
&:after {
content: '';
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 1px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
border-right-color: #fff;
pointer-events: none;
}
}
}
.count {
@include btn-gray;
display: inline-block;
background: white;
border-radius: 2px;
border-width: 1px;
border-style: solid;
font-size: 13px;
font-weight: 600;
line-height: 13px;
padding: $gl-vert-padding $gl-padding;
letter-spacing: .4px;
padding: 7px 14px;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
background-image: none;
white-space: nowrap;
margin: 0 10px 0 4px;
.project-right-buttons {
position: absolute;
right: 16px;
bottom: 0;
a {
color: inherit;
}
@media (max-width: $screen-md-max) {
top: 0;
}
}
@media (max-width: $screen-md-max) {
text-align: center;
.project-info,
.project-image-container {
width: 100%;
&:hover {
background: #fff;
}
}
}
}
@ -421,36 +363,42 @@ a.deploy-project-label {
}
.project-stats {
margin-top: $gl-padding;
margin-bottom: 0;
padding: 0;
background-color: $white-light;
font-size: 0;
border-bottom: 1px solid $border-color;
ul.nav {
display: inline-block;
.nav {
padding-top: 12px;
padding-bottom: 12px;
}
.nav li {
.nav > li {
display: inline-block;
margin: 16px 0;
margin-right: 16px;
&:not(:last-child) {
margin-right: $gl-padding;
}
&.project-repo-buttons-right {
margin-top: 10px;
@media (min-width: $screen-md-min) {
float: right;
margin-top: 0;
}
}
}
.nav > li > a {
padding: 0;
background-color: transparent;
padding: 5px 10px;
font-size: 15px;
line-height: 29px;
color: $notes-light-color;
}
li {
display: inline;
}
a {
float: left;
font-size: 17px;
&:hover,
&:focus {
color: darken($notes-light-color, 15%);
}
}
li.missing {
@ -458,6 +406,8 @@ a.deploy-project-label {
border-radius: $border-radius-default;
a {
padding-left: 10px;
padding-right: 10px;
color: $notes-light-color;
display: block;
}
@ -466,10 +416,6 @@ a.deploy-project-label {
background-color: $gray-normal;
}
}
&.row-content-block.second-block {
margin-top: 0;
}
}
pre.light-well {
@ -529,10 +475,6 @@ pre.light-well {
a:hover {
text-decoration: none;
}
> span {
margin-left: 10px;
}
}
}
@ -557,8 +499,32 @@ pre.light-well {
}
.project-last-commit {
@media (min-width: $screen-sm-min) {
margin-top: $gl-padding;
}
&.container-fluid {
padding-top: 12px;
padding-bottom: 12px;
background-color: $background-color;
border: 1px solid $border-color;
border-right-width: 0;
border-left-width: 0;
@media (min-width: $screen-sm-min) {
border-right-width: 1px;
border-left-width: 1px;
}
}
&.container-limited {
@media (min-width: 1281px) {
border-radius: $border-radius-base;
}
}
.ci-status {
margin-right: 16px;
margin-right: $gl-padding;
}
.commit-row-message {
@ -566,19 +532,12 @@ pre.light-well {
}
.commit_short_id {
margin: 0 5px;
margin-right: 5px;
color: $gl-link-color;
font-weight: 600;
}
.commit-author-link {
margin-left: 7px;
text-decoration: none;
.avatar {
float: none;
margin-right: 4px;
}
.commit-author-name {
font-weight: 600;
}
@ -601,15 +560,10 @@ pre.light-well {
}
.git-clone-holder {
width: 498px;
width: 380px;
.btn-clipboard {
border: 1px solid $border-color;
padding: 6px $gl-padding;
}
.project-home-dropdown + & {
margin-right: 45px;
}
.clone-options {

View File

@ -208,7 +208,7 @@
margin-top: 5px;
@media (min-width: $screen-sm-min) {
width: 160px;
width: 180px;
margin-top: 0;
}
}

View File

@ -87,6 +87,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:version_check_enabled,
:admin_notification_email,
:user_oauth_applications,
:user_default_external,
:shared_runners_enabled,
:shared_runners_text,
:max_artifacts_size,
@ -110,6 +111,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:send_user_confirmation_email,
:container_registry_token_expire_delay,
:repository_storage,
:enabled_git_access_protocol,
restricted_visibility_levels: [],
import_sources: [],
disabled_oauth_sign_in_sources: []

View File

@ -10,6 +10,7 @@ class Admin::GroupsController < Admin::ApplicationController
def show
@members = @group.members.order("access_level DESC").page(params[:members_page])
@requesters = @group.requesters
@projects = @group.projects.page(params[:projects_page])
end

View File

@ -22,7 +22,6 @@ class Admin::HooksController < Admin::ApplicationController
redirect_to admin_hooks_path
end
def test
@hook = SystemHook.find(params[:hook_id])
data = {

View File

@ -5,11 +5,12 @@ class Admin::ProjectsController < Admin::ApplicationController
def index
@projects = Project.all
@projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present?
@projects = @projects.where("projects.visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.where(last_repository_check_failed: true) if params[:last_repository_check_failed].present?
@projects = @projects.non_archived unless params[:with_archived].present?
@projects = @projects.non_archived unless params[:archived].present?
@projects = @projects.personal(current_user) if params[:personal].present?
@projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
@ -20,7 +21,8 @@ class Admin::ProjectsController < Admin::ApplicationController
@group_members = @group.members.order("access_level DESC").page(params[:group_members_page])
end
@project_members = @project.project_members.page(params[:project_members_page])
@project_members = @project.members.page(params[:project_members_page])
@requesters = @project.requesters
end
def transfer

View File

@ -10,7 +10,7 @@ module MembershipActions
end
def approve_access_request
@member = membershipable.members.request.find(params[:id])
@member = membershipable.requesters.find(params[:id])
return render_403 unless can?(current_user, action_member_permission(:update, @member), @member)
@ -20,7 +20,8 @@ module MembershipActions
end
def leave
@member = membershipable.members.find_by(user_id: current_user)
@member = membershipable.members.find_by(user_id: current_user) ||
membershipable.requesters.find_by(user_id: current_user)
Members::DestroyService.new(@member, current_user).execute
source_type = @member.real_source_type.humanize(capitalize: false)

View File

@ -1,5 +1,4 @@
class ConfirmationsController < Devise::ConfirmationsController
def almost_there
flash[:notice] = nil
render layout: "devise_empty"

View File

@ -7,7 +7,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
def index
@project = @group.projects.find(params[:project_id]) if params[:project_id]
@members = @group.group_members
@members = @members.non_pending unless can?(current_user, :admin_group, @group)
@members = @members.non_invite unless can?(current_user, :admin_group, @group)
if params[:search].present?
users = @group.users.search(params[:search]).to_a
@ -15,6 +15,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
end
@members = @members.order('access_level DESC').page(params[:page]).per(50)
@requesters = @group.requesters if can?(current_user, :admin_group, @group)
@group_member = @group.group_members.new
end
@ -34,7 +35,8 @@ class Groups::GroupMembersController < Groups::ApplicationController
end
def destroy
@group_member = @group.group_members.find(params[:id])
@group_member = @group.members.find_by(id: params[:id]) ||
@group.requesters.find_by(id: params[:id])
Members::DestroyService.new(@group_member, current_user).execute

View File

@ -1,5 +1,4 @@
class Import::BaseController < ApplicationController
private
def get_or_create_namespace

View File

@ -5,7 +5,6 @@ class Import::FogbugzController < Import::BaseController
rescue_from Fogbugz::AuthenticationException, with: :fogbugz_unauthorized
def new
end
def callback
@ -22,7 +21,6 @@ class Import::FogbugzController < Import::BaseController
end
def new_user_map
end
def create_user_map

View File

@ -27,10 +27,7 @@ class Import::GitlabProjectsController < Import::BaseController
notice: "Project '#{@project.name}' is being imported."
)
else
redirect_to(
new_import_gitlab_project_path,
alert: "Project could not be imported: #{@project.errors.full_messages.join(', ')}"
)
redirect_back_or_default(options: { alert: "Project could not be imported: #{@project.errors.full_messages.join(', ')}" })
end
end

View File

@ -44,5 +44,4 @@ class Import::GitoriousController < Import::BaseController
def verify_gitorious_import_enabled
render_404 unless gitorious_import_enabled?
end
end

View File

@ -3,7 +3,6 @@ class Import::GoogleCodeController < Import::BaseController
before_action :user_map, only: [:new_user_map, :create_user_map]
def new
end
def callback
@ -34,7 +33,6 @@ class Import::GoogleCodeController < Import::BaseController
end
def new_user_map
end
def create_user_map

View File

@ -5,7 +5,6 @@ class InvitesController < ApplicationController
respond_to :html
def show
end
def accept

View File

@ -57,7 +57,7 @@ class Projects::BlobController < Projects::ApplicationController
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true)
diff_lines = diffy.diff.scan(/.*\n/)[2..-1]
diff_lines = Gitlab::Diff::Parser.new.parse(diff_lines)
@diff_lines = Gitlab::Diff::Highlight.new(diff_lines).highlight
@diff_lines = Gitlab::Diff::Highlight.new(diff_lines, repository: @repository).highlight
render layout: false
end

View File

@ -121,7 +121,6 @@ class Projects::CommitController < Projects::ApplicationController
opts[:ignore_whitespace_change] = true if params[:format] == 'diff'
@diffs = commit.diffs(opts)
@diff_refs = [commit.parent || commit, commit]
@notes_count = commit.notes.count
@statuses = CommitStatus.where(pipeline: pipelines)

View File

@ -14,14 +14,22 @@ class Projects::CompareController < Projects::ApplicationController
def show
compare = CompareService.new.
execute(@project, @head_ref, @project, @base_ref, diff_options)
execute(@project, @head_ref, @project, @start_ref, diff_options)
if compare
@commits = Commit.decorate(compare.commits, @project)
@start_commit = @project.commit(@start_ref)
@commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@base_ref, @head_ref)
@base_commit = @project.merge_base_commit(@start_ref, @head_ref)
@diffs = compare.diffs(diff_options)
@diff_refs = [@base_commit, @commit]
@diff_refs = Gitlab::Diff::DiffRefs.new(
base_sha: @base_commit.try(:sha),
start_sha: @start_commit.try(:sha),
head_sha: @commit.try(:sha)
)
@diff_notes_disabled = true
@grouped_diff_notes = {}
end
@ -35,12 +43,12 @@ class Projects::CompareController < Projects::ApplicationController
private
def assign_ref_vars
@base_ref = Addressable::URI.unescape(params[:from])
@start_ref = Addressable::URI.unescape(params[:from])
@ref = @head_ref = Addressable::URI.unescape(params[:to])
end
def merge_request
@merge_request ||= @project.merge_requests.opened.
find_by(source_project: @project, source_branch: @head_ref, target_branch: @base_ref)
find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
end
end

View File

@ -1,4 +1,9 @@
# This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpController < Projects::ApplicationController
include ActionController::HttpAuthentication::Basic
include KerberosSpnegoHelper
attr_reader :user
# Git clients will not know what authenticity token to send along
@ -14,6 +19,8 @@ class Projects::GitHttpController < Projects::ApplicationController
render_ok
elsif receive_pack? && receive_pack_allowed?
render_ok
elsif http_blocked?
render_not_allowed
else
render_not_found
end
@ -40,9 +47,12 @@ class Projects::GitHttpController < Projects::ApplicationController
private
def authenticate_user
return if project && project.public? && upload_pack?
if project && project.public? && upload_pack?
return # Allow access
end
authenticate_or_request_with_http_basic do |login, password|
if allow_basic_auth? && basic_auth_provided?
login, password = user_name_and_password(request)
auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
if auth_result.type == :ci && upload_pack?
@ -53,8 +63,31 @@ class Projects::GitHttpController < Projects::ApplicationController
@user = auth_result.user
end
ci? || user
if ci? || user
return # Allow access
end
elsif allow_kerberos_spnego_auth? && spnego_provided?
@user = find_kerberos_user
if user
send_final_spnego_response
return # Allow access
end
end
send_challenges
render plain: "HTTP Basic: Access denied\n", status: 401
end
def basic_auth_provided?
has_basic_credentials?(request)
end
def send_challenges
challenges = []
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
challenges << spnego_challenge if allow_kerberos_spnego_auth?
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
end
def ensure_project_found!
@ -120,7 +153,11 @@ class Projects::GitHttpController < Projects::ApplicationController
end
def render_not_found
render text: 'Not Found', status: :not_found
render plain: 'Not Found', status: :not_found
end
def render_not_allowed
render plain: download_access.message, status: :forbidden
end
def ci?
@ -131,12 +168,28 @@ class Projects::GitHttpController < Projects::ApplicationController
return false unless Gitlab.config.gitlab_shell.upload_pack
if user
Gitlab::GitAccess.new(user, project).download_access_check.allowed?
download_access.allowed?
else
ci? || project.public?
end
end
def access
return @access if defined?(@access)
@access = Gitlab::GitAccess.new(user, project, 'http')
end
def download_access
return @download_access if defined?(@download_access)
@download_access = access.check('git-upload-pack')
end
def http_blocked?
!access.protocol_allowed?
end
def receive_pack_allowed?
return false unless Gitlab.config.gitlab_shell.receive_pack

View File

@ -76,7 +76,6 @@ class Projects::IssuesController < Projects::ApplicationController
render json: @issue.to_json(include: [:milestone, :labels])
end
end
end
def create

View File

@ -9,7 +9,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
:ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip
]
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
@ -53,19 +52,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def show
@note_counts = Note.where(commit_id: @merge_request.commits.map(&:id)).
group(:commit_id).count
respond_to do |format|
format.html
format.json { render json: @merge_request }
format.patch do
headers.store(*Gitlab::Workhorse.send_git_patch(@project.repository,
@merge_request.diff_base_commit.id,
@merge_request.last_commit.id))
headers['Content-Disposition'] = 'inline'
head :ok
format.json do
render json: @merge_request
end
format.patch do
return render_404 unless @merge_request.diff_refs
send_git_patch @project.repository, @merge_request.diff_refs
end
format.diff do
return render_404 unless @merge_request.diff_refs
@ -77,18 +76,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diffs
apply_diff_view_cookie!
@commit = @merge_request.last_commit
@base_commit = @merge_request.diff_base_commit
@merge_request_diff = @merge_request.merge_request_diff
# MRs created before 8.4 don't have a diff_base_commit,
# but we need it for the "View file @ ..." link by deleted files
@base_commit ||= @merge_request.first_commit.parent || @merge_request.first_commit
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
@comments_target = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes
Banzai::NoteRenderer.render(
@ -109,7 +107,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def commits
respond_to do |format|
format.html { render 'show' }
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_commits') } }
format.json do
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
render json: { html: view_to_html_string('projects/merge_requests/show/_commits') }
end
end
end
@ -134,7 +140,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@target_project = merge_request.target_project
@source_project = merge_request.source_project
@commits = @merge_request.compare_commits.reverse
@commit = @merge_request.last_commit
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit
@diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
@diff_notes_disabled = true
@ -212,7 +218,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return
end
if params[:sha] != @merge_request.source_sha
if params[:sha] != @merge_request.diff_head_sha
@status = :sha_mismatch
return
end
@ -274,16 +280,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController
status ||= "preparing"
else
ci_service = @merge_request.source_project.ci_service
status = ci_service.commit_status(merge_request.last_commit.sha, merge_request.source_branch) if ci_service
status = ci_service.commit_status(merge_request.diff_head_sha, merge_request.source_branch) if ci_service
if ci_service.respond_to?(:commit_coverage)
coverage = ci_service.commit_coverage(merge_request.last_commit.sha, merge_request.source_branch)
coverage = ci_service.commit_coverage(merge_request.diff_head_sha, merge_request.source_branch)
end
end
response = {
title: merge_request.title,
sha: merge_request.last_commit_short_sha,
sha: merge_request.diff_head_commit.short_id,
status: status,
coverage: coverage
}
@ -308,10 +314,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
alias_method :issuable, :merge_request
alias_method :awardable, :merge_request
def closes_issues
@closes_issues ||= @merge_request.closes_issues
end
def authorize_update_merge_request!
return render_404 unless can?(current_user, :update_merge_request, @merge_request)
end
@ -340,30 +342,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def define_show_vars
# Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request)
@discussions = @merge_request.mr_and_commit_notes.
inc_author_project_award_emoji.
fresh.
discussions
@notes = Banzai::NoteRenderer.render(
@discussions.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
@noteable = @merge_request
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@merge_request_diff = @merge_request.merge_request_diff
@commits_count = @merge_request.commits.count
@pipeline = @merge_request.pipeline
@statuses = @pipeline.statuses if @pipeline
@ -372,12 +352,36 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.unlock_mr
@merge_request.close
end
if request.format == :html || action_name == 'show'
define_show_html_vars
end
end
# Discussion tab data is only required on html requests
def define_show_html_vars
# Build a note object for comment form
@note = @project.notes.new(noteable: @noteable)
@discussions = @noteable.mr_and_commit_notes.
inc_author_project_award_emoji.
fresh.
discussions
# This is not executed lazily
@notes = Banzai::NoteRenderer.render(
@discussions.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
end
def define_widget_vars
@pipeline = @merge_request.pipeline
@pipelines = [@pipeline].compact
closes_issues
end
def invalid_mr

View File

@ -7,7 +7,6 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :authorize_download_code!
def show
@url = namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))
@commit_url = namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")

View File

@ -128,7 +128,7 @@ class Projects::NotesController < Projects::ApplicationController
elsif note.valid?
Banzai::NoteRenderer.render([note], @project, current_user)
{
attrs = {
valid: true,
id: note.id,
discussion_id: note.discussion_id,
@ -138,6 +138,23 @@ class Projects::NotesController < Projects::ApplicationController
discussion_html: note_to_discussion_html(note),
discussion_with_diff_html: note_to_discussion_with_diff_html(note)
}
# The discussion_id is used to add the comment to the correct discussion
# element on the merge request page. Among other things, the discussion_id
# contains the sha of head commit of the merge request.
# When new commits are pushed into the merge request after the initial
# load of the merge request page, the discussion elements will still have
# the old discussion_ids, with the old head commit sha. The new comment,
# however, will have the new discussion_id with the new commit sha.
# To ensure that these new comments will still end up in the correct
# discussion element, we also send the original discussion_id, with the
# old commit sha, along, and fall back on this value when no discussion
# element with the new discussion_id could be found.
if note.new_diff_note? && note.position != note.original_position
attrs[:original_discussion_id] = note.original_discussion_id
end
attrs
else
{
valid: false,
@ -154,7 +171,7 @@ class Projects::NotesController < Projects::ApplicationController
def note_params
params.require(:note).permit(
:note, :noteable, :noteable_id, :noteable_type, :project_id,
:attachment, :line_code, :commit_id, :type
:attachment, :line_code, :commit_id, :type, :position
)
end

View File

@ -6,7 +6,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
def index
@project_members = @project.project_members
@project_members = @project_members.non_pending unless can?(current_user, :admin_project, @project)
@project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project)
if params[:search].present?
users = @project.users.search(params[:search]).to_a
@ -19,7 +19,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
if @group
@group_members = @group.group_members
@group_members = @group_members.non_pending unless can?(current_user, :admin_group, @group)
@group_members = @group_members.non_invite unless can?(current_user, :admin_group, @group)
if params[:search].present?
users = @group.users.search(params[:search]).to_a
@ -29,6 +29,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController
@group_members = @group_members.order('access_level DESC')
end
@requesters = @project.requesters if can?(current_user, :admin_project, @project)
@project_member = @project.project_members.new
@project_group_links = @project.project_group_links
end
@ -48,7 +50,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end
def destroy
@project_member = @project.project_members.find(params[:id])
@project_member = @project.members.find_by(id: params[:id]) ||
@project.requesters.find_by(id: params[:id])
Members::DestroyService.new(@project_member, current_user).execute

View File

@ -2,12 +2,14 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_admin_project!
before_action :load_protected_branch, only: [:show, :update, :destroy]
layout "project_settings"
def index
@branches = @project.protected_branches.to_a
@protected_branches = @project.protected_branches.order(:name).page(params[:page])
@protected_branch = @project.protected_branches.new
gon.push({ open_branches: @project.open_branches.map { |br| { text: br.name, id: br.name, title: br.name } } })
end
def create
@ -16,26 +18,24 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
@project)
end
def show
@matching_branches = @protected_branch.matching(@project.repository.branches)
end
def update
protected_branch = @project.protected_branches.find(params[:id])
if protected_branch &&
protected_branch.update_attributes(
developers_can_push: params[:developers_can_push]
)
if @protected_branch && @protected_branch.update_attributes(protected_branch_params)
respond_to do |format|
format.json { render json: protected_branch, status: :ok }
format.json { render json: @protected_branch, status: :ok }
end
else
respond_to do |format|
format.json { render json: protected_branch.errors, status: :unprocessable_entity }
format.json { render json: @protected_branch.errors, status: :unprocessable_entity }
end
end
end
def destroy
@project.protected_branches.find(params[:id]).destroy
@protected_branch.destroy
respond_to do |format|
format.html { redirect_to namespace_project_protected_branches_path }
@ -45,6 +45,10 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
private
def load_protected_branch
@protected_branch = @project.protected_branches.find(params[:id])
end
def protected_branch_params
params.require(:protected_branch).permit(:name, :developers_can_push)
end

View File

@ -54,7 +54,7 @@ class Projects::SnippetsController < Projects::ApplicationController
def show
@note = @project.notes.new(noteable: @snippet)
@notes = @snippet.notes.fresh
@notes = Banzai::NoteRenderer.render(@snippet.notes.fresh, @project, current_user)
@noteable = @snippet
end

View File

@ -124,5 +124,4 @@ class Projects::WikisController < Projects::ApplicationController
def wiki_params
params[:wiki].slice(:title, :content, :format, :message)
end
end

View File

@ -53,11 +53,11 @@ class ProjectsController < Projects::ApplicationController
notice: "Project '#{@project.name}' was successfully updated."
)
end
format.js
else
format.html { render 'edit' }
format.js
end
format.js
end
end

View File

@ -25,6 +25,7 @@ class TodosFinder
def execute
items = current_user.todos
items = by_action_id(items)
items = by_action(items)
items = by_author(items)
items = by_project(items)
items = by_state(items)
@ -43,6 +44,18 @@ class TodosFinder
params[:action_id]
end
def to_action_id
Todo::ACTION_NAMES.key(action.to_sym)
end
def action?
action.present? && to_action_id
end
def action
params[:action]
end
def author?
params[:author_id].present?
end
@ -96,6 +109,14 @@ class TodosFinder
params[:type]
end
def by_action(items)
if action?
items = items.where(action: to_action_id)
end
items
end
def by_action_id(items)
if action_id?
items = items.where(action: action_id)

View File

@ -31,7 +31,7 @@ module AppearancesHelper
end
end
def navbar_icon(icon_name)
render "shared/icons/#{icon_name}.svg"
def navbar_icon(icon_name, size: 16)
render "shared/icons/#{icon_name}.svg", size: size
end
end

View File

@ -306,4 +306,15 @@ module ApplicationHelper
def truncate_first_line(message, length = 50)
truncate(message.each_line.first.chomp, length: length) if message
end
# While similarly named to Rails's `link_to_if`, this method behaves quite differently.
# If `condition` is truthy, a link will be returned with the result of the block
# as its body. If `condition` is falsy, only the result of the block will be returned.
def conditional_link_to(condition, options, html_options = {}, &block)
if condition
link_to options, html_options, &block
else
capture(&block)
end
end
end

View File

@ -31,6 +31,28 @@ module ApplicationSettingsHelper
current_application_settings.akismet_enabled?
end
def allowed_protocols_present?
current_application_settings.enabled_git_access_protocol.present?
end
def enabled_protocol
case current_application_settings.enabled_git_access_protocol
when 'http'
gitlab_config.protocol
when 'ssh'
'ssh'
end
end
def enabled_project_button(project, protocol)
case protocol
when 'ssh'
ssh_clone_button(project, 'bottom', append_link: false)
else
http_clone_button(project, 'bottom', append_link: false)
end
end
# Return a group of checkboxes that use Bootstrap's button plugin for a
# toggle button effect.
def restricted_level_checkboxes(help_block_id)

View File

@ -12,7 +12,7 @@ module BranchesHelper
def can_push_branch?(project, branch_name)
return false unless project.repository.branch_exists?(branch_name)
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(branch_name)
end
def project_branches

View File

@ -40,33 +40,33 @@ module ButtonHelper
type: :button
end
def http_clone_button(project)
def http_clone_button(project, placement = 'right', append_link: true)
klass = 'http-selector'
klass << ' has-tooltip' if current_user.try(:require_password?)
protocol = gitlab_config.protocol.upcase
content_tag :a, protocol,
content_tag (append_link ? :a : :span), protocol,
class: klass,
href: project.http_url_to_repo,
href: (project.http_url_to_repo if append_link),
data: {
html: true,
placement: 'right',
placement: placement,
container: 'body',
title: "Set a password on your account<br>to pull or push via #{protocol}"
}
end
def ssh_clone_button(project)
def ssh_clone_button(project, placement = 'right', append_link: true)
klass = 'ssh-selector'
klass << ' has-tooltip' if current_user.try(:require_ssh_key?)
content_tag :a, 'SSH',
content_tag (append_link ? :a : :span), 'SSH',
class: klass,
href: project.ssh_url_to_repo,
href: (project.ssh_url_to_repo if append_link),
data: {
html: true,
placement: 'right',
placement: placement,
container: 'body',
title: 'Add an SSH key to your profile<br>to pull or push via SSH.'
}

View File

@ -30,12 +30,8 @@ module DiffHelper
options
end
def safe_diff_files(diffs, diff_refs)
diffs.decorate! { |diff| Gitlab::Diff::File.new(diff, diff_refs) }
end
def generate_line_code(file_path, line)
Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
def safe_diff_files(diffs, diff_refs: nil, repository: nil)
diffs.decorate! { |diff| Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) }
end
def unfold_bottom_class(bottom)
@ -93,6 +89,8 @@ module DiffHelper
end
def commit_for_diff(diff_file)
return diff_file.content_commit if diff_file.content_commit
if diff_file.deleted_file
@base_commit || @commit.parent || @commit
else
@ -100,10 +98,11 @@ module DiffHelper
end
end
def diff_file_html_data(project, diff_commit, diff_file)
def diff_file_html_data(project, diff_file)
commit = commit_for_diff(diff_file)
{
blob_diff_path: namespace_project_blob_diff_path(project.namespace, project,
tree_join(diff_commit.id, diff_file.file_path))
tree_join(commit.id, diff_file.file_path))
}
end

View File

@ -39,7 +39,7 @@ module DropdownsHelper
end
end
def dropdown_toggle(toggle_text, data_attr, options)
def dropdown_toggle(toggle_text, data_attr, options = {})
content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text")
output << icon('chevron-down')

View File

@ -1,5 +1,4 @@
module EmailsHelper
# Google Actions
# https://developers.google.com/gmail/markup/reference/go-to-action
def email_action(url)

View File

@ -1,5 +1,4 @@
module IssuablesHelper
def sidebar_gutter_toggle_icon
sidebar_gutter_collapsed? ? icon('angle-double-left') : icon('angle-double-right')
end

View File

@ -118,7 +118,7 @@ module IssuesHelper
end
def emoji_icon(name, unicode = nil, aliases = [], sprite: true)
unicode ||= Emoji.emoji_filename(name) rescue ""
unicode ||= Gitlab::Emoji.emoji_filename(name) rescue ""
data = {
aliases: aliases.join(" "),

View File

@ -0,0 +1,9 @@
module KerberosSpnegoHelper
def allow_basic_auth?
true # different behavior in GitLab Enterprise Edition
end
def allow_kerberos_spnego_auth?
false # different behavior in GitLab Enterprise Edition
end
end

View File

@ -27,7 +27,7 @@ module MergeRequestsHelper
end
def ci_build_details_path(merge_request)
build_url = merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch)
build_url = merge_request.source_project.ci_service.build_page(merge_request.diff_head_sha, merge_request.source_branch)
return nil unless build_url
parsed_url = URI.parse(build_url)
@ -55,6 +55,10 @@ module MergeRequestsHelper
end.sort.to_sentence
end
def mr_closes_issues
@mr_closes_issues ||= @merge_request.closes_issues
end
def mr_change_branches_path(merge_request)
new_namespace_project_merge_request_path(
@project.namespace, @project,

View File

@ -24,23 +24,55 @@ module NotesHelper
}.to_json
end
def link_to_new_diff_note(line_code, line_type = nil)
discussion_id = LegacyDiffNote.build_discussion_id(
@comments_target[:noteable_type],
@comments_target[:noteable_id] || @comments_target[:commit_id],
line_code
)
def link_to_new_diff_note(line_code, position, line_type = nil)
use_legacy_diff_note = @use_legacy_diff_notes
# If the controller doesn't force the use of legacy diff notes, we
# determine this on a line-by-line basis by seeing if there already exist
# active legacy diff notes at this line, in which case newly created notes
# will use the legacy technology as well.
# We do this because the discussion_id values of legacy and "new" diff
# notes, which are used to group notes on the merge request discussion tab,
# are incompatible.
# If we didn't, diff notes that would show for the same line on the changes
# tab, would show in different discussions on the discussion tab.
use_legacy_diff_note ||= begin
line_diff_notes = @grouped_diff_notes[line_code]
line_diff_notes && line_diff_notes.any?(&:legacy_diff_note?)
end
data = {
noteable_type: @comments_target[:noteable_type],
noteable_id: @comments_target[:noteable_id],
commit_id: @comments_target[:commit_id],
line_type: line_type,
line_code: line_code,
note_type: LegacyDiffNote.name,
discussion_id: discussion_id
line_code: line_code
}
if use_legacy_diff_note
discussion_id = LegacyDiffNote.build_discussion_id(
@comments_target[:noteable_type],
@comments_target[:noteable_id] || @comments_target[:commit_id],
line_code
)
data.merge!(
note_type: LegacyDiffNote.name,
discussion_id: discussion_id
)
else
discussion_id = DiffNote.build_discussion_id(
@comments_target[:noteable_type],
@comments_target[:noteable_id] || @comments_target[:commit_id],
position
)
data.merge!(
position: position.to_json,
note_type: DiffNote.name,
discussion_id: discussion_id
)
end
button_tag(class: 'btn add-diff-note js-add-diff-note-button',
data: data,
title: 'Add a comment to this line') do
@ -60,14 +92,15 @@ module NotesHelper
}
if note.diff_note?
data.merge!(
line_code: note.line_code,
note_type: LegacyDiffNote.name
)
data[:note_type] = note.type
data.merge!(note.diff_attributes)
end
button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button',
data: data, title: 'Add a reply'
content_tag(:div, class: "discussion-reply-holder") do
button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button',
data: data, title: 'Add a reply'
end
end
def note_max_access_for_user(note)
@ -79,4 +112,14 @@ module NotesHelper
full_key = { project: note.project, user_id: note.author_id }
@max_access_by_user_id[full_key]
end
def diff_note_path(note)
return unless note.diff_note?
if note.for_merge_request? && note.active?
diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code)
elsif note.for_commit?
namespace_project_commit_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code)
end
end
end

View File

@ -206,10 +206,14 @@ module ProjectsHelper
end
def default_clone_protocol
if !current_user || current_user.require_ssh_key?
gitlab_config.protocol
if allowed_protocols_present?
enabled_protocol
else
"ssh"
if !current_user || current_user.require_ssh_key?
gitlab_config.protocol
else
'ssh'
end
end
end
@ -289,7 +293,11 @@ module ProjectsHelper
end
def last_push_event
if current_user
return unless current_user
if fork = current_user.fork_of(@project)
current_user.recent_push(fork.id)
else
current_user.recent_push(@project.id)
end
end

View File

@ -1,5 +1,4 @@
module SearchHelper
def search_autocomplete_opts(term)
return unless current_user

View File

@ -23,4 +23,11 @@ module TimeHelper
def date_from_to(from, to)
"#{from.to_s(:short)} - #{to.to_s(:short)}"
end
def duration_in_numbers(finished_at, started_at)
diff_in_seconds = finished_at.to_i - started_at.to_i
time_format = diff_in_seconds < 1.hour ? "%M:%S" : "%H:%M:%S"
Time.at(diff_in_seconds).utc.strftime(time_format)
end
end

View File

@ -1,4 +1,4 @@
# Helpers to send Git blobs, diffs or archives through Workhorse.
# Helpers to send Git blobs, diffs, patches or archives through Workhorse.
# Workhorse will also serve files when using `send_file`.
module WorkhorseHelper
# Send a Git blob through Workhorse
@ -16,6 +16,13 @@ module WorkhorseHelper
head :ok
end
# Send a Git patch through Workhorse
def send_git_patch(repository, diff_refs)
headers.store(*Gitlab::Workhorse.send_git_patch(repository, diff_refs))
headers['Content-Disposition'] = 'inline'
head :ok
end
# Archive a Git repository and send it through Workhorse
def send_git_archive(repository, ref:, format:)
headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))

View File

@ -29,8 +29,7 @@ module Emails
# used in notify layout
@target_url = @message.target_url
@project = Project.find(project_id)
@diff_notes_disabled = true
add_project_headers
headers['X-GitLab-Author'] = @message.author_username

View File

@ -157,10 +157,11 @@ class Ability
# Push abilities on the users team role
rules.push(*project_team_rules(project.team, user))
if project.owner == user ||
(project.group && project.group.has_owner?(user)) ||
user.admin?
owner = user.admin? ||
project.owner == user ||
(project.group && project.group.has_owner?(user))
if owner
rules.push(*project_owner_rules)
end
@ -169,6 +170,10 @@ class Ability
# Allow to read builds for internal projects
rules << :read_build if project.public_builds?
unless owner || project.team.member?(user) || project_group_member?(project, user)
rules << :request_access
end
end
if project.archived?
@ -345,8 +350,11 @@ class Ability
rules = []
rules << :read_group if can_read_group?(user, group)
owner = user.admin? || group.has_owner?(user)
master = owner || group.has_master?(user)
# Only group masters and group owners can create new projects
if group.has_master?(user) || group.has_owner?(user) || user.admin?
if master
rules += [
:create_projects,
:admin_milestones
@ -354,7 +362,7 @@ class Ability
end
# Only group owner and administrators can admin group
if group.has_owner?(user) || user.admin?
if owner
rules += [
:admin_group,
:admin_namespace,
@ -363,6 +371,10 @@ class Ability
]
end
if group.public? || (group.internal? && !user.external?)
rules << :request_access unless group.users.include?(user)
end
rules.flatten
end
@ -564,5 +576,13 @@ class Ability
rules
end
def project_group_member?(project, user)
project.group &&
(
project.group.members.exists?(user_id: user.id) ||
project.group.requesters.exists?(user_id: user.id)
)
end
end
end

View File

@ -59,6 +59,9 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
validates :enabled_git_access_protocol,
inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true }
validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil?
value.each do |level|
@ -139,6 +142,7 @@ class ApplicationSetting < ActiveRecord::Base
send_user_confirmation_email: false,
container_registry_token_expire_delay: 5,
repository_storage: 'default',
user_default_external: false,
)
end

View File

@ -8,7 +8,7 @@ class AwardEmoji < ActiveRecord::Base
belongs_to :user
validates :awardable, :user, presence: true
validates :name, presence: true, inclusion: { in: Emoji.emojis_names }
validates :name, presence: true, inclusion: { in: Gitlab::Emoji.emojis_names }
validates :name, uniqueness: { scope: [:user, :awardable_type, :awardable_id] }
participant :user

View File

@ -13,21 +13,19 @@ module Ci
scope :ignore_failures, ->() { where(allow_failure: false) }
scope :with_artifacts, ->() { where.not(artifacts_file: nil) }
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader
acts_as_taggable
before_save :update_artifacts_size, if: :artifacts_file_changed?
before_destroy { project }
after_create :execute_hooks
class << self
def last_month
where('created_at > ?', Date.today - 1.month)
end
def first_pending
pending.unstarted.order('created_at ASC').first
end
@ -329,7 +327,12 @@ module Ci
end
def artifacts_metadata_entry(path, **options)
Gitlab::Ci::Build::Artifacts::Metadata.new(artifacts_metadata.path, path, **options).to_entry
metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
artifacts_metadata.path,
path,
**options)
metadata.to_entry
end
def erase_artifacts!
@ -375,6 +378,14 @@ module Ci
private
def update_artifacts_size
self.artifacts_size = if artifacts_file.exists?
artifacts_file.size
else
nil
end
end
def erase_trace!
self.trace = nil
end

View File

@ -16,6 +16,7 @@ module Ci
# Invalidate object and save if when touched
after_touch :update_state
after_save :keep_around_commits
def self.truncate_sha(sha)
sha[0...8]
@ -212,5 +213,10 @@ module Ci
self.duration = statuses.latest.duration
save
end
def keep_around_commits
project.repository.keep_around(self.sha)
project.repository.keep_around(self.before_sha)
end
end
end

View File

@ -1,7 +1,7 @@
module Ci
class TriggerRequest < ActiveRecord::Base
extend Ci::Model
belongs_to :trigger, class_name: 'Ci::Trigger'
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build'

View File

@ -214,6 +214,13 @@ class Commit
@raw.short_id(7)
end
def diff_refs
Gitlab::Diff::DiffRefs.new(
base_sha: self.parent_id || self.sha,
head_sha: self.sha
)
end
def pipelines
@pipeline ||= project.pipelines.where(sha: sha)
end

View File

@ -23,7 +23,7 @@ class CommitRange
attr_reader :commit_from, :notation, :commit_to
attr_reader :ref_from, :ref_to
# Optional Project model
# The Project model
attr_accessor :project
# The beginning and ending refs can be named or SHAs, and
@ -56,7 +56,7 @@ class CommitRange
# Initialize a CommitRange
#
# range_string - The String commit range.
# project - An optional Project model.
# project - The Project model.
#
# Raises ArgumentError if `range_string` does not match `PATTERN`.
def initialize(range_string, project)

View File

@ -58,7 +58,6 @@ module Issuable
scope :references_project, -> { references(:project) }
scope :non_archived, -> { join_project.where(projects: { archived: false }) }
delegate :name,
:email,
to: :author,

View File

@ -45,7 +45,7 @@ module Mentionable
def all_references(current_user = nil, text = nil, extractor: nil)
extractor ||= Gitlab::ReferenceExtractor.
new(project, current_user || author)
new(project, current_user)
if text
extractor.analyze(text, author: author)

Some files were not shown because too many files have changed in this diff Show More