Commit 4b1f5f95 authored by Valery Sizov's avatar Valery Sizov

Merge remote-tracking branch 'origin/master' into ce_upstream

parents 22083ed2 561acdeb
...@@ -12,6 +12,7 @@ before_script: ...@@ -12,6 +12,7 @@ before_script:
spec:feature: spec:feature:
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
tags: tags:
- ruby - ruby
......
...@@ -3,18 +3,28 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -3,18 +3,28 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.3.0 (unreleased) v 8.3.0 (unreleased)
- Merge when build succeeds (Zeger-Jan van de Weg) - Merge when build succeeds (Zeger-Jan van de Weg)
v 8.4.0 (unreleased) v 8.4.0 (unreleased)
- Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu)
- Implement new UI for group page - Implement new UI for group page
- Implement search inside emoji picker - Implement search inside emoji picker
- Add API support for looking up a user by username (Stan Hu) - Add API support for looking up a user by username (Stan Hu)
- Add project permissions to all project API endpoints (Stan Hu) - Add project permissions to all project API endpoints (Stan Hu)
- Expose Git's version in the admin area - Only allow group/project members to mention `@all`
- Expose Git's version in the admin area (Trey Davis)
- Add "Frequently used" category to emoji picker - Add "Frequently used" category to emoji picker
- Add CAS support (tduehr) - Add CAS support (tduehr)
- Add link to merge request on build detail page. - Add link to merge request on build detail page
- Revert back upvote and downvote button to the issue and MR pages - Revert back upvote and downvote button to the issue and MR pages
- Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg)
- Add system hook messages for project rename and transfer (Steve Norman)
- Fix version check image in Safari
v 8.3.2 (unreleased) v 8.3.3 (unreleased)
- Enable "Add key" button when user fills in a proper key - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu)
- Enable "Add key" button when user fills in a proper key (Stan Hu)
v 8.3.2
- Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu)
- Add support for Google reCAPTCHA in user registration
v 8.3.1 v 8.3.1
- Fix Error 500 when global milestones have slashes (Stan Hu) - Fix Error 500 when global milestones have slashes (Stan Hu)
......
...@@ -155,6 +155,28 @@ sudo -u git -H bundle exec rake gitlab:env:info) ...@@ -155,6 +155,28 @@ sudo -u git -H bundle exec rake gitlab:env:info)
``` ```
### Issue weight
Issue weight allows us to get an idea of the amount of work required to solve
one or multiple issues. This makes it possible to schedule work more accurately.
You are encouraged to set the weight of any issue. Following the guidelines
below will make it easy to manage this, without unnecessary overhead.
1. Set weight for any issue at the earliest possible convenience
1. If you don't agree with a set weight, discuss with other developers until
consensus is reached about the weight
1. Issue weights are an abstract measurement of complexity of the issue. Do not
relate issue weight directly to time. This is called [anchoring](https://en.wikipedia.org/wiki/Anchoring)
and something you want to avoid.
1. Something that has a weight of 1 (or no weight) is really small and simple.
Something that is 9 is rewriting a large fundamental part of GitLab,
which might lead to many hard problems to solve. Changing some text in GitLab
is probably 1, adding a new Git Hook maybe 4 or 5, big features 7-9.
1. If something is very large, it should probably be split up in multiple
issues or chunks. You can simply not set the weight of a parent issue and set
weights to children issues.
## Merge requests ## Merge requests
We welcome merge requests with fixes and improvements to GitLab code, tests, We welcome merge requests with fixes and improvements to GitLab code, tests,
......
...@@ -36,6 +36,9 @@ gem 'omniauth_crowd' ...@@ -36,6 +36,9 @@ gem 'omniauth_crowd'
gem 'gssapi', group: :kerberos gem 'gssapi', group: :kerberos
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
# reCAPTCHA protection
gem 'recaptcha', require: 'recaptcha/rails'
# Two-factor authentication # Two-factor authentication
gem 'devise-two-factor', '~> 2.0.0' gem 'devise-two-factor', '~> 2.0.0'
gem 'rqrcode-rails3', '~> 0.1.7' gem 'rqrcode-rails3', '~> 0.1.7'
...@@ -171,10 +174,10 @@ gem 'asana', '~> 0.4.0' ...@@ -171,10 +174,10 @@ gem 'asana', '~> 0.4.0'
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
# d3 # d3
gem 'd3_rails', '~> 3.5.5' gem 'd3_rails', '~> 3.5.0'
#cal-heatmap #cal-heatmap
gem "cal-heatmap-rails", "~> 0.0.1" gem 'cal-heatmap-rails', '~> 3.5.0'
# underscore-rails # underscore-rails
gem "underscore-rails", "~> 1.8.0" gem "underscore-rails", "~> 1.8.0"
...@@ -202,7 +205,7 @@ gem 'turbolinks', '~> 2.5.0' ...@@ -202,7 +205,7 @@ gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks', '~> 2.1.0' gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8' gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.0' gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.2' gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.2.0' gem 'gitlab_emoji', '~> 0.2.0'
gem 'gon', '~> 6.0.1' gem 'gon', '~> 6.0.1'
...@@ -219,9 +222,17 @@ gem 'net-ssh', '~> 3.0.1' ...@@ -219,9 +222,17 @@ gem 'net-ssh', '~> 3.0.1'
gem "gitlab-license", "~> 0.0.4" gem "gitlab-license", "~> 0.0.4"
# Metrics
group :metrics do
gem 'allocations', '~> 1.0', require: false, platform: :mri
gem 'method_source', '~> 0.8', require: false
gem 'influxdb', '~> 0.2', require: false
gem 'connection_pool', '~> 2.0', require: false
end
group :development do group :development do
gem "foreman" gem "foreman"
gem 'brakeman', '3.0.1', require: false gem 'brakeman', '~> 3.1.0', require: false
gem "annotate", "~> 2.6.0" gem "annotate", "~> 2.6.0"
gem "letter_opener", '~> 1.1.2' gem "letter_opener", '~> 1.1.2'
......
...@@ -49,6 +49,7 @@ GEM ...@@ -49,6 +49,7 @@ GEM
addressable (2.3.8) addressable (2.3.8)
after_commit_queue (1.3.0) after_commit_queue (1.3.0)
activerecord (>= 3.0) activerecord (>= 3.0)
allocations (1.0.3)
annotate (2.6.10) annotate (2.6.10)
activerecord (>= 3.2, <= 4.3) activerecord (>= 3.2, <= 4.3)
rake (~> 10.4) rake (~> 10.4)
...@@ -65,7 +66,7 @@ GEM ...@@ -65,7 +66,7 @@ GEM
attr_encrypted (1.3.4) attr_encrypted (1.3.4)
encryptor (>= 1.3.0) encryptor (>= 1.3.0)
attr_required (1.0.0) attr_required (1.0.0)
autoprefixer-rails (6.1.1) autoprefixer-rails (6.2.3)
execjs execjs
json json
awesome_print (1.2.0) awesome_print (1.2.0)
...@@ -81,18 +82,20 @@ GEM ...@@ -81,18 +82,20 @@ GEM
erubis (>= 2.6.6) erubis (>= 2.6.6)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootstrap-sass (3.3.5) bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.0.0.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.2.19) sass (>= 3.3.4)
brakeman (3.0.1) brakeman (3.1.4)
erubis (~> 2.6) erubis (~> 2.6)
fastercsv (~> 1.5) fastercsv (~> 1.5)
haml (>= 3.0, < 5.0) haml (>= 3.0, < 5.0)
highline (~> 1.6.20) highline (>= 1.6.20, < 2.0)
multi_json (~> 1.2) multi_json (~> 1.2)
ruby2ruby (~> 2.1.1) ruby2ruby (>= 2.1.1, < 2.3.0)
ruby_parser (~> 3.5.0) ruby_parser (~> 3.7.0)
safe_yaml (>= 1.0)
sass (~> 3.0) sass (~> 3.0)
slim (>= 1.3.6, < 4.0)
terminal-table (~> 1.4) terminal-table (~> 1.4)
browser (1.0.1) browser (1.0.1)
builder (3.2.2) builder (3.2.2)
...@@ -102,8 +105,8 @@ GEM ...@@ -102,8 +105,8 @@ GEM
bundler-audit (0.4.0) bundler-audit (0.4.0)
bundler (~> 1.2) bundler (~> 1.2)
thor (~> 0.18) thor (~> 0.18)
byebug (8.2.0) byebug (8.2.1)
cal-heatmap-rails (0.0.1) cal-heatmap-rails (3.5.1)
capybara (2.4.4) capybara (2.4.4)
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
...@@ -117,6 +120,7 @@ GEM ...@@ -117,6 +120,7 @@ GEM
activemodel (>= 3.2.0) activemodel (>= 3.2.0)
activesupport (>= 3.2.0) activesupport (>= 3.2.0)
json (>= 1.7) json (>= 1.7)
cause (0.1)
charlock_holmes (0.7.3) charlock_holmes (0.7.3)
chunky_png (1.3.5) chunky_png (1.3.5)
cliver (0.3.2) cliver (0.3.2)
...@@ -140,10 +144,10 @@ GEM ...@@ -140,10 +144,10 @@ GEM
term-ansicolor (~> 1.3) term-ansicolor (~> 1.3)
thor (~> 0.19.1) thor (~> 0.19.1)
tins (~> 1.6.0) tins (~> 1.6.0)
crack (0.4.2) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
creole (0.5.0) creole (0.5.0)
d3_rails (3.5.6) d3_rails (3.5.11)
railties (>= 3.1.0) railties (>= 3.1.0)
daemons (1.2.3) daemons (1.2.3)
database_cleaner (1.4.1) database_cleaner (1.4.1)
...@@ -232,7 +236,7 @@ GEM ...@@ -232,7 +236,7 @@ GEM
ipaddress (~> 0.5) ipaddress (~> 0.5)
nokogiri (~> 1.5, >= 1.5.11) nokogiri (~> 1.5, >= 1.5.11)
opennebula opennebula
fog-brightbox (0.9.0) fog-brightbox (0.10.1)
fog-core (~> 1.22) fog-core (~> 1.22)
fog-json fog-json
inflecto (~> 0.0.2) inflecto (~> 0.0.2)
...@@ -251,7 +255,7 @@ GEM ...@@ -251,7 +255,7 @@ GEM
fog-core (>= 1.21.0) fog-core (>= 1.21.0)
fog-json fog-json
fog-xml (>= 0.0.1) fog-xml (>= 0.0.1)
fog-sakuracloud (1.4.0) fog-sakuracloud (1.5.0)
fog-core fog-core
fog-json fog-json
fog-softlayer (1.0.2) fog-softlayer (1.0.2)
...@@ -279,11 +283,11 @@ GEM ...@@ -279,11 +283,11 @@ GEM
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
gemnasium-gitlab-service (0.2.6) gemnasium-gitlab-service (0.2.6)
rugged (~> 0.21) rugged (~> 0.21)
gemojione (2.1.0) gemojione (2.1.1)
json json
get_process_mem (0.2.0) get_process_mem (0.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
github-linguist (4.7.2) github-linguist (4.7.3)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
escape_utils (~> 1.1.0) escape_utils (~> 1.1.0)
mime-types (>= 1.19) mime-types (>= 1.19)
...@@ -301,7 +305,7 @@ GEM ...@@ -301,7 +305,7 @@ GEM
gitlab-license (0.0.4) gitlab-license (0.0.4)
gitlab_emoji (0.2.0) gitlab_emoji (0.2.0)
gemojione (~> 2.1) gemojione (~> 2.1)
gitlab_git (7.2.21) gitlab_git (7.2.22)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
...@@ -352,7 +356,7 @@ GEM ...@@ -352,7 +356,7 @@ GEM
html2haml (>= 1.0.1) html2haml (>= 1.0.1)
railties (>= 4.0.1) railties (>= 4.0.1)
hashie (3.4.3) hashie (3.4.3)
highline (1.6.21) highline (1.7.8)
hike (1.2.3) hike (1.2.3)
hipchat (1.5.2) hipchat (1.5.2)
httparty httparty
...@@ -375,6 +379,9 @@ GEM ...@@ -375,6 +379,9 @@ GEM
i18n (0.7.0) i18n (0.7.0)
ice_nine (0.11.1) ice_nine (0.11.1)
inflecto (0.0.2) inflecto (0.0.2)
influxdb (0.2.3)
cause
json
ipaddress (0.8.0) ipaddress (0.8.0)
jquery-atwho-rails (1.3.2) jquery-atwho-rails (1.3.2)
jquery-rails (4.0.5) jquery-rails (4.0.5)
...@@ -422,7 +429,7 @@ GEM ...@@ -422,7 +429,7 @@ GEM
net-ldap (0.12.1) net-ldap (0.12.1)
net-ssh (3.0.1) net-ssh (3.0.1)
netrc (0.11.0) netrc (0.11.0)
newrelic-grape (2.0.0) newrelic-grape (2.1.0)
grape grape
newrelic_rpm newrelic_rpm
newrelic_rpm (3.9.4.245) newrelic_rpm (3.9.4.245)
...@@ -571,6 +578,8 @@ GEM ...@@ -571,6 +578,8 @@ GEM
trollop trollop
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
recaptcha (1.0.2)
json
redcarpet (3.3.3) redcarpet (3.3.3)
redis (3.2.2) redis (3.2.2)
redis-actionpack (4.0.1) redis-actionpack (4.0.1)
...@@ -641,10 +650,10 @@ GEM ...@@ -641,10 +650,10 @@ GEM
ruby-saml (1.0.0) ruby-saml (1.0.0)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
uuid (~> 2.3) uuid (~> 2.3)
ruby2ruby (2.1.4) ruby2ruby (2.2.0)
ruby_parser (~> 3.1) ruby_parser (~> 3.1)
sexp_processor (~> 4.0) sexp_processor (~> 4.0)
ruby_parser (3.5.0) ruby_parser (3.7.2)
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
...@@ -698,6 +707,9 @@ GEM ...@@ -698,6 +707,9 @@ GEM
tilt (>= 1.3, < 3) tilt (>= 1.3, < 3)
six (0.2.0) six (0.2.0)
slack-notifier (1.2.1) slack-notifier (1.2.1)
slim (3.0.6)
temple (~> 0.7.3)
tilt (>= 1.3.3, < 2.1)
slop (3.6.0) slop (3.6.0)
spinach (0.8.10) spinach (0.8.10)
colorize colorize
...@@ -739,6 +751,7 @@ GEM ...@@ -739,6 +751,7 @@ GEM
railties (>= 3.2.5, < 5) railties (>= 3.2.5, < 5)
teaspoon-jasmine (2.2.0) teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0) teaspoon (>= 1.0.0)
temple (0.7.6)
term-ansicolor (1.3.2) term-ansicolor (1.3.2)
tins (~> 1.0) tins (~> 1.0)
terminal-table (1.5.2) terminal-table (1.5.2)
...@@ -794,7 +807,7 @@ GEM ...@@ -794,7 +807,7 @@ GEM
coercible (~> 1.0) coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3) descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9) equalizer (~> 0.0, >= 0.0.9)
warden (1.2.3) warden (1.2.4)
rack (>= 1.0) rack (>= 1.0)
web-console (2.2.1) web-console (2.2.1)
activemodel (>= 4.0) activemodel (>= 4.0)
...@@ -825,6 +838,7 @@ DEPENDENCIES ...@@ -825,6 +838,7 @@ DEPENDENCIES
acts-as-taggable-on (~> 3.4) acts-as-taggable-on (~> 3.4)
addressable (~> 2.3.8) addressable (~> 2.3.8)
after_commit_queue after_commit_queue
allocations (~> 1.0)
annotate (~> 2.6.0) annotate (~> 2.6.0)
asana (~> 0.4.0) asana (~> 0.4.0)
asciidoctor (~> 1.5.2) asciidoctor (~> 1.5.2)
...@@ -834,22 +848,23 @@ DEPENDENCIES ...@@ -834,22 +848,23 @@ DEPENDENCIES
benchmark-ips benchmark-ips
better_errors (~> 1.0.1) better_errors (~> 1.0.1)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.0) bootstrap-sass (~> 3.3.0)
brakeman (= 3.0.1) brakeman (~> 3.1.0)
browser (~> 1.0.0) browser (~> 1.0.0)
bullet bullet
bundler-audit bundler-audit
byebug byebug
cal-heatmap-rails (~> 0.0.1) cal-heatmap-rails (~> 3.5.0)
capybara (~> 2.4.0) capybara (~> 2.4.0)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave (~> 0.9.0) carrierwave (~> 0.9.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
coffee-rails (~> 4.1.0) coffee-rails (~> 4.1.0)
colorize (~> 0.7.0) colorize (~> 0.7.0)
connection_pool (~> 2.0)
coveralls (~> 0.8.2) coveralls (~> 0.8.2)
creole (~> 0.5.0) creole (~> 0.5.0)
d3_rails (~> 3.5.5) d3_rails (~> 3.5.0)
database_cleaner (~> 1.4.0) database_cleaner (~> 1.4.0)
default_value_for (~> 3.0.0) default_value_for (~> 3.0.0)
devise (~> 3.5.3) devise (~> 3.5.3)
...@@ -887,6 +902,7 @@ DEPENDENCIES ...@@ -887,6 +902,7 @@ DEPENDENCIES
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
httparty (~> 0.13.3) httparty (~> 0.13.3)
influxdb (~> 0.2)
jquery-atwho-rails (~> 1.3.2) jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.0.0) jquery-rails (~> 4.0.0)
jquery-scrollto-rails (~> 1.4.3) jquery-scrollto-rails (~> 1.4.3)
...@@ -895,6 +911,7 @@ DEPENDENCIES ...@@ -895,6 +911,7 @@ DEPENDENCIES
kaminari (~> 0.16.3) kaminari (~> 0.16.3)
letter_opener (~> 1.1.2) letter_opener (~> 1.1.2)
mail_room (~> 0.6.1) mail_room (~> 0.6.1)
method_source (~> 0.8)
minitest (~> 5.7.0) minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6) mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16) mysql2 (~> 0.3.16)
...@@ -933,6 +950,7 @@ DEPENDENCIES ...@@ -933,6 +950,7 @@ DEPENDENCIES
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
rblineprof rblineprof
rdoc (~> 3.6) rdoc (~> 3.6)
recaptcha
redcarpet (~> 3.3.3) redcarpet (~> 3.3.3)
redis-namespace redis-namespace
redis-rails (~> 4.0.0) redis-rails (~> 4.0.0)
......
The GitLab Enterprise Edition (EE) license (the “EE License”)
Copyright (c) 2011-2015 GitLab B.V. Copyright (c) 2011-2015 GitLab B.V.
This software and associated documentation files (the "Software") may only be This software and associated documentation files (the "Software") may only be
......
...@@ -10,13 +10,13 @@ ...@@ -10,13 +10,13 @@
#= require jquery.cookie #= require jquery.cookie
#= require jquery.endless-scroll #= require jquery.endless-scroll
#= require jquery.highlight #= require jquery.highlight
#= require jquery.history
#= require jquery.waitforimages #= require jquery.waitforimages
#= require jquery.atwho #= require jquery.atwho
#= require jquery.scrollTo #= require jquery.scrollTo
#= require jquery.blockUI
#= require jquery.turbolinks #= require jquery.turbolinks
#= require jquery.tablesorter #= require jquery.tablesorter
#= require d3
#= require cal-heatmap
#= require turbolinks #= require turbolinks
#= require autosave #= require autosave
#= require bootstrap #= require bootstrap
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#= require branch-graph #= require branch-graph
#= require ace/ace #= require ace/ace
#= require ace/ext-searchbox #= require ace/ext-searchbox
#= require d3
#= require underscore #= require underscore
#= require nprogress #= require nprogress
#= require nprogress-turbolinks #= require nprogress-turbolinks
...@@ -40,7 +39,6 @@ ...@@ -40,7 +39,6 @@
#= require shortcuts_dashboard_navigation #= require shortcuts_dashboard_navigation
#= require shortcuts_issuable #= require shortcuts_issuable
#= require shortcuts_network #= require shortcuts_network
#= require cal-heatmap
#= require jquery.nicescroll.min #= require jquery.nicescroll.min
#= require_tree . #= require_tree .
......
...@@ -134,15 +134,16 @@ class @AwardsHandler ...@@ -134,15 +134,16 @@ class @AwardsHandler
_.compact(_.uniq(frequently_used_emojis)) _.compact(_.uniq(frequently_used_emojis))
renderFrequentlyUsedBlock: -> renderFrequentlyUsedBlock: ->
frequently_used_emojis = @getFrequentlyUsedEmojis() if $.cookie('frequently_used_emojis')
frequently_used_emojis = @getFrequentlyUsedEmojis()
ul = $("<ul>") ul = $("<ul>")
for emoji in frequently_used_emojis for emoji in frequently_used_emojis
do (emoji) -> do (emoji) ->
$(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul) $(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul)
$("input.emoji-search").after(ul).after($("<h5>").text("Frequently used")) $("input.emoji-search").after(ul).after($("<h5>").text("Frequently used"))
setupSearch: -> setupSearch: ->
$("input.emoji-search").keyup (ev) => $("input.emoji-search").keyup (ev) =>
......
class @Calendar class @Calendar
options =
month: "short"
day: "numeric"
year: "numeric"
constructor: (timestamps, starting_year, starting_month, calendar_activities_path) -> constructor: (timestamps, starting_year, starting_month, calendar_activities_path) ->
cal = new CalHeatMap() cal = new CalHeatMap()
cal.init cal.init
......
...@@ -49,7 +49,7 @@ class Dispatcher ...@@ -49,7 +49,7 @@ class Dispatcher
new DropzoneInput($('.release-form')) new DropzoneInput($('.release-form'))
when 'projects:merge_requests:show' when 'projects:merge_requests:show'
new Diff() new Diff()
shortcut_handler = new ShortcutsIssuable() shortcut_handler = new ShortcutsIssuable(true)
new ZenMode() new ZenMode()
when "projects:merge_requests:diffs" when "projects:merge_requests:diffs"
new Diff() new Diff()
......
#= require flash
#= require jquery.waitforimages #= require jquery.waitforimages
#= require task_list #= require task_list
...@@ -6,13 +7,44 @@ class @Issue ...@@ -6,13 +7,44 @@ class @Issue
# Prevent duplicate event bindings # Prevent duplicate event bindings
@disableTaskList() @disableTaskList()
if $("a.btn-close").length if $('a.btn-close').length
@initTaskList() @initTaskList()
@initIssueBtnEventListeners()
initTaskList: -> initTaskList: ->
$('.detail-page-description .js-task-list-container').taskList('enable') $('.detail-page-description .js-task-list-container').taskList('enable')
$(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
initIssueBtnEventListeners: ->
issueFailMessage = 'Unable to update this issue at this time.'
$('a.btn-close, a.btn-reopen').on 'click', (e) ->
e.preventDefault()
e.stopImmediatePropagation()
$this = $(this)
isClose = $this.hasClass('btn-close')
$this.prop('disabled', true)
url = $this.attr('href')
$.ajax
type: 'PUT'
url: url,
error: (jqXHR, textStatus, errorThrown) ->
issueStatus = if isClose then 'close' else 'open'
new Flash(issueFailMessage, 'alert')
success: (data, textStatus, jqXHR) ->
if data.saved
$this.addClass('hidden')
if isClose
$('a.btn-reopen').removeClass('hidden')
$('div.status-box-closed').removeClass('hidden')
$('div.status-box-open').addClass('hidden')
else
$('a.btn-close').removeClass('hidden')
$('div.status-box-closed').addClass('hidden')
$('div.status-box-open').removeClass('hidden')
else
new Flash(issueFailMessage, 'alert')
$this.prop('disabled', false)
disableTaskList: -> disableTaskList: ->
$('.detail-page-description .js-task-list-container').taskList('disable') $('.detail-page-description .js-task-list-container').taskList('disable')
$(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container' $(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container'
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
form = $("#issue_search_form") form = $("#issue_search_form")
search = $("#issue_search").val() search = $("#issue_search").val()
$('.issues-holder').css("opacity", '0.5') $('.issues-holder').css("opacity", '0.5')
issues_url = form.attr('action') + '? '+ form.serialize() issues_url = form.attr('action') + '?' + form.serialize()
$.ajax $.ajax
type: "GET" type: "GET"
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
success: (data) -> success: (data) ->
$('.issues-holder').html(data.html) $('.issues-holder').html(data.html)
# Change url so if user reload a page - search results are saved # Change url so if user reload a page - search results are saved
History.replaceState {page: issues_url}, document.title, issues_url history.replaceState {page: issues_url}, document.title, issues_url
Issues.reload() Issues.reload()
dataType: "json" dataType: "json"
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
form = $("#issue_search_form") form = $("#issue_search_form")
search = $("#issue_search").val() search = $("#issue_search").val()
$('.merge-requests-holder').css("opacity", '0.5') $('.merge-requests-holder').css("opacity", '0.5')
issues_url = form.attr('action') + '? '+ form.serialize() issues_url = form.attr('action') + '?' + form.serialize()
$.ajax $.ajax
type: "GET" type: "GET"
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
success: (data) -> success: (data) ->
$('.merge-requests-holder').html(data.html) $('.merge-requests-holder').html(data.html)
# Change url so if user reload a page - search results are saved # Change url so if user reload a page - search results are saved
History.replaceState {page: issues_url}, document.title, issues_url history.replaceState {page: issues_url}, document.title, issues_url
MergeRequests.reload() MergeRequests.reload()
dataType: "json" dataType: "json"
......
...@@ -7,7 +7,7 @@ class @Shortcuts ...@@ -7,7 +7,7 @@ class @Shortcuts
selectiveHelp: (e) => selectiveHelp: (e) =>
Shortcuts.showHelp(e, @enabledHelp) Shortcuts.showHelp(e, @enabledHelp)
@showHelp: (e, location) -> @showHelp: (e, location) ->
if $('#modal-shortcuts').length > 0 if $('#modal-shortcuts').length > 0
$('#modal-shortcuts').modal('show') $('#modal-shortcuts').modal('show')
...@@ -17,8 +17,7 @@ class @Shortcuts ...@@ -17,8 +17,7 @@ class @Shortcuts
dataType: 'script', dataType: 'script',
success: (e) -> success: (e) ->
if location and location.length > 0 if location and location.length > 0
for l in location $(l).show() for l in location
$(l).show()
else else
$('.hidden-shortcut').show() $('.hidden-shortcut').show()
$('.js-more-help-button').remove() $('.js-more-help-button').remove()
...@@ -28,3 +27,8 @@ class @Shortcuts ...@@ -28,3 +27,8 @@ class @Shortcuts
@focusSearch: (e) -> @focusSearch: (e) ->
$('#search').focus() $('#search').focus()
e.preventDefault() e.preventDefault()
$(document).on 'click.more_help', '.js-more-help-button', (e) ->
$(@).remove()
$('.hidden-shortcut').show()
e.preventDefault()
...@@ -121,5 +121,5 @@ class @UsersSelect ...@@ -121,5 +121,5 @@ class @UsersSelect
callback(users) callback(users)
buildUrl: (url) -> buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root? url = gon.relative_url_root.replace(/\/$/, '') + url if gon.relative_url_root?
return url return url
...@@ -19,38 +19,33 @@ ...@@ -19,38 +19,33 @@
} }
} }
} }
/** /**
* This overwrites the default values of the cal-heatmap gem * This overwrites the default values of the cal-heatmap gem
*/ */
.calendar { .calendar {
.qi { .qi {
background-color: #999;
fill: #fff; fill: #fff;
} }
.q1 { .q1 {
background-color: #dae289; fill: #ededed !important;
fill: #ededed;
} }
.q2 { .q2 {
background-color: #cedb9c; fill: #ACD5F2 !important;
fill: #ACD5F2;
} }
.q3 { .q3 {
background-color: #b5cf6b; fill: #7FA8D1 !important;
fill: #7FA8D1;
} }
.q4 { .q4 {
background-color: #637939; fill: #49729B !important;
fill: #49729B;
} }
.q5 { .q5 {
background-color: #3b6427; fill: #254E77 !important;
fill: #254E77;
} }
.domain-background { .domain-background {
...@@ -59,32 +54,7 @@ ...@@ -59,32 +54,7 @@
} }
.ch-tooltip { .ch-tooltip {
position: absolute;
display: none;
margin-top: 22px;
margin-left: 1px;
font-size: 13px;
padding: 3px; padding: 3px;
font-weight: 550; font-weight: 550;
background-color: #222;
span {
position: absolute;
width: 200px;
text-align: center;
visibility: hidden;
border-radius: 10px;
&:after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -8px;
width: 0;
height: 0;
border-top: 8px solid #000000;
border-right: 8px solid transparent;
border-left: 8px solid transparent;
}
}
} }
} }
...@@ -69,6 +69,17 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -69,6 +69,17 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:shared_runners_enabled, :shared_runners_enabled,
:max_artifacts_size, :max_artifacts_size,
:max_pages_size, :max_pages_size,
:metrics_enabled,
:metrics_host,
:metrics_port,
:metrics_username,
:metrics_password,
:metrics_pool_size,
:metrics_timeout,
:metrics_method_call_threshold,
:recaptcha_enabled,
:recaptcha_site_key,
:recaptcha_private_key,
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [] import_sources: []
) )
......
class Explore::GroupsController < Explore::ApplicationController class Explore::GroupsController < Explore::ApplicationController
def index def index
@groups = GroupsFinder.new.execute(current_user) @groups = Group.order_id_desc
@groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.search(params[:search]) if params[:search].present?
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page]).per(PER_PAGE) @groups = @groups.page(params[:page]).per(PER_PAGE)
......
...@@ -9,7 +9,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -9,7 +9,7 @@ class Projects::CommitsController < Projects::ApplicationController
def show def show
@repo = @project.repository @repo = @project.repository
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0) @limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i
@commits = @repo.commits(@ref, @path, @limit, @offset) @commits = @repo.commits(@ref, @path, @limit, @offset)
@note_counts = project.notes.where(commit_id: @commits.map(&:id)). @note_counts = project.notes.where(commit_id: @commits.map(&:id)).
......
...@@ -189,7 +189,7 @@ class ProjectsController < ApplicationController ...@@ -189,7 +189,7 @@ class ProjectsController < ApplicationController
def markdown_preview def markdown_preview
text = params[:text] text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user) ext = Gitlab::ReferenceExtractor.new(@project, current_user, current_user)
ext.analyze(text) ext.analyze(text)
render json: { render json: {
......
class RegistrationsController < Devise::RegistrationsController class RegistrationsController < Devise::RegistrationsController
before_action :signup_enabled? before_action :signup_enabled?
include Recaptcha::Verify
def new def new
redirect_to(new_user_session_path) redirect_to(new_user_session_path)
end end
def create
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
super
else
flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code."
flash.delete :recaptcha_error
render action: 'new'
end
end
def destroy def destroy
DeleteUserService.new(current_user).execute(current_user) DeleteUserService.new(current_user).execute(current_user)
...@@ -38,4 +49,16 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -38,4 +49,16 @@ class RegistrationsController < Devise::RegistrationsController
def sign_up_params def sign_up_params
params.require(:user).permit(:username, :email, :name, :password, :password_confirmation) params.require(:user).permit(:username, :email, :name, :password, :password_confirmation)
end end
def resource_name
:user
end
def resource
@resource ||= User.new(sign_up_params)
end
def devise_mapping
@devise_mapping ||= Devise.mappings[:user]
end
end end
class SessionsController < Devise::SessionsController class SessionsController < Devise::SessionsController
include AuthenticatesWithTwoFactor include AuthenticatesWithTwoFactor
include Recaptcha::ClientHelper
prepend_before_action :authenticate_with_two_factor, only: [:create] prepend_before_action :authenticate_with_two_factor, only: [:create]
prepend_before_action :store_redirect_path, only: [:new] prepend_before_action :store_redirect_path, only: [:new]
before_action :auto_sign_in_with_provider, only: [:new] before_action :auto_sign_in_with_provider, only: [:new]
before_action :load_recaptcha
def new def new
if Gitlab.config.ldap.enabled if Gitlab.config.ldap.enabled
...@@ -40,7 +42,7 @@ class SessionsController < Devise::SessionsController ...@@ -40,7 +42,7 @@ class SessionsController < Devise::SessionsController
User.find(session[:otp_user_id]) User.find(session[:otp_user_id])
end end
end end
def store_redirect_path def store_redirect_path
redirect_path = redirect_path =
if request.referer.present? && (params['redirect_to_referer'] == 'yes') if request.referer.present? && (params['redirect_to_referer'] == 'yes')
...@@ -87,14 +89,14 @@ class SessionsController < Devise::SessionsController ...@@ -87,14 +89,14 @@ class SessionsController < Devise::SessionsController
provider = Gitlab.config.omniauth.auto_sign_in_with_provider provider = Gitlab.config.omniauth.auto_sign_in_with_provider
return unless provider.present? return unless provider.present?
# Auto sign in with an Omniauth provider only if the standard "you need to sign-in" alert is # Auto sign in with an Omniauth provider only if the standard "you need to sign-in" alert is
# registered or no alert at all. In case of another alert (such as a blocked user), it is safer # registered or no alert at all. In case of another alert (such as a blocked user), it is safer
# to do nothing to prevent redirection loops with certain Omniauth providers. # to do nothing to prevent redirection loops with certain Omniauth providers.
return unless flash[:alert].blank? || flash[:alert] == I18n.t('devise.failure.unauthenticated') return unless flash[:alert].blank? || flash[:alert] == I18n.t('devise.failure.unauthenticated')
# Prevent alert from popping up on the first page shown after authentication. # Prevent alert from popping up on the first page shown after authentication.
flash[:alert] = nil flash[:alert] = nil
redirect_to user_omniauth_authorize_path(provider.to_sym) redirect_to user_omniauth_authorize_path(provider.to_sym)
end end
...@@ -107,4 +109,8 @@ class SessionsController < Devise::SessionsController ...@@ -107,4 +109,8 @@ class SessionsController < Devise::SessionsController
AuditEventService.new(user, user, options). AuditEventService.new(user, user, options).
for_authentication.security_event for_authentication.security_event
end end
def load_recaptcha
Gitlab::Recaptcha.load_configurations!
end
end end
...@@ -7,7 +7,7 @@ class UsersController < ApplicationController ...@@ -7,7 +7,7 @@ class UsersController < ApplicationController
@projects = PersonalProjectsFinder.new(@user).execute(current_user) @projects = PersonalProjectsFinder.new(@user).execute(current_user)
@groups = JoinedGroupsFinder.new(@user).execute(current_user) @groups = @user.groups.order_id_desc
respond_to do |format| respond_to do |format|
format.html format.html
......
class GroupsFinder
# Finds the groups available to the given user.
#
# current_user - The user to find the groups for.
#
# Returns an ActiveRecord::Relation.
def execute(current_user = nil)
if current_user
relation = groups_visible_to_user(current_user)
else
relation = public_groups
end
relation.order_id_desc
end
private
# This method returns the groups "current_user" can see.
def groups_visible_to_user(current_user)
base = groups_for_projects(public_and_internal_projects)
union = Gitlab::SQL::Union.
new([base.select(:id), current_user.authorized_groups.select(:id)])
Group.where("namespaces.id IN (#{union.to_sql})")
end
def public_groups
groups_for_projects(public_projects)
end
def groups_for_projects(projects)
Group.public_and_given_groups(projects.select(:namespace_id))
end
def public_projects
Project.unscoped.public_only
end
def public_and_internal_projects
Project.unscoped.public_and_internal_only
end
end
# Class for finding the groups a user is a member of.
class JoinedGroupsFinder
def initialize(user = nil)
@user = user
end
# Finds the groups of the source user, optionally limited to those visible to
# the current user.
#
# current_user - If given the groups of "@user" will only include the groups
# "current_user" can also see.
#
# Returns an ActiveRecord::Relation.
def execute(current_user = nil)
if current_user
relation = groups_visible_to_user(current_user)
else
relation = public_groups
end
relation.order_id_desc
end
private
# Returns the groups the user in "current_user" can see.
#
# This list includes all public/internal projects as well as the projects of
# "@user" that "current_user" also has access to.
def groups_visible_to_user(current_user)
base = @user.authorized_groups.visible_to_user(current_user)
extra = public_and_internal_groups
union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)])
Group.where("namespaces.id IN (#{union.to_sql})")
end
def public_groups
groups_for_projects(@user.authorized_projects.public_only)
end
def public_and_internal_groups
groups_for_projects(@user.authorized_projects.public_and_internal_only)
end
def groups_for_projects(projects)
@user.groups.public_and_given_groups(projects.select(:namespace_id))
end
end
...@@ -72,7 +72,7 @@ module ApplicationHelper ...@@ -72,7 +72,7 @@ module ApplicationHelper
if user_or_email.is_a?(User) if user_or_email.is_a?(User)
user = user_or_email user = user_or_email
else else
user = User.find_by(email: user_or_email) user = User.find_by(email: user_or_email.downcase)
end end
if user if user
......
...@@ -69,6 +69,10 @@ module IssuesHelper ...@@ -69,6 +69,10 @@ module IssuesHelper
end end
end end
def issue_button_visibility(issue, closed)
return 'hidden' if issue.closed? == closed
end
def issue_to_atom(xml, issue) def issue_to_atom(xml, issue)
xml.entry do xml.entry do
xml.id namespace_project_issue_url(issue.project.namespace, xml.id namespace_project_issue_url(issue.project.namespace,
...@@ -95,7 +99,7 @@ module IssuesHelper ...@@ -95,7 +99,7 @@ module IssuesHelper
end end
def emoji_icon(name, unicode = nil, aliases = []) def emoji_icon(name, unicode = nil, aliases = [])
unicode ||= Emoji.emoji_filename(name) unicode ||= Emoji.emoji_filename(name) rescue ""
content_tag :div, "", content_tag :div, "",
class: "icon emoji-icon emoji-#{unicode}", class: "icon emoji-icon emoji-#{unicode}",
......
...@@ -70,7 +70,7 @@ module SearchHelper ...@@ -70,7 +70,7 @@ module SearchHelper
# Autocomplete results for the current user's groups # Autocomplete results for the current user's groups
def groups_autocomplete(term, limit = 5) def groups_autocomplete(term, limit = 5)
GroupsFinder.new.execute(current_user).search(term).limit(limit).map do |group| Group.search(term).limit(limit).map do |group|
{ {
label: "group: #{search_result_sanitize(group.name)}", label: "group: #{search_result_sanitize(group.name)}",
url: group_path(group) url: group_path(group)
......
...@@ -85,7 +85,7 @@ class Ability ...@@ -85,7 +85,7 @@ class Ability
subject.group subject.group
end end
if group && group.public_profile? if group && group.projects.public_only.any?
[:read_group] [:read_group]
else else
[] []
......
...@@ -45,24 +45,32 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -45,24 +45,32 @@ class ApplicationSetting < ActiveRecord::Base
attr_accessor :restricted_signup_domains_raw attr_accessor :restricted_signup_domains_raw
validates :session_expire_delay, validates :session_expire_delay,
presence: true, presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 } numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :home_page_url, validates :home_page_url,
allow_blank: true, allow_blank: true,
url: true, url: true,
if: :home_page_url_column_exist if: :home_page_url_column_exist
validates :after_sign_out_path, validates :after_sign_out_path,
allow_blank: true, allow_blank: true,
url: true url: true
validates :admin_notification_email, validates :admin_notification_email,
allow_blank: true, allow_blank: true,
email: true email: true
validates :two_factor_grace_period, validates :two_factor_grace_period,
numericality: { greater_than_or_equal_to: 0 } numericality: { greater_than_or_equal_to: 0 }
validates :recaptcha_site_key,
presence: true,
if: :recaptcha_enabled
validates :recaptcha_private_key,
presence: true,
if: :recaptcha_enabled
validates_each :restricted_visibility_levels do |record, attr, value| validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil? unless value.nil?
......
...@@ -194,8 +194,11 @@ module Ci ...@@ -194,8 +194,11 @@ module Ci
end end
def raw_trace def raw_trace
if File.exist?(path_to_trace) if File.file?(path_to_trace)
File.read(path_to_trace) File.read(path_to_trace)
elsif project.ci_id && File.file?(old_path_to_trace)
# Temporary fix for build trace data integrity
File.read(old_path_to_trace)
else else
# backward compatibility # backward compatibility
read_attribute :trace read_attribute :trace
...@@ -212,8 +215,8 @@ module Ci ...@@ -212,8 +215,8 @@ module Ci
end end
def trace=(trace) def trace=(trace)
unless Dir.exists? dir_to_trace unless Dir.exists?(dir_to_trace)
FileUtils.mkdir_p dir_to_trace FileUtils.mkdir_p(dir_to_trace)
end end
File.write(path_to_trace, trace) File.write(path_to_trace, trace)
...@@ -231,6 +234,55 @@ module Ci ...@@ -231,6 +234,55 @@ module Ci
"#{dir_to_trace}/#{id}.log" "#{dir_to_trace}/#{id}.log"
end end
##
# Deprecated
#
# This is a hotfix for CI build data integrity, see #4246
# Should be removed in 8.4, after CI files migration has been done.
#
def old_dir_to_trace
File.join(
Settings.gitlab_ci.builds_path,
created_at.utc.strftime("%Y_%m"),
project.ci_id.to_s
)
end
##
# Deprecated
#
# This is a hotfix for CI build data integrity, see #4246
# Should be removed in 8.4, after CI files migration has been done.
#
def old_path_to_trace
"#{old_dir_to_trace}/#{id}.log"
end
##
# Deprecated
#
# This contains a hotfix for CI build data integrity, see #4246
#
# This method is used by `ArtifactUploader` to create a store_dir.
# Warning: Uploader uses it after AND before file has been stored.
#
# This method returns old path to artifacts only if it already exists.
#
def artifacts_path
old = File.join(created_at.utc.strftime('%Y_%m'),
project.ci_id.to_s,
id.to_s)
old_store = File.join(ArtifactUploader.artifacts_path, old)
return old if project.ci_id && File.directory?(old_store)
File.join(
created_at.utc.strftime('%Y_%m'),
project.id.to_s,
id.to_s
)
end
def token def token
project.runners_token project.runners_token
end end
......
...@@ -95,14 +95,12 @@ module Issuable ...@@ -95,14 +95,12 @@ module Issuable
opened? || reopened? opened? || reopened?
end end
# Deprecated. Still exists to preserve API compatibility.
def downvotes def downvotes
0 notes.awards.where(note: "thumbsdown").count
end end
# Deprecated. Still exists to preserve API compatibility.
def upvotes def upvotes
0 notes.awards.where(note: "thumbsup").count
end end
def subscribed?(user) def subscribed?(user)
......
...@@ -44,7 +44,7 @@ module Mentionable ...@@ -44,7 +44,7 @@ module Mentionable
end end
def all_references(current_user = self.author, text = nil) def all_references(current_user = self.author, text = nil)
ext = Gitlab::ReferenceExtractor.new(self.project, current_user) ext = Gitlab::ReferenceExtractor.new(self.project, current_user, self.author)
if text if text
ext.analyze(text) ext.analyze(text)
......
...@@ -54,10 +54,6 @@ class Group < Namespace ...@@ -54,10 +54,6 @@ class Group < Namespace
User.reference_pattern User.reference_pattern
end end
def public_and_given_groups(ids)
where('public IS TRUE OR namespaces.id IN (?)', ids)
end
def visible_to_user(user) def visible_to_user(user)
where(id: user.authorized_groups.select(:id).reorder(nil)) where(id: user.authorized_groups.select(:id).reorder(nil))
end end
...@@ -133,10 +129,6 @@ class Group < Namespace ...@@ -133,10 +129,6 @@ class Group < Namespace
Gitlab::Access.options_with_owner.key ldap_access Gitlab::Access.options_with_owner.key ldap_access
end end
def public_profile?
self.public || projects.public_only.any?
end
# NOTE: Backwards compatibility with old ldap situation # NOTE: Backwards compatibility with old ldap situation
def ldap_cn def ldap_cn
ldap_group_links.first.try(:cn) ldap_group_links.first.try(:cn)
......
...@@ -346,14 +346,12 @@ class Note < ActiveRecord::Base ...@@ -346,14 +346,12 @@ class Note < ActiveRecord::Base
read_attribute(:system) read_attribute(:system)
end end
# Deprecated. Still exists to preserve API compatibility.
def downvote? def downvote?
false is_award && note == "thumbsdown"
end end
# Deprecated. Still exists to preserve API compatibility.
def upvote? def upvote?
false is_award && note == "thumbsup"
end end
def editable? def editable?
......
...@@ -83,6 +83,7 @@ class Project < ActiveRecord::Base ...@@ -83,6 +83,7 @@ class Project < ActiveRecord::Base
acts_as_taggable_on :tags acts_as_taggable_on :tags
attr_accessor :new_default_branch attr_accessor :new_default_branch
attr_accessor :old_path_with_namespace
# Relations # Relations
belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
...@@ -643,7 +644,9 @@ class Project < ActiveRecord::Base ...@@ -643,7 +644,9 @@ class Project < ActiveRecord::Base
end end
def send_move_instructions(old_path_with_namespace) def send_move_instructions(old_path_with_namespace)
NotificationService.new.project_was_moved(self, old_path_with_namespace) # New project path needs to be committed to the DB or notification will
# retrieve stale information
run_after_commit { NotificationService.new.project_was_moved(self, old_path_with_namespace) }
end end
def owner def owner
...@@ -797,6 +800,11 @@ class Project < ActiveRecord::Base ...@@ -797,6 +800,11 @@ class Project < ActiveRecord::Base
gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace) send_move_instructions(old_path_with_namespace)
reset_events_cache reset_events_cache
@old_path_with_namespace = old_path_with_namespace
SystemHooksService.new.execute_hooks_for(self, :rename)
@repository = nil @repository = nil
rescue rescue
# Returning false does not rollback after_* transaction but gives # Returning false does not rollback after_* transaction but gives
......
...@@ -78,7 +78,9 @@ class Repository ...@@ -78,7 +78,9 @@ class Repository
path: path, path: path,
limit: limit, limit: limit,
offset: offset, offset: offset,
follow: path.present? # --follow doesn't play well with --skip. See:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
follow: false
} }
commits = Gitlab::Git::Commit.where(options) commits = Gitlab::Git::Commit.where(options)
......
...@@ -58,6 +58,9 @@ module Projects ...@@ -58,6 +58,9 @@ module Projects
# Move pages # Move pages
Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
project.old_path_with_namespace = old_path
SystemHooksService.new.execute_hooks_for(project, :transfer)
true true
end end
end end
......
...@@ -18,7 +18,8 @@ class SystemHooksService ...@@ -18,7 +18,8 @@ class SystemHooksService
def build_event_data(model, event) def build_event_data(model, event)
data = { data = {
event_name: build_event_name(model, event), event_name: build_event_name(model, event),
created_at: model.created_at.xmlschema created_at: model.created_at.xmlschema,
updated_at: model.updated_at.xmlschema
} }
case model case model
...@@ -34,6 +35,14 @@ class SystemHooksService ...@@ -34,6 +35,14 @@ class SystemHooksService
end end
when Project when Project
data.merge!(project_data(model)) data.merge!(project_data(model))
if event == :rename || event == :transfer
data.merge!({
old_path_with_namespace: model.old_path_with_namespace
})
end
data
when User when User
data.merge!({ data.merge!({
name: model.name, name: model.name,
......
...@@ -20,16 +20,12 @@ class ArtifactUploader < CarrierWave::Uploader::Base ...@@ -20,16 +20,12 @@ class ArtifactUploader < CarrierWave::Uploader::Base
@build, @field = build, field @build, @field = build, field
end end
def artifacts_path
File.join(build.created_at.utc.strftime('%Y_%m'), build.project_id.to_s, build.id.to_s)
end
def store_dir def store_dir
File.join(ArtifactUploader.artifacts_path, artifacts_path) File.join(self.class.artifacts_path, @build.artifacts_path)
end end
def cache_dir def cache_dir
File.join(ArtifactUploader.artifacts_cache_path, artifacts_path) File.join(self.class.artifacts_cache_path, @build.artifacts_path)
end end
def file_storage? def file_storage?
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%h3.page-title Report abuse %h3.page-title Report abuse
%p Please use this form to report users who create spam issues, comments or behave inappropriately. %p Please use this form to report users who create spam issues, comments or behave inappropriately.
%hr %hr
= form_for @abuse_report, html: { class: 'form-horizontal'} do |f| = form_for @abuse_report, html: { class: 'form-horizontal js-requires-input'} do |f|
= f.hidden_field :user_id = f.hidden_field :user_id
- if @abuse_report.errors.any? - if @abuse_report.errors.any?
.alert.alert-danger .alert.alert-danger
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.form-group .form-group
= f.label :message, class: 'control-label' = f.label :message, class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_area :message, class: "form-control", rows: 2, required: true = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true
.help-block .help-block
Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment. Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment.
......
...@@ -162,12 +162,89 @@ ...@@ -162,12 +162,89 @@
.checkbox .checkbox
= f.label :shared_runners_enabled do = f.label :shared_runners_enabled do
= f.check_box :shared_runners_enabled = f.check_box :shared_runners_enabled
Enable shared runners for a new projects Enable shared runners for new projects
.form-group .form-group
= f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2' = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
= f.number_field :max_artifacts_size, class: 'form-control' = f.number_field :max_artifacts_size, class: 'form-control'
%fieldset
%legend Metrics
%p
These settings require a restart to take effect.
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :metrics_enabled do
= f.check_box :metrics_enabled
Enable InfluxDB Metrics
.form-group
= f.label :metrics_host, 'InfluxDB host', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :metrics_host, class: 'form-control', placeholder: 'influxdb.example.com'
.form-group
= f.label :metrics_port, 'InfluxDB port', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :metrics_port, class: 'form-control', placeholder: '8089'
.help-block
The UDP port to use for connecting to InfluxDB. InfluxDB requires that
your server configuration specifies a database to store data in when
sending messages to this port, without it metrics data will not be
saved.
.form-group
= f.label :metrics_username, 'InfluxDB username', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :metrics_username, class: 'form-control'
.form-group
= f.label :metrics_password, 'InfluxDB password', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :metrics_password, class: 'form-control'
.form-group
= f.label :metrics_pool_size, 'Connection pool size', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :metrics_pool_size, class: 'form-control'
.help-block
The amount of InfluxDB connections to open. Connections are opened
lazily. Users using multi-threaded application servers should ensure
enough connections are available (at minimum the amount of application
server threads).
.form-group
= f.label :metrics_timeout, 'Connection timeout', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :metrics_timeout, class: 'form-control'
.help-block
The amount of seconds after which an InfluxDB connection will time
out.
.form-group
= f.label :metrics_method_call_threshold, 'Method Call Threshold (ms)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :metrics_method_call_threshold, class: 'form-control'
.help-block
A method call is only tracked when it takes longer to complete than
the given amount of milliseconds.
%fieldset
%legend Spam and Anti-bot Protection
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :recaptcha_enabled do
= f.check_box :recaptcha_enabled
Enable reCAPTCHA
%span.help-block#recaptcha_help_block Helps preventing bots from creating accounts
.form-group
= f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :recaptcha_site_key, class: 'form-control'
.help-block
Generate site and private keys here:
%a{ href: 'http://www.google.com/recaptcha', target: 'blank'} http://www.google.com/recaptcha
.form-group
= f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :recaptcha_private_key, class: 'form-control'
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-primary' = f.submit 'Save', class: 'btn btn-primary'
...@@ -8,17 +8,17 @@ ...@@ -8,17 +8,17 @@
%li{class: ('active' if @scope.nil?)} %li{class: ('active' if @scope.nil?)}
= link_to admin_builds_path do = link_to admin_builds_path do
Running Running
%span.badge.js-running-count= @all_builds.running_or_pending.count(:id) %span.badge.js-running-count= number_with_delimiter(@all_builds.running_or_pending.count(:id))
%li{class: ('active' if @scope == 'finished')} %li{class: ('active' if @scope == 'finished')}
= link_to admin_builds_path(scope: :finished) do = link_to admin_builds_path(scope: :finished) do
Finished Finished
%span.badge.js-running-count= @all_builds.finished.count(:id) %span.badge.js-running-count= number_with_delimiter(@all_builds.finished.count(:id))
%li{class: ('active' if @scope == 'all')} %li{class: ('active' if @scope == 'all')}
= link_to admin_builds_path(scope: :all) do = link_to admin_builds_path(scope: :all) do
All All
%span.badge.js-totalbuilds-count= @all_builds.count(:id) %span.badge.js-totalbuilds-count= number_with_delimiter(@all_builds.count(:id))
.gray-content-block .gray-content-block
#{(@scope || 'running').capitalize} builds #{(@scope || 'running').capitalize} builds
......
...@@ -6,35 +6,35 @@ ...@@ -6,35 +6,35 @@
%p %p
Forks Forks
%span.light.pull-right %span.light.pull-right
= ForkedProjectLink.count = number_with_delimiter(ForkedProjectLink.count)
%p %p
Issues Issues
%span.light.pull-right %span.light.pull-right
= Issue.count = number_with_delimiter(Issue.count)
%p %p
Merge Requests Merge Requests
%span.light.pull-right %span.light.pull-right
= MergeRequest.count = number_with_delimiter(MergeRequest.count)
%p %p
Notes Notes
%span.light.pull-right %span.light.pull-right
= Note.count = number_with_delimiter(Note.count)
%p %p
Snippets Snippets
%span.light.pull-right %span.light.pull-right
= Snippet.count = number_with_delimiter(Snippet.count)
%p %p
SSH Keys SSH Keys
%span.light.pull-right %span.light.pull-right
= Key.count = number_with_delimiter(Key.count)
%p %p
Milestones Milestones
%span.light.pull-right %span.light.pull-right
= Milestone.count = number_with_delimiter(Milestone.count)
%p %p
Active Users Active Users
%span.light.pull-right %span.light.pull-right
= User.active.count = number_with_delimiter(User.active.count)
.col-md-4 .col-md-4
%h4 %h4
Features Features
...@@ -99,7 +99,7 @@ ...@@ -99,7 +99,7 @@
%h4 Projects %h4 Projects
.data .data
= link_to admin_namespaces_projects_path do = link_to admin_namespaces_projects_path do
%h1= Project.count %h1= number_with_delimiter(Project.count)
%hr %hr
= link_to('New Project', new_project_path, class: "btn btn-new") = link_to('New Project', new_project_path, class: "btn btn-new")
.col-sm-4 .col-sm-4
...@@ -107,7 +107,7 @@ ...@@ -107,7 +107,7 @@
%h4 Users %h4 Users
.data .data
= link_to admin_users_path do = link_to admin_users_path do
%h1= User.count %h1= number_with_delimiter(User.count)
%hr %hr
= link_to 'New User', new_admin_user_path, class: "btn btn-new" = link_to 'New User', new_admin_user_path, class: "btn btn-new"
.col-sm-4 .col-sm-4
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
%h4 Groups %h4 Groups
.data .data
= link_to admin_groups_path do = link_to admin_groups_path do
%h1= Group.count %h1= number_with_delimiter(Group.count)
%hr %hr
= link_to 'New Group', new_admin_group_path, class: "btn btn-new" = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
......
- page_title "Groups" - page_title "Groups"
%h3.page-title %h3.page-title
Groups (#{@groups.total_count}) Groups (#{number_with_delimiter(@groups.total_count)})
= link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right" = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
%p.light %p.light
......
...@@ -8,27 +8,27 @@ ...@@ -8,27 +8,27 @@
%li{class: "#{'active' unless params[:filter]}"} %li{class: "#{'active' unless params[:filter]}"}
= link_to admin_users_path do = link_to admin_users_path do
Active Active
%small.pull-right= User.active.count %small.pull-right= number_with_delimiter(User.active.count)
%li{class: "#{'active' if params[:filter] == "admins"}"} %li{class: "#{'active' if params[:filter] == "admins"}"}
= link_to admin_users_path(filter: "admins") do = link_to admin_users_path(filter: "admins") do
Admins Admins
%small.pull-right= User.admins.count %small.pull-right= number_with_delimiter(User.admins.count)
%li.filter-two-factor-enabled{class: "#{'active' if params[:filter] == 'two_factor_enabled'}"} %li.filter-two-factor-enabled{class: "#{'active' if params[:filter] == 'two_factor_enabled'}"}
= link_to admin_users_path(filter: 'two_factor_enabled') do = link_to admin_users_path(filter: 'two_factor_enabled') do
2FA Enabled 2FA Enabled
%small.pull-right= User.with_two_factor.count %small.pull-right= number_with_delimiter(User.with_two_factor.count)
%li.filter-two-factor-disabled{class: "#{'active' if params[:filter] == 'two_factor_disabled'}"} %li.filter-two-factor-disabled{class: "#{'active' if params[:filter] == 'two_factor_disabled'}"}
= link_to admin_users_path(filter: 'two_factor_disabled') do = link_to admin_users_path(filter: 'two_factor_disabled') do
2FA Disabled 2FA Disabled
%small.pull-right= User.without_two_factor.count %small.pull-right= number_with_delimiter(User.without_two_factor.count)
%li{class: "#{'active' if params[:filter] == "blocked"}"} %li{class: "#{'active' if params[:filter] == "blocked"}"}
= link_to admin_users_path(filter: "blocked") do = link_to admin_users_path(filter: "blocked") do
Blocked Blocked
%small.pull-right= User.blocked.count %small.pull-right= number_with_delimiter(User.blocked.count)
%li{class: "#{'active' if params[:filter] == "wop"}"} %li{class: "#{'active' if params[:filter] == "wop"}"}
= link_to admin_users_path(filter: "wop") do = link_to admin_users_path(filter: "wop") do
Without projects Without projects
%small.pull-right= User.without_projects.count %small.pull-right= number_with_delimiter(User.without_projects.count)
%hr %hr
= form_tag admin_users_path, method: :get, class: 'form-inline' do = form_tag admin_users_path, method: :get, class: 'form-inline' do
.form-group .form-group
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
%section.col-md-9 %section.col-md-9
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Users (#{@users.total_count}) Users (#{number_with_delimiter(@users.total_count)})
.panel-head-actions .panel-head-actions
= link_to 'Send email to users', admin_email_path, class: 'btn btn-sm' = link_to 'Send email to users', admin_email_path, class: 'btn btn-sm'
.dropdown.inline .dropdown.inline
......
...@@ -6,17 +6,21 @@ ...@@ -6,17 +6,21 @@
.login-heading .login-heading
%h3 Create an account %h3 Create an account
.login-body .login-body
- user = params[:user].present? ? params[:user] : {}
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
.devise-errors .devise-errors
= devise_error_messages! = devise_error_messages!
%div %div
= f.text_field :name, class: "form-control top", placeholder: "Name", required: true = f.text_field :name, class: "form-control top", value: user[:name], placeholder: "Name", required: true
%div %div
= f.text_field :username, class: "form-control middle", placeholder: "Username", required: true = f.text_field :username, class: "form-control middle", value: user[:username], placeholder: "Username", required: true
%div %div
= f.email_field :email, class: "form-control middle", placeholder: "Email", required: true = f.email_field :email, class: "form-control middle", value: user[:email], placeholder: "Email", required: true
.form-group.append-bottom-20#password-strength .form-group.append-bottom-20#password-strength
= f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true = f.password_field :password, class: "form-control bottom", value: user[:password], id: "user_password_sign_up", placeholder: "Password", required: true
%div
- if current_application_settings.recaptcha_enabled
= recaptcha_tags
%div %div
= f.submit "Sign up", class: "btn-create btn" = f.submit "Sign up", class: "btn-create btn"
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.event-item-timestamp .event-item-timestamp
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
= cache [event, "v2.1"] do = cache [event, current_application_settings, "v2.1"] do
= image_tag avatar_icon(event.author_email, 46), class: "avatar s46", alt:'' = image_tag avatar_icon(event.author_email, 46), class: "avatar s46", alt:''
- if event.created_project? - if event.created_project?
= render "events/event/created_project", event: event = render "events/event/created_project", event: event
......
...@@ -42,15 +42,6 @@ ...@@ -42,15 +42,6 @@
= f.check_box :share_with_group_lock = f.check_box :share_with_group_lock
%span.descr Prevent sharing a project with another group within this group %span.descr Prevent sharing a project with another group within this group
.form-group
%hr
= f.label :public, class: 'control-label' do
Public
.col-sm-10
.checkbox
= f.check_box :public
%span.descr Make this group public (even if there is no any public project inside this group)
.form-actions .form-actions
= f.submit 'Save group', class: "btn btn-save" = f.submit 'Save group', class: "btn btn-save"
......
...@@ -53,5 +53,5 @@ ...@@ -53,5 +53,5 @@
= render "shared_projects", projects: @shared_projects = render "shared_projects", projects: @shared_projects
- else - else
%p %p.center-top-menu.no-top
This group does not have public projects No projects to show
...@@ -219,11 +219,3 @@ ...@@ -219,11 +219,3 @@
%td.shortcut %td.shortcut
.key r .key r
%td Reply (quoting selected text) %td Reply (quoting selected text)
:javascript
$('.js-more-help-button').click(function (e) {
$(this).remove()l
$('.hidden-shortcut').show();
e.preventDefault();
});
%head{prefix: "og: http://ogp.me/ns#"} %head{prefix: "og: http://ogp.me/ns#"}
%meta{charset: "utf-8"} %meta{charset: "utf-8"}
%meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'} %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'}
%meta{name: 'referrer', content: 'origin-when-cross-origin'}
%meta{name: "description", content: page_description}
-# Open Graph - http://ogp.me/ -# Open Graph - http://ogp.me/
%meta{property: 'og:type', content: "object"} %meta{property: 'og:type', content: "object"}
...@@ -19,8 +17,8 @@ ...@@ -19,8 +17,8 @@
%meta{property: 'twitter:image', content: page_image} %meta{property: 'twitter:image', content: page_image}
= page_card_meta_tags = page_card_meta_tags
- page_title "GitLab" %title= page_title('GitLab')
%title= page_title %meta{name: "description", content: page_description}
= favicon_link_tag 'favicon.ico' = favicon_link_tag 'favicon.ico'
...@@ -33,6 +31,8 @@ ...@@ -33,6 +31,8 @@
= include_gon = include_gon
- unless browser.safari?
%meta{name: 'referrer', content: 'origin-when-cross-origin'}
%meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'} %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'}
%meta{name: 'theme-color', content: '#474D57'} %meta{name: 'theme-color', content: '#474D57'}
......
...@@ -29,13 +29,13 @@ ...@@ -29,13 +29,13 @@
= icon('cog fw') = icon('cog fw')
%span %span
Runners Runners
%span.count= Ci::Runner.count(:all) %span.count= number_with_delimiter(Ci::Runner.count(:all))
= nav_link path: 'builds#index' do = nav_link path: 'builds#index' do
= link_to admin_builds_path do = link_to admin_builds_path do
= icon('link fw') = icon('link fw')
%span %span
Builds Builds
%span.count= Ci::Build.count(:all) %span.count= number_with_delimiter(Ci::Build.count(:all))
= nav_link(controller: :logs) do = nav_link(controller: :logs) do
= link_to admin_logs_path, title: 'Logs' do = link_to admin_logs_path, title: 'Logs' do
= icon('file-text fw') = icon('file-text fw')
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
= icon('exclamation-circle fw') = icon('exclamation-circle fw')
%span %span
Abuse Reports Abuse Reports
%span.count= AbuseReport.count(:all) %span.count= number_with_delimiter(AbuseReport.count(:all))
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to admin_application_settings_path, title: 'Settings' do = link_to admin_application_settings_path, title: 'Settings' do
......
...@@ -24,13 +24,13 @@ ...@@ -24,13 +24,13 @@
= icon('exclamation-circle fw') = icon('exclamation-circle fw')
%span %span
Issues Issues
%span.count= current_user.assigned_issues.opened.count %span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
= icon('tasks fw') = icon('tasks fw')
%span %span
Merge Requests Merge Requests
%span.count= current_user.assigned_merge_requests.opened.count %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
= link_to dashboard_snippets_path, title: 'Snippets' do = link_to dashboard_snippets_path, title: 'Snippets' do
= icon('clipboard fw') = icon('clipboard fw')
......
...@@ -25,14 +25,14 @@ ...@@ -25,14 +25,14 @@
%span %span
Issues Issues
- if current_user - if current_user
%span.count= Issue.opened.of_group(@group).count %span.count= number_with_delimiter(Issue.opened.of_group(@group).count)
= nav_link(path: 'groups#merge_requests') do = nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
= icon('tasks fw') = icon('tasks fw')
%span %span
Merge Requests Merge Requests
- if current_user - if current_user
%span.count= MergeRequest.opened.of_group(@group).count %span.count= number_with_delimiter(MergeRequest.opened.of_group(@group).count)
= nav_link(controller: [:group_members]) do = nav_link(controller: [:group_members]) do
= link_to group_group_members_path(@group), title: 'Members' do = link_to group_group_members_path(@group), title: 'Members' do
= icon('users fw') = icon('users fw')
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
= icon('envelope-o fw') = icon('envelope-o fw')
%span %span
Emails Emails
%span.count= current_user.emails.count + 1 %span.count= number_with_delimiter(current_user.emails.count + 1)
- unless current_user.ldap_user? - unless current_user.ldap_user?
= nav_link(controller: :passwords) do = nav_link(controller: :passwords) do
= link_to edit_profile_password_path, title: 'Password' do = link_to edit_profile_password_path, title: 'Password' do
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
= icon('key fw') = icon('key fw')
%span %span
SSH Keys SSH Keys
%span.count= current_user.keys.count %span.count= number_with_delimiter(current_user.keys.count)
= nav_link(controller: :preferences) do = nav_link(controller: :preferences) do
= link_to profile_preferences_path, title: 'Preferences' do = link_to profile_preferences_path, title: 'Preferences' do
-# TODO (rspeicher): Better icon? -# TODO (rspeicher): Better icon?
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
= icon('cubes fw') = icon('cubes fw')
%span %span
Builds Builds
%span.count.builds_counter= @project.builds.running_or_pending.count(:all) %span.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all))
- if project_nav_tab? :graphs - if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do = nav_link(controller: %w(graphs)) do
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
%span %span
Issues Issues
- if @project.default_issues_tracker? - if @project.default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count %span.count.issue_counter= number_with_delimiter(@project.issues.opened.count)
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do = nav_link(controller: :merge_requests) do
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
= icon('tasks fw') = icon('tasks fw')
%span %span
Merge Requests Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count %span.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count)
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(controller: [:project_members, :teams]) do = nav_link(controller: [:project_members, :teams]) do
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- note_count = notes.user.count - note_count = notes.user.count
- ci_commit = project.ci_commit(commit.sha) - ci_commit = project.ci_commit(commit.sha)
- cache_key = [project.path_with_namespace, commit.id, note_count] - cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count]
- cache_key.push(ci_commit.status) if ci_commit - cache_key.push(ci_commit.status) if ci_commit
= cache(cache_key) do = cache(cache_key) do
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- if @issues.present? - if @issues.present?
.issuable-filter-count .issuable-filter-count
%span.pull-right %span.pull-right
= @issues.total_count = number_with_delimiter(@issues.total_count)
issues for this filter issues for this filter
= paginate @issues, theme: "gitlab" = paginate @issues, theme: "gitlab"
...@@ -6,11 +6,8 @@ ...@@ -6,11 +6,8 @@
.issue .issue
.detail-page-header .detail-page-header
.status-box{ class: status_box_class(@issue) } .status-box{ class: "status-box-closed #{issue_button_visibility(@issue, false)}"} Closed
- if @issue.closed? .status-box{ class: "status-box-open #{issue_button_visibility(@issue, true)}"} Open
Closed
- else
Open
%span.identifier %span.identifier
Issue ##{@issue.iid} Issue ##{@issue.iid}
%span.creator %span.creator
...@@ -30,10 +27,8 @@ ...@@ -30,10 +27,8 @@
= icon('plus') = icon('plus')
New Issue New Issue
- if can?(current_user, :update_issue, @issue) - if can?(current_user, :update_issue, @issue)
- if @issue.closed? = link_to 'Reopen', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen Issue'
= link_to 'Reopen', issue_path(@issue, issue: {state_event: :reopen}, status_only: true), method: :put, class: 'btn btn-nr btn-grouped btn-reopen' = link_to 'Close', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close Issue'
- else
= link_to 'Close', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-nr btn-grouped btn-close', title: 'Close Issue'
= link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do
= icon('pencil-square-o') = icon('pencil-square-o')
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- if @merge_requests.present? - if @merge_requests.present?
.issuable-filter-count .issuable-filter-count
%span.pull-right %span.pull-right
= @merge_requests.total_count = number_with_delimiter(@merge_requests.total_count)
merge requests for this filter merge requests for this filter
= paginate @merge_requests, theme: "gitlab" = paginate @merge_requests, theme: "gitlab"
......
...@@ -29,14 +29,14 @@ ...@@ -29,14 +29,14 @@
= check_box_tag "check_all_issues", nil, false, = check_box_tag "check_all_issues", nil, false,
class: "check_all_issues left" class: "check_all_issues left"
.issues-other-filters .issues-other-filters
.filter-item.inline
= users_select_tag(:assignee_id, selected: params[:assignee_id],
placeholder: 'Assignee', class: 'trigger-submit', any_user: "Any Assignee", null_user: true, first_user: true, current_user: true)
.filter-item.inline .filter-item.inline
= users_select_tag(:author_id, selected: params[:author_id], = users_select_tag(:author_id, selected: params[:author_id],
placeholder: 'Author', class: 'trigger-submit', any_user: "Any Author", first_user: true, current_user: true) placeholder: 'Author', class: 'trigger-submit', any_user: "Any Author", first_user: true, current_user: true)
.filter-item.inline
= users_select_tag(:assignee_id, selected: params[:assignee_id],
placeholder: 'Assignee', class: 'trigger-submit', any_user: "Any Assignee", null_user: true, first_user: true, current_user: true)
.filter-item.inline.milestone-filter .filter-item.inline.milestone-filter
= select_tag('milestone_title', projects_milestones_options, = select_tag('milestone_title', projects_milestones_options,
class: 'select2 trigger-submit', include_blank: true, class: 'select2 trigger-submit', include_blank: true,
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- css_class = '' unless local_assigns[:css_class] - css_class = '' unless local_assigns[:css_class]
- css_class += " no-description" unless project.description.present? - css_class += " no-description" unless project.description.present?
%li.project-row{ class: css_class } %li.project-row{ class: css_class }
= cache [project.namespace, project, controller.controller_name, controller.action_name, 'v2.2'] do = cache [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.2'] do
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
- if avatar - if avatar
.dash-project-avatar .dash-project-avatar
......
<%= ENV['RAILS_ENV'] %>:
adapter: <%= ENV['GITLAB_DATABASE_ADAPTER'] || 'postgresql' %>
encoding: <%= ENV['GITLAB_DATABASE_ENCODING'] || 'unicode' %>
database: <%= ENV['GITLAB_DATABASE_DATABASE'] || "gitlab_#{ENV['RAILS_ENV']}" %>
pool: <%= ENV['GITLAB_DATABASE_POOL'] || '10' %>
username: <%= ENV['GITLAB_DATABASE_USERNAME'] || 'root' %>
password: <%= ENV['GITLAB_DATABASE_PASSWORD'] || '' %>
host: <%= ENV['GITLAB_DATABASE_HOST'] || 'localhost' %>
port: <%= ENV['GITLAB_DATABASE_PORT'] || '5432' %>
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
# #
########################### NOTE ##################################### ########################### NOTE #####################################
# This file should not receive new settings. All configuration options # # This file should not receive new settings. All configuration options #
# that do not require an application restart are being moved to # # * are being moved to ApplicationSetting model! #
# ApplicationSetting model! # # If a setting requires an application restart say so in that screen. #
# If you change this file in a Merge Request, please also create # # If you change this file in a Merge Request, please also create #
# a MR on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests # # a MR on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests #
######################################################################## ########################################################################
......
...@@ -205,16 +205,16 @@ Settings.gitlab['default_projects_limit'] ||= 10 ...@@ -205,16 +205,16 @@ Settings.gitlab['default_projects_limit'] ||= 10
Settings.gitlab['default_branch_protection'] ||= 2 Settings.gitlab['default_branch_protection'] ||= 2
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil? Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil? Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil?
Settings.gitlab['host'] ||= 'localhost' Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost'
Settings.gitlab['ssh_host'] ||= Settings.gitlab.host Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || '' Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http" Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil? Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil?
Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}" Settings.gitlab['email_from'] ||= ENV['GITLAB_EMAIL_FROM'] || "gitlab@#{Settings.gitlab.host}"
Settings.gitlab['email_display_name'] ||= "GitLab" Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'GitLab'
Settings.gitlab['email_reply_to'] ||= "noreply@#{Settings.gitlab.host}" Settings.gitlab['email_reply_to'] ||= ENV['GITLAB_EMAIL_REPLY_TO'] || "noreply@#{Settings.gitlab.host}"
Settings.gitlab['base_url'] ||= Settings.send(:build_base_gitlab_url) Settings.gitlab['base_url'] ||= Settings.send(:build_base_gitlab_url)
Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url)
Settings.gitlab['user'] ||= 'git' Settings.gitlab['user'] ||= 'git'
......
if Gitlab::Metrics.enabled?
require 'influxdb'
require 'socket'
require 'connection_pool'
require 'method_source'
# These are manually require'd so the classes are registered properly with
# ActiveSupport.
require 'gitlab/metrics/subscribers/action_view'
require 'gitlab/metrics/subscribers/active_record'
Gitlab::Application.configure do |config|
config.middleware.use(Gitlab::Metrics::RackMiddleware)
end
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Gitlab::Metrics::SidekiqMiddleware
end
end
# This instruments all methods residing in app/models that (appear to) use any
# of the ActiveRecord methods. This has to take place _after_ initializing as
# for some unknown reason calling eager_load! earlier breaks Devise.
Gitlab::Application.config.after_initialize do
Rails.application.eager_load!
models = Rails.root.join('app', 'models').to_s
regex = Regexp.union(
ActiveRecord::Querying.public_instance_methods(false).map(&:to_s)
)
Gitlab::Metrics::Instrumentation.
instrument_class_hierarchy(ActiveRecord::Base) do |klass, method|
# Instrumenting the ApplicationSetting class can lead to an infinite
# loop. Since the data is cached any way we don't really need to
# instrument it.
if klass == ApplicationSetting
false
else
loc = method.source_location
loc && loc[0].start_with?(models) && method.source =~ regex
end
end
end
Gitlab::Metrics::Instrumentation.configure do |config|
config.instrument_instance_methods(Gitlab::Shell)
config.instrument_methods(Gitlab::Git)
Gitlab::Git.constants.each do |name|
const = Gitlab::Git.const_get(name)
config.instrument_methods(const) if const.is_a?(Module)
end
end
GC::Profiler.enable
Gitlab::Metrics::Sampler.new.start
end
# Use this file to easily define all of your cron jobs.
#
# If you make changes to this file, please create also an issue on
# https://gitlab.com/gitlab-org/omnibus-gitlab/issues . This is necessary
# because the omnibus packages manage cron jobs using Chef instead of Whenever.
every 1.hour do
rake "ci:schedule_builds"
end
# Migration type: online
class RemovePublicFromNamespace < ActiveRecord::Migration
def change
remove_column :namespaces, :public, :boolean
end
end
class InfluxdbSettings < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_enabled, :boolean, default: false
add_column :application_settings, :metrics_host, :string,
default: 'localhost'
add_column :application_settings, :metrics_database, :string,
default: 'gitlab'
add_column :application_settings, :metrics_username, :string
add_column :application_settings, :metrics_password, :string
add_column :application_settings, :metrics_pool_size, :integer, default: 16
add_column :application_settings, :metrics_timeout, :integer, default: 10
add_column :application_settings, :metrics_method_call_threshold,
:integer, default: 10
end
end
class AddRecaptchaToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
t.boolean :recaptcha_enabled, default: false
t.string :recaptcha_site_key
t.string :recaptcha_private_key
end
end
end
class InfluxdbUdpPortSetting < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_port, :integer, default: 8089
end
end
class InfluxdbRemoteDatabaseSetting < ActiveRecord::Migration
def change
remove_column :application_settings, :metrics_database
end
end
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- [GitLab Basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab. - [GitLab Basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab.
- [Importing to GitLab](workflow/importing/README.md). - [Importing to GitLab](workflow/importing/README.md).
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system. - [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
- [Migrating from SVN](migration/README.md) Convert a SVN repository to Git and GitLab - [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do. - [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
- [Profile Settings](profile/README.md) - [Profile Settings](profile/README.md)
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat. - [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
- [Log system](logs/logs.md) Log system. - [Log system](logs/logs.md) Log system.
- [Environmental Variables](administration/environment_variables.md) to configure GitLab.
- [Operations](operations/README.md) Keeping GitLab up and running - [Operations](operations/README.md) Keeping GitLab up and running
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects. - [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance. - [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
......
# Environment Variables
## Introduction
Commonly people configure GitLab via the gitlab.rb configuration file in the Omnibus package.
But if you prefer to use environment variables we allow that too.
## Supported environment variables
Variable | Type | Explanation
-------- | ---- | -----------
GITLAB_ROOT_PASSWORD | string | sets the password for the `root` user on installation
GITLAB_HOST | url | hostname of the GitLab server includes http or https
RAILS_ENV | production / development / staging / test | Rails environment
DATABASE_URL | url | For example: postgresql://localhost/blog_development?pool=5
GITLAB_EMAIL_FROM | email | Email address used in the "From" field in mails sent by GitLab
GITLAB_EMAIL_DISPLAY_NAME | string | Name used in the "From" field in mails sent by GitLab
GITLAB_EMAIL_REPLY_TO | email | Email address used in the "Reply-To" field in mails sent by GitLab
## Complete database variables
As explained in the [Heroku documentation](https://devcenter.heroku.com/articles/rails-database-connection-behavior) the DATABASE_URL doesn't let you set:
- adapter
- database
- username
- password
- host
- port
To do so please `cp config/database.yml.env config/database.yml` and use the following variables:
Variable | Default
--- | ---
GITLAB_DATABASE_ADAPTER | postgresql
GITLAB_DATABASE_ENCODING | unicode
GITLAB_DATABASE_DATABASE | gitlab_#{ENV['RAILS_ENV']
GITLAB_DATABASE_POOL | 10
GITLAB_DATABASE_USERNAME | root
GITLAB_DATABASE_PASSWORD |
GITLAB_DATABASE_HOST | localhost
GITLAB_DATABASE_PORT | 5432
## Other variables
We welcome merge requests to make more settings configurable via variables.
Please stick to the naming scheme "GITLAB_#{name 1_settings.rb in upper case}".
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
Get all merge requests for this project. Get all merge requests for this project.
The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. With GitLab 8.2 the return fields `upvotes` and The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
`downvotes` are deprecated and always return `0`.
``` ```
GET /projects/:id/merge_requests GET /projects/:id/merge_requests
...@@ -58,7 +57,7 @@ Parameters: ...@@ -58,7 +57,7 @@ Parameters:
## Get single MR ## Get single MR
Shows information about a single merge request. With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and always return `0`. Shows information about a single merge request.
``` ```
GET /projects/:id/merge_request/:merge_request_id GET /projects/:id/merge_request/:merge_request_id
...@@ -141,8 +140,6 @@ Parameters: ...@@ -141,8 +140,6 @@ Parameters:
## Get single MR changes ## Get single MR changes
Shows information about the merge request including its files and changes. Shows information about the merge request including its files and changes.
With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and
always return `0`.
``` ```
GET /projects/:id/merge_request/:merge_request_id/changes GET /projects/:id/merge_request/:merge_request_id/changes
...@@ -213,9 +210,7 @@ Parameters: ...@@ -213,9 +210,7 @@ Parameters:
## Create MR ## Create MR
Creates a new merge request. With GitLab 8.2 the return fields `upvotes` and ` Creates a new merge request.
downvotes` are deprecated and always return `0`.
``` ```
POST /projects/:id/merge_requests POST /projects/:id/merge_requests
``` ```
...@@ -266,8 +261,7 @@ If an error occurs, an error number and a message explaining the reason is retur ...@@ -266,8 +261,7 @@ If an error occurs, an error number and a message explaining the reason is retur
## Update MR ## Update MR
Updates an existing merge request. You can change the target branch, title, or even close the MR. With GitLab 8.2 the return fields `upvotes` and `downvotes` Updates an existing merge request. You can change the target branch, title, or even close the MR.
are deprecated and always return `0`.
``` ```
PUT /projects/:id/merge_request/:merge_request_id PUT /projects/:id/merge_request/:merge_request_id
...@@ -318,8 +312,7 @@ If an error occurs, an error number and a message explaining the reason is retur ...@@ -318,8 +312,7 @@ If an error occurs, an error number and a message explaining the reason is retur
## Accept MR ## Accept MR
Merge changes submitted with MR using this API. With GitLab 8.2 the return Merge changes submitted with MR using this API.
fields `upvotes` and `downvotes` are deprecated and always return `0`.
If merge success you get `200 OK`. If merge success you get `200 OK`.
......
...@@ -6,8 +6,7 @@ Notes are comments on snippets, issues or merge requests. ...@@ -6,8 +6,7 @@ Notes are comments on snippets, issues or merge requests.
### List project issue notes ### List project issue notes
Gets a list of all notes for a single issue. With GitLab 8.2 the return fields Gets a list of all notes for a single issue.
`upvote` and `downvote` are deprecated and always return `false`.
``` ```
GET /projects/:id/issues/:issue_id/notes GET /projects/:id/issues/:issue_id/notes
......
...@@ -11,9 +11,6 @@ You can add a new trigger by going to your project's **Settings > Triggers**. ...@@ -11,9 +11,6 @@ You can add a new trigger by going to your project's **Settings > Triggers**.
The **Add trigger** button will create a new token which you can then use to The **Add trigger** button will create a new token which you can then use to
trigger a rebuild of this particular project. trigger a rebuild of this particular project.
Once at least one trigger is created, on the **Triggers** page you will find
some descriptive information on how you can
Every new trigger you create, gets assigned a different token which you can Every new trigger you create, gets assigned a different token which you can
then use inside your scripts or `.gitlab-ci.yml`. You also have a nice then use inside your scripts or `.gitlab-ci.yml`. You also have a nice
overview of the time the triggers were last used. overview of the time the triggers were last used.
...@@ -111,7 +108,7 @@ Now, whenever a new tag is pushed on project A, the build will run and the ...@@ -111,7 +108,7 @@ Now, whenever a new tag is pushed on project A, the build will run and the
`stage: test` complete successfully. `stage: test` complete successfully.
_**Note:** If your project is public, passing the token in plain text is _**Note:** If your project is public, passing the token in plain text is
probably not the wiser idea, so you might want to use a probably not the wisest idea, so you might want to use a
[secure variable](../variables/README.md#user-defined-variables-secure-variables) [secure variable](../variables/README.md#user-defined-variables-secure-variables)
for that purpose._ for that purpose._
......
...@@ -15,6 +15,7 @@ See the documentation below for details on how to configure these services. ...@@ -15,6 +15,7 @@ See the documentation below for details on how to configure these services.
- [Kerberos](kerberos.md) Integrate with Kerberos - [Kerberos](kerberos.md) Integrate with Kerberos
- [OAuth2 provider](oauth_provider.md) OAuth2 application creation - [OAuth2 provider](oauth_provider.md) OAuth2 application creation
- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages - [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages
- [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users
GitLab Enterprise Edition contains [advanced JIRA support](http://doc.gitlab.com/ee/integration/jira.html) and [advanced Jenkins support](http://doc.gitlab.com/ee/integration/jenkins.html). GitLab Enterprise Edition contains [advanced JIRA support](http://doc.gitlab.com/ee/integration/jira.html) and [advanced Jenkins support](http://doc.gitlab.com/ee/integration/jenkins.html).
......
# reCAPTCHA
GitLab leverages [Google's reCAPTCHA](https://www.google.com/recaptcha/intro/index.html)
to protect against spam and abuse. GitLab displays the CAPTCHA form on the sign-up page
to confirm that a real user, not a bot, is attempting to create an account.
## Configuration
To use reCAPTCHA, first you must create a site and private key.
1. Go to the URL: https://www.google.com/recaptcha/admin
2. Fill out the form necessary to obtain reCAPTCHA keys.
3. Login to your GitLab server, with administrator credentials.
4. Go to Applications Settings on Admin Area (`admin/application_settings`)
5. Fill all recaptcha fields with keys from previous steps
6. Check the `Enable reCAPTCHA` checkbox
7. Save the configuration.
...@@ -6,11 +6,11 @@ If a user is both in a project group and in the project itself, the highest perm ...@@ -6,11 +6,11 @@ If a user is both in a project group and in the project itself, the highest perm
If a user is a GitLab administrator they receive all permissions. If a user is a GitLab administrator they receive all permissions.
On public projects the Guest role is not enforced. On public projects the Guest role is not enforced.
All users will be able to create issues, leave comments, and pull or download the project code. All users will be able to create issues, leave comments, and pull or download the project code.
To add or import a user, you can follow the [project users and members To add or import a user, you can follow the [project users and members
documentation](doc/workflow/add-user/add-user.md). documentation](../workflow/add-user/add-user.md).
## Project ## Project
......
...@@ -167,6 +167,49 @@ with the name of your bucket: ...@@ -167,6 +167,49 @@ with the name of your bucket:
} }
``` ```
### Uploading to locally mounted shares
You may also send backups to a mounted share (`NFS` / `CIFS` / `SMB` / etc.) by
using the [`Local`](https://github.com/fog/fog-local#usage) storage provider.
The directory pointed to by the `local_root` key **must** be owned by the `git`
user **when mounted** (mounting with the `uid=` of the `git` user for `CIFS` and
`SMB`) or the user that you are executing the backup tasks under (for omnibus
packages, this is the `git` user).
The `backup_upload_remote_directory` **must** be set in addition to the
`local_root` key. This is the sub directory inside the mounted directory that
backups will be copied to, and will be created if it does not exist. If the
directory that you want to copy the tarballs to is the root of your mounted
directory, just use `.` instead.
For omnibus packages:
```ruby
gitlab_rails['backup_upload_connection'] = {
:provider => 'Local',
:local_root => '/mnt/backups'
}
# The directory inside the mounted folder to copy backups to
# Use '.' to store them in the root directory
gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups'
```
For installations from source:
```yaml
backup:
# snip
upload:
# Fog storage connection settings, see http://fog.io/storage/ .
connection:
provider: Local
local_root: '/mnt/backups'
# The directory inside the mounted folder to copy backups to
# Use '.' to store them in the root directory
remote_directory: 'gitlab_backups'
```
## Backup archive permissions ## Backup archive permissions
The backup archives created by GitLab (123456_gitlab_backup.tar) will have owner/group git:git and 0600 permissions by default. The backup archives created by GitLab (123456_gitlab_backup.tar) will have owner/group git:git and 0600 permissions by default.
......
# System hooks # System hooks
Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`. Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `project_rename`, `project_transfer`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`.
System hooks can be used, e.g. for logging or changing information in a LDAP server. System hooks can be used, e.g. for logging or changing information in a LDAP server.
...@@ -17,6 +17,7 @@ X-Gitlab-Event: System Hook ...@@ -17,6 +17,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:30:54Z", "created_at": "2012-07-21T07:30:54Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "project_create", "event_name": "project_create",
"name": "StoreCloud", "name": "StoreCloud",
"owner_email": "johnsmith@gmail.com", "owner_email": "johnsmith@gmail.com",
...@@ -33,6 +34,7 @@ X-Gitlab-Event: System Hook ...@@ -33,6 +34,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:30:58Z", "created_at": "2012-07-21T07:30:58Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "project_destroy", "event_name": "project_destroy",
"name": "Underscore", "name": "Underscore",
"owner_email": "johnsmith@gmail.com", "owner_email": "johnsmith@gmail.com",
...@@ -44,11 +46,48 @@ X-Gitlab-Event: System Hook ...@@ -44,11 +46,48 @@ X-Gitlab-Event: System Hook
} }
``` ```
**Project renamed:**
```json
{
"created_at": "2012-07-21T07:30:58Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "project_rename",
"name": "Underscore",
"path": "underscore",
"path_with_namespace": "jsmith/underscore",
"project_id": 73,
"owner_name": "John Smith",
"owner_email": "johnsmith@gmail.com",
"project_visibility": "internal",
"old_path_with_namespace": "jsmith/overscore",
}
```
**Project transferred:**
```json
{
"created_at": "2012-07-21T07:30:58Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "project_transfer",
"name": "Underscore",
"path": "underscore",
"path_with_namespace": "scores/underscore",
"project_id": 73,
"owner_name": "John Smith",
"owner_email": "johnsmith@gmail.com",
"project_visibility": "internal",
"old_path_with_namespace": "jsmith/overscore",
}
```
**New Team Member:** **New Team Member:**
```json ```json
{ {
"created_at": "2012-07-21T07:30:56Z", "created_at": "2012-07-21T07:30:56Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "user_add_to_team", "event_name": "user_add_to_team",
"project_access": "Master", "project_access": "Master",
"project_id": 74, "project_id": 74,
...@@ -67,6 +106,7 @@ X-Gitlab-Event: System Hook ...@@ -67,6 +106,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:30:56Z", "created_at": "2012-07-21T07:30:56Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "user_remove_from_team", "event_name": "user_remove_from_team",
"project_access": "Master", "project_access": "Master",
"project_id": 74, "project_id": 74,
...@@ -85,6 +125,7 @@ X-Gitlab-Event: System Hook ...@@ -85,6 +125,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:44:07Z", "created_at": "2012-07-21T07:44:07Z",
"updated_at": "2012-07-21T07:38:22Z",
"email": "js@gitlabhq.com", "email": "js@gitlabhq.com",
"event_name": "user_create", "event_name": "user_create",
"name": "John Smith", "name": "John Smith",
...@@ -97,6 +138,7 @@ X-Gitlab-Event: System Hook ...@@ -97,6 +138,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:44:07Z", "created_at": "2012-07-21T07:44:07Z",
"updated_at": "2012-07-21T07:38:22Z",
"email": "js@gitlabhq.com", "email": "js@gitlabhq.com",
"event_name": "user_destroy", "event_name": "user_destroy",
"name": "John Smith", "name": "John Smith",
...@@ -110,6 +152,7 @@ X-Gitlab-Event: System Hook ...@@ -110,6 +152,7 @@ X-Gitlab-Event: System Hook
{ {
"event_name": "key_create", "event_name": "key_create",
"created_at": "2014-08-18 18:45:16 UTC", "created_at": "2014-08-18 18:45:16 UTC",
"updated_at": "2012-07-21T07:38:22Z",
"username": "root", "username": "root",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC58FwqHUbebw2SdT7SP4FxZ0w+lAO/erhy2ylhlcW/tZ3GY3mBu9VeeiSGoGz8hCx80Zrz+aQv28xfFfKlC8XQFpCWwsnWnQqO2Lv9bS8V1fIHgMxOHIt5Vs+9CAWGCCvUOAurjsUDoE2ALIXLDMKnJxcxD13XjWdK54j6ZXDB4syLF0C2PnAQSVY9X7MfCYwtuFmhQhKaBussAXpaVMRHltie3UYSBUUuZaB3J4cg/7TxlmxcNd+ppPRIpSZAB0NI6aOnqoBCpimscO/VpQRJMVLr3XiSYeT6HBiDXWHnIVPfQc03OGcaFqOit6p8lYKMaP/iUQLm+pgpZqrXZ9vB john@localhost", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC58FwqHUbebw2SdT7SP4FxZ0w+lAO/erhy2ylhlcW/tZ3GY3mBu9VeeiSGoGz8hCx80Zrz+aQv28xfFfKlC8XQFpCWwsnWnQqO2Lv9bS8V1fIHgMxOHIt5Vs+9CAWGCCvUOAurjsUDoE2ALIXLDMKnJxcxD13XjWdK54j6ZXDB4syLF0C2PnAQSVY9X7MfCYwtuFmhQhKaBussAXpaVMRHltie3UYSBUUuZaB3J4cg/7TxlmxcNd+ppPRIpSZAB0NI6aOnqoBCpimscO/VpQRJMVLr3XiSYeT6HBiDXWHnIVPfQc03OGcaFqOit6p8lYKMaP/iUQLm+pgpZqrXZ9vB john@localhost",
"id": 4 "id": 4
...@@ -122,6 +165,7 @@ X-Gitlab-Event: System Hook ...@@ -122,6 +165,7 @@ X-Gitlab-Event: System Hook
{ {
"event_name": "key_destroy", "event_name": "key_destroy",
"created_at": "2014-08-18 18:45:16 UTC", "created_at": "2014-08-18 18:45:16 UTC",
"updated_at": "2012-07-21T07:38:22Z",
"username": "root", "username": "root",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC58FwqHUbebw2SdT7SP4FxZ0w+lAO/erhy2ylhlcW/tZ3GY3mBu9VeeiSGoGz8hCx80Zrz+aQv28xfFfKlC8XQFpCWwsnWnQqO2Lv9bS8V1fIHgMxOHIt5Vs+9CAWGCCvUOAurjsUDoE2ALIXLDMKnJxcxD13XjWdK54j6ZXDB4syLF0C2PnAQSVY9X7MfCYwtuFmhQhKaBussAXpaVMRHltie3UYSBUUuZaB3J4cg/7TxlmxcNd+ppPRIpSZAB0NI6aOnqoBCpimscO/VpQRJMVLr3XiSYeT6HBiDXWHnIVPfQc03OGcaFqOit6p8lYKMaP/iUQLm+pgpZqrXZ9vB john@localhost", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC58FwqHUbebw2SdT7SP4FxZ0w+lAO/erhy2ylhlcW/tZ3GY3mBu9VeeiSGoGz8hCx80Zrz+aQv28xfFfKlC8XQFpCWwsnWnQqO2Lv9bS8V1fIHgMxOHIt5Vs+9CAWGCCvUOAurjsUDoE2ALIXLDMKnJxcxD13XjWdK54j6ZXDB4syLF0C2PnAQSVY9X7MfCYwtuFmhQhKaBussAXpaVMRHltie3UYSBUUuZaB3J4cg/7TxlmxcNd+ppPRIpSZAB0NI6aOnqoBCpimscO/VpQRJMVLr3XiSYeT6HBiDXWHnIVPfQc03OGcaFqOit6p8lYKMaP/iUQLm+pgpZqrXZ9vB john@localhost",
"id": 4 "id": 4
...@@ -133,6 +177,7 @@ X-Gitlab-Event: System Hook ...@@ -133,6 +177,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:30:54Z", "created_at": "2012-07-21T07:30:54Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "group_create", "event_name": "group_create",
"name": "StoreCloud", "name": "StoreCloud",
"owner_email": "johnsmith@gmail.com", "owner_email": "johnsmith@gmail.com",
...@@ -147,6 +192,7 @@ X-Gitlab-Event: System Hook ...@@ -147,6 +192,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:30:54Z", "created_at": "2012-07-21T07:30:54Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "group_destroy", "event_name": "group_destroy",
"name": "StoreCloud", "name": "StoreCloud",
"owner_email": "johnsmith@gmail.com", "owner_email": "johnsmith@gmail.com",
...@@ -161,6 +207,7 @@ X-Gitlab-Event: System Hook ...@@ -161,6 +207,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:30:56Z", "created_at": "2012-07-21T07:30:56Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "user_add_to_group", "event_name": "user_add_to_group",
"group_access": "Master", "group_access": "Master",
"group_id": 78, "group_id": 78,
...@@ -176,6 +223,7 @@ X-Gitlab-Event: System Hook ...@@ -176,6 +223,7 @@ X-Gitlab-Event: System Hook
```json ```json
{ {
"created_at": "2012-07-21T07:30:56Z", "created_at": "2012-07-21T07:30:56Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "user_remove_from_group", "event_name": "user_remove_from_group",
"group_access": "Master", "group_access": "Master",
"group_id": 78, "group_id": 78,
......
...@@ -99,8 +99,6 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production ...@@ -99,8 +99,6 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache # Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
# Update init.d script
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
``` ```
### 7. Update configuration files ### 7. Update configuration files
......
...@@ -105,15 +105,6 @@ Feature: Explore Groups ...@@ -105,15 +105,6 @@ Feature: Explore Groups
When I visit the public groups area When I visit the public groups area
Then I should see group "TestGroup" Then I should see group "TestGroup"
Scenario: I should not see group with internal project in public groups area
Given group "TestGroup" has internal project "Internal"
When I visit the public groups area
Then I should not see group "TestGroup"
Scenario: I should not see group with private project in public groups area
When I visit the public groups area
Then I should not see group "TestGroup"
Scenario: I should see group with public project in public groups area as user Scenario: I should see group with public project in public groups area as user
Given group "TestGroup" has public project "Community" Given group "TestGroup" has public project "Community"
When I sign in as a user When I sign in as a user
...@@ -125,9 +116,3 @@ Feature: Explore Groups ...@@ -125,9 +116,3 @@ Feature: Explore Groups
When I sign in as a user When I sign in as a user
And I visit the public groups area And I visit the public groups area
Then I should see group "TestGroup" Then I should see group "TestGroup"
Scenario: I should not see group with private project in public groups area as user
When I sign in as a user
And I visit the public groups area
Then I should not see group "TestGroup"
...@@ -24,7 +24,7 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps ...@@ -24,7 +24,7 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
page.within '.awards' do page.within '.awards' do
expect do expect do
page.find('.award.active').click page.find('.award.active').click
sleep 0.1 sleep 0.3
end.to change{ page.all(".award").size }.from(3).to(2) end.to change{ page.all(".award").size }.from(3).to(2)
end end
end end
......
...@@ -59,15 +59,14 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -59,15 +59,14 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end end
step 'I click "author" dropdown' do step 'I click "author" dropdown' do
first('.ajax-users-select').click first('#s2id_author_id').click
end end
step 'I see current user as the first user' do step 'I see current user as the first user' do
expect(page).to have_selector('.user-result', visible: true, count: 4) expect(page).to have_selector('.user-result', visible: true, count: 3)
users = page.all('.user-name') users = page.all('.user-name')
expect(users[0].text).to eq 'Any Assignee' expect(users[0].text).to eq 'Any Author'
expect(users[1].text).to eq 'Unassigned' expect(users[1].text).to eq current_user.name
expect(users[2].text).to eq current_user.name
end end
step 'I submit new issue "500 error on profile"' do step 'I submit new issue "500 error on profile"' do
......
...@@ -176,7 +176,6 @@ module API ...@@ -176,7 +176,6 @@ module API
class MergeRequest < ProjectEntity class MergeRequest < ProjectEntity
expose :target_branch, :source_branch expose :target_branch, :source_branch
# deprecated, always returns 0
expose :upvotes, :downvotes expose :upvotes, :downvotes
expose :author, :assignee, using: Entities::UserBasic expose :author, :assignee, using: Entities::UserBasic
expose :source_project_id, :target_project_id expose :source_project_id, :target_project_id
......
...@@ -47,7 +47,17 @@ module Banzai ...@@ -47,7 +47,17 @@ module Banzai
{ object_sym => LazyReference.new(object_class, node.attr(data_reference)) } { object_sym => LazyReference.new(object_class, node.attr(data_reference)) }
end end
delegate :object_class, :object_sym, :references_in, to: :class def object_class
self.class.object_class
end
def object_sym
self.class.object_sym
end
def references_in(*args, &block)
self.class.references_in(*args, &block)
end
def find_object(project, id) def find_object(project, id)
# Implement in child class # Implement in child class
......
...@@ -10,8 +10,8 @@ module Banzai ...@@ -10,8 +10,8 @@ module Banzai
# #
class RedactorFilter < HTML::Pipeline::Filter class RedactorFilter < HTML::Pipeline::Filter
def call def call
doc.css('a.gfm').each do |node| Querying.css(doc, 'a.gfm').each do |node|
unless user_can_reference?(node) unless user_can_see_reference?(node)
# The reference should be replaced by the original text, # The reference should be replaced by the original text,
# which is not always the same as the rendered text. # which is not always the same as the rendered text.
text = node.attr('data-original') || node.text text = node.attr('data-original') || node.text
...@@ -24,12 +24,12 @@ module Banzai ...@@ -24,12 +24,12 @@ module Banzai
private private
def user_can_reference?(node) def user_can_see_reference?(node)
if node.has_attribute?('data-reference-filter') if node.has_attribute?('data-reference-filter')
reference_type = node.attr('data-reference-filter') reference_type = node.attr('data-reference-filter')
reference_filter = Banzai::Filter.const_get(reference_type) reference_filter = Banzai::Filter.const_get(reference_type)
reference_filter.user_can_reference?(current_user, node, context) reference_filter.user_can_see_reference?(current_user, node, context)
else else
true true
end end
......
...@@ -12,7 +12,7 @@ module Banzai ...@@ -12,7 +12,7 @@ module Banzai
# :project (required) - Current project, ignored if reference is cross-project. # :project (required) - Current project, ignored if reference is cross-project.
# :only_path - Generate path-only links. # :only_path - Generate path-only links.
class ReferenceFilter < HTML::Pipeline::Filter class ReferenceFilter < HTML::Pipeline::Filter
def self.user_can_reference?(user, node, context) def self.user_can_see_reference?(user, node, context)
if node.has_attribute?('data-project') if node.has_attribute?('data-project')
project_id = node.attr('data-project').to_i project_id = node.attr('data-project').to_i
return true if project_id == context[:project].try(:id) return true if project_id == context[:project].try(:id)
...@@ -24,6 +24,10 @@ module Banzai ...@@ -24,6 +24,10 @@ module Banzai
end end
end end
def self.user_can_reference?(user, node, context)
true
end
def self.referenced_by(node) def self.referenced_by(node)
raise NotImplementedError, "#{self} does not implement #{__method__}" raise NotImplementedError, "#{self} does not implement #{__method__}"
end end
...@@ -120,7 +124,7 @@ module Banzai ...@@ -120,7 +124,7 @@ module Banzai
def replace_link_nodes_with_text(pattern) def replace_link_nodes_with_text(pattern)
return doc if project.nil? return doc if project.nil?
doc.search('a').each do |node| doc.xpath('descendant-or-self::a').each do |node|
klass = node.attr('class') klass = node.attr('class')
next if klass && klass.include?('gfm') next if klass && klass.include?('gfm')
...@@ -158,7 +162,7 @@ module Banzai ...@@ -158,7 +162,7 @@ module Banzai
def replace_link_nodes_with_href(pattern) def replace_link_nodes_with_href(pattern)
return doc if project.nil? return doc if project.nil?
doc.search('a').each do |node| doc.xpath('descendant-or-self::a').each do |node|
klass = node.attr('class') klass = node.attr('class')
next if klass && klass.include?('gfm') next if klass && klass.include?('gfm')
......
...@@ -16,7 +16,7 @@ module Banzai ...@@ -16,7 +16,7 @@ module Banzai
end end
def call def call
doc.css('a.gfm').each do |node| Querying.css(doc, 'a.gfm').each do |node|
gather_references(node) gather_references(node)
end end
...@@ -35,7 +35,9 @@ module Banzai ...@@ -35,7 +35,9 @@ module Banzai
return if context[:reference_filter] && reference_filter != context[:reference_filter] return if context[:reference_filter] && reference_filter != context[:reference_filter]
return unless reference_filter.user_can_reference?(current_user, node, context) return if author && !reference_filter.user_can_reference?(author, node, context)
return unless reference_filter.user_can_see_reference?(current_user, node, context)
references = reference_filter.referenced_by(node) references = reference_filter.referenced_by(node)
return unless references return unless references
...@@ -57,6 +59,10 @@ module Banzai ...@@ -57,6 +59,10 @@ module Banzai
def current_user def current_user
context[:current_user] context[:current_user]
end end
def author
context[:author]
end
end end
end end
end end
...@@ -39,7 +39,7 @@ module Banzai ...@@ -39,7 +39,7 @@ module Banzai
end end
end end
def self.user_can_reference?(user, node, context) def self.user_can_see_reference?(user, node, context)
if node.has_attribute?('data-group') if node.has_attribute?('data-group')
group = Group.find(node.attr('data-group')) rescue nil group = Group.find(node.attr('data-group')) rescue nil
Ability.abilities.allowed?(user, :read_group, group) Ability.abilities.allowed?(user, :read_group, group)
...@@ -48,6 +48,18 @@ module Banzai ...@@ -48,6 +48,18 @@ module Banzai
end end
end end
def self.user_can_reference?(user, node, context)
# Only team members can reference `@all`
if node.has_attribute?('data-project')
project = Project.find(node.attr('data-project')) rescue nil
return false unless project
user && project.team.member?(user)
else
super
end
end
def call def call
replace_text_nodes_matching(User.reference_pattern) do |content| replace_text_nodes_matching(User.reference_pattern) do |content|
user_link_filter(content) user_link_filter(content)
......
module Banzai
module Querying
# Searches a Nokogiri document using a CSS query, optionally optimizing it
# whenever possible.
#
# document - A document/element to search.
# query - The CSS query to use.
#
# Returns a Nokogiri::XML::NodeSet.
def self.css(document, query)
# When using "a.foo" Nokogiri compiles this to "//a[...]" but
# "descendant::a[...]" is quite a bit faster and achieves the same result.
xpath = Nokogiri::CSS.xpath_for(query)[0].gsub(%r{^//}, 'descendant::')
document.xpath(xpath)
end
end
end
...@@ -45,11 +45,11 @@ module Gitlab ...@@ -45,11 +45,11 @@ module Gitlab
end end
def starting_year def starting_year
(Time.now - 1.year).strftime("%Y") 1.year.ago.year
end end
def starting_month def starting_month
Date.today.strftime("%m").to_i Date.today.month
end end
end end
end end
...@@ -38,7 +38,9 @@ module Gitlab ...@@ -38,7 +38,9 @@ module Gitlab
true true
end end
use_db && ActiveRecord::Base.connection.active? && ActiveRecord::Base.connection.table_exists?('application_settings') use_db && ActiveRecord::Base.connection.active? &&
!ActiveRecord::Migrator.needs_migration? &&
ActiveRecord::Base.connection.table_exists?('application_settings')
end end
end end
end end
module Gitlab
module Metrics
extend Gitlab::CurrentSettings
RAILS_ROOT = Rails.root.to_s
METRICS_ROOT = Rails.root.join('lib', 'gitlab', 'metrics').to_s
PATH_REGEX = /^#{RAILS_ROOT}\/?/
def self.pool_size
current_application_settings[:metrics_pool_size] || 16
end
def self.timeout
current_application_settings[:metrics_timeout] || 10
end
def self.enabled?
current_application_settings[:metrics_enabled] || false
end
def self.mri?
RUBY_ENGINE == 'ruby'
end
def self.method_call_threshold
# This is memoized since this method is called for every instrumented
# method. Loading data from an external cache on every method call slows
# things down too much.
@method_call_threshold ||=
(current_application_settings[:metrics_method_call_threshold] || 10)
end
def self.pool
@pool
end
def self.hostname
@hostname
end
# Returns a relative path and line number based on the last application call
# frame.
def self.last_relative_application_frame
frame = caller_locations.find do |l|
l.path.start_with?(RAILS_ROOT) && !l.path.start_with?(METRICS_ROOT)
end
if frame
return frame.path.sub(PATH_REGEX, ''), frame.lineno
else
return nil, nil
end
end
def self.submit_metrics(metrics)
prepared = prepare_metrics(metrics)
pool.with do |connection|
prepared.each do |metric|
begin
connection.write_points([metric])
rescue StandardError
end
end
end
end
def self.prepare_metrics(metrics)
metrics.map do |hash|
new_hash = hash.symbolize_keys
new_hash[:tags].each do |key, value|
if value.blank?
new_hash[:tags].delete(key)
else
new_hash[:tags][key] = escape_value(value)
end
end
new_hash
end
end
def self.escape_value(value)
value.to_s.gsub('=', '\\=')
end
@hostname = Socket.gethostname
# When enabled this should be set before being used as the usual pattern
# "@foo ||= bar" is _not_ thread-safe.
if enabled?
@pool = ConnectionPool.new(size: pool_size, timeout: timeout) do
host = current_application_settings[:metrics_host]
user = current_application_settings[:metrics_username]
pw = current_application_settings[:metrics_password]
port = current_application_settings[:metrics_port]
InfluxDB::Client.
new(udp: { host: host, port: port }, username: user, password: pw)
end
end
end
end
module Gitlab
module Metrics
# Class for calculating the difference between two numeric values.
#
# Every call to `compared_with` updates the internal value. This makes it
# possible to use a single Delta instance to calculate the delta over time
# of an ever increasing number.
#
# Example usage:
#
# delta = Delta.new(0)
#
# delta.compared_with(10) # => 10
# delta.compared_with(15) # => 5
# delta.compared_with(20) # => 5
class Delta
def initialize(value = 0)
@value = value
end
# new_value - The value to compare with as a Numeric.
#
# Returns a new Numeric (depending on the type of `new_value`).
def compared_with(new_value)
delta = new_value - @value
@value = new_value
delta
end
end
end
end
module Gitlab
module Metrics
# Module for instrumenting methods.
#
# This module allows instrumenting of methods without having to actually
# alter the target code (e.g. by including modules).
#
# Example usage:
#
# Gitlab::Metrics::Instrumentation.instrument_method(User, :by_login)
module Instrumentation
SERIES = 'method_calls'
def self.configure
yield self
end
# Instruments a class method.
#
# mod - The module to instrument as a Module/Class.
# name - The name of the method to instrument.
def self.instrument_method(mod, name)
instrument(:class, mod, name)
end
# Instruments an instance method.
#
# mod - The module to instrument as a Module/Class.
# name - The name of the method to instrument.
def self.instrument_instance_method(mod, name)
instrument(:instance, mod, name)
end
# Recursively instruments all subclasses of the given root module.
#
# This can be used to for example instrument all ActiveRecord models (as
# these all inherit from ActiveRecord::Base).
#
# This method can optionally take a block to pass to `instrument_methods`
# and `instrument_instance_methods`.
#
# root - The root module for which to instrument subclasses. The root
# module itself is not instrumented.
def self.instrument_class_hierarchy(root, &block)
visit = root.subclasses
until visit.empty?
klass = visit.pop
instrument_methods(klass, &block)
instrument_instance_methods(klass, &block)
klass.subclasses.each { |c| visit << c }
end
end
# Instruments all public methods of a module.
#
# This method optionally takes a block that can be used to determine if a
# method should be instrumented or not. The block is passed the receiving
# module and an UnboundMethod. If the block returns a non truthy value the
# method is not instrumented.
#
# mod - The module to instrument.
def self.instrument_methods(mod)
mod.public_methods(false).each do |name|
method = mod.method(name)
if method.owner == mod.singleton_class
if !block_given? || block_given? && yield(mod, method)
instrument_method(mod, name)
end
end
end
end
# Instruments all public instance methods of a module.
#
# See `instrument_methods` for more information.
#
# mod - The module to instrument.
def self.instrument_instance_methods(mod)
mod.public_instance_methods(false).each do |name|
method = mod.instance_method(name)
if method.owner == mod
if !block_given? || block_given? && yield(mod, method)
instrument_instance_method(mod, name)
end
end
end
end
# Instruments a method.
#
# type - The type (:class or :instance) of method to instrument.
# mod - The module containing the method.
# name - The name of the method to instrument.
def self.instrument(type, mod, name)
return unless Metrics.enabled?
name = name.to_sym
alias_name = :"_original_#{name}"
target = type == :instance ? mod : mod.singleton_class
if type == :instance
target = mod
label = "#{mod.name}##{name}"
else
target = mod.singleton_class
label = "#{mod.name}.#{name}"
end
target.class_eval <<-EOF, __FILE__, __LINE__ + 1
alias_method #{alias_name.inspect}, #{name.inspect}
def #{name}(*args, &block)
trans = Gitlab::Metrics::Instrumentation.transaction
if trans
start = Time.now
retval = __send__(#{alias_name.inspect}, *args, &block)
duration = (Time.now - start) * 1000.0
if duration >= Gitlab::Metrics.method_call_threshold
trans.add_metric(Gitlab::Metrics::Instrumentation::SERIES,
{ duration: duration },
method: #{label.inspect})
end
retval
else
__send__(#{alias_name.inspect}, *args, &block)
end
end
EOF
end
# Small layer of indirection to make it easier to stub out the current
# transaction.
def self.transaction
Transaction.current
end
end
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment