diff --git a/.gitignore b/.gitignore
index 683e6c45a2..084edd30df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ Vagrantfile
config/gitlab.yml
config/database.yml
config/initializers/omniauth.rb
+config/initializers/rack_attack.rb
config/unicorn.rb
config/resque.yml
config/aws.yml
diff --git a/.travis.yml b/.travis.yml
index d7fbbc7bbc..f0fc2fb882 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,6 @@ branches:
only:
- 'master'
rvm:
- - 1.9.3-p392
- 2.0.0
services:
- mysql
@@ -16,7 +15,4 @@ services:
before_script:
- "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml"
- - "bundle exec rake db:setup RAILS_ENV=test"
- - "bundle exec rake db:seed_fu RAILS_ENV=test"
- - "sh -e /etc/init.d/xvfb start"
-script: "bundle exec rake travis --trace"
+script: "bundle exec rake gitlab:test --trace"
diff --git a/CHANGELOG b/CHANGELOG
index e955925138..6dae128b79 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,26 @@
+v 6.2.0
+ - Public project pages are now visible to everyone (files, issues, wik, etc.)
+ THIS MEANS YOUR ISSUES AND WIKI FOR PUBLIC PROJECTS ARE PUBLICLY VISIBLE AFTER THE UPGRADE
+ - Add group access to permissions page
+ - Require current password to change one
+ - Group owner or admin can remove other group owners
+ - Remove group transfer since we have multiple owners
+ - Respect authorization in Repository API
+ - Improve UI for Project#files page
+ - Add more security specs
+ - Added search for projects by name to api (Izaak Alpert)
+ - Make default user theme configurable (Izaak Alpert)
+ - Update logic for validates_merge_request for tree of MR (Andrew Kumanyaev)
+ - Rake tasks for web hooks management (Jonhnny Weslley)
+ - Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov)
+ - API: Remove group
+ - API: Remove project
+ - Avatar upload on profile page with a maximum of 200KB (Steven Thonus)
+ - Store the sessions in Redis instead of the cookie store
+ - Fixed relative links in markdown
+ - User must confirm his email if signup enabled
+ - User must confirm changed email
+
v 6.1.0
- Project specific IDs for issues, mr, milestones
Above items will get a new id and for example all bookmarked issue urls will change.
diff --git a/Gemfile b/Gemfile
index 7c8f8d1bad..864172cd78 100644
--- a/Gemfile
+++ b/Gemfile
@@ -16,6 +16,7 @@ gem "pg", group: :postgres
# Auth
gem "devise", '~> 2.2'
+gem "devise-async"
gem 'omniauth', "~> 1.1.3"
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
@@ -23,7 +24,7 @@ gem 'omniauth-github'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem "gitlab_git", '2.3.1'
+gem "gitlab_git", "~> 3.0.0.rc2"
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 1.0.1', require: 'grack'
@@ -112,6 +113,9 @@ gem 'tinder', '~> 1.9.2'
# HipChat integration
gem "hipchat", "~> 0.9.0"
+# Flowdock integration
+gem "gitlab-flowdock-git-hook", "~> 0.4.2"
+
# d3
gem "d3_rails", "~> 3.1.4"
@@ -121,6 +125,9 @@ gem "underscore-rails", "~> 1.4.4"
# Sanitize user input
gem "sanitize"
+# Protect against bruteforcing
+gem "rack-attack"
+
group :assets do
gem "sass-rails"
gem "coffee-rails"
@@ -143,10 +150,11 @@ group :assets do
end
group :development do
- gem "annotate", git: "https://github.com/ctran/annotate_models.git"
+ gem "annotate", "~> 2.6.0.beta2"
gem "letter_opener"
gem 'quiet_assets', '~> 1.0.1'
gem 'rack-mini-profiler'
+
# Better errors handler
gem 'better_errors'
gem 'binding_of_caller'
diff --git a/Gemfile.lock b/Gemfile.lock
index fbf72ca42a..35db180a4e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,11 +1,3 @@
-GIT
- remote: https://github.com/ctran/annotate_models.git
- revision: 18a4e2eb77c8f3ef695b563e4a7ca45dfede819b
- specs:
- annotate (2.6.0.beta2)
- activerecord (>= 2.3.0)
- rake (>= 0.8.7)
-
GEM
remote: https://rubygems.org/
specs:
@@ -39,17 +31,20 @@ GEM
acts-as-taggable-on (2.4.1)
rails (>= 3, < 5)
addressable (2.3.4)
+ annotate (2.6.0.beta2)
+ activerecord (>= 2.3.0)
+ rake (>= 0.8.7)
arel (3.0.2)
asciidoctor (0.1.3)
- awesome_print (1.1.0)
+ awesome_print (1.2.0)
backports (3.3.2)
bcrypt-ruby (3.1.1)
- better_errors (0.9.0)
+ better_errors (1.0.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
- bootstrap-sass (2.3.2.0)
+ bootstrap-sass (2.3.2.2)
sass (~> 3.2)
builder (3.0.4)
capybara (2.1.0)
@@ -73,7 +68,7 @@ GEM
sass-rails (>= 3.2)
chunky_png (1.2.8)
cliver (0.2.1)
- code_analyzer (0.3.2)
+ code_analyzer (0.4.3)
sexp_processor
coderay (1.0.9)
coffee-rails (3.2.2)
@@ -92,11 +87,11 @@ GEM
compass-rails (1.0.3)
compass (>= 0.12.2, < 0.14)
connection_pool (1.1.0)
- coveralls (0.6.7)
- colorize
+ coveralls (0.7.0)
multi_json (~> 1.3)
rest-client
simplecov (>= 0.7)
+ term-ansicolor
thor
crack (0.4.0)
safe_yaml (~> 0.9.0)
@@ -111,6 +106,8 @@ GEM
orm_adapter (~> 0.1)
railties (~> 3.1)
warden (~> 1.2.1)
+ devise-async (0.8.0)
+ devise (>= 2.2, < 3.2)
diff-lcs (1.2.4)
dotenv (0.8.0)
email_spec (1.4.0)
@@ -133,7 +130,7 @@ GEM
multipart-post (~> 1.1)
faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9)
- ffaker (1.16.1)
+ ffaker (1.18.0)
ffi (1.9.0)
fog (1.3.1)
builder
@@ -145,7 +142,7 @@ GEM
net-ssh (>= 2.1.3)
nokogiri (~> 1.5.0)
ruby-hmac
- font-awesome-rails (3.2.1.2)
+ font-awesome-rails (3.2.1.3)
railties (>= 3.2, < 5.0)
foreman (0.63.0)
dotenv (>= 0.7)
@@ -161,6 +158,9 @@ GEM
pygments.rb (>= 0.2.13)
github-markdown (0.5.3)
github-markup (0.7.5)
+ gitlab-flowdock-git-hook (0.4.2.2)
+ gitlab-grit (>= 2.4.1)
+ multi_json
gitlab-gollum-lib (1.0.1)
github-markdown (~> 0.5.3)
github-markup (>= 0.7.5, < 1.0.0)
@@ -171,7 +171,7 @@ GEM
stringex (~> 1.5.1)
gitlab-grack (1.0.1)
rack (~> 1.4.1)
- gitlab-grit (2.6.0)
+ gitlab-grit (2.6.1)
charlock_holmes (~> 0.6.9)
diff-lcs (~> 1.1)
mime-types (~> 1.15)
@@ -179,10 +179,10 @@ GEM
gitlab-pygments.rb (0.3.2)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.1.0)
- gitlab_git (2.3.1)
+ gitlab_git (3.0.0.rc2)
activesupport (~> 3.2.13)
github-linguist (~> 2.3.4)
- gitlab-grit (~> 2.6.0)
+ gitlab-grit (~> 2.6.1)
gitlab_meta (6.0)
gitlab_omniauth-ldap (1.0.3)
net-ldap (~> 0.3.1)
@@ -278,7 +278,7 @@ GEM
minitest (4.7.4)
modernizr (2.6.2)
sprockets (~> 2.0)
- multi_json (1.7.9)
+ multi_json (1.8.0)
multi_xml (0.5.4)
multipart-post (1.2.0)
mysql2 (0.3.11)
@@ -334,9 +334,11 @@ GEM
rack (1.4.5)
rack-accept (0.4.5)
rack (>= 0.4)
+ rack-attack (2.2.1)
+ rack
rack-cache (1.2)
rack (>= 0.4)
- rack-mini-profiler (0.1.26)
+ rack-mini-profiler (0.1.31)
rack (>= 1.1.3)
rack-mount (0.8.3)
rack (>= 1.0.0)
@@ -357,13 +359,14 @@ GEM
rails-dev-tweaks (0.6.1)
actionpack (~> 3.1)
railties (~> 3.1)
- rails_best_practices (1.13.8)
+ rails_best_practices (1.14.4)
activesupport
awesome_print
- code_analyzer
+ code_analyzer (>= 0.4.3)
colored
erubis
i18n
+ require_all
ruby-progressbar
railties (3.2.13)
actionpack (= 3.2.13)
@@ -403,6 +406,7 @@ GEM
redis-store (1.1.4)
redis (>= 2.2)
ref (1.0.5)
+ require_all (1.3.1)
rest-client (1.6.7)
mime-types (>= 1.16)
rspec (2.13.0)
@@ -427,7 +431,7 @@ GEM
safe_yaml (0.9.3)
sanitize (2.0.3)
nokogiri (>= 1.4.4, < 1.6)
- sass (3.2.9)
+ sass (3.2.11)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
@@ -447,7 +451,7 @@ GEM
rubyzip
websocket (~> 1.0.4)
settingslogic (2.0.9)
- sexp_processor (4.2.1)
+ sexp_processor (4.3.0)
shoulda-matchers (2.1.0)
activesupport (>= 3.0.0)
sidekiq (2.14.0)
@@ -487,7 +491,9 @@ GEM
state_machine (1.2.0)
stringex (1.5.1)
temple (0.6.5)
- test_after_commit (0.2.0)
+ term-ansicolor (1.2.2)
+ tins (~> 0.8)
+ test_after_commit (0.2.1)
therubyracer (0.11.4)
libv8 (~> 3.11.8.12)
ref
@@ -507,6 +513,7 @@ GEM
mime-types (~> 1.19)
multi_json (~> 1.5)
twitter-stream (~> 0.1)
+ tins (0.11.0)
treetop (1.4.14)
polyglot
polyglot (>= 0.3.1)
@@ -544,7 +551,7 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on
- annotate!
+ annotate (~> 2.6.0.beta2)
asciidoctor
awesome_print
better_errors
@@ -559,6 +566,7 @@ DEPENDENCIES
d3_rails (~> 3.1.4)
database_cleaner
devise (~> 2.2)
+ devise-async
email_spec
enumerize
factory_girl_rails
@@ -569,10 +577,11 @@ DEPENDENCIES
gemoji (~> 1.2.1)
github-linguist
github-markup (~> 0.7.4)
+ gitlab-flowdock-git-hook (~> 0.4.2)
gitlab-gollum-lib (~> 1.0.1)
gitlab-grack (~> 1.0.1)
gitlab-pygments.rb (~> 0.3.2)
- gitlab_git (= 2.3.1)
+ gitlab_git (~> 3.0.0.rc2)
gitlab_meta (= 6.0)
gitlab_omniauth-ldap (= 1.0.3)
gon
@@ -604,6 +613,7 @@ DEPENDENCIES
poltergeist (~> 1.4.1)
pry
quiet_assets (~> 1.0.1)
+ rack-attack
rack-mini-profiler
rails (= 3.2.13)
rails-dev-tweaks
diff --git a/README.md b/README.md
index ce0f4a8a1c..b1c109950a 100644
--- a/README.md
+++ b/README.md
@@ -127,14 +127,17 @@ or start each component separately
### GitLab interfaces
-* [GitLab API](doc/api/README.md)
+* [GitLab API doc](doc/api/README.md) or see the [GitLab API website](http://api.gitlab.org/)
-* [Rake tasks](doc/raketasks)
+* [Rake tasks](doc/raketasks) including a [backup and restore procedure](doc/raketasks/backup_restore.md)
* [Directory structure](doc/install/structure.md)
-* [Databases](doc/install/databases.md)
+* [Database installation](doc/install/databases.md)
+* [Markdown specification](doc/markdown/markdown.md)
+
+* [Security guide](doc/security/rack_attack.md) to throttle abusive requests
### Getting help
@@ -157,10 +160,8 @@ or start each component separately
### Getting in touch
-* [Core team](https://github.com/gitlabhq?tab=members)
+* [Core team](http://gitlab.org/team/)
-* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)
+* [Contributors](http://contributors.gitlab.org/)
-* [Leader](https://github.com/randx)
-
-* [Contact page](http://gitlab.org/contact/)
+* [Community](http://gitlab.org/community/)
diff --git a/VERSION b/VERSION
index bad4faad51..f8a9abe525 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-6.1.0-ee
+6.2.0-ee
diff --git a/app/assets/images/login-logo.png b/app/assets/images/login-logo.png
deleted file mode 100644
index a61c41303c..0000000000
Binary files a/app/assets/images/login-logo.png and /dev/null differ
diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee
index e7974611cb..744f3086d5 100644
--- a/app/assets/javascripts/profile.js.coffee
+++ b/app/assets/javascripts/profile.js.coffee
@@ -16,3 +16,13 @@ $ ->
$('.update-notifications').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton()
+
+
+ $('.js-choose-user-avatar-button').bind "click", ->
+ form = $(this).closest("form")
+ form.find(".js-user-avatar-input").click()
+
+ $('.js-user-avatar-input').bind "change", ->
+ form = $(this).closest("form")
+ filename = $(this).val().replace(/^.*[\\\/]/, '')
+ form.find(".js-avatar-filename").text(filename)
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 8286ca2f0c..327667fede 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -1,9 +1,11 @@
$ ->
userFormatResult = (user) ->
- avatar = gon.gravatar_url
- avatar = avatar.replace('%{hash}', md5(user.email))
- avatar = avatar.replace('%{size}', '24')
-
+ if user.avatar
+ avatar = user.avatar.url
+ else
+ avatar = gon.gravatar_url
+ avatar = avatar.replace('%{hash}', md5(user.email))
+ avatar = avatar.replace('%{size}', '24')
markup = "
"
markup += "
"
markup += "
" + user.name + "
"
diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss
index 5b1088a051..99a46d8541 100644
--- a/app/assets/stylesheets/common.scss
+++ b/app/assets/stylesheets/common.scss
@@ -140,27 +140,6 @@ p.time {
border-bottom: 2px solid #F90;
}
-.status_info {
- font-size: 14px;
- padding: 5px 15px;
- line-height: 26px;
- text-align: center;
- float: right;
- position: relative;
- top: -5px;
- @include border-radius(4px);
-
- &.success {
- background: #4A4;
- color: #FFF;
- }
-
- &.error {
- background: #DA4E49;
- color: #FFF;
- }
-}
-
.thin_area{
height: 150px;
}
@@ -270,27 +249,6 @@ li.note {
}
}
-.oauth_select_holder {
- padding: 20px;
- img {
- padding: 5px;
- margin-right: 10px;
- }
- .active {
- img {
- border: 1px solid #ccc;
- background: $hover;
- @include border-radius(5px);
- }
- }
-}
-
-.btn-build-token {
- float: left;
- padding: 6px 20px;
- margin-right: 12px;
-}
-
.gitlab-promo {
a {
color: #aaa;
@@ -386,3 +344,8 @@ table {
width: 50px;
min-height: 100px;
}
+
+.navbar-gitlab .navbar-inner .nav > li .btn-sign-in {
+ @extend .btn-new;
+ padding: 5px 15px;
+}
diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss
index 1b24683fd9..faf36b702c 100644
--- a/app/assets/stylesheets/gitlab_bootstrap.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap.scss
@@ -62,6 +62,5 @@ $baseLineHeight: 18px !default;
@import "gitlab_bootstrap/buttons.scss";
@import "gitlab_bootstrap/blocks.scss";
@import "gitlab_bootstrap/files.scss";
-@import "gitlab_bootstrap/tables.scss";
@import "gitlab_bootstrap/lists.scss";
@import "gitlab_bootstrap/forms.scss";
diff --git a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss
index c23970c13e..98fbbd64e9 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss
@@ -19,4 +19,5 @@
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
&.s60 { width: 60px; height: 60px; margin-right: 12px; }
&.s90 { width: 90px; height: 90px; margin-right: 15px; }
+ &.s160 { width: 160px; height: 160px; margin-right: 20px; }
}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss
index fcf1159cd3..8235fb6010 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss
@@ -12,7 +12,7 @@
.ui-box {
background: #FFF;
margin-bottom: 20px;
- border: 1px solid #CCC;
+ border: 1px solid #DDD;
word-wrap: break-word;
&.small-box {
@@ -32,8 +32,12 @@
}
&.ui-box-show {
+ text-shadow: 0 1px 1px #fff;
+ color: #666;
margin:20px 0;
background: #FFF;
+ box-shadow: inset 0 1px 0 #fff, 0 1px 5px #f1f1f1;
+ @include linear-gradient(#fafafa, #f1f1f1);
.control-group {
margin-bottom: 0;
@@ -69,7 +73,6 @@
.ui-box-head {
.box-title {
- color: $style_color;
font-size: 18px;
font-weight: normal;
line-height: 28px;
diff --git a/app/assets/stylesheets/gitlab_bootstrap/common.scss b/app/assets/stylesheets/gitlab_bootstrap/common.scss
index bc6c786da5..0d9adcf6d9 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/common.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/common.scss
@@ -87,3 +87,25 @@ pre.well-pre {
font-weight: bold;
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
}
+
+/** Big Labels **/
+.state-label {
+ font-size: 14px;
+ padding: 5px 15px;
+ text-align: center;
+ float: right;
+ position: relative;
+ top: -5px;
+ @include border-radius(4px);
+ text-shadow: none;
+
+ &.state-label-green {
+ background: #4A4;
+ color: #FFF;
+ }
+
+ &.state-label-red {
+ background: #DA4E49;
+ color: #FFF;
+ }
+}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/files.scss b/app/assets/stylesheets/gitlab_bootstrap/files.scss
index a0d6682eb8..a286e530cd 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/files.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/files.scss
@@ -6,6 +6,10 @@
border: 1px solid #CCC;
margin-bottom: 1em;
+ table {
+ @extend .table;
+ }
+
.file-title {
border-bottom: 1px solid #bbb;
@include bg-dark-gray-gradient;
@@ -65,6 +69,12 @@
}
+ &.blob-no-preview {
+ background: #eee;
+ text-shadow: 0 1px 2px #FFF;
+ padding: 100px 0;
+ }
+
/**
* Blame file
*/
diff --git a/app/assets/stylesheets/gitlab_bootstrap/mixins.scss b/app/assets/stylesheets/gitlab_bootstrap/mixins.scss
index e7daf8f43b..8b975a12cf 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/mixins.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/mixins.scss
@@ -95,6 +95,14 @@
font-size: 14px;
line-height: 1.5;
}
+
+ table {
+ @extend .table;
+ @extend .table-bordered;
+ th {
+ background: #EEE;
+ }
+ }
}
@mixin page-title {
diff --git a/app/assets/stylesheets/gitlab_bootstrap/tables.scss b/app/assets/stylesheets/gitlab_bootstrap/tables.scss
deleted file mode 100644
index a1a5139cda..0000000000
--- a/app/assets/stylesheets/gitlab_bootstrap/tables.scss
+++ /dev/null
@@ -1,68 +0,0 @@
-table {
- @extend .table;
- @extend .table-striped;
- border: 1px solid #CCC;
- width: 100%;
-
- &.low {
- td {
- line-height: 18px;
- }
- }
-
- &.headless {
- tr:first-child td{
- border-top: 1px solid #CCC;
- }
- }
-
- th {
- font-weight: bold;
- vertical-align: middle;
- border-bottom: 1px solid #CCC;
- text-shadow: 0 1px 1px #fff;
- @include bg-dark-gray-gradient;
-
- ul.nav {
- text-shadow: none;
- margin: 0;
- }
- }
-
- th, td {
- padding: 10px;
- line-height: 18px;
- text-align: left;
- }
-
- td {
- border-color: #f1f1f1;
- line-height: 28px;
-
- .s16 {
- margin-top: 5px;
- margin-right: 5px;
- }
-
- &:first-child {
- border-left: 1px solid #CCC;
- }
-
- &:last-child {
- border-right: 1px solid #CCC;
- }
- }
-
- &.bordered {
- @extend .table-bordered;
- }
-
- &.lite {
- border: none;
- box-shadow: none;
- tr, td {
- border: none;
- background:none !important;
- }
- }
-}
diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss
index b0cf8b1626..45a83cb608 100644
--- a/app/assets/stylesheets/sections/commits.scss
+++ b/app/assets/stylesheets/sections/commits.scss
@@ -67,6 +67,7 @@
}
table {
+ width: 100%;
font-family: $monospace_font;
border: none;
margin: 0px;
@@ -418,7 +419,6 @@
.commit-title {
margin: 0;
font-size: 20px;
- font-weight: bold;
}
.commit-description {
diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss
index 61331cee41..04df7656b0 100644
--- a/app/assets/stylesheets/sections/dashboard.scss
+++ b/app/assets/stylesheets/sections/dashboard.scss
@@ -60,7 +60,7 @@
}
a {
- border-color: #CCC !important;
+ border-color: #DDD !important;
}
}
}
@@ -85,12 +85,18 @@
color: #666;
}
- .last-activity, .owner-info {
+ .last-activity {
color: #AAA;
display: block;
margin-top: 5px;
- .date, .owner {
+ .date {
color: #777;
}
}
}
+
+.group-row {
+ .arrow {
+ padding: 2px 5px;
+ }
+}
diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss
index fd5c80f988..39b2ad7a09 100644
--- a/app/assets/stylesheets/sections/events.scss
+++ b/app/assets/stylesheets/sections/events.scss
@@ -46,8 +46,8 @@
border-bottom: 1px solid #eee;
.event-title {
color: #333;
- font-weight: bold;
- font-size: 14px;
+ font-weight: normal;
+ font-size: 15px;
.author_name {
color: #333;
}
diff --git a/app/assets/stylesheets/sections/login.scss b/app/assets/stylesheets/sections/login.scss
index 8d9fd037bc..33bef59c08 100644
--- a/app/assets/stylesheets/sections/login.scss
+++ b/app/assets/stylesheets/sections/login.scss
@@ -1,7 +1,8 @@
/* Login Page */
body.login-page{
- background: #474D57;
- .container .content { padding-top: 4%; }
+ .container > .content {
+ padding-top: 20px;
+ }
}
.login-box{
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss
index bae1ac3aa9..94b9ca3b18 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/sections/notes.scss
@@ -135,11 +135,15 @@ ul.notes {
background-color: $white;
border-width: 1px 0;
padding-top: 0;
+
+ li {
+ padding: 5px;
+ }
}
}
.reply-btn {
- margin-top: 8px;
+ margin: 5px;
}
}
diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss
index 5c7516ce6f..21f4ed0577 100644
--- a/app/assets/stylesheets/sections/profile.scss
+++ b/app/assets/stylesheets/sections/profile.scss
@@ -4,3 +4,41 @@
margin-bottom: 0;
}
}
+
+.account-page {
+ fieldset {
+ margin-bottom: 15px;
+ border-bottom: 1px dashed #ddd;
+ padding-bottom: 15px;
+
+ &:last-child {
+ border: none;
+ }
+
+ legend {
+ border: none;
+ margin: 0;
+ }
+ }
+}
+
+.oauth_select_holder {
+ img {
+ padding: 2px;
+ margin-right: 10px;
+ }
+ .active {
+ img {
+ border: 1px solid #4BD;
+ background: $hover;
+ @include border-radius(5px);
+ }
+ }
+}
+
+.btn-build-token {
+ float: left;
+ padding: 6px 20px;
+ margin-right: 12px;
+}
+
diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss
index f2707f6237..0491b68db5 100644
--- a/app/assets/stylesheets/sections/projects.scss
+++ b/app/assets/stylesheets/sections/projects.scss
@@ -79,21 +79,6 @@ ul.nav.nav-projects-tabs {
margin: 0px;
}
-.public-projects {
- li {
- .project-title {
- font-size: 14px;
- line-height: 2;
- font-weight: normal;
- }
-
- .description {
- margin-left: 15px;
- color: #aaa;
- }
- }
-}
-
.my-projects {
li {
.project-title {
@@ -110,7 +95,6 @@ ul.nav.nav-projects-tabs {
}
}
-
.public-clone {
background: #333;
color: #f5f5f5;
@@ -123,3 +107,11 @@ ul.nav.nav-projects-tabs {
position: relative;
top: -5px;
}
+
+.public-projects .repo-info {
+ color: #777;
+
+ a {
+ color: #777;
+ }
+}
diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/sections/tree.scss
index 850ed831df..96f2a5711f 100644
--- a/app/assets/stylesheets/sections/tree.scss
+++ b/app/assets/stylesheets/sections/tree.scss
@@ -13,13 +13,21 @@
}
.tree-table {
+ @extend .table;
@include border-radius(0);
- .tree-item {
- td {
+
+ tr {
+ td, th {
padding: 8px 10px;
- strong {
- font-weight: normal;
- }
+ line-height: 20px;
+ }
+ th {
+ font-weight: normal;
+ font-size: 15px;
+ border-bottom: 1px solid #CCC;
+ }
+ td {
+ border-color: #F1F1F1;
}
&:hover {
td {
@@ -29,12 +37,11 @@
}
cursor: pointer;
}
-
&.selected {
td {
- background: $hover;
- border-top: 1px solid #ADF;
- border-bottom: 1px solid #ADF;
+ background: #f5f5f5;
+ border-top: 1px solid #EEE;
+ border-bottom: 1px solid #EEE;
}
}
}
@@ -56,24 +63,8 @@
}
}
- .tree-table {
- th .btn {
- margin: -2px -1px;
- padding: 2px 10px;
- }
- td {
- line-height: 20px;
- background: #fafafa;
- }
- }
-
.tree_author {
padding-right: 8px;
-
- img.avatar {
- margin-top: 0;
- width: 16px;
- }
}
.tree_commit {
diff --git a/app/contexts/search_context.rb b/app/contexts/search_context.rb
index 0817d52cd0..48def0784f 100644
--- a/app/contexts/search_context.rb
+++ b/app/contexts/search_context.rb
@@ -13,7 +13,7 @@ class SearchContext
projects = Project.where(id: project_ids)
result[:projects] = projects.search(query).limit(20)
- # Search inside singe project
+ # Search inside single project
project = projects.first if projects.length == 1
if params[:search_code].present?
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index a2201f732e..89b395786b 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -20,9 +20,9 @@ class Admin::GroupsController < Admin::ApplicationController
def create
@group = Group.new(params[:group])
@group.path = @group.name.dup.parameterize if @group.name
- @group.owner = current_user
if @group.save
+ @group.add_owner(current_user)
redirect_to [:admin, @group], notice: 'Group was successfully created.'
else
render "new"
@@ -30,14 +30,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def update
- group_params = params[:group].dup
- owner_id =group_params.delete(:owner_id)
-
- if owner_id
- @group.change_owner(User.find(owner_id))
- end
-
- if @group.update_attributes(group_params)
+ if @group.update_attributes(params[:group])
redirect_to [:admin, @group], notice: 'Group was successfully updated.'
else
render "edit"
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 70bbe30656..c9875b9632 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -47,6 +47,8 @@ class Admin::UsersController < Admin::ApplicationController
@user = User.build_user(params[:user].merge(opts), as: :admin)
@user.admin = (admin && admin.to_i > 0)
@user.created_by_id = current_user.id
+ @user.generate_password
+ @user.confirm!
respond_to do |format|
if @user.save
@@ -71,6 +73,7 @@ class Admin::UsersController < Admin::ApplicationController
respond_to do |format|
if user.update_attributes(params[:user], as: :admin)
+ user.confirm!
format.html { redirect_to [:admin, user], notice: 'User was successfully updated.' }
format.json { head :ok }
else
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index da84e1b0d3..ccab2fc5c6 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base
before_filter :authenticate_user!
before_filter :reject_blocked!
before_filter :check_password_expiration
- before_filter :set_current_user_for_thread
+ around_filter :set_current_user_for_thread
before_filter :add_abilities
before_filter :ldap_security_check
before_filter :dev_tools if Rails.env == 'development'
@@ -51,6 +51,11 @@ class ApplicationController < ActionController::Base
def set_current_user_for_thread
Thread.current[:current_user] = current_user
+ begin
+ yield
+ ensure
+ Thread.current[:current_user] = nil
+ end
end
def abilities
@@ -64,6 +69,15 @@ class ApplicationController < ActionController::Base
def project
id = params[:project_id] || params[:id]
+ # Redirect from
+ # localhost/group/project.git
+ # to
+ # localhost/group/project
+ #
+ if id =~ /\.git\Z/
+ redirect_to request.original_url.gsub(/\.git\Z/, '') and return
+ end
+
@project = Project.find_with_namespace(id)
if @project and can?(current_user, :read_project, @project)
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 33b2a5cd7e..ac31938443 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -5,10 +5,14 @@ class DashboardController < ApplicationController
before_filter :event_filter, only: :show
def show
+ # Fetch only 30 projects.
+ # If user needs more - point to Dashboard#projects page
+ @projects_limit = 30
+
@groups = current_user.authorized_groups.sort_by(&:human_name)
@has_authorized_projects = @projects.count > 0
@projects_count = @projects.count
- @projects = @projects.limit(20)
+ @projects = @projects.limit(@projects_limit)
@events = Event.in_projects(current_user.authorized_projects.pluck(:id))
@events = @event_filter.apply_filter(@events)
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 97bd7d6f29..61361da3df 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -21,9 +21,9 @@ class GroupsController < ApplicationController
def create
@group = Group.new(params[:group])
@group.path = @group.name.dup.parameterize if @group.name
- @group.owner = current_user
if @group.save
+ @group.add_owner(current_user)
redirect_to @group, notice: 'Group was successfully created.'
else
render action: "new"
@@ -75,15 +75,7 @@ class GroupsController < ApplicationController
end
def update
- group_params = params[:group].dup
- owner_id = group_params.delete(:owner_id)
-
- if owner_id
- @group.owner = User.find(owner_id)
- @group.save
- end
-
- if @group.update_attributes(group_params)
+ if @group.update_attributes(params[:group])
redirect_to @group, notice: 'Group was successfully updated.'
else
render action: "edit"
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
new file mode 100644
index 0000000000..fe121691a1
--- /dev/null
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -0,0 +1,7 @@
+class Profiles::AccountsController < ApplicationController
+ layout "profile"
+
+ def show
+ @user = current_user
+ end
+end
diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/profiles/groups_controller.rb
index e276d07b48..378ff6bcf3 100644
--- a/app/controllers/profiles/groups_controller.rb
+++ b/app/controllers/profiles/groups_controller.rb
@@ -8,8 +8,8 @@ class Profiles::GroupsController < ApplicationController
def leave
@users_group = group.users_groups.where(user_id: current_user.id).first
- if group.owner == current_user
- redirect_to(profile_groups_path, alert: "You can't leave group. You must transfer it to another owner before leaving.")
+ if group.last_owner?(current_user)
+ redirect_to(profile_groups_path, alert: "You can't leave group. You must add at least one more owner to it.")
else
@users_group.destroy
redirect_to(profile_groups_path, info: "You left #{group.name} group.")
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index 432899f857..df6954554e 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -1,10 +1,11 @@
class Profiles::PasswordsController < ApplicationController
- layout 'navless'
+ layout :determine_layout
- skip_before_filter :check_password_expiration
+ skip_before_filter :check_password_expiration, only: [:new, :create]
before_filter :set_user
before_filter :set_title
+ before_filter :authorize_change_password!
def new
end
@@ -26,6 +27,32 @@ class Profiles::PasswordsController < ApplicationController
end
end
+ def edit
+ end
+
+ def update
+ password_attributes = params[:user].select do |key, value|
+ %w(password password_confirmation).include?(key.to_s)
+ end
+
+ unless @user.valid_password?(params[:user][:current_password])
+ redirect_to edit_profile_password_path, alert: 'You must provide a valid current password'
+ return
+ end
+
+ if @user.update_attributes(password_attributes)
+ flash[:notice] = "Password was successfully updated. Please login with it"
+ redirect_to new_user_session_path
+ else
+ render 'edit'
+ end
+ end
+
+ def reset
+ current_user.send_reset_password_instructions
+ redirect_to edit_profile_password_path, notice: 'We sent you an email with reset password instructions'
+ end
+
private
def set_user
@@ -35,4 +62,16 @@ class Profiles::PasswordsController < ApplicationController
def set_title
@title = "New password"
end
+
+ def determine_layout
+ if [:new, :create].include?(action_name.to_sym)
+ 'navless'
+ else
+ 'profile'
+ end
+ end
+
+ def authorize_change_password!
+ return render_404 if @user.ldap_user?
+ end
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 6fa635d0e3..47cfc5e63f 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -2,7 +2,6 @@ class ProfilesController < ApplicationController
include ActionView::Helpers::SanitizeHelper
before_filter :user
- before_filter :authorize_change_password!, only: :update_password
before_filter :authorize_change_username!, only: :update_username
layout 'profile'
@@ -13,9 +12,6 @@ class ProfilesController < ApplicationController
def design
end
- def account
- end
-
def update
if @user.update_attributes(params[:user])
flash[:notice] = "Profile was successfully updated"
@@ -29,26 +25,12 @@ class ProfilesController < ApplicationController
end
end
- def token
- end
-
- def update_password
- params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"}
-
- if @user.update_attributes(params[:user])
- flash[:notice] = "Password was successfully updated. Please login with it"
- redirect_to new_user_session_path
- else
- render 'account'
- end
- end
-
def reset_private_token
if current_user.reset_authentication_token!
flash[:notice] = "Token was successfully updated"
end
- redirect_to account_profile_path
+ redirect_to profile_account_path
end
def history
@@ -69,10 +51,6 @@ class ProfilesController < ApplicationController
@user = current_user
end
- def authorize_change_password!
- return render_404 if @user.ldap_user?
- end
-
def authorize_change_username!
return render_404 unless @user.can_change_username?
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 1f2a75175c..8fd4565f36 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -1,5 +1,26 @@
class Projects::ApplicationController < ApplicationController
before_filter :project
before_filter :repository
- layout 'projects'
+ layout :determine_layout
+
+ def authenticate_user!
+ # Restrict access to Projects area only
+ # for non-signed users
+ if !current_user
+ id = params[:project_id] || params[:id]
+ @project = Project.find_with_namespace(id)
+
+ return if @project && @project.public
+ end
+
+ super
+ end
+
+ def determine_layout
+ if current_user
+ 'projects'
+ else
+ 'public_projects'
+ end
+ end
end
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index e58b450720..a3c4130167 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -8,7 +8,7 @@ class Projects::BlameController < Projects::ApplicationController
before_filter :require_non_empty_project
def show
- @blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
+ @blob = @repository.blob_at(@commit.id, @path)
@blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path)
end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index b1329c01ce..ba466251b2 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -8,6 +8,8 @@ class Projects::BlobController < Projects::ApplicationController
before_filter :require_non_empty_project
def show
- @blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
+ @blob = @repository.blob_at(@commit.id, @path)
+
+ not_found! unless @blob
end
end
diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb
index 3b945fc712..5d05c585ec 100644
--- a/app/controllers/projects/edit_tree_controller.rb
+++ b/app/controllers/projects/edit_tree_controller.rb
@@ -10,7 +10,7 @@ class Projects::EditTreeController < Projects::ApplicationController
before_filter :edit_requirements, only: [:show, :update]
def show
- @last_commit = Gitlab::Git::Commit.last_for_path(@project.repository, @ref, @path).sha
+ @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end
def update
@@ -32,9 +32,9 @@ class Projects::EditTreeController < Projects::ApplicationController
private
def edit_requirements
- @blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
+ @blob = @repository.blob_at(@commit.id, @path)
- unless @blob.exists? && @blob.text?
+ unless @blob
redirect_to project_blob_path(@project, @id), notice: "You can only edit text files"
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 3367ddb5d1..1a94dbab5e 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -1,7 +1,6 @@
class Projects::HooksController < Projects::ApplicationController
# Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_admin_project!, only: [:new, :create, :destroy]
+ before_filter :authorize_admin_project!
respond_to :html
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 3bc50b0418..0cc09caf1d 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -157,12 +157,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def validates_merge_request
- # Show git not found page if target branch doesn't exist
- return invalid_mr unless @merge_request.target_project.repository.branch_names.include?(@merge_request.target_branch)
+ # Show git not found page
+ # if there is no saved commits between source & target branch
+ if @merge_request.commits.blank?
+ # and if source target doesn't exist
+ return invalid_mr unless @merge_request.target_project.repository.branch_names.include?(@merge_request.target_branch)
- # Show git not found page if source branch doesn't exist
- # and there is no saved commits between source & target branch
- return invalid_mr if !@merge_request.source_project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank?
+ # or if source branch doesn't exist
+ return invalid_mr unless @merge_request.source_project.repository.branch_names.include?(@merge_request.source_branch)
+ end
end
def define_show_vars
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 39cd579cce..3e88656cdf 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -14,7 +14,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestones = case params[:f]
when 'all'; @project.milestones.order("state, due_date DESC")
when 'closed'; @project.milestones.closed.order("due_date DESC")
- else @project.milestones.active.order("due_date DESC")
+ else @project.milestones.active.order("due_date ASC")
end
@milestones = @milestones.includes(:project)
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 0c23d411f4..18ace028b0 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -8,9 +8,9 @@ class Projects::RawController < Projects::ApplicationController
before_filter :require_non_empty_project
def show
- @blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
+ @blob = @repository.blob_at(@commit.id, @path)
- if @blob.exists?
+ if @blob
type = if @blob.mime_type =~ /html|javascript/
'text/plain; charset=utf-8'
else
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index e5c090e1f4..16621c0371 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -24,13 +24,14 @@ class Projects::RefsController < Projects::ApplicationController
format.js do
@ref = params[:ref]
define_tree_vars
+ tree
render "tree"
end
end
end
def logs_tree
- contents = @tree.entries
+ contents = tree.entries
@logs = contents.map do |content|
file = params[:path] ? File.join(params[:path], content.name) : content.name
last_commit = @repo.commits(@commit.id, file, 1).last
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 59063103ec..dd0c1a5708 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -14,8 +14,6 @@ class Projects::SnippetsController < Projects::ApplicationController
# Allow destroy snippet
before_filter :authorize_admin_project_snippet!, only: [:destroy]
- layout 'projects'
-
respond_to :html
def index
diff --git a/app/controllers/projects/team_members_controller.rb b/app/controllers/projects/team_members_controller.rb
index 6fee770cae..b4b318fa59 100644
--- a/app/controllers/projects/team_members_controller.rb
+++ b/app/controllers/projects/team_members_controller.rb
@@ -1,7 +1,6 @@
class Projects::TeamMembersController < Projects::ApplicationController
# Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_admin_project!, except: [:index, :show]
+ before_filter :authorize_admin_project!
layout "project_settings"
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 5d543f3566..1150efbea9 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -8,6 +8,8 @@ class Projects::TreeController < Projects::ApplicationController
before_filter :require_non_empty_project
def show
+ return not_found! if tree.entries.empty?
+
respond_to do |format|
format.html
# Disable cache so browser history works
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 23b54ec44a..7264128691 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,6 +1,7 @@
-class ProjectsController < Projects::ApplicationController
- skip_before_filter :project, only: [:new, :create]
- skip_before_filter :repository, only: [:new, :create]
+class ProjectsController < ApplicationController
+ skip_before_filter :authenticate_user!, only: [:show]
+ before_filter :project, except: [:new, :create]
+ before_filter :repository, except: [:new, :create]
# Authorize
before_filter :authorize_read_project!, except: [:index, :new, :create]
@@ -54,8 +55,9 @@ class ProjectsController < Projects::ApplicationController
end
def show
- limit = (params[:limit] || 20).to_i
+ return authenticate_user! unless @project.public || current_user
+ limit = (params[:limit] || 20).to_i
@events = @project.events.recent
@events = event_filter.apply_filter(@events)
@events = @events.limit(limit).offset(params[:offset] || 0)
@@ -67,10 +69,12 @@ class ProjectsController < Projects::ApplicationController
respond_to do |format|
format.html do
if @project.empty_repo?
- render "projects/empty"
+ render "projects/empty", layout: user_layout
else
- @last_push = current_user.recent_push(@project.id)
- render :show
+ if current_user
+ @last_push = current_user.recent_push(@project.id)
+ end
+ render :show, layout: user_layout
end
end
format.js
@@ -121,4 +125,8 @@ class ProjectsController < Projects::ApplicationController
def set_title
@title = 'New Project'
end
+
+ def user_layout
+ current_user ? "projects" : "public_projects"
+ end
end
diff --git a/app/controllers/public/projects_controller.rb b/app/controllers/public/projects_controller.rb
index 85216cd327..87e903a1d2 100644
--- a/app/controllers/public/projects_controller.rb
+++ b/app/controllers/public/projects_controller.rb
@@ -1,7 +1,7 @@
class Public::ProjectsController < ApplicationController
skip_before_filter :authenticate_user!,
- :reject_blocked, :set_current_user_for_observers,
- :add_abilities
+ :reject_blocked, :set_current_user_for_observers,
+ :add_abilities
layout 'public'
@@ -10,15 +10,4 @@ class Public::ProjectsController < ApplicationController
@projects = @projects.search(params[:search]) if params[:search].present?
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
end
-
- def show
- @project = Project.public_only.find_with_namespace(params[:id])
- render_404 and return unless @project
-
- @repository = @project.repository
- @recent_tags = @repository.tags.first(10)
-
- @commit = @repository.commit(params[:ref])
- @tree = Tree.new(@repository, @commit.id)
- end
end
diff --git a/app/controllers/users_groups_controller.rb b/app/controllers/users_groups_controller.rb
index df13b86fdc..749da1e141 100644
--- a/app/controllers/users_groups_controller.rb
+++ b/app/controllers/users_groups_controller.rb
@@ -19,7 +19,7 @@ class UsersGroupsController < ApplicationController
def destroy
@users_group = @group.users_groups.find(params[:id])
- @users_group.destroy unless @users_group.user == @group.owner
+ @users_group.destroy
respond_to do |format|
format.html { redirect_to members_group_path(@group), notice: 'User was successfully removed from group.' }
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 4209b081bf..e2d9790141 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -49,6 +49,15 @@ module ApplicationHelper
args.any? { |v| v.to_s.downcase == action_name }
end
+ def avatar_icon(user_email = '', size = nil)
+ user = User.find_by_email(user_email)
+ if user && user.avatar.present?
+ user.avatar.url
+ else
+ gravatar_icon(user_email, size)
+ end
+ end
+
def gravatar_icon(user_email = '', size = nil)
size = 40 if size.nil? || size <= 0
@@ -89,49 +98,6 @@ module ApplicationHelper
grouped_options_for_select(options, @ref || @project.default_branch)
end
- def search_autocomplete_source
- projects = current_user.authorized_projects.map { |p| { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } }
- groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } }
-
- default_nav = [
- { label: "My Profile", url: profile_path },
- { label: "My SSH Keys", url: profile_keys_path },
- { label: "My Dashboard", url: root_path },
- { label: "Admin Section", url: admin_root_path },
- ]
-
- help_nav = [
- { label: "help: API Help", url: help_api_path },
- { label: "help: Markdown Help", url: help_markdown_path },
- { label: "help: Permissions Help", url: help_permissions_path },
- { label: "help: Public Access Help", url: help_public_access_path },
- { label: "help: Rake Tasks Help", url: help_raketasks_path },
- { label: "help: SSH Keys Help", url: help_ssh_path },
- { label: "help: System Hooks Help", url: help_system_hooks_path },
- { label: "help: Web Hooks Help", url: help_web_hooks_path },
- { label: "help: Workflow Help", url: help_workflow_path },
- ]
-
- project_nav = []
- if @project && @project.repository.exists? && @project.repository.root_ref
- project_nav = [
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Files", url: project_tree_path(@project, @ref || @project.repository.root_ref) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Network", url: project_network_path(@project, @ref || @project.repository.root_ref) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Graph", url: project_graph_path(@project, @ref || @project.repository.root_ref) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Merge Requests", url: project_merge_requests_path(@project) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Milestones", url: project_milestones_path(@project) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Snippets", url: project_snippets_path(@project) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Team", url: project_team_index_path(@project) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Wall", url: project_wall_path(@project) },
- { label: "#{simple_sanitize(@project.name_with_namespace)} - Wiki", url: project_wikis_path(@project) },
- ]
- end
-
- [groups, projects, default_nav, project_nav, help_nav].flatten.to_json
- end
-
def emoji_autocomplete_source
# should be an array of strings
# so to_s can be called, because it is sufficient and to_json is too slow
@@ -181,7 +147,7 @@ module ApplicationHelper
alt: "Sign in with #{provider.to_s.titleize}")
end
- def simple_sanitize str
+ def simple_sanitize(str)
sanitize(str, tags: %w(a span))
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index f8f84ff8b6..7e24cbd3e5 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -108,7 +108,7 @@ module CommitsHelper
source_name = commit.send "#{options[:source]}_name".to_sym
source_email = commit.send "#{options[:source]}_email".to_sym
text = if options[:avatar]
- avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
+ avatar = image_tag(avatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
%Q{#{avatar}
#{source_name} }
else
source_name
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 375f8861da..b34f703353 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -34,7 +34,8 @@ module GitlabMarkdownHelper
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
filter_html: true,
with_toc_data: true,
- hard_wrap: true)
+ hard_wrap: true,
+ safe_links_only: true)
@markdown = Redcarpet::Markdown.new(gitlab_renderer,
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
no_intra_emphasis: true,
@@ -57,4 +58,104 @@ module GitlabMarkdownHelper
wiki_page.formatted_content.html_safe
end
end
+
+ # text - whole text from a markdown file
+ # project_path_with_namespace - namespace/projectname, eg. gitlabhq/gitlabhq
+ # ref - name of the branch or reference, eg. stable
+ # requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from
+ # wiki - whether the markdown is from wiki or not
+ def create_relative_links(text, project_path_with_namespace, ref, requested_path, wiki = false)
+ paths = extract_paths(text)
+ paths.each do |file_path|
+ new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref)
+ # Replacing old string with a new one with brackets ]() to prevent replacing occurence of a word
+ # e.g. If we have a markdown like [test](test) this will replace ](test) and not the word test
+ text.gsub!("](#{file_path})", "](/#{new_path})")
+ end
+ text
+ end
+
+ def extract_paths(markdown_text)
+ all_markdown_paths = pick_out_paths(markdown_text)
+ paths = remove_empty(all_markdown_paths)
+ select_relative(paths)
+ end
+
+ # Split the markdown text to each line and find all paths, this will match anything with - ]("some_text")
+ def pick_out_paths(markdown_text)
+ markdown_text.split("\n").map { |text| text.scan(/\]\(([^(]+)\)/) }
+ end
+
+ # Removes any empty result produced by not matching the regexp
+ def remove_empty(paths)
+ paths.reject{|l| l.empty? }.flatten
+ end
+
+ # Reject any path that contains ignored protocol
+ # eg. reject "https://gitlab.org} but accept "doc/api/README.md"
+ def select_relative(paths)
+ paths.reject{|path| ignored_protocols.map{|protocol| path.include?(protocol)}.any?}
+ end
+
+ def ignored_protocols
+ ["http://","https://", "ftp://", "mailto:"]
+ end
+
+ def rebuild_path(path_with_namespace, path, requested_path, ref)
+ file_path = relative_file_path(path, requested_path)
+ [
+ path_with_namespace,
+ path_with_ref(file_path, ref),
+ file_path
+ ].compact.join("/")
+ end
+
+ # Checks if the path exists in the repo
+ # eg. checks if doc/README.md exists, if it doesn't then it is a wiki link
+ def path_with_ref(path, ref)
+ if file_exists?(path)
+ "#{local_path(path)}/#{correct_ref(ref)}"
+ else
+ "wikis"
+ end
+ end
+
+ def relative_file_path(path, requested_path)
+ nested_path = build_nested_path(path, requested_path)
+ return nested_path if file_exists?(nested_path)
+ path
+ end
+
+ # Covering a special case, when the link is referencing file in the same directory eg:
+ # If we are at doc/api/README.md and the README.md contains relative links like [Users](users.md)
+ # this takes the request path(doc/api/README.md), and replaces the README.md with users.md so the path looks like doc/api/users.md
+ # If we are at doc/api and the README.md shown in below the tree view
+ # this takes the rquest path(doc/api) and adds users.md so the path looks like doc/api/users.md
+ def build_nested_path(path, request_path)
+ return path unless request_path
+ if local_path(request_path) == "tree"
+ base = request_path.split("/").push(path)
+ base.join("/")
+ else
+ base = request_path.split("/")
+ base.pop
+ base.push(path).join("/")
+ end
+ end
+
+ def file_exists?(path)
+ return false if path.nil? || path.empty?
+ File.exists?(Rails.root.join(path))
+ end
+
+ # Check if the path is pointing to a directory(tree) or a file(blob)
+ # eg. doc/api is directory and doc/README.md is file
+ def local_path(path)
+ File.directory?(Rails.root.join(path)) ? "tree" : "blob"
+ end
+
+ # We will assume that if no ref exists we can point to master
+ def correct_ref(ref)
+ ref ? ref : "master"
+ end
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 5977c9cbae..2221583912 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -16,7 +16,7 @@ module IssuesHelper
def url_for_project_issues
return "" if @project.nil?
- if @project.used_default_issues_tracker?
+ if @project.used_default_issues_tracker? || !external_issues_tracker_enabled?
project_issues_path(@project)
else
url = Gitlab.config.issues_tracker[@project.issues_tracker]["project_url"]
@@ -28,7 +28,7 @@ module IssuesHelper
def url_for_new_issue
return "" if @project.nil?
- if @project.used_default_issues_tracker?
+ if @project.used_default_issues_tracker? || !external_issues_tracker_enabled?
url = new_project_issue_path project_id: @project
else
url = Gitlab.config.issues_tracker[@project.issues_tracker]["new_issue_url"]
@@ -40,7 +40,7 @@ module IssuesHelper
def url_for_issue(issue_iid)
return "" if @project.nil?
- if @project.used_default_issues_tracker?
+ if @project.used_default_issues_tracker? || !external_issues_tracker_enabled?
url = project_issue_url project_id: @project, id: issue_iid
else
url = Gitlab.config.issues_tracker[@project.issues_tracker]["issues_url"]
@@ -59,4 +59,13 @@ module IssuesHelper
""
end
end
+
+ # Checks if issues_tracker setting exists in gitlab.yml
+ def external_issues_tracker_enabled?
+ if Gitlab.config.issues_tracker && Gitlab.config.issues_tracker.values.any?
+ true
+ else
+ false
+ end
+ end
end
diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb
index 88d9f184d0..dd9e03d95a 100644
--- a/app/helpers/profile_helper.rb
+++ b/app/helpers/profile_helper.rb
@@ -14,6 +14,6 @@ module ProfileHelper
end
def show_profile_remove_tab?
- Gitlab.config.gitlab.signup_enabled && !current_user.ldap_user?
+ gitlab_config.signup_enabled && !current_user.ldap_user?
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 3a1cf59fd1..864f6c9af9 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -5,10 +5,10 @@ module ProjectsHelper
def link_to_project project
link_to project do
- title = content_tag(:strong, project.name)
+ title = content_tag(:span, project.name, class: 'projet-name')
if project.namespace
- namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'tiny')
+ namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name')
title = namespace + title
end
@@ -25,7 +25,7 @@ module ProjectsHelper
author_html = ""
# Build avatar image tag
- author_html << image_tag(gravatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
+ author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
# Build name span tag
author_html << content_tag(:span, sanitize(author.name), class: 'author') if opts[:name]
@@ -77,7 +77,19 @@ module ProjectsHelper
end
def project_active_milestones
- @project.milestones.active.order("id desc").all
+ @project.milestones.active.order("due_date, title ASC").all
+ end
+
+ def project_issues_trackers
+ values = Project.issues_tracker.values.map do |tracker_key|
+ if tracker_key.to_sym == :gitlab
+ ['GitLab', tracker_key]
+ else
+ [Gitlab.config.issues_tracker[tracker_key]['title'] || tracker_key, tracker_key]
+ end
+ end
+
+ options_for_select(values)
end
private
@@ -103,4 +115,29 @@ module ProjectsHelper
nav_tabs.flatten
end
+
+ def git_user_name
+ if current_user
+ current_user.name
+ else
+ "Your name"
+ end
+ end
+
+ def git_user_email
+ if current_user
+ current_user.email
+ else
+ "your@email.com"
+ end
+ end
+
+ def repository_size
+ "#{@project.repository.size} MB"
+ rescue
+ # In order to prevent 500 error
+ # when application cannot allocate memory
+ # to calculate repo size - just show 'Unknown'
+ 'unknown'
+ end
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
new file mode 100644
index 0000000000..33c5e4fb9d
--- /dev/null
+++ b/app/helpers/search_helper.rb
@@ -0,0 +1,78 @@
+module SearchHelper
+ def search_autocomplete_source
+ return unless current_user
+
+ [
+ groups_autocomplete,
+ projects_autocomplete,
+ default_autocomplete,
+ project_autocomplete,
+ help_autocomplete
+ ].flatten.to_json
+ end
+
+ private
+
+ # Autocomplete results for various settings pages
+ def default_autocomplete
+ [
+ { label: "My Profile settings", url: profile_path },
+ { label: "My SSH Keys", url: profile_keys_path },
+ { label: "My Dashboard", url: root_path },
+ { label: "Admin Section", url: admin_root_path },
+ ]
+ end
+
+ # Autocomplete results for internal help pages
+ def help_autocomplete
+ [
+ { label: "help: API Help", url: help_api_path },
+ { label: "help: Markdown Help", url: help_markdown_path },
+ { label: "help: Permissions Help", url: help_permissions_path },
+ { label: "help: Public Access Help", url: help_public_access_path },
+ { label: "help: Rake Tasks Help", url: help_raketasks_path },
+ { label: "help: SSH Keys Help", url: help_ssh_path },
+ { label: "help: System Hooks Help", url: help_system_hooks_path },
+ { label: "help: Web Hooks Help", url: help_web_hooks_path },
+ { label: "help: Workflow Help", url: help_workflow_path },
+ ]
+ end
+
+ # Autocomplete results for the current project, if it's defined
+ def project_autocomplete
+ if @project && @project.repository.exists? && @project.repository.root_ref
+ prefix = simple_sanitize(@project.name_with_namespace)
+ ref = @ref || @project.repository.root_ref
+
+ [
+ { label: "#{prefix} - Files", url: project_tree_path(@project, ref) },
+ { label: "#{prefix} - Commits", url: project_commits_path(@project, ref) },
+ { label: "#{prefix} - Network", url: project_network_path(@project, ref) },
+ { label: "#{prefix} - Graph", url: project_graph_path(@project, ref) },
+ { label: "#{prefix} - Issues", url: project_issues_path(@project) },
+ { label: "#{prefix} - Merge Requests", url: project_merge_requests_path(@project) },
+ { label: "#{prefix} - Milestones", url: project_milestones_path(@project) },
+ { label: "#{prefix} - Snippets", url: project_snippets_path(@project) },
+ { label: "#{prefix} - Team", url: project_team_index_path(@project) },
+ { label: "#{prefix} - Wall", url: project_wall_path(@project) },
+ { label: "#{prefix} - Wiki", url: project_wikis_path(@project) },
+ ]
+ else
+ []
+ end
+ end
+
+ # Autocomplete results for the current user's groups
+ def groups_autocomplete
+ current_user.authorized_groups.map do |group|
+ { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) }
+ end
+ end
+
+ # Autocomplete results for the current user's projects
+ def projects_autocomplete
+ current_user.authorized_projects.map do |p|
+ { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
+ end
+ end
+end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 73d36d0801..2dbc1cffb1 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -67,9 +67,9 @@ module TreeHelper
end
def tree_breadcrumbs(tree, max_links = 2)
- if tree.path
+ if @path.present?
part_path = ""
- parts = tree.path.split("\/")
+ parts = @path.split("\/")
yield('..', nil) if parts.count > max_links
@@ -78,14 +78,14 @@ module TreeHelper
part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links
- yield(part, tree_join(tree.ref, part_path))
+ yield(part, tree_join(@ref, part_path))
end
end
end
def up_dir_path tree
- file = File.join(tree.path, "..")
- tree_join(tree.ref, file)
+ file = File.join(@path, "..")
+ tree_join(@ref, file)
end
def leave_edit_message
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index 761b4c8161..ba4f0dd862 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -18,7 +18,7 @@ module Emails
@note = Note.find(note_id)
@merge_request = @note.noteable
@project = @note.project
- mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.iid}"))
+ mail(to: recipient(recipient_id), subject: subject("note for merge request ##{@merge_request.iid}"))
end
def note_wall_email(recipient_id, note_id)
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 8335829f91..8547608914 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -1,6 +1,7 @@
class Ability
class << self
def allowed(user, subject)
+ return not_auth_abilities(user, subject) if user.nil?
return [] unless user.kind_of?(User)
return [] if user.blocked?
@@ -17,6 +18,34 @@ class Ability
end.concat(global_abilities(user))
end
+ # List of possible abilities
+ # for non-authenticated user
+ def not_auth_abilities(user, subject)
+ project = if subject.kind_of?(Project)
+ subject
+ elsif subject.respond_to?(:project)
+ subject.project
+ else
+ nil
+ end
+
+ if project && project.public
+ [
+ :read_project,
+ :read_wiki,
+ :read_issue,
+ :read_milestone,
+ :read_project_snippet,
+ :read_team_member,
+ :read_merge_request,
+ :read_note,
+ :download_code
+ ]
+ else
+ []
+ end
+ end
+
def global_abilities(user)
rules = []
rules << :create_group if user.can_create_group
@@ -50,7 +79,7 @@ class Ability
rules << project_admin_rules
end
- if project.group && project.group.owners.include?(user)
+ if project.group && project.group.has_owner?(user)
rules << project_admin_rules
end
@@ -58,19 +87,9 @@ class Ability
end
def public_project_rules
- [
+ project_guest_rules + [
:download_code,
:fork_project,
- :read_project,
- :read_wiki,
- :read_issue,
- :read_milestone,
- :read_project_snippet,
- :read_team_member,
- :read_merge_request,
- :read_note,
- :write_issue,
- :write_note
]
end
@@ -135,12 +154,12 @@ class Ability
def group_abilities user, group
rules = []
- if group.users.include?(user)
+ if group.users.include?(user) || user.admin?
rules << :read_group
end
# Only group owner and administrators can manage group
- if group.owners.include?(user) || user.admin?
+ if group.has_owner?(user) || user.admin?
rules << [
:manage_group,
:manage_namespace
diff --git a/app/models/flowdock_service.rb b/app/models/flowdock_service.rb
new file mode 100644
index 0000000000..6ec431d4a1
--- /dev/null
+++ b/app/models/flowdock_service.rb
@@ -0,0 +1,52 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# token :string(255)
+# project_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# active :boolean default(FALSE), not null
+# project_url :string(255)
+#
+
+require "flowdock-git-hook"
+
+class FlowdockService < Service
+ validates :token, presence: true, if: :activated?
+
+ def title
+ 'Flowdock'
+ end
+
+ def description
+ 'Flowdock is a collaboration web app for technical teams.'
+ end
+
+ def to_param
+ 'flowdock'
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'token', placeholder: '' }
+ ]
+ end
+
+ def execute(push_data)
+ repo_path = File.join(Gitlab.config.gitlab_shell.repos_path, "#{project.path_with_namespace}.git")
+ Flowdock::Git.post(
+ push_data[:ref],
+ push_data[:before],
+ push_data[:after],
+ token: token,
+ repo: repo_path,
+ repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
+ commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s",
+ diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s",
+ )
+ end
+end
diff --git a/app/models/gollum_wiki.rb b/app/models/gollum_wiki.rb
index d1edbab453..05fc2a745b 100644
--- a/app/models/gollum_wiki.rb
+++ b/app/models/gollum_wiki.rb
@@ -45,6 +45,10 @@ class GollumWiki
end
end
+ def empty?
+ pages.empty?
+ end
+
# Returns an Array of Gitlab WikiPage instances or an
# empty Array if this Wiki has no pages.
def pages
diff --git a/app/models/group.rb b/app/models/group.rb
index df52e0af3f..ba9461c42c 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -5,7 +5,7 @@
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
-# owner_id :integer not null
+# owner_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
@@ -26,36 +26,38 @@ class Group < Namespace
presence: true,
if: ->(group) { group.ldap_cn.present? }
- after_create :add_owner
-
def human_name
name
end
def owners
- @owners ||= (users_groups.owners.map(&:user) << owner).uniq
+ @owners ||= users_groups.owners.map(&:user)
end
def add_users(user_ids, group_access)
user_ids.compact.each do |user_id|
- self.users_groups.create(user_id: user_id, group_access: group_access)
+ user = self.users_groups.find_or_initialize_by_user_id(user_id)
+ user.update_attributes(group_access: group_access)
end
end
- def change_owner(user)
- self.owner = user
- membership = users_groups.where(user_id: user.id).first
-
- if membership
- membership.update_attributes(group_access: UsersGroup::OWNER)
- else
- add_owner
- end
+ def add_user(user, group_access)
+ self.users_groups.create(user_id: user.id, group_access: group_access)
end
- private
+ def add_owner(user)
+ self.add_user(user, UsersGroup::OWNER)
+ end
- def add_owner
- self.add_users([owner.id], UsersGroup::OWNER)
+ def has_owner?(user)
+ owners.include?(user)
+ end
+
+ def last_owner?(user)
+ has_owner?(user) && owners.size == 1
+ end
+
+ def members
+ users_groups
end
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 7f367588b2..b164ea1107 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -18,6 +18,7 @@
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
+# description :text
#
require Rails.root.join("app/models/commit")
@@ -221,7 +222,11 @@ class MergeRequest < ActiveRecord::Base
def mr_and_commit_notes
commit_ids = commits.map(&:id)
- Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
+ project.notes.where(
+ "(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
+ mr_id: id,
+ commit_ids: commit_ids
+ )
end
# Returns the raw diff for this merge request
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 1895916693..fde06649c7 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -5,7 +5,7 @@
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
-# owner_id :integer not null
+# owner_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
@@ -20,7 +20,7 @@ class Namespace < ActiveRecord::Base
has_many :projects, dependent: :destroy
belongs_to :owner, class_name: "User"
- validates :owner, presence: true
+ validates :owner, presence: true, unless: ->(n) { n.type == "Group" }
validates :name, presence: true, uniqueness: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb
index e31adcebbe..8417f200e3 100644
--- a/app/models/network/commit.rb
+++ b/app/models/network/commit.rb
@@ -1,5 +1,3 @@
-require "grit"
-
module Network
class Commit
include ActionView::Helpers::TagHelper
diff --git a/app/models/note.rb b/app/models/note.rb
index e819a5516b..7e7387abed 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -14,6 +14,7 @@
# commit_id :string(255)
# noteable_id :integer
# st_diff :text
+# system :boolean default(FALSE), not null
#
require 'carrierwave/orm/activerecord'
diff --git a/app/models/pivotaltracker_service.rb b/app/models/pivotaltracker_service.rb
index f28e142da7..c5b1b9ab8d 100644
--- a/app/models/pivotaltracker_service.rb
+++ b/app/models/pivotaltracker_service.rb
@@ -10,6 +10,9 @@
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
+# project_url :string(255)
+# subdomain :string(255)
+# room :string(255)
#
class PivotaltrackerService < Service
diff --git a/app/models/project.rb b/app/models/project.rb
index 9426747218..4988cc8bc1 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -24,8 +24,6 @@
# import_url :string(255)
#
-require "grit"
-
class Project < ActiveRecord::Base
include Gitlab::ShellAdapter
extend Enumerize
@@ -48,12 +46,14 @@ class Project < ActiveRecord::Base
has_one :campfire_service, dependent: :destroy
has_one :pivotaltracker_service, dependent: :destroy
has_one :hipchat_service, dependent: :destroy
+ has_one :flowdock_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
has_many :services, dependent: :destroy
has_many :events, dependent: :destroy
has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
+ has_many :fork_merge_requests,dependent: :destroy, foreign_key: "source_project_id", class_name: MergeRequest
has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC"
has_many :milestones, dependent: :destroy
has_many :notes, dependent: :destroy
@@ -224,7 +224,7 @@ class Project < ActiveRecord::Base
end
def available_services_names
- %w(gitlab_ci campfire hipchat pivotaltracker)
+ %w(gitlab_ci campfire hipchat pivotaltracker flowdock)
end
def gitlab_ci?
@@ -252,10 +252,10 @@ class Project < ActiveRecord::Base
end
def owner
- if namespace
- namespace_owner
+ if group
+ group
else
- creator
+ namespace.try(:owner)
end
end
@@ -279,10 +279,6 @@ class Project < ActiveRecord::Base
end
end
- def namespace_owner
- namespace.try(:owner)
- end
-
def path_with_namespace
if namespace
namespace.path + '/' + path
@@ -320,8 +316,11 @@ class Project < ActiveRecord::Base
branch_name = ref.gsub("refs/heads/", "")
c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
- # Update code for merge requests
+ # Update code for merge requests into project between project branches
mrs = self.merge_requests.opened.by_branch(branch_name).all
+ # Update code for merge requests between project and project fork
+ mrs += self.fork_merge_requests.opened.by_branch(branch_name).all
+
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
# Close merge requests
diff --git a/app/models/repository.rb b/app/models/repository.rb
index aeec48ee5c..97b4330092 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1,14 +1,19 @@
class Repository
include Gitlab::ShellAdapter
- attr_accessor :raw_repository
+ attr_accessor :raw_repository, :path_with_namespace
def initialize(path_with_namespace, default_branch)
- @raw_repository = Gitlab::Git::Repository.new(path_with_namespace, default_branch)
+ @path_with_namespace = path_with_namespace
+ @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
rescue Gitlab::Git::Repository::NoRepository
nil
end
+ def path_to_repo
+ @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, path_with_namespace + ".git")
+ end
+
def exists?
raw_repository
end
@@ -150,4 +155,8 @@ class Repository
super
end
+
+ def blob_at(sha, path)
+ Gitlab::Git::Blob.find(self, sha, path)
+ end
end
diff --git a/app/models/tree.rb b/app/models/tree.rb
index 042050527c..ed06cb1a12 100644
--- a/app/models/tree.rb
+++ b/app/models/tree.rb
@@ -1,17 +1,26 @@
class Tree
- attr_accessor :raw
+ attr_accessor :entries, :readme
- def initialize(repository, sha, ref = nil, path = nil)
- @raw = Gitlab::Git::Tree.new(repository, sha, ref, path)
+ def initialize(repository, sha, path = '/')
+ path = '/' if path.blank?
+ git_repo = repository.raw_repository
+ @entries = Gitlab::Git::Tree.where(git_repo, sha, path)
+
+ if readme_tree = @entries.find(&:readme?)
+ readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name)
+ @readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path)
+ end
end
- def method_missing(m, *args, &block)
- @raw.send(m, *args, &block)
+ def trees
+ @entries.select(&:dir?)
end
- def respond_to?(method)
- return true if @raw.respond_to?(method)
+ def blobs
+ @entries.select(&:file?)
+ end
- super
+ def submodules
+ @entries.select(&:submodule?)
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index f4361987de..aeb3fe2def 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -38,13 +38,16 @@
# created_by_id :integer
#
+require 'carrierwave/orm/activerecord'
+require 'file_size_validator'
+
class User < ActiveRecord::Base
- devise :database_authenticatable, :token_authenticatable, :lockable,
- :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :registerable
+ devise :database_authenticatable, :token_authenticatable, :lockable, :async,
+ :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :confirmable, :registerable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password,
- :extern_uid, :provider, :password_expires_at,
+ :extern_uid, :provider, :password_expires_at, :avatar,
as: [:default, :admin]
attr_accessible :projects_limit, :can_create_group,
@@ -113,6 +116,8 @@ class User < ActiveRecord::Base
validate :namespace_uniq, if: ->(user) { user.username_changed? }
+ validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
+
before_validation :generate_password, on: :create
before_validation :sanitize_attrs
@@ -135,7 +140,7 @@ class User < ActiveRecord::Base
# Remove user from all groups
user.users_groups.find_each do |membership|
# skip owned resources
- next if membership.group.owners.include?(user)
+ next if membership.group.last_owner?(user)
return false unless membership.destroy
end
@@ -150,6 +155,8 @@ class User < ActiveRecord::Base
end
end
+ mount_uploader :avatar, AttachmentUploader
+
# Scopes
scope :admins, -> { where(admin: true) }
scope :blocked, -> { with_state(:blocked) }
@@ -167,7 +174,7 @@ class User < ActiveRecord::Base
# Class methods
#
class << self
- # Devise method overridden to allow sing in with email or username
+ # Devise method overridden to allow sign in with email or username
def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
@@ -192,11 +199,7 @@ class User < ActiveRecord::Base
end
def by_username_or_id(name_or_id)
- if (name_or_id.is_a?(Integer))
- User.find_by_id(name_or_id)
- else
- User.find_by_username(name_or_id)
- end
+ where('username = ? OR id = ?', name_or_id, name_or_id).first
end
def build_user(attrs = {}, options= {})
@@ -211,7 +214,7 @@ class User < ActiveRecord::Base
{
projects_limit: Gitlab.config.gitlab.default_projects_limit,
can_create_group: Gitlab.config.gitlab.default_can_create_group,
- theme_id: Gitlab::Theme::MARS
+ theme_id: Gitlab.config.gitlab.default_theme
}
end
end
diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb
index acbb719b69..f301f30645 100644
--- a/app/observers/project_observer.rb
+++ b/app/observers/project_observer.rb
@@ -14,6 +14,17 @@ class ProjectObserver < BaseObserver
log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
end
+
+ if project.wiki_enabled?
+ begin
+ # force the creation of a wiki,
+ GollumWiki.new(project, project.owner).wiki
+ rescue GollumWiki::CouldNotCreateWikiError => ex
+ # Prevent project observer crash
+ # if failed to create wiki
+ nil
+ end
+ end
end
def after_update(project)
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index f9d43e60de..3ad41de397 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -112,6 +112,7 @@ class GitPushService
# ref: String,
# user_id: String,
# user_name: String,
+ # project_id: String,
# repository: {
# name: String,
# url: String,
@@ -136,6 +137,7 @@ class GitPushService
ref: ref,
user_id: user.id,
user_name: user.name,
+ project_id: project.id,
repository: {
name: project.name,
url: project.url_to_repo,
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index c872b27ba3..de4fa9b79e 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -23,18 +23,21 @@ class SystemHooksService
case model
when Project
+ owner = model.owner
+
data.merge!({
name: model.name,
path: model.path,
path_with_namespace: model.path_with_namespace,
project_id: model.id,
- owner_name: model.owner.name,
- owner_email: model.owner.email
+ owner_name: owner.name,
+ owner_email: owner.respond_to?(:email) ? owner.email : nil
})
when User
data.merge!({
name: model.name,
- email: model.email
+ email: model.email,
+ user_id: model.id
})
when UsersProject
data.merge!({
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index 2d4ffc10d5..122e952d87 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -2,3 +2,37 @@
%br
.ui-box
%iframe{src: sidekiq_path, width: '100%', height: 900, style: "border: none"}
+%h4 Sidekiq running processes
+- sidekiq_processes = `ps -eo euser,pid,pcpu,pmem,stat,start,command | grep sidekiq | grep -v grep`
+- if sidekiq_processes.empty?
+ %b There are no running sidekiq processes
+ %b Please restart GitLab
+- else
+ .ui-box
+ %table.zebra-striped
+ %thead
+ %th USER
+ %th
+ %th PID
+ %th
+ %th CPU
+ %th
+ %th MEM
+ %th
+ %th STATE
+ %th
+ %th START
+ %th
+ %th COMMAND
+ %th
+ - sidekiq_processes.split("\n").each do |process|
+ - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
+ - data = process.gsub!(/\s+/m, '|').strip.split('|')
+ %tr
+ - 6.times do
+ %td= data.shift
+ %td
+ %td= data.join(" ")
+ %b If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
+ %br
+ %b If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u git -f sidekiq) and restart GitLab.
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 9ed788d0d9..3064763b99 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -1,30 +1,35 @@
+%h3.page-title
+ Admin area
+%p.light
+ You can manage projects, users and other GitLab data from here.
+%hr
.admin_dash.row
.span4
- .ui-box
- .title Projects
- .data.padded
+ .light-well
+ %h4 Projects
+ .data
= link_to admin_projects_path do
%h1= Project.count
%hr
- = link_to 'New Project', new_project_path, class: "btn btn-small"
+ = link_to 'New Project', new_project_path, class: "btn btn-new"
.span4
- .ui-box
- .title Users
- .data.padded
+ .light-well
+ %h4 Users
+ .data
= link_to admin_users_path do
%h1= User.count
%hr
- = link_to 'New User', new_admin_user_path, class: "btn btn-small"
+ = link_to 'New User', new_admin_user_path, class: "btn btn-new"
.span4
- .ui-box
- .title Groups
- .data.padded
+ .light-well
+ %h4 Groups
+ .data
= link_to admin_groups_path do
%h1= Group.count
%hr
- = link_to 'New Group', new_admin_group_path, class: "btn btn-small"
+ = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
-.row
+.row.prepend-top-10
.span4
%h4 Latest projects
%hr
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 8e45dc76ec..c9d7c24f20 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -31,11 +31,8 @@
.clearfix.light.append-bottom-10
%span
- %b Owner:
- - if group.owner
- = link_to group.owner_name, admin_user_path(group.owner)
- - else
- (deleted)
+ %b Members:
+ %span.badge= group.members.size
\|
%span
%b Projects:
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 5509811bae..1566c34580 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -24,26 +24,6 @@
%strong
= @group.description
- %li
- %span.light Owned by:
- %strong
- - if @group.owner
- = link_to @group.owner_name, admin_user_path(@group.owner)
- - else
- (deleted)
- .pull-right
- = link_to "#", class: "btn btn-small change-owner-link" do
- %i.icon-edit
- Change owner
- %li.change-owner-holder.hide.bgred
- .form-holder
- %strong.cred New Owner:
- = form_for [:admin, @group] do |f|
- = users_select_tag(:"group[owner_id]")
- .prepend-top-10
- = f.submit 'Change Owner', class: "btn btn-remove"
- = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
-
%li
%span.light Created at:
%strong
@@ -92,4 +72,5 @@
= link_to user.name, admin_user_path(user)
%span.pull-right.light
= member.human_access
-
+ = link_to group_users_group_path(@group, member), confirm: remove_user_from_group_message(@group, user), method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
+ %i.icon-minus.icon-white
diff --git a/app/views/admin/hooks/_data_ex.html.erb b/app/views/admin/hooks/_data_ex.html.erb
index b69aa92716..5b16dfb398 100644
--- a/app/views/admin/hooks/_data_ex.html.erb
+++ b/app/views/admin/hooks/_data_ex.html.erb
@@ -52,7 +52,8 @@
"created_at": "2012-07-21T07:44:07Z",
"email": "js@gitlabhq.com",
"event_name": "user_create",
- "name": "John Smith"
+ "name": "John Smith",
+ "user_id": 41
}
6. User removed:
@@ -60,7 +61,8 @@
"created_at": "2012-07-21T07:44:07Z",
"email": "js@gitlabhq.com",
"event_name": "user_destroy",
- "name": "John Smith"
+ "name": "John Smith",
+ "user_id": 41
}
eos
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index eb6570af30..1bd7b8eac6 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -1,9 +1,12 @@
-.alert.alert-info
- %span
- Post-receive hooks for binding events.
- %br
- Read more about system hooks
- %strong #{link_to "here", help_system_hooks_path, class: "vlink"}
+%h3.page-title
+ System Hooks
+
+%p.light
+ #{link_to "System hooks ", help_system_hooks_path, class: "vlink"} can be
+ used for binding events when GitLab creates a User or Project.
+
+%hr
+
= form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f|
-if @hook.errors.any?
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index cecc84fe09..c8a8732820 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -25,7 +25,7 @@
%span.light Owned by:
%strong
- if @project.owner
- = link_to @project.owner_name, admin_user_path(@project.owner)
+ = link_to @project.owner_name, [:admin, @project.owner]
- else
(deleted)
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 3df9903e40..655ed5f7f7 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -1,5 +1,5 @@
%h3.page-title
- User:
+ %span.cgray User:
= @user.name
- if @user.blocked?
%span.cred (Blocked)
@@ -10,8 +10,6 @@
= link_to edit_admin_user_path(@user), class: "btn grouped" do
%i.icon-edit
Edit
- - if @user.blocked?
- = link_to 'Unblock', unblock_admin_user_path(@user), method: :put, class: "btn grouped success"
%hr
.row
@@ -20,7 +18,7 @@
.title
Account:
.pull-right
- = image_tag gravatar_icon(@user.email, 32), class: "avatar s32"
+ = image_tag avatar_icon(@user.email, 32), class: "avatar s32"
%ul.well-list
%li
%span.light Name:
@@ -67,17 +65,30 @@
= link_to @user.created_by.name, [:admin, @user.created_by]
- unless @user == current_user
- .alert
- %h4 Block user
- %br
- %p Blocking user has the following effects:
- %ul
- %li User will not be able to login
- %li User will not be able to access git repositories
- %li User will be removed from joined projects and groups
- %li Personal projects will be left
- %li Owned groups will be left
- = link_to 'Block user', block_admin_user_path(@user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-remove"
+ - if @user.blocked?
+ .alert.alert-info
+ %h4 This user is blocked
+ %br
+ %p Blocking user has the following effects:
+ %ul
+ %li User will not be able to login
+ %li User will not be able to access git repositories
+ %li User will be removed from joined projects and groups
+ %li Personal projects will be left
+ %li Owned groups will be left
+ = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-new", confirm: 'Are you sure?'
+ - else
+ .alert
+ %h4 Block this user
+ %br
+ %p Blocking user has the following effects:
+ %ul
+ %li User will not be able to login
+ %li User will not be able to access git repositories
+ %li User will be removed from joined projects and groups
+ %li Personal projects will be left
+ %li Owned groups will be left
+ = link_to 'Block user', block_admin_user_path(@user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-remove"
.alert.alert-error
%h4
@@ -117,11 +128,7 @@
- tm = project.team.find_tm(@user.id)
%li.users_project
= link_to admin_project_path(project), class: dom_class(project) do
- - if project.namespace
- = project.namespace.human_name
- \/
- %strong.well-title
- = truncate(project.name, length: 45)
+ = project.name_with_namespace
- if tm
.pull-right
diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml
index 8b32c5642b..b4f3866228 100644
--- a/app/views/dashboard/_groups.html.haml
+++ b/app/views/dashboard/_groups.html.haml
@@ -14,9 +14,6 @@
= truncate(group.name, length: 35)
%span.arrow
%i.icon-angle-right
- %span.owner-info
- %span Owner:
- %span.owner= group.owner_name
- if groups.blank?
%li
%h3.nothing_here_message You have no groups yet.
diff --git a/app/views/dashboard/_project.html.haml b/app/views/dashboard/_project.html.haml
new file mode 100644
index 0000000000..50b833f3d8
--- /dev/null
+++ b/app/views/dashboard/_project.html.haml
@@ -0,0 +1,12 @@
+= link_to project_path(project), class: dom_class(project) do
+ %span.namespace-name
+ - if project.namespace
+ = project.namespace.human_name
+ \/
+ %span.project-name.filter-title
+ = truncate(project.name, length: 25)
+ %span.arrow
+ %i.icon-angle-right
+ %span.last-activity
+ %span Last activity:
+ %span.date= project_last_activity(project)
diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml
index 90b843de01..b79b27fc95 100644
--- a/app/views/dashboard/_projects.html.haml
+++ b/app/views/dashboard/_projects.html.haml
@@ -10,21 +10,16 @@
%ul.well-list.dash-list
- projects.each do |project|
%li.project-row
- = link_to project_path(project), class: dom_class(project) do
- %span.namespace-name
- - if project.namespace
- = project.namespace.human_name
- \/
- %span.project-name.filter-title
- = truncate(project.name, length: 25)
- %span.arrow
- %i.icon-angle-right
- %span.last-activity
- %span Last activity:
- %span.date= project_last_activity(project)
+ = render "project", project: project
+
- if projects.blank?
%li
%h3.nothing_here_message There are no projects here.
- - if @projects_count > 20
+ - if @projects_count > @projects_limit
%li.bottom
- %strong= link_to "show all projects", projects_dashboard_path
+ %span.light
+ #{@projects_limit} of #{pluralize(@projects_count, 'project')} displayed.
+ .pull-right.append-right-10
+ = link_to projects_dashboard_path do
+ Show all
+ %i.icon-angle-right
diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder
index 0f0f3466e9..f541355778 100644
--- a/app/views/dashboard/issues.atom.builder
+++ b/app/views/dashboard/issues.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => project_issue_url(issue.project, issue)
xml.title truncate(issue.title, :length => 80)
xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email)
+ xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
xml.author do |author|
xml.name issue.author_name
xml.email issue.author_email
diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/show.atom.builder
index a913df9229..f4cf24ccd9 100644
--- a/app/views/dashboard/show.atom.builder
+++ b/app/views/dashboard/show.atom.builder
@@ -17,7 +17,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => event_link
xml.title truncate(event_title, :length => 80)
xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email)
+ xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email)
xml.author do |author|
xml.name event.author_name
xml.email event.author_email
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index c6e1d8db57..15854effbf 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -18,7 +18,7 @@
= render 'devise/sessions/oauth_providers' if devise_mapping.omniauthable?
- - if Gitlab.config.gitlab.signup_enabled
+ - if gitlab_config.signup_enabled
%hr
%div
Don't have an account?
diff --git a/app/views/devise/shared/_links.erb b/app/views/devise/shared/_links.erb
index a47b5ff1ec..49e99e25c1 100644
--- a/app/views/devise/shared/_links.erb
+++ b/app/views/devise/shared/_links.erb
@@ -2,7 +2,7 @@
<%= link_to "Sign in", new_session_path(resource_name), class: "btn" %>
<% end -%>
-<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
+<%- if devise_mapping.registerable? && controller_name != 'registrations' && gitlab_config.signup_enabled %>
<%= link_to "Sign up", new_registration_path(resource_name) %>
<% end -%>
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index b3543460d6..892dacafa6 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -4,7 +4,7 @@
#{time_ago_in_words(event.created_at)} ago.
= cache event do
- = image_tag gravatar_icon(event.author_email), class: "avatar s24", alt:''
+ = image_tag avatar_icon(event.author_email), class: "avatar s24", alt:''
- if event.push?
= render "events/event/push", event: event
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index adba9a5f61..f181df23eb 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -7,7 +7,7 @@
= link_to project_commits_path(event.project, event.ref_name) do
%strong= truncate(event.ref_name, length: 30)
at
- %strong= link_to_project event.project
+ = link_to_project event.project
- if event.push_with_commits?
- project = event.project
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 3b1d633af1..f76a9d9586 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -11,8 +11,6 @@
Projects
%li
= link_to 'LDAP', '#tab-ldap', 'data-toggle' => 'tab'
- %li
- = link_to 'Transfer', '#tab-transfer', 'data-toggle' => 'tab'
%li
= link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab'
@@ -87,17 +85,6 @@
.form-actions
= f.submit 'Save group', class: "btn btn-save"
- .tab-pane#tab-transfer
- .ui-box.ui-box-danger
- .title Transfer group
- .ui-box-body
- %p
- Transferring group will cause loss of admin control over group and all child projects
- = form_for @group do |f|
- = users_select_tag(:'group[owner_id]')
- %hr
- = f.submit 'Transfer group', class: "btn btn-small btn-remove"
-
.tab-pane#tab-remove
.ui-box.ui-box-danger
.title Remove group
diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder
index 701747bdbc..f2005193f8 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => project_issue_url(issue.project, issue)
xml.title truncate(issue.title, :length => 80)
xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email)
+ xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
xml.author do |author|
xml.name issue.author_name
xml.email issue.author_email
diff --git a/app/views/groups/members.html.haml b/app/views/groups/members.html.haml
index 77bab11b33..124560e478 100644
--- a/app/views/groups/members.html.haml
+++ b/app/views/groups/members.html.haml
@@ -2,6 +2,9 @@
Group members
%p.light
Members of group have access to all group projects.
+ Read more about permissions
+ %strong= link_to "here", help_permissions_path, class: "vlink"
+
%hr
- can_manage_group = current_user.can? :manage_group, @group
.ui-box
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index edf03642d8..e07bb7d2fb 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -16,7 +16,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => event_link
xml.title truncate(event_title, :length => 80)
xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email)
+ xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email)
xml.author do |author|
xml.name event.author_name
xml.email event.author_email
diff --git a/app/views/help/_layout.html.haml b/app/views/help/_layout.html.haml
index ac8660dcbb..da917888ee 100644
--- a/app/views/help/_layout.html.haml
+++ b/app/views/help/_layout.html.haml
@@ -30,5 +30,8 @@
%li
%strong= link_to "Public Access", help_public_access_path
+ %li
+ %strong= link_to "Security", help_security_path
+
.span9.pull-right
= yield
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 9b18d03408..7df53c6d38 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -56,7 +56,7 @@
%p Get familiar with GitLab's permission levels.
%li
- %strong= link_to "API", help_api_path
+ %strong= link_to "API", help_api_file_path(category: 'README')
%p Explore how you can access GitLab via a simple and powerful API.
%li
@@ -80,3 +80,7 @@
%li
%strong= link_to "Public Access", help_public_access_path
%p Learn how you can allow public access to a project.
+
+ %li
+ %strong= link_to "Security", help_security_path
+ %p Learn what you can do to secure your GitLab instance.
diff --git a/app/views/help/permissions.html.haml b/app/views/help/permissions.html.haml
index da5210bad3..df35f41fc9 100644
--- a/app/views/help/permissions.html.haml
+++ b/app/views/help/permissions.html.haml
@@ -1,6 +1,11 @@
= render layout: 'help/layout' do
%h3.page-title Permissions
+ %p.light Users have different abilities depending on the access level they have in particular group or project.
+ %p.light If a user is both in a project group and in the project itself the highest permission level is used.
+ %p.light If a user is a GitLab administrator they receive all permissions.
+ %hr
+ %h4 Project:
%table.table
%thead
%tr
@@ -158,3 +163,50 @@
%td
%td
%td.permission-x ✓
+
+ %h4 Group
+ %table.table
+ %thead
+ %tr
+ %th Action
+ %th Guest
+ %th Reporter
+ %th Developer
+ %th Master
+ %th Owner
+ %tbody
+ %tr
+ %td Browse group
+ %td.permission-x ✓
+ %td.permission-x ✓
+ %td.permission-x ✓
+ %td.permission-x ✓
+ %td.permission-x ✓
+ %tr
+ %td Edit group
+ %td
+ %td
+ %td
+ %td
+ %td.permission-x ✓
+ %tr
+ %td Create project in group
+ %td
+ %td
+ %td
+ %td
+ %td.permission-x ✓
+ %tr
+ %td Manage group members
+ %td
+ %td
+ %td
+ %td
+ %td.permission-x ✓
+ %tr
+ %td Remove group
+ %td
+ %td
+ %td
+ %td
+ %td.permission-x ✓
diff --git a/app/views/help/security.html.haml b/app/views/help/security.html.haml
new file mode 100644
index 0000000000..72f21e9f63
--- /dev/null
+++ b/app/views/help/security.html.haml
@@ -0,0 +1,15 @@
+= render layout: 'help/layout' do
+ %h3.page-title Security
+
+ %p.slead
+ If your GitLab instance is visible from the internet chances are it will be 'tested' by bots sooner or later.
+ %br
+ %br
+ %br
+ .file-holder
+ .file-title
+ %i.icon-file
+ Dealing with bruteforcing
+ .file-content.wiki
+ = preserve do
+ = markdown File.read(Rails.root.join("doc", "security", "rack_attack.md"))
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index 6492c122ba..80c42fe638 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -30,11 +30,11 @@
= link_to new_project_path, title: "New project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do
%i.icon-plus
%li
- = link_to profile_path, title: "My profile", class: 'has_bottom_tooltip', 'data-original-title' => 'My profile' do
+ = link_to profile_path, title: "Profile settings", class: 'has_bottom_tooltip', 'data-original-title' => 'Profile settings"' do
%i.icon-user
%li
= link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do
%i.icon-signout
%li
= link_to current_user, class: "profile-pic", id: 'profile-pic' do
- = image_tag gravatar_icon(current_user.email, 26), alt: ''
+ = image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml
new file mode 100644
index 0000000000..3c4bd857c2
--- /dev/null
+++ b/app/views/layouts/_public_head_panel.html.haml
@@ -0,0 +1,22 @@
+%header.navbar.navbar-static-top.navbar-gitlab
+ .navbar-inner
+ .container
+ %div.app_logo
+ %span.separator
+ = link_to public_root_path, class: "home" do
+ %h1 GITLAB
+ %span.separator
+ %h1.project_name
+ - if @project
+ = project_title(@project)
+ - else
+ Public Projects
+
+ %ul.nav
+ %li
+ %a
+ %div.hide.turbolink-spinner
+ %i.icon-refresh.icon-spin
+ Loading...
+ %li
+ = link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in'
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index fb20e8acf9..165a1487a7 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -6,7 +6,12 @@
.container
.content
%center
- = image_tag image_path "login-logo.png"
+ %h1 GitLab
+ %p.light
+ GitLab is open source software to collaborate on code.
+ %br
+ #{link_to "Sign in", new_user_session_path} or browse for #{link_to "public projects", public_projects_path}.
+ %hr
= yield
%center
%h5.cwhite Enterprise Edition
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 7c3acfc398..d44cb975ea 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -2,8 +2,11 @@
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: "Profile" do
%i.icon-home
- = nav_link(path: 'profiles#account') do
- = link_to "Account", account_profile_path
+ = nav_link(controller: :accounts) do
+ = link_to "Account", profile_account_path
+ - unless current_user.ldap_user?
+ = nav_link(controller: :passwords) do
+ = link_to "Password", edit_profile_password_path
= nav_link(controller: :notifications) do
= link_to "Notifications", profile_notifications_path
= nav_link(controller: :keys) do
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
index 7dce0cbeae..f922dcc420 100644
--- a/app/views/layouts/public.html.haml
+++ b/app/views/layouts/public.html.haml
@@ -1,28 +1,11 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Public Projects"
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
+ %body{class: "ui_mars application", :'data-page' => body_data_page}
- if current_user
= render "layouts/head_panel", title: "Public Projects"
- else
- %header.navbar.navbar-static-top.navbar-gitlab
- .navbar-inner
- .container
- %div.app_logo
- %span.separator
- = link_to public_root_path, class: "home" do
- %h1 GITLAB
- %span.separator
- %h1.project_name Public Projects
- %ul.nav
- %li
- %a
- %div.hide.turbolink-spinner
- %i.icon-refresh.icon-spin
- Loading...
- %li
- = link_to "Sign in", new_session_path(:user)
+ = render "layouts/public_head_panel"
.container.navless-container
- .content
- = yield
+ .content= yield
diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml
new file mode 100644
index 0000000000..cfe6a63055
--- /dev/null
+++ b/app/views/layouts/public_projects.html.haml
@@ -0,0 +1,9 @@
+!!! 5
+%html{ lang: "en"}
+ = render "layouts/head", title: @project.name_with_namespace
+ %body{class: "ui_mars application", :'data-page' => body_data_page}
+ = render "layouts/public_head_panel"
+ %nav.main-nav
+ .container= render 'layouts/nav/project'
+ .container
+ .content= yield
diff --git a/app/views/profiles/account.html.haml b/app/views/profiles/account.html.haml
deleted file mode 100644
index 8f43db6624..0000000000
--- a/app/views/profiles/account.html.haml
+++ /dev/null
@@ -1,132 +0,0 @@
-%h3.page-title
- Account settings
-%p.light
- You can change your password, username and private token here.
- - if current_user.ldap_user?
- Some options are unavailable for LDAP accounts
-%hr
-
-
-.row
- .span2
- %ul.nav.nav-pills.nav-stacked.nav-stacked-menu
- %li.active
- = link_to '#tab-token', 'data-toggle' => 'tab' do
- Private Token
- %li
- = link_to '#tab-password', 'data-toggle' => 'tab' do
- Password
-
- - if show_profile_social_tab?
- %li
- = link_to '#tab-social', 'data-toggle' => 'tab' do
- Social Accounts
-
- - if show_profile_username_tab?
- %li
- = link_to '#tab-username', 'data-toggle' => 'tab' do
- Change Username
-
- - if show_profile_remove_tab?
- %li
- = link_to '#tab-remove', 'data-toggle' => 'tab' do
- Remove Account
- .span10
- .tab-content
- .tab-pane.active#tab-token
- %fieldset.update-token
- %legend
- Private token
- %span.cred.pull-right
- keep it secret!
- %div
- = form_for @user, url: reset_private_token_profile_path, method: :put do |f|
- .data
- %p.slead
- Your private token is used to access application resources without authentication.
- %br
- It can be used for atom feeds or the API.
- %p.cgray
- - if current_user.private_token
- = text_field_tag "token", current_user.private_token, class: "input-xxlarge large_text input-xpadding"
- = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token"
- - else
- %span You don`t have one yet. Click generate to fix it.
- = f.submit 'Generate', class: "btn success btn-build-token"
-
- .tab-pane#tab-password
- %fieldset.update-password
- %legend Password
- = form_for @user, url: update_password_profile_path, method: :put do |f|
- %div
- %p.slead After a successful password update you will be redirected to login page where you should login with your new password
- -if @user.errors.any?
- .alert.alert-error
- %ul
- - @user.errors.full_messages.each do |msg|
- %li= msg
- .control-group
- = f.label :password
- .controls= f.password_field :password, required: true
- .control-group
- = f.label :password_confirmation
- .controls
- = f.password_field :password_confirmation, required: true
- .control-group
- .controls
- = f.submit 'Save password', class: "btn btn-save"
-
- - if show_profile_social_tab?
- .tab-pane#tab-social
- %fieldset
- %legend Social Accounts
- .oauth_select_holder
- %p.hint Tip: Click on icon to activate signin with one of the following services
- - enabled_social_providers.each do |provider|
- %span{class: oauth_active_class(provider) }
- = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider)
-
- - if show_profile_username_tab?
- .tab-pane#tab-username
- %fieldset.update-username
- %legend
- Username
- %small.cred.pull-right
- Changing your username can have unintended side effects!
- = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
- %div
- .control-group
- = f.label :username
- .controls
- = f.text_field :username, required: true
-
- %span.loading-gif.hide= image_tag "ajax_loader.gif"
- %span.update-success.cgreen.hide
- %i.icon-ok
- Saved
- %span.update-failed.cred.hide
- %i.icon-remove
- Failed
- %ul.cred
- %li This will change the web URL for personal projects.
- %li This will change the git path to repositories for personal projects.
- .controls
- = f.submit 'Save username', class: "btn btn-save"
-
- - if show_profile_remove_tab?
- .tab-pane#tab-remove
- %fieldset.remove-account
- %legend
- Remove account
- %div
- %p Deleting an account has the following effects:
- %ul
- %li All user content like authored issues, snippets, comments will be removed
- - rp = current_user.personal_projects.count
- - unless rp.zero?
- %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
- - if current_user.solo_owned_groups.present?
- %li
- Next groups will be abandoned. You should transfer or remove them:
- %strong #{current_user.solo_owned_groups.map(&:name).join(', ')}
- = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove"
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
new file mode 100644
index 0000000000..453ef8b60d
--- /dev/null
+++ b/app/views/profiles/accounts/show.html.haml
@@ -0,0 +1,73 @@
+%h3.page-title
+ Account settings
+%p.light
+ You can change your username and private token here.
+ - if current_user.ldap_user?
+ Some options are unavailable for LDAP accounts
+%hr
+
+
+.account-page
+ %fieldset.update-token
+ %legend
+ Private token
+ %div
+ = form_for @user, url: reset_private_token_profile_path, method: :put do |f|
+ .data
+ %p
+ Your private token is used to access application resources without authentication.
+ %br
+ It can be used for atom feeds or the API.
+ %span.cred
+ Keep it secret!
+
+ %p.cgray
+ - if current_user.private_token
+ = text_field_tag "token", current_user.private_token, class: "input-xlarge input-xpadding pull-left"
+ = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token prepend-left-10"
+ - else
+ %span You don`t have one yet. Click generate to fix it.
+ = f.submit 'Generate', class: "btn success btn-build-token"
+
+
+ - if show_profile_social_tab?
+ %fieldset
+ %legend Social Accounts
+ .oauth_select_holder.append-bottom-10
+ %p Click on icon to activate signin with one of the following services
+ - enabled_social_providers.each do |provider|
+ %span{class: oauth_active_class(provider) }
+ = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider)
+
+ - if show_profile_username_tab?
+ %fieldset.update-username
+ %legend
+ Username
+ = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
+ %p
+ Changing your username will change path to all personl projects!
+ %div
+ = f.text_field :username, required: true, class: 'input-xlarge input-xpadding'
+
+ %span.loading-gif.hide= image_tag "ajax_loader.gif"
+ %p.light
+ = user_url(@user)
+ %div
+ = f.submit 'Save username', class: "btn btn-save"
+
+ - if show_profile_remove_tab?
+ %fieldset.remove-account
+ %legend
+ Remove account
+ %div
+ %p Deleting an account has the following effects:
+ %ul
+ %li All user content like authored issues, snippets, comments will be removed
+ - rp = current_user.personal_projects.count
+ - unless rp.zero?
+ %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
+ - if current_user.solo_owned_groups.present?
+ %li
+ Next groups will be abandoned. You should transfer or remove them:
+ %strong #{current_user.solo_owned_groups.map(&:name).join(', ')}
+ = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove"
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
new file mode 100644
index 0000000000..a5fa6e7f18
--- /dev/null
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -0,0 +1,32 @@
+%h3.page-title Password
+%p.light
+ Change your password or recover your current one.
+%hr
+.update-password
+ = form_for @user, url: profile_password_path, method: :put do |f|
+ %div
+ %p.slead
+ You must provide current password in order to change it.
+ %br
+ After a successful password update you will be redirected to login page where you should login with your new password
+ -if @user.errors.any?
+ .alert.alert-error
+ %ul
+ - @user.errors.full_messages.each do |msg|
+ %li= msg
+ .control-group
+ = f.label :current_password
+ .controls
+ = f.password_field :current_password, required: true
+ %div
+ = link_to "Forgot your password?", reset_profile_password_path, method: :put
+
+ .control-group
+ = f.label :password, 'New password'
+ .controls= f.password_field :password, required: true
+ .control-group
+ = f.label :password_confirmation
+ .controls
+ = f.password_field :password_confirmation, required: true
+ .form-actions
+ = f.submit 'Save password', class: "btn btn-save"
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 25bf7912f1..2dce690c8d 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,18 +1,14 @@
-= image_tag gravatar_icon(@user.email, 60), alt: '', class: 'avatar s60'
%h3.page-title
- = @user.name
- %br
- %small
- = @user.email
-
- .pull-right
- = link_to destroy_user_session_path, class: "logout", method: :delete do
- %small
- %i.icon-signout
- Logout
+ Profile settings
+%p.light
+ This information appears on your profile.
+ - if current_user.ldap_user?
+ Some options are unavailable for LDAP accounts
%hr
-= form_for @user, url: profile_path, method: :put, html: { class: "edit_user form-horizontal" } do |f|
+
+
+= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit_user form-horizontal" } do |f|
-if @user.errors.any?
%div.alert.alert-error
%ul
@@ -29,7 +25,12 @@
= f.label :email, class: "control-label"
.controls
= f.text_field :email, class: "input-xlarge", required: true
- %span.help-block We also use email for avatar detection.
+ - if @user.unconfirmed_email.present?
+ %span.help-block
+ We sent confirmation email to
+ %strong #{@user.unconfirmed_email}
+ - else
+ %span.help-block We also use email for avatar detection if no avatar is uploaded.
.control-group
= f.label :skype, class: "control-label"
.controls= f.text_field :skype, class: "input-xlarge"
@@ -46,45 +47,23 @@
%span.help-block Tell us about yourself in fewer than 250 characters.
.span5.pull-right
- %fieldset.tips
- %legend Tips:
- %ul
- %li
- %p You can change your password on the Account page
- - if Gitlab.config.gravatar.enabled
- %li
- %p You can change your avatar at #{link_to "gravatar.com", "http://gravatar.com"}
+ .light-well
+ = image_tag avatar_icon(@user.email, 160), alt: '', class: 'avatar s160'
- - if Gitlab.config.omniauth.enabled && @user.provider?
- %li
- %p
- You can login through #{@user.provider.titleize}!
- = link_to "click here to change", account_profile_path
- - if current_user.can_create_group?
- %li
- %p
- Need a group for several dependent projects?
- = link_to new_group_path, class: "btn btn-tiny" do
- Create a group
- - unless current_user.projects_limit_left > 100
- %fieldset
- %legend
- Personal projects:
- %small.pull-right
- %span= current_user.personal_projects.count
- of
- %span= current_user.projects_limit
- .padded
- .progress
- .bar{style: "width: #{current_user.projects_limit_percent}%;"}
-
- %fieldset
- %legend
- SSH public keys:
- %span.pull-right
- = link_to pluralize(current_user.keys.count, 'key'), profile_keys_path
- .padded
- = link_to "Add Public Key", new_profile_key_path, class: "btn btn-small"
+ .clearfix
+ .profile-avatar-form-option
+ %p.light
+ You can upload an avatar here
+ %br
+ or change it at #{link_to "gravatar.com", "http://gravatar.com"}
+ %hr
+ %a.choose-btn.btn.btn-small.js-choose-user-avatar-button
+ %i.icon-paper-clip
+ %span Choose File ...
+
+ %span.file_name.js-avatar-filename File name...
+ = f.file_field :avatar, class: "js-user-avatar-input hide"
+ %span.help-block The maximum file size allowed is 200KB.
.form-actions
= f.submit 'Save changes', class: "btn btn-save"
diff --git a/app/views/profiles/update_username.js.haml b/app/views/profiles/update_username.js.haml
index abd90269c9..249680bcab 100644
--- a/app/views/profiles/update_username.js.haml
+++ b/app/views/profiles/update_username.js.haml
@@ -1,6 +1,6 @@
- if @user.valid?
:plain
- $('.update-username .update-success').show();
+ new Flash("Username sucessfully changed", "notice")
- else
:plain
- $('.update-username .update-failed').show();
+ new Flash("Username change failed - #{@user.errors.full_messages.first}", "alert")
diff --git a/app/views/projects/_clone_panel.html.haml b/app/views/projects/_clone_panel.html.haml
index 7228c760d2..c2f85e8ebe 100644
--- a/app/views/projects/_clone_panel.html.haml
+++ b/app/views/projects/_clone_panel.html.haml
@@ -5,7 +5,7 @@
.span3.pull-right
.pull-right
- unless @project.empty_repo?
- - if can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
+ - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
- if current_user.already_forked?(@project)
= link_to project_path(current_user.fork_of(@project)), class: 'btn grouped disabled' do
%i.icon-code-fork
@@ -19,37 +19,38 @@
%i.icon-download-alt
%span.only-wide Download
- .dropdown.pull-right
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.icon-plus-sign-alt
- %span.only-wide New
- %b.caret
- %ul.dropdown-menu
- - if @project.issues_enabled && can?(current_user, :write_issue, @project)
- %li
- = link_to url_for_new_issue, title: "New Issue" do
- Issue
- - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project)
- %li
- = link_to new_project_merge_request_path(@project), title: "New Merge Request" do
- Merge Request
- - if @project.snippets_enabled && can?(current_user, :write_snippet, @project)
- %li
- = link_to new_project_snippet_path(@project), title: "New Snippet" do
- Snippet
- - if can? current_user, :push_code, @project
- %li.divider
- %li
- = link_to new_project_branch_path(@project) do
- %i.icon-code-fork
- Git branch
- %li
- = link_to new_project_tag_path(@project) do
- %i.icon-tag
- Git tag
+ - if current_user
+ .dropdown.pull-right
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.icon-plus-sign-alt
+ %span.only-wide New
+ %b.caret
+ %ul.dropdown-menu
+ - if @project.issues_enabled && can?(current_user, :write_issue, @project)
+ %li
+ = link_to url_for_new_issue, title: "New Issue" do
+ Issue
+ - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project)
+ %li
+ = link_to new_project_merge_request_path(@project), title: "New Merge Request" do
+ Merge Request
+ - if @project.snippets_enabled && can?(current_user, :write_snippet, @project)
+ %li
+ = link_to new_project_snippet_path(@project), title: "New Snippet" do
+ Snippet
+ - if can? current_user, :push_code, @project
+ %li.divider
+ %li
+ = link_to new_project_branch_path(@project) do
+ %i.icon-code-fork
+ Git branch
+ %li
+ = link_to new_project_tag_path(@project) do
+ %i.icon-tag
+ Git tag
- - if can?(current_user, :admin_team_member, @project)
- %li.divider
- %li
- = link_to new_project_team_member_path(@project), title: "New project member" do
- Project member
+ - if can?(current_user, :admin_team_member, @project)
+ %li.divider
+ %li
+ = link_to new_project_team_member_path(@project), title: "New project member" do
+ Project member
diff --git a/app/views/projects/blob/_download.html.haml b/app/views/projects/blob/_download.html.haml
index f3da1a2a21..ff317f9020 100644
--- a/app/views/projects/blob/_download.html.haml
+++ b/app/views/projects/blob/_download.html.haml
@@ -1,8 +1,7 @@
-.file-content.blob_file
+.file-content.blob_file.blob-no-preview
%center
= link_to project_raw_path(@project, @id) do
- %div.padded
- %h4
- %i.icon-download-alt
- %br
- Download (#{number_to_human_size blob.size})
+ %h1.light
+ %i.icon-download-alt
+ %h4
+ Download (#{number_to_human_size blob.size})
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index b839252579..4372647a41 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -24,7 +24,7 @@
%p
= link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do
= commit.short_id
- = image_tag gravatar_icon(commit.author_email), class: "avatar s16", alt: ''
+ = image_tag avatar_icon(commit.author_email), class: "avatar s16", alt: ''
%span.light
= gfm escape_once(truncate(commit.title, length: 40))
%span
diff --git a/app/views/projects/commits/_diffs.html.haml b/app/views/projects/commits/_diffs.html.haml
index c51f1b6eff..2e61b9ece1 100644
--- a/app/views/projects/commits/_diffs.html.haml
+++ b/app/views/projects/commits/_diffs.html.haml
@@ -37,9 +37,9 @@
- unless @suppress_diff
- diffs.each_with_index do |diff, i|
- next if diff.diff.empty?
- - file = Gitlab::Git::Blob.new(project.repository, @commit.id, @ref, diff.new_path)
- - file = Gitlab::Git::Blob.new(project.repository, @commit.parent_id, @ref, diff.old_path) unless file.exists?
- - next unless file.exists?
+ - file = project.repository.blob_at(@commit.id, diff.new_path)
+ - file = project.repository.blob_at(@commit.parent_id, diff.old_path) unless file
+ - next unless file
.file{id: "diff-#{i}"}
.header
- if diff.deleted_file
@@ -64,7 +64,7 @@
- if file.text?
= render "projects/commits/text_file", diff: diff, index: i
- elsif file.image?
- - old_file = Gitlab::Git::Blob.new(project.repository, @commit.parent_id, @ref, diff.old_path) if @commit.parent_id
+ - old_file = project.repository.blob_at(@commit.parent_id, diff.old_path) if @commit.parent_id
= render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i
- else
%p.nothing_here_message No preview for this file type
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index 624604142b..c2da9f273b 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -21,7 +21,7 @@
Stats
- - if current_controller?(:commits) && current_user.private_token
+ - if current_user && current_controller?(:commits) && current_user.private_token
%li.pull-right
= link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed" do
%i.icon-rss
diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index 46f9838e84..27c8fa6da7 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => project_commit_url(@project, :id => commit.id)
xml.title truncate(commit.title, :length => 80)
xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(commit.author_email)
+ xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(commit.author_email)
xml.author do |author|
xml.name commit.author_name
xml.email commit.author_email
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 117ee13140..03cd3cbd65 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -67,7 +67,7 @@
- if Project.issues_tracker.values.count > 1
.control-group
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
- .controls= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
+ .controls= f.select(:issues_tracker, project_issues_trackers, {}, { disabled: !@project.issues_enabled })
.control-group
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 001857cefd..b5dffb772a 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -6,7 +6,7 @@
= image_tag "ajax_loader.gif"
%h3 Importing repository.
%p.monospace git clone --bare #{@project.import_url}
- %p Please wait until we import repository for you. Refresh at will.
+ %p Please wait while we import the repository for you. Refresh at will.
:javascript
new ProjectImport();
@@ -16,8 +16,8 @@
%legend Git global setup:
%pre.dark
:preserve
- git config --global user.name "#{current_user.name}"
- git config --global user.email "#{current_user.email}"
+ git config --global user.name "#{git_user_name}"
+ git config --global user.email "#{git_user_email}"
%fieldset
%legend Create Repository
diff --git a/app/views/projects/hooks/_data_ex.html.erb b/app/views/projects/hooks/_data_ex.html.erb
index b4281fa18c..5092aaf675 100644
--- a/app/views/projects/hooks/_data_ex.html.erb
+++ b/app/views/projects/hooks/_data_ex.html.erb
@@ -5,6 +5,7 @@
"ref": "refs/heads/master",
"user_id": 4,
"user_name": "John Smith",
+ "project_id": 15,
"repository": {
"name": "Diaspora",
"url": "git@localhost:diaspora.git",
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index 6acad9134d..05cfb2788b 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -1,5 +1,5 @@
%div.issue-form-holder
- %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}"
+ %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
= form_for [@project, @issue] do |f|
-if @issue.errors.any?
.alert.alert-error
diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml
index 44d14d5cdf..438cc02b47 100644
--- a/app/views/projects/issues/_head.html.haml
+++ b/app/views/projects/issues/_head.html.haml
@@ -5,6 +5,7 @@
= link_to 'Milestones', project_milestones_path(@project), class: "tab"
= nav_link(controller: :labels) do
= link_to 'Labels', project_labels_path(@project), class: "tab"
- %li.pull-right
- = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
- %i.icon-rss
+ - if current_user
+ %li.pull-right
+ = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
+ %i.icon-rss
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index b9a2c18efd..effad718c8 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -6,6 +6,9 @@
.issue-title
%span.light= "##{issue.iid}"
= link_to_gfm truncate(issue.title, length: 100), project_issue_path(issue.project, issue), class: "row_title"
+ - if issue.closed?
+ %small.pull-right
+ = "CLOSED"
.issue-info
- if issue.assignee
diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml
index 539c45edd9..427d653313 100644
--- a/app/views/projects/issues/_issues.html.haml
+++ b/app/views/projects/issues/_issues.html.haml
@@ -52,7 +52,7 @@
- @project.team.members.sort_by(&:name).each do |user|
%li
= link_to project_filter_path(assignee_id: user.id) do
- = image_tag gravatar_icon(user.email), class: "avatar s16", alt: ''
+ = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
.dropdown.inline.prepend-left-10
diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder
index 00ddd4bf70..012ba23595 100644
--- a/app/views/projects/issues/index.atom.builder
+++ b/app/views/projects/issues/index.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => project_issue_url(@project, issue)
xml.title truncate(issue.title, :length => 80)
xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email)
+ xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
xml.author do |author|
xml.name issue.author_name
xml.email issue.author_email
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index d36a831432..d08a8af2bb 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -6,9 +6,10 @@
= @issue.created_at.stamp("Aug 21, 2011")
%span.pull-right
- = link_to new_project_issue_path(@project), class: "btn grouped", title: "New Issue", id: "new_issue_link" do
- %i.icon-plus
- New Issue
+ - if can?(current_user, :write_issue, @project)
+ = link_to new_project_issue_path(@project), class: "btn grouped", title: "New Issue", id: "new_issue_link" do
+ %i.icon-plus
+ New Issue
- if can?(current_user, :modify_issue, @issue)
- if @issue.closed?
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue"
@@ -31,7 +32,7 @@
.ui-box-head
%h4.box-title
- if @issue.closed?
- .error.status_info Closed
+ .state-label.state-label-red Closed
= gfm escape_once(@issue.title)
.ui-box-body
diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml
index 67a1541d9b..839c63986a 100644
--- a/app/views/projects/merge_requests/edit.html.haml
+++ b/app/views/projects/merge_requests/edit.html.haml
@@ -1,4 +1,4 @@
%h3.page-title
- = "Edit merge request ##{@merge_request.id}"
+ = "Edit merge request ##{@merge_request.iid}"
%hr
= render 'form'
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 2bd5a027a0..e256ee2153 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -35,7 +35,7 @@
- @project.team.members.sort_by(&:name).each do |user|
%li
= link_to project_filter_path(assignee_id: user.id) do
- = image_tag gravatar_icon(user.email), class: "avatar s16", alt: ''
+ = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
.dropdown.inline.prepend-left-10
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index 1f750e22c6..93ae602a69 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -3,11 +3,12 @@
%h4.box-title
= gfm escape_once(@merge_request.title)
- if @merge_request.merged?
- .success.status_info
+ .state-label.state-label-green
%i.icon-ok
Merged
- elsif @merge_request.closed?
- .error.status_info Closed
+ .state-label.state-label-red
+ Closed
.ui-box-body
%div
@@ -32,7 +33,7 @@
%span
%i.icon-remove
Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
- %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
+ %span #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
- if @merge_request.merged?
.ui-box-bottom.alert-success
%span
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 78e4cd2243..b2fd1a9284 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,4 +1,4 @@
-%h3.page-title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}"
+%h3.page-title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.iid}"
.back-link
= link_to project_milestones_path(@project) do
← To milestones
diff --git a/app/views/projects/milestones/_issues.html.haml b/app/views/projects/milestones/_issues.html.haml
index bf81cfda45..21939ad013 100644
--- a/app/views/projects/milestones/_issues.html.haml
+++ b/app/views/projects/milestones/_issues.html.haml
@@ -8,4 +8,4 @@
= link_to_gfm truncate(issue.title, length: 40), [@project, issue]
- if issue.assignee
.pull-right
- = image_tag gravatar_icon(issue.assignee.email, 16), class: "avatar s16"
+ = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16"
diff --git a/app/views/projects/milestones/_merge_request.html.haml b/app/views/projects/milestones/_merge_request.html.haml
index 7f81589406..8e30a42a60 100644
--- a/app/views/projects/milestones/_merge_request.html.haml
+++ b/app/views/projects/milestones/_merge_request.html.haml
@@ -1,5 +1,5 @@
%li
= link_to [@project, merge_request] do
- %span.badge.badge-info ##{merge_request.id}
+ %span.badge.badge-info ##{merge_request.iid}
–
= link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request]
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index b755a81341..a5bd7a2901 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,6 +1,6 @@
= render "projects/issues/head"
%h3.page-title
- Milestone ##{@milestone.id}
+ Milestone ##{@milestone.iid}
%small
= @milestone.expires_at
.pull-right
@@ -26,9 +26,9 @@
.ui-box-head
%h4.box-title
- if @milestone.closed?
- .error.status_info Closed
+ .state-label.state-label-red Closed
- elsif @milestone.expired?
- .error.status_info Expired
+ .state-label.state-label-red Expired
= gfm escape_once(@milestone.title)
@@ -99,7 +99,7 @@
- @users.each do |user|
%li
= link_to user, title: user.name, class: "dark" do
- = image_tag gravatar_icon(user.email, 32), class: "avatar s32"
+ = image_tag avatar_icon(user.email, 32), class: "avatar s32"
%strong= truncate(user.name, lenght: 40)
%br
%small.cgray= user.username
diff --git a/app/views/projects/network/show.json.erb b/app/views/projects/network/show.json.erb
index f0bedcf2d3..dc82adcb2c 100644
--- a/app/views/projects/network/show.json.erb
+++ b/app/views/projects/network/show.json.erb
@@ -9,7 +9,7 @@
author: {
name: c.author_name,
email: c.author_email,
- icon: gravatar_icon(c.author_email, 20)
+ icon: avatar_icon(c.author_email, 20)
},
time: c.time,
space: c.spaces.first,
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 0213576927..980b3783a1 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -51,7 +51,7 @@
= f.label :public do
%span Public project
.controls
- = f.check_box :public, { checked: Gitlab.config.gitlab.default_projects_features.public }, true, false
+ = f.check_box :public, { checked: gitlab_config.default_projects_features.public }, true, false
%span.help-inline Make project visible to everyone
.form-actions
diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml
index a964d86a8d..e0aad0cc95 100644
--- a/app/views/projects/notes/_discussion.html.haml
+++ b/app/views/projects/notes/_discussion.html.haml
@@ -8,7 +8,7 @@
= link_to "javascript:;", class: "js-details-target turn-off js-toggler-target" do
%i.icon-eye-open
Show discussion
- = image_tag gravatar_icon(note.author_email), class: "avatar s32"
+ = image_tag avatar_icon(note.author_email), class: "avatar s32"
%div
= link_to_member(@project, note.author, avatar: false)
- if note.for_merge_request?
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index fbc924c4e1..e56e9153c2 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -5,7 +5,7 @@
%i.icon-link
Link here
- - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
+ - if(note.author_id == current_user.try(:id)) || can?(current_user, :admin_note, @project)
= link_to "#", title: "Edit comment", class: "js-note-edit" do
%i.icon-edit
Edit
@@ -13,7 +13,7 @@
= link_to project_note_path(@project, note), title: "Remove comment", method: :delete, confirm: 'Are you sure you want to remove this comment?', remote: true, class: "danger js-note-delete" do
%i.icon-trash.cred
Remove
- = image_tag gravatar_icon(note.author_email), class: "avatar s32"
+ = image_tag avatar_icon(note.author_email), class: "avatar s32"
= link_to_member(@project, note.author, avatar: false)
%span.note-last-update
= note_timestamp(note)
diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml
index faa3ed1746..6e537d2959 100644
--- a/app/views/projects/repositories/_feed.html.haml
+++ b/app/views/projects/repositories/_feed.html.haml
@@ -11,7 +11,7 @@
%div
= link_to project_commits_path(@project, commit.id) do
%code= commit.short_id
- = image_tag gravatar_icon(commit.author_email), class: "", width: 16, alt: ''
+ = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: ''
= gfm escape_once(truncate(commit.title, length: 40))
%td
%span.pull-right.cgray
diff --git a/app/views/projects/repositories/stats.html.haml b/app/views/projects/repositories/stats.html.haml
index 454296e82f..679b4211cd 100644
--- a/app/views/projects/repositories/stats.html.haml
+++ b/app/views/projects/repositories/stats.html.haml
@@ -19,7 +19,7 @@
%ol.styled
- @stats.authors[0...50].each do |author|
%li
- = image_tag gravatar_icon(author.email, 16), class: 'avatar s16', alt: ''
+ = image_tag avatar_icon(author.email, 16), class: 'avatar s16', alt: ''
= author.name
%small.light= author.email
.pull-right
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 06ca5169df..aefcd83395 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -17,7 +17,7 @@
%p
%p
%span.light Repo size is
- #{@project.repository.size} MB
+ = repository_size
%p
%span.light Created at
#{@project.created_at.stamp('Aug 22, 2013')}
diff --git a/app/views/projects/snippets/_form.html.haml b/app/views/projects/snippets/_form.html.haml
index e83f5d0d65..d414ee2d1e 100644
--- a/app/views/projects/snippets/_form.html.haml
+++ b/app/views/projects/snippets/_form.html.haml
@@ -31,10 +31,10 @@
= f.submit 'Create snippet', class: "btn-create btn"
- else
= f.submit 'Save', class: "btn-save btn"
- = link_to "Cancel", project_snippets_path(@project), class: " btn btn-cancel"
- - unless @snippet.new_record?
- .pull-right= link_to 'Destroy', project_snippet_path(@project, @snippet), confirm: 'Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
+ = link_to "Cancel", project_snippets_path(@project), class: "btn btn-cancel"
+ - unless @snippet.new_record?
+ = link_to 'Remove snippet', project_snippet_path(@project, @snippet), confirm: 'Are you sure?', method: :delete, class: "btn pull-right btn-remove delete-snippet prepend-left-10", id: "destroy_snippet_#{@snippet.id}"
:javascript
var editor = ace.edit("editor");
diff --git a/app/views/projects/snippets/_snippet.html.haml b/app/views/projects/snippets/_snippet.html.haml
index fc1c0893b0..6185e35cf6 100644
--- a/app/views/projects/snippets/_snippet.html.haml
+++ b/app/views/projects/snippets/_snippet.html.haml
@@ -16,6 +16,6 @@
= "##{snippet.id}"
%span
by
- = image_tag gravatar_icon(snippet.author_email), class: "avatar avatar-inline s16"
+ = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16"
= snippet.author_name
%span.light #{time_ago_in_words(snippet.created_at)} ago
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index 4a07ebf7fd..ac32f4866b 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -5,7 +5,7 @@
= "##{@snippet.id}"
%span.light
by
- = image_tag gravatar_icon(@snippet.author_email), class: "avatar avatar-inline s16"
+ = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16"
= @snippet.author_name
%div= render 'projects/snippets/blob'
%div#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml
index 916cf2e7a8..5c93e6e3ea 100644
--- a/app/views/projects/team_members/_team_member.html.haml
+++ b/app/views/projects/team_members/_team_member.html.haml
@@ -9,7 +9,7 @@
= link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do
%i.icon-minus.icon-white
- = image_tag gravatar_icon(user.email, 32), class: "avatar s32"
+ = image_tag avatar_icon(user.email, 32), class: "avatar s32"
%p
%strong= user.name
%span.cgray= user.username
diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml
index ec15b608f8..b179ad7d24 100644
--- a/app/views/projects/tree/_blob_item.html.haml
+++ b/app/views/projects/tree/_blob_item.html.haml
@@ -1,7 +1,7 @@
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%td.tree-item-file-name
= tree_icon(type)
- %strong= link_to truncate(blob_item.name, length: 40), project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name))
+ %span= link_to truncate(blob_item.name, length: 40), project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name))
%td.tree_time_ago.cgray
%span.log_loading.hide
Loading commit data...
diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml
index 092a024afb..badc7d992b 100644
--- a/app/views/projects/tree/_submodule_item.html.haml
+++ b/app/views/projects/tree/_submodule_item.html.haml
@@ -1,11 +1,10 @@
-- url = submodule_item.url(@ref) rescue ''
-- name = submodule_item.basename
-- return '' unless url
-%tr{ class: "tree-item", url: url }
+%tr{ class: "tree-item" }
%td.tree-item-file-name
= image_tag "submodule.png"
- %strong= truncate(name, length: 40)
+ %span
+ = link_to truncate(submodule_item.name, length: 40), submodule_item.submodule_url
+ @
+ %span.monospace #{submodule_item.id[0..10]}
+ %td
+ %td
%td
- %code= submodule_item.id[0..10]
- %td{ colspan: 2 }
- = link_to truncate(url, length: 40), url
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 0b1148b3e7..eadfd33bd3 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -26,9 +26,9 @@
= link_to @commit.short_id, project_commit_path(@project, @commit)
–
= truncate(@commit.title, length: 50)
- %th= link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny pull-right"
+ %th= link_to "history", project_commits_path(@project, @id), class: "pull-right"
- - if tree.up_dir?
+ - if @path.present?
%tr.tree-item
%td.tree-item-file-name
= image_tag "file_empty.png", size: '16x16'
diff --git a/app/views/projects/tree/_tree_commit_column.html.haml b/app/views/projects/tree/_tree_commit_column.html.haml
index 7ae2582c13..67b5b2b96e 100644
--- a/app/views/projects/tree/_tree_commit_column.html.haml
+++ b/app/views/projects/tree/_tree_commit_column.html.haml
@@ -1,2 +1,2 @@
-%span.tree_author= commit_author_link(commit, avatar: true)
+%span.tree_author= commit_author_link(commit, avatar: true, size: 16)
= link_to_gfm truncate(commit.title, length: 80), project_commit_path(@project, commit.id), class: "tree-commit-link"
diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml
index 0a76d5c21b..f8856afc86 100644
--- a/app/views/projects/tree/_tree_item.html.haml
+++ b/app/views/projects/tree/_tree_item.html.haml
@@ -1,7 +1,7 @@
%tr{ class: "tree-item #{tree_hex_class(tree_item)}" }
%td.tree-item-file-name
= tree_icon(type)
- %strong= link_to truncate(tree_item.name, length: 40), project_tree_path(@project, tree_join(@id || @commit.id, tree_item.name))
+ %span= link_to truncate(tree_item.name, length: 40), project_tree_path(@project, tree_join(@id || @commit.id, tree_item.name))
%td.tree_time_ago.cgray
%span.log_loading.hide
Loading commit data...
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index dd01bb9904..57ad4c93ad 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -11,7 +11,7 @@
.form-horizontal
.input-prepend.project_clone_holder
%button{class: "btn active", :"data-clone" => @gollum_wiki.ssh_url_to_repo} SSH
- %button{class: "btn", :"data-clone" => @gollum_wiki.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
+ %button{class: "btn", :"data-clone" => @gollum_wiki.http_url_to_repo}= gitlab_config.protocol.upcase
= text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
.git-empty
%fieldset
diff --git a/app/views/public/projects/_tree.html.haml b/app/views/public/projects/_tree.html.haml
deleted file mode 100644
index bd09c236a0..0000000000
--- a/app/views/public/projects/_tree.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- if tree.readme
- = render "projects/tree/readme", readme: tree.readme
-- else
- .alert
- %h3.nothing_here_message This project does not have README file
diff --git a/app/views/public/projects/index.html.haml b/app/views/public/projects/index.html.haml
index 7dbe560e7f..21aee64457 100644
--- a/app/views/public/projects/index.html.haml
+++ b/app/views/public/projects/index.html.haml
@@ -2,29 +2,40 @@
.span6
%h3.page-title
Projects (#{@projects.total_count})
- %small with read-only access
+ .light
+ You can browse public projects in read-only mode until signed in.
+
.span6
.pull-right
= form_tag public_projects_path, method: :get, class: 'form-inline' do |f|
.search-holder
- .controls
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "span3 search-text-input", id: "projects_search"
- = submit_tag 'Search', class: "btn btn-primary wide"
-
+ = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "span3 search-text-input", id: "projects_search"
+ = submit_tag 'Search', class: "btn btn-primary wide"
+%hr
.public-projects
- %ul.bordered-list
+ %ul.bordered-list.top-list
- @projects.each do |project|
%li
- .project-title
- %i.icon-share.cgray
- = link_to public_project_path(project) do
- %strong= project.name_with_namespace
+ %h4
+ = link_to project_path(project) do
+ = project.name_with_namespace
.pull-right
%pre.public-clone git clone #{project.http_url_to_repo}
- if project.description.present?
- %div.description
+ %p
= project.description
+
+ .repo-info
+ - unless project.empty_repo?
+ = link_to pluralize(project.repository.round_commit_count, 'commit'), project_commits_path(project, project.default_branch)
+ ·
+ = link_to pluralize(project.repository.branch_names.count, 'branch'), project_branches_path(project)
+ ·
+ = link_to pluralize(project.repository.tag_names.count, 'tag'), project_tags_path(project)
+ - else
+ %i.icon-warning-sign
+ Empty repository
- unless @projects.present?
%h3.nothing_here_message No public projects
diff --git a/app/views/public/projects/show.html.haml b/app/views/public/projects/show.html.haml
deleted file mode 100644
index c4d8a4f5a5..0000000000
--- a/app/views/public/projects/show.html.haml
+++ /dev/null
@@ -1,46 +0,0 @@
-%h3.page-title
- = @project.name_with_namespace
- .pull-right
- %pre.public-clone git clone #{@project.http_url_to_repo}
- .pull-right
- - if current_user
- = link_to 'Browse project', @project, class: 'btn btn-create append-right-10'
-
-
-%div
- = link_to public_root_path do
- ← To projects list
- .pull-right
- %span.light= @project.description
-
-%br
-.row
- .span9
- = render 'tree', tree: @tree
- .span3
- %h5 Repository:
- %div
- %p
- %span.light Bare size is
- #{@project.repository.size} MB
-
- %p
- = pluralize(@repository.round_commit_count, 'commit')
- %p
- = pluralize(@repository.branch_names.count, 'branch')
- %p
- = pluralize(@repository.tag_names.count, 'tag')
-
- - if @recent_tags.present?
- %hr
- %h5 Most Recent Tags:
- %ul.unstyled
- - @recent_tags.each do |tag|
- %li
- %p
- %i.icon-tag
- %strong= tag.name
- %small.light.pull-right
- %i.icon-calendar
- = time_ago_in_words(tag.commit.committed_date)
- ago
diff --git a/app/views/shared/_project_filter.html.haml b/app/views/shared/_project_filter.html.haml
index 5e0f503c81..f3d032ef98 100644
--- a/app/views/shared/_project_filter.html.haml
+++ b/app/views/shared/_project_filter.html.haml
@@ -1,15 +1,16 @@
= form_tag project_entities_path, method: 'get' do
%fieldset
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if params[:scope].blank?)}
- = link_to project_filter_path(scope: nil) do
- Everyone's
- %li{class: ("active" if params[:scope] == 'assigned-to-me')}
- = link_to project_filter_path(scope: 'assigned-to-me') do
- Assigned to me
- %li{class: ("active" if params[:scope] == 'created-by-me')}
- = link_to project_filter_path(scope: 'created-by-me') do
- Created by me
+ - if current_user
+ %ul.nav.nav-pills.nav-stacked
+ %li{class: ("active" if params[:scope].blank?)}
+ = link_to project_filter_path(scope: nil) do
+ Everyone's
+ %li{class: ("active" if params[:scope] == 'assigned-to-me')}
+ = link_to project_filter_path(scope: 'assigned-to-me') do
+ Assigned to me
+ %li{class: ("active" if params[:scope] == 'created-by-me')}
+ = link_to project_filter_path(scope: 'created-by-me') do
+ Created by me
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if params[:state].blank?)}
diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml
index 9689c9c4d3..8514bc3ddd 100644
--- a/app/views/snippets/_snippet.html.haml
+++ b/app/views/snippets/_snippet.html.haml
@@ -18,6 +18,6 @@
%span
by
= link_to user_snippets_path(snippet.author) do
- = image_tag gravatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: ''
+ = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: ''
= snippet.author_name
%span.light #{time_ago_in_words(snippet.created_at)} ago
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 37f9e7576f..a680e5eb5b 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -17,7 +17,7 @@
%span.light
by
= link_to user_snippets_path(@snippet.author) do
- = image_tag gravatar_icon(@snippet.author_email), class: "avatar avatar-inline s16"
+ = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16"
= @snippet.author_name
.back-link
diff --git a/app/views/snippets/user_index.html.haml b/app/views/snippets/user_index.html.haml
index 49636c3f5f..1cb53ec6a2 100644
--- a/app/views/snippets/user_index.html.haml
+++ b/app/views/snippets/user_index.html.haml
@@ -1,5 +1,5 @@
%h3.page-title
- = image_tag gravatar_icon(@user.email), class: "avatar s24"
+ = image_tag avatar_icon(@user.email), class: "avatar s24"
= @user.name
%span
\/
diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml
index f1b2c8dd7f..a61c6ba5b8 100644
--- a/app/views/users/_projects.html.haml
+++ b/app/views/users/_projects.html.haml
@@ -3,9 +3,4 @@
%ul.well-list
- @projects.each do |project|
%li
- = link_to project_path(project), class: dom_class(project) do
- - if project.namespace
- = project.namespace.human_name
- \/
- %strong.well-title
- = truncate(project.name, length: 45)
+ = link_to_project project
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 743ab0949a..53a0a9232a 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,13 +1,13 @@
.row
.span8
%h3.page-title
- = image_tag gravatar_icon(@user.email, 90), class: "avatar s90", alt: ''
+ = image_tag avatar_icon(@user.email, 90), class: "avatar s90", alt: ''
= @user.name
- if @user == current_user
.pull-right
= link_to profile_path, class: 'btn' do
%i.icon-edit
- Edit Profile
+ Edit Profile settings
%br
%small #{@user.username}
%br
diff --git a/app/views/users_groups/_users_group.html.haml b/app/views/users_groups/_users_group.html.haml
index c8d306838f..5934ff72e8 100644
--- a/app/views/users_groups/_users_group.html.haml
+++ b/app/views/users_groups/_users_group.html.haml
@@ -1,7 +1,7 @@
- user = member.user
- return unless user
%li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)}
- = image_tag gravatar_icon(user.email, 16), class: "avatar s16"
+ = image_tag avatar_icon(user.email, 16), class: "avatar s16"
%strong= user.name
%span.cgray= user.username
- if user == current_user
@@ -10,7 +10,7 @@
%span.pull-right
%strong= member.human_access
- - if show_controls && user != @group.owner && user != current_user
+ - if show_controls && can?(current_user, :manage_group, @group) && current_user != user
= link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do
%i.icon-edit
= link_to group_users_group_path(@group, member), confirm: remove_user_from_group_message(@group, user), method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
@@ -20,4 +20,4 @@
= form_for [@group, member], remote: true do |f|
.alert.prepend-top-20
= f.select :group_access, options_for_select(UsersGroup.group_access_roles, member.group_access)
- = f.submit 'Save', class: 'btn btn-save'
+ = f.submit 'Save', class: 'btn btn-save btn-small'
diff --git a/config/application.rb b/config/application.rb
index 8ac07ef337..d85bcab788 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -70,12 +70,15 @@ module Gitlab
config.assets.version = '1.0'
# Uncomment and customize the last line to run in a non-root path
- # WARNING: This feature is no longer supported
+ # WARNING: This feature is known to work, but unsupported
# Note that three settings need to be changed for this to work.
# 1) In your application.rb file: config.relative_url_root = "/gitlab"
# 2) In your gitlab.yml file: relative_url_root: /gitlab
- # 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT']
+ # 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
#
# config.relative_url_root = "/gitlab"
+
+ # Uncomment to enable rack attack middleware
+ # config.middleware.use Rack::Attack
end
end
diff --git a/config/database.yml.mysql b/config/database.yml.mysql
index a3eff1a74f..e7a9227e41 100644
--- a/config/database.yml.mysql
+++ b/config/database.yml.mysql
@@ -7,7 +7,7 @@ production:
reconnect: false
database: gitlabhq_production
pool: 10
- username: root
+ username: gitlab
password: "secure password"
# host: localhost
# socket: /tmp/mysql.sock
diff --git a/config/database.yml.postgresql b/config/database.yml.postgresql
index 4b74f3348f..66960551cf 100644
--- a/config/database.yml.postgresql
+++ b/config/database.yml.postgresql
@@ -6,8 +6,8 @@ production:
encoding: unicode
database: gitlabhq_production
pool: 10
- username: git
- password:
+ # username: git
+ # password:
# host: localhost
# port: 5432
# socket: /tmp/postgresql.sock
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index bc1ac5319a..5dbef84d1c 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -20,11 +20,11 @@ production: &base
https: false
# Uncomment and customize the last line to run in a non-root path
- # WARNING: This feature is no longer supported
+ # WARNING: This feature is known to work, but unsupported
# Note that three settings need to be changed for this to work.
# 1) In your application.rb file: config.relative_url_root = "/gitlab"
# 2) In your gitlab.yml file: relative_url_root: /gitlab
- # 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT']
+ # 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
#
# relative_url_root: /gitlab
@@ -42,14 +42,24 @@ production: &base
default_projects_limit: 10
# default_can_create_group: false # default: true
# username_changing_enabled: false # default: true - User can change her username/namespace
+ ## Default theme
+ ## BASIC = 1
+ ## MARS = 2
+ ## MODERN = 3
+ ## GRAY = 4
+ ## COLOR = 5
+ # default_theme: 2 # default: 2
+
## Users management
- # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled.
+ # default: false - Account passwords are not sent via the email if signup is enabled.
+ # signup_enabled: true
## Automatic issue closing
- # If a commit message matches this regular express, all issues referenced from the matched text will be closed
- # if it's pushed to a project's default branch.
- # issue_closing_pattern: ^([Cc]loses|[Ff]ixes) +#\d+
+ # If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
+ # This happends when the commit is pushed or merged into the default branch of a project.
+ # When not specified the default issue_closing_pattern as specified below will be used.
+ # issue_closing_pattern: ([Cc]loses|[Ff]ixes) +#\d+
## Default project features settings
default_projects_features:
@@ -63,6 +73,7 @@ production: &base
## External issues trackers
issues_tracker:
# redmine:
+ # title: "Redmine"
# ## If not nil, link 'Issues' on project page will be replaced with this
# ## Use placeholders:
# ## :project_id - GitLab project identifier
@@ -83,6 +94,7 @@ production: &base
# new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new"
#
# jira:
+ # title: "Atlassian Jira"
# project_url: "http://jira.sample/issues/?jql=project=:issues_tracker_id"
# issues_url: "http://jira.sample/browse/:id"
# new_issue_url: "http://jira.sample/secure/CreateIssue.jspa"
@@ -215,6 +227,7 @@ test:
<<: *base
issues_tracker:
redmine:
+ title: "Redmine"
project_url: "http://redmine/projects/:issues_tracker_id"
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index da7a83a62a..942b77ffd2 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -52,6 +52,7 @@ Settings['issues_tracker'] ||= {}
Settings['gitlab'] ||= Settingslogic.new({})
Settings.gitlab['default_projects_limit'] ||= 10
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
+Settings.gitlab['default_theme'] = Gitlab::Theme::MARS if Settings.gitlab['default_theme'].nil?
Settings.gitlab['host'] ||= 'localhost'
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
@@ -68,7 +69,7 @@ rescue ArgumentError # no user configured
end
Settings.gitlab['signup_enabled'] ||= false
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '^([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
diff --git a/config/initializers/5_backend.rb b/config/initializers/5_backend.rb
index e60d9559c9..7c2e7f3900 100644
--- a/config/initializers/5_backend.rb
+++ b/config/initializers/5_backend.rb
@@ -6,6 +6,3 @@ require Rails.root.join("lib", "gitlab", "backend", "shell")
# GitLab shell adapter
require Rails.root.join("lib", "gitlab", "backend", "shell_adapter")
-
-# Gitlab Git repos path
-Gitlab::Git::Repository.repos_path = Gitlab.config.gitlab_shell.repos_path
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 39c1b7c235..b7cb808d2e 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -54,6 +54,8 @@ Devise.setup do |config|
# The realm used in Http Basic Authentication. "Application" by default.
# config.http_authentication_realm = "Application"
+ config.reconfirmable = true
+
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
diff --git a/config/initializers/devise_async.rb b/config/initializers/devise_async.rb
new file mode 100644
index 0000000000..05a1852cdb
--- /dev/null
+++ b/config/initializers/devise_async.rb
@@ -0,0 +1 @@
+Devise::Async.backend = :sidekiq
diff --git a/config/initializers/rack_attack.rb.example b/config/initializers/rack_attack.rb.example
new file mode 100644
index 0000000000..76fa7ad282
--- /dev/null
+++ b/config/initializers/rack_attack.rb.example
@@ -0,0 +1,16 @@
+# To enable rack-attack for your GitLab instance do the following:
+# 1. In config/application.rb find and uncomment the following line:
+# config.middleware.use Rack::Attack
+# 2. Rename this file to rack_attack.rb
+# 3. Review the paths_to_be_protected and add any other path you need protecting
+# 4. Restart GitLab instance
+#
+
+paths_to_be_protected = [
+ "#{Rails.application.config.relative_url_root}/users/password",
+ "#{Rails.application.config.relative_url_root}/users/sign_in",
+ "#{Rails.application.config.relative_url_root}/users"
+]
+Rack::Attack.throttle('protected paths', limit: 6, period: 60.seconds) do |req|
+ req.ip if paths_to_be_protected.include?(req.path) && req.post?
+end
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index 52a099c3e1..501cad4a83 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -1,11 +1,9 @@
# Be sure to restart your server when you modify this file.
-Gitlab::Application.config.session_store :cookie_store, key: '_gitlab_session',
- secure: Gitlab::Application.config.force_ssl,
- httponly: true,
- path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root
-
-# Use the database for sessions instead of the cookie-based default,
-# which shouldn't be used to store highly confidential information
-# (create the session table with "rails generate session_migration")
-# Gitlab::Application.config.session_store :active_record_store
+Gitlab::Application.config.session_store(
+ :redis_store, # Using the cookie_store would enable session replay attacks.
+ key: '_gitlab_session',
+ secure: Gitlab::Application.config.force_ssl,
+ httponly: true,
+ path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root
+)
diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample
index e62ad0f4b7..9ebb1dde6f 100644
--- a/config/initializers/smtp_settings.rb.sample
+++ b/config/initializers/smtp_settings.rb.sample
@@ -1,11 +1,11 @@
# To enable smtp email delivery for your GitLab instance do next:
-# 1. Change config/environments/production.rb to use smtp
-# config.action_mailer.delivery_method = :smtp
-# 2. Rename this file to smtp_settings.rb
-# 3. Edit settings inside this file
-# 4. Restart GitLab instance
+# 1. Rename this file to smtp_settings.rb
+# 2. Edit settings inside this file
+# 3. Restart GitLab instance
#
-if Gitlab::Application.config.action_mailer.delivery_method == :smtp
+if Rails.env.production? do
+ Gitlab::Application.config.action_mailer.delivery_method = :smtp
+
ActionMailer::Base.smtp_settings = {
address: "email.server.com",
port: 456,
diff --git a/config/routes.rb b/config/routes.rb
index 37dc26d36c..3a4b1c12b6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -39,6 +39,7 @@ Gitlab::Application.routes.draw do
get 'help/web_hooks' => 'help#web_hooks'
get 'help/workflow' => 'help#workflow'
get 'help/shortcuts'
+ get 'help/security'
#
# Global snippets
@@ -55,8 +56,6 @@ Gitlab::Application.routes.draw do
#
namespace :public do
resources :projects, only: [:index]
- resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:show]
-
root to: "projects#index"
end
@@ -100,19 +99,21 @@ Gitlab::Application.routes.draw do
#
resource :profile, only: [:show, :update] do
member do
- get :account
get :history
- get :token
get :design
- put :update_password
put :reset_private_token
put :update_username
end
scope module: :profiles do
+ resource :account, only: [:show, :update]
resource :notifications, only: [:show, :update]
- resource :password, only: [:new, :create]
+ resource :password, only: [:new, :create, :edit, :update] do
+ member do
+ put :reset
+ end
+ end
resources :keys
resources :groups, only: [:index] do
member do
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index 5c933df073..e4e1342683 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -8,6 +8,15 @@
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.
+# Uncomment and customize the last line to run in a non-root path
+# WARNING: This feature is known to work, but unsupported
+# Note that three settings need to be changed for this to work.
+# 1) In your application.rb file: config.relative_url_root = "/gitlab"
+# 2) In your gitlab.yml file: relative_url_root: /gitlab
+# 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
+#
+# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
+
# Use at least one worker per core if you're on a dedicated server,
# more will usually help for _short_ waits on databases/caches.
worker_processes 2
diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb
index ecea821139..3e76d76e83 100644
--- a/db/fixtures/development/01_admin.rb
+++ b/db/fixtures/development/01_admin.rb
@@ -11,3 +11,5 @@ User.seed(:id, [
theme_id: Gitlab::Theme::MARS
}
])
+
+User.find(1).confirm!
diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb
index 43178dee25..6fdb163c27 100644
--- a/db/fixtures/development/04_project.rb
+++ b/db/fixtures/development/04_project.rb
@@ -26,6 +26,7 @@ project_urls.each_with_index do |url, i|
name: group_path.titleize,
path: group_path
)
+ group.description = Faker::Lorem.sentence
group.owner = User.first
group.save
end
@@ -35,7 +36,8 @@ project_urls.each_with_index do |url, i|
params = {
import_url: url,
namespace_id: group.id,
- name: project_path.titleize
+ name: project_path.titleize,
+ description: Faker::Lorem.sentence
}
project = Projects::CreateContext.new(User.first, params).execute
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index 31ba77254a..85e309b633 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -11,17 +11,23 @@ Gitlab::Seeder.quiet do
next unless user
user_id = user.id
- Thread.current[:current_user] = user
- Issue.seed(:id, [{
- id: i,
- project_id: project.id,
- author_id: user_id,
- assignee_id: user_id,
- state: ['opened', 'closed'].sample,
- milestone: project.milestones.sample,
- title: Faker::Lorem.sentence(6)
- }])
+ begin
+ Thread.current[:current_user] = user
+
+ Issue.seed(:id, [{
+ id: i,
+ project_id: project.id,
+ author_id: user_id,
+ assignee_id: user_id,
+ state: ['opened', 'closed'].sample,
+ milestone: project.milestones.sample,
+ title: Faker::Lorem.sentence(6),
+ description: Faker::Lorem.sentence
+ }])
+ ensure
+ Thread.current[:current_user] = nil
+ end
print('.')
end
diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb
index 1e61ea2863..75f6e56985 100644
--- a/db/fixtures/development/10_merge_requests.rb
+++ b/db/fixtures/development/10_merge_requests.rb
@@ -17,19 +17,23 @@ Gitlab::Seeder.quiet do
next if branches.uniq.size < 2
user_id = user.id
- Thread.current[:current_user] = user
+ begin
+ Thread.current[:current_user] = user
- MergeRequest.seed(:id, [{
- id: i,
- source_branch: branches.first,
- target_branch: branches.last,
- source_project_id: project.id,
- target_project_id: project.id,
- author_id: user_id,
- assignee_id: user_id,
- milestone: project.milestones.sample,
- title: Faker::Lorem.sentence(6)
- }])
+ MergeRequest.seed(:id, [{
+ id: i,
+ source_branch: branches.first,
+ target_branch: branches.last,
+ source_project_id: project.id,
+ target_project_id: project.id,
+ author_id: user_id,
+ assignee_id: user_id,
+ milestone: project.milestones.sample,
+ title: Faker::Lorem.sentence(6)
+ }])
+ ensure
+ Thread.current[:current_user] = nil
+ end
print('.')
end
end
diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb
index 1b77d94905..a919fad704 100644
--- a/db/fixtures/production/001_admin.rb
+++ b/db/fixtures/production/001_admin.rb
@@ -12,6 +12,7 @@ admin = User.create(
admin.projects_limit = 10000
admin.admin = true
admin.save!
+admin.confirm!
if admin.valid?
puts %q[
diff --git a/db/migrate/20130926081215_change_owner_id_for_group.rb b/db/migrate/20130926081215_change_owner_id_for_group.rb
new file mode 100644
index 0000000000..8f1992c37a
--- /dev/null
+++ b/db/migrate/20130926081215_change_owner_id_for_group.rb
@@ -0,0 +1,9 @@
+class ChangeOwnerIdForGroup < ActiveRecord::Migration
+ def up
+ change_column :namespaces, :owner_id, :integer, null: true
+ end
+
+ def down
+ change_column :namespaces, :owner_id, :integer, null: false
+ end
+end
diff --git a/db/migrate/20131005191208_add_avatar_to_users.rb b/db/migrate/20131005191208_add_avatar_to_users.rb
new file mode 100644
index 0000000000..7b4de37ad7
--- /dev/null
+++ b/db/migrate/20131005191208_add_avatar_to_users.rb
@@ -0,0 +1,5 @@
+class AddAvatarToUsers < ActiveRecord::Migration
+ def change
+ add_column :users, :avatar, :string
+ end
+end
diff --git a/db/migrate/20131009115346_add_confirmable_to_users.rb b/db/migrate/20131009115346_add_confirmable_to_users.rb
new file mode 100644
index 0000000000..249cbe704e
--- /dev/null
+++ b/db/migrate/20131009115346_add_confirmable_to_users.rb
@@ -0,0 +1,15 @@
+class AddConfirmableToUsers < ActiveRecord::Migration
+ def self.up
+ add_column :users, :confirmation_token, :string
+ add_column :users, :confirmed_at, :datetime
+ add_column :users, :confirmation_sent_at, :datetime
+ add_column :users, :unconfirmed_email, :string
+ add_index :users, :confirmation_token, unique: true
+ User.update_all(confirmed_at: Time.now)
+ end
+
+ def self.down
+ remove_column :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
+ remove_column :users, :unconfirmed_email
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 71e8432cca..843a58efae 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20130909132950) do
+ActiveRecord::Schema.define(:version => 20131009115346) do
create_table "deploy_keys_projects", :force => true do |t|
t.integer "deploy_key_id", :null => false
@@ -129,7 +129,7 @@ ActiveRecord::Schema.define(:version => 20130909132950) do
create_table "namespaces", :force => true do |t|
t.string "name", :null => false
t.string "path", :null => false
- t.integer "owner_id", :null => false
+ t.integer "owner_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "type"
@@ -291,10 +291,16 @@ ActiveRecord::Schema.define(:version => 20130909132950) do
t.datetime "password_expires_at"
t.integer "created_by_id"
t.datetime "last_credential_check_at"
+ t.string "avatar"
+ t.string "confirmation_token"
+ t.datetime "confirmed_at"
+ t.datetime "confirmation_sent_at"
+ t.string "unconfirmed_email"
end
add_index "users", ["admin"], :name => "index_users_on_admin"
add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true
+ add_index "users", ["confirmation_token"], :name => "index_users_on_confirmation_token", :unique => true
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["name"], :name => "index_users_on_name"
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
diff --git a/doc/api/README.md b/doc/api/README.md
index 6971e08f01..517a9fae6f 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -96,13 +96,30 @@ curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" --header "SUDO: username" "h
curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" --header "SUDO: 23" "http://example.com/api/v3/projects"
```
-#### Pagination
+## Pagination
When listing resources you can pass the following parameters:
+ `page` (default: `1`) - page number
+ `per_page` (default: `20`, max: `100`) - number of items to list per page
+## id vs iid
+
+When you work with API you may notice two similar fields in api entites: id and iid.
+The main difference between them is scope. Example:
+
+Issue
+ id: 46
+ iid: 5
+
+* id - is uniq across all Issues table. It used for any api calls.
+* iid - is uniq only in scope of single project. When you browse issues or merge requests with Web UI - you see iid.
+
+So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json`
+But when you want to create a link to web page - use `http:://host/project/issues/:iid.json`
+
+
+
## Contents
+ [Users](users.md)
@@ -117,7 +134,7 @@ When listing resources you can pass the following parameters:
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
-+ [User Teams](user_teams.md)
+
## Clients
diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md
index fbb1e45bcc..50d2b82c36 100644
--- a/doc/api/deploy_keys.md
+++ b/doc/api/deploy_keys.md
@@ -20,13 +20,15 @@ Parameters:
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "created_at":"2013-10-02T10:12:29Z"
},
{
"id": 3,
"title" : "Another Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
- soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
+ soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "created_at":"2013-10-02T11:12:29Z"
}
]
```
@@ -51,7 +53,8 @@ Parameters:
"title" : "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
- soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
+ soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "created_at":"2013-10-02T10:12:29Z"
}
```
diff --git a/doc/api/groups.md b/doc/api/groups.md
index cf5a961a88..e3a7d11a6e 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -12,7 +12,6 @@ GET /groups
"id": 1,
"name": "Foobar Group",
"path": "foo-bar",
- "owner_id": 18,
"ldap_cn": "ldap-group-name",
"ldap_access": 30,
}
@@ -58,11 +57,37 @@ POST /groups/:id/projects/:project_id
Parameters:
+ `id` (required) - The ID of a group
-+ `project_id (required) - The ID of a project
++ `project_id` (required) - The ID of a project
+
+
+## Remove group
+
+Removes group with all projects inside.
+
+```
+DELETE /groups/:id
+```
+
+Parameters:
+
++ `id` (required) - The ID of a user group
## Group members
+
+**Group access levels**
+
+The group access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
+
+```
+ GUEST = 10
+ REPORTER = 20
+ DEVELOPER = 30
+ MASTER = 40
+ OWNER = 50
+```
+
### List group members
Get a list of group members viewable by the authenticated user.
@@ -74,22 +99,22 @@ GET /groups/:id/members
```json
[
{
- id: 1,
- username: "raymond_smith",
- email: "ray@smith.org",
- name: "Raymond Smith",
- state: "active",
- created_at: "2012-10-22T14:13:35Z",
- access_level: 30
+ "id": 1,
+ "username": "raymond_smith",
+ "email": "ray@smith.org",
+ "name": "Raymond Smith",
+ "state": "active",
+ "created_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
},
{
- id: 2,
- username: "john_doe",
- email: "joh@doe.org",
- name: "John Doe",
- state: "active",
- created_at: "2012-10-22T14:13:35Z",
- access_level: 30
+ "id": 2,
+ "username": "john_doe",
+ "email": "joh@doe.org",
+ "name": "John Doe",
+ "state": "active",
+ "created_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
}
]
```
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 723c8acf38..ef37940faf 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -11,6 +11,7 @@ GET /issues
[
{
"id": 43,
+ "iid": 3,
"project_id": 8,
"title": "4xx/5xx pages",
"description": "",
@@ -22,15 +23,16 @@ GET /issues
"username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
- "blocked": false,
+ "state": "active",
"created_at": "2012-05-23T08:00:58Z"
},
- "state": 'closed',
+ "state": "closed",
"updated_at": "2012-07-02T17:53:12Z",
"created_at": "2012-07-02T17:53:12Z"
},
{
"id": 42,
+ "iid": 4,
"project_id": 8,
"title": "Add user settings",
"description": "",
@@ -42,7 +44,7 @@ GET /issues
"title": "v1.0",
"description": "",
"due_date": "2012-07-20",
- "state": 'reopenend',
+ "state": "reopenend",
"updated_at": "2012-07-04T13:42:48Z",
"created_at": "2012-07-04T13:42:48Z"
},
@@ -51,7 +53,7 @@ GET /issues
"username": "jack_smith",
"email": "jack@example.com",
"name": "Jack Smith",
- "blocked": false,
+ "state": "active",
"created_at": "2012-05-23T08:01:01Z"
},
"author": {
@@ -59,10 +61,10 @@ GET /issues
"username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
- "blocked": false,
+ "state": "active",
"created_at": "2012-05-23T08:00:58Z"
},
- "state": 'opened',
+ "state": "opened",
"updated_at": "2012-07-12T13:43:19Z",
"created_at": "2012-06-28T12:58:06Z"
}
@@ -100,6 +102,7 @@ Parameters:
```json
{
"id": 42,
+ "iid": 3,
"project_id": 8,
"title": "Add user settings",
"description": "",
@@ -111,7 +114,7 @@ Parameters:
"title": "v1.0",
"description": "",
"due_date": "2012-07-20",
- "state": 'closed',
+ "state": "closed",
"updated_at": "2012-07-04T13:42:48Z",
"created_at": "2012-07-04T13:42:48Z"
},
@@ -120,7 +123,7 @@ Parameters:
"username": "jack_smith",
"email": "jack@example.com",
"name": "Jack Smith",
- "blocked": false,
+ "state": "active",
"created_at": "2012-05-23T08:01:01Z"
},
"author": {
@@ -128,10 +131,10 @@ Parameters:
"username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
- "blocked": false,
+ "state": "active",
"created_at": "2012-05-23T08:00:58Z"
},
- "state": 'opened',
+ "state": "opened",
"updated_at": "2012-07-12T13:43:19Z",
"created_at": "2012-06-28T12:58:06Z"
}
@@ -190,4 +193,3 @@ Parameters:
+ `id` (required) - The project ID
+ `issue_id` (required) - The ID of the issue
-
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 111c52112e..2ddaea5a58 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -15,18 +15,20 @@ Parameters:
[
{
"id":1,
+ "iid":1,
"target_branch":"master",
"source_branch":"test1",
"project_id":3,
"title":"test1",
- "closed":true,
- "merged":false,
+ "state":"opened",
+ "upvotes":0,
+ "downvotes":0,
"author":{
"id":1,
"username": "admin",
"email":"admin@local.host",
"name":"Administrator",
- "blocked":false,
+ "state":"active",
"created_at":"2012-04-29T08:46:00Z"
},
"assignee":{
@@ -34,7 +36,7 @@ Parameters:
"username": "admin",
"email":"admin@local.host",
"name":"Administrator",
- "blocked":false,
+ "state":"active",
"created_at":"2012-04-29T08:46:00Z"
}
}
@@ -58,18 +60,20 @@ Parameters:
```json
{
"id":1,
+ "iid":1,
"target_branch":"master",
"source_branch":"test1",
"project_id":3,
"title":"test1",
- "closed":true,
- "merged":false,
+ "state":"merged",
+ "upvotes":0,
+ "downvotes":0,
"author":{
"id":1,
"username": "admin",
"email":"admin@local.host",
"name":"Administrator",
- "blocked":false,
+ "state":"active",
"created_at":"2012-04-29T08:46:00Z"
},
"assignee":{
@@ -77,7 +81,7 @@ Parameters:
"username": "admin",
"email":"admin@local.host",
"name":"Administrator",
- "blocked":false,
+ "state":"active",
"created_at":"2012-04-29T08:46:00Z"
}
}
@@ -97,7 +101,7 @@ Parameters:
+ `id` (required) - The ID of a project
+ `source_branch` (required) - The source branch
+ `target_branch` (required) - The target branch
-+ `assignee_id` - Assignee user ID
++ `assignee_id` (optional) - Assignee user ID
+ `title` (required) - Title of MR
```json
@@ -107,14 +111,15 @@ Parameters:
"source_branch":"test1",
"project_id":3,
"title":"test1",
- "closed":true,
- "merged":false,
+ "state":"opened",
+ "upvotes":0,
+ "downvotes":0,
"author":{
"id":1,
"username": "admin",
"email":"admin@local.host",
"name":"Administrator",
- "blocked":false,
+ "state":"active",
"created_at":"2012-04-29T08:46:00Z"
},
"assignee":{
@@ -122,7 +127,7 @@ Parameters:
"username": "admin",
"email":"admin@local.host",
"name":"Administrator",
- "blocked":false,
+ "state":"active",
"created_at":"2012-04-29T08:46:00Z"
}
}
@@ -145,24 +150,24 @@ Parameters:
+ `target_branch` - The target branch
+ `assignee_id` - Assignee user ID
+ `title` - Title of MR
-+ `closed` - Status of MR. true - closed
-
```json
+
{
"id":1,
"target_branch":"master",
"source_branch":"test1",
"project_id":3,
"title":"test1",
- "closed":true,
- "merged":false,
+ "state":"opened",
+ "upvotes":0,
+ "downvotes":0,
"author":{
"id":1,
"username": "admin",
"email":"admin@local.host",
"name":"Administrator",
- "blocked":false,
+ "state":"active",
"created_at":"2012-04-29T08:46:00Z"
},
"assignee":{
@@ -170,7 +175,7 @@ Parameters:
"username": "admin",
"email":"admin@local.host",
"name":"Administrator",
- "blocked":false,
+ "state":"active",
"created_at":"2012-04-29T08:46:00Z"
}
}
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index aa8f1bf5e0..e899e28d21 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -6,6 +6,22 @@ Returns a list of project milestones.
GET /projects/:id/milestones
```
+```json
+[
+ {
+ "id":12,
+ "iid":3,
+ "project_id":16,
+ "title":"10.0",
+ "description":"Version",
+ "due_date":"2013-11-29",
+ "state":"active",
+ "updated_at":"2013-10-02T09:24:18Z",
+ "created_at":"2013-10-02T09:24:18Z"
+ }
+]
+```
+
Parameters:
+ `id` (required) - The ID of a project
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 4b57f636a0..397aa87aad 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -8,17 +8,22 @@ Get a list of project wall notes.
GET /projects/:id/notes
```
+Parameters:
+
++ `id` (required) - The ID of a project
+
```json
[
{
"id": 522,
"body": "The solution is rather tricky",
+ "attachment":null,
"author": {
"id": 1,
"username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
- "blocked": false,
+ "state": "active",
"created_at": "2012-05-23T08:00:58Z"
},
"created_at": "2012-11-27T19:16:44Z"
@@ -26,11 +31,6 @@ GET /projects/:id/notes
]
```
-Parameters:
-
-+ `id` (required) - The ID of a project
-
-
### Get single wall note
Returns a single wall note.
@@ -74,6 +74,38 @@ Parameters:
+ `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of an issue
+```json
+[
+ {
+ "id":302,
+ "body":"_Status changed to closed_",
+ "attachment":null,
+ "author":{
+ "id":1,
+ "username":"pipin",
+ "email":"admin@example.com",
+ "name":"Pip",
+ "state":"active",
+ "created_at":"2013-09-30T13:46:01Z"
+ },
+ "created_at":"2013-10-02T09:22:45Z"
+ },
+ {
+ "id":305,
+ "body":"Text of the comment\r\n",
+ "attachment":null,
+ "author":{
+ "id":1,
+ "username":"pipin",
+ "email":"admin@example.com",
+ "name":"Pip",
+ "state":"active",
+ "created_at":"2013-09-30T13:46:01Z"
+ },
+ "created_at":"2013-10-02T09:56:03Z"
+ }
+]
+```
### Get single issue note
@@ -135,6 +167,24 @@ Parameters:
+ `snippet_id` (required) - The ID of a project snippet
+ `note_id` (required) - The ID of an snippet note
+```json
+{
+ "id":52,
+ "title":"Snippet",
+ "file_name":"snippet.rb",
+ "author":{
+ "id":1,
+ "username":"pipin",
+ "email":"admin@example.com",
+ "name":"Pip",
+ "state":"active",
+ "created_at":"2013-09-30T13:46:01Z"
+ },
+ "expires_at":null,
+ "updated_at":"2013-10-02T07:34:20Z",
+ "created_at":"2013-10-02T07:34:20Z"
+}
+```
### Create new snippet note
@@ -181,6 +231,22 @@ Parameters:
+ `merge_request_id` (required) - The ID of a project merge request
+ `note_id` (required) - The ID of a merge request note
+```json
+{
+ "id":301,
+ "body":"Comment for MR",
+ "attachment":null,
+ "author":{
+ "id":1,
+ "username":"pipin",
+ "email":"admin@example.com",
+ "name":"Pip",
+ "state":"active",
+ "created_at":"2013-09-30T13:46:01Z"
+ },
+ "created_at":"2013-10-02T08:57:14Z"
+}
+```
### Create new merge request note
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index 04ea367d51..f7b7fc8fbb 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -34,7 +34,7 @@ Parameters:
"username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
- "blocked": false,
+ "state": "active",
"created_at": "2012-05-23T08:00:58Z"
},
"expires_at": null,
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 9fbc9a3eb3..90dd39231b 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -11,51 +11,72 @@ GET /projects
```json
[
{
- "id": 3,
- "name": "rails",
+ "id": 4,
"description": null,
"default_branch": "master",
+ "public": false,
+ "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
+ "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
+ "web_url": "http://example.com/diaspora/diaspora-client",
"owner": {
- "id": 1,
- "username": "john_smith",
- "email": "john@example.com",
- "name": "John Smith",
- "blocked": false,
- "created_at": "2012-05-23T08:00:58Z"
+ "id": 3,
+ "name": "Diaspora",
+ "created_at": "2013-09-30T13: 46: 02Z"
},
- "public": true,
- "path": "rails",
- "path_with_namespace": "rails/rails",
- "issues_enabled": false,
- "merge_requests_enabled": false,
- "wall_enabled": true,
- "wiki_enabled": true,
- "created_at": "2012-05-23T08:05:02Z",
- "last_activity_at": "2012-05-23T08:05:02Z"
- },
- {
- "id": 5,
- "name": "gitlab",
- "description": null,
- "default_branch": "api",
- "owner": {
- "id": 1,
- "username": "john_smith",
- "email": "john@example.com",
- "name": "John Smith",
- "blocked": false,
- "created_at": "2012-05-23T08:00:58Z"
- },
- "public": true,
- "path": "gitlab",
- "path_with_namespace": "randx/gitlab",
+ "name": "Diaspora Client",
+ "name_with_namespace": "Diaspora / Diaspora Client",
+ "path": "diaspora-client",
+ "path_with_namespace": "diaspora/diaspora-client",
"issues_enabled": true,
"merge_requests_enabled": true,
- "wall_enabled": true,
+ "wall_enabled": false,
"wiki_enabled": true,
- "snippets_enabled": true,
- "created_at": "2012-05-30T12:49:20Z",
- "last_activity_at": "2012-05-23T08:05:02Z"
+ "snippets_enabled": false,
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "last_activity_at": "2013-09-30T13: 46: 02Z",
+ "namespace": {
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "description": "",
+ "id": 3,
+ "name": "Diaspora",
+ "owner_id": 1,
+ "path": "diaspora",
+ "updated_at": "2013-09-30T13: 46: 02Z"
+ }
+ },
+ {
+ "id": 6,
+ "description": null,
+ "default_branch": "master",
+ "public": false,
+ "ssh_url_to_repo": "git@example.com:brightbox/puppet.git",
+ "http_url_to_repo": "http://example.com/brightbox/puppet.git",
+ "web_url": "http://example.com/brightbox/puppet",
+ "owner": {
+ "id": 4,
+ "name": "Brightbox",
+ "created_at": "2013-09-30T13:46:02Z"
+ },
+ "name": "Puppet",
+ "name_with_namespace": "Brightbox / Puppet",
+ "path": "puppet",
+ "path_with_namespace": "brightbox/puppet",
+ "issues_enabled": true,
+ "merge_requests_enabled": true,
+ "wall_enabled": false,
+ "wiki_enabled": true,
+ "snippets_enabled": false,
+ "created_at": "2013-09-30T13:46:02Z",
+ "last_activity_at": "2013-09-30T13:46:02Z",
+ "namespace": {
+ "created_at": "2013-09-30T13:46:02Z",
+ "description": "",
+ "id": 4,
+ "name": "Brightbox",
+ "owner_id": 1,
+ "path": "brightbox",
+ "updated_at": "2013-09-30T13:46:02Z"
+ }
}
]
```
@@ -63,8 +84,8 @@ GET /projects
### Get single project
-Get a specific project, identified by project ID or NAME, which is owned by the authentication user.
-Currently namespaced projects cannot retrieved by name.
+Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user.
+If using namespaced projects call make sure that the NAMESPACE/PROJECT_NAME is URL-encoded, eg. `/api/v3/projects/diaspora%2Fdiaspora` (where `/` is represented by `%2F`).
```
GET /projects/:id
@@ -72,33 +93,42 @@ GET /projects/:id
Parameters:
-+ `id` (required) - The ID or NAME of a project
++ `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
```json
{
- "id": 5,
- "name": "gitlab",
- "name_with_namespace": "GitLab / gitlabhq",
+ "id": 3,
"description": null,
- "default_branch": "api",
+ "default_branch": "master",
+ "public": false,
+ "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
+ "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
+ "web_url": "http://example.com/diaspora/diaspora-project-site",
"owner": {
- "id": 1,
- "username": "john_smith",
- "email": "john@example.com",
- "name": "John Smith",
- "blocked": false,
- "created_at": "2012-05-23T08:00:58Z"
+ "id": 3,
+ "name": "Diaspora",
+ "created_at": "2013-09-30T13: 46: 02Z"
},
- "public": true,
- "path": "gitlab",
- "path_with_namespace": "randx/gitlab",
+ "name": "Diaspora Project Site",
+ "name_with_namespace": "Diaspora / Diaspora Project Site",
+ "path": "diaspora-project-site",
+ "path_with_namespace": "diaspora/diaspora-project-site",
"issues_enabled": true,
"merge_requests_enabled": true,
- "wall_enabled": true,
+ "wall_enabled": false,
"wiki_enabled": true,
- "snippets_enabled": true,
- "created_at": "2012-05-30T12:49:20Z",
- "last_activity_at": "2012-05-23T08:05:02Z"
+ "snippets_enabled": false,
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "last_activity_at": "2013-09-30T13: 46: 02Z",
+ "namespace": {
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "description": "",
+ "id": 3,
+ "name": "Diaspora",
+ "owner_id": 1,
+ "path": "diaspora",
+ "updated_at": "2013-09-30T13: 46: 02Z"
+ }
}
```
@@ -191,17 +221,6 @@ Parameters:
+ `snippets_enabled` (optional)
+ `public` (optional)
-**Project access levels**
-
-The project access levels are defined in the `user_project.rb` class. Currently, these levels are recognized:
-
-```
- GUEST = 10
- REPORTER = 20
- DEVELOPER = 30
- MASTER = 40
-```
-
### Create project for user
@@ -225,6 +244,18 @@ Parameters:
+ `public` (optional)
+## Remove project
+
+Removes project with all resources(issues, merge requests etc)
+
+```
+DELETE /projects/:id
+```
+
+Parameters:
+
++ `id` (required) - The ID of a project
+
## Team members
@@ -261,7 +292,7 @@ Parameters:
"username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
- "blocked": false,
+ "state": "active",
"created_at": "2012-05-23T08:00:58Z",
"access_level": 40
}
@@ -417,6 +448,55 @@ Parameters:
+ `id` (required) - The ID of the project
+```json
+[
+ {
+ "name":"async",
+ "commit": {
+ "id":"a2b702edecdf41f07b42653eb1abe30ce98b9fca",
+ "parents": [{
+ "id":"3f94fc7c85061973edc9906ae170cc269b07ca55"
+ }],
+ "tree": "c68537c6534a02cc2b176ca1549f4ffa190b58ee",
+ "message":"give caolan his credit where it's due (up top)",
+ "author": {
+ "name":"Jeremy Ashkenas",
+ "email":"jashkenas@example.com"
+ },
+ "committer": {
+ "name":"Jeremy Ashkenas",
+ "email":"jashkenas@example.com"
+ },
+ "authored_date":"2010-12-08T21:28:50+00:00",
+ "committed_date":"2010-12-08T21:28:50+00:00"
+ },
+ "protected":false
+ },
+ {
+ "name": "gh-pages",
+ "commit": {
+ "id": "101c10a60019fe870d21868835f65c25d64968fc",
+ "parents": [{
+ "id": "9c15d2e26945a665131af5d7b6d30a06ba338aaa"
+ }],
+ "tree": "fb5cc9d45da3014b17a876ad539976a0fb9b352a",
+ "message": "Underscore.js 1.5.2",
+ "author": {
+ "name": "Jeremy Ashkenas",
+ "email": "jashkenas@example.com"
+ },
+ "committer": {
+ "name": "Jeremy Ashkenas",
+ "email": "jashkenas@example.com"
+ },
+ "authored_date": "2013-09-07T12: 58: 21+00: 00",
+ "committed_date": "2013-09-07T12: 58: 21+00: 00"
+ },
+ "protected": false
+ }
+]
+
+```
### List single branch
@@ -495,9 +575,22 @@ You must be a master or owner of project in order to perform action
```
POST /projects/:id/share
```
-
Parameters:
+ `id` (required) - The ID of a project
+ `group_id` (required) - The ID of a group to share with
+ `group_access` (required) - Group access level
+
+## Search for projects by name
+
+Search for projects by name which are public or the calling user has access to
+
+```
+GET /projects/search/:query
+```
+
+Parameters:
+
++ query (required) - A string contained in the project name
++ per_page (optional) - number of projects to return per page
++ page (optional) - the page to retrieve
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index cb0626972e..2769c22d6a 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -356,3 +356,16 @@ Parameters:
+ `id` (required) - The ID of a project
+ `sha` (required) - The commit or branch name
+ `filepath` (required) - The path the file
+
+
+## Get file archive
+
+Get a an archive of the repository
+
+```
+GET /projects/:id/repository/archive
+```
+
+Parameters:
++ `id` (required) - The ID of a project
++ `sha` (optional) - The commit sha to download defaults to the tip of the default branch
\ No newline at end of file
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index dca22c43f8..355ce31c12 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -1,5 +1,7 @@
All methods require admin authorization.
+The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).
+
## List system hooks
Get list of system hooks
@@ -12,6 +14,15 @@ Parameters:
+ **none**
+```json
+[
+ {
+ "id":3,
+ "url":"http://example.com/hook",
+ "created_at":"2013-10-02T10:15:31Z"
+ }
+]
+```
## Add new system hook hook
@@ -34,6 +45,16 @@ Parameters:
+ `id` (required) - The ID of hook
+```json
+{
+ "event_name":"project_create",
+ "name":"Ruby",
+ "path":"ruby",
+ "project_id":1,
+ "owner_name":"Someone",
+ "owner_email":"example@gitlabhq.com"
+}
+```
## Delete system hook
diff --git a/doc/api/user_teams.md b/doc/api/user_teams.md
deleted file mode 100644
index cf467a5466..0000000000
--- a/doc/api/user_teams.md
+++ /dev/null
@@ -1,209 +0,0 @@
-## User teams
-
-### List user teams
-
-Get a list of user teams viewable by the authenticated user.
-
-```
-GET /user_teams
-```
-
-```json
-[
- {
- id: 1,
- name: "User team 1",
- path: "user_team1",
- owner_id: 1
- },
- {
- id: 2,
- name: "User team 2",
- path: "user_team2",
- owner_id: 1
- }
-]
-```
-
-
-### Get single user team
-
-Get a specific user team, identified by user team ID, which is viewable by the authenticated user.
-
-```
-GET /user_teams/:id
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user_team
-
-```json
-{
- id: 1,
- name: "User team 1",
- path: "user_team1",
- owner_id: 1
-}
-```
-
-
-### Create user team
-
-Creates new user team owned by user. Available only for admins.
-
-```
-POST /user_teams
-```
-
-Parameters:
-
-+ `name` (required) - new user team name
-+ `path` (required) - new user team internal name
-
-
-
-## User team members
-
-### List user team members
-
-Get a list of project team members.
-
-```
-GET /user_teams/:id/members
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user_team
-
-
-### Get user team member
-
-Gets a user team member.
-
-```
-GET /user_teams/:id/members/:user_id
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user_team
-+ `user_id` (required) - The ID of a user
-
-```json
-{
- id: 2,
- username: "john_doe",
- email: "joh@doe.org",
- name: "John Doe",
- state: "active",
- created_at: "2012-10-22T14:13:35Z",
- access_level: 30
-}
-```
-
-
-### Add user team member
-
-Adds a user to a user team.
-
-```
-POST /user_teams/:id/members
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user team
-+ `user_id` (required) - The ID of a user to add
-+ `access_level` (required) - Project access level
-
-
-### Remove user team member
-
-Removes user from user team.
-
-```
-DELETE /user_teams/:id/members/:user_id
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user team
-+ `user_id` (required) - The ID of a team member
-
-## User team projects
-
-### List user team projects
-
-Get a list of project team projects.
-
-```
-GET /user_teams/:id/projects
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user_team
-
-
-### Get user team project
-
-Gets a user team project.
-
-```
-GET /user_teams/:id/projects/:project_id
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user_team
-+ `project_id` (required) - The ID of a user
-
-```json
-{
- id: 12,
- name: "project1",
- description: null,
- default_branch: "develop",
- public: false,
- path: "project1",
- path_with_namespace: "group1/project1",
- issues_enabled: false,
- merge_requests_enabled: true,
- wall_enabled: true,
- wiki_enabled: false,
- created_at: "2013-03-11T12:59:08Z",
- greatest_access_level: 30
-}
-```
-
-
-### Add user team project
-
-Adds a project to a user team.
-
-```
-POST /user_teams/:id/projects
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user team
-+ `project_id` (required) - The ID of a project to add
-+ `greatest_access_level` (required) - Maximum project access level
-
-
-### Remove user team project
-
-Removes project from user team.
-
-```
-DELETE /user_teams/:id/projects/:project_id
-```
-
-Parameters:
-
-+ `id` (required) - The ID of a user team
-+ `project_id` (required) - The ID of a team project
-
diff --git a/doc/api/users.md b/doc/api/users.md
index 49afbab8c6..16479ea6e0 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -23,7 +23,9 @@ GET /users
"extern_uid": "john.smith",
"provider": "provider_name",
"theme_id": 1,
- "color_scheme_id": 2
+ "color_scheme_id": 2,
+ "is_admin": false,
+ "can_create_group": true
},
{
"id": 2,
@@ -39,7 +41,10 @@ GET /users
"extern_uid": "jack.smith",
"provider": "provider_name",
"theme_id": 1,
- "color_scheme_id": 3
+ "color_scheme_id": 3,
+ "is_admin": false,
+ "can_create_group": true,
+ "can_create_project": true
}
]
```
@@ -72,7 +77,10 @@ Parameters:
"extern_uid": "john.smith",
"provider": "provider_name",
"theme_id": 1,
- "color_scheme_id": 2
+ "color_scheme_id": 2,
+ "is_admin": false,
+ "can_create_group": true,
+ "can_create_project": true
}
```
@@ -87,17 +95,19 @@ POST /users
Parameters:
-+ `email` (required) - Email
-+ `password` (required) - Password
-+ `username` (required) - Username
-+ `name` (required) - Name
-+ `skype` (optional) - Skype ID
-+ `linkedin` (optional) - Linkedin
-+ `twitter` (optional) - Twitter account
-+ `projects_limit` (optional) - Number of projects user can create
-+ `extern_uid` (optional) - External UID
-+ `provider` (optional) - External provider name
-+ `bio` (optional) - User's bio
++ `email` (required) - Email
++ `password` (required) - Password
++ `username` (required) - Username
++ `name` (required) - Name
++ `skype` (optional) - Skype ID
++ `linkedin` (optional) - Linkedin
++ `twitter` (optional) - Twitter account
++ `projects_limit` (optional) - Number of projects user can create
++ `extern_uid` (optional) - External UID
++ `provider` (optional) - External provider name
++ `bio` (optional) - User's bio
++ `admin` (optional) - User is admin - true or false (default)
++ `can_create_group` (optional) - User can create groups - true or false
## User modification
@@ -121,6 +131,8 @@ Parameters:
+ `extern_uid` - External UID
+ `provider` - External provider name
+ `bio` - User's bio
++ `admin` (optional) - User is admin - true or false (default)
++ `can_create_group` (optional) - User can create groups - true or false
Note, at the moment this method does only return a 404 error, even in cases where a 409 (Conflict) would
be more appropriate, e.g. when renaming the email address to some existing one.
@@ -166,7 +178,6 @@ GET /user
"color_scheme_id": 2,
"is_admin": false,
"can_create_group" : true,
- "can_create_team" : true,
"can_create_project" : true
}
```
diff --git a/doc/install/databases.md b/doc/install/databases.md
index 5ec1d0c652..be7bc0aad2 100644
--- a/doc/install/databases.md
+++ b/doc/install/databases.md
@@ -14,6 +14,9 @@ GitLab supports the following databases:
# Pick a database root password (can be anything), type it and press enter
# Retype the database root password and press enter
+ # Secure your installation.
+ sudo mysql_secure_installation
+
# Login to MySQL
mysql -u root -p
@@ -55,7 +58,7 @@ GitLab supports the following databases:
sudo -u postgres psql -d template1
# Create a user for GitLab. (change $password to a real password)
- template1=# CREATE USER git WITH PASSWORD '$password';
+ template1=# CREATE USER git;
# Create the GitLab production database & grant all privileges on database
template1=# CREATE DATABASE gitlabhq_production OWNER git;
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 1034656ff7..389886d76d 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -52,7 +52,7 @@ If you are not familiar with vim please skip this and keep using the default edi
Install the required packages:
- sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl git-core openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev
+ sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl git-core openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate
Make sure you have the right version of Python installed.
@@ -63,7 +63,7 @@ Make sure you have the right version of Python installed.
python --version
# If it's Python 3 you might need to install Python 2 separately
- sudo apt-get install python2.7
+ sudo apt-get install -y python2.7
# Make sure you can access Python via python2
python2 --version
@@ -72,7 +72,7 @@ Make sure you have the right version of Python installed.
sudo ln -s /usr/bin/python /usr/bin/python2
# For reStructuredText markup language support install required package:
- sudo apt-get install python-docutils
+ sudo apt-get install -y python-docutils
**Note:** In order to receive mail notifications, make sure to install a
mail server. By default, Debian is shipped with exim4 whereas Ubuntu
@@ -86,14 +86,14 @@ Then select 'Internet Site' and press enter to confirm the hostname.
Remove the old Ruby 1.8 if present
- sudo apt-get remove -y ruby1.8
+ sudo apt-get remove ruby1.8
Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby
curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p247.tar.gz | tar xz
cd ruby-2.0.0-p247
- ./configure
+ ./configure --disable-install-rdoc
make
sudo make install
@@ -153,10 +153,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
cd /home/git/gitlab
# Checkout to stable release
- sudo -u git -H git checkout 6-1-stable-ee
+ sudo -u git -H git checkout 6-2-stable-ee
**Note:**
-You can change `6-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+You can change `6-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
## Configure it
@@ -195,6 +195,13 @@ You can change `6-1-stable` to `master` if you want the *bleeding edge* version,
# Ex. change amount of workers to 3 for 2GB RAM server
sudo -u git -H editor config/unicorn.rb
+ # Copy the example Rack attack config
+ sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
+
+ # Enable rack attack middleware
+ # Find and uncomment the line 'config.middleware.use Rack::Attack'
+ sudo -u git -H editor config/application.rb
+
# Configure Git global settings for git user, useful when editing via web
# Edit user.email according to what is set in gitlab.yml
sudo -u git -H git config --global user.name "GitLab"
@@ -209,18 +216,18 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup.
# Mysql
sudo -u git cp config/database.yml.mysql config/database.yml
+ # Make sure to update username/password in config/database.yml.
+ # You only need to adapt the production settings (first part).
+ # If you followed the database guide then please do as follows:
+ # Change 'secure password' with the value you have given to $password
+ # You can keep the double quotes around the password
+ sudo -u git -H editor config/database.yml
+
or
# PostgreSQL
sudo -u git cp config/database.yml.postgresql config/database.yml
- # Make sure to update username/password in config/database.yml.
- # You only need to adapt the production settings (first part).
- # If you followed the database guide then please do as follows:
- # Change 'root' to 'gitlab'
- # Change 'secure password' with the value you have given to $password
- # You can keep the double quotes around the password
- sudo -u git -H editor config/database.yml
# Make config/database.yml readable to git only
sudo -u git -H chmod o-rwx config/database.yml
@@ -258,6 +265,9 @@ Make GitLab start on boot:
sudo update-rc.d gitlab defaults 21
+## Set up logrotate
+
+ sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
## Check Application Status
@@ -337,6 +347,12 @@ a different host, you can configure its connection string via the
# example
production: redis://redis.example.tld:6379
+If you want to connect the Redis server via socket, then use the "unix:" URL scheme
+and the path to the Redis socket file in the `config/resque.yml` file.
+
+ # example
+ production: unix:/path/to/redis/socket
+
## Custom SSH Connection
If you are running SSH on a non-standard port, you must change the gitlab user's SSH config.
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 1dba04f423..30a9564674 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -41,24 +41,31 @@ some work on your part.
## CPU
-We recommend a processor with **4 cores**. At a minimum you need a processor with 2 cores to responsively run an unmodified installation.
+- 1 core works for under 100 users but the responsiveness might suffer
+- **2 cores** is the **recommended** number of cores and supports up to 100 users
+- 4 cores supports about 1,000 users
+- 8 cores supports up to 10,000 users
## Memory
- 512MB is too little memory, GitLab will be very slow and you will need 250MB of swap
-- 768MB is the minimal memory size and supports up to 100 users
-- **1GB** is the **recommended** memory size and supports up to 1,000 users
-- 1.5GB supports up to 10,000 users
+- 768MB is the minimal memory size but we advise against this
+- 1GB supports up to 100 users if you do not have individual repo's over 250MB
+- **2GB** is the **recommended** memory size and supports up to 1,000 users
+- 4GB supports up to 10,000 users
## Storage
The necessary hard drive space largely depends on the size of the repos you want
to store in GitLab. But as a *rule of thumb* you should have at least twice as much
-free space as your all repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo. Apart from a local hard drive you can also mount a volume that supports the network file system (NFS) protocol. This volume might be located on a file server, a network attached storage (NAS) device, a storage area network (SAN) or on an Amazon Web Services (AWS) Elastic Block Store (EBS) volume.
+free space as your all repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo.
+
+If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them.
+
+Apart from a local hard drive you can also mount a volume that supports the network file system (NFS) protocol. This volume might be located on a file server, a network attached storage (NAS) device, a storage area network (SAN) or on an Amazon Web Services (AWS) Elastic Block Store (EBS) volume.
If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab.
-
# Installation troubles and reporting success or failure
If you have troubles installing GitLab following the [official installation guide](installation.md)
diff --git a/doc/make_release.md b/doc/make_release.md
index 5be0d5980a..7f19a17da6 100644
--- a/doc/make_release.md
+++ b/doc/make_release.md
@@ -1,5 +1,6 @@
# Things to do when creating new release
-NOTE: This is a developer guide. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update).
+NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update).
+
## Install guide up to date?
* References correct GitLab branch `x-x-stable` and correct GitLab shell tag?
@@ -41,7 +42,7 @@ Check if changed since last release (~22nd of last month depending on when last
#### 10. Check application status
-## Make sure code status is good
+## Make sure the code quality indicatiors are good
* [](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
@@ -53,4 +54,18 @@ Check if changed since last release (~22nd of last month depending on when last
* [](https://coveralls.io/r/gitlabhq/gitlabhq)
-## Make release branch
+## Make a release branch
+
+After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked.
+
+- 5 days before release: feature freeze
+- 3 days before release: UI freeze
+- 1 day before release: code freeze
+
+## Last actions
+
+1. Write a blog post (mention what GitLab is on the first line, select a MVP)
+1. Update VERSION and CHANGELOG
+1. Create a git tag vX.X.X
+1. Publish the blog post
+1. Tweet about the release
diff --git a/doc/raketasks/web_hooks.md b/doc/raketasks/web_hooks.md
new file mode 100644
index 0000000000..1ca5bacb9d
--- /dev/null
+++ b/doc/raketasks/web_hooks.md
@@ -0,0 +1,31 @@
+### Add a web hook for **ALL** projects:
+
+ RAILS_ENV=production bundle exec rake gitlab:web_hook:add URL="http://example.com/hook"
+
+
+### Add a web hook for projects in a given **NAMESPACE**:
+
+ RAILS_ENV=production bundle exec rake gitlab:web_hook:add URL="http://example.com/hook" NAMESPACE=acme
+
+
+### Remove a web hook from **ALL** projects using:
+
+ RAILS_ENV=production bundle exec rake gitlab:web_hook:rm URL="http://example.com/hook"
+
+
+### Remove a web hook from projects in a given **NAMESPACE**:
+
+ RAILS_ENV=production bundle exec rake gitlab:web_hook:rm URL="http://example.com/hook" NAMESPACE=acme
+
+
+### List **ALL** web hooks:
+
+ RAILS_ENV=production bundle exec rake gitlab:web_hook:list
+
+
+### List the web hooks from projects in a given **NAMESPACE**:
+
+ RAILS_ENV=production bundle exec rake gitlab:web_hook:list NAMESPACE=/
+
+> Note: `/` is the global namespace.
+
diff --git a/doc/security/rack_attack.md b/doc/security/rack_attack.md
new file mode 100644
index 0000000000..a0d02b1650
--- /dev/null
+++ b/doc/security/rack_attack.md
@@ -0,0 +1,19 @@
+To prevent abusive clients doing damage GitLab uses rack-attack gem.
+If you installed or upgraded GitLab by following the official guides this should be enabled by default.
+If you are missing `config/initializers/rack_attack.rb` the following steps need to be taken in order to enable protection for your GitLab instance:
+
+1. In config/application.rb find and uncomment the following line:
+ config.middleware.use Rack::Attack
+2. Rename config/initializers/rack_attack.rb.example to config/initializers/rack_attack.rb
+3. Review the paths_to_be_protected and add any other path you need protecting
+4. Restart GitLab instance
+
+By default, user sign-in, user sign-up(if enabled) and user password reset is limited to 6 requests per minute.
+After trying for 6 times, client will have to wait for the next minute to be able to try again.
+These settings can be found in `config/initializers/rack_attack.rb`
+
+If you want more restrictive/relaxed throttle rule change the `limit` or `period` values. For example, more relaxed throttle rule will be if you set limit: 3 and period: 1.second(this will allow 3 requests per second). You can also add other paths to the protected list by adding to `paths_to_be_protected` variable. If you change any of these settings do not forget to restart your GitLab instance.
+
+In case you find throttling is not enough to protect you against abusive clients, rack-attack gem offers IP whitelisting, blacklisting, Fail2ban style filter and tracking.
+
+For more information on how to use these options check out [rack-attack README](https://github.com/kickstarter/rack-attack/blob/master/README.md).
\ No newline at end of file
diff --git a/doc/update/4.1-to-4.2.md b/doc/update/4.1-to-4.2.md
index 536f22415e..3aaf17b772 100644
--- a/doc/update/4.1-to-4.2.md
+++ b/doc/update/4.1-to-4.2.md
@@ -12,15 +12,15 @@
cd /home/gitlab/gitlab/
# Get latest code
-sudo -u gitlab git fetch
+sudo -u gitlab -H git fetch
-sudo -u gitlab git checkout 4-2-stable
+sudo -u gitlab -H git checkout 4-2-stable
# Install libs
-sudo -u gitlab bundle install --without development test postgres --deployment
+sudo -u gitlab -H bundle install --without development test postgres --deployment
# update db
-sudo -u gitlab bundle exec rake db:migrate RAILS_ENV=production
+sudo -u gitlab -H bundle exec rake db:migrate RAILS_ENV=production
```
diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md
index 053a50ebc8..64b89ee93c 100644
--- a/doc/update/4.2-to-5.0.md
+++ b/doc/update/4.2-to-5.0.md
@@ -85,7 +85,7 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:shell:setup RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:shell:build_missing_projects RAILS_ENV=production
-sudo -u git -H mkdir /home/git/gitlab-satellites
+sudo -u git -H mkdir -p /home/git/gitlab-satellites
sudo -u git -H bundle exec rake gitlab:satellites:create RAILS_ENV=production
# migrate wiki to git
@@ -101,7 +101,7 @@ sudo chown -R git /home/git/gitlab/log/
sudo chown -R git /home/git/gitlab/tmp/
sudo chmod -R u+rwX /home/git/gitlab/log/
sudo chmod -R u+rwX /home/git/gitlab/tmp/
-sudo -u git -H mkdir /home/git/gitlab/tmp/pids/
+sudo -u git -H mkdir -p /home/git/gitlab/tmp/pids/
sudo chmod -R u+rwX /home/git/gitlab/tmp/pids
```
diff --git a/doc/update/5.1-to-5.2.md b/doc/update/5.1-to-5.2.md
index 8599c4323e..27f992ecfe 100644
--- a/doc/update/5.1-to-5.2.md
+++ b/doc/update/5.1-to-5.2.md
@@ -7,7 +7,7 @@ It's useful to make a backup just in case things go south:
```bash
cd /home/git/gitlab
-sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:create
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
@@ -95,5 +95,5 @@ Follow the [`upgrade guide from 5.0 to 5.1`](5.0-to-5.1.md), except for the data
```bash
cd /home/git/gitlab
-sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:restore
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md
index e00dfa3951..a8bb530902 100644
--- a/doc/update/5.2-to-5.3.md
+++ b/doc/update/5.2-to-5.3.md
@@ -7,7 +7,7 @@ It's useful to make a backup just in case things go south:
```bash
cd /home/git/gitlab
-sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:create
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
@@ -78,5 +78,5 @@ Follow the [`upgrade guide from 5.1 to 5.2`](5.1-to-5.2.md), except for the data
```bash
cd /home/git/gitlab
-sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:restore
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
diff --git a/doc/update/5.3-to-5.4.md b/doc/update/5.3-to-5.4.md
index 5fba0e26af..315bf03a6d 100644
--- a/doc/update/5.3-to-5.4.md
+++ b/doc/update/5.3-to-5.4.md
@@ -7,7 +7,7 @@ It's useful to make a backup just in case things go south:
```bash
cd /home/git/gitlab
-sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:create
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
@@ -86,5 +86,5 @@ Follow the [`upgrade guide from 5.2 to 5.3`](5.2-to-5.3.md), except for the data
```bash
cd /home/git/gitlab
-sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:restore
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
diff --git a/doc/update/5.4-to-6.0.md b/doc/update/5.4-to-6.0.md
index 3b1d987820..fc010cd818 100644
--- a/doc/update/5.4-to-6.0.md
+++ b/doc/update/5.4-to-6.0.md
@@ -24,7 +24,7 @@ It's useful to make a backup just in case things go south:
```bash
cd /home/git/gitlab
-sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:create
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
@@ -91,7 +91,7 @@ Note: We switched from Puma in GitLab 5.4 to unicorn in GitLab 6.0.
```bash
sudo rm /etc/init.d/gitlab
-sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/master/lib/support/init.d/gitlab
+sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/6-0-stable/lib/support/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
```
@@ -111,3 +111,21 @@ To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade complete!
+
+### Troubleshooting
+The migrations in this update are very sensitive to incomplete or inconsistent data. If you have a long-running GitLab installation and some of the previous upgrades did not work out 100% correct this may bite you now. The following commands can be run in the rails console to look for 'bad' data.
+
+All project owners should have an owner
+```
+Project.all.select { |project| project.owner.blank? }
+```
+
+Every user should have a namespace
+```
+User.all.select { |u| u.namespace.blank? }
+```
+
+Projects in the global namespace should not conflict with projects in the owner namespace
+```
+Project.where(namespace_id: nil).select { |p| Project.where(path: p.path, namespace_id: p.owner.try(:namespace).try(:id)).present? }
+```
diff --git a/doc/update/6.0-to-6.1.md b/doc/update/6.0-to-6.1.md
index ba80603499..8fe703309e 100644
--- a/doc/update/6.0-to-6.1.md
+++ b/doc/update/6.0-to-6.1.md
@@ -53,6 +53,7 @@ sudo -u git -H bundle install --without development test mysql --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
@@ -79,6 +80,7 @@ sudo chmod +x /etc/init.d/gitlab
Check if GitLab and its environment are configured correctly:
+ cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
diff --git a/doc/update/6.1-to-6.2.md b/doc/update/6.1-to-6.2.md
new file mode 100644
index 0000000000..e7f6c2a5ba
--- /dev/null
+++ b/doc/update/6.1-to-6.2.md
@@ -0,0 +1,102 @@
+# From 6.1 to 6.2
+
+# You should update to 6.1 before installing 6.2 so all the necessary conversions are run.
+
+### 0. Backup
+
+It's useful to make a backup just in case things go south:
+(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Get latest code
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H git fetch
+sudo -u git -H git checkout 6-2-stable
+```
+
+### 3. Install additional packages
+
+```bash
+# Add support for lograte for better log file handling
+sudo apt-get install logrotate
+```
+
+### 4. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL
+sudo -u git -H bundle install --without development test postgres --deployment
+
+#PostgreSQL
+sudo -u git -H bundle install --without development test mysql --deployment
+
+
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+### 5. Update config files
+
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-2-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` same as https://github.com/gitlabhq/gitlabhq/blob/6-2-stable/config/unicorn.rb.example but with your settings.
+* Copy rack attack middleware config
+```bash
+sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
+```
+* Uncomment `config.middleware.use Rack::Attack` in `/home/git/gitlab/config/application.rb`
+* Set up logrotate
+```bash
+sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
+```
+
+### 6. Update Init script
+
+```bash
+sudo rm /etc/init.d/gitlab
+sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/6-2-stable/lib/support/init.d/gitlab
+sudo chmod +x /etc/init.d/gitlab
+```
+
+### 7. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 8. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations upgrade complete!
+
+## Things went south? Revert to previous version (6.1)
+
+### 1. Revert the code to the previous version
+Follow the [`upgrade guide from 6.0 to 6.1`](6.0-to-6.1.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup:
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
diff --git a/features/profile/profile.feature b/features/profile/profile.feature
index 3b61552a73..6198fd2b30 100644
--- a/features/profile/profile.feature
+++ b/features/profile/profile.feature
@@ -11,21 +11,32 @@ Feature: Profile
Then I change my contact info
And I should see new contact info
+ Scenario: I change my password without old one
+ Given I visit profile password page
+ When I try change my password w/o old one
+ Then I should see a missing password error message
+ And I should be redirected to password page
+
Scenario: I change my password
- Given I visit profile account page
+ Given I visit profile password page
Then I change my password
And I should be redirected to sign in page
+ Scenario: I edit my avatar
+ Given I visit profile page
+ Then I change my avatar
+ And I should see new avatar
+
Scenario: My password is expired
Given my password is expired
And I am not an ldap user
- And I visit profile account page
+ Given I visit profile password page
Then I redirected to expired password page
And I submit new password
And I redirected to sign in page
Scenario: I unsuccessfully change my password
- Given I visit profile account page
+ Given I visit profile password page
When I unsuccessfully change my password
Then I should see a password error message
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 022188f825..67986784bc 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -3,6 +3,7 @@ Feature: Project Issues
Given I sign in as a user
And I own project "Shop"
And project "Shop" have "Release 0.4" open issue
+ And project "Shop" have "Tweet control" open issue
And project "Shop" have "Release 0.3" closed issue
And I visit project "Shop" issues page
@@ -37,20 +38,20 @@ Feature: Project Issues
@javascript
Scenario: I search issue
- Given I fill in issue search with "Release"
+ Given I fill in issue search with "Re"
Then I should see "Release 0.4" in issues
And I should not see "Release 0.3" in issues
+ And I should not see "Tweet control" in issues
@javascript
Scenario: I search issue that not exist
- Given I fill in issue search with "Bug"
+ Given I fill in issue search with "Bu"
Then I should not see "Release 0.4" in issues
And I should not see "Release 0.3" in issues
-
@javascript
Scenario: I search all issues
Given I click link "All"
- And I fill in issue search with "0.3"
+ And I fill in issue search with ".3"
Then I should see "Release 0.3" in issues
And I should not see "Release 0.4" in issues
diff --git a/features/project/service.feature b/features/project/service.feature
index e685c385d1..4805d2befb 100644
--- a/features/project/service.feature
+++ b/features/project/service.feature
@@ -24,3 +24,9 @@ Feature: Project Services
And I click pivotaltracker service link
And I fill pivotaltracker settings
Then I should see pivotaltracker service settings saved
+
+ Scenario: Activate Flowdock service
+ When I visit project "Shop" services page
+ And I click Flowdock service link
+ And I fill Flowdock settings
+ Then I should see Flowdock service settings saved
diff --git a/features/project/snippets.feature b/features/project/snippets.feature
index a26c8dc847..dfaa02663a 100644
--- a/features/project/snippets.feature
+++ b/features/project/snippets.feature
@@ -31,5 +31,5 @@ Feature: Project Snippets
Scenario: I destroy "Snippet one"
Given I visit snippet page "Snippet one"
And I click link "Edit"
- And I click link "Destroy"
+ And I click link "Remove Snippet"
Then I should not see "Snippet one" in snippets
diff --git a/features/project/source/markdown_render.feature b/features/project/source/markdown_render.feature
new file mode 100644
index 0000000000..8b4b89e85a
--- /dev/null
+++ b/features/project/source/markdown_render.feature
@@ -0,0 +1,80 @@
+Feature: Project markdown render
+ Background:
+ Given I sign in as a user
+ And I own project "Delta"
+ Given I visit project source page
+
+ Scenario: I browse files from master branch
+ Then I should see files from repository in master
+ And I should see rendered README which contains correct links
+ And I click on Gitlab API in README
+ Then I should see correct document rendered
+
+ Scenario: I view README in master branch
+ Then I should see files from repository in master
+ And I should see rendered README which contains correct links
+ And I click on Rake tasks in README
+ Then I should see correct directory rendered
+
+ Scenario: I navigate to doc directory to view documentation in master
+ And I navigate to the doc/api/README
+ And I see correct file rendered
+ And I click on users in doc/api/README
+ Then I should see the correct document file
+
+ Scenario: I navigate to doc directory to view user doc in master
+ And I navigate to the doc/api/README
+ And I see correct file rendered
+ And I click on raketasks in doc/api/README
+ Then I should see correct directory rendered
+
+ Scenario: I browse files from markdown branch
+ When I visit markdown branch
+ Then I should see files from repository in markdown branch
+ And I should see rendered README which contains correct links
+ And I click on Gitlab API in README
+ Then I should see correct document rendered for markdown branch
+
+ Scenario: I browse directory from markdown branch
+ When I visit markdown branch
+ Then I should see files from repository in markdown branch
+ And I should see rendered README which contains correct links
+ And I click on Rake tasks in README
+ Then I should see correct directory rendered for markdown branch
+
+ Scenario: I navigate to doc directory to view documentation in markdown branch
+ When I visit markdown branch
+ And I navigate to the doc/api/README
+ And I see correct file rendered in markdown branch
+ And I click on users in doc/api/README
+ Then I should see the users document file in markdown branch
+
+ Scenario: I navigate to doc directory to view user doc in markdown branch
+ When I visit markdown branch
+ And I navigate to the doc/api/README
+ And I see correct file rendered in markdown branch
+ And I click on raketasks in doc/api/README
+ Then I should see correct directory rendered for markdown branch
+
+ Scenario: I create a wiki page with different links
+ Given I go to wiki page
+ And I add various links to the wiki page
+ Then Wiki page should have added links
+ And I click on test link
+ Then I see new wiki page named test
+ When I go back to wiki page home
+ And I click on GitLab API doc link
+ Then I see Gitlab API document
+ When I go back to wiki page home
+ And I click on Rake tasks link
+ Then I see Rake tasks directory
+
+ Scenario: I visit the help page with markdown
+ Given I visit to the help page
+ And I select a page with markdown
+ Then I should see a help page with markdown
+
+ Scenario: Tree view should have correct links in README
+ Given I go directory which contains README file
+ And I click on a relative link in README
+ Then I should see the correct markdown
diff --git a/features/public/public_projects.feature b/features/public/public_projects.feature
index c4f1b6203e..178a769194 100644
--- a/features/public/public_projects.feature
+++ b/features/public/public_projects.feature
@@ -9,6 +9,10 @@ Feature: Public Projects Feature
And I should not see project "Enterprise"
Scenario: I visit public project page
- When I visit public page for "Community" project
- Then I should see public project details
- And I should see project readme
+ When I visit project "Community" page
+ Then I should see project "Community" home page
+
+ Scenario: I visit an empty public project page
+ Given public empty project "Empty Public Project"
+ When I visit empty project page
+ Then I should see empty public project details
diff --git a/features/steps/group/group.rb b/features/steps/group/group.rb
index b3d557ddcd..0ad1746bed 100644
--- a/features/steps/group/group.rb
+++ b/features/steps/group/group.rb
@@ -10,7 +10,8 @@ class Groups < Spinach::FeatureSteps
end
And 'I have group with projects' do
- @group = create(:group, owner: current_user)
+ @group = create(:group)
+ @group.add_owner(current_user)
@project = create(:project, namespace: @group)
@event = create(:closed_issue_event, project: @project)
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 6944977c3f..753e2c19bc 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -3,9 +3,7 @@ class Profile < Spinach::FeatureSteps
include SharedPaths
step 'I should see my profile info' do
- page.should have_content "Profile"
- page.should have_content @user.name
- page.should have_content @user.email
+ page.should have_content "Profile settings"
end
step 'I change my contact info' do
@@ -22,8 +20,28 @@ class Profile < Spinach::FeatureSteps
@user.twitter.should == 'testtwitter'
end
+ step 'I change my avatar' do
+ attach_file(:user_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png'))
+ click_button "Save changes"
+ @user.reload
+ end
+
+ step 'I should see new avatar' do
+ @user.avatar.should be_instance_of AttachmentUploader
+ @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png"
+ end
+
+ step 'I try change my password w/o old one' do
+ within '.update-password' do
+ fill_in "user_password", with: "222333"
+ fill_in "user_password_confirmation", with: "222333"
+ click_button "Save"
+ end
+ end
+
step 'I change my password' do
within '.update-password' do
+ fill_in "user_current_password", with: "123456"
fill_in "user_password", with: "222333"
fill_in "user_password_confirmation", with: "222333"
click_button "Save"
@@ -32,12 +50,17 @@ class Profile < Spinach::FeatureSteps
step 'I unsuccessfully change my password' do
within '.update-password' do
+ fill_in "user_current_password", with: "123456"
fill_in "user_password", with: "password"
fill_in "user_password_confirmation", with: "confirmation"
click_button "Save"
end
end
+ step "I should see a missing password error message" do
+ page.should have_content "You must provide a valid current password"
+ end
+
step "I should see a password error message" do
page.should have_content "Password doesn't match confirmation"
end
@@ -110,6 +133,14 @@ class Profile < Spinach::FeatureSteps
current_path.should == new_user_session_path
end
+ step 'I should be redirected to password page' do
+ current_path.should == edit_profile_password_path
+ end
+
+ step 'I should be redirected to account page' do
+ current_path.should == profile_account_path
+ end
+
step 'I click on my profile picture' do
click_link 'profile-pic'
end
diff --git a/features/steps/project/project_issues.rb b/features/steps/project/project_issues.rb
index 6c18177498..801fff78a5 100644
--- a/features/steps/project/project_issues.rb
+++ b/features/steps/project/project_issues.rb
@@ -12,6 +12,10 @@ class ProjectIssues < Spinach::FeatureSteps
page.should_not have_content "Release 0.3"
end
+ And 'I should not see "Tweet control" in issues' do
+ page.should_not have_content "Tweet control"
+ end
+
Given 'I click link "Closed"' do
click_link "Closed"
end
@@ -56,16 +60,16 @@ class ProjectIssues < Spinach::FeatureSteps
page.should have_content issue.project.name
end
- Given 'I fill in issue search with "Release"' do
- fill_in 'issue_search', with: "Release"
+ Given 'I fill in issue search with "Re"' do
+ fill_in 'issue_search', with: "Re"
end
- Given 'I fill in issue search with "Bug"' do
- fill_in 'issue_search', with: "Bug"
+ Given 'I fill in issue search with "Bu"' do
+ fill_in 'issue_search', with: "Bu"
end
- And 'I fill in issue search with "0.3"' do
- fill_in 'issue_search', with: "0.3"
+ And 'I fill in issue search with ".3"' do
+ fill_in 'issue_search', with: ".3"
end
And 'I fill in issue search with "Something"' do
@@ -120,6 +124,14 @@ class ProjectIssues < Spinach::FeatureSteps
author: project.users.first)
end
+ And 'project "Shop" have "Tweet control" open issue' do
+ project = Project.find_by_name("Shop")
+ create(:issue,
+ title: "Tweet control",
+ project: project,
+ author: project.users.first)
+ end
+
And 'project "Shop" have "Release 0.3" closed issue' do
project = Project.find_by_name("Shop")
create(:closed_issue,
diff --git a/features/steps/project/project_markdown_render.rb b/features/steps/project/project_markdown_render.rb
new file mode 100644
index 0000000000..95d73c9362
--- /dev/null
+++ b/features/steps/project/project_markdown_render.rb
@@ -0,0 +1,179 @@
+class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+
+ And 'I own project "Delta"' do
+ @project = Project.find_by_name "Delta"
+ @project ||= create(:project_with_code, name: "Delta", namespace: @user.namespace)
+ @project.team << [@user, :master]
+ end
+
+ Then 'I should see files from repository in master' do
+ current_path.should == project_tree_path(@project, "master")
+ page.should have_content "Gemfile"
+ page.should have_content "app"
+ page.should have_content "README"
+ end
+
+ And 'I should see rendered README which contains correct links' do
+ page.should have_content "Welcome to GitLab GitLab is a free project and repository management application"
+ page.should have_link "GitLab API doc"
+ page.should have_link "GitLab API website"
+ page.should have_link "Rake tasks"
+ page.should have_link "backup and restore procedure"
+ end
+
+ And 'I click on Gitlab API in README' do
+ click_link "GitLab API doc"
+ end
+
+ Then 'I should see correct document rendered' do
+ current_path.should == project_blob_path(@project, "master/doc/api/README.md")
+ page.should have_content "All API requests require authentication"
+ end
+
+ And 'I click on Rake tasks in README' do
+ click_link "Rake tasks"
+ end
+
+ Then 'I should see correct directory rendered' do
+ current_path.should == project_tree_path(@project, "master/doc/raketasks")
+ page.should have_content "backup_restore.md"
+ page.should have_content "maintenance.md"
+ end
+
+ And 'I navigate to the doc/api/README' do
+ click_link "doc"
+ click_link "api"
+ click_link "README.md"
+ end
+
+ And 'I see correct file rendered' do
+ current_path.should == project_blob_path(@project, "master/doc/api/README.md")
+ page.should have_content "Contents"
+ page.should have_link "Users"
+ page.should have_link "Rake tasks"
+ end
+
+ And 'I click on users in doc/api/README' do
+ click_link "Users"
+ end
+
+ Then 'I should see the correct document file' do
+ current_path.should == project_blob_path(@project, "master/doc/api/users.md")
+ page.should have_content "Get a list of users."
+ end
+
+ And 'I click on raketasks in doc/api/README' do
+ click_link "Rake tasks"
+ end
+
+ When 'I visit markdown branch' do
+ visit project_tree_path(@project, "markdown")
+ end
+
+ Then 'I should see files from repository in markdown branch' do
+ current_path.should == project_tree_path(@project, "markdown")
+ page.should have_content "Gemfile"
+ page.should have_content "app"
+ page.should have_content "README"
+ end
+
+ And 'I see correct file rendered in markdown branch' do
+ current_path.should == project_blob_path(@project, "markdown/doc/api/README.md")
+ page.should have_content "Contents"
+ page.should have_link "Users"
+ page.should have_link "Rake tasks"
+ end
+
+ Then 'I should see correct document rendered for markdown branch' do
+ current_path.should == project_blob_path(@project, "markdown/doc/api/README.md")
+ page.should have_content "All API requests require authentication"
+ end
+
+ Then 'I should see correct directory rendered for markdown branch' do
+ current_path.should == project_tree_path(@project, "markdown/doc/raketasks")
+ page.should have_content "backup_restore.md"
+ page.should have_content "maintenance.md"
+ end
+
+ Then 'I should see the users document file in markdown branch' do
+ current_path.should == project_blob_path(@project, "markdown/doc/api/users.md")
+ page.should have_content "Get a list of users."
+ end
+
+ Given 'I go to wiki page' do
+ click_link "Wiki"
+ current_path.should == project_wiki_path(@project, "home")
+ end
+
+ And 'I add various links to the wiki page' do
+ fill_in "wiki[content]", with: "[test](test)\n[GitLab API doc](doc/api/README.md)\n[Rake tasks](doc/raketasks)\n"
+ fill_in "wiki[message]", with: "Adding links to wiki"
+ click_button "Create page"
+ end
+
+ Then 'Wiki page should have added links' do
+ current_path.should == project_wiki_path(@project, "home")
+ page.should have_content "test GitLab API doc Rake tasks"
+ end
+
+ And 'I click on test link' do
+ click_link "test"
+ end
+
+ Then 'I see new wiki page named test' do
+ current_path.should == project_wiki_path(@project, "test")
+ page.should have_content "Editing page"
+ end
+
+ When 'I go back to wiki page home' do
+ visit project_wiki_path(@project, "home")
+ current_path.should == project_wiki_path(@project, "home")
+ end
+
+ And 'I click on GitLab API doc link' do
+ click_link "GitLab API"
+ end
+
+ Then 'I see Gitlab API document' do
+ current_path.should == project_blob_path(@project, "master/doc/api/README.md")
+ page.should have_content "Status codes"
+ end
+
+ And 'I click on Rake tasks link' do
+ click_link "Rake tasks"
+ end
+
+ Then 'I see Rake tasks directory' do
+ current_path.should == project_tree_path(@project, "master/doc/raketasks")
+ page.should have_content "backup_restore.md"
+ page.should have_content "maintenance.md"
+ end
+
+ Given 'I visit to the help page' do
+ visit help_path
+ end
+
+ And 'I select a page with markdown' do
+ click_link "Rake Tasks"
+ end
+
+ Then 'I should see a help page with markdown' do
+ page.should have_content "GitLab provides some specific rake tasks to enable special features or perform maintenance tasks"
+ end
+
+ Given 'I go directory which contains README file' do
+ visit project_tree_path(@project, "master/doc/api")
+ current_path.should == project_tree_path(@project, "master/doc/api")
+ end
+
+ And 'I click on a relative link in README' do
+ click_link "Users"
+ end
+
+ Then 'I should see the correct markdown' do
+ current_path.should == project_blob_path(@project, "master/doc/api/users.md")
+ page.should have_content "List users"
+ end
+end
diff --git a/features/steps/project/project_merge_requests.rb b/features/steps/project/project_merge_requests.rb
index 7c70482deb..6c794ef8f1 100644
--- a/features/steps/project/project_merge_requests.rb
+++ b/features/steps/project/project_merge_requests.rb
@@ -21,7 +21,9 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end
Then 'I should see merge request "Wiki Feature"' do
- page.should have_content "Wiki Feature"
+ within '.merge-request' do
+ page.should have_content "Wiki Feature"
+ end
end
Then 'I should see closed merge request "Bug NS-04"' do
@@ -56,16 +58,20 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end
And 'I submit new merge request "Wiki Feature"' do
- #this must come first, so that the target branch is set by the time the "select" for "notes_refactoring" is executed
- select project.path_with_namespace, :from => "merge_request_target_project_id"
- fill_in "merge_request_title", :with => "Wiki Feature"
- select "master", :from => "merge_request_source_branch"
+ fill_in "merge_request_title", with: "Wiki Feature"
+
+ # this must come first, so that the target branch is set
+ # by the time the "select" for "notes_refactoring" is executed
+ select project.path_with_namespace, from: "merge_request_target_project_id"
+ select "master", from: "merge_request_source_branch"
+
find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s
find(:select, "merge_request_source_project_id", {}).value.should == project.id.to_s
- #using "notes_refactoring" because "Bug NS-04" uses master/stable, this will fail merge_request validation if the branches are the same
+ # using "notes_refactoring" because "Bug NS-04" uses master/stable,
+ # this will fail merge_request validation if the branches are the same
find(:select, "merge_request_target_branch", {}).find(:option, "notes_refactoring", {}).value.should == "notes_refactoring"
- select "notes_refactoring", :from => "merge_request_target_branch"
+ select "notes_refactoring", from: "merge_request_target_branch"
click_button "Submit merge request"
end
diff --git a/features/steps/project/project_services.rb b/features/steps/project/project_services.rb
index a24100ff8c..70eafc875d 100644
--- a/features/steps/project/project_services.rb
+++ b/features/steps/project/project_services.rb
@@ -58,4 +58,18 @@ class ProjectServices < Spinach::FeatureSteps
Then 'I should see pivotaltracker service settings saved' do
find_field('Token').value.should == 'verySecret'
end
+
+ And 'I click Flowdock service link' do
+ click_link 'Flowdock'
+ end
+
+ And 'I fill Flowdock settings' do
+ check 'Active'
+ fill_in 'Token', with: 'verySecret'
+ click_button 'Save'
+ end
+
+ Then 'I should see Flowdock service settings saved' do
+ find_field('Token').value.should == 'verySecret'
+ end
end
diff --git a/features/steps/project/project_snippets.rb b/features/steps/project/project_snippets.rb
index 86c0685256..5082b31198 100644
--- a/features/steps/project/project_snippets.rb
+++ b/features/steps/project/project_snippets.rb
@@ -47,8 +47,8 @@ class ProjectSnippets < Spinach::FeatureSteps
end
end
- And 'I click link "Destroy"' do
- click_link "Destroy"
+ And 'I click link "Remove Snippet"' do
+ click_link "Remove snippet"
end
And 'I submit new snippet "Snippet three"' do
diff --git a/features/steps/public/projects_feature.rb b/features/steps/public/projects_feature.rb
index 8d612498fb..e9a4d56e36 100644
--- a/features/steps/public/projects_feature.rb
+++ b/features/steps/public/projects_feature.rb
@@ -9,6 +9,10 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
page.should_not have_content "Enterprise"
end
+ step 'I should see project "Empty Public Project"' do
+ page.should have_content "Empty Public Project"
+ end
+
step 'I should see public project details' do
page.should have_content '32 branches'
page.should have_content '16 tags'
@@ -19,13 +23,35 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end
step 'public project "Community"' do
- create :project_with_code, name: 'Community', public: true
+ create :project_with_code, name: 'Community', public: true, default_branch: 'master'
+ end
+
+ step 'public empty project "Empty Public Project"' do
+ create :project, name: 'Empty Public Project', public: true
+ end
+
+ step 'I visit empty project page' do
+ project = Project.find_by_name('Empty Public Project')
+ visit project_path(project)
+ end
+
+ step 'I visit project "Community" page' do
+ project = Project.find_by_name('Community')
+ visit project_path(project)
+ end
+
+ step 'I should see empty public project details' do
+ page.should have_content 'Git global setup'
end
step 'private project "Enterprise"' do
create :project, name: 'Enterprise'
end
+ step 'I should see project "Community" home page' do
+ page.should have_content 'Repo size is'
+ end
+
private
def project
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index c30eccce1c..156fa5bab4 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -65,8 +65,12 @@ module SharedPaths
visit profile_path
end
+ step 'I visit profile password page' do
+ visit edit_profile_password_path
+ end
+
step 'I visit profile account page' do
- visit account_profile_path
+ visit profile_account_path
end
step 'I visit profile SSH keys page' do
diff --git a/features/support/env.rb b/features/support/env.rb
index 8798e62ea7..d27a73edab 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -51,4 +51,6 @@ Spinach.hooks.before_run do
RSpec::Mocks::setup self
include FactoryGirl::Syntax::Methods
+ MergeRequestObserver.any_instance.stub(current_user: create(:user))
end
+
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 55c947eb17..218b3d8eee 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -2,6 +2,7 @@ module API
# Projects API
class DeployKeys < Grape::API
before { authenticate! }
+ before { authorize_admin_project }
resource :projects do
helpers do
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 7e8a928d47..5efd971cfb 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -3,6 +3,9 @@ module API
class User < Grape::Entity
expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter,
:theme_id, :color_scheme_id, :state, :created_at, :extern_uid, :provider
+ expose :is_admin?, as: :is_admin
+ expose :can_create_group?, as: :can_create_group
+ expose :can_create_project?, as: :can_create_project
end
class UserSafe < Grape::Entity
@@ -15,10 +18,6 @@ module API
class UserLogin < User
expose :private_token
- expose :is_admin?, as: :is_admin
- expose :can_create_group?, as: :can_create_group
- expose :can_create_project?, as: :can_create_project
- expose :can_create_team?, as: :can_create_team
end
class Hook < Grape::Entity
@@ -92,15 +91,16 @@ module API
expose :expires_at, :updated_at, :created_at
end
- class Milestone < Grape::Entity
- expose :id
- expose (:project_id) {|milestone| milestone.project.id}
+ class ProjectEntity < Grape::Entity
+ expose :id, :iid
+ expose (:project_id) { |entity| entity.project.id }
+ end
+
+ class Milestone < ProjectEntity
expose :title, :description, :due_date, :state, :updated_at, :created_at
end
- class Issue < Grape::Entity
- expose :id
- expose (:project_id) {|issue| issue.project.id}
+ class Issue < ProjectEntity
expose :title, :description
expose :label_list, as: :labels
expose :milestone, using: Entities::Milestone
@@ -108,14 +108,14 @@ module API
expose :state, :updated_at, :created_at
end
- class SSHKey < Grape::Entity
- expose :id, :title, :key, :created_at
+ class MergeRequest < ProjectEntity
+ expose :target_branch, :source_branch, :title, :state, :upvotes, :downvotes
+ expose :author, :assignee, using: Entities::UserBasic
+ expose :source_project_id, :target_project_id
end
- class MergeRequest < Grape::Entity
- expose :id, :target_branch, :source_branch, :title, :state
- expose :target_project_id, as: :project_id
- expose :author, :assignee, using: Entities::UserBasic
+ class SSHKey < Grape::Entity
+ expose :id, :title, :key, :created_at
end
class Note < Grape::Entity
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index d6ebb74bc1..55aa484435 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -7,12 +7,14 @@ module API
helpers do
def find_group(id)
group = Group.find(id)
- if current_user.admin or current_user.groups.include? group
+
+ if can?(current_user, :read_group, group)
group
else
render_api_error!("403 Forbidden - #{current_user.username} lacks sufficient access to #{group.name}", 403)
end
end
+
def validate_access_level?(level)
Gitlab::Access.options_with_owner.values.include? level.to_i
end
@@ -64,6 +66,18 @@ module API
present group, with: Entities::GroupDetail
end
+ # Remove group
+ #
+ # Parameters:
+ # id (required) - The ID of a group
+ # Example Request:
+ # DELETE /groups/:id
+ delete ":id" do
+ group = find_group(params[:id])
+ authorize! :manage_group, group
+ group.destroy
+ end
+
# Transfer a project to the Group namespace
#
# Parameters:
@@ -132,7 +146,6 @@ module API
member.destroy
end
end
-
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 4f189f3519..edc662eaaa 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -11,12 +11,8 @@ module API
# If the sudo is the current user do nothing
if (identifier && !(@current_user.id == identifier || @current_user.username == identifier))
render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin?
- begin
- @current_user = User.by_username_or_id(identifier)
- rescue => ex
- not_found!("No user id or username for: #{identifier}")
- end
- not_found!("No user id or username for: #{identifier}") if current_user.nil?
+ @current_user = User.by_username_or_id(identifier)
+ not_found!("No user id or username for: #{identifier}") if @current_user.nil?
end
@current_user
end
@@ -31,6 +27,15 @@ module API
end
end
+ def set_current_user_for_thread
+ Thread.current[:current_user] = current_user
+ begin
+ yield
+ ensure
+ Thread.current[:current_user] = nil
+ end
+ end
+
def user_project
@project ||= find_project(params[:id])
@project || not_found!
@@ -64,6 +69,10 @@ module API
end
end
+ def authorize_admin_project
+ authorize! :admin_project, user_project
+ end
+
def can?(object, action, subject)
abilities.allowed?(object, action, subject)
end
@@ -82,7 +91,7 @@ module API
def attributes_for_keys(keys)
attrs = {}
keys.each do |key|
- attrs[key] = params[key] if params[key].present?
+ attrs[key] = params[key] if params[key].present? or (params.has_key?(key) and params[key] == false)
end
attrs
end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 79f8eb3a54..ed6b50c3a6 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -35,6 +35,7 @@ module API
user = key.user
return false if user.blocked?
+ return false if user.ldap_user? && Gitlab::LDAP::User.blocked?(user.extern_uid)
action = case git_cmd
when *DOWNLOAD_COMMANDS
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index a15203d156..3d15c35b8c 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -2,7 +2,6 @@ module API
# Issues API
class Issues < Grape::API
before { authenticate! }
- before { Thread.current[:current_user] = current_user }
resource :issues do
# Get currently authenticated user's issues
@@ -49,15 +48,17 @@ module API
# Example Request:
# POST /projects/:id/issues
post ":id/issues" do
- required_attributes! [:title]
- attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
- attrs[:label_list] = params[:labels] if params[:labels].present?
- @issue = user_project.issues.new attrs
- @issue.author = current_user
- if @issue.save
- present @issue, with: Entities::Issue
- else
- not_found!
+ set_current_user_for_thread do
+ required_attributes! [:title]
+ attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
+ attrs[:label_list] = params[:labels] if params[:labels].present?
+ @issue = user_project.issues.new attrs
+ @issue.author = current_user
+ if @issue.save
+ present @issue, with: Entities::Issue
+ else
+ not_found!
+ end
end
end
@@ -75,16 +76,18 @@ module API
# Example Request:
# PUT /projects/:id/issues/:issue_id
put ":id/issues/:issue_id" do
- @issue = user_project.issues.find(params[:issue_id])
- authorize! :modify_issue, @issue
+ set_current_user_for_thread do
+ @issue = user_project.issues.find(params[:issue_id])
+ authorize! :modify_issue, @issue
- attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
- attrs[:label_list] = params[:labels] if params[:labels].present?
+ attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
+ attrs[:label_list] = params[:labels] if params[:labels].present?
- if @issue.update_attributes attrs
- present @issue, with: Entities::Issue
- else
- not_found!
+ if @issue.update_attributes attrs
+ present @issue, with: Entities::Issue
+ else
+ not_found!
+ end
end
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index d690f1d07e..3f4bec895b 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -2,7 +2,6 @@ module API
# MergeRequest API
class MergeRequests < Grape::API
before { authenticate! }
- before { Thread.current[:current_user] = current_user }
resource :projects do
helpers do
@@ -70,28 +69,30 @@ module API
# POST /projects/:id/merge_requests
#
post ":id/merge_requests" do
- authorize! :write_merge_request, user_project
- required_attributes! [:source_branch, :target_branch, :title]
- attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
- merge_request = user_project.merge_requests.new(attrs)
- merge_request.author = current_user
- merge_request.source_project = user_project
- target_project_id = attrs[:target_project_id]
- if not_fork?(target_project_id, user_project)
- merge_request.target_project = user_project
- else
- if target_matches_fork(target_project_id,user_project)
- merge_request.target_project = Project.find_by_id(attrs[:target_project_id])
+ set_current_user_for_thread do
+ authorize! :write_merge_request, user_project
+ required_attributes! [:source_branch, :target_branch, :title]
+ attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
+ merge_request = user_project.merge_requests.new(attrs)
+ merge_request.author = current_user
+ merge_request.source_project = user_project
+ target_project_id = attrs[:target_project_id]
+ if not_fork?(target_project_id, user_project)
+ merge_request.target_project = user_project
else
- render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)
+ if target_matches_fork(target_project_id,user_project)
+ merge_request.target_project = Project.find_by_id(attrs[:target_project_id])
+ else
+ render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)
+ end
end
- end
- if merge_request.save
- merge_request.reload_code
- present merge_request, with: Entities::MergeRequest
- else
- handle_merge_request_errors! merge_request.errors
+ if merge_request.save
+ merge_request.reload_code
+ present merge_request, with: Entities::MergeRequest
+ else
+ handle_merge_request_errors! merge_request.errors
+ end
end
end
@@ -109,17 +110,19 @@ module API
# PUT /projects/:id/merge_request/:merge_request_id
#
put ":id/merge_request/:merge_request_id" do
- attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event]
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
+ set_current_user_for_thread do
+ attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event]
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
- authorize! :modify_merge_request, merge_request
+ authorize! :modify_merge_request, merge_request
- if merge_request.update_attributes attrs
- merge_request.reload_code
- merge_request.mark_as_unchecked
- present merge_request, with: Entities::MergeRequest
- else
- handle_merge_request_errors! merge_request.errors
+ if merge_request.update_attributes attrs
+ merge_request.reload_code
+ merge_request.mark_as_unchecked
+ present merge_request, with: Entities::MergeRequest
+ else
+ handle_merge_request_errors! merge_request.errors
+ end
end
end
@@ -133,16 +136,18 @@ module API
# POST /projects/:id/merge_request/:merge_request_id/comments
#
post ":id/merge_request/:merge_request_id/comments" do
- required_attributes! [:note]
+ set_current_user_for_thread do
+ required_attributes! [:note]
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
- note = merge_request.notes.new(note: params[:note], project_id: user_project.id)
- note.author = current_user
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+ note = merge_request.notes.new(note: params[:note], project_id: user_project.id)
+ note.author = current_user
- if note.save
- present note, with: Entities::MRNote
- else
- not_found!
+ if note.save
+ present note, with: Entities::MRNote
+ else
+ not_found!
+ end
end
end
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index aee12e7dc4..f7e63b2309 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -40,15 +40,17 @@ module API
# Example Request:
# POST /projects/:id/milestones
post ":id/milestones" do
- authorize! :admin_milestone, user_project
- required_attributes! [:title]
+ set_current_user_for_thread do
+ authorize! :admin_milestone, user_project
+ required_attributes! [:title]
- attrs = attributes_for_keys [:title, :description, :due_date]
- @milestone = user_project.milestones.new attrs
- if @milestone.save
- present @milestone, with: Entities::Milestone
- else
- not_found!
+ attrs = attributes_for_keys [:title, :description, :due_date]
+ @milestone = user_project.milestones.new attrs
+ if @milestone.save
+ present @milestone, with: Entities::Milestone
+ else
+ not_found!
+ end
end
end
@@ -64,14 +66,16 @@ module API
# Example Request:
# PUT /projects/:id/milestones/:milestone_id
put ":id/milestones/:milestone_id" do
- authorize! :admin_milestone, user_project
+ set_current_user_for_thread do
+ authorize! :admin_milestone, user_project
- @milestone = user_project.milestones.find(params[:milestone_id])
- attrs = attributes_for_keys [:title, :description, :due_date, :state_event]
- if @milestone.update_attributes attrs
- present @milestone, with: Entities::Milestone
- else
- not_found!
+ @milestone = user_project.milestones.find(params[:milestone_id])
+ attrs = attributes_for_keys [:title, :description, :due_date, :state_event]
+ if @milestone.update_attributes attrs
+ present @milestone, with: Entities::Milestone
+ else
+ not_found!
+ end
end
end
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index cb2bc76447..f21907b1ff 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -41,17 +41,19 @@ module API
# Example Request:
# POST /projects/:id/notes
post ":id/notes" do
- required_attributes! [:body]
+ set_current_user_for_thread do
+ required_attributes! [:body]
- @note = user_project.notes.new(note: params[:body])
- @note.author = current_user
+ @note = user_project.notes.new(note: params[:body])
+ @note.author = current_user
- if @note.save
- present @note, with: Entities::Note
- else
- # :note is exposed as :body, but :note is set on error
- bad_request!(:note) if @note.errors[:note].any?
- not_found!
+ if @note.save
+ present @note, with: Entities::Note
+ else
+ # :note is exposed as :body, but :note is set on error
+ bad_request!(:note) if @note.errors[:note].any?
+ not_found!
+ end
end
end
@@ -97,17 +99,19 @@ module API
# POST /projects/:id/issues/:noteable_id/notes
# POST /projects/:id/snippets/:noteable_id/notes
post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
- required_attributes! [:body]
+ set_current_user_for_thread do
+ required_attributes! [:body]
- @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
- @note = @noteable.notes.new(note: params[:body])
- @note.author = current_user
- @note.project = user_project
+ @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
+ @note = @noteable.notes.new(note: params[:body])
+ @note.author = current_user
+ @note.project = user_project
- if @note.save
- present @note, with: Entities::Note
- else
- not_found!
+ if @note.save
+ present @note, with: Entities::Note
+ else
+ not_found!
+ end
end
end
end
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 2850125679..738974955f 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -2,6 +2,7 @@ module API
# Projects API
class ProjectHooks < Grape::API
before { authenticate! }
+ before { authorize_admin_project }
resource :projects do
helpers do
@@ -20,7 +21,6 @@ module API
# Example Request:
# GET /projects/:id/hooks
get ":id/hooks" do
- authorize! :admin_project, user_project
@hooks = paginate user_project.hooks
present @hooks, with: Entities::Hook
end
@@ -33,7 +33,6 @@ module API
# Example Request:
# GET /projects/:id/hooks/:hook_id
get ":id/hooks/:hook_id" do
- authorize! :admin_project, user_project
@hook = user_project.hooks.find(params[:hook_id])
present @hook, with: Entities::Hook
end
@@ -47,7 +46,6 @@ module API
# Example Request:
# POST /projects/:id/hooks
post ":id/hooks" do
- authorize! :admin_project, user_project
required_attributes! [:url]
@hook = user_project.hooks.new({"url" => params[:url]})
@@ -71,7 +69,6 @@ module API
# PUT /projects/:id/hooks/:hook_id
put ":id/hooks/:hook_id" do
@hook = user_project.hooks.find(params[:hook_id])
- authorize! :admin_project, user_project
required_attributes! [:url]
attrs = attributes_for_keys [:url]
@@ -93,7 +90,6 @@ module API
# Example Request:
# DELETE /projects/:id/hooks/:hook_id
delete ":id/hooks/:hook_id" do
- authorize! :admin_project, user_project
required_attributes! [:hook_id]
begin
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 4b9dac9107..14cafe1f95 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -73,16 +73,16 @@ module API
post do
required_attributes! [:name]
attrs = attributes_for_keys [:name,
- :path,
- :description,
- :default_branch,
- :issues_enabled,
- :wall_enabled,
- :merge_requests_enabled,
- :wiki_enabled,
- :snippets_enabled,
- :namespace_id,
- :public]
+ :path,
+ :description,
+ :default_branch,
+ :issues_enabled,
+ :wall_enabled,
+ :merge_requests_enabled,
+ :wiki_enabled,
+ :snippets_enabled,
+ :namespace_id,
+ :public]
@project = ::Projects::CreateContext.new(current_user, attrs).execute
if @project.saved?
present @project, with: Entities::Project
@@ -113,14 +113,14 @@ module API
authenticated_as_admin!
user = User.find(params[:user_id])
attrs = attributes_for_keys [:name,
- :description,
- :default_branch,
- :issues_enabled,
- :wall_enabled,
- :merge_requests_enabled,
- :wiki_enabled,
- :snippets_enabled,
- :public]
+ :description,
+ :default_branch,
+ :issues_enabled,
+ :wall_enabled,
+ :merge_requests_enabled,
+ :wiki_enabled,
+ :snippets_enabled,
+ :public]
@project = ::Projects::CreateContext.new(user, attrs).execute
if @project.saved?
present @project, with: Entities::Project
@@ -129,6 +129,16 @@ module API
end
end
+ # Remove project
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # Example Request:
+ # DELETE /projects/:id
+ delete ":id" do
+ authorize! :remove_project, user_project
+ user_project.destroy
+ end
# Mark this project as forked from another
#
@@ -165,7 +175,6 @@ module API
end
end
-
# Get a project team members
#
# Parameters:
@@ -285,6 +294,20 @@ module API
render_api_error!(link.errors.full_messages.first, 409)
end
end
+
+ # search for projects current_user has access to
+ #
+ # Parameters:
+ # query (required) - A string contained in the project name
+ # per_page (optional) - number of projects to return per page
+ # page (optional) - the page to retrieve
+ # Example Request:
+ # GET /projects/search/:query
+ get "/search/:query" do
+ ids = current_user.authorized_projects.map(&:id)
+ projects = Project.where("(id in (?) OR public = true) AND (name LIKE (?))", ids, "%#{params[:query]}%")
+ present paginate(projects), with: Entities::Project
+ end
end
end
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index fef32d3a2f..c9422fdb16 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -2,6 +2,7 @@ module API
# Projects API
class Repositories < Grape::API
before { authenticate! }
+ before { authorize! :download_code, user_project }
resource :projects do
helpers do
@@ -44,13 +45,12 @@ module API
# Example Request:
# PUT /projects/:id/repository/branches/:branch/protect
put ":id/repository/branches/:branch/protect" do
- @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
- not_found! unless @branch
- protected = user_project.protected_branches.find_by_name(@branch.name)
+ authorize_admin_project
- unless protected
- user_project.protected_branches.create(name: @branch.name)
- end
+ @branch = user_project.repository.find_branch(params[:branch])
+ not_found! unless @branch
+ protected_branch = user_project.protected_branches.find_by_name(@branch.name)
+ user_project.protected_branches.create(name: @branch.name) unless protected_branch
present @branch, with: Entities::RepoObject, project: user_project
end
@@ -63,13 +63,12 @@ module API
# Example Request:
# PUT /projects/:id/repository/branches/:branch/unprotect
put ":id/repository/branches/:branch/unprotect" do
- @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
- not_found! unless @branch
- protected = user_project.protected_branches.find_by_name(@branch.name)
+ authorize_admin_project
- if protected
- protected.destroy
- end
+ @branch = user_project.repository.find_branch(params[:branch])
+ not_found! unless @branch
+ protected_branch = user_project.protected_branches.find_by_name(@branch.name)
+ protected_branch.destroy if protected_branch
present @branch, with: Entities::RepoObject, project: user_project
end
@@ -92,8 +91,6 @@ module API
# Example Request:
# GET /projects/:id/repository/commits
get ":id/repository/commits" do
- authorize! :download_code, user_project
-
page = (params[:page] || 0).to_i
per_page = (params[:per_page] || 20).to_i
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
@@ -110,7 +107,6 @@ module API
# Example Request:
# GET /projects/:id/repository/commits/:sha
get ":id/repository/commits/:sha" do
- authorize! :download_code, user_project
sha = params[:sha]
commit = user_project.repository.commit(sha)
not_found! "Commit" unless commit
@@ -125,7 +121,6 @@ module API
# Example Request:
# GET /projects/:id/repository/commits/:sha/diff
get ":id/repository/commits/:sha/diff" do
- authorize! :download_code, user_project
sha = params[:sha]
result = CommitLoadContext.new(user_project, current_user, {id: sha}).execute
not_found! "Commit" unless result[:commit]
@@ -140,18 +135,16 @@ module API
# Example Request:
# GET /projects/:id/repository/tree
get ":id/repository/tree" do
- authorize! :download_code, user_project
-
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
path = params[:path] || nil
commit = user_project.repository.commit(ref)
- tree = Tree.new(user_project.repository, commit.id, ref, path)
+ tree = Tree.new(user_project.repository, commit.id, path)
trees = []
%w(trees blobs submodules).each do |type|
- trees += tree.send(type).map { |t| { name: t.name, type: type.singularize, mode: t.mode, id: t.id } }
+ trees += tree.send(type).map { |t| {name: t.name, type: type.singularize, mode: t.mode, id: t.id} }
end
trees
@@ -166,7 +159,6 @@ module API
# Example Request:
# GET /projects/:id/repository/blobs/:sha
get [ ":id/repository/blobs/:sha", ":id/repository/commits/:sha/blob" ] do
- authorize! :download_code, user_project
required_attributes! [:filepath]
ref = params[:sha]
@@ -176,14 +168,42 @@ module API
commit = repo.commit(ref)
not_found! "Commit" unless commit
- blob = Gitlab::Git::Blob.new(repo, commit.id, ref, params[:filepath])
- not_found! "File" unless blob.exists?
+ blob = Gitlab::Git::Blob.find(repo, commit.id, params[:filepath])
+ not_found! "File" unless blob
env['api.format'] = :txt
content_type blob.mime_type
present blob.data
end
+
+ # Get a an archive of the repository
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # sha (optional) - the commit sha to download defaults to the tip of the default branch
+ # Example Request:
+ # GET /projects/:id/repository/archive
+ get ":id/repository/archive" do
+ authorize! :download_code, user_project
+ repo = user_project.repository
+ ref = params[:sha]
+ storage_path = Rails.root.join("tmp", "repositories")
+
+ file_path = repo.archive_repo(ref, storage_path)
+ if file_path && File.exists?(file_path)
+ data = File.open(file_path, 'rb').read
+
+ header "Content-Disposition:", " infile; filename=\"#{File.basename(file_path)}\""
+ content_type 'application/x-gzip'
+
+ env['api.format'] = :binary
+
+ present data
+ else
+ not_found!
+ end
+ end
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 00dc2311ff..54d3aeecb7 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -40,13 +40,17 @@ module API
# extern_uid - External authentication provider UID
# provider - External provider
# bio - Bio
+ # admin - User is admin - true or false (default)
+ # can_create_group - User can create groups - true or false
# Example Request:
# POST /users
post do
authenticated_as_admin!
required_attributes! [:email, :password, :name, :username]
- attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio]
+ attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio, :can_create_group, :admin]
user = User.build_user(attrs, as: :admin)
+ admin = attrs.delete(:admin)
+ user.admin = admin unless admin.nil?
if user.save
present user, with: Entities::User
else
@@ -67,16 +71,20 @@ module API
# extern_uid - External authentication provider UID
# provider - External provider
# bio - Bio
+ # admin - User is admin - true or false (default)
+ # can_create_group - User can create groups - true or false
# Example Request:
# PUT /users/:id
put ":id" do
authenticated_as_admin!
- attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio]
+ attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio, :can_create_group, :admin]
user = User.find(params[:id])
not_found!("User not found") unless user
- if user.update_attributes(attrs)
+ admin = attrs.delete(:admin)
+ user.admin = admin unless admin.nil?
+ if user.update_attributes(attrs, as: :admin)
present user, with: Entities::User
else
not_found!
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index c5e3d049fd..252201f11b 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -28,7 +28,9 @@ module Backup
if File.exists?(path_to_repo(wiki))
print " * #{wiki.path_with_namespace} ... "
- if system("cd #{path_to_repo(wiki)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(wiki)} --all > /dev/null 2>&1")
+ if wiki.empty?
+ puts " [SKIPPED]".cyan
+ elsif system("cd #{path_to_repo(wiki)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(wiki)} --all > /dev/null 2>&1")
puts " [DONE]".green
else
puts " [FAILED]".red
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index 53bc079296..6e7872dcd0 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -86,7 +86,6 @@ module ExtractsPath
# - @ref - A string representing the ref (e.g., the branch, tag, or commit SHA)
# - @path - A string representing the filesystem path
# - @commit - A Commit representing the commit from the given ref
- # - @tree - A Tree representing the tree at the given ref/path
#
# If the :id parameter appears to be requesting a specific response format,
# that will be handled as well.
@@ -107,15 +106,20 @@ module ExtractsPath
else
@commit = @repo.commit(@options[:extended_sha1])
end
- @tree = Tree.new(@repo, @commit.id, @ref, @path)
+
+ raise InvalidPathError unless @commit
+
@hex_path = Digest::SHA1.hexdigest(@path)
@logs_path = logs_file_project_ref_path(@project, @ref, @path)
- raise InvalidPathError unless @tree.exists?
rescue RuntimeError, NoMethodError, InvalidPathError
not_found!
end
+ def tree
+ @tree ||= Tree.new(@repo, @commit.id, @path)
+ end
+
private
def get_id
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 06c1e37063..4c5b63261a 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -26,7 +26,7 @@ module Gitlab
# * When user already has account and need to link his LDAP account.
# * LDAP uid changed for user with same email and we need to update his uid
#
- user = model.find_by_email(email)
+ user = find_user(email)
if user
user.update_attributes(extern_uid: uid, provider: provider)
@@ -43,6 +43,19 @@ module Gitlab
user
end
+ def find_user(email)
+ user = model.find_by_email(email)
+
+ # If no user found and allow_username_or_email_login is true
+ # we look for user by extracting part of his email
+ if !user && email && ldap_conf['allow_username_or_email_login']
+ uname = email.partition('@').first
+ user = model.find_by_username(uname)
+ end
+
+ user
+ end
+
def authenticate(login, password)
# Check user against LDAP backend if user is not authenticated
# Only check with valid login and password to prevent anonymous bind results
@@ -58,6 +71,16 @@ module Gitlab
find_by_uid(ldap_user.dn) if ldap_user
end
+ # Check LDAP user existance by dn. User in git over ssh check
+ #
+ # It covers 2 cases:
+ # * when ldap account was removed
+ # * when ldap account was deactivated by change of OU membership in 'dn'
+ def blocked?(dn)
+ ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
+ ldap.connection.search(base: dn, size: 1).blank?
+ end
+
private
def find_by_uid(uid)
diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb
index 1b32b99f4b..ea9badba2c 100644
--- a/lib/gitlab/oauth/user.rb
+++ b/lib/gitlab/oauth/user.rb
@@ -29,6 +29,7 @@ module Gitlab
user = model.build_user(opts, as: :admin)
user.save!
+ user.confirm!
log.info "(OAuth) Creating user #{email} from login with extern_uid => #{uid}"
if Gitlab.config.omniauth['block_auto_created_users'] && !ldap?
diff --git a/lib/gitlab/satellite/merge_action.rb b/lib/gitlab/satellite/merge_action.rb
index 156483be8d..d74d4194ff 100644
--- a/lib/gitlab/satellite/merge_action.rb
+++ b/lib/gitlab/satellite/merge_action.rb
@@ -146,10 +146,8 @@ module Gitlab
repo.remote_fetch('source')
repo.git.checkout(default_options({b: true}), merge_request.target_branch, "origin/#{merge_request.target_branch}")
else
- # We can't trust the input here being branch names, we can't always check it out because it could be a relative ref i.e. HEAD~3
- # we could actually remove the if true, because it should never ever happen (as long as the satellite has been prepared)
repo.git.checkout(default_options, "#{merge_request.source_branch}")
- repo.git.checkout(default_options, "#{merge_request.target_branch}")
+ repo.git.checkout(default_options({t: true}), "origin/#{merge_request.target_branch}")
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index d9c2d3b626..b84c005524 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -6,6 +6,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
def initialize(template, options = {})
@template = template
@project = @template.instance_variable_get("@project")
+ @ref = @template.instance_variable_get("@ref")
+ @request_path = @template.instance_variable_get("@path")
super options
end
@@ -32,7 +34,19 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
h.link_to_gfm(content, link, title: title)
end
+ def preprocess(full_document)
+ if @project
+ h.create_relative_links(full_document, @project.path_with_namespace, @ref, @request_path, is_wiki?)
+ else
+ full_document
+ end
+ end
+
def postprocess(full_document)
h.gfm(full_document)
end
+
+ def is_wiki?
+ @template.instance_variable_get("@wiki")
+ end
end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index dbdf7db114..26ca50c22f 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -22,7 +22,6 @@ RAILS_ENV="production"
# /bin/sh variables such as PATH, EDITOR or SHELL.
app_root="/home/git/gitlab"
app_user="git"
-unicorn_conf="$app_root/config/unicorn.rb"
pid_path="$app_root/tmp/pids"
socket_path="$app_root/tmp/sockets"
web_server_pid_path="$pid_path/unicorn.pid"
@@ -129,7 +128,7 @@ start() {
# Remove old socket if it exists
rm -f "$socket_path"/gitlab.socket 2>/dev/null
# Start the webserver
- bundle exec unicorn_rails -D -c "$unicorn_conf" -E "$RAILS_ENV"
+ RAILS_ENV=$RAILS_ENV script/web start
fi
# If sidekiq is already running, don't start it again.
@@ -137,7 +136,7 @@ start() {
echo "The Sidekiq job dispatcher is already running with pid $spid, not restarting"
else
echo "Starting the GitLab Sidekiq event dispatcher..."
- RAILS_ENV=$RAILS_ENV bundle exec rake sidekiq:start
+ RAILS_ENV=$RAILS_ENV script/background_jobs start
# We are sleeping a bit here because sidekiq is slow at writing it's pid
sleep 2
fi
@@ -151,7 +150,7 @@ stop() {
exit_if_not_running
# If the Unicorn web server is running, tell it to stop;
if [ "$web_status" = "0" ]; then
- kill -QUIT "$wpid" &
+ RAILS_ENV=$RAILS_ENV script/web stop
echo "Stopping the GitLab Unicorn web server..."
stopping=true
else
@@ -160,7 +159,7 @@ stop() {
# And do the same thing for the Sidekiq.
if [ "$sidekiq_status" = "0" ]; then
printf "Stopping Sidekiq job dispatcher."
- RAILS_ENV=$RAILS_ENV bundle exec rake sidekiq:stop &
+ RAILS_ENV=$RAILS_ENV script/background_jobs stop
stopping=true
else
echo "The Sidekiq was not running, must have run out of breath."
@@ -204,7 +203,7 @@ status() {
printf "The GitLab Sidekiq job dispatcher is \033[31mnot running\033[0m.\n"
fi
if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then
- printf "GitLab and all it's components are \033[32mup and running\033[0m.\n"
+ printf "GitLab and all its components are \033[32mup and running\033[0m.\n"
fi
}
@@ -215,12 +214,10 @@ reload(){
exit 1
fi
printf "Reloading GitLab Unicorn configuration... "
- kill -USR2 "$wpid"
+ RAILS_ENV=$RAILS_ENV script/web reload
echo "Done."
echo "Restarting GitLab Sidekiq since it isn't capable of reloading its config..."
- RAILS_ENV=$RAILS_ENV bundle exec rake sidekiq:stop
- echo "Starting Sidekiq..."
- RAILS_ENV=$RAILS_ENV bundle exec rake sidekiq:start
+ RAILS_ENV=$RAILS_ENV script/background_jobs restart
# Waiting 2 seconds for sidekiq to write it.
sleep 2
status
diff --git a/lib/support/logrotate/gitlab b/lib/support/logrotate/gitlab
new file mode 100644
index 0000000000..df9398d079
--- /dev/null
+++ b/lib/support/logrotate/gitlab
@@ -0,0 +1,22 @@
+# GitLab logrotate settings
+# based on: http://stackoverflow.com/a/4883967
+
+/home/git/gitlab/log/*.log {
+ weekly
+ missingok
+ rotate 52
+ compress
+ delaycompress
+ notifempty
+ copytruncate
+}
+
+/home/git/gitlab-shell/gitlab-shell.log {
+ weekly
+ missingok
+ rotate 52
+ compress
+ delaycompress
+ notifempty
+ copytruncate
+}
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index c445c0fbdd..e01f3b23d0 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -289,7 +289,6 @@ namespace :gitlab do
########################
def check_gitlab_git_config
- gitlab_user = Gitlab.config.gitlab.user
print "Git configured for #{gitlab_user} user? ... "
options = {
@@ -377,6 +376,7 @@ namespace :gitlab do
check_repo_base_permissions
check_update_hook_is_up_to_date
check_repos_update_hooks_is_link
+ check_gitlab_shell_self_test
finished_checking "GitLab Shell"
end
@@ -392,14 +392,20 @@ namespace :gitlab do
hook_file = "update"
gitlab_shell_hooks_path = Gitlab.config.gitlab_shell.hooks_path
gitlab_shell_hook_file = File.join(gitlab_shell_hooks_path, hook_file)
- gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
- unless File.exists?(gitlab_shell_hook_file)
- puts "can't check because of previous errors".magenta
- return
+ if File.exists?(gitlab_shell_hook_file)
+ puts "yes".green
+ else
+ puts "no".red
+ puts "Could not find #{gitlab_shell_hook_file}"
+ try_fixing_it(
+ 'Check the hooks_path in config/gitlab.yml',
+ 'Check your gitlab-shell installation'
+ )
+ for_more_information(
+ see_installation_guide_section "GitLab Shell"
+ )
end
-
- puts "yes".green
end
def check_repo_base_exists
@@ -553,6 +559,23 @@ namespace :gitlab do
end
end
+ def check_gitlab_shell_self_test
+ gitlab_shell_repo_base = File.expand_path('gitlab-shell', gitlab_shell_user_home)
+ check_cmd = File.expand_path('bin/check', gitlab_shell_repo_base)
+ puts "Running #{check_cmd}"
+ if system(check_cmd, chdir: gitlab_shell_repo_base)
+ puts 'gitlab-shell self-check successful'.green
+ else
+ puts 'gitlab-shell self-check failed'.red
+ try_fixing_it(
+ 'Make sure GitLab is running;',
+ 'Check the gitlab-shell configuration file:',
+ sudo_gitlab("editor #{File.expand_path('config.yml', gitlab_shell_repo_base)}")
+ )
+ fix_and_rerun
+ end
+ end
+
def check_projects_have_namespace
print "projects have namespace: ... "
@@ -608,6 +631,7 @@ namespace :gitlab do
start_checking "Sidekiq"
check_sidekiq_running
+ only_one_sidekiq_running
finished_checking "Sidekiq"
end
@@ -619,7 +643,7 @@ namespace :gitlab do
def check_sidekiq_running
print "Running? ... "
- if run_and_match("ps aux | grep -i sidekiq", /sidekiq \d+\.\d+\.\d+.+$/)
+ if sidekiq_process_count > 0
puts "yes".green
else
puts "no".red
@@ -633,6 +657,29 @@ namespace :gitlab do
fix_and_rerun
end
end
+
+ def only_one_sidekiq_running
+ process_count = sidekiq_process_count
+ return if process_count.zero?
+
+ print 'Number of Sidekiq processes ... '
+ if process_count == 1
+ puts '1'.green
+ else
+ puts "#{process_count}".red
+ try_fixing_it(
+ 'sudo service gitlab stop',
+ "sudo pkill -u #{gitlab_user} -f sidekiq",
+ "sleep 10 && sudo pkill -9 -u #{gitlab_user} -f sidekiq",
+ 'sudo service gitlab start'
+ )
+ fix_and_rerun
+ end
+ end
+
+ def sidekiq_process_count
+ `ps ux`.scan(/sidekiq \d+\.\d+\.\d+/).count
+ end
end
@@ -667,10 +714,13 @@ namespace :gitlab do
end
def sudo_gitlab(command)
- gitlab_user = Gitlab.config.gitlab.user
"sudo -u #{gitlab_user} -H #{command}"
end
+ def gitlab_user
+ Gitlab.config.gitlab.user
+ end
+
def start_checking(component)
puts "Checking #{component.yellow} ..."
puts ""
diff --git a/lib/tasks/gitlab/test.rake b/lib/tasks/gitlab/test.rake
index 03b3fc5ea2..011748c971 100644
--- a/lib/tasks/gitlab/test.rake
+++ b/lib/tasks/gitlab/test.rake
@@ -1,4 +1,18 @@
namespace :gitlab do
- desc "GITLAB | Run both spinach and rspec"
- task test: ['spinach', 'spec']
+ desc "GITLAB | Run all tests"
+ task :test do
+ cmds = [
+ "rake db:setup",
+ "rake db:seed_fu",
+ "rake spinach",
+ "rake spec",
+ "rake jasmine:ci"
+ ]
+
+ cmds.each do |cmd|
+ system(cmd + " RAILS_ENV=test")
+
+ raise "#{cmd} failed!" unless $?.exitstatus.zero?
+ end
+ end
end
diff --git a/lib/tasks/gitlab/web_hook.rake b/lib/tasks/gitlab/web_hook.rake
new file mode 100644
index 0000000000..f9f586db93
--- /dev/null
+++ b/lib/tasks/gitlab/web_hook.rake
@@ -0,0 +1,65 @@
+namespace :gitlab do
+ namespace :web_hook do
+ desc "GITLAB | Adds a web hook to the projects"
+ task :add => :environment do
+ web_hook_url = ENV['URL']
+ namespace_path = ENV['NAMESPACE']
+
+ projects = find_projects(namespace_path)
+
+ puts "Adding web hook '#{web_hook_url}' to:"
+ projects.find_each(batch_size: 1000) do |project|
+ print "- #{project.name} ... "
+ web_hook = project.hooks.new(url: web_hook_url)
+ if web_hook.save
+ puts "added".green
+ else
+ print "failed".red
+ puts " [#{web_hook.errors.full_messages.to_sentence}]"
+ end
+ end
+ end
+
+ desc "GITLAB | Remove a web hook from the projects"
+ task :rm => :environment do
+ web_hook_url = ENV['URL']
+ namespace_path = ENV['NAMESPACE']
+
+ projects = find_projects(namespace_path)
+ projects_ids = projects.pluck(:id)
+
+ puts "Removing web hooks with the url '#{web_hook_url}' ... "
+ count = WebHook.where(url: web_hook_url, project_id: projects_ids, type: 'ProjectHook').delete_all
+ puts "#{count} web hooks were removed."
+ end
+
+ desc "GITLAB | List web hooks"
+ task :list => :environment do
+ namespace_path = ENV['NAMESPACE']
+
+ projects = find_projects(namespace_path)
+ web_hooks = projects.all.map(&:hooks).flatten
+ web_hooks.each do |hook|
+ puts "#{hook.project.name.truncate(20).ljust(20)} -> #{hook.url}"
+ end
+
+ puts "\n#{web_hooks.size} web hooks found."
+ end
+ end
+
+ def find_projects(namespace_path)
+ if namespace_path.blank?
+ Project
+ elsif namespace_path == '/'
+ Project.where(namespace_id: nil)
+ else
+ namespace = Namespace.where(path: namespace_path).first
+ if namespace
+ Project.where(namespace_id: namespace.id)
+ else
+ puts "Namespace not found: #{namespace_path}".red
+ exit 2
+ end
+ end
+ end
+end
diff --git a/lib/tasks/sidekiq.rake b/lib/tasks/sidekiq.rake
index d0e9dfe46a..23d41f8ed2 100644
--- a/lib/tasks/sidekiq.rake
+++ b/lib/tasks/sidekiq.rake
@@ -1,20 +1,19 @@
namespace :sidekiq do
desc "GITLAB | Stop sidekiq"
task :stop do
- system "bundle exec sidekiqctl stop #{pidfile}"
+ system "script/background_jobs stop"
end
- desc "GITLAB | Start sidekiq"
- task :start do
- system "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
+ desc "GITLAB | Start sidekiq" do
+ system "script/background_jobs start"
+ end
+
+ desc 'GitLab | Restart sidekiq' do
+ system "script/background_jobs restart"
end
desc "GITLAB | Start sidekiq with launchd on Mac OS X"
task :launchd do
- system "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1"
- end
-
- def pidfile
- Rails.root.join("tmp", "pids", "sidekiq.pid")
+ system "script/background_jobs start_no_deamonize"
end
end
diff --git a/lib/tasks/travis.rake b/lib/tasks/travis.rake
deleted file mode 100644
index bc1b8aadbc..0000000000
--- a/lib/tasks/travis.rake
+++ /dev/null
@@ -1,5 +0,0 @@
-desc "Travis run tests"
-task travis: [
- :spinach,
- :spec
-]
diff --git a/script/background_jobs b/script/background_jobs
new file mode 100755
index 0000000000..623e26a283
--- /dev/null
+++ b/script/background_jobs
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+cd $(dirname $0)/..
+app_root=$(pwd)
+sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid"
+sidekiq_logfile="$app_root/log/sidekiq.log"
+gitlab_user=$(ls -l config.ru | awk '{print $3}')
+
+function stop
+{
+ bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1
+}
+
+function killall
+{
+ pkill -u $gitlab_user -f sidekiq
+}
+
+function restart
+{
+ if [ -f $sidekiq_pidfile ]; then
+ stop
+ fi
+ killall
+ start_sidekiq -d -L $sidekiq_logfile
+}
+
+function start_no_deamonize
+{
+ start_sidekiq
+}
+
+function start_sidekiq
+{
+ bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
+}
+
+case "$1" in
+ stop)
+ stop
+ ;;
+ start)
+ restart
+ ;;
+ start_no_deamonize)
+ start_no_deamonize
+ ;;
+ restart)
+ restart
+ ;;
+ killall)
+ killall
+ ;;
+ *)
+ echo "Usage: RAILS_ENV=your_env $0 {stop|start|start_no_deamonize|restart|killall}"
+esac
diff --git a/script/web b/script/web
new file mode 100755
index 0000000000..5464ed040a
--- /dev/null
+++ b/script/web
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+cd $(dirname $0)/..
+app_root=$(pwd)
+
+unicorn_pidfile="$app_root/tmp/pids/unicorn.pid"
+unicorn_config="$app_root/config/unicorn.rb"
+
+function get_unicorn_pid
+{
+ local pid=$(cat $unicorn_pidfile)
+ if [ -z $pid ] ; then
+ echo "Could not find a PID in $unicorn_pidfile"
+ exit 1
+ fi
+ unicorn_pid=$pid
+}
+
+function start
+{
+ bundle exec unicorn_rails -D -c $unicorn_config -E $RAILS_ENV
+}
+
+function stop
+{
+ get_unicorn_pid
+ kill -QUIT $unicorn_pid
+}
+
+function reload
+{
+ get_unicorn_pid
+ kill -USR2 $unicorn_pid
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ reload)
+ reload
+ ;;
+ *)
+ echo "Usage: RAILS_ENV=your_env $0 {start|stop|reload}"
+ ;;
+esac
diff --git a/spec/contexts/projects_create_context_spec.rb b/spec/contexts/projects_create_context_spec.rb
index 7b5fa530a8..8b2a49dbee 100644
--- a/spec/contexts/projects_create_context_spec.rb
+++ b/spec/contexts/projects_create_context_spec.rb
@@ -25,13 +25,15 @@ describe Projects::CreateContext do
context 'group namespace' do
before do
- @group = create :group, owner: @user
+ @group = create :group
+ @group.add_owner(@user)
+
@opts.merge!(namespace_id: @group.id)
@project = create_project(@user, @opts)
end
it { @project.should be_valid }
- it { @project.owner.should == @user }
+ it { @project.owner.should == @group }
it { @project.namespace.should == @group }
end
diff --git a/spec/factories.rb b/spec/factories.rb
index 4c5e687ac3..624cb0f765 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -17,6 +17,8 @@ FactoryGirl.define do
sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" }
password "123456"
password_confirmation { password }
+ confirmed_at { Time.now }
+ confirmation_token { nil }
trait :admin do
admin true
@@ -71,7 +73,6 @@ FactoryGirl.define do
factory :group do
sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') }
- owner
type 'Group'
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 6fff59f036..8cb984946b 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -24,7 +24,7 @@ describe "Issues" do
end
it "should open new issue popup" do
- page.should have_content("Issue ##{issue.id}")
+ page.should have_content("Issue ##{issue.iid}")
end
describe "fill in" do
@@ -45,40 +45,6 @@ describe "Issues" do
end
end
- describe "Search issue", js: true do
- before do
- ['foobar', 'foobar2', 'gitlab'].each do |title|
- create(:issue,
- author: @user,
- assignee: @user,
- project: project,
- title: title)
- end
- end
-
- it "should be able to search on different statuses" do
- issue = Issue.first # with title 'foobar'
- issue.close
-
- visit project_issues_path(project)
- click_link 'Closed'
- fill_in 'issue_search', with: 'foobar'
-
- page.should have_content 'foobar'
- page.should_not have_content 'foobar2'
- page.should_not have_content 'gitlab'
- end
-
- it "should search for term and return the correct results" do
- visit project_issues_path(project)
- fill_in 'issue_search', with: 'foobar'
-
- page.should have_content 'foobar'
- page.should have_content 'foobar2'
- page.should_not have_content 'gitlab'
- end
- end
-
describe "Filter issue" do
before do
['foobar', 'barbaz', 'gitlab'].each do |title|
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index ba580d9484..d29bed4dea 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -216,12 +216,6 @@ describe "On a merge request diff", js: true, focus: true do
end
end
- it do
- within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do
- should have_no_css(".js-temp-notes-holder")
- end
- end
-
it 'should be added as discussion' do
should have_content("Another comment on line 17")
should have_css(".notes_holder")
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index 80c9f5d7f1..b67ce3c67f 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -12,7 +12,7 @@ describe "Profile account page" do
describe "when signup is enabled" do
before do
Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
- visit account_profile_path
+ visit profile_account_path
end
it { page.should have_content("Remove account") }
@@ -26,12 +26,12 @@ describe "Profile account page" do
describe "when signup is disabled" do
before do
Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)
- visit account_profile_path
+ visit profile_account_path
end
it "should not have option to remove account" do
page.should_not have_content("Remove account")
- current_path.should == account_profile_path
+ current_path.should == profile_account_path
end
end
end
diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb
new file mode 100644
index 0000000000..adec5926c6
--- /dev/null
+++ b/spec/features/security/dashboard_access_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe "Dashboard access" do
+ describe "GET /dashboard" do
+ subject { dashboard_path }
+
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /dashboard/issues" do
+ subject { issues_dashboard_path }
+
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /dashboard/merge_requests" do
+ subject { merge_requests_dashboard_path }
+
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /dashboard/projects" do
+ subject { projects_dashboard_path }
+
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /help" do
+ subject { help_path }
+
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /projects/new" do
+ it { new_project_path.should be_allowed_for :admin }
+ it { new_project_path.should be_allowed_for :user }
+ it { new_project_path.should be_denied_for :visitor }
+ end
+
+ describe "GET /groups/new" do
+ it { new_group_path.should be_allowed_for :admin }
+ it { new_group_path.should be_allowed_for :user }
+ it { new_group_path.should be_denied_for :visitor }
+ end
+end
diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb
new file mode 100644
index 0000000000..dea957962a
--- /dev/null
+++ b/spec/features/security/group_access_spec.rb
@@ -0,0 +1,85 @@
+require 'spec_helper'
+
+describe "Group access" do
+ describe "GET /projects/new" do
+ it { new_group_path.should be_allowed_for :admin }
+ it { new_group_path.should be_allowed_for :user }
+ it { new_group_path.should be_denied_for :visitor }
+ end
+
+ describe "Group" do
+ let(:group) { create(:group) }
+
+ let(:owner) { create(:owner) }
+ let(:master) { create(:user) }
+ let(:reporter) { create(:user) }
+ let(:guest) { create(:user) }
+
+ before do
+ group.add_user(owner, Gitlab::Access::OWNER)
+ group.add_user(master, Gitlab::Access::MASTER)
+ group.add_user(reporter, Gitlab::Access::REPORTER)
+ group.add_user(guest, Gitlab::Access::GUEST)
+ end
+
+ describe "GET /groups/:path" do
+ subject { group_path(group) }
+
+ it { should be_allowed_for owner }
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /groups/:path/issues" do
+ subject { issues_group_path(group) }
+
+ it { should be_allowed_for owner }
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /groups/:path/merge_requests" do
+ subject { merge_requests_group_path(group) }
+
+ it { should be_allowed_for owner }
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /groups/:path/members" do
+ subject { members_group_path(group) }
+
+ it { should be_allowed_for owner }
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /groups/:path/edit" do
+ subject { edit_group_path(group) }
+
+ it { should be_allowed_for owner }
+ it { should be_denied_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+ end
+end
diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb
index 52130b3f8c..078c257538 100644
--- a/spec/features/security/profile_access_spec.rb
+++ b/spec/features/security/profile_access_spec.rb
@@ -29,7 +29,7 @@ describe "Users Security" do
end
describe "GET /profile/account" do
- subject { account_profile_path }
+ subject { profile_account_path }
it { should be_allowed_for @u1 }
it { should be_allowed_for :admin }
@@ -45,5 +45,32 @@ describe "Users Security" do
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
+
+ describe "GET /profile/history" do
+ subject { history_profile_path }
+
+ it { should be_allowed_for @u1 }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /profile/notifications" do
+ subject { profile_notifications_path }
+
+ it { should be_allowed_for @u1 }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /profile/groups" do
+ subject { profile_groups_path }
+
+ it { should be_allowed_for @u1 }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
end
end
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
new file mode 100644
index 0000000000..7f3f8c50f0
--- /dev/null
+++ b/spec/features/security/project/private_access_spec.rb
@@ -0,0 +1,218 @@
+require 'spec_helper'
+
+describe "Private Project Access" do
+ let(:project) { create(:project_with_code) }
+
+ let(:master) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:reporter) { create(:user) }
+
+ before do
+ # full access
+ project.team << [master, :master]
+
+ # readonly
+ project.team << [reporter, :reporter]
+ end
+
+ describe "GET /:project_path" do
+ subject { project_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/tree/master" do
+ subject { project_tree_path(project, project.repository.root_ref) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/commits/master" do
+ subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/commit/:sha" do
+ subject { project_commit_path(project, project.repository.commit) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/compare" do
+ subject { project_compare_index_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/team" do
+ subject { project_team_index_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/wall" do
+ subject { project_wall_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/blob" do
+ before do
+ commit = project.repository.commit
+ path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
+ @blob_path = project_blob_path(project, File.join(commit.id, path))
+ end
+
+ it { @blob_path.should be_allowed_for master }
+ it { @blob_path.should be_allowed_for reporter }
+ it { @blob_path.should be_allowed_for :admin }
+ it { @blob_path.should be_denied_for guest }
+ it { @blob_path.should be_denied_for :user }
+ it { @blob_path.should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/edit" do
+ subject { edit_project_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/deploy_keys" do
+ subject { project_deploy_keys_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/issues" do
+ subject { project_issues_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets" do
+ subject { project_snippets_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/merge_requests" do
+ subject { project_merge_requests_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/branches/recent" do
+ subject { recent_project_branches_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/branches" do
+ subject { project_branches_path(project) }
+
+ before do
+ # Speed increase
+ Project.any_instance.stub(:branches).and_return([])
+ end
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/tags" do
+ subject { project_tags_path(project) }
+
+ before do
+ # Speed increase
+ Project.any_instance.stub(:tags).and_return([])
+ end
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/hooks" do
+ subject { project_hooks_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+end
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
new file mode 100644
index 0000000000..267643fd8e
--- /dev/null
+++ b/spec/features/security/project/public_access_spec.rb
@@ -0,0 +1,251 @@
+require 'spec_helper'
+
+describe "Public Project Access" do
+ let(:project) { create(:project_with_code) }
+
+ let(:master) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:reporter) { create(:user) }
+
+ before do
+ # public project
+ project.public = true
+ project.save!
+
+ # full access
+ project.team << [master, :master]
+
+ # readonly
+ project.team << [reporter, :reporter]
+
+ end
+
+ describe "Project should be public" do
+ subject { project }
+
+ its(:public?) { should be_true }
+ end
+
+ describe "GET /:project_path" do
+ subject { project_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/tree/master" do
+ subject { project_tree_path(project, project.repository.root_ref) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/commits/master" do
+ subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/commit/:sha" do
+ subject { project_commit_path(project, project.repository.commit) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/compare" do
+ subject { project_compare_index_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/team" do
+ subject { project_team_index_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/wall" do
+ subject { project_wall_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/blob" do
+ before do
+ commit = project.repository.commit
+ path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
+ @blob_path = project_blob_path(project, File.join(commit.id, path))
+ end
+
+ it { @blob_path.should be_allowed_for master }
+ it { @blob_path.should be_allowed_for reporter }
+ it { @blob_path.should be_allowed_for :admin }
+ it { @blob_path.should be_allowed_for guest }
+ it { @blob_path.should be_allowed_for :user }
+ it { @blob_path.should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/edit" do
+ subject { edit_project_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/deploy_keys" do
+ subject { project_deploy_keys_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/issues" do
+ subject { project_issues_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets" do
+ subject { project_snippets_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/new" do
+ subject { new_project_snippet_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/merge_requests" do
+ subject { project_merge_requests_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/merge_requests/new" do
+ subject { new_project_merge_request_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/branches/recent" do
+ subject { recent_project_branches_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/branches" do
+ subject { project_branches_path(project) }
+
+ before do
+ # Speed increase
+ Project.any_instance.stub(:branches).and_return([])
+ end
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/tags" do
+ subject { project_tags_path(project) }
+
+ before do
+ # Speed increase
+ Project.any_instance.stub(:tags).and_return([])
+ end
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/hooks" do
+ subject { project_hooks_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+end
diff --git a/spec/features/security/project_access_spec.rb b/spec/features/security/project_access_spec.rb
deleted file mode 100644
index d0964a947d..0000000000
--- a/spec/features/security/project_access_spec.rb
+++ /dev/null
@@ -1,474 +0,0 @@
-require 'spec_helper'
-
-describe "Application access" do
- describe "GET /" do
- it { root_path.should be_allowed_for :admin }
- it { root_path.should be_allowed_for :user }
- it { root_path.should be_denied_for :visitor }
- end
-
- describe "GET /projects/new" do
- it { new_project_path.should be_allowed_for :admin }
- it { new_project_path.should be_allowed_for :user }
- it { new_project_path.should be_denied_for :visitor }
- end
-
- describe "Project" do
- let(:project) { create(:project_with_code) }
-
- let(:master) { create(:user) }
- let(:guest) { create(:user) }
- let(:reporter) { create(:user) }
-
- before do
- # full access
- project.team << [master, :master]
-
- # readonly
- project.team << [reporter, :reporter]
- end
-
- describe "GET /project_code" do
- subject { project_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/tree/master" do
- subject { project_tree_path(project, project.repository.root_ref) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/commits/master" do
- subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/commit/:sha" do
- subject { project_commit_path(project, project.repository.commit) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/compare" do
- subject { project_compare_index_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/team" do
- subject { project_team_index_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/wall" do
- subject { project_wall_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/blob" do
- before do
- commit = project.repository.commit
- path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
- @blob_path = project_blob_path(project, File.join(commit.id, path))
- end
-
- it { @blob_path.should be_allowed_for master }
- it { @blob_path.should be_allowed_for reporter }
- it { @blob_path.should be_allowed_for :admin }
- it { @blob_path.should be_denied_for guest }
- it { @blob_path.should be_denied_for :user }
- it { @blob_path.should be_denied_for :visitor }
- end
-
- describe "GET /project_code/edit" do
- subject { edit_project_path(project) }
-
- it { should be_allowed_for master }
- it { should be_denied_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/deploy_keys" do
- subject { project_deploy_keys_path(project) }
-
- it { should be_allowed_for master }
- it { should be_denied_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/issues" do
- subject { project_issues_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/snippets" do
- subject { project_snippets_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/merge_requests" do
- subject { project_merge_requests_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/branches/recent" do
- subject { recent_project_branches_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/branches" do
- subject { project_branches_path(project) }
-
- before do
- # Speed increase
- Project.any_instance.stub(:branches).and_return([])
- end
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/tags" do
- subject { project_tags_path(project) }
-
- before do
- # Speed increase
- Project.any_instance.stub(:tags).and_return([])
- end
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/hooks" do
- subject { project_hooks_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
- end
-
-
- describe "PublicProject" do
- let(:project) { create(:project_with_code) }
-
- let(:master) { create(:user) }
- let(:guest) { create(:user) }
- let(:reporter) { create(:user) }
-
- let(:admin) { create(:user) }
-
- before do
- # public project
- project.public = true
- project.save!
-
- # full access
- project.team << [master, :master]
-
- # readonly
- project.team << [reporter, :reporter]
-
- end
-
- describe "Project should be public" do
- subject { project }
-
- its(:public?) { should be_true }
- end
-
- describe "GET /project_code" do
- subject { project_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/tree/master" do
- subject { project_tree_path(project, project.repository.root_ref) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/commits/master" do
- subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/commit/:sha" do
- subject { project_commit_path(project, project.repository.commit) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/compare" do
- subject { project_compare_index_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/team" do
- subject { project_team_index_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/wall" do
- subject { project_wall_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/blob" do
- before do
- commit = project.repository.commit
- path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
- @blob_path = project_blob_path(project, File.join(commit.id, path))
- end
-
- it { @blob_path.should be_allowed_for master }
- it { @blob_path.should be_allowed_for reporter }
- it { @blob_path.should be_allowed_for :admin }
- it { @blob_path.should be_allowed_for guest }
- it { @blob_path.should be_allowed_for :user }
- it { @blob_path.should be_denied_for :visitor }
- end
-
- describe "GET /project_code/edit" do
- subject { edit_project_path(project) }
-
- it { should be_allowed_for master }
- it { should be_denied_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/deploy_keys" do
- subject { project_deploy_keys_path(project) }
-
- it { should be_allowed_for master }
- it { should be_denied_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/issues" do
- subject { project_issues_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/snippets" do
- subject { project_snippets_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/snippets/new" do
- subject { new_project_snippet_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_denied_for guest }
- it { should be_denied_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/merge_requests" do
- subject { project_merge_requests_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/branches/recent" do
- subject { recent_project_branches_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/branches" do
- subject { project_branches_path(project) }
-
- before do
- # Speed increase
- Project.any_instance.stub(:branches).and_return([])
- end
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/tags" do
- subject { project_tags_path(project) }
-
- before do
- # Speed increase
- Project.any_instance.stub(:tags).and_return([])
- end
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
-
- describe "GET /project_code/hooks" do
- subject { project_hooks_path(project) }
-
- it { should be_allowed_for master }
- it { should be_allowed_for reporter }
- it { should be_allowed_for :admin }
- it { should be_allowed_for guest }
- it { should be_allowed_for :user }
- it { should be_denied_for :visitor }
- end
- end
-end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 229f49659c..d63a2de880 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -38,6 +38,24 @@ describe ApplicationHelper do
current_action?(:baz, :bar, :foo).should be_true
end
end
+
+ describe "avatar_icon" do
+ avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
+
+ it "should return an url for the avatar" do
+ user = create(:user)
+ user.avatar = File.open(avatar_file_path)
+ user.save!
+ avatar_icon(user.email).to_s.should == "/uploads/user/avatar/#{ user.id }/gitlab_logo.png"
+ end
+
+ it "should call gravatar_icon when no avatar is present" do
+ user = create(:user)
+ user.save!
+ stub!(:gravatar_icon).and_return('gravatar_method_called')
+ avatar_icon(user.email).to_s.should == "gravatar_method_called"
+ end
+ end
describe "gravatar_icon" do
let(:user_email) { 'user@email.com' }
@@ -105,4 +123,21 @@ describe ApplicationHelper do
end
end
+ describe "simple_sanitize" do
+ let(:a_tag) { '
Foo ' }
+
+ it "allows the a tag" do
+ simple_sanitize(a_tag).should == a_tag
+ end
+
+ it "allows the span tag" do
+ input = '
Bar '
+ simple_sanitize(input).should == input
+ end
+
+ it "disallows other tags" do
+ input = "
#{a_tag} "
+ simple_sanitize(input).should == a_tag
+ end
+ end
end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index d49247accc..a0bbc02642 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -406,6 +406,30 @@ describe GitlabMarkdownHelper do
it "should generate absolute urls for emoji" do
markdown(":smile:").should include("src=\"#{url_to_image("emoji/smile")}")
end
+
+ it "should handle relative urls for a file in master" do
+ actual = "[GitLab API doc](doc/api/README.md)\n"
+ expected = "
GitLab API doc
\n"
+ markdown(actual).should match(expected)
+ end
+
+ it "should handle relative urls for a directory in master" do
+ actual = "[GitLab API doc](doc/api)\n"
+ expected = "
GitLab API doc
\n"
+ markdown(actual).should match(expected)
+ end
+
+ it "should handle absolute urls" do
+ actual = "[GitLab](https://www.gitlab.com)\n"
+ expected = "
GitLab
\n"
+ markdown(actual).should match(expected)
+ end
+
+ it "should handle wiki urls" do
+ actual = "[Link](test/link)\n"
+ expected = "
Link
\n"
+ markdown(actual).should match(expected)
+ end
end
describe "#render_wiki_content" do
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 3595af3243..9c95bc044f 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -47,6 +47,17 @@ describe IssuesHelper do
url_for_project_issues.should eq ""
end
+
+ describe "when external tracker was enabled and then config removed" do
+ before do
+ @project = ext_project
+ Gitlab.config.stub(:issues_tracker).and_return(nil)
+ end
+
+ it "should return path to internal tracker" do
+ url_for_project_issues.should match(polymorphic_path([@project]))
+ end
+ end
end
describe :url_for_issue do
@@ -75,6 +86,17 @@ describe IssuesHelper do
url_for_issue(issue.iid).should eq ""
end
+
+ describe "when external tracker was enabled and then config removed" do
+ before do
+ @project = ext_project
+ Gitlab.config.stub(:issues_tracker).and_return(nil)
+ end
+
+ it "should return internal path" do
+ url_for_issue(issue.iid).should match(polymorphic_path([@project, issue]))
+ end
+ end
end
describe :url_for_new_issue do
@@ -101,6 +123,17 @@ describe IssuesHelper do
url_for_new_issue.should eq ""
end
+
+ describe "when external tracker was enabled and then config removed" do
+ before do
+ @project = ext_project
+ Gitlab.config.stub(:issues_tracker).and_return(nil)
+ end
+
+ it "should return internal path" do
+ url_for_new_issue.should match(new_project_issue_path(@project))
+ end
+ end
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
new file mode 100644
index 0000000000..62f88dd522
--- /dev/null
+++ b/spec/helpers/projects_helper_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe ProjectsHelper do
+ describe '#project_issues_trackers' do
+ it "returns the correct issues trackers available" do
+ project_issues_trackers.should ==
+ "
Redmine \n" \
+ "
GitLab "
+ end
+ end
+end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
new file mode 100644
index 0000000000..55b6b6b4da
--- /dev/null
+++ b/spec/helpers/search_helper_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe SearchHelper do
+ # Override simple_sanitize for our testing purposes
+ def simple_sanitize(str)
+ str
+ end
+
+ describe 'search_autocomplete_source' do
+ context "with no current user" do
+ before { stub!(:current_user).and_return(nil) }
+
+ it "it returns nil" do
+ search_autocomplete_source.should be_nil
+ end
+ end
+
+ context "with a user" do
+ let(:user) { create(:user) }
+ let(:result) { JSON.parse(search_autocomplete_source) }
+
+ before do
+ stub!(:current_user).and_return(user)
+ end
+
+ it "includes Help sections" do
+ result.select { |h| h['label'] =~ /^help:/ }.length.should == 9
+ end
+
+ it "includes default sections" do
+ result.count { |h| h['label'] =~ /^(My|Admin)\s/ }.should == 4
+ end
+
+ it "includes the user's groups" do
+ create(:group).add_owner(user)
+ result.count { |h| h['label'] =~ /^group:/ }.should == 1
+ end
+
+ it "includes the user's projects" do
+ create(:project, namespace: create(:namespace, owner: user))
+ result.count { |h| h['label'] =~ /^project:/ }.should == 1
+ end
+
+ context "with a current project" do
+ before { @project = create(:project_with_code) }
+
+ it "includes project-specific sections" do
+ result.count { |h| h['label'] =~ /^#{@project.name_with_namespace} - / }.should == 11
+ end
+
+ it "uses @ref in urls if defined" do
+ @ref = "foo_bar"
+ result.count { |h| h['url'] == project_tree_path(@project, @ref) }.should == 1
+ end
+ end
+
+ context "with no current project" do
+ it "does not include project-specific sections" do
+ result.count { |h| h['label'] =~ /Files$/ }.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/javascripts/stat_graph_contributors_util_spec.js b/spec/javascripts/stat_graph_contributors_util_spec.js
index 367f0af05f..2e52479ccb 100644
--- a/spec/javascripts/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/stat_graph_contributors_util_spec.js
@@ -3,10 +3,10 @@ describe("ContributorsStatGraphUtil", function () {
describe("#parse_log", function () {
it("returns a correctly parsed log", function () {
var fake_log = [
- {author: "Karlo Soriano", date: "2013-05-09", additions: 471},
- {author: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 6, deletions: 1},
- {author: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 19, deletions: 3},
- {author: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 29, deletions: 3}]
+ {author_email: "karlo@email.com", author_name: "Karlo Soriano", date: "2013-05-09", additions: 471},
+ {author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 6, deletions: 1},
+ {author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 19, deletions: 3},
+ {author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 29, deletions: 3}]
var correct_parsed_log = {
total: [
@@ -15,11 +15,11 @@ describe("ContributorsStatGraphUtil", function () {
by_author:
[
{
- author: "Karlo Soriano",
+ author_name: "Karlo Soriano", author_email: "karlo@email.com",
"2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
},
{
- author: "Dmitriy Zaporozhets",
+ author_name: "Dmitriy Zaporozhets",author_email: "dzaporozhets@email.com",
"2013-05-08": {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}
}
]
@@ -112,10 +112,10 @@ describe("ContributorsStatGraphUtil", function () {
describe("#add_author", function () {
it("adds an author field to the collection", function () {
- var fake_author = "Author"
+ var fake_author = { author_name: "Author", author_email: 'fake@email.com' }
var fake_collection = {}
ContributorsStatGraphUtil.add_author(fake_author, fake_collection)
- expect(fake_collection[fake_author].author).toEqual("Author")
+ expect(fake_collection[fake_author.author_name].author_name).toEqual("Author")
})
})
@@ -153,30 +153,35 @@ describe("ContributorsStatGraphUtil", function () {
describe("#get_author_data", function () {
it("returns the log by author sorted by specified field", function () {
var fake_parsed_log = {
- total: [{date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
- {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}],
- by_author:[
- {
- author: "Karlo Soriano",
- "2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
- },
- {
- author: "Dmitriy Zaporozhets",
- "2013-05-08": {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}
+ total: [
+ {date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
+ {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}
+ ],
+ by_author: [
+ {
+ author_name: "Karlo Soriano", author_email: "karlo@email.com",
+ "2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
+ },
+ {
+ author_name: "Dmitriy Zaporozhets", author_email: "dzaporozhets@email.com",
+ "2013-05-08": {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}
+ }
+ ]
}
- ]}
- var correct_author_data = [{author:"Dmitriy Zaporozhets",dates:{"2013-05-08":3},deletions:7,additions:54,"commits":3},
- {author:"Karlo Soriano",dates:{"2013-05-09":1},deletions:0,additions:471,commits:1}]
+ var correct_author_data = [
+ {author_name:"Dmitriy Zaporozhets",author_email:"dzaporozhets@email.com",dates:{"2013-05-08":3},deletions:7,additions:54,"commits":3},
+ {author_name:"Karlo Soriano",author_email:"karlo@email.com",dates:{"2013-05-09":1},deletions:0,additions:471,commits:1}
+ ]
expect(ContributorsStatGraphUtil.get_author_data(fake_parsed_log, "commits")).toEqual(correct_author_data)
})
})
describe("#parse_log_entry", function () {
it("adds the corresponding info from the log entry to the author", function () {
- var fake_log_entry = { author: "Karlo Soriano",
+ var fake_log_entry = { author_name: "Karlo Soriano", author_email: "karlo@email.com",
"2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
}
- var correct_parsed_log = {author:"Karlo Soriano",dates:{"2013-05-09":1},deletions:0,additions:471,commits:1}
+ var correct_parsed_log = {author_name:"Karlo Soriano",author_email:"karlo@email.com",dates:{"2013-05-09":1},deletions:0,additions:471,commits:1}
expect(ContributorsStatGraphUtil.parse_log_entry(fake_log_entry, 'commits', null)).toEqual(correct_parsed_log)
})
})
@@ -197,4 +202,4 @@ describe("ContributorsStatGraphUtil", function () {
})
-})
\ No newline at end of file
+})
diff --git a/spec/javascripts/support/jasmine_helper.rb b/spec/javascripts/support/jasmine_helper.rb
index 986a4c16f3..34b418a9ca 100644
--- a/spec/javascripts/support/jasmine_helper.rb
+++ b/spec/javascripts/support/jasmine_helper.rb
@@ -1,11 +1,5 @@
-#Use this file to set/override Jasmine configuration options
-#You can remove it if you don't need it.
-#This file is loaded *after* jasmine.yml is interpreted.
-#
-#Example: using a different boot file.
-#Jasmine.configure do |config|
-# @config.boot_dir = '/absolute/path/to/boot_dir'
-# @config.boot_files = lambda { ['/absolute/path/to/boot_dir/file.js'] }
-#end
-#
+WebMock.allow_net_connect!
+Jasmine.configure do |config|
+ config.browser = :phantomjs
+end
diff --git a/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb b/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb
new file mode 100644
index 0000000000..df5252e461
--- /dev/null
+++ b/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb
@@ -0,0 +1,58 @@
+require 'spec_helper'
+
+describe Gitlab::LDAP do
+ let(:gl_auth) { Gitlab::LDAP::User }
+
+ before do
+ Gitlab.config.stub(omniauth: {})
+
+ @info = mock(
+ uid: '12djsak321',
+ name: 'John',
+ email: 'john@mail.com',
+ nickname: 'john'
+ )
+ end
+
+ describe :find_for_ldap_auth do
+ before do
+ @auth = mock(
+ uid: '12djsak321',
+ info: @info,
+ provider: 'ldap'
+ )
+ end
+
+ it "should update credentials by email if missing uid" do
+ user = double('User')
+ User.stub find_by_extern_uid_and_provider: nil
+ User.stub find_by_email: user
+ user.should_receive :update_attributes
+ gl_auth.find_or_create(@auth)
+ end
+
+ it "should update credentials by username if missing uid and Gitlab.config.ldap.allow_username_or_email_login is true" do
+ user = double('User')
+ value = Gitlab.config.ldap.allow_username_or_email_login
+ Gitlab.config.ldap['allow_username_or_email_login'] = true
+ User.stub find_by_extern_uid_and_provider: nil
+ User.stub find_by_email: nil
+ User.stub find_by_username: user
+ user.should_receive :update_attributes
+ gl_auth.find_or_create(@auth)
+ Gitlab.config.ldap['allow_username_or_email_login'] = value
+ end
+
+ it "should not update credentials by username if missing uid and Gitlab.config.ldap.allow_username_or_email_login is false" do
+ user = double('User')
+ value = Gitlab.config.ldap.allow_username_or_email_login
+ Gitlab.config.ldap['allow_username_or_email_login'] = false
+ User.stub find_by_extern_uid_and_provider: nil
+ User.stub find_by_email: nil
+ User.stub find_by_username: user
+ user.should_not_receive :update_attributes
+ gl_auth.find_or_create(@auth)
+ Gitlab.config.ldap['allow_username_or_email_login'] = value
+ end
+ end
+end
diff --git a/spec/lib/gitlab/satellite/merge_action_spec.rb b/spec/lib/gitlab/satellite/merge_action_spec.rb
index 3be14383e0..e40ff73b7f 100644
--- a/spec/lib/gitlab/satellite/merge_action_spec.rb
+++ b/spec/lib/gitlab/satellite/merge_action_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'Gitlab::Satellite::MergeAction' do
before(:each) do
# TestEnv.init(mailer: false, init_repos: true, repos: true)
- @master = ['master', 'bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a']
+ @master = ['master', 'b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828']
@one_after_stable = ['stable', '6ea87c47f0f8a24ae031c3fff17bc913889ecd00'] #this commit sha is one after stable
@wiki_branch = ['wiki', '635d3e09b72232b6e92a38de6cc184147e5bcb41'] #this is the commit sha where the wiki branch goes off from master
@conflicting_metior = ['metior', '313d96e42b313a0af5ab50fa233bf43e27118b3f'] #this branch conflicts with the wiki branch
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 0787bdbea6..666c6cceff 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -320,7 +320,7 @@ describe Notify do
it_behaves_like 'a note email'
it 'has the correct subject' do
- should have_subject /note for merge request !#{merge_request.iid}/
+ should have_subject /note for merge request ##{merge_request.iid}/
end
it 'contains a link to the merge request note' do
diff --git a/spec/models/flowdock_service_spec.rb b/spec/models/flowdock_service_spec.rb
new file mode 100644
index 0000000000..b22193c9e9
--- /dev/null
+++ b/spec/models/flowdock_service_spec.rb
@@ -0,0 +1,48 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# token :string(255)
+# project_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# active :boolean default(FALSE), not null
+# project_url :string(255)
+#
+
+require 'spec_helper'
+
+describe FlowdockService do
+ describe "Associations" do
+ it { should belong_to :project }
+ it { should have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project_with_code) }
+
+ before do
+ @flowdock_service = FlowdockService.new
+ @flowdock_service.stub(
+ project_id: project.id,
+ project: project,
+ service_hook: true,
+ token: 'verySecret'
+ )
+ @sample_data = GitPushService.new.sample_data(project, user)
+ @api_url = 'https://api.flowdock.com/v1/git/verySecret'
+ WebMock.stub_request(:post, @api_url)
+ end
+
+ it "should call FlowDock API" do
+ @flowdock_service.execute(@sample_data)
+ WebMock.should have_requested(:post, @api_url).with(
+ body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/
+ ).once
+ end
+ end
+end
diff --git a/spec/models/gollum_wiki_spec.rb b/spec/models/gollum_wiki_spec.rb
index aa850dfd0a..9e07d9ee19 100644
--- a/spec/models/gollum_wiki_spec.rb
+++ b/spec/models/gollum_wiki_spec.rb
@@ -86,6 +86,27 @@ describe GollumWiki do
end
end
+ describe "#empty?" do
+ context "when the wiki repository is empty" do
+ before do
+ Gitlab::Shell.any_instance.stub(:add_repository) do
+ create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git")
+ end
+ project.stub(:path_with_namespace).and_return("non-existant")
+ end
+
+ its(:empty?) { should be_true }
+ end
+
+ context "when the wiki has pages" do
+ before do
+ create_page("index", "This is an awesome new Gollum Wiki")
+ end
+
+ its(:empty?) { should be_false }
+ end
+ end
+
describe "#pages" do
before do
create_page("index", "This is an awesome new Gollum Wiki")
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 78c39548eb..12b84700eb 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -5,7 +5,7 @@
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
-# owner_id :integer not null
+# owner_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
@@ -26,10 +26,10 @@ describe Group do
it { should validate_uniqueness_of(:name) }
it { should validate_presence_of :path }
it { should validate_uniqueness_of(:path) }
- it { should validate_presence_of :owner }
+ it { should_not validate_presence_of :owner }
describe :users do
- it { group.users.should == [group.owner] }
+ it { group.users.should == group.owners }
end
describe :human_name do
@@ -38,8 +38,20 @@ describe Group do
describe :add_users do
let(:user) { create(:user) }
- before { group.add_users([user.id], UsersGroup::MASTER) }
+ before { group.add_user(user, UsersGroup::MASTER) }
it { group.users_groups.masters.map(&:user).should include(user) }
end
+
+ describe :add_users do
+ let(:user) { create(:user) }
+ before { group.add_users([user.id], UsersGroup::GUEST) }
+
+ it "should update the group permission" do
+ group.users_groups.guests.map(&:user).should include(user)
+ group.add_users([user.id], UsersGroup::DEVELOPER)
+ group.users_groups.developers.map(&:user).should include(user)
+ group.users_groups.guests.map(&:user).should_not include(user)
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 18c32d4fb7..b17183a281 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -18,6 +18,7 @@
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
+# description :text
#
require 'spec_helper'
@@ -48,8 +49,8 @@ describe MergeRequest do
before do
merge_request.stub(:commits) { [merge_request.source_project.repository.commit] }
- create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit')
- create(:note, noteable: merge_request)
+ create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.project)
+ create(:note, noteable: merge_request, project: merge_request.project)
end
it "should include notes for commits" do
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 0a0509bcd2..c38554e2f7 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -5,7 +5,7 @@
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
-# owner_id :integer not null
+# owner_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index ef143debcc..42c405d8e5 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -14,6 +14,7 @@
# commit_id :string(255)
# noteable_id :integer
# st_diff :text
+# system :boolean default(FALSE), not null
#
require 'spec_helper'
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c3d2bf5d4a..88ea692679 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -27,8 +27,8 @@
require 'spec_helper'
describe Project do
- before(:each) { enable_observers }
- after(:each) { disable_observers }
+ before { enable_observers }
+ after { disable_observers }
describe "Associations" do
it { should belong_to(:group) }
@@ -85,7 +85,6 @@ describe Project do
it { should respond_to(:execute_hooks) }
it { should respond_to(:transfer) }
it { should respond_to(:name_with_namespace) }
- it { should respond_to(:namespace_owner) }
it { should respond_to(:owner) }
it { should respond_to(:path_with_namespace) }
end
@@ -133,17 +132,17 @@ describe Project do
it "should close merge request if last commit from source branch was pushed to target branch" do
@merge_request.reloaded_commits
- @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
- project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/stable", @key.user)
+ @merge_request.last_commit.id.should == "b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828"
+ project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828", "refs/heads/stable", @key.user)
@merge_request.reload
@merge_request.merged?.should be_true
end
it "should update merge request commits with new one if pushed to source branch" do
@merge_request.last_commit.should == nil
- project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/master", @key.user)
+ project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828", "refs/heads/master", @key.user)
@merge_request.reload
- @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
+ @merge_request.last_commit.id.should == "b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828"
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 79915daa46..f6c9f82c4e 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -130,11 +130,12 @@ describe User do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
- @group = create :group, owner: @user
+ @group = create :group
+ @group.add_owner(@user)
end
it { @user.several_namespaces?.should be_true }
- it { @user.namespaces.should include(@user.namespace, @group) }
+ it { @user.namespaces.should include(@user.namespace) }
it { @user.authorized_groups.should == [@group] }
it { @user.owned_groups.should == [@group] }
end
@@ -144,9 +145,10 @@ describe User do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@user2 = create :user
- @group = create :group, owner: @user
+ @group = create :group
+ @group.add_owner(@user)
- @group.add_users([@user2.id], UsersGroup::OWNER)
+ @group.add_user(@user2, UsersGroup::OWNER)
end
it { @user2.several_namespaces?.should be_true }
@@ -221,9 +223,9 @@ describe User do
let(:user) { User.build_user({}, as: :admin) }
it "should apply defaults to user" do
- user.projects_limit.should == 42
- user.can_create_group.should be_false
- user.theme_id.should == Gitlab::Theme::MARS
+ user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit
+ user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group
+ user.theme_id.should == Gitlab.config.gitlab.default_theme
end
end
@@ -231,6 +233,9 @@ describe User do
let(:user) { User.build_user({projects_limit: 123, can_create_group: true, can_create_team: true, theme_id: Gitlab::Theme::BASIC}, as: :admin) }
it "should apply defaults to user" do
+ Gitlab.config.gitlab.default_projects_limit.should_not == 123
+ Gitlab.config.gitlab.default_can_create_group.should_not be_true
+ Gitlab.config.gitlab.default_theme.should_not == Gitlab::Theme::BASIC
user.projects_limit.should == 123
user.can_create_group.should be_true
user.theme_id.should == Gitlab::Theme::BASIC
@@ -243,9 +248,9 @@ describe User do
let(:user) { User.build_user }
it "should apply defaults to user" do
- user.projects_limit.should == 42
- user.can_create_group.should be_false
- user.theme_id.should == Gitlab::Theme::MARS
+ user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit
+ user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group
+ user.theme_id.should == Gitlab.config.gitlab.default_theme
end
end
@@ -253,9 +258,9 @@ describe User do
let(:user) { User.build_user(projects_limit: 123, can_create_group: true, theme_id: Gitlab::Theme::BASIC) }
it "should apply defaults to user" do
- user.projects_limit.should == 42
- user.can_create_group.should be_false
- user.theme_id.should == Gitlab::Theme::MARS
+ user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit
+ user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group
+ user.theme_id.should == Gitlab.config.gitlab.default_theme
end
end
end
diff --git a/spec/observers/users_project_observer_spec.rb b/spec/observers/users_project_observer_spec.rb
index e33d8cc50f..e7c624fce5 100644
--- a/spec/observers/users_project_observer_spec.rb
+++ b/spec/observers/users_project_observer_spec.rb
@@ -65,4 +65,30 @@ describe UsersProjectObserver do
@users_project.destroy
end
end
-end
+
+ describe "#after_create" do
+ context 'wiki_enabled creates repository directory' do
+ context 'wiki_enabled true creates wiki repository directory' do
+ before do
+ @project = create(:project, wiki_enabled: true)
+ @path = GollumWiki.new(@project, user).send(:path_to_repo)
+ end
+
+ after do
+ FileUtils.rm_rf(@path)
+ end
+
+ it { File.exists?(@path).should be_true }
+ end
+
+ context 'wiki_enabled false does not create wiki repository directory' do
+ before do
+ @project = create(:project, wiki_enabled: false)
+ @path = GollumWiki.new(@project, user).send(:path_to_repo)
+ end
+
+ it { File.exists?(@path).should be_false }
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 1df2746cb5..d6acc80617 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -6,8 +6,13 @@ describe API::API do
let(:user1) { create(:user) }
let(:user2) { create(:user) }
let(:admin) { create(:admin) }
- let!(:group1) { create(:group, owner: user1, ldap_cn: "ldap-group", ldap_access: Gitlab::Access::MASTER ) }
- let!(:group2) { create(:group, owner: user2) }
+ let!(:group1) { create(:group, ldap_cn: "ldap-group", ldap_access: Gitlab::Access::MASTER ) }
+ let!(:group2) { create(:group) }
+
+ before do
+ group1.add_owner(user1)
+ group2.add_owner(user2)
+ end
describe "GET /groups" do
context "when unauthenticated" do
@@ -103,6 +108,44 @@ describe API::API do
end
end
+ describe "DELETE /groups/:id" do
+ context "when authenticated as user" do
+ it "should remove group" do
+ delete api("/groups/#{group1.id}", user1)
+ response.status.should == 200
+ end
+
+ it "should not remove a group if not an owner" do
+ user3 = create(:user)
+ group1.add_user(user3, Gitlab::Access::MASTER)
+ delete api("/groups/#{group1.id}", user3)
+ response.status.should == 403
+ end
+
+ it "should not remove a non existing group" do
+ delete api("/groups/1328", user1)
+ response.status.should == 404
+ end
+
+ it "should not remove a group not attached to user1" do
+ delete api("/groups/#{group2.id}", user1)
+ response.status.should == 403
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "should remove any existing group" do
+ delete api("/groups/#{group2.id}", admin)
+ response.status.should == 200
+ end
+
+ it "should not remove a non existing group" do
+ delete api("/groups/1328", admin)
+ response.status.should == 404
+ end
+ end
+ end
+
describe "POST /groups/:id/projects/:project_id" do
let(:project) { create(:project) }
before(:each) do
@@ -132,14 +175,19 @@ describe API::API do
let(:master) { create(:user) }
let(:guest) { create(:user) }
let!(:group_with_members) do
- group = create(:group, owner: owner)
+ group = create(:group)
group.add_users([reporter.id], UsersGroup::REPORTER)
group.add_users([developer.id], UsersGroup::DEVELOPER)
group.add_users([master.id], UsersGroup::MASTER)
group.add_users([guest.id], UsersGroup::GUEST)
group
end
- let!(:group_no_members) { create(:group, owner: owner) }
+ let!(:group_no_members) { create(:group) }
+
+ before do
+ group_with_members.add_owner owner
+ group_no_members.add_owner owner
+ end
describe "GET /groups/:id/members" do
context "when authenticated as user that is part or the group" do
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index a97d6a282a..c55025d72b 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -42,6 +42,7 @@ describe API::API do
get api("/projects/#{project.id}/issues/#{issue.id}", user)
response.status.should == 200
json_response['title'].should == issue.title
+ json_response['iid'].should == issue.iid
end
it "should return 404 if issue id not found" do
@@ -99,4 +100,16 @@ describe API::API do
response.status.should == 405
end
end
+
+ describe "PUT /projects/:id/issues/:issue_id to test observer on close" do
+ before { enable_observers }
+ after { disable_observers }
+
+ it "should create an activity event when an issue is closed" do
+ Event.should_receive(:create)
+
+ put api("/projects/#{project.id}/issues/#{issue.id}", user),
+ state_event: "close"
+ end
+ end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 2f11f562aa..f31b4da90c 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -34,6 +34,7 @@ describe API::API do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}", user)
response.status.should == 200
json_response['title'].should == merge_request.title
+ json_response['iid'].should == merge_request.iid
end
it "should return a 404 error if merge_request_id not found" do
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index 246fe262ce..febfc63921 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -30,6 +30,7 @@ describe API::API do
get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
response.status.should == 200
json_response['title'].should == milestone.title
+ json_response['iid'].should == milestone.iid
end
it "should return 401 error if user not authenticated" do
@@ -89,4 +90,16 @@ describe API::API do
json_response['state'].should == 'closed'
end
end
+
+ describe "PUT /projects/:id/milestones/:milestone_id to test observer on close" do
+ before { enable_observers }
+ after { disable_observers }
+
+ it "should create an activity event when an milestone is closed" do
+ Event.should_receive(:create)
+
+ put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
+ state_event: 'close'
+ end
+ end
end
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index ba18b12303..6ed96eb97f 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -176,4 +176,16 @@ describe API::API do
end
end
end
+
+ describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do
+ before { enable_observers }
+ after { disable_observers }
+
+ it "should create an activity event when an issue note is created" do
+ Event.should_receive(:create)
+
+ post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
+ end
+ end
+
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index de4f01b785..cf939ce76d 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -723,4 +723,80 @@ describe API::API do
json_response['message'].should == 'Group access is not included in the list'
end
end
+
+ describe "GET /projects/search/:query" do
+ let!(:query) { 'query'}
+ let!(:search) { create(:project, name: query, creator_id: user.id, namespace: user.namespace) }
+ let!(:pre) { create(:project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
+ let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
+ let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
+ let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
+ let!(:public) { create(:project, name: "another #{query}",public: true) }
+ let!(:unfound_public) { create(:project, name: 'unfound public', public: true) }
+
+ context "when unauthenticated" do
+ it "should return authentication error" do
+ get api("/projects/search/#{query}")
+ response.status.should == 401
+ end
+ end
+
+ context "when authenticated" do
+ it "should return an array of projects" do
+ get api("/projects/search/#{query}",user)
+ response.status.should == 200
+ json_response.should be_an Array
+ json_response.size.should == 5
+ json_response.each {|project| project['name'].should =~ /.*query.*/}
+ end
+ end
+
+ context "when authenticated as a different user" do
+ it "should return matching public projects" do
+ get api("/projects/search/#{query}", user2)
+ response.status.should == 200
+ json_response.should be_an Array
+ json_response.size.should == 1
+ json_response.first['name'].should == "another #{query}"
+ end
+ end
+ end
+
+ describe "DELETE /projects/:id" do
+ context "when authenticated as user" do
+ it "should remove project" do
+ delete api("/projects/#{project.id}", user)
+ response.status.should == 200
+ end
+
+ it "should not remove a project if not an owner" do
+ user3 = create(:user)
+ project.team << [user3, :developer]
+ delete api("/projects/#{project.id}", user3)
+ response.status.should == 403
+ end
+
+ it "should not remove a non existing project" do
+ delete api("/projects/1328", user)
+ response.status.should == 404
+ end
+
+ it "should not remove a project not attached to user" do
+ delete api("/projects/#{project.id}", user2)
+ response.status.should == 404
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "should remove any existing project" do
+ delete api("/projects/#{project.id}", admin)
+ response.status.should == 200
+ end
+
+ it "should not remove a non existing project" do
+ delete api("/projects/1328", admin)
+ response.status.should == 404
+ end
+ end
+ end
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index f15abdd358..9649c4d09c 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -8,7 +8,8 @@ describe API::API do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project_with_code, creator_id: user.id) }
- let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
+ let!(:master) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
+ let!(:guest) { create(:users_project, user: user2, project: project, project_access: UsersProject::GUEST) }
before { project.team << [user, :reporter] }
@@ -32,6 +33,11 @@ describe API::API do
json_response['protected'].should == false
end
+ it "should return a 403 error if guest" do
+ get api("/projects/#{project.id}/repository/branches", user2)
+ response.status.should == 403
+ end
+
it "should return a 404 error if branch is not available" do
get api("/projects/#{project.id}/repository/branches/unknown", user)
response.status.should == 404
@@ -53,6 +59,11 @@ describe API::API do
response.status.should == 404
end
+ it "should return a 403 error if guest" do
+ put api("/projects/#{project.id}/repository/branches/new_design/protect", user2)
+ response.status.should == 403
+ end
+
it "should return success when protect branch again" do
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
@@ -214,4 +225,16 @@ describe API::API do
end
end
+ describe "GET /projects/:id/repository/archive/:sha" do
+ it "should get the archive" do
+ get api("/projects/#{project.id}/repository/archive", user)
+ response.status.should == 200
+ response.content_type.should == 'application/x-gzip'
+ end
+
+ it "should return 404 for invalid sha" do
+ get api("/projects/#{project.id}/repository/archive/?sha=xxx", user)
+ response.status.should == 404
+ end
+ end
end
diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb
index 88c17f26a6..0fd90c567e 100644
--- a/spec/requests/api/session_spec.rb
+++ b/spec/requests/api/session_spec.rb
@@ -14,7 +14,6 @@ describe API::API do
json_response['email'].should == user.email
json_response['private_token'].should == user.private_token
json_response['is_admin'].should == user.is_admin?
- json_response['can_create_team'].should == user.can_create_team?
json_response['can_create_project'].should == user.can_create_project?
json_response['can_create_group'].should == user.can_create_group?
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 2fced3ec94..4ef78b8e5d 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -52,6 +52,35 @@ describe API::API do
}.to change { User.count }.by(1)
end
+ it "should create user with correct attributes" do
+ post api('/users', admin), attributes_for(:user, admin: true, can_create_group: true)
+ response.status.should == 201
+ user_id = json_response['id']
+ new_user = User.find(user_id)
+ new_user.should_not == nil
+ new_user.admin.should == true
+ new_user.can_create_group.should == true
+ end
+
+ it "should create non-admin user" do
+ post api('/users', admin), attributes_for(:user, admin: false, can_create_group: false)
+ response.status.should == 201
+ user_id = json_response['id']
+ new_user = User.find(user_id)
+ new_user.should_not == nil
+ new_user.admin.should == false
+ new_user.can_create_group.should == false
+ end
+
+ it "should create non-admin users by default" do
+ post api('/users', admin), attributes_for(:user)
+ response.status.should == 201
+ user_id = json_response['id']
+ new_user = User.find(user_id)
+ new_user.should_not == nil
+ new_user.admin.should == false
+ end
+
it "should return 201 Created on success" do
post api("/users", admin), attributes_for(:user, projects_limit: 3)
response.status.should == 201
@@ -135,6 +164,8 @@ describe API::API do
end
describe "PUT /users/:id" do
+ let!(:admin_user) { create(:admin) }
+
before { admin }
it "should update user with new bio" do
@@ -144,6 +175,21 @@ describe API::API do
user.reload.bio.should == 'new test bio'
end
+ it "should update admin status" do
+ put api("/users/#{user.id}", admin), {admin: true}
+ response.status.should == 200
+ json_response['is_admin'].should == true
+ user.reload.admin.should == true
+ end
+
+ it "should not update admin status" do
+ put api("/users/#{admin_user.id}", admin), {can_create_group: false}
+ response.status.should == 200
+ json_response['is_admin'].should == true
+ admin_user.reload.admin.should == true
+ admin_user.can_create_group.should == false
+ end
+
it "should not allow invalid update" do
put api("/users/#{user.id}", admin), {email: 'invalid email'}
response.status.should == 404
@@ -228,7 +274,6 @@ describe API::API do
response.status.should == 200
json_response['email'].should == user.email
json_response['is_admin'].should == user.is_admin?
- json_response['can_create_team'].should == user.can_create_team?
json_response['can_create_project'].should == user.can_create_project?
json_response['can_create_group'].should == user.can_create_group?
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 946ef7c28c..1b1d19d26b 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -128,7 +128,7 @@ end
# profile_update PUT /profile/update(.:format) profile#update
describe ProfilesController, "routing" do
it "to #account" do
- get("/profile/account").should route_to('profiles#account')
+ get("/profile/account").should route_to('profiles/accounts#show')
end
it "to #history" do
diff --git a/spec/seed_project.tar.gz b/spec/seed_project.tar.gz
index 3ffafed18d..7abb51ebdf 100644
Binary files a/spec/seed_project.tar.gz and b/spec/seed_project.tar.gz differ
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index e3c25fa046..2870f59195 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -26,6 +26,7 @@ describe GitPushService do
it { should include(ref: @ref) }
it { should include(user_id: user.id) }
it { should include(user_name: user.name) }
+ it { should include(project_id: project.id) }
context "with repository data" do
subject { @push_data[:repository] }
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 7f1590f559..ebc1ed51d2 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -5,37 +5,29 @@ describe SystemHooksService do
let (:project) { create :project }
let (:users_project) { create :users_project }
- context 'it should build event data' do
- it 'should build event data for user' do
- SystemHooksService.build_event_data(user, :create).should include(:event_name, :name, :created_at, :email)
- end
-
- it 'should build event data for project' do
- SystemHooksService.build_event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email)
- end
-
- it 'should build event data for users project' do
- SystemHooksService.build_event_data(users_project, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access)
- end
+ context 'event data' do
+ it { event_data(user, :create).should include(:event_name, :name, :created_at, :email, :user_id) }
+ it { event_data(user, :destroy).should include(:event_name, :name, :created_at, :email, :user_id) }
+ it { event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email) }
+ it { event_data(project, :destroy).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email) }
+ it { event_data(users_project, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access) }
+ it { event_data(users_project, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access) }
end
- context 'it should build event names' do
- it 'should build event names for user' do
- SystemHooksService.build_event_name(user, :create).should eq "user_create"
+ context 'event names' do
+ it { event_name(user, :create).should eq "user_create" }
+ it { event_name(user, :destroy).should eq "user_destroy" }
+ it { event_name(project, :create).should eq "project_create" }
+ it { event_name(project, :destroy).should eq "project_destroy" }
+ it { event_name(users_project, :create).should eq "user_add_to_team" }
+ it { event_name(users_project, :destroy).should eq "user_remove_from_team" }
+ end
- SystemHooksService.build_event_name(user, :destroy).should eq "user_destroy"
- end
+ def event_data(*args)
+ SystemHooksService.build_event_data(*args)
+ end
- it 'should build event names for project' do
- SystemHooksService.build_event_name(project, :create).should eq "project_create"
-
- SystemHooksService.build_event_name(project, :destroy).should eq "project_destroy"
- end
-
- it 'should build event names for users project' do
- SystemHooksService.build_event_name(users_project, :create).should eq "user_add_to_team"
-
- SystemHooksService.build_event_name(users_project, :destroy).should eq "user_remove_from_team"
- end
+ def event_name(*args)
+ SystemHooksService.build_event_name(*args)
end
end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 203e661f51..16e10b1a62 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -84,6 +84,10 @@ module TestEnv
Repository.any_instance.stub(
size: 12.45
)
+
+ ActivityObserver.any_instance.stub(
+ current_user: double("current_user", id: 1)
+ )
end
def clear_repo_dir(namespace, name)