Commit 906c65b6243e1f55f96d58cc1d4d60dd64c5cc65

Authored by Crom (Thibaut CHARLES)
2 parents 87fc3507 dbf8ae73

Merge branch 'master' of https://github.com/gitlabhq/gitlabhq

Conflicts:
	config/application.rb
	config/gitlab.yml.example
	config/unicorn.rb.example
Showing 303 changed files with 4882 additions and 1336 deletions   Show diff stats
@@ -9,7 +9,6 @@ env: @@ -9,7 +9,6 @@ env:
9 - TASK=jasmine:ci 9 - TASK=jasmine:ci
10 before_install: 10 before_install:
11 - sudo apt-get install libicu-dev -y 11 - sudo apt-get install libicu-dev -y
12 - - gem install charlock_holmes -v="0.6.9"  
13 branches: 12 branches:
14 only: 13 only:
15 - 'master' 14 - 'master'
@@ -17,9 +16,12 @@ rvm: @@ -17,9 +16,12 @@ rvm:
17 - 2.0.0 16 - 2.0.0
18 services: 17 services:
19 - mysql 18 - mysql
  19 + - redis-server
20 before_script: 20 before_script:
21 - "cp config/database.yml.$DB config/database.yml" 21 - "cp config/database.yml.$DB config/database.yml"
22 - "cp config/gitlab.yml.example config/gitlab.yml" 22 - "cp config/gitlab.yml.example config/gitlab.yml"
23 - "bundle exec rake db:setup" 23 - "bundle exec rake db:setup"
24 - "bundle exec rake db:seed_fu" 24 - "bundle exec rake db:seed_fu"
25 script: "bundle exec rake $TASK --trace" 25 script: "bundle exec rake $TASK --trace"
  26 +notifications:
  27 + email: false
  1 +v 6.4.0
  2 + - Added sorting to project issues page (Jason Blanchard)
  3 + - Assembla integration (Carlos Paramio)
  4 + - Fixed another 500 error with submodules
  5 + - UI: More compact issues page
  6 + - Minimal password length increased to 8 symbols
  7 + - Side-by-side diff view (Steven Thonus)
  8 + - Internal projects (Jason Hollingsworth)
  9 + - Allow removal of avatar (Drew Blessing)
  10 + - Project web hooks now support issues and merge request events
  11 + - Visiting project page while not logged in will redirect to sign-in instead of 404 (Jason Hollingsworth)
  12 +
1 v 6.3.0 13 v 6.3.0
2 - API for adding gitlab-ci service 14 - API for adding gitlab-ci service
3 - Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey) 15 - Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey)
@@ -9,6 +21,34 @@ v 6.3.0 @@ -9,6 +21,34 @@ v 6.3.0
9 - Fixed issue with 500 error when group did not exist 21 - Fixed issue with 500 error when group did not exist
10 - Ability to leave project 22 - Ability to leave project
11 - You can create file in repo using UI 23 - You can create file in repo using UI
  24 + - You can remove file from repo using UI
  25 + - API: dropped default_branch attribute from project during creation
  26 + - Project default_branch is not stored in db any more. It takes from repo now.
  27 + - Admin broadcast messages
  28 + - UI improvements
  29 + - Dont show last push widget if user removed this branch
  30 + - Fix 500 error for repos with newline in file name
  31 + - Extended html titles
  32 + - API: create/update/delete repo files
  33 + - Admin can transfer project to any namespace
  34 + - API: projects/all for admin users
  35 + - Fix recent branches order
  36 +
  37 +v 6.2.4
  38 + - Security: Cast API private_token to string (CVE-2013-4580)
  39 + - Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
  40 + - Fix for Git SSH access for LDAP users
  41 +
  42 +v 6.2.3
  43 + - Security: More protection against CVE-2013-4489
  44 + - Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
  45 + - Fix sidekiq rake tasks
  46 +
  47 +v 6.2.2
  48 + - Security: Update gitlab_git (CVE-2013-4489)
  49 +
  50 +v 6.2.1
  51 + - Security: Fix issue with generated passwords for new users
12 52
13 v 6.2.0 53 v 6.2.0
14 - Public project pages are now visible to everyone (files, issues, wik, etc.) 54 - Public project pages are now visible to everyone (files, issues, wik, etc.)
@@ -30,7 +70,7 @@ v 6.2.0 @@ -30,7 +70,7 @@ v 6.2.0
30 - Avatar upload on profile page with a maximum of 100KB (Steven Thonus) 70 - Avatar upload on profile page with a maximum of 100KB (Steven Thonus)
31 - Store the sessions in Redis instead of the cookie store 71 - Store the sessions in Redis instead of the cookie store
32 - Fixed relative links in markdown 72 - Fixed relative links in markdown
33 - - User must confirm his email if signup enabled 73 + - User must confirm their email if signup enabled
34 - User must confirm changed email 74 - User must confirm changed email
35 75
36 v 6.1.0 76 v 6.1.0
@@ -52,7 +92,7 @@ v 6.1.0 @@ -52,7 +92,7 @@ v 6.1.0
52 - Add links to create branch/tag from project home page 92 - Add links to create branch/tag from project home page
53 - Add public-project? checkbox to new-project view 93 - Add public-project? checkbox to new-project view
54 - Improved compare page. Added link to proceed into Merge Request 94 - Improved compare page. Added link to proceed into Merge Request
55 - - Send email to user when he was added to group 95 + - Send an email to a user when they are added to group
56 - New landing page when you have 0 projects 96 - New landing page when you have 0 projects
57 97
58 v 6.0.0 98 v 6.0.0
@@ -95,6 +135,14 @@ v 6.0.0 @@ -95,6 +135,14 @@ v 6.0.0
95 - Improved MR comments logic 135 - Improved MR comments logic
96 - Render readme file for projects in public area 136 - Render readme file for projects in public area
97 137
  138 +v 5.4.2
  139 + - Security: Cast API private_token to string (CVE-2013-4580)
  140 + - Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
  141 +
  142 +v 5.4.1
  143 + - Security: Fixes for CVE-2013-4489
  144 + - Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
  145 +
98 v 5.4.0 146 v 5.4.0
99 - Ability to edit own comments 147 - Ability to edit own comments
100 - Documentation improvements 148 - Documentation improvements
CONTRIBUTING.md
@@ -9,6 +9,14 @@ This guide details how to use issues and pull requests to improve GitLab. @@ -9,6 +9,14 @@ This guide details how to use issues and pull requests to improve GitLab.
9 9
10 If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md). 10 If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md).
11 11
  12 +## Contributor license agreement
  13 +
  14 +By submitting code as an individual you agree to the [individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). By submitting code as an entity you agree to the [corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
  15 +
  16 +## Security vulnerability disclosure
  17 +
  18 +Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
  19 +
12 ## Closing policy for issues and pull requests 20 ## Closing policy for issues and pull requests
13 21
14 GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice. 22 GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice.
@@ -74,6 +82,3 @@ We will accept pull requests if: @@ -74,6 +82,3 @@ We will accept pull requests if:
74 * It is a single commit (please use `git rebase -i` to squash commits) 82 * It is a single commit (please use `git rebase -i` to squash commits)
75 83
76 For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed). 84 For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed).
77 -  
78 -## Security vulnerabilities  
79 -Please report security vulnerabilities in private to support@gitlab.com; also see http://www.gitlab.com/disclosure/. Do NOT create GitHub issues for security vulnerabilities.  
@@ -8,7 +8,7 @@ def linux_only(require_as) @@ -8,7 +8,7 @@ def linux_only(require_as)
8 RUBY_PLATFORM.include?('linux') && require_as 8 RUBY_PLATFORM.include?('linux') && require_as
9 end 9 end
10 10
11 -gem "rails", "3.2.15" 11 +gem "rails", "3.2.16"
12 12
13 # Supported DBs 13 # Supported DBs
14 gem "mysql2", group: :mysql 14 gem "mysql2", group: :mysql
@@ -24,26 +24,27 @@ gem 'omniauth-github' @@ -24,26 +24,27 @@ gem 'omniauth-github'
24 24
25 # Extracting information from a git repository 25 # Extracting information from a git repository
26 # Provide access to Gitlab::Git library 26 # Provide access to Gitlab::Git library
27 -gem "gitlab_git", "~> 3.0.0.rc2" 27 +gem "gitlab_git", "~> 3.1.0"
28 28
29 # Ruby/Rack Git Smart-HTTP Server Handler 29 # Ruby/Rack Git Smart-HTTP Server Handler
30 -gem 'gitlab-grack', '~> 1.0.1', require: 'grack' 30 +gem 'gitlab-grack', '~> 1.1.0', require: 'grack'
31 31
32 # LDAP Auth 32 # LDAP Auth
33 gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap" 33 gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap"
34 34
35 # Syntax highlighter 35 # Syntax highlighter
36 -gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb' 36 +gem "gitlab-pygments.rb", '~> 0.5.4', require: 'pygments.rb'
37 37
38 # Git Wiki 38 # Git Wiki
39 -gem "gitlab-gollum-lib", "~> 1.0.1", require: 'gollum-lib' 39 +gem "gitlab-gollum-lib", "~> 1.0.2", require: 'gollum-lib'
40 40
41 # Language detection 41 # Language detection
42 -gem "github-linguist", require: "linguist" 42 +gem "gitlab-linguist", "~> 2.9.6", require: "linguist"
43 43
44 # API 44 # API
45 gem "grape", "~> 0.4.1" 45 gem "grape", "~> 0.4.1"
46 gem "grape-entity", "~> 0.3.0" 46 gem "grape-entity", "~> 0.3.0"
  47 +gem 'rack-cors', require: 'rack/cors'
47 48
48 # Format dates and times 49 # Format dates and times
49 # based on human-friendly examples 50 # based on human-friendly examples
@@ -135,7 +136,7 @@ group :assets do @@ -135,7 +136,7 @@ group :assets do
135 gem 'turbolinks' 136 gem 'turbolinks'
136 gem 'jquery-turbolinks' 137 gem 'jquery-turbolinks'
137 138
138 - gem 'chosen-rails', "1.0.0" 139 + gem 'chosen-rails', "1.0.1"
139 gem 'select2-rails' 140 gem 'select2-rails'
140 gem 'jquery-atwho-rails', "0.3.0" 141 gem 'jquery-atwho-rails', "0.3.0"
141 gem "jquery-rails", "2.1.3" 142 gem "jquery-rails", "2.1.3"
1 GEM 1 GEM
2 remote: https://rubygems.org/ 2 remote: https://rubygems.org/
3 specs: 3 specs:
4 - actionmailer (3.2.15)  
5 - actionpack (= 3.2.15) 4 + actionmailer (3.2.16)
  5 + actionpack (= 3.2.16)
6 mail (~> 2.5.4) 6 mail (~> 2.5.4)
7 - actionpack (3.2.15)  
8 - activemodel (= 3.2.15)  
9 - activesupport (= 3.2.15) 7 + actionpack (3.2.16)
  8 + activemodel (= 3.2.16)
  9 + activesupport (= 3.2.16)
10 builder (~> 3.0.0) 10 builder (~> 3.0.0)
11 erubis (~> 2.7.0) 11 erubis (~> 2.7.0)
12 journey (~> 1.0.4) 12 journey (~> 1.0.4)
@@ -14,18 +14,18 @@ GEM @@ -14,18 +14,18 @@ GEM
14 rack-cache (~> 1.2) 14 rack-cache (~> 1.2)
15 rack-test (~> 0.6.1) 15 rack-test (~> 0.6.1)
16 sprockets (~> 2.2.1) 16 sprockets (~> 2.2.1)
17 - activemodel (3.2.15)  
18 - activesupport (= 3.2.15) 17 + activemodel (3.2.16)
  18 + activesupport (= 3.2.16)
19 builder (~> 3.0.0) 19 builder (~> 3.0.0)
20 - activerecord (3.2.15)  
21 - activemodel (= 3.2.15)  
22 - activesupport (= 3.2.15) 20 + activerecord (3.2.16)
  21 + activemodel (= 3.2.16)
  22 + activesupport (= 3.2.16)
23 arel (~> 3.0.2) 23 arel (~> 3.0.2)
24 tzinfo (~> 0.3.29) 24 tzinfo (~> 0.3.29)
25 - activeresource (3.2.15)  
26 - activemodel (= 3.2.15)  
27 - activesupport (= 3.2.15)  
28 - activesupport (3.2.15) 25 + activeresource (3.2.16)
  26 + activemodel (= 3.2.16)
  27 + activesupport (= 3.2.16)
  28 + activesupport (3.2.16)
29 i18n (~> 0.6, >= 0.6.4) 29 i18n (~> 0.6, >= 0.6.4)
30 multi_json (~> 1.0) 30 multi_json (~> 1.0)
31 acts-as-taggable-on (2.4.1) 31 acts-as-taggable-on (2.4.1)
@@ -34,11 +34,11 @@ GEM @@ -34,11 +34,11 @@ GEM
34 annotate (2.6.0.beta2) 34 annotate (2.6.0.beta2)
35 activerecord (>= 2.3.0) 35 activerecord (>= 2.3.0)
36 rake (>= 0.8.7) 36 rake (>= 0.8.7)
37 - arel (3.0.2) 37 + arel (3.0.3)
38 asciidoctor (0.1.3) 38 asciidoctor (0.1.3)
39 awesome_print (1.2.0) 39 awesome_print (1.2.0)
40 backports (3.3.2) 40 backports (3.3.2)
41 - bcrypt-ruby (3.1.1) 41 + bcrypt-ruby (3.1.2)
42 better_errors (1.0.1) 42 better_errors (1.0.1)
43 coderay (>= 1.0.0) 43 coderay (>= 1.0.0)
44 erubis (>= 2.6.6) 44 erubis (>= 2.6.6)
@@ -61,12 +61,12 @@ GEM @@ -61,12 +61,12 @@ GEM
61 charlock_holmes (0.6.9.4) 61 charlock_holmes (0.6.9.4)
62 childprocess (0.3.9) 62 childprocess (0.3.9)
63 ffi (~> 1.0, >= 1.0.11) 63 ffi (~> 1.0, >= 1.0.11)
64 - chosen-rails (1.0.0) 64 + chosen-rails (1.0.1)
65 coffee-rails (>= 3.2) 65 coffee-rails (>= 3.2)
66 compass-rails (>= 1.0) 66 compass-rails (>= 1.0)
67 railties (>= 3.0) 67 railties (>= 3.0)
68 sass-rails (>= 3.2) 68 sass-rails (>= 3.2)
69 - chunky_png (1.2.8) 69 + chunky_png (1.2.9)
70 cliver (0.2.1) 70 cliver (0.2.1)
71 code_analyzer (0.4.3) 71 code_analyzer (0.4.3)
72 sexp_processor 72 sexp_processor
@@ -77,7 +77,7 @@ GEM @@ -77,7 +77,7 @@ GEM
77 coffee-script (2.2.0) 77 coffee-script (2.2.0)
78 coffee-script-source 78 coffee-script-source
79 execjs 79 execjs
80 - coffee-script-source (1.6.2) 80 + coffee-script-source (1.6.3)
81 colored (1.2) 81 colored (1.2)
82 colorize (0.5.8) 82 colorize (0.5.8)
83 compass (0.12.2) 83 compass (0.12.2)
@@ -101,14 +101,14 @@ GEM @@ -101,14 +101,14 @@ GEM
101 database_cleaner (1.1.1) 101 database_cleaner (1.1.1)
102 debug_inspector (0.0.2) 102 debug_inspector (0.0.2)
103 descendants_tracker (0.0.1) 103 descendants_tracker (0.0.1)
104 - devise (2.2.5) 104 + devise (2.2.8)
105 bcrypt-ruby (~> 3.0) 105 bcrypt-ruby (~> 3.0)
106 orm_adapter (~> 0.1) 106 orm_adapter (~> 0.1)
107 railties (~> 3.1) 107 railties (~> 3.1)
108 warden (~> 1.2.1) 108 warden (~> 1.2.1)
109 devise-async (0.8.0) 109 devise-async (0.8.0)
110 devise (>= 2.2, < 3.2) 110 devise (>= 2.2, < 3.2)
111 - diff-lcs (1.2.4) 111 + diff-lcs (1.2.5)
112 dotenv (0.8.0) 112 dotenv (0.8.0)
113 email_spec (1.4.0) 113 email_spec (1.4.0)
114 launchy (~> 2.1) 114 launchy (~> 2.1)
@@ -119,8 +119,7 @@ GEM @@ -119,8 +119,7 @@ GEM
119 escape_utils (0.2.4) 119 escape_utils (0.2.4)
120 eventmachine (1.0.3) 120 eventmachine (1.0.3)
121 excon (0.13.4) 121 excon (0.13.4)
122 - execjs (1.4.0)  
123 - multi_json (~> 1.0) 122 + execjs (2.0.2)
124 factory_girl (4.2.0) 123 factory_girl (4.2.0)
125 activesupport (>= 3.0.0) 124 activesupport (>= 3.0.0)
126 factory_girl_rails (4.2.1) 125 factory_girl_rails (4.2.1)
@@ -151,38 +150,39 @@ GEM @@ -151,38 +150,39 @@ GEM
151 fssm (0.2.10) 150 fssm (0.2.10)
152 gemoji (1.2.1) 151 gemoji (1.2.1)
153 gherkin-ruby (0.3.0) 152 gherkin-ruby (0.3.0)
154 - github-linguist (2.3.4)  
155 - charlock_holmes (~> 0.6.6)  
156 - escape_utils (~> 0.2.3)  
157 - mime-types (~> 1.19)  
158 - pygments.rb (>= 0.2.13)  
159 - github-markdown (0.5.3) 153 + github-markdown (0.5.5)
160 github-markup (0.7.5) 154 github-markup (0.7.5)
161 gitlab-flowdock-git-hook (0.4.2.2) 155 gitlab-flowdock-git-hook (0.4.2.2)
162 gitlab-grit (>= 2.4.1) 156 gitlab-grit (>= 2.4.1)
163 multi_json 157 multi_json
164 - gitlab-gollum-lib (1.0.1) 158 + gitlab-gollum-lib (1.0.2)
165 github-markdown (~> 0.5.3) 159 github-markdown (~> 0.5.3)
166 github-markup (>= 0.7.5, < 1.0.0) 160 github-markup (>= 0.7.5, < 1.0.0)
167 - gitlab-grit (>= 2.5.1) 161 + gitlab-grit (~> 2.6.1)
  162 + gitlab-pygments.rb (~> 0.5.4)
168 nokogiri (~> 1.5.9) 163 nokogiri (~> 1.5.9)
169 - pygments.rb (~> 0.4.2)  
170 sanitize (~> 2.0.3) 164 sanitize (~> 2.0.3)
171 stringex (~> 1.5.1) 165 stringex (~> 1.5.1)
172 - gitlab-grack (1.0.1) 166 + gitlab-grack (1.1.0)
173 rack (~> 1.4.1) 167 rack (~> 1.4.1)
174 - gitlab-grit (2.6.1) 168 + gitlab-grit (2.6.3)
175 charlock_holmes (~> 0.6.9) 169 charlock_holmes (~> 0.6.9)
176 diff-lcs (~> 1.1) 170 diff-lcs (~> 1.1)
177 mime-types (~> 1.15) 171 mime-types (~> 1.15)
178 posix-spawn (~> 0.3.6) 172 posix-spawn (~> 0.3.6)
179 - gitlab-pygments.rb (0.3.2) 173 + gitlab-linguist (2.9.6)
  174 + charlock_holmes (~> 0.6.6)
  175 + escape_utils (~> 0.2.4)
  176 + gitlab-pygments.rb (~> 0.5.4)
  177 + mime-types (~> 1.19)
  178 + gitlab-pygments.rb (0.5.4)
180 posix-spawn (~> 0.3.6) 179 posix-spawn (~> 0.3.6)
181 yajl-ruby (~> 1.1.0) 180 yajl-ruby (~> 1.1.0)
182 - gitlab_git (3.0.0.rc2) 181 + gitlab_git (3.1.0)
183 activesupport (~> 3.2.13) 182 activesupport (~> 3.2.13)
184 - github-linguist (~> 2.3.4)  
185 gitlab-grit (~> 2.6.1) 183 gitlab-grit (~> 2.6.1)
  184 + gitlab-linguist (~> 2.9.5)
  185 + gitlab-pygments.rb (~> 0.5.4)
186 gitlab_meta (6.0) 186 gitlab_meta (6.0)
187 gitlab_omniauth-ldap (1.0.3) 187 gitlab_omniauth-ldap (1.0.3)
188 net-ldap (~> 0.3.1) 188 net-ldap (~> 0.3.1)
@@ -235,7 +235,7 @@ GEM @@ -235,7 +235,7 @@ GEM
235 multi_json (~> 1.0) 235 multi_json (~> 1.0)
236 multi_xml (>= 0.5.2) 236 multi_xml (>= 0.5.2)
237 httpauth (0.2.0) 237 httpauth (0.2.0)
238 - i18n (0.6.5) 238 + i18n (0.6.9)
239 jasmine (1.3.2) 239 jasmine (1.3.2)
240 jasmine-core (~> 1.3.1) 240 jasmine-core (~> 1.3.1)
241 rack (~> 1.0) 241 rack (~> 1.0)
@@ -274,7 +274,7 @@ GEM @@ -274,7 +274,7 @@ GEM
274 mime-types (~> 1.16) 274 mime-types (~> 1.16)
275 treetop (~> 1.4.8) 275 treetop (~> 1.4.8)
276 method_source (0.8.1) 276 method_source (0.8.1)
277 - mime-types (1.25) 277 + mime-types (1.25.1)
278 minitest (4.7.4) 278 minitest (4.7.4)
279 modernizr (2.6.2) 279 modernizr (2.6.2)
280 sprockets (~> 2.0) 280 sprockets (~> 2.0)
@@ -312,7 +312,7 @@ GEM @@ -312,7 +312,7 @@ GEM
312 omniauth-twitter (0.0.17) 312 omniauth-twitter (0.0.17)
313 multi_json (~> 1.3) 313 multi_json (~> 1.3)
314 omniauth-oauth (~> 1.0) 314 omniauth-oauth (~> 1.0)
315 - orm_adapter (0.4.0) 315 + orm_adapter (0.5.0)
316 pg (0.15.1) 316 pg (0.15.1)
317 poltergeist (1.4.1) 317 poltergeist (1.4.1)
318 capybara (~> 2.1.0) 318 capybara (~> 2.1.0)
@@ -325,9 +325,6 @@ GEM @@ -325,9 +325,6 @@ GEM
325 coderay (~> 1.0.5) 325 coderay (~> 1.0.5)
326 method_source (~> 0.8) 326 method_source (~> 0.8)
327 slop (~> 3.4) 327 slop (~> 3.4)
328 - pygments.rb (0.4.2)  
329 - posix-spawn (~> 0.3.6)  
330 - yajl-ruby (~> 1.1.0)  
331 pyu-ruby-sasl (0.0.3.3) 328 pyu-ruby-sasl (0.0.3.3)
332 quiet_assets (1.0.2) 329 quiet_assets (1.0.2)
333 railties (>= 3.1, < 5.0) 330 railties (>= 3.1, < 5.0)
@@ -338,6 +335,7 @@ GEM @@ -338,6 +335,7 @@ GEM
338 rack 335 rack
339 rack-cache (1.2) 336 rack-cache (1.2)
340 rack (>= 0.4) 337 rack (>= 0.4)
  338 + rack-cors (0.2.9)
341 rack-mini-profiler (0.1.31) 339 rack-mini-profiler (0.1.31)
342 rack (>= 1.1.3) 340 rack (>= 1.1.3)
343 rack-mount (0.8.3) 341 rack-mount (0.8.3)
@@ -348,14 +346,14 @@ GEM @@ -348,14 +346,14 @@ GEM
348 rack 346 rack
349 rack-test (0.6.2) 347 rack-test (0.6.2)
350 rack (>= 1.0) 348 rack (>= 1.0)
351 - rails (3.2.15)  
352 - actionmailer (= 3.2.15)  
353 - actionpack (= 3.2.15)  
354 - activerecord (= 3.2.15)  
355 - activeresource (= 3.2.15)  
356 - activesupport (= 3.2.15) 349 + rails (3.2.16)
  350 + actionmailer (= 3.2.16)
  351 + actionpack (= 3.2.16)
  352 + activerecord (= 3.2.16)
  353 + activeresource (= 3.2.16)
  354 + activesupport (= 3.2.16)
357 bundler (~> 1.0) 355 bundler (~> 1.0)
358 - railties (= 3.2.15) 356 + railties (= 3.2.16)
359 rails-dev-tweaks (0.6.1) 357 rails-dev-tweaks (0.6.1)
360 actionpack (~> 3.1) 358 actionpack (~> 3.1)
361 railties (~> 3.1) 359 railties (~> 3.1)
@@ -368,9 +366,9 @@ GEM @@ -368,9 +366,9 @@ GEM
368 i18n 366 i18n
369 require_all 367 require_all
370 ruby-progressbar 368 ruby-progressbar
371 - railties (3.2.15)  
372 - actionpack (= 3.2.15)  
373 - activesupport (= 3.2.15) 369 + railties (3.2.16)
  370 + actionpack (= 3.2.16)
  371 + activesupport (= 3.2.16)
374 rack-ssl (~> 1.3.2) 372 rack-ssl (~> 1.3.2)
375 rake (>= 0.8.7) 373 rake (>= 0.8.7)
376 rdoc (~> 3.4) 374 rdoc (~> 3.4)
@@ -431,7 +429,7 @@ GEM @@ -431,7 +429,7 @@ GEM
431 safe_yaml (0.9.3) 429 safe_yaml (0.9.3)
432 sanitize (2.0.3) 430 sanitize (2.0.3)
433 nokogiri (>= 1.4.4, < 1.6) 431 nokogiri (>= 1.4.4, < 1.6)
434 - sass (3.2.11) 432 + sass (3.2.12)
435 sass-rails (3.2.6) 433 sass-rails (3.2.6)
436 railties (~> 3.2.0) 434 railties (~> 3.2.0)
437 sass (>= 3.1.10) 435 sass (>= 3.1.10)
@@ -559,7 +557,7 @@ DEPENDENCIES @@ -559,7 +557,7 @@ DEPENDENCIES
559 bootstrap-sass 557 bootstrap-sass
560 capybara 558 capybara
561 carrierwave 559 carrierwave
562 - chosen-rails (= 1.0.0) 560 + chosen-rails (= 1.0.1)
563 coffee-rails 561 coffee-rails
564 colored 562 colored
565 coveralls 563 coveralls
@@ -575,13 +573,13 @@ DEPENDENCIES @@ -575,13 +573,13 @@ DEPENDENCIES
575 font-awesome-rails 573 font-awesome-rails
576 foreman 574 foreman
577 gemoji (~> 1.2.1) 575 gemoji (~> 1.2.1)
578 - github-linguist  
579 github-markup (~> 0.7.4) 576 github-markup (~> 0.7.4)
580 gitlab-flowdock-git-hook (~> 0.4.2) 577 gitlab-flowdock-git-hook (~> 0.4.2)
581 - gitlab-gollum-lib (~> 1.0.1)  
582 - gitlab-grack (~> 1.0.1)  
583 - gitlab-pygments.rb (~> 0.3.2)  
584 - gitlab_git (~> 3.0.0.rc2) 578 + gitlab-gollum-lib (~> 1.0.2)
  579 + gitlab-grack (~> 1.1.0)
  580 + gitlab-linguist (~> 2.9.6)
  581 + gitlab-pygments.rb (~> 0.5.4)
  582 + gitlab_git (~> 3.1.0)
585 gitlab_meta (= 6.0) 583 gitlab_meta (= 6.0)
586 gitlab_omniauth-ldap (= 1.0.3) 584 gitlab_omniauth-ldap (= 1.0.3)
587 gon 585 gon
@@ -613,8 +611,9 @@ DEPENDENCIES @@ -613,8 +611,9 @@ DEPENDENCIES
613 pry 611 pry
614 quiet_assets (~> 1.0.1) 612 quiet_assets (~> 1.0.1)
615 rack-attack 613 rack-attack
  614 + rack-cors
616 rack-mini-profiler 615 rack-mini-profiler
617 - rails (= 3.2.15) 616 + rails (= 3.2.16)
618 rails-dev-tweaks 617 rails-dev-tweaks
619 rails_best_practices 618 rails_best_practices
620 raphael-rails (~> 2.1.2) 619 raphael-rails (~> 2.1.2)
@@ -32,7 +32,9 @@ @@ -32,7 +32,9 @@
32 32
33 * GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/) 33 * GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/)
34 34
35 -* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server 35 +* [GitLab Enterprise Edition](https://www.gitlab.com/features/) offers additional features that are useful for larger organizations (100+ users).
  36 +
  37 +* [GitLab CI](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) is a continuous integration (CI) server that is easy to integrate with GitLab.
36 38
37 ### Requirements 39 ### Requirements
38 40
@@ -44,31 +46,24 @@ @@ -44,31 +46,24 @@
44 46
45 ** More details are in the [requirements doc](doc/install/requirements.md) 47 ** More details are in the [requirements doc](doc/install/requirements.md)
46 48
47 -### Installation  
48 -  
49 -#### Official production installation  
50 -  
51 -* [Installation guide for a production server](doc/install/installation.md) 49 +### Official installation methods
52 50
  51 +* [Manual installation guide for a production server](doc/install/installation.md)
53 52
54 -#### Official development installation 53 +* [GitLab Chef Cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md) This cookbook can be used both for development installations and production installations. If you want to [contribute](CONTRIBUTE.md) to GitLab we suggest you follow the [development installation on a virtual machine with Vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) instructions to install all testing dependencies.
55 54
56 -If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working with all dependencies. 55 +### Third party one-click installers
57 56
58 -* [Vagrant virtual machine for development](https://github.com/gitlabhq/gitlab-vagrant-vm) 57 +* [Digital Ocean 1-Click Application Install](https://www.digitalocean.com/blog_posts/host-your-git-repositories-in-55-seconds-with-gitlab) Have a new server up in 55 seconds. Digital Ocean uses SSD disks which is great for an IO intensive app such as GitLab.
59 58
  59 +* [BitNami one-click installers](http://bitnami.com/stack/gitlab) This package contains both GitLab and GitLab CI. It is available as installer, virtual machine or for cloud hosting providers (Amazon Web Services/Azure/etc.).
60 60
61 -#### Unofficial production installations 61 +#### Unofficial installation methods
62 62
63 * [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) repository with unofficial guides for using GitLab with different software (operating systems, webservers, etc.) than the official version. 63 * [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) repository with unofficial guides for using GitLab with different software (operating systems, webservers, etc.) than the official version.
64 64
65 * [Installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) public wiki with unofficial guides to install GitLab on different operating systems. 65 * [Installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) public wiki with unofficial guides to install GitLab on different operating systems.
66 66
67 -* [BitNami one-click installers](http://bitnami.com/stack/gitlab)  
68 -  
69 -* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab)  
70 -  
71 -  
72 ### New versions and upgrading 67 ### New versions and upgrading
73 68
74 Since 2011 GitLab is released on the 22nd of every month. Every new release includes an upgrade guide. 69 Since 2011 GitLab is released on the 22nd of every month. Every new release includes an upgrade guide.
@@ -79,7 +74,6 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl @@ -79,7 +74,6 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl
79 74
80 * Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). 75 * Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
81 76
82 -  
83 ### Run in production mode 77 ### Run in production mode
84 78
85 The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually: 79 The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually:
@@ -110,7 +104,7 @@ or start each component separately @@ -110,7 +104,7 @@ or start each component separately
110 104
111 * Run all tests 105 * Run all tests
112 106
113 - bundle exec rake gitlab:test 107 + bundle exec rake gitlab:test RAILS_ENV=test
114 108
115 * [RSpec](http://rspec.info/) unit and functional tests 109 * [RSpec](http://rspec.info/) unit and functional tests
116 110
@@ -147,15 +141,17 @@ or start each component separately @@ -147,15 +141,17 @@ or start each component separately
147 141
148 * [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. 142 * [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix.
149 143
150 -* [Unofficial #gitlab IRC on Freenode](http://www.freenode.net/) is another way to get in touch with other GitLab users who may be able to help you.  
151 -  
152 * [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab. 144 * [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab.
153 145
154 * [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed. 146 * [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed.
155 147
156 * [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions. 148 * [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions.
157 149
158 -* [Consultancy](http://www.gitlab.com/consultancy/) allows you hire GitLab experts for installations, upgrades and customizations. 150 +* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations.
  151 +
  152 +* [#gitlab IRC channel](http://www.freenode.net/) on Freenode is unofficial but offers a way to get in touch with other GitLab users who may be able to help you.
  153 +
  154 +* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview.
159 155
160 156
161 ### Getting in touch 157 ### Getting in touch
1 -6.3.0.pre 1 +6.4.0.pre
app/assets/images/favicon.ico
No preview for this file type
app/assets/images/logo-black.png

3.01 KB | W: | H:

2.95 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
app/assets/images/logo-white.png

5.59 KB | W: | H:

8.14 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
app/assets/javascripts/api.js.coffee
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 users_path: "/api/:version/users.json" 2 users_path: "/api/:version/users.json"
3 user_path: "/api/:version/users/:id.json" 3 user_path: "/api/:version/users/:id.json"
4 notes_path: "/api/:version/projects/:id/notes.json" 4 notes_path: "/api/:version/projects/:id/notes.json"
  5 + namespaces_path: "/api/:version/namespaces.json"
5 6
6 # Get 20 (depends on api) recent notes 7 # Get 20 (depends on api) recent notes
7 # and sort the ascending from oldest to newest 8 # and sort the ascending from oldest to newest
@@ -49,6 +50,20 @@ @@ -49,6 +50,20 @@
49 ).done (users) -> 50 ).done (users) ->
50 callback(users) 51 callback(users)
51 52
  53 + # Return namespaces list. Filtered by query
  54 + namespaces: (query, callback) ->
  55 + url = Api.buildUrl(Api.namespaces_path)
  56 +
  57 + $.ajax(
  58 + url: url
  59 + data:
  60 + private_token: gon.api_token
  61 + search: query
  62 + per_page: 20
  63 + dataType: "json"
  64 + ).done (namespaces) ->
  65 + callback(namespaces)
  66 +
52 buildUrl: (url) -> 67 buildUrl: (url) ->
53 url = gon.relative_url_root + url if gon.relative_url_root? 68 url = gon.relative_url_root + url if gon.relative_url_root?
54 return url.replace(':version', gon.api_version) 69 return url.replace(':version', gon.api_version)
app/assets/javascripts/blob.js.coffee
1 class BlobView 1 class BlobView
2 constructor: -> 2 constructor: ->
  3 + # handle multi-line select
  4 + handleMultiSelect = (e) ->
  5 + [ first_line, last_line ] = parseSelectedLines()
  6 + [ line_number ] = parseSelectedLines($(this).attr("id"))
  7 + hash = "L#{line_number}"
  8 +
  9 + if e.shiftKey and not isNaN(first_line) and not isNaN(line_number)
  10 + if line_number < first_line
  11 + last_line = first_line
  12 + first_line = line_number
  13 + else
  14 + last_line = line_number
  15 +
  16 + hash = if first_line == last_line then "L#{first_line}" else "L#{first_line}-#{last_line}"
  17 +
  18 + setHash(hash)
  19 + e.preventDefault()
  20 +
3 # See if there are lines selected 21 # See if there are lines selected
4 # "#L12" and "#L34-56" supported 22 # "#L12" and "#L34-56" supported
5 - highlightBlobLines = ->  
6 - if window.location.hash isnt ""  
7 - matches = window.location.hash.match(/\#L(\d+)(\-(\d+))?/) 23 + highlightBlobLines = (e) ->
  24 + [ first_line, last_line ] = parseSelectedLines()
  25 +
  26 + unless isNaN first_line
  27 + $("#tree-content-holder .highlight .line").removeClass("hll")
  28 + $("#LC#{line}").addClass("hll") for line in [first_line..last_line]
  29 + $("#L#{first_line}").ScrollTo() unless e?
  30 +
  31 + # parse selected lines from hash
  32 + # always return first and last line (initialized to NaN)
  33 + parseSelectedLines = (str) ->
  34 + first_line = NaN
  35 + last_line = NaN
  36 + hash = str || window.location.hash
  37 +
  38 + if hash isnt ""
  39 + matches = hash.match(/\#?L(\d+)(\-(\d+))?/)
8 first_line = parseInt(matches?[1]) 40 first_line = parseInt(matches?[1])
9 last_line = parseInt(matches?[3]) 41 last_line = parseInt(matches?[3])
  42 + last_line = first_line if isNaN(last_line)
  43 +
  44 + [ first_line, last_line ]
  45 +
  46 + setHash = (hash) ->
  47 + hash = hash.replace(/^\#/, "")
  48 + nodes = $("#" + hash)
  49 + # if any nodes are using this id, they must be temporarily changed
  50 + # also, add a temporary div at the top of the screen to prevent scrolling
  51 + if nodes.length > 0
  52 + scroll_top = $(document).scrollTop()
  53 + nodes.attr("id", "")
  54 + tmp = $("<div></div>")
  55 + .css({ position: "absolute", visibility: "hidden", top: scroll_top + "px" })
  56 + .attr("id", hash)
  57 + .appendTo(document.body)
  58 +
  59 + window.location.hash = hash
  60 +
  61 + # restore the nodes
  62 + if nodes.length > 0
  63 + tmp.remove()
  64 + nodes.attr("id", hash)
10 65
11 - unless isNaN first_line  
12 - last_line = first_line if isNaN(last_line)  
13 - $("#tree-content-holder .highlight .line").removeClass("hll")  
14 - $("#LC#{line}").addClass("hll") for line in [first_line..last_line]  
15 - $("#L#{first_line}").ScrollTo() 66 + # initialize multi-line select
  67 + $("#tree-content-holder .line_numbers a[id^=L]").on("click", handleMultiSelect)
16 68
17 # Highlight the correct lines on load 69 # Highlight the correct lines on load
18 highlightBlobLines() 70 highlightBlobLines()
19 71
20 # Highlight the correct lines when the hash part of the URL changes 72 # Highlight the correct lines when the hash part of the URL changes
21 - $(window).on 'hashchange', highlightBlobLines 73 + $(window).on("hashchange", highlightBlobLines)
22 74
23 75
24 @BlobView = BlobView 76 @BlobView = BlobView
app/assets/javascripts/commits.js.coffee
@@ -4,13 +4,13 @@ class CommitsList @@ -4,13 +4,13 @@ class CommitsList
4 limit: 0 4 limit: 0
5 offset: 0 5 offset: 0
6 @disable = false 6 @disable = false
7 - 7 +
8 @showProgress: -> 8 @showProgress: ->
9 $('.loading').show() 9 $('.loading').show()
10 - 10 +
11 @hideProgress: -> 11 @hideProgress: ->
12 $('.loading').hide() 12 $('.loading').hide()
13 - 13 +
14 @init: (ref, limit) -> 14 @init: (ref, limit) ->
15 $(".day-commits-table li.commit").live 'click', (event) -> 15 $(".day-commits-table li.commit").live 'click', (event) ->
16 if event.target.nodeName != "A" 16 if event.target.nodeName != "A"
@@ -21,7 +21,7 @@ class CommitsList @@ -21,7 +21,7 @@ class CommitsList
21 @data.ref = ref 21 @data.ref = ref
22 @data.limit = limit 22 @data.limit = limit
23 @data.offset = limit 23 @data.offset = limit
24 - 24 +
25 this.initLoadMore() 25 this.initLoadMore()
26 this.showProgress() 26 this.showProgress()
27 27
@@ -32,7 +32,9 @@ class CommitsList @@ -32,7 +32,9 @@ class CommitsList
32 url: location.href 32 url: location.href
33 data: @data 33 data: @data
34 complete: this.hideProgress 34 complete: this.hideProgress
35 - dataType: "script" 35 + success: (data) ->
  36 + CommitsList.append(data.count, data.html)
  37 + dataType: "json"
36 38
37 @append: (count, html) -> 39 @append: (count, html) ->
38 $("#commits-list").append(html) 40 $("#commits-list").append(html)
@@ -40,7 +42,7 @@ class CommitsList @@ -40,7 +42,7 @@ class CommitsList
40 @data.offset += count 42 @data.offset += count
41 else 43 else
42 @disable = true 44 @disable = true
43 - 45 +
44 @initLoadMore: -> 46 @initLoadMore: ->
45 $(document).unbind('scroll') 47 $(document).unbind('scroll')
46 $(document).endlessScroll 48 $(document).endlessScroll
app/assets/javascripts/issues.js.coffee
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 backgroundColor: '#DDD' 22 backgroundColor: '#DDD'
23 opacity: .4 23 opacity: .4
24 ) 24 )
25 - 25 +
26 reload: -> 26 reload: ->
27 Issues.initSelects() 27 Issues.initSelects()
28 Issues.initChecks() 28 Issues.initChecks()
@@ -54,7 +54,16 @@ @@ -54,7 +54,16 @@
54 unless terms is last_terms 54 unless terms is last_terms
55 last_terms = terms 55 last_terms = terms
56 if terms.length >= 2 or terms.length is 0 56 if terms.length >= 2 or terms.length is 0
57 - form.submit() 57 + $.ajax
  58 + type: "GET"
  59 + url: location.href
  60 + data: "issue_search=" + terms
  61 + complete: ->
  62 + $(".loading").hide()
  63 + success: (data) ->
  64 + $('.issues-holder').html(data.html)
  65 + Issues.reload()
  66 + dataType: "json"
58 67
59 checkChanged: -> 68 checkChanged: ->
60 checked_issues = $(".selected_issue:checked") 69 checked_issues = $(".selected_issue:checked")
app/assets/javascripts/main.js.coffee
1 -window.updatePage = (data) ->  
2 - $.ajax({type: "GET", url: location.href, data: data, dataType: "script"})  
3 -  
4 window.slugify = (text) -> 1 window.slugify = (text) ->
5 text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() 2 text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
6 3
app/assets/javascripts/merge_requests.js.coffee
@@ -21,7 +21,7 @@ class MergeRequest @@ -21,7 +21,7 @@ class MergeRequest
21 this.initMergeWidget() 21 this.initMergeWidget()
22 this.$('.show-all-commits').on 'click', => 22 this.$('.show-all-commits').on 'click', =>
23 this.showAllCommits() 23 this.showAllCommits()
24 - 24 +
25 modal = $('#modal_merge_info').modal(show: false) 25 modal = $('#modal_merge_info').modal(show: false)
26 26
27 # Local jQuery finder 27 # Local jQuery finder
@@ -83,12 +83,12 @@ class MergeRequest @@ -83,12 +83,12 @@ class MergeRequest
83 url: this.$('.nav-tabs .diffs-tab a').attr('href') 83 url: this.$('.nav-tabs .diffs-tab a').attr('href')
84 beforeSend: => 84 beforeSend: =>
85 this.$('.status').addClass 'loading' 85 this.$('.status').addClass 'loading'
86 -  
87 complete: => 86 complete: =>
88 @diffs_loaded = true 87 @diffs_loaded = true
89 this.$('.status').removeClass 'loading' 88 this.$('.status').removeClass 'loading'
90 -  
91 - dataType: 'script' 89 + success: (data) =>
  90 + this.$(".diffs").html(data.html)
  91 + dataType: 'json'
92 92
93 showAllCommits: -> 93 showAllCommits: ->
94 this.$('.first-commits').remove() 94 this.$('.first-commits').remove()
app/assets/javascripts/namespace_select.js.coffee 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +$ ->
  2 + namespaceFormatResult = (namespace) ->
  3 + markup = "<div class='namespace-result'>"
  4 + markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
  5 + markup += "<span class='namespace-path'>" + namespace.path + "</span>"
  6 + markup += "</div>"
  7 + markup
  8 +
  9 + formatSelection = (namespace) ->
  10 + namespace.kind + ": " + namespace.path
  11 +
  12 + $('.ajax-namespace-select').each (i, select) ->
  13 + $(select).select2
  14 + placeholder: "Search for namespace"
  15 + multiple: $(select).hasClass('multiselect')
  16 + minimumInputLength: 0
  17 + query: (query) ->
  18 + Api.namespaces query.term, (namespaces) ->
  19 + data = { results: namespaces }
  20 + query.callback(data)
  21 +
  22 + dropdownCssClass: "ajax-namespace-dropdown"
  23 + formatResult: namespaceFormatResult
  24 + formatSelection: formatSelection
app/assets/javascripts/notes.js
@@ -6,7 +6,7 @@ var NoteList = { @@ -6,7 +6,7 @@ var NoteList = {
6 target_type: null, 6 target_type: null,
7 7
8 init: function(tid, tt, path) { 8 init: function(tid, tt, path) {
9 - NoteList.notes_path = path + ".js"; 9 + NoteList.notes_path = path + ".json";
10 NoteList.target_id = tid; 10 NoteList.target_id = tid;
11 NoteList.target_type = tt; 11 NoteList.target_type = tt;
12 NoteList.target_params = "target_type=" + NoteList.target_type + "&target_id=" + NoteList.target_id; 12 NoteList.target_params = "target_type=" + NoteList.target_type + "&target_id=" + NoteList.target_id;
@@ -233,10 +233,12 @@ var NoteList = { @@ -233,10 +233,12 @@ var NoteList = {
233 form.show(); 233 form.show();
234 234
235 var textarea = form.find("textarea"); 235 var textarea = form.find("textarea");
236 - var p = $("<p></p>").text(textarea.val());  
237 - var hidden_div = $('<div class="note-original-content"></div>').append(p);  
238 - form.append(hidden_div);  
239 - hidden_div.hide(); 236 + if (form.find(".note-original-content").length === 0) {
  237 + var p = $("<p></p>").text(textarea.val());
  238 + var hidden_div = $('<div class="note-original-content"></div>').append(p);
  239 + form.append(hidden_div);
  240 + hidden_div.hide();
  241 + }
240 textarea.focus(); 242 textarea.focus();
241 }, 243 },
242 244
@@ -409,7 +411,10 @@ var NoteList = { @@ -409,7 +411,10 @@ var NoteList = {
409 data: NoteList.target_params, 411 data: NoteList.target_params,
410 complete: function(){ $('.js-notes-busy').removeClass("loading")}, 412 complete: function(){ $('.js-notes-busy').removeClass("loading")},
411 beforeSend: function() { $('.js-notes-busy').addClass("loading") }, 413 beforeSend: function() { $('.js-notes-busy').addClass("loading") },
412 - dataType: "script" 414 + success: function(data) {
  415 + NoteList.setContent(data.html);
  416 + },
  417 + dataType: "json"
413 }); 418 });
414 }, 419 },
415 420
@@ -417,7 +422,7 @@ var NoteList = { @@ -417,7 +422,7 @@ var NoteList = {
417 * Called in response to getContent(). 422 * Called in response to getContent().
418 * Replaces the content of #notes-list with the given html. 423 * Replaces the content of #notes-list with the given html.
419 */ 424 */
420 - setContent: function(newNoteIds, html) { 425 + setContent: function(html) {
421 $("#notes-list").html(html); 426 $("#notes-list").html(html);
422 }, 427 },
423 428
@@ -532,6 +537,8 @@ var NoteList = { @@ -532,6 +537,8 @@ var NoteList = {
532 note_text.html(response.note).show(); 537 note_text.html(response.note).show();
533 538
534 var note_form = note_li.find(".note-edit-form"); 539 var note_form = note_li.find(".note-edit-form");
  540 + var original_content = note_form.find(".note-original-content");
  541 + original_content.remove();
535 note_form.hide(); 542 note_form.hide();
536 note_form.find(".btn-save").enableButton(); 543 note_form.find(".btn-save").enableButton();
537 544
app/assets/javascripts/pager.js.coffee
@@ -19,8 +19,9 @@ @@ -19,8 +19,9 @@
19 data: "limit=" + @limit + "&offset=" + @offset 19 data: "limit=" + @limit + "&offset=" + @offset
20 complete: -> 20 complete: ->
21 $(".loading").hide() 21 $(".loading").hide()
22 -  
23 - dataType: "script" 22 + success: (data) ->
  23 + Pager.append(data.count, data.html)
  24 + dataType: "json"
24 25
25 append: (count, html) -> 26 append: (count, html) ->
26 $(".content_list").append html 27 $(".content_list").append html
app/assets/javascripts/project.js.coffee
@@ -40,3 +40,9 @@ $ -&gt; @@ -40,3 +40,9 @@ $ -&gt;
40 # Ref switcher 40 # Ref switcher
41 $('.project-refs-select').on 'change', -> 41 $('.project-refs-select').on 'change', ->
42 $(@).parents('form').submit() 42 $(@).parents('form').submit()
  43 +
  44 + $('.hide-no-ssh-message').on 'click', (e) ->
  45 + path = '/'
  46 + $.cookie('hide_no_ssh_message', 'false', { path: path })
  47 + $(@).parents('.no-ssh-key-message').hide()
  48 + e.preventDefault()
app/assets/stylesheets/common.scss
@@ -220,7 +220,6 @@ li.note { @@ -220,7 +220,6 @@ li.note {
220 .error-message { 220 .error-message {
221 padding: 10px; 221 padding: 10px;
222 background: #C67; 222 background: #C67;
223 - padding-left: 20px;  
224 margin: 0; 223 margin: 0;
225 color: #FFF; 224 color: #FFF;
226 225
@@ -228,8 +227,18 @@ li.note { @@ -228,8 +227,18 @@ li.note {
228 color: #fff; 227 color: #fff;
229 text-decoration: underline; 228 text-decoration: underline;
230 } 229 }
231 - &.centered {  
232 - text-align: center; 230 +}
  231 +
  232 +.no-ssh-key-message {
  233 + padding: 10px 0;
  234 + background: #C67;
  235 + margin: 0;
  236 + color: #FFF;
  237 + text-align: center;
  238 +
  239 + a {
  240 + color: #fff;
  241 + text-decoration: underline;
233 } 242 }
234 } 243 }
235 244
@@ -341,4 +350,46 @@ table { @@ -341,4 +350,46 @@ table {
341 .navbar-gitlab .navbar-inner .nav > li .btn-sign-in { 350 .navbar-gitlab .navbar-inner .nav > li .btn-sign-in {
342 @extend .btn-new; 351 @extend .btn-new;
343 padding: 5px 15px; 352 padding: 5px 15px;
  353 + text-shadow: none;
  354 +}
  355 +
  356 +.broadcast-message {
  357 + padding: 10px;
  358 + text-align: center;
  359 + background: #555;
  360 + color: #BBB;
  361 +}
  362 +
  363 +.ajax-users-select {
  364 + width: 400px;
  365 +
  366 + &.input-large {
  367 + width: 210px;
  368 + }
  369 +
  370 + &.input-clamp {
  371 + max-width: 100%;
  372 + }
  373 +}
  374 +
  375 +.user-result {
  376 + .user-image {
  377 + float: left;
  378 + }
  379 + .user-name {
  380 + }
  381 + .user-username {
  382 + color: #999;
  383 + }
  384 +}
  385 +
  386 +.namespace-result {
  387 + .namespace-kind {
  388 + color: #AAA;
  389 + font-weight: normal;
  390 + }
  391 + .namespace-path {
  392 + margin-left: 10px;
  393 + font-weight: bolder;
  394 + }
344 } 395 }
app/assets/stylesheets/gitlab_bootstrap/blocks.scss
@@ -34,9 +34,7 @@ @@ -34,9 +34,7 @@
34 &.ui-box-show { 34 &.ui-box-show {
35 color: #666; 35 color: #666;
36 margin:20px 0; 36 margin:20px 0;
37 - background: #FFF;  
38 - box-shadow: inset 0 1px 0 #fff, 0 1px 5px #f1f1f1;  
39 - @include linear-gradient(#fafafa, #f1f1f1); 37 + background: #FAFAFA;
40 38
41 .control-group { 39 .control-group {
42 margin-bottom: 0; 40 margin-bottom: 0;
@@ -44,11 +42,13 @@ @@ -44,11 +42,13 @@
44 } 42 }
45 43
46 &.ui-box-danger { 44 &.ui-box-danger {
  45 + background: #f7f7f7;
  46 + border: none;
  47 +
47 .title { 48 .title {
48 - @include linear-gradient(#F26E5E, #bd362f); 49 + background: #D65;
49 color: #fff; 50 color: #fff;
50 text-shadow: 0 1px 1px #900; 51 text-shadow: 0 1px 1px #900;
51 - font-weight: bold;  
52 } 52 }
53 } 53 }
54 54
@@ -98,9 +98,9 @@ @@ -98,9 +98,9 @@
98 } 98 }
99 99
100 .title { 100 .title {
101 - @include bg-gray-gradient;  
102 - border-bottom: 1px solid #CCC;  
103 - color: #456; 101 + background-color: #EEE;
  102 + border-bottom: 1px solid #DDD;
  103 + color: #666;
104 font-size: 16px; 104 font-size: 16px;
105 text-shadow: 0 1px 1px #fff; 105 text-shadow: 0 1px 1px #fff;
106 padding: 0 10px; 106 padding: 0 10px;
app/assets/stylesheets/gitlab_bootstrap/buttons.scss
1 .btn { 1 .btn {
2 display: inline-block; 2 display: inline-block;
3 - padding: 6px 12px;  
4 margin-bottom: 0; 3 margin-bottom: 0;
5 - font-size: 13px;  
6 - line-height: $baseLineHeight; 4 + font-weight: normal;
7 text-align: center; 5 text-align: center;
8 vertical-align: middle; 6 vertical-align: middle;
9 cursor: pointer; 7 cursor: pointer;
10 - border: 1px solid #BBB;  
11 - color: $style_color;  
12 - @include border-radius($baseBorderRadius);  
13 - @include box-shadow(inset 0 1px 0 rgba(255,255,255,.2));  
14 - @include linear-gradient(#f1f1f1, #e1e1e1);  
15 - text-shadow: 0 1px 1px #FFF;  
16 - text-decoration: none; 8 + background-image: none;
  9 + border: 1px solid transparent;
  10 + white-space: nowrap;
  11 + padding: 6px 12px;
  12 + font-size: 13px;
  13 + line-height: 18px;
  14 + border-radius: 4px;
  15 + -webkit-user-select: none;
  16 + -moz-user-select: none;
  17 + -ms-user-select: none;
  18 + -o-user-select: none;
  19 + user-select: none;
  20 + color: #444444;
  21 + background-color: #fff;
  22 + border-color: #ccc;
  23 + text-shadow: none;
17 24
18 &.hover, 25 &.hover,
19 &:hover { 26 &:hover {
20 - color: $style_color;  
21 - background: #f1f1f1;  
22 - border-color: #AAA; 27 + color: #444444;
23 text-decoration: none; 28 text-decoration: none;
24 - @include linear-gradient(#fAfAfA, #f1f1f1); 29 + background-color: #ebebeb;
  30 + border-color: #adadad;
25 } 31 }
26 32
27 &.focus, 33 &.focus,
28 &:focus { 34 &:focus {
  35 + color: #444444;
29 text-decoration: none; 36 text-decoration: none;
30 - @include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); 37 + outline: thin dotted #333;
  38 + outline: 5px auto -webkit-focus-ring-color;
  39 + outline-offset: -2px;
31 } 40 }
32 41
33 &.active, 42 &.active,
34 &:active { 43 &:active {
35 - background-image: none;  
36 outline: 0; 44 outline: 0;
37 - text-decoration: none;  
38 - @include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); 45 + background-image: none;
  46 + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
  47 + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
39 } 48 }
40 49
41 &.disabled, 50 &.disabled,
42 &[disabled] { 51 &[disabled] {
43 - cursor: default;  
44 - background-image: none;  
45 - @include opacity(65);  
46 - @include box-shadow(none); 52 + cursor: not-allowed;
  53 + pointer-events: none;
  54 + opacity: 0.65;
  55 + filter: alpha(opacity=65);
  56 + -webkit-box-shadow: none;
  57 + box-shadow: none;
47 } 58 }
48 59
49 &.btn-primary { 60 &.btn-primary {
50 - color: #FFF;  
51 - border-color: #189;  
52 - text-shadow: 0 1px 1px #189;  
53 - @include linear-gradient(#4AC, #289); 61 + color: #ffffff;
  62 + background-color: #429bca;
  63 + border-color: #358ebd;
54 64
55 &.hover, 65 &.hover,
56 &:hover, 66 &:hover,
57 &.disabled, 67 &.disabled,
58 &[disabled] { 68 &[disabled] {
59 - color: #FFF;  
60 - background: #389; 69 + color: #ffffff;
  70 + background-color: #3286b1;
  71 + border-color: #286e8e;
61 } 72 }
62 } 73 }
63 74
64 &.btn-success { 75 &.btn-success {
65 - color: #FFF;  
66 - border-color: #1A1;  
67 - text-shadow: 0 1px 1px #FFF;  
68 - text-shadow: 0 1px 1px #181;  
69 - @include linear-gradient(#62C452, #51a351); 76 + color: #ffffff;
  77 + background-color: #5cb85c;
  78 + border-color: #4cae4c;
70 79
71 80
72 &.hover, 81 &.hover,
73 &:hover, 82 &:hover,
74 &.disabled, 83 &.disabled,
75 &[disabled] { 84 &[disabled] {
76 - color: #FFF;  
77 - background: #2A2; 85 + color: #ffffff;
  86 + background-color: #47a447;
  87 + border-color: #398439;
78 } 88 }
79 } 89 }
80 90
81 &.btn-danger { 91 &.btn-danger {
82 - color: #FFF;  
83 - text-shadow: 0 1px 1px #811;  
84 - border-color: #BD362F;  
85 - @include linear-gradient(#EE5F5B, #BD362F); 92 + color: #ffffff;
  93 + background-color: #d9534f;
  94 + border-color: #d43f3a;
86 95
87 96
88 &.hover, 97 &.hover,
89 &:hover, 98 &:hover,
90 &.disabled, 99 &.disabled,
91 &[disabled] { 100 &[disabled] {
92 - color: #FFF;  
93 - background: #A22; 101 + color: #ffffff;
  102 + background-color: #d2322d;
  103 + border-color: #ac2925;
94 } 104 }
95 } 105 }
96 106
app/assets/stylesheets/gitlab_bootstrap/common.scss
1 /** COLORS **/ 1 /** COLORS **/
2 .cgray { color: gray } 2 .cgray { color: gray }
  3 +.clgray { color: #BBB }
3 .cred { color: #D12F19 } 4 .cred { color: #D12F19 }
4 .cgreen { color: #4a2 } 5 .cgreen { color: #4a2 }
5 .cblue { color: #29A } 6 .cblue { color: #29A }
6 .cblack { color: #111 } 7 .cblack { color: #111 }
7 .cdark { color: #444 } 8 .cdark { color: #444 }
  9 +.camber { color: #ffc000 }
8 .cwhite { color: #fff!important } 10 .cwhite { color: #fff!important }
9 .bgred { background: #F2DEDE!important } 11 .bgred { background: #F2DEDE!important }
10 12
@@ -93,6 +95,12 @@ pre.well-pre { @@ -93,6 +95,12 @@ pre.well-pre {
93 font-size: 12px; 95 font-size: 12px;
94 font-style: normal; 96 font-style: normal;
95 font-weight: normal; 97 font-weight: normal;
  98 +
  99 + &.label-gray {
  100 + background-color: #eee;
  101 + color: #999;
  102 + text-shadow: none;
  103 + }
96 } 104 }
97 105
98 /** Big Labels **/ 106 /** Big Labels **/
@@ -116,3 +124,12 @@ pre.well-pre { @@ -116,3 +124,12 @@ pre.well-pre {
116 color: #FFF; 124 color: #FFF;
117 } 125 }
118 } 126 }
  127 +
  128 +.dropdown-menu > li > a {
  129 + text-shadow: none;
  130 +}
  131 +
  132 +.dropdown-menu > li > a:hover,
  133 +.dropdown-menu > li > a:focus {
  134 + background: #29b;
  135 +}
app/assets/stylesheets/gitlab_bootstrap/files.scss
@@ -11,8 +11,8 @@ @@ -11,8 +11,8 @@
11 } 11 }
12 12
13 .file-title { 13 .file-title {
14 - border-bottom: 1px solid #bbb;  
15 - @include bg-dark-gray-gradient; 14 + background: #DDD;
  15 + border-bottom: 1px solid #CCC;
16 text-shadow: 0 1px 1px #fff; 16 text-shadow: 0 1px 1px #fff;
17 margin: 0; 17 margin: 0;
18 font-weight: normal; 18 font-weight: normal;
app/assets/stylesheets/gitlab_bootstrap/forms.scss
@@ -3,6 +3,23 @@ form { @@ -3,6 +3,23 @@ form {
3 3
4 label { 4 label {
5 @extend .control-label; 5 @extend .control-label;
  6 +
  7 + &.radio-label {
  8 + text-align: left;
  9 + width: 100%;
  10 + margin-left: 0;
  11 +
  12 + input[type="radio"] {
  13 + margin-top: 1px !important;
  14 + }
  15 + }
  16 +
  17 + &.list-label {
  18 + float: none;
  19 + padding: 0 !important;
  20 + margin: 0;
  21 + text-align: left;
  22 + }
6 } 23 }
7 } 24 }
8 25
@@ -49,3 +66,9 @@ fieldset legend { @@ -49,3 +66,9 @@ fieldset legend {
49 font-size: 16px; 66 font-size: 16px;
50 margin-bottom: 10px; 67 margin-bottom: 10px;
51 } 68 }
  69 +
  70 +.datetime-controls {
  71 + select {
  72 + width: 100px;
  73 + }
  74 +}
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
@@ -89,10 +89,26 @@ @@ -89,10 +89,26 @@
89 } 89 }
90 90
91 code { padding: 0 4px; } 91 code { padding: 0 4px; }
92 - h1 { margin-top: 30px;}  
93 - h2 { margin-top: 25px;}  
94 - h3 { margin-top: 20px;}  
95 - h4 { margin-top: 15px;} 92 +
  93 + h1 {
  94 + margin-top: 45px;
  95 + font-size: 2.5em;
  96 + }
  97 +
  98 + h2 {
  99 + margin-top: 40px;
  100 + font-size: 2em;
  101 + }
  102 +
  103 + h3 {
  104 + margin-top: 35px;
  105 + font-size: 2em;
  106 + }
  107 +
  108 + h4 {
  109 + margin-top: 30px;
  110 + font-size: 1.5em;
  111 + }
96 112
97 blockquote p { 113 blockquote p {
98 color: #888; 114 color: #888;
@@ -107,6 +123,16 @@ @@ -107,6 +123,16 @@
107 background: #EEE; 123 background: #EEE;
108 } 124 }
109 } 125 }
  126 +
  127 + code {
  128 + font-size: inherit;
  129 + font-weight: inherit;
  130 + color: #555;
  131 + }
  132 +
  133 + li {
  134 + line-height: 1.5;
  135 + }
110 } 136 }
111 137
112 @mixin page-title { 138 @mixin page-title {
app/assets/stylesheets/gitlab_bootstrap/nav.scss
@@ -15,18 +15,16 @@ @@ -15,18 +15,16 @@
15 > li > a { 15 > li > a {
16 border-left: 4px solid #EEE; 16 border-left: 4px solid #EEE;
17 padding: 12px; 17 padding: 12px;
  18 + color: #777;
18 } 19 }
19 > .active > a { 20 > .active > a {
20 border-color: $primary_color; 21 border-color: $primary_color;
21 - border-radius: 0;  
22 - background: #F1F1F1;  
23 - color: $style_color;  
24 - font-weight: bold;  
25 - text-shadow: 0 1px 1px #fff; 22 + background: none;
  23 + color: #333;
  24 + font-weight: bolder;
26 } 25 }
27 26
28 &.nav-stacked-menu { 27 &.nav-stacked-menu {
29 - background: #FAFAFA;  
30 li > a { 28 li > a {
31 padding: 16px; 29 padding: 16px;
32 } 30 }
@@ -36,6 +34,7 @@ @@ -36,6 +34,7 @@
36 &.nav-pills-small { 34 &.nav-pills-small {
37 > li > a { 35 > li > a {
38 padding: 8px 12px; 36 padding: 8px 12px;
  37 + font-size: 12px;
39 } 38 }
40 } 39 }
41 } 40 }
app/assets/stylesheets/sections/admin.scss
@@ -20,4 +20,19 @@ @@ -20,4 +20,19 @@
20 label { width: 110px; } 20 label { width: 110px; }
21 .controls { margin-left: 130px; } 21 .controls { margin-left: 130px; }
22 .form-actions { padding-left: 130px; background: #fff } 22 .form-actions { padding-left: 130px; background: #fff }
  23 + .visibility-levels {
  24 + .controls {
  25 + margin-bottom: 9px;
  26 + }
  27 +
  28 + i {
  29 + color: inherit;
  30 + }
  31 + }
  32 +}
  33 +
  34 +.broadcast-messages {
  35 + .message {
  36 + line-height: 2;
  37 + }
23 } 38 }
app/assets/stylesheets/sections/commits.scss
@@ -16,37 +16,29 @@ @@ -16,37 +16,29 @@
16 16
17 .header { 17 .header {
18 @extend .clearfix; 18 @extend .clearfix;
  19 + background: #DDD;
  20 + border-bottom: 1px solid #CCC;
19 padding: 5px 5px 5px 10px; 21 padding: 5px 5px 5px 10px;
20 color: #555; 22 color: #555;
21 - border-bottom: 1px solid #CCC;  
22 - background: #eee;  
23 - // TODO Replace with linear-gradient mixin  
24 - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));  
25 - background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);  
26 - background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);  
27 - background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);  
28 - background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);  
29 -  
30 - a{  
31 - color: $style_color;  
32 - }  
33 23
34 > span { 24 > span {
35 font-family: $monospace_font; 25 font-family: $monospace_font;
36 font-size: 14px; 26 font-size: 14px;
37 - line-height: 30px; 27 + line-height: 2;
38 } 28 }
39 29
40 - a.view-file{ 30 + .view-file {
41 font-weight: bold; 31 font-weight: bold;
  32 + float: right;
  33 + background-color: #EEE;
42 } 34 }
43 35
44 - .commit-short-id{ 36 + .commit-short-id {
45 font-family: $monospace_font; 37 font-family: $monospace_font;
46 font-size: smaller; 38 font-size: smaller;
47 } 39 }
48 40
49 - .file-mode{ 41 + .file-mode {
50 font-family: $monospace_font; 42 font-family: $monospace_font;
51 } 43 }
52 } 44 }
@@ -56,13 +48,13 @@ @@ -56,13 +48,13 @@
56 background: #FFF; 48 background: #FFF;
57 color: #333; 49 color: #333;
58 font-size: 12px; 50 font-size: 12px;
59 - .old{  
60 - span.idiff{ 51 + .old {
  52 + span.idiff {
61 background-color: #FAA; 53 background-color: #FAA;
62 } 54 }
63 } 55 }
64 - .new{  
65 - span.idiff{ 56 + .new {
  57 + span.idiff {
66 background-color: #AFA; 58 background-color: #AFA;
67 } 59 }
68 } 60 }
@@ -78,7 +70,7 @@ @@ -78,7 +70,7 @@
78 font-size: 12px; 70 font-size: 12px;
79 } 71 }
80 } 72 }
81 - .old_line, .new_line { 73 + .old_line, .new_line, .diff_line {
82 margin: 0px; 74 margin: 0px;
83 padding: 0px; 75 padding: 0px;
84 border: none; 76 border: none;
@@ -100,6 +92,15 @@ @@ -100,6 +92,15 @@
100 text-decoration: underline; 92 text-decoration: underline;
101 } 93 }
102 } 94 }
  95 + &.new {
  96 + background: #CFD;
  97 + }
  98 + &.old {
  99 + background: #FDD;
  100 + }
  101 + }
  102 + .diff_line {
  103 + padding: 0;
103 } 104 }
104 .line_holder { 105 .line_holder {
105 &.old .old_line, 106 &.old .old_line,
@@ -130,6 +131,11 @@ @@ -130,6 +131,11 @@
130 color: #ccc; 131 color: #ccc;
131 background: #fafafa; 132 background: #fafafa;
132 } 133 }
  134 + &.parallel {
  135 + display: table-cell;
  136 + overflow: hidden;
  137 + width: 50%;
  138 + }
133 } 139 }
134 } 140 }
135 .image { 141 .image {
app/assets/stylesheets/sections/dashboard.scss
@@ -51,7 +51,7 @@ @@ -51,7 +51,7 @@
51 li { 51 li {
52 &.active { 52 &.active {
53 a { 53 a {
54 - @include linear-gradient(#f5f5f5, #eee); 54 + background-color: #EEE;
55 border-bottom: 1px solid #EEE !important; 55 border-bottom: 1px solid #EEE !important;
56 &:hover { 56 &:hover {
57 background: #eee; 57 background: #eee;
@@ -100,3 +100,21 @@ @@ -100,3 +100,21 @@
100 padding: 2px 5px; 100 padding: 2px 5px;
101 } 101 }
102 } 102 }
  103 +
  104 +.project-access-icon {
  105 + margin-left: 10px;
  106 + float: left;
  107 + margin-right: 15px;
  108 + font-size: 20px;
  109 + margin-bottom: 15px;
  110 + border: 1px solid #EEE;
  111 + padding: 8px 12px;
  112 + border-radius: 50px;
  113 + background: #f5f5f5;
  114 + width: 16px;
  115 + text-align: center;
  116 +
  117 + i {
  118 + color: #BBB;
  119 + }
  120 +}
app/assets/stylesheets/sections/header.scss
@@ -36,8 +36,8 @@ header { @@ -36,8 +36,8 @@ header {
36 float: left; 36 float: left;
37 margin-right: 9px; 37 margin-right: 9px;
38 position: relative; 38 position: relative;
39 - top: -5px;  
40 - padding-top: 5px; 39 + top: -3px;
  40 + padding-top: 3px;
41 41
42 a { 42 a {
43 float: left; 43 float: left;
@@ -46,8 +46,8 @@ header { @@ -46,8 +46,8 @@ header {
46 46
47 h1 { 47 h1 {
48 margin: 0; 48 margin: 0;
49 - background: url('logo-black.png') no-repeat center 1px;  
50 - background-size: 38px; 49 + background: url('logo-black.png') no-repeat center center;
  50 + background-size: 32px;
51 float: left; 51 float: left;
52 height: 40px; 52 height: 40px;
53 width: 40px; 53 width: 40px;
@@ -152,8 +152,8 @@ header { @@ -152,8 +152,8 @@ header {
152 .app_logo { 152 .app_logo {
153 a { 153 a {
154 h1 { 154 h1 {
155 - background: url('logo-white.png') no-repeat center 1px;  
156 - background-size: 38px; 155 + background: url('logo-white.png') no-repeat center center;
  156 + background-size: 32px;
157 color: #fff; 157 color: #fff;
158 text-shadow: 0 1px 1px #444; 158 text-shadow: 0 1px 1px #444;
159 } 159 }
app/assets/stylesheets/sections/issues.scss
@@ -77,8 +77,8 @@ input.check_all_issues { @@ -77,8 +77,8 @@ input.check_all_issues {
77 @media (min-width: 800px) { .issues_filters select { width: 160px; } } 77 @media (min-width: 800px) { .issues_filters select { width: 160px; } }
78 @media (min-width: 1200px) { .issues_filters select { width: 220px; } } 78 @media (min-width: 1200px) { .issues_filters select { width: 220px; } }
79 79
80 -@media (min-width: 800px) { .issues_bulk_update select { width: 120px; } }  
81 -@media (min-width: 1200px) { .issues_bulk_update select { width: 160px; } } 80 +@media (min-width: 800px) { .issues_bulk_update .chosen-container { min-width: 120px; } }
  81 +@media (min-width: 1200px) { .issues_bulk_update .chosen-container { min-width: 160px; } }
82 82
83 .issues-holder { 83 .issues-holder {
84 .issues_filters { 84 .issues_filters {
@@ -103,3 +103,19 @@ input.check_all_issues { @@ -103,3 +103,19 @@ input.check_all_issues {
103 .participants { 103 .participants {
104 margin-bottom: 10px; 104 margin-bottom: 10px;
105 } 105 }
  106 +
  107 +.issues_bulk_update {
  108 + .chosen-container {
  109 + text-shadow: none;
  110 + }
  111 +}
  112 +
  113 +.issue-search-form {
  114 + margin: 0;
  115 + height: 24px;
  116 +
  117 + .issue_search {
  118 + border: 1px solid #DDD !important;
  119 + background-color: #f4f4f4;
  120 + }
  121 +}
app/assets/stylesheets/sections/merge_requests.scss
@@ -110,9 +110,29 @@ @@ -110,9 +110,29 @@
110 110
111 .merge-request-angle { 111 .merge-request-angle {
112 text-align: center; 112 text-align: center;
113 - margin: 0; 113 + margin: 0 auto;
  114 + background: #eee;
  115 + border-radius: 100px;
  116 + width: 60px;
  117 + line-height: 60px;
  118 + color: #777;
  119 + text-shadow: 0 1px 2px #FFF;
114 } 120 }
115 121
116 .merge-request-form-info { 122 .merge-request-form-info {
117 - padding: 15px 0; 123 + padding-top: 15px;
  124 +}
  125 +
  126 +.merge-request-branches {
  127 + .commit-row-message {
  128 + font-weight: normal !important;
  129 + }
  130 +
  131 + .chosen-container .chosen-single {
  132 + padding: 2px 0 2px 10px;
  133 + span {
  134 + font-weight: bold;
  135 + color: #555;
  136 + }
  137 + }
118 } 138 }
app/assets/stylesheets/sections/notes.scss
@@ -130,6 +130,12 @@ ul.notes { @@ -130,6 +130,12 @@ ul.notes {
130 &.notes_line { 130 &.notes_line {
131 text-align: center; 131 text-align: center;
132 padding: 10px 0; 132 padding: 10px 0;
  133 + background: #eee;
  134 + }
  135 + &.notes_line2 {
  136 + text-align: center;
  137 + padding: 10px 0;
  138 + border-left: 1px solid #ddd !important;
133 } 139 }
134 &.notes_content { 140 &.notes_content {
135 background-color: $white; 141 background-color: $white;
@@ -270,10 +276,9 @@ ul.notes { @@ -270,10 +276,9 @@ ul.notes {
270 276
271 // preview/edit buttons 277 // preview/edit buttons
272 > a { 278 > a {
273 - font-size: 24px;  
274 - padding: 4px;  
275 position: absolute; 279 position: absolute;
276 - right: 10px; 280 + right: 5px;
  281 + bottom: -60px;
277 } 282 }
278 .note_preview { 283 .note_preview {
279 background: #f5f5f5; 284 background: #f5f5f5;
@@ -306,10 +311,8 @@ ul.notes { @@ -306,10 +311,8 @@ ul.notes {
306 311
307 .common-note-form { 312 .common-note-form {
308 margin: 0; 313 margin: 0;
309 - height: 140px;  
310 background: #F9F9F9; 314 background: #F9F9F9;
311 padding: 3px; 315 padding: 3px;
312 - padding-bottom: 25px;  
313 border: 1px solid #DDD; 316 border: 1px solid #DDD;
314 } 317 }
315 318
@@ -320,7 +323,7 @@ ul.notes { @@ -320,7 +323,7 @@ ul.notes {
320 padding: 0 5px; 323 padding: 0 5px;
321 324
322 .note-form-option { 325 .note-form-option {
323 - margin-top: 10px; 326 + margin-top: 8px;
324 margin-left: 30px; 327 margin-left: 30px;
325 @extend .pull-left; 328 @extend .pull-left;
326 } 329 }
@@ -358,3 +361,7 @@ ul.notes { @@ -358,3 +361,7 @@ ul.notes {
358 .js-note-attachment-delete { 361 .js-note-attachment-delete {
359 display: none; 362 display: none;
360 } 363 }
  364 +
  365 +.parallel-comment {
  366 + padding: 6px;
  367 +}
app/assets/stylesheets/sections/profile.scss
@@ -42,3 +42,8 @@ @@ -42,3 +42,8 @@
42 margin-right: 12px; 42 margin-right: 12px;
43 } 43 }
44 44
  45 +.profile-avatar-form-option {
  46 + hr {
  47 + margin: 10px 0;
  48 + }
  49 +}
app/assets/stylesheets/sections/projects.scss
@@ -16,9 +16,15 @@ @@ -16,9 +16,15 @@
16 16
17 .project-home-panel { 17 .project-home-panel {
18 border-bottom: 1px solid #DDD; 18 border-bottom: 1px solid #DDD;
19 - padding-bottom: 30px; 19 + padding-bottom: 25px;
20 margin-bottom: 30px; 20 margin-bottom: 30px;
21 21
  22 + &.empty-project {
  23 + border-bottom: 0px;
  24 + padding-bottom: 15px;
  25 + margin-bottom: 0px;
  26 + }
  27 +
22 .project-home-title { 28 .project-home-title {
23 font-size: 18px; 29 font-size: 18px;
24 color: #777; 30 color: #777;
@@ -45,7 +51,7 @@ @@ -45,7 +51,7 @@
45 } 51 }
46 } 52 }
47 53
48 - .public-label { 54 + .visibility-level-label {
49 font-size: 14px; 55 font-size: 14px;
50 background: #f1f1f1; 56 background: #f1f1f1;
51 padding: 8px 10px; 57 padding: 8px 10px;
@@ -53,6 +59,10 @@ @@ -53,6 +59,10 @@
53 margin-left: 10px; 59 margin-left: 10px;
54 color: #888; 60 color: #888;
55 text-shadow: 0 1px 1px #FFF; 61 text-shadow: 0 1px 1px #FFF;
  62 +
  63 + i {
  64 + color: inherit;
  65 + }
56 } 66 }
57 } 67 }
58 68
@@ -87,9 +97,40 @@ @@ -87,9 +97,40 @@
87 } 97 }
88 } 98 }
89 99
90 -.project-public-holder {  
91 - .help-inline {  
92 - padding-top: 7px; 100 +.project-visibility-level-holder {
  101 + .controls {
  102 + padding-bottom: 9px;
  103 + }
  104 +
  105 + .controls {
  106 + input {
  107 + float: left;
  108 + }
  109 + .descr {
  110 + display: block;
  111 + margin-left: 1.5em;
  112 + &.restricted {
  113 + color: #888;
  114 + }
  115 +
  116 + label {
  117 + float: none;
  118 + padding: 0;
  119 + margin: 0;
  120 + text-align: left;
  121 + }
  122 + }
  123 + .info {
  124 + display: block;
  125 + margin-top: 5px;
  126 + }
  127 + strong {
  128 + display: inline-block;
  129 + width: 4em;
  130 + }
  131 + }
  132 + i {
  133 + color: inherit;
93 } 134 }
94 } 135 }
95 136
@@ -130,7 +171,8 @@ ul.nav.nav-projects-tabs { @@ -130,7 +171,8 @@ ul.nav.nav-projects-tabs {
130 margin: 0px; 171 margin: 0px;
131 } 172 }
132 173
133 -.my-projects { 174 +.my-projects,
  175 +.public-projects {
134 li { 176 li {
135 .project-info { 177 .project-info {
136 margin-bottom: 10px; 178 margin-bottom: 10px;
@@ -166,3 +208,61 @@ ul.nav.nav-projects-tabs { @@ -166,3 +208,61 @@ ul.nav.nav-projects-tabs {
166 color: #777; 208 color: #777;
167 } 209 }
168 } 210 }
  211 +
  212 +.project-side {
  213 + .btn-block {
  214 + background-image: none;
  215 + background-color: #F1f1f1;
  216 + border-color: #EEE;
  217 + &:hover {
  218 + background-color: #eee;
  219 + border-color: #DDD;
  220 + }
  221 + }
  222 + .project-fork-icon {
  223 + float: left;
  224 + font-size: 26px;
  225 + margin-right: 10px;
  226 + line-height: 1.5;
  227 + }
  228 +}
  229 +
  230 +.transfer-project .chosen-container {
  231 + min-width: 200px;
  232 +}
  233 +
  234 +/** Branch/tag selector **/
  235 +.project-refs-form {
  236 + margin: 0;
  237 + span {
  238 + background:none !important;
  239 + position:static !important;
  240 + width:auto !important;
  241 + height:auto !important;
  242 + }
  243 +}
  244 +.project-refs-select {
  245 + width: 120px;
  246 +}
  247 +
  248 +.project-refs-form .chosen-container {
  249 + position: relative;
  250 + top: 0;
  251 + left: 0;
  252 + margin-right: 10px;
  253 +
  254 + .chosen-single span {
  255 + font-weight: bold;
  256 + color: #555;
  257 + }
  258 +
  259 + &.chosen-container-active {
  260 + .chosen-drop {
  261 + min-width: 400px;
  262 + }
  263 +
  264 + .chosen-results {
  265 + max-height: 400px;
  266 + }
  267 + }
  268 +}
app/assets/stylesheets/selects.scss
1 -/* CHZN reset few styles */  
2 -.chosen-container-single .chosen-single {  
3 - background: #FFF;  
4 - border: 1px solid #bbb;  
5 - box-shadow: none;  
6 -}  
7 -.chosen-container-active .chosen-single {  
8 - background: #fff;  
9 -}  
10 -  
11 -.ajax-users-select {  
12 - width: 400px;  
13 -  
14 - &.input-large {  
15 - width: 210px;  
16 - }  
17 -}  
18 -  
19 -.user-result {  
20 - .user-image {  
21 - float: left;  
22 - }  
23 - .user-name {  
24 - }  
25 - .user-username {  
26 - color: #999;  
27 - }  
28 -}  
29 -  
30 -/** Branch/tag selector **/  
31 -.project-refs-form {  
32 - margin: 0;  
33 - span {  
34 - background:none !important;  
35 - position:static !important;  
36 - width:auto !important;  
37 - height:auto !important;  
38 - }  
39 -}  
40 -.project-refs-select {  
41 - width: 120px;  
42 -}  
43 -  
44 -.project-refs-form .chosen-container {  
45 - position: relative;  
46 - top: 0;  
47 - left: 0;  
48 - margin-right: 10px;  
49 -  
50 - .chosen-drop {  
51 - min-width: 400px;  
52 - .chosen-results {  
53 - max-height: 300px;  
54 - }  
55 - .chosen-search input {  
56 - min-width: 365px;  
57 - }  
58 - }  
59 -}  
60 -  
61 -/** Fix for Search Dropdown Border **/ 1 +/** Chosen.js selectbox style override **/
62 .chosen-container { 2 .chosen-container {
63 min-width: 100px; 3 min-width: 100px;
64 4
65 - .chosen-search {  
66 - input:focus {  
67 - @include box-shadow(none);  
68 - } 5 + .chosen-single {
  6 + background: #EEE !important;
  7 + border: 1px solid #DDD !important;
  8 + @include box-shadow(none !important);
  9 + @include border-radius(4px !important);
69 } 10 }
70 11
71 - .chosen-drop {  
72 - margin: 7px 0;  
73 - min-width: 200px;  
74 - border: 1px solid #bbb;  
75 - @include border-radius(0);  
76 -  
77 - .chosen-results {  
78 - margin-top: 5px;  
79 - max-height: 300px;  
80 -  
81 - .group-result {  
82 - color: $style_color;  
83 - border-bottom: 1px solid #EEE;  
84 - padding: 8px;  
85 - }  
86 - .active-result {  
87 - @include border-radius(0);  
88 -  
89 - &.highlighted {  
90 - background: $hover;  
91 - color: $style_color;  
92 - }  
93 - &.result-selected {  
94 - background: #EEE;  
95 - border-left: 4px solid #CCC;  
96 - }  
97 - }  
98 - }  
99 -  
100 - .chosen-search {  
101 - @include bg-gray-gradient;  
102 - input {  
103 - min-width: 165px;  
104 - border-color: #CCC;  
105 - }  
106 - } 12 + .chosen-results li.highlighted {
  13 + background: #29b;
107 } 14 }
108 -}  
109 15
110 -.chosen-container .chosen-single,  
111 -.chosen-container.chosen-with-drop .chosen-single {  
112 - @include bg-light-gray-gradient;  
113 -  
114 - div {  
115 - background: transparent;  
116 - border-left: none; 16 + .chosen-drop {
  17 + margin-top: 10px;
  18 + border: 1px solid #DDD !important;
  19 + @include border-radius(4px !important);
117 } 20 }
118 21
119 - span {  
120 - font-weight: normal; 22 + .chosen-search input {
  23 + border: 1px solid #CCC !important;
  24 + @include box-shadow(none !important);
121 } 25 }
122 } 26 }
123 27
124 /** Select2 styling **/ 28 /** Select2 styling **/
125 .select2-container .select2-choice { 29 .select2-container .select2-choice {
126 - background: #f1f1f1;  
127 - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, whitesmoke), to(#e1e1e1));  
128 - background-image: -webkit-linear-gradient(whitesmoke 6.6%, #e1e1e1);  
129 - background-image: -moz-linear-gradient(whitesmoke 6.6%, #e1e1e1);  
130 - background-image: -ms-linear-gradient(whitesmoke 6.6%, #e1e1e1);  
131 - background-image: -o-linear-gradient(whitesmoke 6.6%, #e1e1e1); 30 + @include bg-light-gray-gradient;
132 } 31 }
133 32
134 .select2-container .select2-choice div { 33 .select2-container .select2-choice div {
app/assets/stylesheets/themes/ui_color.scss
@@ -27,6 +27,12 @@ @@ -27,6 +27,12 @@
27 background: #435; 27 background: #435;
28 border-left: 1px solid #658; 28 border-left: 1px solid #658;
29 } 29 }
  30 + .nav > li > a {
  31 + color: #98B;
  32 + }
  33 + .search-input {
  34 + border-color: #98B;
  35 + }
30 } 36 }
31 } 37 }
32 } 38 }
app/assets/stylesheets/themes/ui_mars.scss
@@ -23,12 +23,17 @@ @@ -23,12 +23,17 @@
23 background-color: #373D47; 23 background-color: #373D47;
24 } 24 }
25 } 25 }
  26 + .separator {
  27 + background: #373D47;
  28 + border-left: 1px solid #575D67;
  29 + }
  30 + .nav > li > a {
  31 + color: #979DA7;
  32 + }
  33 + .search-input {
  34 + border-color: #979DA7;
  35 + }
26 } 36 }
27 } 37 }
28 -  
29 - .separator {  
30 - background: #31363E;  
31 - border-left: 1px solid #666;  
32 - }  
33 } 38 }
34 } 39 }
app/assets/stylesheets/themes/ui_modern.scss
@@ -27,6 +27,12 @@ @@ -27,6 +27,12 @@
27 background: #234; 27 background: #234;
28 border-left: 1px solid #456; 28 border-left: 1px solid #456;
29 } 29 }
  30 + .nav > li > a {
  31 + color: #89A;
  32 + }
  33 + .search-input {
  34 + border-color: #89A;
  35 + }
30 } 36 }
31 } 37 }
32 } 38 }
app/contexts/files/create_context.rb
@@ -15,29 +15,23 @@ module Files @@ -15,29 +15,23 @@ module Files
15 return error("You can only create files if you are on top of a branch") 15 return error("You can only create files if you are on top of a branch")
16 end 16 end
17 17
18 - file_name = params[:file_name] 18 + file_name = File.basename(path)
  19 + file_path = path
19 20
20 unless file_name =~ Gitlab::Regex.path_regex 21 unless file_name =~ Gitlab::Regex.path_regex
21 return error("Your changes could not be commited, because file name contains not allowed characters") 22 return error("Your changes could not be commited, because file name contains not allowed characters")
22 end 23 end
23 24
24 - file_path = if path.blank?  
25 - file_name  
26 - else  
27 - File.join(path, file_name)  
28 - end  
29 -  
30 blob = repository.blob_at(ref, file_path) 25 blob = repository.blob_at(ref, file_path)
31 26
32 if blob 27 if blob
33 return error("Your changes could not be commited, because file with such name exists") 28 return error("Your changes could not be commited, because file with such name exists")
34 end 29 end
35 30
36 - new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, path) 31 + new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
37 created_successfully = new_file_action.commit!( 32 created_successfully = new_file_action.commit!(
38 params[:content], 33 params[:content],
39 - params[:commit_message],  
40 - file_name, 34 + params[:commit_message]
41 ) 35 )
42 36
43 if created_successfully 37 if created_successfully
app/contexts/files/delete_context.rb 0 → 100644
@@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
  1 +module Files
  2 + class DeleteContext < BaseContext
  3 + def execute
  4 + allowed = if project.protected_branch?(ref)
  5 + can?(current_user, :push_code_to_protected_branches, project)
  6 + else
  7 + can?(current_user, :push_code, project)
  8 + end
  9 +
  10 + unless allowed
  11 + return error("You are not allowed to push into this branch")
  12 + end
  13 +
  14 + unless repository.branch_names.include?(ref)
  15 + return error("You can only create files if you are on top of a branch")
  16 + end
  17 +
  18 + blob = repository.blob_at(ref, path)
  19 +
  20 + unless blob
  21 + return error("You can only edit text files")
  22 + end
  23 +
  24 + delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path)
  25 +
  26 + deleted_successfully = delete_file_action.commit!(
  27 + nil,
  28 + params[:commit_message]
  29 + )
  30 +
  31 + if deleted_successfully
  32 + success
  33 + else
  34 + error("Your changes could not be commited, because the file has been changed")
  35 + end
  36 + end
  37 + end
  38 +end
app/contexts/files/update_context.rb
@@ -24,8 +24,7 @@ module Files @@ -24,8 +24,7 @@ module Files
24 new_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path) 24 new_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
25 created_successfully = new_file_action.commit!( 25 created_successfully = new_file_action.commit!(
26 params[:content], 26 params[:content],
27 - params[:commit_message],  
28 - params[:last_commit] 27 + params[:commit_message]
29 ) 28 )
30 29
31 if created_successfully 30 if created_successfully
app/contexts/issues/list_context.rb
@@ -29,8 +29,26 @@ module Issues @@ -29,8 +29,26 @@ module Issues
29 if params[:milestone_id].present? 29 if params[:milestone_id].present?
30 @issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id])) 30 @issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
31 end 31 end
  32 +
  33 + # Sort by :sort param
  34 + @issues = sort(@issues, params[:sort])
32 35
33 @issues 36 @issues
34 end 37 end
  38 +
  39 + private
  40 +
  41 + def sort(issues, condition)
  42 + case condition
  43 + when 'newest' then issues.except(:order).order('created_at DESC')
  44 + when 'oldest' then issues.except(:order).order('created_at ASC')
  45 + when 'recently_updated' then issues.except(:order).order('updated_at DESC')
  46 + when 'last_updated' then issues.except(:order).order('updated_at ASC')
  47 + when 'milestone_due_soon' then issues.except(:order).joins(:milestone).order("milestones.due_date ASC")
  48 + when 'milestone_due_later' then issues.except(:order).joins(:milestone).order("milestones.due_date DESC")
  49 + else issues
  50 + end
  51 + end
  52 +
35 end 53 end
36 end 54 end
app/contexts/projects/create_context.rb
@@ -8,6 +8,11 @@ module Projects @@ -8,6 +8,11 @@ module Projects
8 # get namespace id 8 # get namespace id
9 namespace_id = params.delete(:namespace_id) 9 namespace_id = params.delete(:namespace_id)
10 10
  11 + # check that user is allowed to set specified visibility_level
  12 + unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
  13 + params.delete(:visibility_level)
  14 + end
  15 +
11 # Load default feature settings 16 # Load default feature settings
12 default_features = Gitlab.config.gitlab.default_projects_features 17 default_features = Gitlab.config.gitlab.default_projects_features
13 18
@@ -17,7 +22,7 @@ module Projects @@ -17,7 +22,7 @@ module Projects
17 wall_enabled: default_features.wall, 22 wall_enabled: default_features.wall,
18 snippets_enabled: default_features.snippets, 23 snippets_enabled: default_features.snippets,
19 merge_requests_enabled: default_features.merge_requests, 24 merge_requests_enabled: default_features.merge_requests,
20 - public: default_features.public 25 + visibility_level: default_features.visibility_level
21 }.stringify_keys 26 }.stringify_keys
22 27
23 @project = Project.new(default_opts.merge(params)) 28 @project = Project.new(default_opts.merge(params))
@@ -47,8 +52,6 @@ module Projects @@ -47,8 +52,6 @@ module Projects
47 @project.creator = current_user 52 @project.creator = current_user
48 53
49 if @project.save 54 if @project.save
50 - @project.discover_default_branch  
51 -  
52 unless @project.group 55 unless @project.group
53 @project.users_projects.create( 56 @project.users_projects.create(
54 project_access: UsersProject::MASTER, 57 project_access: UsersProject::MASTER,
app/contexts/projects/update_context.rb
@@ -2,7 +2,23 @@ module Projects @@ -2,7 +2,23 @@ module Projects
2 class UpdateContext < BaseContext 2 class UpdateContext < BaseContext
3 def execute(role = :default) 3 def execute(role = :default)
4 params[:project].delete(:namespace_id) 4 params[:project].delete(:namespace_id)
5 - params[:project].delete(:public) unless can?(current_user, :change_public_mode, project) 5 + # check that user is allowed to set specified visibility_level
  6 + unless can?(current_user, :change_visibility_level, project) && Gitlab::VisibilityLevel.allowed_for?(current_user, params[:project][:visibility_level])
  7 + params[:project].delete(:visibility_level)
  8 + end
  9 +
  10 + new_branch = params[:project].delete(:default_branch)
  11 +
  12 + if project.repository.exists? && new_branch != project.default_branch
  13 + GitlabShellWorker.perform_async(
  14 + :update_repository_head,
  15 + project.path_with_namespace,
  16 + new_branch
  17 + )
  18 +
  19 + project.reload_default_branch
  20 + end
  21 +
6 project.update_attributes(params[:project], as: role) 22 project.update_attributes(params[:project], as: role)
7 end 23 end
8 end 24 end
app/contexts/search_context.rb
1 class SearchContext 1 class SearchContext
2 - attr_accessor :project_ids, :params 2 + attr_accessor :project_ids, :current_user, :params
3 3
4 - def initialize(project_ids, params)  
5 - @project_ids, @params = project_ids, params.dup 4 + def initialize(project_ids, user, params)
  5 + @project_ids, @current_user, @params = project_ids, user, params.dup
6 end 6 end
7 7
8 def execute 8 def execute
@@ -10,7 +10,8 @@ class SearchContext @@ -10,7 +10,8 @@ class SearchContext
10 query = Shellwords.shellescape(query) if query.present? 10 query = Shellwords.shellescape(query) if query.present?
11 11
12 return result unless query.present? 12 return result unless query.present?
13 - result[:projects] = Project.where("projects.id in (?) OR projects.public = true", project_ids).search(query).limit(20) 13 + visibility_levels = @current_user ? [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ] : [ Gitlab::VisibilityLevel::PUBLIC ]
  14 + result[:projects] = Project.where("projects.id in (?) OR projects.visibility_level in (?)", project_ids, visibility_levels).search(query).limit(20)
14 15
15 # Search inside single project 16 # Search inside single project
16 single_project_search(Project.where(id: project_ids), query) 17 single_project_search(Project.where(id: project_ids), query)
app/controllers/admin/broadcast_messages_controller.rb 0 → 100644
@@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
  1 +class Admin::BroadcastMessagesController < Admin::ApplicationController
  2 + before_filter :broadcast_messages
  3 +
  4 + def index
  5 + @broadcast_message = BroadcastMessage.new
  6 + end
  7 +
  8 + def create
  9 + @broadcast_message = BroadcastMessage.new(params[:broadcast_message])
  10 +
  11 + if @broadcast_message.save
  12 + redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully created.'
  13 + else
  14 + render :index
  15 + end
  16 + end
  17 +
  18 + def destroy
  19 + BroadcastMessage.find(params[:id]).destroy
  20 +
  21 + respond_to do |format|
  22 + format.html { redirect_to :back }
  23 + format.js { render nothing: true }
  24 + end
  25 + end
  26 +
  27 + protected
  28 +
  29 + def broadcast_messages
  30 + @broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page])
  31 + end
  32 +end
app/controllers/admin/dashboard_controller.rb
@@ -2,5 +2,6 @@ class Admin::DashboardController &lt; Admin::ApplicationController @@ -2,5 +2,6 @@ class Admin::DashboardController &lt; Admin::ApplicationController
2 def index 2 def index
3 @projects = Project.order("created_at DESC").limit(10) 3 @projects = Project.order("created_at DESC").limit(10)
4 @users = User.order("created_at DESC").limit(10) 4 @users = User.order("created_at DESC").limit(10)
  5 + @groups = Group.order("created_at DESC").limit(10)
5 end 6 end
6 end 7 end
app/controllers/admin/projects_controller.rb
1 class Admin::ProjectsController < Admin::ApplicationController 1 class Admin::ProjectsController < Admin::ApplicationController
2 - before_filter :project, only: [:edit, :show, :update, :destroy, :team_update] 2 + before_filter :project, only: [:show, :transfer]
  3 + before_filter :group, only: [:show, :transfer]
  4 + before_filter :repository, only: [:show, :transfer]
3 5
4 def index 6 def index
5 owner_id = params[:owner_id] 7 owner_id = params[:owner_id]
6 user = User.find_by_id(owner_id) 8 user = User.find_by_id(owner_id)
7 9
8 @projects = user ? user.owned_projects : Project.scoped 10 @projects = user ? user.owned_projects : Project.scoped
9 - @projects = @projects.where(public: true) if params[:public_only].present? 11 + @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
10 @projects = @projects.with_push if params[:with_push].present? 12 @projects = @projects.with_push if params[:with_push].present?
11 @projects = @projects.abandoned if params[:abandoned].present? 13 @projects = @projects.abandoned if params[:abandoned].present?
12 @projects = @projects.search(params[:name]) if params[:name].present? 14 @projects = @projects.search(params[:name]) if params[:name].present?
@@ -14,8 +16,16 @@ class Admin::ProjectsController &lt; Admin::ApplicationController @@ -14,8 +16,16 @@ class Admin::ProjectsController &lt; Admin::ApplicationController
14 end 16 end
15 17
16 def show 18 def show
17 - @repository = @project.repository  
18 - @group = @project.group 19 + end
  20 +
  21 + def transfer
  22 + result = ::Projects::TransferContext.new(@project, current_user, project: params).execute(:admin)
  23 +
  24 + if result
  25 + redirect_to [:admin, @project]
  26 + else
  27 + render :show
  28 + end
19 end 29 end
20 30
21 protected 31 protected
@@ -26,4 +36,12 @@ class Admin::ProjectsController &lt; Admin::ApplicationController @@ -26,4 +36,12 @@ class Admin::ProjectsController &lt; Admin::ApplicationController
26 @project = Project.find_with_namespace(id) 36 @project = Project.find_with_namespace(id)
27 @project || render_404 37 @project || render_404
28 end 38 end
  39 +
  40 + def group
  41 + @group ||= project.group
  42 + end
  43 +
  44 + def repository
  45 + @repository ||= project.repository
  46 + end
29 end 47 end
app/controllers/application_controller.rb
@@ -81,6 +81,9 @@ class ApplicationController &lt; ActionController::Base @@ -81,6 +81,9 @@ class ApplicationController &lt; ActionController::Base
81 81
82 if @project and can?(current_user, :read_project, @project) 82 if @project and can?(current_user, :read_project, @project)
83 @project 83 @project
  84 + elsif current_user.nil?
  85 + @project = nil
  86 + authenticate_user!
84 else 87 else
85 @project = nil 88 @project = nil
86 render_404 and return 89 render_404 and return
@@ -102,7 +105,7 @@ class ApplicationController &lt; ActionController::Base @@ -102,7 +105,7 @@ class ApplicationController &lt; ActionController::Base
102 end 105 end
103 106
104 def authorize_code_access! 107 def authorize_code_access!
105 - return access_denied! unless can?(current_user, :download_code, project) or project.public? 108 + return access_denied! unless can?(current_user, :download_code, project)
106 end 109 end
107 110
108 def authorize_push! 111 def authorize_push!
@@ -174,4 +177,26 @@ class ApplicationController &lt; ActionController::Base @@ -174,4 +177,26 @@ class ApplicationController &lt; ActionController::Base
174 filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? 177 filters = cookies['event_filter'].split(',') if cookies['event_filter'].present?
175 @event_filter ||= EventFilter.new(filters) 178 @event_filter ||= EventFilter.new(filters)
176 end 179 end
  180 +
  181 + # JSON for infinite scroll via Pager object
  182 + def pager_json(partial, count)
  183 + html = render_to_string(
  184 + partial,
  185 + layout: false,
  186 + formats: [:html]
  187 + )
  188 +
  189 + render json: {
  190 + html: html,
  191 + count: count
  192 + }
  193 + end
  194 +
  195 + def view_to_html_string(partial)
  196 + render_to_string(
  197 + partial,
  198 + layout: false,
  199 + formats: [:html]
  200 + )
  201 + end
177 end 202 end
app/controllers/dashboard_controller.rb
@@ -22,7 +22,7 @@ class DashboardController &lt; ApplicationController @@ -22,7 +22,7 @@ class DashboardController &lt; ApplicationController
22 22
23 respond_to do |format| 23 respond_to do |format|
24 format.html 24 format.html
25 - format.js 25 + format.json { pager_json("events/_events", @events.count) }
26 format.atom { render layout: false } 26 format.atom { render layout: false }
27 end 27 end
28 end 28 end
@@ -40,6 +40,7 @@ class DashboardController &lt; ApplicationController @@ -40,6 +40,7 @@ class DashboardController &lt; ApplicationController
40 end 40 end
41 41
42 @projects = @projects.where(namespace_id: Group.find_by_name(params[:group])) if params[:group].present? 42 @projects = @projects.where(namespace_id: Group.find_by_name(params[:group])) if params[:group].present?
  43 + @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
43 @projects = @projects.includes(:namespace).sorted_by_activity 44 @projects = @projects.includes(:namespace).sorted_by_activity
44 45
45 @labels = current_user.authorized_projects.tags_on(:labels) 46 @labels = current_user.authorized_projects.tags_on(:labels)
app/controllers/groups_controller.rb
@@ -38,7 +38,7 @@ class GroupsController &lt; ApplicationController @@ -38,7 +38,7 @@ class GroupsController &lt; ApplicationController
38 38
39 respond_to do |format| 39 respond_to do |format|
40 format.html 40 format.html
41 - format.js 41 + format.json { pager_json("events/_events", @events.count) }
42 format.atom { render layout: false } 42 format.atom { render layout: false }
43 end 43 end
44 end 44 end
app/controllers/profiles/avatars_controller.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +class Profiles::AvatarsController < ApplicationController
  2 + layout "profile"
  3 +
  4 + def destroy
  5 + @user = current_user
  6 + @user.remove_avatar!
  7 +
  8 + @user.save
  9 + redirect_to profile_path
  10 + end
  11 +end
app/controllers/profiles_controller.rb
@@ -13,6 +13,8 @@ class ProfilesController &lt; ApplicationController @@ -13,6 +13,8 @@ class ProfilesController &lt; ApplicationController
13 end 13 end
14 14
15 def update 15 def update
  16 + params[:user].delete(:email) if @user.ldap_user?
  17 +
16 if @user.update_attributes(params[:user]) 18 if @user.update_attributes(params[:user])
17 flash[:notice] = "Profile was successfully updated" 19 flash[:notice] = "Profile was successfully updated"
18 else 20 else
app/controllers/projects/application_controller.rb
@@ -10,7 +10,7 @@ class Projects::ApplicationController &lt; ApplicationController @@ -10,7 +10,7 @@ class Projects::ApplicationController &lt; ApplicationController
10 id = params[:project_id] || params[:id] 10 id = params[:project_id] || params[:id]
11 @project = Project.find_with_namespace(id) 11 @project = Project.find_with_namespace(id)
12 12
13 - return if @project && @project.public 13 + return if @project && @project.public?
14 end 14 end
15 15
16 super 16 super
app/controllers/projects/blob_controller.rb
@@ -7,9 +7,30 @@ class Projects::BlobController &lt; Projects::ApplicationController @@ -7,9 +7,30 @@ class Projects::BlobController &lt; Projects::ApplicationController
7 before_filter :authorize_code_access! 7 before_filter :authorize_code_access!
8 before_filter :require_non_empty_project 8 before_filter :require_non_empty_project
9 9
  10 + before_filter :blob
  11 +
10 def show 12 def show
11 - @blob = @repository.blob_at(@commit.id, @path) 13 + end
  14 +
  15 + def destroy
  16 + result = Files::DeleteContext.new(@project, current_user, params, @ref, @path).execute
  17 +
  18 + if result[:status] == :success
  19 + flash[:notice] = "Your changes have been successfully commited"
  20 + redirect_to project_tree_path(@project, @ref)
  21 + else
  22 + flash[:alert] = result[:error]
  23 + render :show
  24 + end
  25 + end
  26 +
  27 + private
  28 +
  29 + def blob
  30 + @blob ||= @repository.blob_at(@commit.id, @path)
  31 +
  32 + return not_found! unless @blob
12 33
13 - not_found! unless @blob 34 + @blob
14 end 35 end
15 end 36 end
app/controllers/projects/commits_controller.rb
@@ -16,7 +16,7 @@ class Projects::CommitsController &lt; Projects::ApplicationController @@ -16,7 +16,7 @@ class Projects::CommitsController &lt; Projects::ApplicationController
16 16
17 respond_to do |format| 17 respond_to do |format|
18 format.html # index.html.erb 18 format.html # index.html.erb
19 - format.js 19 + format.json { pager_json("projects/commits/_commits", @commits.size) }
20 format.atom { render layout: false } 20 format.atom { render layout: false }
21 end 21 end
22 end 22 end
app/controllers/projects/issues_controller.rb
@@ -11,7 +11,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController @@ -11,7 +11,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController
11 # Allow modify issue 11 # Allow modify issue
12 before_filter :authorize_modify_issue!, only: [:edit, :update] 12 before_filter :authorize_modify_issue!, only: [:edit, :update]
13 13
14 - respond_to :js, :html 14 + respond_to :html
15 15
16 def index 16 def index
17 terms = params['issue_search'] 17 terms = params['issue_search']
@@ -23,11 +23,18 @@ class Projects::IssuesController &lt; Projects::ApplicationController @@ -23,11 +23,18 @@ class Projects::IssuesController &lt; Projects::ApplicationController
23 assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] 23 assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
24 @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? 24 @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
25 @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? 25 @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
  26 + sort_param = params[:sort] || 'newest'
  27 + @sort = sort_param.humanize unless sort_param.empty?
  28 +
26 29
27 respond_to do |format| 30 respond_to do |format|
28 - format.html # index.html.erb  
29 - format.js 31 + format.html
30 format.atom { render layout: false } 32 format.atom { render layout: false }
  33 + format.json do
  34 + render json: {
  35 + html: view_to_html_string("projects/issues/_issues")
  36 + }
  37 + end
31 end 38 end
32 end 39 end
33 40
@@ -45,10 +52,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController @@ -45,10 +52,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController
45 @target_type = :issue 52 @target_type = :issue
46 @target_id = @issue.id 53 @target_id = @issue.id
47 54
48 - respond_to do |format|  
49 - format.html  
50 - format.js  
51 - end 55 + respond_with(@issue)
52 end 56 end
53 57
54 def create 58 def create
app/controllers/projects/merge_requests_controller.rb
@@ -2,8 +2,8 @@ require &#39;gitlab/satellite/satellite&#39; @@ -2,8 +2,8 @@ require &#39;gitlab/satellite/satellite&#39;
2 2
3 class Projects::MergeRequestsController < Projects::ApplicationController 3 class Projects::MergeRequestsController < Projects::ApplicationController
4 before_filter :module_enabled 4 before_filter :module_enabled
5 - before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status]  
6 - before_filter :closes_issues, only: [:edit, :update, :show, :commits, :diffs] 5 + before_filter :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status]
  6 + before_filter :closes_issues, only: [:edit, :update, :show, :diffs]
7 before_filter :validates_merge_request, only: [:show, :diffs] 7 before_filter :validates_merge_request, only: [:show, :diffs]
8 before_filter :define_show_vars, only: [:show, :diffs] 8 before_filter :define_show_vars, only: [:show, :diffs]
9 9
@@ -26,8 +26,6 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController @@ -26,8 +26,6 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController
26 def show 26 def show
27 respond_to do |format| 27 respond_to do |format|
28 format.html 28 format.html
29 - format.js  
30 -  
31 format.diff { render text: @merge_request.to_diff(current_user) } 29 format.diff { render text: @merge_request.to_diff(current_user) }
32 format.patch { render text: @merge_request.to_patch(current_user) } 30 format.patch { render text: @merge_request.to_patch(current_user) }
33 end 31 end
@@ -44,6 +42,11 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController @@ -44,6 +42,11 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController
44 diff_line_count = Commit::diff_line_count(@merge_request.diffs) 42 diff_line_count = Commit::diff_line_count(@merge_request.diffs)
45 @suppress_diff = Commit::diff_suppress?(@merge_request.diffs, diff_line_count) && !params[:force_show_diff] 43 @suppress_diff = Commit::diff_suppress?(@merge_request.diffs, diff_line_count) && !params[:force_show_diff]
46 @force_suppress_diff = Commit::diff_force_suppress?(@merge_request.diffs, diff_line_count) 44 @force_suppress_diff = Commit::diff_force_suppress?(@merge_request.diffs, diff_line_count)
  45 +
  46 + respond_to do |format|
  47 + format.html
  48 + format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
  49 + end
47 end 50 end
48 51
49 def new 52 def new
app/controllers/projects/milestones_controller.rb
@@ -34,11 +34,6 @@ class Projects::MilestonesController &lt; Projects::ApplicationController @@ -34,11 +34,6 @@ class Projects::MilestonesController &lt; Projects::ApplicationController
34 @issues = @milestone.issues 34 @issues = @milestone.issues
35 @users = @milestone.participants.uniq 35 @users = @milestone.participants.uniq
36 @merge_requests = @milestone.merge_requests 36 @merge_requests = @milestone.merge_requests
37 -  
38 - respond_to do |format|  
39 - format.html  
40 - format.js  
41 - end  
42 end 37 end
43 38
44 def create 39 def create
app/controllers/projects/new_tree_controller.rb
@@ -5,11 +5,12 @@ class Projects::NewTreeController &lt; Projects::BaseTreeController @@ -5,11 +5,12 @@ class Projects::NewTreeController &lt; Projects::BaseTreeController
5 end 5 end
6 6
7 def update 7 def update
8 - result = Files::CreateContext.new(@project, current_user, params, @ref, @path).execute 8 + file_path = File.join(@path, File.basename(params[:file_name]))
  9 + result = Files::CreateContext.new(@project, current_user, params, @ref, file_path).execute
9 10
10 if result[:status] == :success 11 if result[:status] == :success
11 flash[:notice] = "Your changes have been successfully commited" 12 flash[:notice] = "Your changes have been successfully commited"
12 - redirect_to project_blob_path(@project, File.join(@id, params[:file_name])) 13 + redirect_to project_blob_path(@project, File.join(@ref, file_path))
13 else 14 else
14 flash[:alert] = result[:error] 15 flash[:alert] = result[:error]
15 render :show 16 render :show
app/controllers/projects/notes_controller.rb
@@ -14,7 +14,14 @@ class Projects::NotesController &lt; Projects::ApplicationController @@ -14,7 +14,14 @@ class Projects::NotesController &lt; Projects::ApplicationController
14 @discussions = discussions_from_notes 14 @discussions = discussions_from_notes
15 end 15 end
16 16
17 - respond_with(@notes) 17 + respond_to do |format|
  18 + format.html { redirect_to :back }
  19 + format.json do
  20 + render json: {
  21 + html: view_to_html_string("projects/notes/_notes")
  22 + }
  23 + end
  24 + end
18 end 25 end
19 26
20 def create 27 def create
app/controllers/projects_controller.rb
@@ -55,17 +55,13 @@ class ProjectsController &lt; ApplicationController @@ -55,17 +55,13 @@ class ProjectsController &lt; ApplicationController
55 end 55 end
56 56
57 def show 57 def show
58 - return authenticate_user! unless @project.public || current_user 58 + return authenticate_user! unless @project.public? || current_user
59 59
60 limit = (params[:limit] || 20).to_i 60 limit = (params[:limit] || 20).to_i
61 @events = @project.events.recent 61 @events = @project.events.recent
62 @events = event_filter.apply_filter(@events) 62 @events = event_filter.apply_filter(@events)
63 @events = @events.limit(limit).offset(params[:offset] || 0) 63 @events = @events.limit(limit).offset(params[:offset] || 0)
64 64
65 - # Ensure project default branch is set if it possible  
66 - # Normally it defined on push or during creation  
67 - @project.discover_default_branch  
68 -  
69 respond_to do |format| 65 respond_to do |format|
70 format.html do 66 format.html do
71 if @project.empty_repo? 67 if @project.empty_repo?
@@ -77,7 +73,7 @@ class ProjectsController &lt; ApplicationController @@ -77,7 +73,7 @@ class ProjectsController &lt; ApplicationController
77 render :show, layout: user_layout 73 render :show, layout: user_layout
78 end 74 end
79 end 75 end
80 - format.js 76 + format.json { pager_json("events/_events", @events.count) }
81 end 77 end
82 end 78 end
83 79
app/controllers/public/projects_controller.rb
@@ -6,7 +6,7 @@ class Public::ProjectsController &lt; ApplicationController @@ -6,7 +6,7 @@ class Public::ProjectsController &lt; ApplicationController
6 layout 'public' 6 layout 'public'
7 7
8 def index 8 def index
9 - @projects = Project.public_only 9 + @projects = Project.public_or_internal_only(current_user)
10 @projects = @projects.search(params[:search]) if params[:search].present? 10 @projects = @projects.search(params[:search]) if params[:search].present?
11 @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) 11 @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
12 end 12 end
app/controllers/search_controller.rb
@@ -14,7 +14,7 @@ class SearchController &lt; ApplicationController @@ -14,7 +14,7 @@ class SearchController &lt; ApplicationController
14 project_ids.select! { |id| id == project_id.to_i} 14 project_ids.select! { |id| id == project_id.to_i}
15 end 15 end
16 16
17 - result = SearchContext.new(project_ids, params).execute 17 + result = SearchContext.new(project_ids, current_user, params).execute
18 18
19 @projects = result[:projects] 19 @projects = result[:projects]
20 @merge_requests = result[:merge_requests] 20 @merge_requests = result[:merge_requests]
app/helpers/application_helper.rb
@@ -84,8 +84,8 @@ module ApplicationHelper @@ -84,8 +84,8 @@ module ApplicationHelper
84 repository = @project.repository 84 repository = @project.repository
85 85
86 options = [ 86 options = [
87 - ["Branch", repository.branch_names ],  
88 - [ "Tag", repository.tag_names ] 87 + ["Branches", repository.branch_names],
  88 + ["Tags", repository.tag_names]
89 ] 89 ]
90 90
91 # If reference is commit id - 91 # If reference is commit id -
@@ -126,6 +126,9 @@ module ApplicationHelper @@ -126,6 +126,9 @@ module ApplicationHelper
126 # Skip if user already created appropriate MR 126 # Skip if user already created appropriate MR
127 return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? 127 return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
128 128
  129 + # Skip if user removed branch right after that
  130 + return false unless project.repository.branch_names.include?(event.branch_name)
  131 +
129 true 132 true
130 end 133 end
131 134
@@ -184,14 +187,6 @@ module ApplicationHelper @@ -184,14 +187,6 @@ module ApplicationHelper
184 Gitlab.config.extra 187 Gitlab.config.extra
185 end 188 end
186 189
187 - def public_icon  
188 - content_tag :i, nil, class: 'icon-globe cblue'  
189 - end  
190 -  
191 - def private_icon  
192 - content_tag :i, nil, class: 'icon-lock cgreen'  
193 - end  
194 -  
195 def search_placeholder 190 def search_placeholder
196 if @project && @project.persisted? 191 if @project && @project.persisted?
197 "Search in this project" 192 "Search in this project"
@@ -208,4 +203,16 @@ module ApplicationHelper @@ -208,4 +203,16 @@ module ApplicationHelper
208 line += "..." if lines.size > 1 203 line += "..." if lines.size > 1
209 line 204 line
210 end 205 end
  206 +
  207 + def broadcast_message
  208 + BroadcastMessage.current
  209 + end
  210 +
  211 + def highlight_js(&block)
  212 + string = capture(&block)
  213 +
  214 + content_tag :div, class: user_color_scheme_class do
  215 + Pygments::Lexer[:js].highlight(string).html_safe
  216 + end
  217 + end
211 end 218 end
app/helpers/commits_helper.rb
@@ -105,6 +105,10 @@ module CommitsHelper @@ -105,6 +105,10 @@ module CommitsHelper
105 branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe 105 branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
106 end 106 end
107 107
  108 + def get_old_file(project, commit, diff)
  109 + project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
  110 + end
  111 +
108 protected 112 protected
109 113
110 # Private: Returns a link to a person. If the person has a matching user and 114 # Private: Returns a link to a person. If the person has a matching user and
@@ -125,7 +129,9 @@ module CommitsHelper @@ -125,7 +129,9 @@ module CommitsHelper
125 source_name 129 source_name
126 end 130 end
127 131
128 - user = User.where('name like ? or email like ?', source_name, source_email).first 132 + # Prefer email match over name match
  133 + user = User.where(email: source_email).first
  134 + user ||= User.where(name: source_name).first
129 135
130 options = { 136 options = {
131 class: "commit-#{options[:source]}-link has_tooltip", 137 class: "commit-#{options[:source]}-link has_tooltip",
app/helpers/compare_helper.rb
1 module CompareHelper 1 module CompareHelper
2 def compare_to_mr_button? 2 def compare_to_mr_button?
3 - params[:from].present? && params[:to].present? && 3 + @project.merge_requests_enabled &&
  4 + params[:from].present? &&
  5 + params[:to].present? &&
4 @repository.branch_names.include?(params[:from]) && 6 @repository.branch_names.include?(params[:from]) &&
5 @repository.branch_names.include?(params[:to]) && 7 @repository.branch_names.include?(params[:to]) &&
6 params[:from] != params[:to] && 8 params[:from] != params[:to] &&
app/helpers/events_helper.rb
@@ -102,15 +102,11 @@ module EventsHelper @@ -102,15 +102,11 @@ module EventsHelper
102 end 102 end
103 elsif event.note_project_snippet? 103 elsif event.note_project_snippet?
104 link_to(project_snippet_path(event.project, event.note_target)) do 104 link_to(project_snippet_path(event.project, event.note_target)) do
105 - content_tag :strong do  
106 - "#{event.note_target_type} ##{truncate event.note_target_id}"  
107 - end 105 + "#{event.note_target_type} ##{truncate event.note_target_id}"
108 end 106 end
109 else 107 else
110 link_to event_note_target_path(event) do 108 link_to event_note_target_path(event) do
111 - content_tag :strong do  
112 - "#{event.note_target_type} ##{truncate event.note_target_iid}"  
113 - end 109 + "#{event.note_target_type} ##{truncate event.note_target_iid}"
114 end 110 end
115 end 111 end
116 elsif event.wall_note? 112 elsif event.wall_note?
app/helpers/gitlab_markdown_helper.rb
@@ -64,7 +64,9 @@ module GitlabMarkdownHelper @@ -64,7 +64,9 @@ module GitlabMarkdownHelper
64 # ref - name of the branch or reference, eg. stable 64 # ref - name of the branch or reference, eg. stable
65 # 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 65 # 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
66 # wiki - whether the markdown is from wiki or not 66 # wiki - whether the markdown is from wiki or not
67 - def create_relative_links(text, project_path_with_namespace, ref, requested_path, wiki = false) 67 + def create_relative_links(text, project, ref, requested_path, wiki = false)
  68 + @path_to_satellite = project.satellite.path
  69 + project_path_with_namespace = project.path_with_namespace
68 paths = extract_paths(text) 70 paths = extract_paths(text)
69 paths.each do |file_path| 71 paths.each do |file_path|
70 new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref) 72 new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref)
@@ -145,13 +147,18 @@ module GitlabMarkdownHelper @@ -145,13 +147,18 @@ module GitlabMarkdownHelper
145 147
146 def file_exists?(path) 148 def file_exists?(path)
147 return false if path.nil? || path.empty? 149 return false if path.nil? || path.empty?
148 - File.exists?(Rails.root.join(path)) 150 + File.exists?(path_on_fs(path))
149 end 151 end
150 152
151 # Check if the path is pointing to a directory(tree) or a file(blob) 153 # Check if the path is pointing to a directory(tree) or a file(blob)
152 # eg. doc/api is directory and doc/README.md is file 154 # eg. doc/api is directory and doc/README.md is file
153 def local_path(path) 155 def local_path(path)
154 - File.directory?(Rails.root.join(path)) ? "tree" : "blob" 156 + File.directory?(path_on_fs(path)) ? "tree" : "blob"
  157 + end
  158 +
  159 + # Path to the file in the satellites repository on the filesystem
  160 + def path_on_fs(path)
  161 + [@path_to_satellite, path].join("/")
155 end 162 end
156 163
157 # We will assume that if no ref exists we can point to master 164 # We will assume that if no ref exists we can point to master
app/helpers/groups_helper.rb
@@ -2,4 +2,23 @@ module GroupsHelper @@ -2,4 +2,23 @@ module GroupsHelper
2 def remove_user_from_group_message(group, user) 2 def remove_user_from_group_message(group, user)
3 "You are going to remove #{user.name} from #{group.name} Group. Are you sure?" 3 "You are going to remove #{user.name} from #{group.name} Group. Are you sure?"
4 end 4 end
  5 +
  6 + def group_head_title
  7 + title = @group.name
  8 +
  9 + title = if current_action?(:issues)
  10 + "Issues - " + title
  11 + elsif current_action?(:merge_requests)
  12 + "Merge requests - " + title
  13 + elsif current_action?(:members)
  14 + "Members - " + title
  15 + elsif current_action?(:edit)
  16 + "Settings - " + title
  17 + else
  18 + title
  19 + end
  20 +
  21 + title
  22 +
  23 + end
5 end 24 end
app/helpers/icons_helper.rb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +module IconsHelper
  2 + def boolean_to_icon(value)
  3 + if value.to_s == "true"
  4 + content_tag :i, nil, class: 'icon-ok cgreen'
  5 + else
  6 + content_tag :i, nil, class: 'icon-off clgray'
  7 + end
  8 + end
  9 +
  10 + def public_icon
  11 + content_tag :i, nil, class: 'icon-globe'
  12 + end
  13 +
  14 + def internal_icon
  15 + content_tag :i, nil, class: 'icon-shield'
  16 + end
  17 +
  18 + def private_icon
  19 + content_tag :i, nil, class: 'icon-lock'
  20 + end
  21 +end
app/helpers/issues_helper.rb
@@ -68,4 +68,12 @@ module IssuesHelper @@ -68,4 +68,12 @@ module IssuesHelper
68 false 68 false
69 end 69 end
70 end 70 end
  71 +
  72 + def bulk_update_milestone_options
  73 + options_for_select(["None (backlog)", nil]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id])
  74 + end
  75 +
  76 + def bulk_update_assignee_options
  77 + options_for_select(["None (unassigned)", nil]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id])
  78 + end
71 end 79 end
app/helpers/namespaces_helper.rb
@@ -16,4 +16,13 @@ module NamespacesHelper @@ -16,4 +16,13 @@ module NamespacesHelper
16 16
17 grouped_options_for_select(options, selected) 17 grouped_options_for_select(options, selected)
18 end 18 end
  19 +
  20 + def namespace_select_tag(id, opts = {})
  21 + css_class = "ajax-namespace-select "
  22 + css_class << "multiselect " if opts[:multiple]
  23 + css_class << (opts[:class] || '')
  24 + value = opts[:selected] || ''
  25 +
  26 + hidden_field_tag(id, value, class: css_class)
  27 + end
19 end 28 end
app/helpers/projects_helper.rb
@@ -70,6 +70,8 @@ module ProjectsHelper @@ -70,6 +70,8 @@ module ProjectsHelper
70 scope: params[:scope], 70 scope: params[:scope],
71 label_name: params[:label_name], 71 label_name: params[:label_name],
72 milestone_id: params[:milestone_id], 72 milestone_id: params[:milestone_id],
  73 + assignee_id: params[:assignee_id],
  74 + sort: params[:sort],
73 } 75 }
74 76
75 options = exist_opts.merge(options) 77 options = exist_opts.merge(options)
@@ -135,12 +137,46 @@ module ProjectsHelper @@ -135,12 +137,46 @@ module ProjectsHelper
135 end 137 end
136 end 138 end
137 139
138 - def repository_size  
139 - "#{@project.repository.size} MB" 140 + def repository_size(project = nil)
  141 + "#{(project || @project).repository.size} MB"
140 rescue 142 rescue
141 # In order to prevent 500 error 143 # In order to prevent 500 error
142 # when application cannot allocate memory 144 # when application cannot allocate memory
143 # to calculate repo size - just show 'Unknown' 145 # to calculate repo size - just show 'Unknown'
144 'unknown' 146 'unknown'
145 end 147 end
  148 +
  149 + def project_head_title
  150 + title = @project.name_with_namespace
  151 +
  152 + title = if current_controller?(:tree)
  153 + "#{@project.path}\/#{@path} at #{@ref} - " + title
  154 + elsif current_controller?(:issues)
  155 + if current_action?(:show)
  156 + "Issue ##{@issue.iid} - " + title
  157 + else
  158 + "Issues - " + title
  159 + end
  160 + elsif current_controller?(:blob)
  161 + "#{@project.path}\/#{@blob.path} at #{@ref} - " + title
  162 + elsif current_controller?(:commits)
  163 + "Commits at #{@ref} - " + title
  164 + elsif current_controller?(:merge_requests)
  165 + if current_action?(:show)
  166 + "Merge request ##{@merge_request.iid} - " + title
  167 + else
  168 + "Merge requests - " + title
  169 + end
  170 + elsif current_controller?(:wikis)
  171 + "Wiki - " + title
  172 + elsif current_controller?(:network)
  173 + "Network graph - " + title
  174 + elsif current_controller?(:graphs)
  175 + "Graphs - " + title
  176 + else
  177 + title
  178 + end
  179 +
  180 + title
  181 + end
146 end 182 end
app/helpers/search_helper.rb
1 module SearchHelper 1 module SearchHelper
2 def search_autocomplete_source 2 def search_autocomplete_source
3 return unless current_user 3 return unless current_user
4 -  
5 [ 4 [
6 groups_autocomplete, 5 groups_autocomplete,
7 projects_autocomplete, 6 projects_autocomplete,
  7 + public_projects_autocomplete,
8 default_autocomplete, 8 default_autocomplete,
9 project_autocomplete, 9 project_autocomplete,
10 help_autocomplete 10 help_autocomplete
@@ -75,4 +75,11 @@ module SearchHelper @@ -75,4 +75,11 @@ module SearchHelper
75 { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } 75 { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
76 end 76 end
77 end 77 end
  78 +
  79 + # Autocomplete results for the current user's projects
  80 + def public_projects_autocomplete
  81 + Project.public_or_internal_only(current_user).map do |p|
  82 + { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
  83 + end
  84 + end
78 end 85 end
app/helpers/visibility_level_helper.rb 0 → 100644
@@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
  1 +module VisibilityLevelHelper
  2 + def visibility_level_color(level)
  3 + case level
  4 + when Gitlab::VisibilityLevel::PRIVATE
  5 + 'cgreen'
  6 + when Gitlab::VisibilityLevel::INTERNAL
  7 + 'camber'
  8 + when Gitlab::VisibilityLevel::PUBLIC
  9 + 'cblue'
  10 + end
  11 + end
  12 +
  13 + def visibility_level_description(level)
  14 + capture_haml do
  15 + haml_tag :span do
  16 + case level
  17 + when Gitlab::VisibilityLevel::PRIVATE
  18 + haml_concat "Project access must be granted explicitly for each user."
  19 + when Gitlab::VisibilityLevel::INTERNAL
  20 + haml_concat "The project can be cloned by"
  21 + haml_concat "any logged in user."
  22 + when Gitlab::VisibilityLevel::PUBLIC
  23 + haml_concat "The project can be cloned"
  24 + haml_concat "without any"
  25 + haml_concat "authentication."
  26 + end
  27 + end
  28 + end
  29 + end
  30 +
  31 + def visibility_level_icon(level)
  32 + case level
  33 + when Gitlab::VisibilityLevel::PRIVATE
  34 + private_icon
  35 + when Gitlab::VisibilityLevel::INTERNAL
  36 + internal_icon
  37 + when Gitlab::VisibilityLevel::PUBLIC
  38 + public_icon
  39 + end
  40 + end
  41 +
  42 + def visibility_level_label(level)
  43 + Project.visibility_levels.key(level)
  44 + end
  45 +
  46 + def restricted_visibility_levels
  47 + current_user.is_admin? ? [] : gitlab_config.restricted_visibility_levels
  48 + end
  49 +end
app/mailers/emails/groups.rb
@@ -5,7 +5,7 @@ module Emails @@ -5,7 +5,7 @@ module Emails
5 @group = @membership.group 5 @group = @membership.group
6 6
7 mail(to: @membership.user.email, 7 mail(to: @membership.user.email,
8 - subject: subject("access to group was granted")) 8 + subject: subject("Access to group was granted"))
9 end 9 end
10 end 10 end
11 end 11 end
app/mailers/emails/issues.rb
@@ -3,14 +3,14 @@ module Emails @@ -3,14 +3,14 @@ module Emails
3 def new_issue_email(recipient_id, issue_id) 3 def new_issue_email(recipient_id, issue_id)
4 @issue = Issue.find(issue_id) 4 @issue = Issue.find(issue_id)
5 @project = @issue.project 5 @project = @issue.project
6 - mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.iid}", @issue.title)) 6 + mail(to: recipient(recipient_id), subject: subject("New issue ##{@issue.iid}", @issue.title))
7 end 7 end
8 8
9 def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) 9 def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
10 @issue = Issue.find(issue_id) 10 @issue = Issue.find(issue_id)
11 @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id 11 @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
12 @project = @issue.project 12 @project = @issue.project
13 - mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.iid}", @issue.title)) 13 + mail(to: recipient(recipient_id), subject: subject("Changed issue ##{@issue.iid}", @issue.title))
14 end 14 end
15 15
16 def closed_issue_email(recipient_id, issue_id, updated_by_user_id) 16 def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
@@ -27,7 +27,7 @@ module Emails @@ -27,7 +27,7 @@ module Emails
27 @project = @issue.project 27 @project = @issue.project
28 @updated_by = User.find updated_by_user_id 28 @updated_by = User.find updated_by_user_id
29 mail(to: recipient(recipient_id), 29 mail(to: recipient(recipient_id),
30 - subject: subject("changed issue ##{@issue.iid}", @issue.title)) 30 + subject: subject("Changed issue ##{@issue.iid}", @issue.title))
31 end 31 end
32 end 32 end
33 end 33 end
app/mailers/emails/merge_requests.rb
@@ -2,24 +2,24 @@ module Emails @@ -2,24 +2,24 @@ module Emails
2 module MergeRequests 2 module MergeRequests
3 def new_merge_request_email(recipient_id, merge_request_id) 3 def new_merge_request_email(recipient_id, merge_request_id)
4 @merge_request = MergeRequest.find(merge_request_id) 4 @merge_request = MergeRequest.find(merge_request_id)
5 - mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.iid}", @merge_request.title)) 5 + mail(to: recipient(recipient_id), subject: subject("New merge request ##{@merge_request.iid}", @merge_request.title))
6 end 6 end
7 7
8 def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) 8 def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
9 @merge_request = MergeRequest.find(merge_request_id) 9 @merge_request = MergeRequest.find(merge_request_id)
10 @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id 10 @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
11 - mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.iid}", @merge_request.title)) 11 + mail(to: recipient(recipient_id), subject: subject("Changed merge request ##{@merge_request.iid}", @merge_request.title))
12 end 12 end
13 13
14 def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) 14 def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
15 @merge_request = MergeRequest.find(merge_request_id) 15 @merge_request = MergeRequest.find(merge_request_id)
16 @updated_by = User.find updated_by_user_id 16 @updated_by = User.find updated_by_user_id
17 - mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.iid}", @merge_request.title)) 17 + mail(to: recipient(recipient_id), subject: subject("Closed merge request ##{@merge_request.iid}", @merge_request.title))
18 end 18 end
19 19
20 def merged_merge_request_email(recipient_id, merge_request_id) 20 def merged_merge_request_email(recipient_id, merge_request_id)
21 @merge_request = MergeRequest.find(merge_request_id) 21 @merge_request = MergeRequest.find(merge_request_id)
22 - mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.iid}", @merge_request.title)) 22 + mail(to: recipient(recipient_id), subject: subject("Accepted merge request ##{@merge_request.iid}", @merge_request.title))
23 end 23 end
24 end 24 end
25 25
app/mailers/emails/notes.rb
@@ -4,27 +4,27 @@ module Emails @@ -4,27 +4,27 @@ module Emails
4 @note = Note.find(note_id) 4 @note = Note.find(note_id)
5 @commit = @note.noteable 5 @commit = @note.noteable
6 @project = @note.project 6 @project = @note.project
7 - mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) 7 + mail(to: recipient(recipient_id), subject: subject("Note for commit #{@commit.short_id}", @commit.title))
8 end 8 end
9 9
10 def note_issue_email(recipient_id, note_id) 10 def note_issue_email(recipient_id, note_id)
11 @note = Note.find(note_id) 11 @note = Note.find(note_id)
12 @issue = @note.noteable 12 @issue = @note.noteable
13 @project = @note.project 13 @project = @note.project
14 - mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.iid}")) 14 + mail(to: recipient(recipient_id), subject: subject("Note for issue ##{@issue.iid}"))
15 end 15 end
16 16
17 def note_merge_request_email(recipient_id, note_id) 17 def note_merge_request_email(recipient_id, note_id)
18 @note = Note.find(note_id) 18 @note = Note.find(note_id)
19 @merge_request = @note.noteable 19 @merge_request = @note.noteable
20 @project = @note.project 20 @project = @note.project
21 - mail(to: recipient(recipient_id), subject: subject("note for merge request ##{@merge_request.iid}")) 21 + mail(to: recipient(recipient_id), subject: subject("Note for merge request ##{@merge_request.iid}"))
22 end 22 end
23 23
24 def note_wall_email(recipient_id, note_id) 24 def note_wall_email(recipient_id, note_id)
25 @note = Note.find(note_id) 25 @note = Note.find(note_id)
26 @project = @note.project 26 @project = @note.project
27 - mail(to: recipient(recipient_id), subject: subject("note on wall")) 27 + mail(to: recipient(recipient_id), subject: subject("Note on wall"))
28 end 28 end
29 end 29 end
30 end 30 end
app/mailers/emails/projects.rb
@@ -4,14 +4,14 @@ module Emails @@ -4,14 +4,14 @@ module Emails
4 @users_project = UsersProject.find user_project_id 4 @users_project = UsersProject.find user_project_id
5 @project = @users_project.project 5 @project = @users_project.project
6 mail(to: @users_project.user.email, 6 mail(to: @users_project.user.email,
7 - subject: subject("access to project was granted")) 7 + subject: subject("Access to project was granted"))
8 end 8 end
9 9
10 def project_was_moved_email(project_id, user_id) 10 def project_was_moved_email(project_id, user_id)
11 @user = User.find user_id 11 @user = User.find user_id
12 @project = Project.find project_id 12 @project = Project.find project_id
13 mail(to: @user.email, 13 mail(to: @user.email,
14 - subject: subject("project was moved")) 14 + subject: subject("Project was moved"))
15 end 15 end
16 end 16 end
17 end 17 end
app/models/ability.rb
@@ -29,7 +29,7 @@ class Ability @@ -29,7 +29,7 @@ class Ability
29 nil 29 nil
30 end 30 end
31 31
32 - if project && project.public 32 + if project && project.public?
33 [ 33 [
34 :read_project, 34 :read_project,
35 :read_wiki, 35 :read_wiki,
@@ -71,7 +71,7 @@ class Ability @@ -71,7 +71,7 @@ class Ability
71 rules << project_guest_rules 71 rules << project_guest_rules
72 end 72 end
73 73
74 - if project.public? 74 + if project.public? || project.internal?
75 rules << public_project_rules 75 rules << public_project_rules
76 end 76 end
77 77
@@ -89,7 +89,7 @@ class Ability @@ -89,7 +89,7 @@ class Ability
89 def public_project_rules 89 def public_project_rules
90 project_guest_rules + [ 90 project_guest_rules + [
91 :download_code, 91 :download_code,
92 - :fork_project, 92 + :fork_project
93 ] 93 ]
94 end 94 end
95 95
@@ -145,7 +145,7 @@ class Ability @@ -145,7 +145,7 @@ class Ability
145 def project_admin_rules 145 def project_admin_rules
146 project_master_rules + [ 146 project_master_rules + [
147 :change_namespace, 147 :change_namespace,
148 - :change_public_mode, 148 + :change_visibility_level,
149 :rename_project, 149 :rename_project,
150 :remove_project 150 :remove_project
151 ] 151 ]
app/models/assembla_service.rb 0 → 100644
@@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
  1 +# == Schema Information
  2 +#
  3 +# Table name: services
  4 +#
  5 +# id :integer not null, primary key
  6 +# type :string(255)
  7 +# title :string(255)
  8 +# token :string(255)
  9 +# project_id :integer not null
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +# active :boolean default(FALSE), not null
  13 +# project_url :string(255)
  14 +# subdomain :string(255)
  15 +# room :string(255)
  16 +#
  17 +
  18 +class AssemblaService < Service
  19 + include HTTParty
  20 +
  21 + validates :token, presence: true, if: :activated?
  22 +
  23 + def title
  24 + 'Assembla'
  25 + end
  26 +
  27 + def description
  28 + 'Project Management Software (Source Commits Endpoint)'
  29 + end
  30 +
  31 + def to_param
  32 + 'assembla'
  33 + end
  34 +
  35 + def fields
  36 + [
  37 + { type: 'text', name: 'token', placeholder: '' }
  38 + ]
  39 + end
  40 +
  41 + def execute(push)
  42 + url = "https://atlas.assembla.com/spaces/ouposp/github_tool?secret_key=#{token}"
  43 + AssemblaService.post(url, body: { payload: push }.to_json, headers: { 'Content-Type' => 'application/json' })
  44 + end
  45 +end
app/models/broadcast_message.rb 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +# == Schema Information
  2 +#
  3 +# Table name: broadcast_messages
  4 +#
  5 +# id :integer not null, primary key
  6 +# message :text default(""), not null
  7 +# starts_at :datetime
  8 +# ends_at :datetime
  9 +# alert_type :integer
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +#
  13 +
  14 +class BroadcastMessage < ActiveRecord::Base
  15 + attr_accessible :alert_type, :ends_at, :message, :starts_at
  16 +
  17 + validates :message, presence: true
  18 + validates :starts_at, presence: true
  19 + validates :ends_at, presence: true
  20 +
  21 + def self.current
  22 + where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last
  23 + end
  24 +end
app/models/concerns/issuable.rb
@@ -111,4 +111,11 @@ module Issuable @@ -111,4 +111,11 @@ module Issuable
111 end 111 end
112 users.concat(mentions.reduce([], :|)).uniq 112 users.concat(mentions.reduce([], :|)).uniq
113 end 113 end
  114 +
  115 + def to_hook_data
  116 + {
  117 + object_kind: self.class.name.underscore,
  118 + object_attributes: self.attributes
  119 + }
  120 + end
114 end 121 end
app/models/event.rb
@@ -168,7 +168,7 @@ class Event &lt; ActiveRecord::Base @@ -168,7 +168,7 @@ class Event &lt; ActiveRecord::Base
168 end 168 end
169 169
170 def valid_push? 170 def valid_push?
171 - data[:ref] 171 + data[:ref] && ref_name.present?
172 rescue => ex 172 rescue => ex
173 false 173 false
174 end 174 end
@@ -223,7 +223,7 @@ class Event &lt; ActiveRecord::Base @@ -223,7 +223,7 @@ class Event &lt; ActiveRecord::Base
223 223
224 # Max 20 commits from push DESC 224 # Max 20 commits from push DESC
225 def commits 225 def commits
226 - @commits ||= data[:commits].reverse 226 + @commits ||= (data[:commits] || []).reverse
227 end 227 end
228 228
229 def commits_count 229 def commits_count
app/models/flowdock_service.rb
@@ -11,6 +11,8 @@ @@ -11,6 +11,8 @@
11 # updated_at :datetime not null 11 # updated_at :datetime not null
12 # active :boolean default(FALSE), not null 12 # active :boolean default(FALSE), not null
13 # project_url :string(255) 13 # project_url :string(255)
  14 +# subdomain :string(255)
  15 +# room :string(255)
14 # 16 #
15 17
16 require "flowdock-git-hook" 18 require "flowdock-git-hook"
app/models/gollum_wiki.rb
@@ -33,7 +33,7 @@ class GollumWiki @@ -33,7 +33,7 @@ class GollumWiki
33 end 33 end
34 34
35 def http_url_to_repo 35 def http_url_to_repo
36 - http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') 36 + [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
37 end 37 end
38 38
39 # Returns the Gollum::Wiki object. 39 # Returns the Gollum::Wiki object.
app/models/hipchat_service.rb
@@ -25,7 +25,7 @@ class HipchatService &lt; Service @@ -25,7 +25,7 @@ class HipchatService &lt; Service
25 end 25 end
26 26
27 def description 27 def description
28 - 'Simple web-based real-time group chat' 28 + 'Private group chat and IM'
29 end 29 end
30 30
31 def to_param 31 def to_param
app/models/issue.rb
@@ -21,6 +21,8 @@ class Issue &lt; ActiveRecord::Base @@ -21,6 +21,8 @@ class Issue &lt; ActiveRecord::Base
21 include Issuable 21 include Issuable
22 include InternalId 22 include InternalId
23 23
  24 + ActsAsTaggableOn.strict_case_match = true
  25 +
24 belongs_to :project 26 belongs_to :project
25 validates :project, presence: true 27 validates :project, presence: true
26 28
app/models/namespace.rb
@@ -87,4 +87,8 @@ class Namespace &lt; ActiveRecord::Base @@ -87,4 +87,8 @@ class Namespace &lt; ActiveRecord::Base
87 def send_update_instructions 87 def send_update_instructions
88 projects.each(&:send_move_instructions) 88 projects.each(&:send_move_instructions)
89 end 89 end
  90 +
  91 + def kind
  92 + type == 'Group' ? 'group' : 'user'
  93 + end
90 end 94 end
app/models/note.rb
@@ -157,7 +157,8 @@ class Note &lt; ActiveRecord::Base @@ -157,7 +157,8 @@ class Note &lt; ActiveRecord::Base
157 # otherwise false is returned 157 # otherwise false is returned
158 def downvote? 158 def downvote?
159 votable? && (note.start_with?('-1') || 159 votable? && (note.start_with?('-1') ||
160 - note.start_with?(':-1:') 160 + note.start_with?(':-1:') ||
  161 + note.start_with?(':thumbsdown:')
161 ) 162 )
162 end 163 end
163 164
@@ -206,7 +207,8 @@ class Note &lt; ActiveRecord::Base @@ -206,7 +207,8 @@ class Note &lt; ActiveRecord::Base
206 # otherwise false is returned 207 # otherwise false is returned
207 def upvote? 208 def upvote?
208 votable? && (note.start_with?('+1') || 209 votable? && (note.start_with?('+1') ||
209 - note.start_with?(':+1:') 210 + note.start_with?(':+1:') ||
  211 + note.start_with?(':thumbsup:')
210 ) 212 )
211 end 213 end
212 214
app/models/project.rb
@@ -9,33 +9,37 @@ @@ -9,33 +9,37 @@
9 # created_at :datetime not null 9 # created_at :datetime not null
10 # updated_at :datetime not null 10 # updated_at :datetime not null
11 # creator_id :integer 11 # creator_id :integer
12 -# default_branch :string(255)  
13 # issues_enabled :boolean default(TRUE), not null 12 # issues_enabled :boolean default(TRUE), not null
14 # wall_enabled :boolean default(TRUE), not null 13 # wall_enabled :boolean default(TRUE), not null
15 # merge_requests_enabled :boolean default(TRUE), not null 14 # merge_requests_enabled :boolean default(TRUE), not null
16 # wiki_enabled :boolean default(TRUE), not null 15 # wiki_enabled :boolean default(TRUE), not null
17 # namespace_id :integer 16 # namespace_id :integer
18 -# public :boolean default(FALSE), not null  
19 # issues_tracker :string(255) default("gitlab"), not null 17 # issues_tracker :string(255) default("gitlab"), not null
20 # issues_tracker_id :string(255) 18 # issues_tracker_id :string(255)
21 # snippets_enabled :boolean default(TRUE), not null 19 # snippets_enabled :boolean default(TRUE), not null
22 # last_activity_at :datetime 20 # last_activity_at :datetime
23 # imported :boolean default(FALSE), not null 21 # imported :boolean default(FALSE), not null
24 # import_url :string(255) 22 # import_url :string(255)
  23 +# visibility_level :integer default(0), not null
25 # 24 #
26 25
27 class Project < ActiveRecord::Base 26 class Project < ActiveRecord::Base
28 include Gitlab::ShellAdapter 27 include Gitlab::ShellAdapter
  28 + include Gitlab::VisibilityLevel
29 extend Enumerize 29 extend Enumerize
30 30
31 - attr_accessible :name, :path, :description, :default_branch, :issues_tracker, :label_list, 31 + ActsAsTaggableOn.strict_case_match = true
  32 +
  33 + attr_accessible :name, :path, :description, :issues_tracker, :label_list,
32 :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, 34 :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
33 - :wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin] 35 + :wiki_enabled, :visibility_level, :import_url, :last_activity_at, as: [:default, :admin]
34 36
35 attr_accessible :namespace_id, :creator_id, as: :admin 37 attr_accessible :namespace_id, :creator_id, as: :admin
36 38
37 acts_as_taggable_on :labels, :issues_default_labels 39 acts_as_taggable_on :labels, :issues_default_labels
38 40
  41 + attr_accessor :new_default_branch
  42 +
39 # Relations 43 # Relations
40 belongs_to :creator, foreign_key: "creator_id", class_name: "User" 44 belongs_to :creator, foreign_key: "creator_id", class_name: "User"
41 belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" 45 belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
@@ -47,6 +51,7 @@ class Project &lt; ActiveRecord::Base @@ -47,6 +51,7 @@ class Project &lt; ActiveRecord::Base
47 has_one :pivotaltracker_service, dependent: :destroy 51 has_one :pivotaltracker_service, dependent: :destroy
48 has_one :hipchat_service, dependent: :destroy 52 has_one :hipchat_service, dependent: :destroy
49 has_one :flowdock_service, dependent: :destroy 53 has_one :flowdock_service, dependent: :destroy
  54 + has_one :assembla_service, dependent: :destroy
50 has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" 55 has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
51 has_one :forked_from_project, through: :forked_project_link 56 has_one :forked_from_project, through: :forked_project_link
52 57
@@ -104,7 +109,8 @@ class Project &lt; ActiveRecord::Base @@ -104,7 +109,8 @@ class Project &lt; ActiveRecord::Base
104 scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") } 109 scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") }
105 scope :personal, ->(user) { where(namespace_id: user.namespace_id) } 110 scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
106 scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } 111 scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
107 - scope :public_only, -> { where(public: true) } 112 + scope :public_only, -> { where(visibility_level: PUBLIC) }
  113 + scope :public_or_internal_only, ->(user) { where("visibility_level IN (:levels)", levels: user ? [ INTERNAL, PUBLIC ] : [ PUBLIC ]) }
108 114
109 enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab 115 enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
110 116
@@ -136,6 +142,10 @@ class Project &lt; ActiveRecord::Base @@ -136,6 +142,10 @@ class Project &lt; ActiveRecord::Base
136 where(path: id, namespace_id: nil).last 142 where(path: id, namespace_id: nil).last
137 end 143 end
138 end 144 end
  145 +
  146 + def visibility_levels
  147 + Gitlab::VisibilityLevel.options
  148 + end
139 end 149 end
140 150
141 def team 151 def team
@@ -143,7 +153,7 @@ class Project &lt; ActiveRecord::Base @@ -143,7 +153,7 @@ class Project &lt; ActiveRecord::Base
143 end 153 end
144 154
145 def repository 155 def repository
146 - @repository ||= Repository.new(path_with_namespace, default_branch) 156 + @repository ||= Repository.new(path_with_namespace)
147 end 157 end
148 158
149 def saved? 159 def saved?
@@ -221,7 +231,7 @@ class Project &lt; ActiveRecord::Base @@ -221,7 +231,7 @@ class Project &lt; ActiveRecord::Base
221 end 231 end
222 232
223 def available_services_names 233 def available_services_names
224 - %w(gitlab_ci campfire hipchat pivotaltracker flowdock) 234 + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla)
225 end 235 end
226 236
227 def gitlab_ci? 237 def gitlab_ci?
@@ -288,8 +298,10 @@ class Project &lt; ActiveRecord::Base @@ -288,8 +298,10 @@ class Project &lt; ActiveRecord::Base
288 ProjectTransferService.new.transfer(self, new_namespace) 298 ProjectTransferService.new.transfer(self, new_namespace)
289 end 299 end
290 300
291 - def execute_hooks(data)  
292 - hooks.each { |hook| hook.async_execute(data) } 301 + def execute_hooks(data, hooks_scope = :push_hooks)
  302 + hooks.send(hooks_scope).each do |hook|
  303 + hook.async_execute(data)
  304 + end
293 end 305 end
294 306
295 def execute_services(data) 307 def execute_services(data)
@@ -300,14 +312,6 @@ class Project &lt; ActiveRecord::Base @@ -300,14 +312,6 @@ class Project &lt; ActiveRecord::Base
300 end 312 end
301 end 313 end
302 314
303 - def discover_default_branch  
304 - # Discover the default branch, but only if it hasn't already been set to  
305 - # something else  
306 - if repository.exists? && default_branch.nil?  
307 - update_attributes(default_branch: self.repository.discover_default_branch)  
308 - end  
309 - end  
310 -  
311 def update_merge_requests(oldrev, newrev, ref, user) 315 def update_merge_requests(oldrev, newrev, ref, user)
312 return true unless ref =~ /heads/ 316 return true unless ref =~ /heads/
313 branch_name = ref.gsub("refs/heads/", "") 317 branch_name = ref.gsub("refs/heads/", "")
@@ -390,7 +394,7 @@ class Project &lt; ActiveRecord::Base @@ -390,7 +394,7 @@ class Project &lt; ActiveRecord::Base
390 end 394 end
391 395
392 def http_url_to_repo 396 def http_url_to_repo
393 - http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') 397 + [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
394 end 398 end
395 399
396 # Check if current branch name is marked as protected in the system 400 # Check if current branch name is marked as protected in the system
@@ -451,4 +455,17 @@ class Project &lt; ActiveRecord::Base @@ -451,4 +455,17 @@ class Project &lt; ActiveRecord::Base
451 def project_member(user) 455 def project_member(user)
452 users_projects.where(user_id: user).first 456 users_projects.where(user_id: user).first
453 end 457 end
  458 +
  459 + def default_branch
  460 + @default_branch ||= repository.root_ref if repository.exists?
  461 + end
  462 +
  463 + def reload_default_branch
  464 + @default_branch = nil
  465 + default_branch
  466 + end
  467 +
  468 + def visibility_level_field
  469 + visibility_level
  470 + end
454 end 471 end
app/models/project_hook.rb
@@ -2,15 +2,24 @@ @@ -2,15 +2,24 @@
2 # 2 #
3 # Table name: web_hooks 3 # Table name: web_hooks
4 # 4 #
5 -# id :integer not null, primary key  
6 -# url :string(255)  
7 -# project_id :integer  
8 -# created_at :datetime not null  
9 -# updated_at :datetime not null  
10 -# type :string(255) default("ProjectHook")  
11 -# service_id :integer 5 +# id :integer not null, primary key
  6 +# url :string(255)
  7 +# project_id :integer
  8 +# created_at :datetime not null
  9 +# updated_at :datetime not null
  10 +# type :string(255) default("ProjectHook")
  11 +# service_id :integer
  12 +# push_events :boolean default(TRUE), not null
  13 +# issues_events :boolean default(FALSE), not null
  14 +# merge_requests_events :boolean default(FALSE), not null
12 # 15 #
13 16
14 class ProjectHook < WebHook 17 class ProjectHook < WebHook
15 belongs_to :project 18 belongs_to :project
  19 +
  20 + attr_accessible :push_events, :issues_events, :merge_requests_events
  21 +
  22 + scope :push_hooks, -> { where(push_events: true) }
  23 + scope :issue_hooks, -> { where(issues_events: true) }
  24 + scope :merge_request_hooks, -> { where(merge_requests_events: true) }
16 end 25 end
app/models/repository.rb
@@ -3,7 +3,7 @@ class Repository @@ -3,7 +3,7 @@ class Repository
3 3
4 attr_accessor :raw_repository, :path_with_namespace 4 attr_accessor :raw_repository, :path_with_namespace
5 5
6 - def initialize(path_with_namespace, default_branch) 6 + def initialize(path_with_namespace, default_branch = nil)
7 @path_with_namespace = path_with_namespace 7 @path_with_namespace = path_with_namespace
8 @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace 8 @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
9 rescue Gitlab::Git::Repository::NoRepository 9 rescue Gitlab::Git::Repository::NoRepository
@@ -57,7 +57,7 @@ class Repository @@ -57,7 +57,7 @@ class Repository
57 57
58 def recent_branches(limit = 20) 58 def recent_branches(limit = 20)
59 branches.sort do |a, b| 59 branches.sort do |a, b|
60 - a.commit.committed_date <=> b.commit.committed_date 60 + b.commit.committed_date <=> a.commit.committed_date
61 end[0..limit] 61 end[0..limit]
62 end 62 end
63 63
@@ -133,6 +133,7 @@ class Repository @@ -133,6 +133,7 @@ class Repository
133 Rails.cache.delete(cache_key(:tag_names)) 133 Rails.cache.delete(cache_key(:tag_names))
134 Rails.cache.delete(cache_key(:commit_count)) 134 Rails.cache.delete(cache_key(:commit_count))
135 Rails.cache.delete(cache_key(:graph_log)) 135 Rails.cache.delete(cache_key(:graph_log))
  136 + Rails.cache.delete(cache_key(:readme))
136 end 137 end
137 138
138 def graph_log 139 def graph_log
@@ -159,4 +160,10 @@ class Repository @@ -159,4 +160,10 @@ class Repository
159 def blob_at(sha, path) 160 def blob_at(sha, path)
160 Gitlab::Git::Blob.find(self, sha, path) 161 Gitlab::Git::Blob.find(self, sha, path)
161 end 162 end
  163 +
  164 + def readme
  165 + Rails.cache.fetch(cache_key(:readme)) do
  166 + Tree.new(self, self.root_ref).readme
  167 + end
  168 + end
162 end 169 end
app/models/service_hook.rb
@@ -2,13 +2,16 @@ @@ -2,13 +2,16 @@
2 # 2 #
3 # Table name: web_hooks 3 # Table name: web_hooks
4 # 4 #
5 -# id :integer not null, primary key  
6 -# url :string(255)  
7 -# project_id :integer  
8 -# created_at :datetime not null  
9 -# updated_at :datetime not null  
10 -# type :string(255) default("ProjectHook")  
11 -# service_id :integer 5 +# id :integer not null, primary key
  6 +# url :string(255)
  7 +# project_id :integer
  8 +# created_at :datetime not null
  9 +# updated_at :datetime not null
  10 +# type :string(255) default("ProjectHook")
  11 +# service_id :integer
  12 +# push_events :boolean default(TRUE), not null
  13 +# issues_events :boolean default(FALSE), not null
  14 +# merge_requests_events :boolean default(FALSE), not null
12 # 15 #
13 16
14 class ServiceHook < WebHook 17 class ServiceHook < WebHook
app/models/system_hook.rb
@@ -2,13 +2,16 @@ @@ -2,13 +2,16 @@
2 # 2 #
3 # Table name: web_hooks 3 # Table name: web_hooks
4 # 4 #
5 -# id :integer not null, primary key  
6 -# url :string(255)  
7 -# project_id :integer  
8 -# created_at :datetime not null  
9 -# updated_at :datetime not null  
10 -# type :string(255) default("ProjectHook")  
11 -# service_id :integer 5 +# id :integer not null, primary key
  6 +# url :string(255)
  7 +# project_id :integer
  8 +# created_at :datetime not null
  9 +# updated_at :datetime not null
  10 +# type :string(255) default("ProjectHook")
  11 +# service_id :integer
  12 +# push_events :boolean default(TRUE), not null
  13 +# issues_events :boolean default(FALSE), not null
  14 +# merge_requests_events :boolean default(FALSE), not null
12 # 15 #
13 16
14 class SystemHook < WebHook 17 class SystemHook < WebHook
app/models/user.rb
@@ -36,6 +36,11 @@ @@ -36,6 +36,11 @@
36 # notification_level :integer default(1), not null 36 # notification_level :integer default(1), not null
37 # password_expires_at :datetime 37 # password_expires_at :datetime
38 # created_by_id :integer 38 # created_by_id :integer
  39 +# avatar :string(255)
  40 +# confirmation_token :string(255)
  41 +# confirmed_at :datetime
  42 +# confirmation_sent_at :datetime
  43 +# unconfirmed_email :string(255)
39 # 44 #
40 45
41 require 'carrierwave/orm/activerecord' 46 require 'carrierwave/orm/activerecord'
app/models/web_hook.rb
@@ -2,13 +2,16 @@ @@ -2,13 +2,16 @@
2 # 2 #
3 # Table name: web_hooks 3 # Table name: web_hooks
4 # 4 #
5 -# id :integer not null, primary key  
6 -# url :string(255)  
7 -# project_id :integer  
8 -# created_at :datetime not null  
9 -# updated_at :datetime not null  
10 -# type :string(255) default("ProjectHook")  
11 -# service_id :integer 5 +# id :integer not null, primary key
  6 +# url :string(255)
  7 +# project_id :integer
  8 +# created_at :datetime not null
  9 +# updated_at :datetime not null
  10 +# type :string(255) default("ProjectHook")
  11 +# service_id :integer
  12 +# push_events :boolean default(TRUE), not null
  13 +# issues_events :boolean default(FALSE), not null
  14 +# merge_requests_events :boolean default(FALSE), not null
12 # 15 #
13 16
14 class WebHook < ActiveRecord::Base 17 class WebHook < ActiveRecord::Base
app/observers/issue_observer.rb
1 class IssueObserver < BaseObserver 1 class IssueObserver < BaseObserver
2 def after_create(issue) 2 def after_create(issue)
3 notification.new_issue(issue, current_user) 3 notification.new_issue(issue, current_user)
4 -  
5 issue.create_cross_references!(issue.project, current_user) 4 issue.create_cross_references!(issue.project, current_user)
  5 + execute_hooks(issue)
6 end 6 end
7 7
8 def after_close(issue, transition) 8 def after_close(issue, transition)
9 notification.close_issue(issue, current_user) 9 notification.close_issue(issue, current_user)
10 -  
11 create_note(issue) 10 create_note(issue)
  11 + execute_hooks(issue)
12 end 12 end
13 13
14 def after_reopen(issue, transition) 14 def after_reopen(issue, transition)
@@ -29,4 +29,8 @@ class IssueObserver &lt; BaseObserver @@ -29,4 +29,8 @@ class IssueObserver &lt; BaseObserver
29 def create_note(issue) 29 def create_note(issue)
30 Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit) 30 Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit)
31 end 31 end
  32 +
  33 + def execute_hooks(issue)
  34 + issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)
  35 + end
32 end 36 end
app/observers/merge_request_observer.rb
@@ -7,15 +7,15 @@ class MergeRequestObserver &lt; ActivityObserver @@ -7,15 +7,15 @@ class MergeRequestObserver &lt; ActivityObserver
7 end 7 end
8 8
9 notification.new_merge_request(merge_request, current_user) 9 notification.new_merge_request(merge_request, current_user)
10 -  
11 merge_request.create_cross_references!(merge_request.project, current_user) 10 merge_request.create_cross_references!(merge_request.project, current_user)
  11 + execute_hooks(merge_request)
12 end 12 end
13 13
14 def after_close(merge_request, transition) 14 def after_close(merge_request, transition)
15 create_event(merge_request, Event::CLOSED) 15 create_event(merge_request, Event::CLOSED)
16 - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)  
17 -  
18 notification.close_mr(merge_request, current_user) 16 notification.close_mr(merge_request, current_user)
  17 + create_note(merge_request)
  18 + execute_hooks(merge_request)
19 end 19 end
20 20
21 def after_merge(merge_request, transition) 21 def after_merge(merge_request, transition)
@@ -31,11 +31,13 @@ class MergeRequestObserver &lt; ActivityObserver @@ -31,11 +31,13 @@ class MergeRequestObserver &lt; ActivityObserver
31 action: Event::MERGED, 31 action: Event::MERGED,
32 author_id: merge_request.author_id_of_changes 32 author_id: merge_request.author_id_of_changes
33 ) 33 )
  34 +
  35 + execute_hooks(merge_request)
34 end 36 end
35 37
36 def after_reopen(merge_request, transition) 38 def after_reopen(merge_request, transition)
37 create_event(merge_request, Event::REOPENED) 39 create_event(merge_request, Event::REOPENED)
38 - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) 40 + create_note(merge_request)
39 end 41 end
40 42
41 def after_update(merge_request) 43 def after_update(merge_request)
@@ -53,4 +55,17 @@ class MergeRequestObserver &lt; ActivityObserver @@ -53,4 +55,17 @@ class MergeRequestObserver &lt; ActivityObserver
53 author_id: current_user.id 55 author_id: current_user.id
54 ) 56 )
55 end 57 end
  58 +
  59 + private
  60 +
  61 + # Create merge request note with service comment like 'Status changed to closed'
  62 + def create_note(merge_request)
  63 + Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
  64 + end
  65 +
  66 + def execute_hooks(merge_request)
  67 + if merge_request.project
  68 + merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks)
  69 + end
  70 + end
56 end 71 end
app/observers/project_observer.rb
@@ -30,12 +30,6 @@ class ProjectObserver &lt; BaseObserver @@ -30,12 +30,6 @@ class ProjectObserver &lt; BaseObserver
30 def after_update(project) 30 def after_update(project)
31 project.send_move_instructions if project.namespace_id_changed? 31 project.send_move_instructions if project.namespace_id_changed?
32 project.rename_repo if project.path_changed? 32 project.rename_repo if project.path_changed?
33 -  
34 - GitlabShellWorker.perform_async(  
35 - :update_repository_head,  
36 - project.path_with_namespace,  
37 - project.default_branch  
38 - ) if project.default_branch_changed?  
39 end 33 end
40 34
41 def before_destroy(project) 35 def before_destroy(project)
app/observers/users_group_observer.rb
@@ -4,6 +4,6 @@ class UsersGroupObserver &lt; BaseObserver @@ -4,6 +4,6 @@ class UsersGroupObserver &lt; BaseObserver
4 end 4 end
5 5
6 def after_update(membership) 6 def after_update(membership)
7 - notification.update_group_member(membership) 7 + notification.update_group_member(membership) if membership.group_access_changed?
8 end 8 end
9 end 9 end
app/services/git_push_service.rb
@@ -24,7 +24,6 @@ class GitPushService @@ -24,7 +24,6 @@ class GitPushService
24 create_push_event 24 create_push_event
25 25
26 project.ensure_satellite_exists 26 project.ensure_satellite_exists
27 - project.discover_default_branch  
28 project.repository.expire_cache 27 project.repository.expire_cache
29 28
30 if push_to_existing_branch?(ref, oldrev) 29 if push_to_existing_branch?(ref, oldrev)
@@ -33,7 +32,7 @@ class GitPushService @@ -33,7 +32,7 @@ class GitPushService
33 end 32 end
34 33
35 if push_to_branch?(ref) 34 if push_to_branch?(ref)
36 - project.execute_hooks(@push_data.dup) 35 + project.execute_hooks(@push_data.dup, :push_hooks)
37 project.execute_services(@push_data.dup) 36 project.execute_services(@push_data.dup)
38 end 37 end
39 38
app/services/notification_service.rb
@@ -19,7 +19,7 @@ class NotificationService @@ -19,7 +19,7 @@ class NotificationService
19 19
20 # When create an issue we should send next emails: 20 # When create an issue we should send next emails:
21 # 21 #
22 - # * issue assignee if his notification level is not Disabled 22 + # * issue assignee if their notification level is not Disabled
23 # * project team members with notification level higher then Participating 23 # * project team members with notification level higher then Participating
24 # 24 #
25 def new_issue(issue, current_user) 25 def new_issue(issue, current_user)
@@ -28,8 +28,8 @@ class NotificationService @@ -28,8 +28,8 @@ class NotificationService
28 28
29 # When we close an issue we should send next emails: 29 # When we close an issue we should send next emails:
30 # 30 #
31 - # * issue author if his notification level is not Disabled  
32 - # * issue assignee if his notification level is not Disabled 31 + # * issue author if their notification level is not Disabled
  32 + # * issue assignee if their notification level is not Disabled
33 # * project team members with notification level higher then Participating 33 # * project team members with notification level higher then Participating
34 # 34 #
35 def close_issue(issue, current_user) 35 def close_issue(issue, current_user)
@@ -38,8 +38,8 @@ class NotificationService @@ -38,8 +38,8 @@ class NotificationService
38 38
39 # When we reassign an issue we should send next emails: 39 # When we reassign an issue we should send next emails:
40 # 40 #
41 - # * issue old assignee if his notification level is not Disabled  
42 - # * issue new assignee if his notification level is not Disabled 41 + # * issue old assignee if their notification level is not Disabled
  42 + # * issue new assignee if their notification level is not Disabled
43 # 43 #
44 def reassigned_issue(issue, current_user) 44 def reassigned_issue(issue, current_user)
45 reassign_resource_email(issue, issue.project, current_user, 'reassigned_issue_email') 45 reassign_resource_email(issue, issue.project, current_user, 'reassigned_issue_email')
@@ -48,7 +48,7 @@ class NotificationService @@ -48,7 +48,7 @@ class NotificationService
48 48
49 # When create a merge request we should send next emails: 49 # When create a merge request we should send next emails:
50 # 50 #
51 - # * mr assignee if his notification level is not Disabled 51 + # * mr assignee if their notification level is not Disabled
52 # 52 #
53 def new_merge_request(merge_request, current_user) 53 def new_merge_request(merge_request, current_user)
54 new_resource_email(merge_request, merge_request.target_project, 'new_merge_request_email') 54 new_resource_email(merge_request, merge_request.target_project, 'new_merge_request_email')
@@ -56,8 +56,8 @@ class NotificationService @@ -56,8 +56,8 @@ class NotificationService
56 56
57 # When we reassign a merge_request we should send next emails: 57 # When we reassign a merge_request we should send next emails:
58 # 58 #
59 - # * merge_request old assignee if his notification level is not Disabled  
60 - # * merge_request assignee if his notification level is not Disabled 59 + # * merge_request old assignee if their notification level is not Disabled
  60 + # * merge_request assignee if their notification level is not Disabled
61 # 61 #
62 def reassigned_merge_request(merge_request, current_user) 62 def reassigned_merge_request(merge_request, current_user)
63 reassign_resource_email(merge_request, merge_request.target_project, current_user, 'reassigned_merge_request_email') 63 reassign_resource_email(merge_request, merge_request.target_project, current_user, 'reassigned_merge_request_email')
@@ -65,8 +65,8 @@ class NotificationService @@ -65,8 +65,8 @@ class NotificationService
65 65
66 # When we close a merge request we should send next emails: 66 # When we close a merge request we should send next emails:
67 # 67 #
68 - # * merge_request author if his notification level is not Disabled  
69 - # * merge_request assignee if his notification level is not Disabled 68 + # * merge_request author if their notification level is not Disabled
  69 + # * merge_request assignee if their notification level is not Disabled
70 # * project team members with notification level higher then Participating 70 # * project team members with notification level higher then Participating
71 # 71 #
72 def close_mr(merge_request, current_user) 72 def close_mr(merge_request, current_user)
@@ -75,8 +75,8 @@ class NotificationService @@ -75,8 +75,8 @@ class NotificationService
75 75
76 # When we merge a merge request we should send next emails: 76 # When we merge a merge request we should send next emails:
77 # 77 #
78 - # * merge_request author if his notification level is not Disabled  
79 - # * merge_request assignee if his notification level is not Disabled 78 + # * merge_request author if their notification level is not Disabled
  79 + # * merge_request assignee if their notification level is not Disabled
80 # * project team members with notification level higher then Participating 80 # * project team members with notification level higher then Participating
81 # 81 #
82 def merge_mr(merge_request) 82 def merge_mr(merge_request)
app/services/project_transfer_service.rb
@@ -18,6 +18,10 @@ class ProjectTransferService @@ -18,6 +18,10 @@ class ProjectTransferService
18 raise TransferError.new("Project with same path in target namespace already exists") 18 raise TransferError.new("Project with same path in target namespace already exists")
19 end 19 end
20 20
  21 + # Remove old satellite
  22 + project.satellite.destroy
  23 +
  24 + # Apply new namespace id
21 project.namespace = new_namespace 25 project.namespace = new_namespace
22 project.save! 26 project.save!
23 27
@@ -29,8 +33,8 @@ class ProjectTransferService @@ -29,8 +33,8 @@ class ProjectTransferService
29 # Move wiki repo also if present 33 # Move wiki repo also if present
30 gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") 34 gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
31 35
32 - # create satellite repo  
33 - project.ensure_satellite_exists 36 + # Create a new satellite (reload project from DB)
  37 + Project.find(project.id).ensure_satellite_exists
34 38
35 # clear project cached events 39 # clear project cached events
36 project.reset_events_cache 40 project.reset_events_cache
app/views/admin/broadcast_messages/index.html.haml 0 → 100644
@@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
  1 +%h3.page-title
  2 + Broadcast Messages
  3 +%p.light
  4 + Broadcast messages are displayed for every user and can be used to notify users about scheduled maintenance, recent upgrades and more.
  5 +%hr
  6 +
  7 += form_for [:admin, @broadcast_message] do |f|
  8 + -if @broadcast_message.errors.any?
  9 + .alert.alert-error
  10 + - @broadcast_message.errors.full_messages.each do |msg|
  11 + %p= msg
  12 + .control-group
  13 + = f.label :message
  14 + .controls
  15 + = f.text_area :message, class: "input-xxlarge", rows: 2, required: true
  16 + .control-group
  17 + = f.label :starts_at
  18 + .controls.datetime-controls
  19 + = f.datetime_select :starts_at
  20 + .control-group
  21 + = f.label :ends_at
  22 + .controls.datetime-controls
  23 + = f.datetime_select :ends_at
  24 + .form-actions
  25 + = f.submit "Add broadcast message", class: "btn btn-create"
  26 +
  27 +-if @broadcast_messages.any?
  28 + %ul.bordered-list.broadcast-messages
  29 + - @broadcast_messages.each do |broadcast_message|
  30 + %li
  31 + .pull-right
  32 + - if broadcast_message.starts_at
  33 + %strong
  34 + #{broadcast_message.starts_at.to_s(:short)}
  35 + \...
  36 + - if broadcast_message.ends_at
  37 + %strong
  38 + #{broadcast_message.ends_at.to_s(:short)}
  39 + &nbsp;
  40 + = link_to [:admin, broadcast_message], method: :delete, remote: true, class: 'remove-row btn btn-tiny' do
  41 + %i.icon-remove.cred
  42 +
  43 + .message= broadcast_message.message
  44 +
  45 +
  46 + = paginate @broadcast_messages
app/views/admin/dashboard/index.html.haml
@@ -52,6 +52,19 @@ @@ -52,6 +52,19 @@
52 ago 52 ago
53 53
54 .span4 54 .span4
  55 + %h4 Latest groups
  56 + %hr
  57 + - @groups.each do |group|
  58 + %p
  59 + = link_to [:admin, group] do
  60 + = group.name
  61 + %span.light.pull-right
  62 + = time_ago_in_words group.created_at
  63 + ago
  64 +
  65 +%br
  66 +.row
  67 + .span4
55 %h4 Stats 68 %h4 Stats
56 %hr 69 %hr
57 %p 70 %p
@@ -82,3 +95,43 @@ @@ -82,3 +95,43 @@
82 Milestones 95 Milestones
83 %span.light.pull-right 96 %span.light.pull-right
84 = Milestone.count 97 = Milestone.count
  98 + .span4
  99 + %h4
  100 + Features
  101 + %hr
  102 + %p
  103 + Sign up
  104 + %span.light.pull-right
  105 + = boolean_to_icon gitlab_config.signup_enabled
  106 + %p
  107 + LDAP
  108 + %span.light.pull-right
  109 + = boolean_to_icon Gitlab.config.ldap.enabled
  110 + %p
  111 + Gravatar
  112 + %span.light.pull-right
  113 + = boolean_to_icon Gitlab.config.gravatar.enabled
  114 + %p
  115 + OmniAuth
  116 + %span.light.pull-right
  117 + = boolean_to_icon Gitlab.config.omniauth.enabled
  118 + .span4
  119 + %h4 Components
  120 + %hr
  121 + %p
  122 + GitLab
  123 + %span.pull-right
  124 + = Gitlab::VERSION
  125 + %p
  126 + GitLab Shell
  127 + %span.pull-right
  128 + = Gitlab::Shell.new.version
  129 + %p
  130 + Ruby
  131 + %span.pull-right
  132 + #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
  133 +
  134 + %p
  135 + Rails
  136 + %span.pull-right
  137 + #{Rails::VERSION::STRING}
app/views/admin/groups/index.html.haml
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right" 7 = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
8 %br 8 %br
9 = form_tag admin_groups_path, method: :get, class: 'form-inline' do 9 = form_tag admin_groups_path, method: :get, class: 'form-inline' do
10 - = text_field_tag :name, params[:name], class: "span6" 10 + = text_field_tag :name, params[:name], class: "span6 input-xpadding"
11 = submit_tag "Search", class: "btn submit btn-primary" 11 = submit_tag "Search", class: "btn submit btn-primary"
12 12
13 %hr 13 %hr
app/views/admin/groups/show.html.haml
@@ -39,6 +39,8 @@ @@ -39,6 +39,8 @@
39 %li 39 %li
40 %strong 40 %strong
41 = link_to project.name_with_namespace, [:admin, project] 41 = link_to project.name_with_namespace, [:admin, project]
  42 + %span.label.label-gray
  43 + = repository_size(project)
42 %span.pull-right.light 44 %span.pull-right.light
43 %span.monospace= project.path_with_namespace + ".git" 45 %span.monospace= project.path_with_namespace + ".git"
44 46
app/views/admin/projects/index.html.haml
@@ -10,11 +10,15 @@ @@ -10,11 +10,15 @@
10 .control-group 10 .control-group
11 = label_tag :owner_id, 'Owner:', class: 'control-label' 11 = label_tag :owner_id, 'Owner:', class: 'control-label'
12 .controls 12 .controls
13 - = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large'  
14 - .control-group  
15 - = label_tag :public_only, 'Public Only', class: 'control-label'  
16 - .controls  
17 - = check_box_tag :public_only, 1, params[:public_only] 13 + = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large input-clamp'
  14 + .control-group.visibility-levels
  15 + = label_tag :visibility_level, 'Visibility Levels', class: 'control-label'
  16 + - Project.visibility_levels.each do |label, level|
  17 + .controls
  18 + = check_box_tag 'visibility_levels[]', level, params[:visibility_levels].present? && params[:visibility_levels].include?(level.to_s)
  19 + %span.descr
  20 + = visibility_level_icon(level)
  21 + = label
18 .control-group 22 .control-group
19 = label_tag :with_push, 'Not empty', class: 'control-label' 23 = label_tag :with_push, 'Not empty', class: 'control-label'
20 .controls 24 .controls
@@ -42,12 +46,12 @@ @@ -42,12 +46,12 @@
42 %ul.well-list 46 %ul.well-list
43 - @projects.each do |project| 47 - @projects.each do |project|
44 %li 48 %li
45 - - if project.public  
46 - = public_icon  
47 - - else  
48 - = private_icon 49 + %span{ class: visibility_level_color(project.visibility_level) }
  50 + = visibility_level_icon(project.visibility_level)
49 = link_to project.name_with_namespace, [:admin, project] 51 = link_to project.name_with_namespace, [:admin, project]
50 .pull-right 52 .pull-right
  53 + %span.label.label-gray
  54 + = repository_size(project)
51 = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" 55 = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
52 = link_to 'Destroy', [project], confirm: remove_project_message(project), method: :delete, class: "btn btn-small btn-remove" 56 = link_to 'Destroy', [project], confirm: remove_project_message(project), method: :delete, class: "btn btn-small btn-remove"
53 - if @projects.blank? 57 - if @projects.blank?
app/views/admin/projects/show.html.haml
@@ -66,14 +66,24 @@ @@ -66,14 +66,24 @@
66 %li 66 %li
67 %span.light access: 67 %span.light access:
68 %strong 68 %strong
69 - - if @project.public  
70 - %span.cblue  
71 - %i.icon-share  
72 - Public  
73 - - else  
74 - %span.cgreen  
75 - %i.icon-lock  
76 - Private 69 + %span{ class: visibility_level_color(@project.visibility_level) }
  70 + = visibility_level_icon(@project.visibility_level)
  71 + = visibility_level_label(@project.visibility_level)
  72 +
  73 + .ui-box
  74 + .title
  75 + Transfer project
  76 + .ui-box-body
  77 + = form_for @project, url: transfer_admin_project_path(@project), method: :put do |f|
  78 + .control-group
  79 + = f.label :namespace_id, "Namespace"
  80 + .controls
  81 + = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
  82 +
  83 + .control-group
  84 + .controls
  85 + = f.submit 'Transfer', class: 'btn btn-primary'
  86 +
77 .span6 87 .span6
78 - if @group 88 - if @group
79 .ui-box 89 .ui-box
app/views/admin/users/index.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 .span3 2 .span3
3 .admin-filter 3 .admin-filter
4 = form_tag admin_users_path, method: :get, class: 'form-inline' do 4 = form_tag admin_users_path, method: :get, class: 'form-inline' do
5 - = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'search-text-input span2'  
6 - = button_tag type: 'submit', class: 'btn' do 5 + = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'input-xpadding span2'
  6 + = button_tag type: 'submit', class: 'btn btn-primary' do
7 %i.icon-search 7 %i.icon-search
8 %ul.nav.nav-pills.nav-stacked 8 %ul.nav.nav-pills.nav-stacked
9 %li{class: "#{'active' unless params[:filter]}"} 9 %li{class: "#{'active' unless params[:filter]}"}
app/views/dashboard/projects.html.haml
@@ -26,6 +26,14 @@ @@ -26,6 +26,14 @@
26 %span.pull-right 26 %span.pull-right
27 = current_user.owned_projects.count 27 = current_user.owned_projects.count
28 28
  29 + %fieldset
  30 + %legend Visibility
  31 + %ul.bordered-list.visibility-filter
  32 + - Gitlab::VisibilityLevel.values.each do |level|
  33 + %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
  34 + = link_to projects_dashboard_path(visibility_level: level) do
  35 + = visibility_level_icon(level)
  36 + = visibility_level_label(level)
29 37
30 - if @groups.present? 38 - if @groups.present?
31 %fieldset 39 %fieldset
@@ -56,12 +64,10 @@ @@ -56,12 +64,10 @@
56 - @projects.each do |project| 64 - @projects.each do |project|
57 %li.my-project-row 65 %li.my-project-row
58 %h4.project-title 66 %h4.project-title
  67 + .project-access-icon
  68 + = visibility_level_icon(project.visibility_level)
59 = link_to project_path(project), class: dom_class(project) do 69 = link_to project_path(project), class: dom_class(project) do
60 = project.name_with_namespace 70 = project.name_with_namespace
61 - - if project.public  
62 - %small.access-icon  
63 - = public_icon  
64 - Public  
65 71
66 - if current_user.can_leave_project?(project) 72 - if current_user.can_leave_project?(project)
67 .pull-right 73 .pull-right
app/views/dashboard/show.js.haml
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -:plain  
2 - Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");  
app/views/devise/confirmations/new.html.erb
@@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
1 -<h2>Resend confirmation instructions</h2>  
2 -  
3 -<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>  
4 - <%= devise_error_messages! %>  
5 -  
6 - <div><%= f.label :email %><br />  
7 - <%= f.email_field :email %></div>  
8 -  
9 - <div><%= f.submit "Resend confirmation instructions" %></div>  
10 -<% end %>  
11 -  
12 -<%= render partial: "devise/shared/links" %>  
app/views/devise/confirmations/new.html.haml 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +.login-box
  2 + %h3.page-title Resend confirmation instructions
  3 + = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
  4 + = devise_error_messages!
  5 + = f.email_field :email, placeholder: 'Email'
  6 + %div= f.submit "Resend confirmation instructions", class: 'btn btn-success'
  7 + %hr
  8 + = link_to "Sign in", new_session_path(resource_name)
app/views/events/_events.html.haml 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 += render @events
app/views/groups/edit.html.haml
@@ -51,10 +51,7 @@ @@ -51,10 +51,7 @@
51 %ul.well-list 51 %ul.well-list
52 - @group.projects.each do |project| 52 - @group.projects.each do |project|
53 %li 53 %li
54 - - if project.public  
55 - = public_icon  
56 - - else  
57 - = private_icon 54 + = visibility_level_icon(project.visibility_level)
58 = link_to project.name_with_namespace, project 55 = link_to project.name_with_namespace, project
59 .pull-right 56 .pull-right
60 = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" 57 = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
app/views/groups/show.js.haml
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -:plain  
2 - Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");  
app/views/help/_layout.html.haml
1 .row 1 .row
2 .span3{:"data-spy" => 'affix'} 2 .span3{:"data-spy" => 'affix'}
3 - .ui-box  
4 - .title  
5 - Help  
6 - %ul.well-list  
7 - %li  
8 - %strong= link_to "Workflow", help_workflow_path  
9 - %li  
10 - %strong= link_to "SSH keys", help_ssh_path  
11 -  
12 - %li  
13 - %strong= link_to "GitLab Markdown", help_markdown_path  
14 -  
15 - %li  
16 - %strong= link_to "Permissions", help_permissions_path  
17 -  
18 - %li  
19 - %strong= link_to "API", help_api_path  
20 -  
21 - %li  
22 - %strong= link_to "Web Hooks", help_web_hooks_path  
23 -  
24 - %li  
25 - %strong= link_to "Rake Tasks", help_raketasks_path  
26 -  
27 - %li  
28 - %strong= link_to "System Hooks", help_system_hooks_path  
29 -  
30 - %li  
31 - %strong= link_to "Public Access", help_public_access_path  
32 -  
33 - %li  
34 - %strong= link_to "Security", help_security_path 3 + %h3.page-title Help
  4 + %ul.nav.nav-pills.nav-stacked
  5 + - links = {:"Workflow" => help_workflow_path, :"SSH Keys" => help_ssh_path, :"GitLab Markdown" => help_markdown_path, :"Permissions" => help_permissions_path, :"API" => help_api_path, :"Web Hooks" => help_web_hooks_path, :"Rake Tasks" => help_raketasks_path, :"System Hooks" => help_system_hooks_path, :"Public Access" => help_public_access_path, :"Security" => help_security_path}
  6 + - links.each do |title,path|
  7 + %li{class: current_page?(path) ? 'active' : nil}
  8 + = link_to title, path
35 9
36 .span9.pull-right 10 .span9.pull-right
37 = yield 11 = yield
app/views/help/permissions.html.haml
@@ -143,7 +143,7 @@ @@ -143,7 +143,7 @@
143 %td.permission-x &#10003; 143 %td.permission-x &#10003;
144 %td.permission-x &#10003; 144 %td.permission-x &#10003;
145 %tr 145 %tr
146 - %td Switch public mode 146 + %td Switch visibility level
147 %td 147 %td
148 %td 148 %td
149 %td 149 %td
app/views/help/public_access.html.haml
1 = render layout: 'help/layout' do 1 = render layout: 'help/layout' do
2 %h3.page-title Public Access 2 %h3.page-title Public Access
3 3
4 - %p  
5 - GitLab allows you to open selected projects to be accessed publicly.  
6 - These projects will be cloneable  
7 - %em without any  
8 - authentication.  
9 - Also they will be listed on the #{link_to "public access directory", public_root_path}. 4 + %p.slead
  5 + GitLab allows you to open selected projects to be accessed
  6 + %strong publicly
  7 + or
  8 + %strong internally
  9 + \.
  10 + %br
  11 + Projects with either of these visibility levels will be listed in the #{link_to "public access directory", public_root_path}.
  12 + %br
  13 + Internal projects will only be available to authenticated users.
10 14
  15 + .clearfix
  16 + .dashboard-intro-icon
  17 + = public_icon
  18 + %h4
  19 + Public projects
  20 + %p
  21 + Public project can be cloned
  22 + %strong without any
  23 + authentication.
  24 + %br
  25 + It will also be listed on the #{link_to "public access directory", public_root_path}.
  26 + %br
  27 + %strong Any logged in user
  28 + will have #{link_to "Guest", help_permissions_path} permissions on the repository.
  29 +
  30 + .clearfix
  31 + .dashboard-intro-icon
  32 + = internal_icon
  33 + %h4
  34 + Internal projects
  35 + %p
  36 + Internal project can be cloned by any logged in user.
  37 + %br
  38 + It will also be listed on the #{link_to "public access directory", public_root_path} for logged in users.
  39 + %br
  40 + Any logged in user will have #{link_to "Guest", help_permissions_path} permissions on the repository.
  41 +
  42 + %h4 How to change project visibility
11 %ol 43 %ol
12 %li Go to your project dashboard 44 %li Go to your project dashboard
13 %li Click on the "Edit" tab 45 %li Click on the "Edit" tab
14 - %li Select "Public clone access"  
15 - 46 + %li Change "Visibility Level"
app/views/help/web_hooks.html.haml
1 = render layout: 'help/layout' do 1 = render layout: 'help/layout' do
2 - %h3.page-title Web hooks 2 + %h3.page-title Project web hooks
  3 + %p.light
  4 + Project web hooks allow you to trigger url if new code is pushed or new issue is created
  5 + %hr
3 6
4 %p.slead 7 %p.slead
5 - Every GitLab project can trigger a web server whenever the repo is pushed to. 8 + You can configure web hook to listen for specific events like pushes, issues, merge requests.
  9 + %br
  10 + GitLab will send POST request with data to web hook url.
6 %br 11 %br
7 Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. 12 Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
  13 + %hr
  14 +
  15 + %h4 Push events
  16 + %p.light
  17 + Triggered when you push to the repository except pushing tags.
8 %br 18 %br
9 - GitLab will send POST request with commits information on every push.  
10 - %h5 Hooks request example:  
11 - = render "projects/hooks/data_ex" 19 + Request body:
  20 + = highlight_js do
  21 + :erb
  22 + {
  23 + "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
  24 + "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
  25 + "ref": "refs/heads/master",
  26 + "user_id": 4,
  27 + "user_name": "John Smith",
  28 + "project_id": 15,
  29 + "repository": {
  30 + "name": "Diaspora",
  31 + "url": "git@localhost:diaspora.git",
  32 + "description": "",
  33 + "homepage": "http://localhost/diaspora",
  34 + },
  35 + "commits": [
  36 + {
  37 + "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
  38 + "message": "Update Catalan translation to e38cb41.",
  39 + "timestamp": "2011-12-12T14:27:31+02:00",
  40 + "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
  41 + "author": {
  42 + "name": "Jordi Mallach",
  43 + "email": "jordi@softcatala.org",
  44 + }
  45 + },
  46 + // ...
  47 + {
  48 + "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
  49 + "message": "fixed readme",
  50 + "timestamp": "2012-01-03T23:36:29+02:00",
  51 + "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
  52 + "author": {
  53 + "name": "GitLab dev user",
  54 + "email": "gitlabdev@dv6700.(none)",
  55 + },
  56 + },
  57 + ],
  58 + "total_commits_count": 4,
  59 + };
  60 +
12 61
  62 + %h4.prepend-top-20 Issues events
  63 + %p.light
  64 + Triggered when new issue created or existing issue was closed.
  65 + %br
  66 + Request body:
  67 + = highlight_js do
  68 + :erb
  69 + {
  70 + "object_kind":"issue",
  71 + "object_attributes":{
  72 + "id":301,
  73 + "title":"New API: create/update/delete file",
  74 + "assignee_id":51,
  75 + "author_id":51,
  76 + "project_id":14,
  77 + "created_at":"2013-12-03T17:15:43Z",
  78 + "updated_at":"2013-12-03T17:15:43Z",
  79 + "position":0,
  80 + "branch_name":null,
  81 + "description":"Create new API for manipulations with repository",
  82 + "milestone_id":null,
  83 + "state":"opened",
  84 + "iid":23
  85 + }
  86 + }
  87 + %h4.prepend-top-20 Merge request events
  88 + %p.light
  89 + Triggered when new merge request created or existing merge request was merged/closed.
  90 + %br
  91 + Request body:
  92 + = highlight_js do
  93 + :erb
  94 + {
  95 + "object_kind":"merge_request",
  96 + "object_attributes":{
  97 + "id":99,
  98 + "target_branch":"master",
  99 + "source_branch":"ms-viewport",
  100 + "source_project_id":14,
  101 + "author_id":51,
  102 + "assignee_id":6,
  103 + "title":"MS-Viewport",
  104 + "created_at":"2013-12-03T17:23:34Z",
  105 + "updated_at":"2013-12-03T17:23:34Z",
  106 + "st_commits":null,
  107 + "st_diffs":null,
  108 + "milestone_id":null,
  109 + "state":"opened",
  110 + "merge_status":"unchecked",
  111 + "target_project_id":14,
  112 + "iid":1,
  113 + "description":""
  114 + }
  115 + }
app/views/layouts/_broadcast.html.haml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +- if broadcast_message.present?
  2 + .broadcast-message
  3 + %i.icon-bullhorn
  4 + = broadcast_message.message
app/views/layouts/application.html.haml
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: "Dashboard" 3 = render "layouts/head", title: "Dashboard"
4 %body{class: "#{app_theme} application", :'data-page' => body_data_page } 4 %body{class: "#{app_theme} application", :'data-page' => body_data_page }
  5 + = render "layouts/broadcast"
5 = render "layouts/head_panel", title: "Dashboard" 6 = render "layouts/head_panel", title: "Dashboard"
6 = render "layouts/flash" 7 = render "layouts/flash"
7 %nav.main-nav 8 %nav.main-nav
app/views/layouts/group.html.haml
1 !!! 5 1 !!! 5
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 - = render "layouts/head", title: "#{@group.name}" 3 + = render "layouts/head", title: group_head_title
4 %body{class: "#{app_theme} application", :'data-page' => body_data_page} 4 %body{class: "#{app_theme} application", :'data-page' => body_data_page}
  5 + = render "layouts/broadcast"
5 = render "layouts/head_panel", title: "group: #{@group.name}" 6 = render "layouts/head_panel", title: "group: #{@group.name}"
6 = render "layouts/flash" 7 = render "layouts/flash"
7 %nav.main-nav 8 %nav.main-nav
app/views/layouts/nav/_admin.html.haml
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 = link_to "Users", admin_users_path 10 = link_to "Users", admin_users_path
11 = nav_link(controller: :logs) do 11 = nav_link(controller: :logs) do
12 = link_to "Logs", admin_logs_path 12 = link_to "Logs", admin_logs_path
  13 + = nav_link(controller: :broadcast_messages) do
  14 + = link_to "Messages", admin_broadcast_messages_path
13 = nav_link(controller: :hooks) do 15 = nav_link(controller: :hooks) do
14 = link_to "Hooks", admin_hooks_path 16 = link_to "Hooks", admin_hooks_path
15 = nav_link(controller: :background_jobs) do 17 = nav_link(controller: :background_jobs) do
app/views/layouts/profile.html.haml
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: "Profile" 3 = render "layouts/head", title: "Profile"
4 %body{class: "#{app_theme} profile", :'data-page' => body_data_page} 4 %body{class: "#{app_theme} profile", :'data-page' => body_data_page}
  5 + = render "layouts/broadcast"
5 = render "layouts/head_panel", title: "Profile" 6 = render "layouts/head_panel", title: "Profile"
6 = render "layouts/flash" 7 = render "layouts/flash"
7 %nav.main-nav 8 %nav.main-nav
app/views/layouts/project_settings.html.haml
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: @project.name_with_namespace 3 = render "layouts/head", title: @project.name_with_namespace
4 %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } 4 %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
  5 + = render "layouts/broadcast"
5 = render "layouts/head_panel", title: project_title(@project) 6 = render "layouts/head_panel", title: project_title(@project)
6 = render "layouts/init_auto_complete" 7 = render "layouts/init_auto_complete"
7 = render "layouts/flash" 8 = render "layouts/flash"
app/views/layouts/projects.html.haml
1 !!! 5 1 !!! 5
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 - = render "layouts/head", title: @project.name_with_namespace 3 + = render "layouts/head", title: project_head_title
4 %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } 4 %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
  5 + = render "layouts/broadcast"
5 = render "layouts/head_panel", title: project_title(@project) 6 = render "layouts/head_panel", title: project_title(@project)
6 = render "layouts/init_auto_complete" 7 = render "layouts/init_auto_complete"
7 = render "layouts/flash" 8 = render "layouts/flash"
app/views/layouts/public.html.haml
1 !!! 5 1 !!! 5
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: "Public Projects" 3 = render "layouts/head", title: "Public Projects"
4 - %body{class: "ui_mars application", :'data-page' => body_data_page} 4 + %body{class: "#{app_theme} application", :'data-page' => body_data_page}
5 - if current_user 5 - if current_user
6 = render "layouts/head_panel", title: "Public Projects" 6 = render "layouts/head_panel", title: "Public Projects"
7 - else 7 - else
app/views/layouts/public_projects.html.haml
1 !!! 5 1 !!! 5
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: @project.name_with_namespace 3 = render "layouts/head", title: @project.name_with_namespace
4 - %body{class: "ui_mars application", :'data-page' => body_data_page} 4 + %body{class: "#{app_theme} application", :'data-page' => body_data_page}
5 = render "layouts/public_head_panel" 5 = render "layouts/public_head_panel"
6 %nav.main-nav 6 %nav.main-nav
7 .container= render 'layouts/nav/project' 7 .container= render 'layouts/nav/project'
app/views/notify/_note_message.html.haml
1 %p 1 %p
2 %strong #{@note.author_name} 2 %strong #{@note.author_name}
3 - left next message: 3 + wrote:
4 4
5 %cite{style: 'color: #666'} 5 %cite{style: 'color: #666'}
6 = markdown(@note.note) 6 = markdown(@note.note)
app/views/notify/new_merge_request_email.html.haml
1 %p 1 %p
2 - = "New Merge Request !#{@merge_request.iid}" 2 + = "New Merge Request ##{@merge_request.iid}"
3 %p 3 %p
4 = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) 4 = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
5 %p 5 %p
app/views/notify/new_merge_request_email.text.erb
1 -New Merge Request <%= @merge_request.iid %> 1 +New Merge Request #<%= @merge_request.iid %>
2 2
3 <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> 3 <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
4 4
app/views/notify/reassigned_merge_request_email.html.haml
1 %p 1 %p
2 - = "Reassigned Merge Request !#{@merge_request.iid}" 2 + = "Reassigned Merge Request ##{@merge_request.iid}"
3 = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request) 3 = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request)
4 %p 4 %p
5 Assignee changed 5 Assignee changed
app/views/notify/reassigned_merge_request_email.text.erb
1 -Reassigned Merge Request <%= @merge_request.iid %> 1 +Reassigned Merge Request #<%= @merge_request.iid %>
2 2
3 <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> 3 <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
4 4
app/views/profiles/show.html.haml
@@ -21,16 +21,22 @@ @@ -21,16 +21,22 @@
21 .controls 21 .controls
22 = f.text_field :name, class: "input-xlarge", required: true 22 = f.text_field :name, class: "input-xlarge", required: true
23 %span.help-block Enter your name, so people you know can recognize you. 23 %span.help-block Enter your name, so people you know can recognize you.
  24 +
24 .control-group 25 .control-group
25 = f.label :email, class: "control-label" 26 = f.label :email, class: "control-label"
26 .controls 27 .controls
27 - = f.text_field :email, class: "input-xlarge", required: true  
28 - - if @user.unconfirmed_email.present?  
29 - %span.help-block  
30 - We sent confirmation email to  
31 - %strong #{@user.unconfirmed_email} 28 + - if @user.ldap_user?
  29 + = f.text_field :email, class: "input-xlarge", required: true, readonly: true
  30 + %span.help-block.light
  31 + Email is read-only for LDAP user
32 - else 32 - else
33 - %span.help-block We also use email for avatar detection if no avatar is uploaded. 33 + = f.text_field :email, class: "input-xlarge", required: true
  34 + - if @user.unconfirmed_email.present?
  35 + %span.help-block
  36 + We sent confirmation email to
  37 + %strong #{@user.unconfirmed_email}
  38 + - else
  39 + %span.help-block We also use email for avatar detection if no avatar is uploaded.
34 .control-group 40 .control-group
35 = f.label :skype, class: "control-label" 41 = f.label :skype, class: "control-label"
36 .controls= f.text_field :skype, class: "input-xlarge" 42 .controls= f.text_field :skype, class: "input-xlarge"
@@ -53,9 +59,14 @@ @@ -53,9 +59,14 @@
53 .clearfix 59 .clearfix
54 .profile-avatar-form-option 60 .profile-avatar-form-option
55 %p.light 61 %p.light
56 - You can upload an avatar here  
57 - %br  
58 - or change it at #{link_to "gravatar.com", "http://gravatar.com"} 62 + - if @user.avatar?
  63 + You can change your avatar here
  64 + %br
  65 + or remove the current avatar to revert to #{link_to "gravatar.com", "http://gravatar.com"}
  66 + - else
  67 + You can upload an avatar here
  68 + %br
  69 + or change it at #{link_to "gravatar.com", "http://gravatar.com"}
59 %hr 70 %hr
60 %a.choose-btn.btn.btn-small.js-choose-user-avatar-button 71 %a.choose-btn.btn.btn-small.js-choose-user-avatar-button
61 %i.icon-paper-clip 72 %i.icon-paper-clip
@@ -63,7 +74,10 @@ @@ -63,7 +74,10 @@
63 &nbsp; 74 &nbsp;
64 %span.file_name.js-avatar-filename File name... 75 %span.file_name.js-avatar-filename File name...
65 = f.file_field :avatar, class: "js-user-avatar-input hide" 76 = f.file_field :avatar, class: "js-user-avatar-input hide"
66 - %span.help-block The maximum file size allowed is 100KB. 77 + .light The maximum file size allowed is 100KB.
  78 + - if @user.avatar?
  79 + %hr
  80 + = link_to 'Remove avatar', profile_avatar_path, confirm: "Avatar will be removed. Are you sure?", method: :delete, class: "btn btn-remove btn-small remove-avatar"
67 81
68 .form-actions 82 .form-actions
69 = f.submit 'Save changes', class: "btn btn-save" 83 = f.submit 'Save changes', class: "btn btn-save"
app/views/projects/_dropdown.html.haml
@@ -6,15 +6,19 @@ @@ -6,15 +6,19 @@
6 - if @project.issues_enabled && can?(current_user, :write_issue, @project) 6 - if @project.issues_enabled && can?(current_user, :write_issue, @project)
7 %li 7 %li
8 = link_to url_for_new_issue, title: "New Issue" do 8 = link_to url_for_new_issue, title: "New Issue" do
9 - Issue 9 + New issue
10 - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project) 10 - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project)
11 %li 11 %li
12 = link_to new_project_merge_request_path(@project), title: "New Merge Request" do 12 = link_to new_project_merge_request_path(@project), title: "New Merge Request" do
13 - Merge Request 13 + New merge request
14 - if @project.snippets_enabled && can?(current_user, :write_snippet, @project) 14 - if @project.snippets_enabled && can?(current_user, :write_snippet, @project)
15 %li 15 %li
16 = link_to new_project_snippet_path(@project), title: "New Snippet" do 16 = link_to new_project_snippet_path(@project), title: "New Snippet" do
17 - Snippet 17 + New snippet
  18 + - if can?(current_user, :admin_team_member, @project)
  19 + %li
  20 + = link_to new_project_team_member_path(@project), title: "New project member" do
  21 + New project member
18 - if can? current_user, :push_code, @project 22 - if can? current_user, :push_code, @project
19 %li.divider 23 %li.divider
20 %li 24 %li
@@ -26,9 +30,4 @@ @@ -26,9 +30,4 @@
26 %i.icon-tag 30 %i.icon-tag
27 Git tag 31 Git tag
28 32
29 - - if can?(current_user, :admin_team_member, @project)  
30 - %li.divider  
31 - %li  
32 - = link_to new_project_team_member_path(@project), title: "New project member" do  
33 - Project member  
34 33
app/views/projects/_home_panel.html.haml 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +- empty_repo = @project.empty_repo?
  2 +.project-home-panel{:class => ("empty-project" if empty_repo)}
  3 + .row
  4 + .span5
  5 + %h4.project-home-title
  6 + = @project.name_with_namespace
  7 + %span.visibility-level-label
  8 + = visibility_level_icon(@project.visibility_level)
  9 + = visibility_level_label(@project.visibility_level)
  10 +
  11 + .span7
  12 + - unless empty_repo
  13 + .project-home-dropdown
  14 + = render "dropdown"
  15 + .form-horizontal
  16 + = render "shared/clone_panel"
  17 +
  18 + .project-home-extra.clearfix
  19 + .project-home-desc
  20 + - if @project.description.present?
  21 + = @project.description
  22 + - if can?(current_user, :admin_project, @project)
  23 + &ndash;
  24 + %strong= link_to 'Edit', edit_project_path
  25 +
  26 + - unless empty_repo
  27 + .project-home-links
  28 + = link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
  29 + = link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project)
  30 + = link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project)
  31 + %span.light.prepend-left-20= repository_size
0 \ No newline at end of file 32 \ No newline at end of file
app/views/projects/_visibility_level.html.haml 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +.control-group.project-visibility-level-holder
  2 + = f.label :visibility_level do
  3 + Visibility Level
  4 + = link_to "(?)", help_public_access_path
  5 + - if can_change_visibility_level
  6 + - Gitlab::VisibilityLevel.values.each do |level|
  7 + - restricted = restricted_visibility_levels.include?(level)
  8 + .controls
  9 + = f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
  10 + %span.descr{:class => ("restricted" if restricted)}
  11 + = label :project_visibility_level, level do
  12 + = visibility_level_icon(level)
  13 + %strong
  14 + = visibility_level_label(level)
  15 + .light= visibility_level_description(level)
  16 + - unless restricted_visibility_levels.empty?
  17 + .controls
  18 + %span.info
  19 + Some visibility level settings have been restricted by the administrator.
  20 + - else
  21 + .controls
  22 + %span.info
  23 + = visibility_level_icon(visibility_level)
  24 + %strong
  25 + = visibility_level_label(visibility_level)
  26 + .light= visibility_level_description(visibility_level)
app/views/projects/blob/_actions.html.haml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 - if allowed_tree_edit? 4 - if allowed_tree_edit?
5 = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-small" 5 = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-small"
6 - else 6 - else
7 - %span.btn.btn-small.disabled Edit 7 + %span.btn.btn-small.disabled edit
8 = link_to "raw", project_raw_path(@project, @id), class: "btn btn-small", target: "_blank" 8 = link_to "raw", project_raw_path(@project, @id), class: "btn btn-small", target: "_blank"
9 -# only show normal/blame view links for text files 9 -# only show normal/blame view links for text files
10 - if @blob.text? 10 - if @blob.text?
@@ -13,3 +13,7 @@ @@ -13,3 +13,7 @@
13 - else 13 - else
14 = link_to "blame", project_blame_path(@project, @id), class: "btn btn-small" unless @blob.empty? 14 = link_to "blame", project_blame_path(@project, @id), class: "btn btn-small" unless @blob.empty?
15 = link_to "history", project_commits_path(@project, @id), class: "btn btn-small" 15 = link_to "history", project_commits_path(@project, @id), class: "btn btn-small"
  16 +
  17 + - if allowed_tree_edit?
  18 + = link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do
  19 + remove
app/views/projects/blob/_remove.html.haml 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +%div#modal-remove-blob.modal.hide
  2 + .modal-header
  3 + %a.close{href: "#", "data-dismiss" => "modal"} ×
  4 + %h3.page-title Remove #{@blob.name}
  5 + %p.light
  6 + From branch
  7 + %strong= @ref
  8 +
  9 + .modal-body
  10 + = form_tag project_blob_path(@project, @id), method: :delete do
  11 + .control-group.commit_message-group
  12 + = label_tag 'commit_message', class: "control-label" do
  13 + Commit message
  14 + .controls
  15 + = text_area_tag 'commit_message', params[:commit_message], placeholder: "Removed this file because...", required: true, rows: 3
  16 + .control-group
  17 + .controls
  18 + = submit_tag 'Remove file', class: 'btn btn-remove'
  19 + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
app/views/projects/blob/show.html.haml
@@ -2,3 +2,6 @@ @@ -2,3 +2,6 @@
2 = render 'shared/ref_switcher', destination: 'blob', path: @path 2 = render 'shared/ref_switcher', destination: 'blob', path: @path
3 %div#tree-holder.tree-holder 3 %div#tree-holder.tree-holder
4 = render 'blob', blob: @blob 4 = render 'blob', blob: @blob
  5 +
  6 +- if allowed_tree_edit?
  7 + = render 'projects/blob/remove'
app/views/projects/branches/_filter.html.haml
1 %ul.nav.nav-pills.nav-stacked 1 %ul.nav.nav-pills.nav-stacked
2 = nav_link(path: 'branches#recent') do 2 = nav_link(path: 'branches#recent') do
3 - = link_to 'Recent', recent_project_branches_path(@project) 3 + = link_to recent_project_branches_path(@project) do
  4 + Recent
  5 + .pull-right
  6 + = @repository.recent_branches.count
  7 +
4 = nav_link(path: 'protected_branches#index') do 8 = nav_link(path: 'protected_branches#index') do
5 = link_to project_protected_branches_path(@project) do 9 = link_to project_protected_branches_path(@project) do
6 Protected 10 Protected
7 %i.icon-lock 11 %i.icon-lock
  12 + .pull-right
  13 + = @project.protected_branches.count
  14 +
8 = nav_link(path: 'branches#index') do 15 = nav_link(path: 'branches#index') do
9 - = link_to 'All branches', project_branches_path(@project) 16 + = link_to project_branches_path(@project) do
  17 + All branches
  18 + .pull-right
  19 + = @repository.branch_names.count
10 20
11 21
12 %hr 22 %hr
app/views/projects/commits/_commit.html.haml
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 .notes_count 7 .notes_count
8 - notes = project.notes.for_commit_id(commit.id) 8 - notes = project.notes.for_commit_id(commit.id)
9 - if notes.any? 9 - if notes.any?
10 - %span.badge.badge-info 10 + %span.label.label-gray
11 %i.icon-comment 11 %i.icon-comment
12 = notes.count 12 = notes.count
13 13
app/views/projects/commits/_diffs.html.haml
@@ -30,6 +30,10 @@ @@ -30,6 +30,10 @@
30 %strong.cgreen #{@commit.stats.additions} additions 30 %strong.cgreen #{@commit.stats.additions} additions
31 and 31 and
32 %strong.cred #{@commit.stats.deletions} deletions 32 %strong.cred #{@commit.stats.deletions} deletions
  33 + - if params[:view] == 'parallel'
  34 + = link_to "Inline Diff", url_for(view: 'inline'), {id: "commit-diff-viewtype", class: 'btn btn-tiny pull-right'}
  35 + - else
  36 + = link_to "Side-by-side Diff", url_for(view: 'parallel'), {id: "commit-diff-viewtype", class: 'btn btn-tiny pull-right'}
33 .file-stats 37 .file-stats
34 = render "projects/commits/diff_head", diffs: diffs 38 = render "projects/commits/diff_head", diffs: diffs
35 39
@@ -46,7 +50,7 @@ @@ -46,7 +50,7 @@
46 %span= diff.old_path 50 %span= diff.old_path
47 51
48 - if @commit.parent_ids.present? 52 - if @commit.parent_ids.present?
49 - = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do 53 + = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), { class: 'btn btn-small view-file' } do
50 View file @ 54 View file @
51 %span.commit-short-id= @commit.short_id(6) 55 %span.commit-short-id= @commit.short_id(6)
52 - else 56 - else
@@ -54,7 +58,7 @@ @@ -54,7 +58,7 @@
54 - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode 58 - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
55 %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" 59 %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"
56 60
57 - = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do 61 + = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), { class: 'btn btn-small view-file' } do
58 View file @ 62 View file @
59 %span.commit-short-id= @commit.short_id(6) 63 %span.commit-short-id= @commit.short_id(6)
60 64
@@ -62,7 +66,10 @@ @@ -62,7 +66,10 @@
62 -# Skipp all non non-supported blobs 66 -# Skipp all non non-supported blobs
63 - next unless file.respond_to?('text?') 67 - next unless file.respond_to?('text?')
64 - if file.text? 68 - if file.text?
65 - = render "projects/commits/text_file", diff: diff, index: i 69 + - if params[:view] == 'parallel'
  70 + = render "projects/commits/parallel_view", diff: diff, project: project, file: file, index: i
  71 + - else
  72 + = render "projects/commits/text_file", diff: diff, index: i
66 - elsif file.image? 73 - elsif file.image?
67 - old_file = project.repository.blob_at(@commit.parent_id, diff.old_path) if @commit.parent_id 74 - old_file = project.repository.blob_at(@commit.parent_id, diff.old_path) if @commit.parent_id
68 = render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i 75 = render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i
app/views/projects/commits/_parallel_view.html.haml 0 → 100644
@@ -0,0 +1,75 @@ @@ -0,0 +1,75 @@
  1 +/ Side-by-side diff view
  2 +- old_file = get_old_file(project, @commit, diff)
  3 +- deleted_lines = {}
  4 +- added_lines = {}
  5 +- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
  6 + - if type == "old"
  7 + - deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
  8 + - elsif type == "new"
  9 + - added_lines[line_new] = { line_code: line_code, type: type, line: line }
  10 +
  11 +- max_length = old_file.sloc + added_lines.length if old_file
  12 +- max_length ||= file.sloc
  13 +- offset1 = 0
  14 +- offset2 = 0
  15 +
  16 +%div.text-file-parallel
  17 + %table{ style: "table-layout: fixed;" }
  18 + - max_length.times do |line_index|
  19 + - line_index1 = line_index - offset1
  20 + - line_index2 = line_index - offset2
  21 + - deleted_line = deleted_lines[line_index1 + 1]
  22 + - added_line = added_lines[line_index2 + 1]
  23 + - old_line = old_file.lines[line_index1] if old_file
  24 + - new_line = file.lines[line_index2]
  25 +
  26 + - if deleted_line && added_line
  27 + - elsif deleted_line
  28 + - new_line = nil
  29 + - offset2 += 1
  30 + - elsif added_line
  31 + - old_line = nil
  32 + - offset1 += 1
  33 +
  34 + %tr.line_holder.parallel
  35 + - if line_index == 0 && diff.new_file
  36 + %td.line_content.parallel= "File was created"
  37 + %td.old_line= ""
  38 + - elsif deleted_line
  39 + %td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line
  40 + %td.old_line.old
  41 + = line_index1 + 1
  42 + - if @comments_allowed
  43 + =# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code]
  44 + - elsif old_line
  45 + %td.line_content.parallel= old_line
  46 + %td.old_line= line_index1 + 1
  47 + - else
  48 + %td.line_content.parallel= ""
  49 + %td.old_line= ""
  50 +
  51 + %td.diff_line= ""
  52 +
  53 + - if diff.deleted_file && line_index == 0
  54 + %td.new_line= ""
  55 + %td.line_content.parallel= "File was deleted"
  56 + - elsif added_line
  57 + %td.new_line.new
  58 + = line_index2 + 1
  59 + - if @comments_allowed
  60 + =# render "projects/notes/diff_note_link", line_code: added_line[:line_code]
  61 + %td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line
  62 + - elsif new_line
  63 + %td.new_line= line_index2 + 1
  64 + %td.line_content.parallel= new_line
  65 + - else
  66 + %td.new_line= ""
  67 + %td.line_content.parallel= ""
  68 +
  69 + - if @reply_allowed
  70 + - comments1 = []
  71 + - comments2 = []
  72 + - comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line
  73 + - comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line
  74 + - unless comments1.empty? && comments2.empty?
  75 + = render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line
0 \ No newline at end of file 76 \ No newline at end of file
app/views/projects/commits/_text_file.html.haml
@@ -21,3 +21,4 @@ @@ -21,3 +21,4 @@
21 - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at) 21 - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
22 - unless comments.empty? 22 - unless comments.empty?
23 = render "projects/notes/diff_notes_with_reply", notes: comments, line: line 23 = render "projects/notes/diff_notes_with_reply", notes: comments, line: line
  24 +
app/views/projects/commits/show.js.haml
@@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
1 -:plain  
2 - CommitsList.append(#{@commits.count}, "#{escape_javascript(render('projects/commits/commits'))}");  
3 -  
app/views/projects/edit.html.haml
@@ -29,22 +29,7 @@ @@ -29,22 +29,7 @@
29 .controls= f.select(:default_branch, @repository.branch_names, {}, {class: 'chosen'}) 29 .controls= f.select(:default_branch, @repository.branch_names, {}, {class: 'chosen'})
30 30
31 31
32 - - if can?(current_user, :change_public_mode, @project)  
33 - %fieldset.public-mode  
34 - %legend  
35 - Public mode:  
36 - .control-group  
37 - = f.label :public, class: 'control-label' do  
38 - %span Public access  
39 - .controls  
40 - = f.check_box :public  
41 - %span.descr  
42 - If checked, this project can be cloned  
43 - %em without any  
44 - authentication.  
45 - It will also be listed on the #{link_to "public access directory", public_root_path}.  
46 - %em Any  
47 - user will have #{link_to "Guest", help_permissions_path} permissions on the repository. 32 + = render "visibility_level", f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project)
48 33
49 %fieldset.features 34 %fieldset.features
50 %legend 35 %legend
@@ -124,7 +109,7 @@ @@ -124,7 +109,7 @@
124 %span Namespace 109 %span Namespace
125 .controls 110 .controls
126 .control-group 111 .control-group
127 - = f.select :namespace_id, namespaces_options(@project.namespace_id), {prompt: 'Choose a project namespace'}, {class: 'chosen'} 112 + = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'chosen' }
128 %ul 113 %ul
129 %li Be careful. Changing the project's namespace can have unintended side effects. 114 %li Be careful. Changing the project's namespace can have unintended side effects.
130 %li You can only transfer the project to namespaces you manage. 115 %li You can only transfer the project to namespaces you manage.
@@ -144,7 +129,9 @@ @@ -144,7 +129,9 @@
144 %span Path 129 %span Path
145 .controls 130 .controls
146 .control-group 131 .control-group
147 - = f.text_field :path 132 + .input-append
  133 + = f.text_field :path
  134 + %span.add-on .git
148 %ul 135 %ul
149 %li Be careful. Renaming a project's repository can have unintended side effects. 136 %li Be careful. Renaming a project's repository can have unintended side effects.
150 %li You will need to update your local repositories to point to the new location. 137 %li You will need to update your local repositories to point to the new location.
app/views/projects/empty.html.haml
1 -%h3.page-title  
2 - = @project.name_with_namespace  
3 - .form-horizontal.pull-right  
4 - = render "shared/clone_panel" 1 += render "home_panel"
5 2
6 - if @project.import? && !@project.imported 3 - if @project.import? && !@project.imported
7 .save-project-loader 4 .save-project-loader
app/views/projects/fork.html.haml
@@ -3,9 +3,9 @@ @@ -3,9 +3,9 @@
3 %i.icon-code-fork 3 %i.icon-code-fork
4 Fork Error! 4 Fork Error!
5 %p 5 %p
6 - You are trying to fork 6 + You tried to fork
7 = link_to_project @project 7 = link_to_project @project
8 - but it fails due to next reason: 8 + but it failed for the following reason:
9 9
10 10
11 - if @forked_project && @forked_project.errors.any? 11 - if @forked_project && @forked_project.errors.any?
app/views/projects/hooks/_data_ex.html.erb
@@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
1 -<% data_ex_str = <<eos  
2 -{  
3 - "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",  
4 - "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",  
5 - "ref": "refs/heads/master",  
6 - "user_id": 4,  
7 - "user_name": "John Smith",  
8 - "project_id": 15,  
9 - "repository": {  
10 - "name": "Diaspora",  
11 - "url": "git@localhost:diaspora.git",  
12 - "description": "",  
13 - "homepage": "http://localhost/diaspora",  
14 - },  
15 - "commits": [  
16 - {  
17 - "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",  
18 - "message": "Update Catalan translation to e38cb41.",  
19 - "timestamp": "2011-12-12T14:27:31+02:00",  
20 - "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",  
21 - "author": {  
22 - "name": "Jordi Mallach",  
23 - "email": "jordi@softcatala.org",  
24 - }  
25 - },  
26 - // ...  
27 - {  
28 - "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",  
29 - "message": "fixed readme",  
30 - "timestamp": "2012-01-03T23:36:29+02:00",  
31 - "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",  
32 - "author": {  
33 - "name": "GitLab dev user",  
34 - "email": "gitlabdev@dv6700.(none)",  
35 - },  
36 - },  
37 - ],  
38 - "total_commits_count": 4,  
39 -};  
40 -eos  
41 -%>  
42 -<div class="<%= user_color_scheme_class%>">  
43 - <%= raw Pygments::Lexer[:js].highlight(data_ex_str) %>  
44 -</div>  
app/views/projects/hooks/index.html.haml
1 %h3.page-title 1 %h3.page-title
2 - Post-receive hooks 2 + Web hooks
3 3
4 %p.light 4 %p.light
5 - #{link_to "Post-receive hooks ", help_web_hooks_path, class: "vlink"} can be  
6 - used for binding events when someone pushes to the repository. 5 + #{link_to "Web hooks ", help_web_hooks_path, class: "vlink"} can be
  6 + used for binding events when something happends to the the project.
7 7
8 %hr.clearfix 8 %hr.clearfix
9 9
@@ -13,23 +13,50 @@ @@ -13,23 +13,50 @@
13 - @hook.errors.full_messages.each do |msg| 13 - @hook.errors.full_messages.each do |msg|
14 %p= msg 14 %p= msg
15 .control-group 15 .control-group
16 - = f.label :url, "URL:" 16 + = f.label :url, "URL"
17 .controls 17 .controls
18 = f.text_field :url, class: "text_field input-xxlarge input-xpadding", placeholder: 'http://example.com/trigger-ci.json' 18 = f.text_field :url, class: "text_field input-xxlarge input-xpadding", placeholder: 'http://example.com/trigger-ci.json'
19 &nbsp; 19 &nbsp;
20 = f.submit "Add Web Hook", class: "btn btn-create" 20 = f.submit "Add Web Hook", class: "btn btn-create"
  21 + .control-group
  22 + = f.label :url, "Trigger"
  23 + .controls
  24 + %div
  25 + = f.check_box :push_events, class: 'pull-left'
  26 + .prepend-left-20
  27 + = f.label :push_events, class: 'list-label' do
  28 + %strong Push events
  29 + %p.light
  30 + This url will be triggered in case of push to repository
  31 + %div
  32 + = f.check_box :issues_events, class: 'pull-left'
  33 + .prepend-left-20
  34 + = f.label :issues_events, class: 'list-label' do
  35 + %strong Issues events
  36 + %p.light
  37 + This url will be triggered for created issues
  38 + %div
  39 + = f.check_box :merge_requests_events, class: 'pull-left'
  40 + .prepend-left-20
  41 + = f.label :merge_requests_events, class: 'list-label' do
  42 + %strong Merge Request events
  43 + %p.light
  44 + This url will be triggered for created merge requests
21 %hr 45 %hr
22 46
23 -if @hooks.any? 47 -if @hooks.any?
24 .ui-box 48 .ui-box
25 .title 49 .title
26 - Hooks (#{@hooks.count}) 50 + Web Hooks (#{@hooks.count})
27 %ul.well-list 51 %ul.well-list
28 - @hooks.each do |hook| 52 - @hooks.each do |hook|
29 %li 53 %li
30 - %span.badge.badge-info POST  
31 - &rarr;  
32 - %span.monospace= hook.url  
33 .pull-right 54 .pull-right
34 = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped" 55 = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped"
35 = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped" 56 = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped"
  57 + .clearfix
  58 + %span.monospace= hook.url
  59 + %p
  60 + - %w(push_events issues_events merge_requests_events).each do |trigger|
  61 + - if hook.send(trigger)
  62 + %span.label.label-gray= trigger.titleize
app/views/projects/issues/_head.html.haml
1 %ul.nav.nav-tabs 1 %ul.nav.nav-tabs
2 = nav_link(controller: :issues) do 2 = nav_link(controller: :issues) do
3 - = link_to 'Browse Issues', project_issues_path(@project), class: "tab" 3 + = link_to project_issues_path(@project), class: "tab" do
  4 + Browse Issues
  5 + - if current_controller?(:issues)
  6 + %span.badge.issue_counter #{@issues.total_count}
4 = nav_link(controller: :milestones) do 7 = nav_link(controller: :milestones) do
5 = link_to 'Milestones', project_milestones_path(@project), class: "tab" 8 = link_to 'Milestones', project_milestones_path(@project), class: "tab"
6 = nav_link(controller: :labels) do 9 = nav_link(controller: :labels) do
7 = link_to 'Labels', project_labels_path(@project), class: "tab" 10 = link_to 'Labels', project_labels_path(@project), class: "tab"
8 - - if current_user 11 +
  12 + - if current_controller?(:issues)
  13 + - if current_user
  14 + %li
  15 + = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
  16 + %i.icon-rss
  17 +
9 %li.pull-right 18 %li.pull-right
10 - = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do  
11 - %i.icon-rss 19 + .pull-right
  20 + - if can? current_user, :write_issue, @project
  21 + = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-right", title: "New Issue", id: "new_issue_link" do
  22 + %i.icon-plus
  23 + New Issue
  24 + = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-right issue-search-form' do
  25 + = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'input-xpadding issue_search input-xlarge append-right-10 search-text-input' }
app/views/projects/issues/_issues.html.haml
@@ -6,8 +6,8 @@ @@ -6,8 +6,8 @@
6 = form_tag bulk_update_project_issues_path(@project), method: :post do 6 = form_tag bulk_update_project_issues_path(@project), method: :post do
7 %span Update selected issues with &nbsp; 7 %span Update selected issues with &nbsp;
8 = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status") 8 = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status")
9 - = select_tag('update[assignee_id]', options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id]), prompt: "Assignee")  
10 - = select_tag('update[milestone_id]', options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone") 9 + = select_tag('update[assignee_id]', bulk_update_assignee_options, prompt: "Assignee")
  10 + = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
11 = hidden_field_tag 'update[issues_ids]', [] 11 = hidden_field_tag 'update[issues_ids]', []
12 = hidden_field_tag :status, params[:status] 12 = hidden_field_tag :status, params[:status]
13 = button_tag "Save", class: "btn update_selected_issues btn-small btn-save" 13 = button_tag "Save", class: "btn update_selected_issues btn-small btn-save"
@@ -78,6 +78,29 @@ @@ -78,6 +78,29 @@
78 %strong= milestone.title 78 %strong= milestone.title
79 %small.light= milestone.expires_at 79 %small.light= milestone.expires_at
80 80
  81 + .dropdown.inline.prepend-left-10
  82 + %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
  83 + %span.light sort:
  84 + - if @sort.present?
  85 + = @sort
  86 + - else
  87 + Newest
  88 + %b.caret
  89 + %ul.dropdown-menu
  90 + %li
  91 + = link_to project_filter_path(sort: 'newest') do
  92 + Newest
  93 + = link_to project_filter_path(sort: 'oldest') do
  94 + Oldest
  95 + = link_to project_filter_path(sort: 'recently_updated') do
  96 + Recently updated
  97 + = link_to project_filter_path(sort: 'last_updated') do
  98 + Last updated
  99 + = link_to project_filter_path(sort: 'milestone_due_soon') do
  100 + Milestone due soon
  101 + = link_to project_filter_path(sort: 'milestone_due_later') do
  102 + Milestone due later
  103 +
81 104
82 %ul.well-list.issues-list 105 %ul.well-list.issues-list
83 = render @issues 106 = render @issues
@@ -90,4 +113,4 @@ @@ -90,4 +113,4 @@
90 %span.issue_counter #{@issues.total_count} 113 %span.issue_counter #{@issues.total_count}
91 issues for this filter 114 issues for this filter
92 115
93 - = paginate @issues, remote: true, theme: "gitlab" 116 + = paginate @issues, theme: "gitlab"
app/views/projects/issues/index.html.haml
1 = render "head" 1 = render "head"
2 -.issues_content  
3 - %h3.page-title  
4 - Issues  
5 - %span (<span class=issue_counter>#{@issues.total_count}</span>)  
6 - .pull-right  
7 - .span6  
8 - - if can? current_user, :write_issue, @project  
9 - = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-right", title: "New Issue", id: "new_issue_link" do  
10 - %i.icon-plus  
11 - New Issue  
12 - = form_tag project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: 'pull-right' do  
13 - = hidden_field_tag :status, params[:status], id: 'search_status'  
14 - = hidden_field_tag :assignee_id, params[:assignee_id], id: 'search_assignee_id'  
15 - = hidden_field_tag :milestone_id, params[:milestone_id], id: 'search_milestone_id'  
16 - = hidden_field_tag :label_name, params[:label_name], id: 'search_label_name'  
17 - = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'input-xpadding issue_search input-xlarge append-right-10 search-text-input' }  
18 -  
19 .row 2 .row
20 .span3 3 .span3
21 = render 'shared/project_filter', project_entities_path: project_issues_path(@project) 4 = render 'shared/project_filter', project_entities_path: project_issues_path(@project)
app/views/projects/issues/index.js.haml
@@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
1 -:plain  
2 - $('.issues-holder').html("#{escape_javascript(render('issues'))}");  
3 - History.replaceState({path: "#{request.url}"}, document.title, "#{request.url}");  
4 - Issues.reload();  
app/views/projects/issues/show.html.haml
@@ -33,6 +33,8 @@ @@ -33,6 +33,8 @@
33 %h4.box-title 33 %h4.box-title
34 - if @issue.closed? 34 - if @issue.closed?
35 .state-label.state-label-red Closed 35 .state-label.state-label-red Closed
  36 + - else
  37 + .state-label.state-label-green Open
36 = gfm escape_once(@issue.title) 38 = gfm escape_once(@issue.title)
37 39
38 .ui-box-body 40 .ui-box-body
@@ -71,4 +73,4 @@ @@ -71,4 +73,4 @@
71 - @issue.participants.each do |participant| 73 - @issue.participants.each do |participant|
72 = link_to_member(@project, participant, name: false, size: 24) 74 = link_to_member(@project, participant, name: false, size: 24)
73 75
74 -.voting_notes#notes= render "projects/notes/notes_with_form" 76 -.voting_notes#notes= render "projects/notes/notes_with_form"
  77 +.voting_notes#notes= render "projects/notes/notes_with_form"
75 \ No newline at end of file 78 \ No newline at end of file
app/views/projects/merge_requests/_form.html.haml
@@ -13,7 +13,6 @@ @@ -13,7 +13,6 @@
13 = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'}) 13 = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'})
14 .pull-left 14 .pull-left
15 &nbsp; 15 &nbsp;
16 - %i.icon-code-fork  
17 = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'}) 16 = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'})
18 .mr_source_commit.prepend-top-10 17 .mr_source_commit.prepend-top-10
19 .span2 18 .span2
@@ -26,7 +25,6 @@ @@ -26,7 +25,6 @@
26 = f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span3'}) 25 = f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span3'})
27 .pull-left 26 .pull-left
28 &nbsp; 27 &nbsp;
29 - %i.icon-code-fork  
30 = f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'}) 28 = f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'})
31 .mr_target_commit.prepend-top-10 29 .mr_target_commit.prepend-top-10
32 30
app/views/projects/merge_requests/commits.js.haml
@@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
1 -:plain  
2 - merge_request.$(".commits").html("#{escape_javascript(render(partial: "commits"))}");  
3 -  
4 -  
app/views/projects/merge_requests/diffs.js.haml
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -:plain  
2 - merge_request.$(".diffs").html("#{escape_javascript(render(partial: "projects/merge_requests/show/diffs"))}");  
app/views/projects/merge_requests/show.js.haml
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -:plain  
2 - merge_request.$(".notes").html("#{escape_javascript(render "notes/notes_with_form")}");  
app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -13,13 +13,14 @@ @@ -13,13 +13,14 @@
13 .ui-box-body 13 .ui-box-body
14 %div 14 %div
15 %cite.cgray 15 %cite.cgray
16 - Created on #{@merge_request.created_at.stamp("Aug 21, 2011")} by #{link_to_member(@project, @merge_request.author)} 16 + Created on #{@merge_request.created_at.stamp("Aug 21, 2011")} by #{link_to_member(@project, @merge_request.author)}.
17 - if @merge_request.assignee 17 - if @merge_request.assignee
18 - \, currently assigned to #{link_to_member(@project, @merge_request.assignee)} 18 + Currently assigned to #{link_to_member(@project, @merge_request.assignee)}.
19 - if @merge_request.milestone 19 - if @merge_request.milestone
20 - milestone = @merge_request.milestone 20 - milestone = @merge_request.milestone
21 - %cite.cgray and attached to milestone 21 + %cite.cgray Attached to milestone
22 %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) 22 %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
  23 + \.
23 24
24 25
25 - if @merge_request.description.present? 26 - if @merge_request.description.present?
app/views/projects/new.html.haml
@@ -47,12 +47,7 @@ @@ -47,12 +47,7 @@
47 %span.light (optional) 47 %span.light (optional)
48 .controls 48 .controls
49 = f.text_area :description, placeholder: "Awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3 49 = f.text_area :description, placeholder: "Awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3
50 - .control-group.project-public-holder  
51 - = f.label :public do  
52 - %span Public project  
53 - .controls  
54 - = f.check_box :public, { checked: gitlab_config.default_projects_features.public }, true, false  
55 - %span.help-inline Make project visible to everyone 50 + = render "visibility_level", f: f, visibility_level: gitlab_config.default_projects_features.visibility_level, can_change_visibility_level: true
56 51
57 .form-actions 52 .form-actions
58 = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 53 = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +- note1 = notes1.first # example note
  2 +- note2 = notes2.first # example note
  3 +%tr.notes_holder
  4 + -# Check if line want not changed since comment was left
  5 + /- if !defined?(line1) || line1 == note1.diff_line
  6 + - if note1
  7 + %td.notes_content
  8 + %ul.notes{ rel: note1.discussion_id }
  9 + = render notes1
  10 + = render "projects/notes/discussion_reply_button", note: note1
  11 + %td.notes_line2
  12 + %span.btn.disabled.parallel-comment
  13 + %i.icon-comment
  14 + = notes1.count
  15 + - else
  16 + %td= ""
  17 + %td= ""
  18 +
  19 + %td= ""
  20 +
  21 + -# Check if line want not changed since comment was left
  22 + /- if !defined?(line2) || line2 == note2.diff_line
  23 + - if note2
  24 + %td.notes_line
  25 + %span.btn.disabled.parallel-comment
  26 + %i.icon-comment
  27 + = notes2.count
  28 + %td.notes_content
  29 + %ul.notes{ rel: note2.discussion_id }
  30 + = render notes2
  31 + = render "projects/notes/discussion_reply_button", note: note2
  32 + - else
  33 + %td= ""
  34 + %td= ""
app/views/projects/notes/_form.html.haml
@@ -7,10 +7,12 @@ @@ -7,10 +7,12 @@
7 = f.hidden_field :noteable_type 7 = f.hidden_field :noteable_type
8 8
9 .note_text_and_preview.js-toggler-container 9 .note_text_and_preview.js-toggler-container
10 - %a.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", title: "Preview", data: {url: preview_project_notes_path(@project)} } 10 + %a.btn.btn-primary.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", data: {url: preview_project_notes_path(@project)} }
11 %i.icon-eye-open 11 %i.icon-eye-open
12 - %a.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;", title: "Edit" } 12 + Preview
  13 + %a.btn.btn-primary.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;" }
13 %i.icon-edit 14 %i.icon-edit
  15 + Write
14 16
15 = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on' 17 = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on'
16 .note_preview.js-note-preview.turn-off 18 .note_preview.js-note-preview.turn-off
@@ -27,7 +29,7 @@ @@ -27,7 +29,7 @@
27 %a.btn.grouped.js-close-discussion-note-form Cancel 29 %a.btn.grouped.js-close-discussion-note-form Cancel
28 30
29 .note-form-option 31 .note-form-option
30 - %a.choose-btn.btn.btn-small.js-choose-note-attachment-button 32 + %a.choose-btn.btn.js-choose-note-attachment-button
31 %i.icon-paper-clip 33 %i.icon-paper-clip
32 %span Choose File ... 34 %span Choose File ...
33 &nbsp; 35 &nbsp;
app/views/projects/notes/index.js.haml
@@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
1 -- unless @notes.blank?  
2 - var notesHtml = "#{escape_javascript(render 'projects/notes/notes')}";  
3 - - new_note_ids = @notes.map(&:id)  
4 - NoteList.setContent(#{new_note_ids}, notesHtml);  
app/views/projects/services/_form.html.haml
1 %h3.page-title 1 %h3.page-title
2 - - if @service.activated?  
3 - %span.cgreen  
4 - %i.icon-circle  
5 - - else  
6 - %span.cgray  
7 - %i.icon-circle-blank  
8 = @service.title 2 = @service.title
  3 + = boolean_to_icon @service.activated?
9 4
10 %p= @service.description 5 %p= @service.description
11 6
app/views/projects/services/index.html.haml
@@ -6,12 +6,8 @@ @@ -6,12 +6,8 @@
6 - @services.each do |service| 6 - @services.each do |service|
7 %li 7 %li
8 %h4 8 %h4
9 - - if service.activated?  
10 - %span.cgreen  
11 - %i.icon-circle  
12 - - else  
13 - %span.cgray  
14 - %i.icon-circle-blank  
15 = link_to edit_project_service_path(@project, service.to_param) do 9 = link_to edit_project_service_path(@project, service.to_param) do
16 = service.title 10 = service.title
  11 + .pull-right
  12 + = boolean_to_icon service.activated?
17 %p= service.description 13 %p= service.description
app/views/projects/show.html.haml
1 -.project-home-panel  
2 - .row  
3 - .span4  
4 - %h4.project-home-title  
5 - = @project.name_with_namespace  
6 - - if @project.public  
7 - %span.public-label Public  
8 - - else  
9 - %span.public-label Private  
10 -  
11 - .span8  
12 - .project-home-dropdown  
13 - = render "dropdown"  
14 - .form-horizontal  
15 - = render "shared/clone_panel"  
16 -  
17 - .project-home-extra.clearfix  
18 - .project-home-desc  
19 - - if @project.description.present?  
20 - = @project.description  
21 - - if can?(current_user, :admin_project, @project)  
22 - &ndash;  
23 - %strong= link_to 'Edit', edit_project_path  
24 -  
25 - .project-home-links  
26 - = link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref)  
27 - = link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project)  
28 - = link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project)  
29 - %span.light.prepend-left-20= repository_size 1 += render "home_panel"
30 2
31 .row 3 .row
32 .span9 4 .span9
@@ -34,19 +6,20 @@ @@ -34,19 +6,20 @@
34 = render 'shared/event_filter' 6 = render 'shared/event_filter'
35 .content_list 7 .content_list
36 .loading.hide 8 .loading.hide
37 - .span3 9 + .span3.project-side
38 .clearfix 10 .clearfix
39 - if @project.forked_from_project 11 - if @project.forked_from_project
40 .alert.alert-success 12 .alert.alert-success
41 - %i.icon-code-fork 13 + %i.icon-code-fork.project-fork-icon
42 Forked from: 14 Forked from:
  15 + %br
43 = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) 16 = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
44 - unless @project.empty_repo? 17 - unless @project.empty_repo?
45 - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace 18 - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
46 - if current_user.already_forked?(@project) 19 - if current_user.already_forked?(@project)
47 = link_to project_path(current_user.fork_of(@project)), class: 'btn btn-block' do 20 = link_to project_path(current_user.fork_of(@project)), class: 'btn btn-block' do
48 - %i.icon-ok  
49 - Already forked 21 + %i.icon-compass
  22 + Go to fork
50 - else 23 - else
51 = link_to fork_project_path(@project), title: "Fork", class: "btn btn-block", method: "POST" do 24 = link_to fork_project_path(@project), title: "Fork", class: "btn btn-block", method: "POST" do
52 %i.icon-code-fork 25 %i.icon-code-fork
@@ -56,8 +29,15 @@ @@ -56,8 +29,15 @@
56 = link_to archive_project_repository_path(@project), class: "btn btn-block" do 29 = link_to archive_project_repository_path(@project), class: "btn btn-block" do
57 %i.icon-download-alt 30 %i.icon-download-alt
58 %span Download 31 %span Download
59 - %br  
60 - .light-well 32 + = link_to project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do
  33 + Compare code
  34 +
  35 + - if @repository.readme
  36 + - readme = @repository.readme
  37 + = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)), class: 'btn btn-block' do
  38 + = readme.name
  39 +
  40 + .prepend-top-10
61 %p 41 %p
62 %span.light Created on 42 %span.light Created on
63 #{@project.created_at.stamp('Aug 22, 2013')} 43 #{@project.created_at.stamp('Aug 22, 2013')}
app/views/projects/show.js.haml
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -:plain  
2 - Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");  
app/views/public/projects/index.html.haml
@@ -19,6 +19,10 @@ @@ -19,6 +19,10 @@
19 %h4 19 %h4
20 = link_to project_path(project) do 20 = link_to project_path(project) do
21 = project.name_with_namespace 21 = project.name_with_namespace
  22 + - if project.internal?
  23 + %small.access-icon
  24 + = internal_icon
  25 + Internal
22 .pull-right 26 .pull-right
23 %pre.public-clone git clone #{project.http_url_to_repo} 27 %pre.public-clone git clone #{project.http_url_to_repo}
24 28
app/views/shared/_clone_panel.html.haml
1 .git-clone-holder 1 .git-clone-holder
2 - %button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH  
3 - %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= gitlab_config.protocol.upcase  
4 - = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5", readonly: true 2 + %button{class: "btn #{ current_user ? 'active' : '' }", :"data-clone" => @project.ssh_url_to_repo} SSH
  3 + %button{class: "btn #{ current_user ? '' : 'active' }", :"data-clone" => @project.http_url_to_repo}= gitlab_config.protocol.upcase
  4 + = text_field_tag :project_clone, (current_user ? @project.url_to_repo : @project.http_url_to_repo), class: "one_click_select span5", readonly: true
app/views/shared/_merge_requests.html.haml
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 - project = group[0] 4 - project = group[0]
5 .title 5 .title
6 = link_to_project project 6 = link_to_project project
  7 + = link_to 'show all', project_merge_requests_path(project), class: 'pull-right'
7 %ul.well-list.mr-list 8 %ul.well-list.mr-list
8 - group[1].each do |merge_request| 9 - group[1].each do |merge_request|
9 = render 'projects/merge_requests/merge_request', merge_request: merge_request 10 = render 'projects/merge_requests/merge_request', merge_request: merge_request
app/views/shared/_no_ssh.html.haml
1 -- if current_user.require_ssh_key? && alert.blank? && notice.blank?  
2 - %p.error-message.centered  
3 - You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile 1 +- if cookies[:hide_no_ssh_message].blank? && current_user.require_ssh_key?
  2 + .no-ssh-key-message
  3 + .container
  4 + You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile
  5 + = link_to '#', class: 'pull-right hide-no-ssh-message' do
  6 + %i.icon-remove
app/views/snippets/_blob.html.haml
@@ -8,9 +8,18 @@ @@ -8,9 +8,18 @@
8 = link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-tiny", title: 'Edit Snippet' 8 = link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-tiny", title: 'Edit Snippet'
9 = link_to "Delete", snippet_path(@snippet), method: :delete, confirm: "Are you sure?", class: "btn btn-tiny", title: 'Delete Snippet' 9 = link_to "Delete", snippet_path(@snippet), method: :delete, confirm: "Are you sure?", class: "btn btn-tiny", title: 'Delete Snippet'
10 = link_to "Raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank" 10 = link_to "Raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank"
11 - .file-content.code  
12 - - unless @snippet.content.empty?  
13 - %div{class: user_color_scheme_class}  
14 - = raw @snippet.colorize(formatter: :gitlab) 11 + - unless @snippet.content.empty?
  12 + - if gitlab_markdown?(@snippet.file_name)
  13 + .file-content.wiki
  14 + = preserve do
  15 + = markdown(@snippet.data)
  16 + - elsif markup?(@snippet.file_name)
  17 + .file-content.wiki
  18 + = raw GitHub::Markup.render(@snippet.file_name, @snippet.data)
15 - else 19 - else
  20 + .file-content.code
  21 + %div{class: user_color_scheme_class}
  22 + = raw @snippet.colorize(formatter: :gitlab)
  23 + - else
  24 + .file-content.code
16 %p.nothing_here_message Empty file 25 %p.nothing_here_message Empty file
app/views/snippets/_form.html.haml
@@ -13,9 +13,20 @@ @@ -13,9 +13,20 @@
13 = f.label :title 13 = f.label :title
14 .controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true 14 .controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true
15 .control-group 15 .control-group
16 - = f.label "Private?" 16 + = f.label "Access"
17 .controls 17 .controls
18 - = f.check_box :private, {class: ''} 18 + = f.label :private_true, class: 'radio-label' do
  19 + = f.radio_button :private, true
  20 + %span
  21 + %strong Private
  22 + (only you can see this snippet)
  23 + %br
  24 + = f.label :private_false, class: 'radio-label' do
  25 + = f.radio_button :private, false
  26 + %span
  27 + %strong Public
  28 + (GitLab users can can see this snippet)
  29 +
19 .control-group 30 .control-group
20 .file-editor 31 .file-editor
21 = f.label :file_name, "File" 32 = f.label :file_name, "File"
@@ -33,9 +44,10 @@ @@ -33,9 +44,10 @@
33 - else 44 - else
34 = f.submit 'Save', class: "btn-save btn" 45 = f.submit 'Save', class: "btn-save btn"
35 46
36 - = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"  
37 - unless @snippet.new_record? 47 - unless @snippet.new_record?
38 - .pull-right= link_to 'Destroy', snippet_path(@snippet), confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" 48 + .pull-right.prepend-left-20
  49 + = link_to 'Remove', snippet_path(@snippet), confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn btn-remove delete-snippet", id: "destroy_snippet_#{@snippet.id}"
  50 + = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
39 51
40 52
41 :javascript 53 :javascript
app/views/snippets/_snippet.html.haml
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 = link_to reliable_snippet_path(snippet) do 3 = link_to reliable_snippet_path(snippet) do
4 = truncate(snippet.title, length: 60) 4 = truncate(snippet.title, length: 60)
5 - if snippet.private? 5 - if snippet.private?
6 - %span.label.label-success 6 + %span.label.label-gray
7 %i.icon-lock 7 %i.icon-lock
8 private 8 private
9 %span.cgray.monospace.tiny.pull-right 9 %span.cgray.monospace.tiny.pull-right
app/workers/repository_import_worker.rb
@@ -14,7 +14,6 @@ class RepositoryImportWorker @@ -14,7 +14,6 @@ class RepositoryImportWorker
14 project.imported = true 14 project.imported = true
15 project.save 15 project.save
16 project.satellite.create unless project.satellite.exists? 16 project.satellite.create unless project.satellite.exists?
17 - project.discover_default_branch  
18 else 17 else
19 project.imported = false 18 project.imported = false
20 end 19 end
config/application.rb
@@ -70,7 +70,7 @@ module Gitlab @@ -70,7 +70,7 @@ module Gitlab
70 config.assets.version = '1.0' 70 config.assets.version = '1.0'
71 71
72 # Uncomment and customize the last line to run in a non-root path 72 # Uncomment and customize the last line to run in a non-root path
73 - # WARNING: This feature is known to work, but unsupported 73 + # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
74 # Note that four settings need to be changed for this to work. 74 # Note that four settings need to be changed for this to work.
75 # 1) In your application.rb file: config.relative_url_root = "/gitlab" 75 # 1) In your application.rb file: config.relative_url_root = "/gitlab"
76 # 2) In your gitlab.yml file: relative_url_root: /gitlab 76 # 2) In your gitlab.yml file: relative_url_root: /gitlab
@@ -80,7 +80,14 @@ module Gitlab @@ -80,7 +80,14 @@ module Gitlab
80 # 80 #
81 # config.relative_url_root = "/gitlab" 81 # config.relative_url_root = "/gitlab"
82 82
83 - # Uncomment to enable rack attack middleware  
84 - # config.middleware.use Rack::Attack 83 + config.middleware.use Rack::Attack
  84 +
  85 + # Allow access to GitLab API from other domains
  86 + config.middleware.use Rack::Cors do
  87 + allow do
  88 + origins '*'
  89 + resource '/api/*', headers: :any, methods: [:get, :post, :options, :put]
  90 + end
  91 + end
85 end 92 end
86 end 93 end
config/database.yml.mysql
@@ -7,7 +7,7 @@ production: @@ -7,7 +7,7 @@ production:
7 reconnect: false 7 reconnect: false
8 database: gitlabhq_production 8 database: gitlabhq_production
9 pool: 10 9 pool: 10
10 - username: gitlab 10 + username: git
11 password: "secure password" 11 password: "secure password"
12 # host: localhost 12 # host: localhost
13 # socket: /tmp/mysql.sock 13 # socket: /tmp/mysql.sock
config/gitlab.yml.example
@@ -20,7 +20,7 @@ production: &amp;base @@ -20,7 +20,7 @@ production: &amp;base
20 https: false 20 https: false
21 21
22 # Uncomment and customize the last line to run in a non-root path 22 # Uncomment and customize the last line to run in a non-root path
23 - # WARNING: This feature is known to work, but unsupported 23 + # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
24 # Note that four settings need to be changed for this to work. 24 # Note that four settings need to be changed for this to work.
25 # 1) In your application.rb file: config.relative_url_root = "/gitlab" 25 # 1) In your application.rb file: config.relative_url_root = "/gitlab"
26 # 2) In your gitlab.yml file: relative_url_root: /gitlab 26 # 2) In your gitlab.yml file: relative_url_root: /gitlab
@@ -57,11 +57,15 @@ production: &amp;base @@ -57,11 +57,15 @@ production: &amp;base
57 # default: false - Account passwords are not sent via the email if signup is enabled. 57 # default: false - Account passwords are not sent via the email if signup is enabled.
58 # signup_enabled: true 58 # signup_enabled: true
59 59
  60 + # Restrict setting visibility levels for non-admin users.
  61 + # The default is to allow all levels.
  62 + #restricted_visibility_levels: [ "public" ]
  63 +
60 ## Automatic issue closing 64 ## Automatic issue closing
61 # If a commit message matches this regular expression, all issues referenced from the matched text will be closed. 65 # If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
62 - # This happends when the commit is pushed or merged into the default branch of a project. 66 + # This happens when the commit is pushed or merged into the default branch of a project.
63 # When not specified the default issue_closing_pattern as specified below will be used. 67 # When not specified the default issue_closing_pattern as specified below will be used.
64 - # issue_closing_pattern: ([Cc]loses|[Ff]ixes) +#\d+ 68 + # issue_closing_pattern: ([Cc]lose[sd]|[Ff]ixe[sd]) +#\d+
65 69
66 ## Default project features settings 70 ## Default project features settings
67 default_projects_features: 71 default_projects_features:
@@ -70,7 +74,7 @@ production: &amp;base @@ -70,7 +74,7 @@ production: &amp;base
70 wiki: true 74 wiki: true
71 wall: false 75 wall: false
72 snippets: false 76 snippets: false
73 - public: false 77 + visibility_level: "private" # can be "private" | "internal" | "public"
74 78
75 ## External issues trackers 79 ## External issues trackers
76 issues_tracker: 80 issues_tracker:
@@ -112,6 +116,8 @@ production: &amp;base @@ -112,6 +116,8 @@ production: &amp;base
112 # ========================== 116 # ==========================
113 117
114 ## LDAP settings 118 ## LDAP settings
  119 + # You can inspect the first 100 LDAP users with login access by running:
  120 + # bundle exec rake gitlab:ldap:check[100] RAILS_ENV=production
115 ldap: 121 ldap:
116 enabled: false 122 enabled: false
117 host: '_your_ldap_server' 123 host: '_your_ldap_server'
@@ -138,7 +144,7 @@ production: &amp;base @@ -138,7 +144,7 @@ production: &amp;base
138 ## Auth providers 144 ## Auth providers
139 # Uncomment the following lines and fill in the data of the auth provider you want to use 145 # Uncomment the following lines and fill in the data of the auth provider you want to use
140 # If your favorite auth provider is not listed you can use others: 146 # If your favorite auth provider is not listed you can use others:
141 - # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers 147 + # see https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-custom-omniauth-provider-configurations
142 # The 'app_id' and 'app_secret' parameters are always passed as the first two 148 # The 'app_id' and 'app_secret' parameters are always passed as the first two
143 # arguments, followed by optional 'args' which can be either a hash or an array. 149 # arguments, followed by optional 'args' which can be either a hash or an array.
144 providers: 150 providers:
config/initializers/1_settings.rb
@@ -30,6 +30,29 @@ class Settings &lt; Settingslogic @@ -30,6 +30,29 @@ class Settings &lt; Settingslogic
30 gitlab.relative_url_root 30 gitlab.relative_url_root
31 ].join('') 31 ].join('')
32 end 32 end
  33 +
  34 + # check that values in `current` (string or integer) is a contant in `modul`.
  35 + def verify_constant_array(modul, current, default)
  36 + values = default || []
  37 + if !current.nil?
  38 + values = []
  39 + current.each do |constant|
  40 + values.push(verify_constant(modul, constant, nil))
  41 + end
  42 + values.delete_if { |value| value.nil? }
  43 + end
  44 + values
  45 + end
  46 +
  47 + # check that `current` (string or integer) is a contant in `modul`.
  48 + def verify_constant(modul, current, default)
  49 + constant = modul.constants.find{ |name| modul.const_get(name) == current }
  50 + value = constant.nil? ? default : modul.const_get(constant)
  51 + if current.is_a? String
  52 + value = modul.const_get(current.upcase) rescue default
  53 + end
  54 + value
  55 + end
33 end 56 end
34 end 57 end
35 58
@@ -68,6 +91,7 @@ rescue ArgumentError # no user configured @@ -68,6 +91,7 @@ rescue ArgumentError # no user configured
68 '/home/' + Settings.gitlab['user'] 91 '/home/' + Settings.gitlab['user']
69 end 92 end
70 Settings.gitlab['signup_enabled'] ||= false 93 Settings.gitlab['signup_enabled'] ||= false
  94 +Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
71 Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? 95 Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
72 Settings.gitlab['issue_closing_pattern'] = '([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil? 96 Settings.gitlab['issue_closing_pattern'] = '([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
73 Settings.gitlab['default_projects_features'] ||= {} 97 Settings.gitlab['default_projects_features'] ||= {}
@@ -76,7 +100,7 @@ Settings.gitlab.default_projects_features[&#39;merge_requests&#39;] = true if Settings.g @@ -76,7 +100,7 @@ Settings.gitlab.default_projects_features[&#39;merge_requests&#39;] = true if Settings.g
76 Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? 100 Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
77 Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil? 101 Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil?
78 Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? 102 Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
79 -Settings.gitlab.default_projects_features['public'] = false if Settings.gitlab.default_projects_features['public'].nil? 103 +Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
80 104
81 # 105 #
82 # Gravatar 106 # Gravatar
config/initializers/devise.rb
@@ -74,8 +74,8 @@ Devise.setup do |config| @@ -74,8 +74,8 @@ Devise.setup do |config|
74 # config.pepper = "2ef62d549c4ff98a5d3e0ba211e72cff592060247e3bbbb9f499af1222f876f53d39b39b823132affb32858168c79c1d7741d26499901b63c6030a42129924ef" 74 # config.pepper = "2ef62d549c4ff98a5d3e0ba211e72cff592060247e3bbbb9f499af1222f876f53d39b39b823132affb32858168c79c1d7741d26499901b63c6030a42129924ef"
75 75
76 # ==> Configuration for :confirmable 76 # ==> Configuration for :confirmable
77 - # The time you want to give your user to confirm his account. During this time  
78 - # he will be able to access your application without confirming. Default is 0.days 77 + # The time you want to give a user to confirm their account. During this time
  78 + # they will be able to access your application without confirming. Default is 0.days
79 # When confirm_within is zero, the user won't be able to sign in without confirming. 79 # When confirm_within is zero, the user won't be able to sign in without confirming.
80 # You can use this to let your user access some features of your application 80 # You can use this to let your user access some features of your application
81 # without confirming the account, but blocking it after a certain period 81 # without confirming the account, but blocking it after a certain period
@@ -101,7 +101,7 @@ Devise.setup do |config| @@ -101,7 +101,7 @@ Devise.setup do |config|
101 101
102 # ==> Configuration for :validatable 102 # ==> Configuration for :validatable
103 # Range for password length. Default is 6..128. 103 # Range for password length. Default is 6..128.
104 - config.password_length = 6..128 104 + config.password_length = 8..128
105 105
106 # Email regex used to validate email formats. It simply asserts that 106 # Email regex used to validate email formats. It simply asserts that
107 # an one (and only one) @ exists in the given string. This is mainly 107 # an one (and only one) @ exists in the given string. This is mainly
config/initializers/rack_attack.rb.example
1 -# To enable rack-attack for your GitLab instance do the following:  
2 -# 1. In config/application.rb find and uncomment the following line:  
3 -# config.middleware.use Rack::Attack  
4 -# 2. Rename this file to rack_attack.rb  
5 -# 3. Review the paths_to_be_protected and add any other path you need protecting  
6 -# 4. Restart GitLab instance 1 +# 1. Rename this file to rack_attack.rb
  2 +# 2. Review the paths_to_be_protected and add any other path you need protecting
7 # 3 #
8 4
9 paths_to_be_protected = [ 5 paths_to_be_protected = [
10 "#{Rails.application.config.relative_url_root}/users/password", 6 "#{Rails.application.config.relative_url_root}/users/password",
11 "#{Rails.application.config.relative_url_root}/users/sign_in", 7 "#{Rails.application.config.relative_url_root}/users/sign_in",
  8 + "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session.json",
  9 + "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session",
12 "#{Rails.application.config.relative_url_root}/users" 10 "#{Rails.application.config.relative_url_root}/users"
13 ] 11 ]
14 -Rack::Attack.throttle('protected paths', limit: 6, period: 60.seconds) do |req|  
15 - req.ip if paths_to_be_protected.include?(req.path) && req.post? 12 +
  13 +unless Rails.env.test?
  14 + Rack::Attack.throttle('protected paths', limit: 10, period: 60.seconds) do |req|
  15 + req.ip if paths_to_be_protected.include?(req.path) && req.post?
  16 + end
16 end 17 end
config/routes.rb
@@ -86,9 +86,16 @@ Gitlab::Application.routes.draw do @@ -86,9 +86,16 @@ Gitlab::Application.routes.draw do
86 get :test 86 get :test
87 end 87 end
88 88
  89 + resources :broadcast_messages, only: [:index, :create, :destroy]
89 resource :logs, only: [:show] 90 resource :logs, only: [:show]
90 resource :background_jobs, controller: 'background_jobs', only: [:show] 91 resource :background_jobs, controller: 'background_jobs', only: [:show]
91 - resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] 92 +
  93 + resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do
  94 + member do
  95 + put :transfer
  96 + end
  97 + end
  98 +
92 root to: "dashboard#index" 99 root to: "dashboard#index"
93 end 100 end
94 101
@@ -120,6 +127,7 @@ Gitlab::Application.routes.draw do @@ -120,6 +127,7 @@ Gitlab::Application.routes.draw do
120 delete :leave 127 delete :leave
121 end 128 end
122 end 129 end
  130 + resource :avatar, only: [:destroy]
123 end 131 end
124 end 132 end
125 133
@@ -166,7 +174,7 @@ Gitlab::Application.routes.draw do @@ -166,7 +174,7 @@ Gitlab::Application.routes.draw do
166 end 174 end
167 175
168 scope module: :projects do 176 scope module: :projects do
169 - resources :blob, only: [:show], constraints: {id: /.+/} 177 + resources :blob, only: [:show, :destroy], constraints: {id: /.+/}
170 resources :raw, only: [:show], constraints: {id: /.+/} 178 resources :raw, only: [:show], constraints: {id: /.+/}
171 resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } 179 resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
172 resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' 180 resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
config/unicorn.rb.example
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 # documentation. 9 # documentation.
10 10
11 # Uncomment and customize the last line to run in a non-root path 11 # Uncomment and customize the last line to run in a non-root path
12 -# WARNING: This feature is known to work, but unsupported 12 +# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
13 # Note that four settings need to be changed for this to work. 13 # Note that four settings need to be changed for this to work.
14 # 1) In your application.rb file: config.relative_url_root = "/gitlab" 14 # 1) In your application.rb file: config.relative_url_root = "/gitlab"
15 # 2) In your gitlab.yml file: relative_url_root: /gitlab 15 # 2) In your gitlab.yml file: relative_url_root: /gitlab
db/migrate/20131106151520_remove_default_branch.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class RemoveDefaultBranch < ActiveRecord::Migration
  2 + def up
  3 + remove_column :projects, :default_branch
  4 + end
  5 +
  6 + def down
  7 + add_column :projects, :default_branch, :string
  8 + end
  9 +end
db/migrate/20131112114325_create_broadcast_messages.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +class CreateBroadcastMessages < ActiveRecord::Migration
  2 + def change
  3 + create_table :broadcast_messages do |t|
  4 + t.text :message, null: false
  5 + t.datetime :starts_at
  6 + t.datetime :ends_at
  7 + t.integer :alert_type
  8 +
  9 + t.timestamps
  10 + end
  11 + end
  12 +end
db/migrate/20131112220935_add_visibility_level_to_projects.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class AddVisibilityLevelToProjects < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :projects, :visibility_level, :integer, :default => 0, :null => false
  4 + Project.where(public: true).update_all(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  5 + remove_column :projects, :public
  6 + end
  7 +
  8 + def self.down
  9 + add_column :projects, :public, :boolean, :default => false, :null => false
  10 + Project.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).update_all(public: true)
  11 + remove_column :projects, :visibility_level
  12 + end
  13 +end
db/migrate/20131202192556_add_event_fields_for_web_hook.rb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +class AddEventFieldsForWebHook < ActiveRecord::Migration
  2 + def change
  3 + add_column :web_hooks, :push_events, :boolean, default: true, null: false
  4 + add_column :web_hooks, :issues_events, :boolean, default: false, null: false
  5 + add_column :web_hooks, :merge_requests_events, :boolean, default: false, null: false
  6 + end
  7 +end
@@ -11,7 +11,16 @@ @@ -11,7 +11,16 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
13 13
14 -ActiveRecord::Schema.define(:version => 20131009115346) do 14 +ActiveRecord::Schema.define(:version => 20131202192556) do
  15 +
  16 + create_table "broadcast_messages", :force => true do |t|
  17 + t.text "message", :null => false
  18 + t.datetime "starts_at"
  19 + t.datetime "ends_at"
  20 + t.integer "alert_type"
  21 + t.datetime "created_at", :null => false
  22 + t.datetime "updated_at", :null => false
  23 + end
15 24
16 create_table "deploy_keys_projects", :force => true do |t| 25 create_table "deploy_keys_projects", :force => true do |t|
17 t.integer "deploy_key_id", :null => false 26 t.integer "deploy_key_id", :null => false
@@ -171,19 +180,18 @@ ActiveRecord::Schema.define(:version =&gt; 20131009115346) do @@ -171,19 +180,18 @@ ActiveRecord::Schema.define(:version =&gt; 20131009115346) do
171 t.datetime "created_at", :null => false 180 t.datetime "created_at", :null => false
172 t.datetime "updated_at", :null => false 181 t.datetime "updated_at", :null => false
173 t.integer "creator_id" 182 t.integer "creator_id"
174 - t.string "default_branch"  
175 t.boolean "issues_enabled", :default => true, :null => false 183 t.boolean "issues_enabled", :default => true, :null => false
176 t.boolean "wall_enabled", :default => true, :null => false 184 t.boolean "wall_enabled", :default => true, :null => false
177 t.boolean "merge_requests_enabled", :default => true, :null => false 185 t.boolean "merge_requests_enabled", :default => true, :null => false
178 t.boolean "wiki_enabled", :default => true, :null => false 186 t.boolean "wiki_enabled", :default => true, :null => false
179 t.integer "namespace_id" 187 t.integer "namespace_id"
180 - t.boolean "public", :default => false, :null => false  
181 t.string "issues_tracker", :default => "gitlab", :null => false 188 t.string "issues_tracker", :default => "gitlab", :null => false
182 t.string "issues_tracker_id" 189 t.string "issues_tracker_id"
183 t.boolean "snippets_enabled", :default => true, :null => false 190 t.boolean "snippets_enabled", :default => true, :null => false
184 t.datetime "last_activity_at" 191 t.datetime "last_activity_at"
185 t.boolean "imported", :default => false, :null => false 192 t.boolean "imported", :default => false, :null => false
186 t.string "import_url" 193 t.string "import_url"
  194 + t.integer "visibility_level", :default => 0, :null => false
187 end 195 end
188 196
189 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" 197 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
@@ -326,10 +334,13 @@ ActiveRecord::Schema.define(:version =&gt; 20131009115346) do @@ -326,10 +334,13 @@ ActiveRecord::Schema.define(:version =&gt; 20131009115346) do
326 create_table "web_hooks", :force => true do |t| 334 create_table "web_hooks", :force => true do |t|
327 t.string "url" 335 t.string "url"
328 t.integer "project_id" 336 t.integer "project_id"
329 - t.datetime "created_at", :null => false  
330 - t.datetime "updated_at", :null => false  
331 - t.string "type", :default => "ProjectHook" 337 + t.datetime "created_at", :null => false
  338 + t.datetime "updated_at", :null => false
  339 + t.string "type", :default => "ProjectHook"
332 t.integer "service_id" 340 t.integer "service_id"
  341 + t.boolean "push_events", :default => true, :null => false
  342 + t.boolean "issues_events", :default => false, :null => false
  343 + t.boolean "merge_requests_events", :default => false, :null => false
333 end 344 end
334 345
335 add_index "web_hooks", ["project_id"], :name => "index_web_hooks_on_project_id" 346 add_index "web_hooks", ["project_id"], :name => "index_web_hooks_on_project_id"
doc/api/projects.md
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 2
3 ### List projects 3 ### List projects
4 4
5 -Get a list of projects owned by the authenticated user. 5 +Get a list of projects accessible by the authenticated user.
6 6
7 ``` 7 ```
8 GET /projects 8 GET /projects
@@ -15,6 +15,7 @@ GET /projects @@ -15,6 +15,7 @@ GET /projects
15 "description": null, 15 "description": null,
16 "default_branch": "master", 16 "default_branch": "master",
17 "public": false, 17 "public": false,
  18 + "visibility_level": 0,
18 "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git", 19 "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
19 "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git", 20 "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
20 "web_url": "http://example.com/diaspora/diaspora-client", 21 "web_url": "http://example.com/diaspora/diaspora-client",
@@ -49,6 +50,7 @@ GET /projects @@ -49,6 +50,7 @@ GET /projects
49 "description": null, 50 "description": null,
50 "default_branch": "master", 51 "default_branch": "master",
51 "public": false, 52 "public": false,
  53 + "visibility_level": 0,
52 "ssh_url_to_repo": "git@example.com:brightbox/puppet.git", 54 "ssh_url_to_repo": "git@example.com:brightbox/puppet.git",
53 "http_url_to_repo": "http://example.com/brightbox/puppet.git", 55 "http_url_to_repo": "http://example.com/brightbox/puppet.git",
54 "web_url": "http://example.com/brightbox/puppet", 56 "web_url": "http://example.com/brightbox/puppet",
@@ -82,6 +84,22 @@ GET /projects @@ -82,6 +84,22 @@ GET /projects
82 ``` 84 ```
83 85
84 86
  87 +#### List owned projects
  88 +
  89 +Get a list of projects owned by the authenticated user.
  90 +
  91 +```
  92 +GET /projects/owned
  93 +```
  94 +
  95 +#### List ALL projects
  96 +
  97 +Get a list of all GitLab projects (admin only).
  98 +
  99 +```
  100 +GET /projects/all
  101 +```
  102 +
85 ### Get single project 103 ### Get single project
86 104
87 Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user. 105 Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user.
@@ -101,6 +119,7 @@ Parameters: @@ -101,6 +119,7 @@ Parameters:
101 "description": null, 119 "description": null,
102 "default_branch": "master", 120 "default_branch": "master",
103 "public": false, 121 "public": false,
  122 + "visibility_level": 0,
104 "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git", 123 "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
105 "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git", 124 "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
106 "web_url": "http://example.com/diaspora/diaspora-project-site", 125 "web_url": "http://example.com/diaspora/diaspora-project-site",
@@ -213,13 +232,13 @@ Parameters: @@ -213,13 +232,13 @@ Parameters:
213 232
214 + `name` (required) - new project name 233 + `name` (required) - new project name
215 + `description` (optional) - short project description 234 + `description` (optional) - short project description
216 -+ `default_branch` (optional) - 'master' by default  
217 + `issues_enabled` (optional) 235 + `issues_enabled` (optional)
218 + `wall_enabled` (optional) 236 + `wall_enabled` (optional)
219 + `merge_requests_enabled` (optional) 237 + `merge_requests_enabled` (optional)
220 + `wiki_enabled` (optional) 238 + `wiki_enabled` (optional)
221 + `snippets_enabled` (optional) 239 + `snippets_enabled` (optional)
222 -+ `public` (optional) 240 ++ `public` (optional) - if `true` same as setting visibility_level = 20
  241 ++ `visibility_level` (optional)
223 242
224 243
225 ### Create project for user 244 ### Create project for user
@@ -241,7 +260,8 @@ Parameters: @@ -241,7 +260,8 @@ Parameters:
241 + `merge_requests_enabled` (optional) 260 + `merge_requests_enabled` (optional)
242 + `wiki_enabled` (optional) 261 + `wiki_enabled` (optional)
243 + `snippets_enabled` (optional) 262 + `snippets_enabled` (optional)
244 -+ `public` (optional) 263 ++ `public` (optional) - if `true` same as setting visibility_level = 20
  264 ++ `visibility_level` (optional)
245 265
246 266
247 ## Remove project 267 ## Remove project
@@ -382,6 +402,10 @@ Parameters: @@ -382,6 +402,10 @@ Parameters:
382 { 402 {
383 "id": 1, 403 "id": 1,
384 "url": "http://example.com/hook", 404 "url": "http://example.com/hook",
  405 + "project_id": 3,
  406 + "push_events": "true",
  407 + "issues_events": "true",
  408 + "merge_requests_events": "true",
385 "created_at": "2012-10-12T17:04:47Z" 409 "created_at": "2012-10-12T17:04:47Z"
386 } 410 }
387 ``` 411 ```
@@ -399,6 +423,9 @@ Parameters: @@ -399,6 +423,9 @@ Parameters:
399 423
400 + `id` (required) - The ID or NAME of a project 424 + `id` (required) - The ID or NAME of a project
401 + `url` (required) - The hook URL 425 + `url` (required) - The hook URL
  426 ++ `push_events` - Trigger hook on push events
  427 ++ `issues_events` - Trigger hook on issues events
  428 ++ `merge_requests_events` - Trigger hook on merge_requests events
402 429
403 430
404 ### Edit project hook 431 ### Edit project hook
@@ -414,6 +441,9 @@ Parameters: @@ -414,6 +441,9 @@ Parameters:
414 + `id` (required) - The ID or NAME of a project 441 + `id` (required) - The ID or NAME of a project
415 + `hook_id` (required) - The ID of a project hook 442 + `hook_id` (required) - The ID of a project hook
416 + `url` (required) - The hook URL 443 + `url` (required) - The hook URL
  444 ++ `push_events` - Trigger hook on push events
  445 ++ `issues_events` - Trigger hook on issues events
  446 ++ `merge_requests_events` - Trigger hook on merge_requests events
417 447
418 448
419 ### Delete project hook 449 ### Delete project hook
@@ -458,7 +488,7 @@ Parameters: @@ -458,7 +488,7 @@ Parameters:
458 "id":"3f94fc7c85061973edc9906ae170cc269b07ca55" 488 "id":"3f94fc7c85061973edc9906ae170cc269b07ca55"
459 }], 489 }],
460 "tree": "c68537c6534a02cc2b176ca1549f4ffa190b58ee", 490 "tree": "c68537c6534a02cc2b176ca1549f4ffa190b58ee",
461 - "message":"give caolan his credit where it's due (up top)", 491 + "message":"give caolan credit where it's due (up top)",
462 "author": { 492 "author": {
463 "name":"Jeremy Ashkenas", 493 "name":"Jeremy Ashkenas",
464 "email":"jashkenas@example.com" 494 "email":"jashkenas@example.com"
doc/api/repositories.md
@@ -368,4 +368,43 @@ GET /projects/:id/repository/archive @@ -368,4 +368,43 @@ GET /projects/:id/repository/archive
368 368
369 Parameters: 369 Parameters:
370 + `id` (required) - The ID of a project 370 + `id` (required) - The ID of a project
371 -+ `sha` (optional) - The commit sha to download defaults to the tip of the default branch  
372 \ No newline at end of file 371 \ No newline at end of file
  372 ++ `sha` (optional) - The commit sha to download defaults to the tip of the default branch
  373 +
  374 +
  375 +## Create new file in repository
  376 +
  377 +```
  378 +POST /projects/:id/repository/files
  379 +```
  380 +
  381 +Parameters:
  382 +
  383 ++ `file_path` (optional) - Full path to new file. Ex. lib/class.rb
  384 ++ `branch_name` (required) - The name of branch
  385 ++ `content` (required) - File content
  386 ++ `commit_message` (required) - Commit message
  387 +
  388 +## Update existing file in repository
  389 +
  390 +```
  391 +PUT /projects/:id/repository/files
  392 +```
  393 +
  394 +Parameters:
  395 +
  396 ++ `file_path` (required) - Full path to file. Ex. lib/class.rb
  397 ++ `branch_name` (required) - The name of branch
  398 ++ `content` (required) - New file content
  399 ++ `commit_message` (required) - Commit message
  400 +
  401 +## Delete existing file in repository
  402 +
  403 +```
  404 +DELETE /projects/:id/repository/files
  405 +```
  406 +
  407 +Parameters:
  408 +
  409 ++ `file_path` (required) - Full path to file. Ex. lib/class.rb
  410 ++ `branch_name` (required) - The name of branch
  411 ++ `commit_message` (required) - Commit message
doc/development/architecture.md 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +# GitLab project architecture
  2 +
  3 +GitLab project consists of two parts: GitLab and GitLab shell.
  4 +
  5 +## GitLab
  6 +
  7 +Web application with background jobs workers.
  8 +Provides you with UI and most of functionality.
  9 +For some operations like repo creation - uses GitLab shell.
  10 +
  11 +Uses:
  12 + * Ruby as main language for application code and most libraries.
  13 + * [Rails](http://rubyonrails.org/) web framework as main framework for application.
  14 + * Mysql or postgres as main databases. Used for persistent data storage(users, project, issues etc).
  15 + * Redis database. Used for cache and exchange data between some components.
  16 + * Python2 because of [pygments](http://pygments.org/) as code syntax highlighter.
  17 +
  18 +## GitLab shell
  19 +
  20 +Command line ruby application. Used by GitLab through shell commands.
  21 +It provides interface to all kind of manipulations with repositories and ssh keys.
  22 +Full list of commands you can find in README of GitLab shell repo.
  23 +Works on pure ruby and do not require any additional software.
doc/install/databases.md
@@ -25,19 +25,19 @@ GitLab supports the following databases: @@ -25,19 +25,19 @@ GitLab supports the following databases:
25 # Create a user for GitLab 25 # Create a user for GitLab
26 # do not type the 'mysql>', this is part of the prompt 26 # do not type the 'mysql>', this is part of the prompt
27 # change $password in the command below to a real password you pick 27 # change $password in the command below to a real password you pick
28 - mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password'; 28 + mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
29 29
30 # Create the GitLab production database 30 # Create the GitLab production database
31 mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; 31 mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
32 32
33 # Grant the GitLab user necessary permissions on the table. 33 # Grant the GitLab user necessary permissions on the table.
34 - mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost'; 34 + mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'git'@'localhost';
35 35
36 # Quit the database session 36 # Quit the database session
37 mysql> \q 37 mysql> \q
38 38
39 # Try connecting to the new database with the new user 39 # Try connecting to the new database with the new user
40 - sudo -u git -H mysql -u gitlab -p -D gitlabhq_production 40 + sudo -u git -H mysql -u git -p -D gitlabhq_production
41 41
42 # Type the password you replaced $password with earlier 42 # Type the password you replaced $password with earlier
43 43
doc/install/installation.md
@@ -118,8 +118,8 @@ Remove the old Ruby 1.8 if present @@ -118,8 +118,8 @@ Remove the old Ruby 1.8 if present
118 Download Ruby and compile it: 118 Download Ruby and compile it:
119 119
120 mkdir /tmp/ruby && cd /tmp/ruby 120 mkdir /tmp/ruby && cd /tmp/ruby
121 - curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p247.tar.gz | tar xz  
122 - cd ruby-2.0.0-p247 121 + curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz
  122 + cd ruby-2.0.0-p353
123 ./configure --disable-install-rdoc 123 ./configure --disable-install-rdoc
124 make 124 make
125 sudo make install 125 sudo make install
@@ -149,7 +149,7 @@ GitLab Shell is an ssh access and repository management software developed speci @@ -149,7 +149,7 @@ GitLab Shell is an ssh access and repository management software developed speci
149 cd gitlab-shell 149 cd gitlab-shell
150 150
151 # switch to right version 151 # switch to right version
152 - sudo -u git -H git checkout v1.7.4 152 + sudo -u git -H git checkout v1.7.9
153 153
154 sudo -u git -H cp config.yml.example config.yml 154 sudo -u git -H cp config.yml.example config.yml
155 155
@@ -180,10 +180,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install @@ -180,10 +180,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
180 cd /home/git/gitlab 180 cd /home/git/gitlab
181 181
182 # Checkout to stable release 182 # Checkout to stable release
183 - sudo -u git -H git checkout 6-2-stable 183 + sudo -u git -H git checkout 6-3-stable
184 184
185 **Note:** 185 **Note:**
186 -You can change `6-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! 186 +You can change `6-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
187 187
188 ## Configure it 188 ## Configure it
189 189
@@ -227,10 +227,6 @@ You can change `6-2-stable` to `master` if you want the *bleeding edge* version, @@ -227,10 +227,6 @@ You can change `6-2-stable` to `master` if you want the *bleeding edge* version,
227 # Copy the example Rack attack config 227 # Copy the example Rack attack config
228 sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb 228 sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
229 229
230 - # Enable rack attack middleware  
231 - # Find and uncomment the line 'config.middleware.use Rack::Attack'  
232 - sudo -u git -H editor config/application.rb  
233 -  
234 # Configure Git global settings for git user, useful when editing via web 230 # Configure Git global settings for git user, useful when editing via web
235 # Edit user.email according to what is set in gitlab.yml 231 # Edit user.email according to what is set in gitlab.yml
236 sudo -u git -H git config --global user.name "GitLab" 232 sudo -u git -H git config --global user.name "GitLab"
@@ -265,8 +261,6 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup. @@ -265,8 +261,6 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup.
265 261
266 cd /home/git/gitlab 262 cd /home/git/gitlab
267 263
268 - sudo gem install charlock_holmes --version '0.6.9.4'  
269 -  
270 # For MySQL (note, the option says "without ... postgres") 264 # For MySQL (note, the option says "without ... postgres")
271 sudo -u git -H bundle install --deployment --without development test postgres aws 265 sudo -u git -H bundle install --deployment --without development test postgres aws
272 266
@@ -428,5 +422,5 @@ These steps are fairly general and you will need to figure out the exact details @@ -428,5 +422,5 @@ These steps are fairly general and you will need to figure out the exact details
428 ### Examples 422 ### Examples
429 423
430 If you have successfully set up a provider that is not shipped with GitLab itself, please let us know. 424 If you have successfully set up a provider that is not shipped with GitLab itself, please let us know.
431 -You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-Custom-Omniauth-Provider-Configurations). 425 +You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations).
432 While we can't officially support every possible auth mechanism out there, we'd like to at least help those with special needs. 426 While we can't officially support every possible auth mechanism out there, we'd like to at least help those with special needs.
doc/install/requirements.md
1 # Operating Systems 1 # Operating Systems
2 2
3 -## Linux  
4 -  
5 GitLab is developed for the Linux operating system. 3 GitLab is developed for the Linux operating system.
6 4
7 -GitLab officially supports (recent versions of) these Linux distributions: 5 +## GitLab officially supports
8 6
9 - Ubuntu Linux 7 - Ubuntu Linux
10 - Debian/GNU Linux 8 - Debian/GNU Linux
11 9
12 -It should also work on (though they are not officially supported): 10 +## GitLab.com offers paid support for
13 11
14 -- Arch 12 +- Red Hat Enterprise Linux (RHEL)
15 - CentOS 13 - CentOS
  14 +- Oracle Linux
  15 +
  16 +## Not officially supported
  17 +
  18 +- Arch Linux
16 - Fedora 19 - Fedora
17 - Gentoo 20 - Gentoo
18 -- RHEL  
19 21
20 -## Other Unix Systems 22 +On the above distributions it is pretty easy to install GitLab yourself.
  23 +
  24 +## Unsupported Unix Systems
21 25
22 -There is nothing that prevents GitLab from running on other Unix operating  
23 -systems. This means you may get it to work on systems running FreeBSD or OS X.  
24 -**If you want to try, please proceed with caution!** 26 +There is nothing that prevents GitLab from running on other Unix operating systems.
  27 +This means you may get it to work on systems running FreeBSD or OS X.
  28 +If you want to do this, please be aware it could be a lot of work.
  29 +Please consider using a virtual machine to run GitLab.
25 30
26 -## Windows 31 +## Other operating systems such as Windows
27 32
28 -GitLab does **not** run on Windows and we have no plans of supporting it in the  
29 -near future. Please consider using a virtual machine to run GitLab. 33 +GitLab does **not** run on Windows and we have no plans of supporting it in the near future.
  34 +Please consider using a virtual machine to run GitLab.
30 35
31 36
32 -# Rubies 37 +# Ruby versions
33 38
34 -GitLab requires Ruby (MRI) 1.9.3 and several Gems with native components.  
35 -While it is generally possible to use other Rubies (like  
36 -[JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/)) it might require  
37 -some work on your part. 39 +GitLab requires Ruby (MRI) 1.9.3 or 2.0+.
  40 +While it is generally possible to use other Rubies
  41 +(like [JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/))
  42 +it might require some work since GitLab uses several Gems that have native extensions.
38 43
39 44
40 # Hardware requirements 45 # Hardware requirements
doc/legal/corporate_contributor_license_agreement.md 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab.com. Except for the license granted herein to GitLab.com and recipients of software distributed by GitLab.com, You reserve all right, title, and interest in and to Your Contributions.
  2 +
  3 +1. Definitions.
  4 +
  5 + "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab.com. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
  6 +
  7 + "Contribution" shall mean the code, documentation or other original works of authorship expressly identified in Schedule B, as well as any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab.com for inclusion in, or documentation of, any of the products owned or managed by GitLab.com (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab.com or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab.com for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
  8 +
  9 +2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
  10 +
  11 +3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
  12 +
  13 +4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation.
  14 +
  15 +5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others).
  16 +
  17 +6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
  18 +
  19 +7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab.com separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
  20 +
  21 +8. It is your responsibility to notify GitLab.com when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab.com.
  22 +
  23 +---------------------------------------
  24 +
  25 +This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
doc/legal/individual_contributor_license_agreement.md 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab.com. Except for the license granted herein to GitLab.com and recipients of software distributed by GitLab.com, You reserve all right, title, and interest in and to Your Contributions.
  2 +
  3 +1. Definitions.
  4 +
  5 + "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab.com. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
  6 +
  7 + "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab.com for inclusion in, or documentation of, any of the products owned or managed by GitLab.com (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab.com or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab.com for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
  8 +
  9 +2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
  10 +
  11 +3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
  12 +
  13 +4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to GitLab.com, or that your employer has executed a separate Corporate CLA with GitLab.com.
  14 +
  15 +5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
  16 +
  17 +6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
  18 +
  19 +7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab.com separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [[]named here]".
  20 +
  21 +8. You agree to notify GitLab.com of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
  22 +
  23 +---------------------------------------
  24 +
  25 +This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
doc/make_release.md
@@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
1 -# Things to do when creating new release  
2 -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).  
3 -  
4 -## Install guide up to date?  
5 -  
6 -* References correct GitLab branch `x-x-stable` and correct GitLab shell tag?  
7 -  
8 -## Make upgrade guide  
9 -  
10 -### From x.x to x.x  
11 -  
12 -#### 0. Any major changes? Database updates? Web server change? File structure changes?  
13 -  
14 -#### 1. Make backup  
15 -  
16 -#### 2. Stop server  
17 -  
18 -#### 3. Do users need to update dependencies like `git`?  
19 -  
20 -#### 4. Get latest code  
21 -  
22 -#### 5. Does GitLab shell need to be updated?  
23 -  
24 -#### 6. Install libs, migrations, etc.  
25 -  
26 -#### 7. Any config files updated since last release?  
27 -  
28 -Check if any of these changed since last release (~22nd of last month depending on when last release branch was created):  
29 -  
30 -* https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/nginx/gitlab  
31 -* https://github.com/gitlabhq/gitlab-shell/commits/master/config.yml.example  
32 -* https://github.com/gitlabhq/gitlabhq/commits/master/config/gitlab.yml.example  
33 -* https://github.com/gitlabhq/gitlabhq/commits/master/config/unicorn.rb.example  
34 -* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.mysql  
35 -* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.postgresql  
36 -  
37 -#### 8. Need to update init script?  
38 -  
39 -Check if changed since last release (~22nd of last month depending on when last release branch was created): https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/init.d/gitlab  
40 -  
41 -#### 9. Start application  
42 -  
43 -#### 10. Check application status  
44 -  
45 -## Make sure the code quality indicatiors are good  
46 -  
47 -* [![build status](http://ci.gitlab.org/projects/1/status.png?ref=master)](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)  
48 -  
49 -* [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) on travis-ci.org (master branch)  
50 -  
51 -* [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)  
52 -  
53 -* [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq) this button can be yellow (small updates are available) but must not be red (a security fix or an important update is available)  
54 -  
55 -* [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)  
56 -  
57 -## Make a release branch  
58 -  
59 -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.  
60 -  
61 -* 5 days before release: feature freeze  
62 -* 3 days before release: UI freeze  
63 -* 1 day before release: code freeze  
64 -  
65 -# Write a blog post  
66 -  
67 -* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.  
68 -* Select and thank the the Most Valuable Person (MVP) of this release.  
69 -* Note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.  
70 -  
71 -## Last actions  
72 -  
73 -1. Update VERSION and CHANGELOG  
74 -1. Create a git tag vX.X.X  
75 -1. Publish the blog post  
76 -1. Tweet about the release  
doc/release/monthly.md 0 → 100644
@@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
  1 +# Things to do when creating new monthly minor or major release
  2 +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).
  3 +
  4 +## Install guide up to date?
  5 +
  6 +* References correct GitLab branch `x-x-stable` and correct GitLab shell tag?
  7 +
  8 +## Make upgrade guide
  9 +
  10 +### From x.x to x.x
  11 +
  12 +#### 0. Any major changes? Database updates? Web server change? File structure changes?
  13 +
  14 +#### 1. Make backup
  15 +
  16 +#### 2. Stop server
  17 +
  18 +#### 3. Do users need to update dependencies like `git`?
  19 +
  20 +#### 4. Get latest code
  21 +
  22 +#### 5. Does GitLab shell need to be updated?
  23 +
  24 +#### 6. Install libs, migrations, etc.
  25 +
  26 +#### 7. Any config files updated since last release?
  27 +
  28 +Check if any of these changed since last release (~22nd of last month depending on when last release branch was created):
  29 +
  30 +* https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/nginx/gitlab
  31 +* https://github.com/gitlabhq/gitlab-shell/commits/master/config.yml.example
  32 +* https://github.com/gitlabhq/gitlabhq/commits/master/config/gitlab.yml.example
  33 +* https://github.com/gitlabhq/gitlabhq/commits/master/config/unicorn.rb.example
  34 +* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.mysql
  35 +* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.postgresql
  36 +
  37 +#### 8. Need to update init script?
  38 +
  39 +Check if changed since last release (~22nd of last month depending on when last release branch was created): https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/init.d/gitlab
  40 +
  41 +#### 9. Start application
  42 +
  43 +#### 10. Check application status
  44 +
  45 +## Make sure the code quality indicatiors are good
  46 +
  47 +* [![build status](http://ci.gitlab.org/projects/1/status.png?ref=master)](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
  48 +
  49 +* [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) on travis-ci.org (master branch)
  50 +
  51 +* [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
  52 +
  53 +* [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq) this button can be yellow (small updates are available) but must not be red (a security fix or an important update is available)
  54 +
  55 +* [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)
  56 +
  57 +## Make a release branch
  58 +
  59 +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.
  60 +
  61 +* 5 days before release: feature freeze
  62 +* 3 days before release: UI freeze
  63 +* 1 day before release: code freeze
  64 +
  65 +# Write a blog post
  66 +
  67 +* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
  68 +* Select and thank the the Most Valuable Person (MVP) of this release.
  69 +* Note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
  70 +
  71 +## Last actions
  72 +
  73 +1. Update VERSION and CHANGELOG
  74 +1. Create a git tag vX.X.X
  75 +1. Publish the blog post
  76 +1. Tweet about the release
doc/release/security.md 0 → 100644
@@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
  1 +# Things to do when doing an out-of-bound security release
  2 +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).
  3 +
  4 +## When to do a security release
  5 +
  6 +Do a security release when there is a critical issue that needs to be adresses before the next monthly release. Otherwise include it in the monthly release and note there was a security fix in the release announcement.
  7 +
  8 +## Security vulnerability disclosure
  9 +
  10 +Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
  11 +
  12 +## Release Procedure
  13 +
  14 +1. Verify that the issue can be repoduced
  15 +1. Acknowledge the issue to the researcher that disclosed it
  16 +1. Fix the issue on a feature branch, do this on the private GitLab development server and update the VERSION and CHANGELOG in this branch
  17 +1. Consider creating and testing workarounds
  18 +1. Create feature branches for the blog posts on GitLab.org and GitLab.com and link them from the code branch
  19 +1. Merge the code feature branch into master
  20 +1. Cherry-pick the code into the latest stable branch
  21 +1. Create a git tag vX.X.X for CE and another patch release for EE
  22 +1. Push the code and the tags to all the CE and EE repositories
  23 +1. Apply the patch to GitLab Cloud and the private GitLab development server
  24 +1. Merge and publish the blog posts
  25 +1. Send tweets about the release from @gitlabhq and @git_lab
  26 +1. Send out an email to the subscribers mailing list on MailChimp
  27 +1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq)
  28 +1. Send out an email to [the GitLab newsletter list](http://gitlab.us5.list-manage.com/subscribe?u=498dccd07cf3e9482bee33ba4&id=98a9a4992c)
  29 +1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number
  30 +1. Add the security researcher to the [Security Researcher Acknowledgments list](http://www.gitlab.com/vulnerability-acknowledgements/)
  31 +1. Thank the security researcher in an email for their cooperation
  32 +1. Update the blogpost and the CHANGELOG when we receive the CVE number
  33 +
  34 +The timing of the code merge into master should be coordinated in advance.
  35 +After the merge we strive to publish the announcements within 60 minutes.
  36 +
  37 +## Blog post template
  38 +
  39 +XXX Security Advisory for GitLab
  40 +
  41 +A recently discovered critical vulnerability in GitLab allows [unauthenticated API access|remote code execution|unauthorized access to repositories|XXX|PICKSOMETHING]. All users should update GitLab and gitlab-shell immediately.
  42 +We [have|haven't|XXX|PICKSOMETHING|] heard of this vulnerability being actively exploited.
  43 +
  44 +### Version affected
  45 +
  46 +GitLab Community Edition XXX and lower
  47 +GitLab Enterprise Edition XXX and lower
  48 +
  49 +### Fixed versions
  50 +
  51 +GitLab Community Edition XXX and up
  52 +GitLab Enterprise Edition XXX and up
  53 +
  54 +### Impact
  55 +
  56 +On GitLab installations which use MySQL as their database backend it is possible for an attacker to assume the identity of any existing GitLab user in certain API calls. This attack can be performed by [unauthenticated|authenticated|XXX|PICKSOMETHING] users.
  57 +
  58 +### Workarounds
  59 +
  60 +If you are unable to upgrade you should apply the following patch and restart GitLab.
  61 +
  62 +XXX
  63 +
  64 +### Credit
  65 +
  66 +We want to thank XXX of XXX for the reponsible disclosure of this vulnerability.
  67 +
  68 +## Email template
  69 +
  70 +We just announced a security advisory for GitLab at XXX
  71 +
  72 +Please contact us at support@gitlab.com if you have any questions.
  73 +
  74 +## Tweet template
  75 +
  76 +We just announced a security advisory for GitLab at XXX
doc/update/5.1-to-5.4.md
@@ -31,7 +31,7 @@ sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses @@ -31,7 +31,7 @@ sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses
31 ```bash 31 ```bash
32 cd /home/git/gitlab-shell 32 cd /home/git/gitlab-shell
33 sudo -u git -H git fetch 33 sudo -u git -H git fetch
34 -sudo -u git -H git checkout v1.7.4 # Addresses CVE-2013-4490 34 +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
35 ``` 35 ```
36 36
37 ### 4. Install libs, migrations, etc. 37 ### 4. Install libs, migrations, etc.
doc/update/5.1-to-6.0.md
@@ -47,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable @@ -47,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable
47 ```bash 47 ```bash
48 cd /home/git/gitlab-shell 48 cd /home/git/gitlab-shell
49 sudo -u git -H git fetch 49 sudo -u git -H git fetch
50 -sudo -u git -H git checkout v1.7.0 50 +sudo -u git -H git checkout v1.7.9
51 ``` 51 ```
52 52
53 ### 4. Install additional packages 53 ### 4. Install additional packages
doc/update/5.3-to-5.4.md
@@ -30,7 +30,7 @@ sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses @@ -30,7 +30,7 @@ sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses
30 ```bash 30 ```bash
31 cd /home/git/gitlab-shell 31 cd /home/git/gitlab-shell
32 sudo -u git -H git fetch 32 sudo -u git -H git fetch
33 -sudo -u git -H git checkout v1.7.4 # Addresses CVE-2013-4490 33 +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
34 ``` 34 ```
35 35
36 ### 4. Install libs, migrations, etc. 36 ### 4. Install libs, migrations, etc.
doc/update/5.4-to-6.0.md
@@ -47,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable @@ -47,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable
47 ```bash 47 ```bash
48 cd /home/git/gitlab-shell 48 cd /home/git/gitlab-shell
49 sudo -u git -H git fetch 49 sudo -u git -H git fetch
50 -sudo -u git -H git checkout v1.7.0 50 +sudo -u git -H git checkout v1.7.9
51 ``` 51 ```
52 52
53 ### 4. Install additional packages 53 ### 4. Install additional packages
doc/update/6.0-to-6.1.md
@@ -39,7 +39,7 @@ sudo -u git -H git checkout 6-1-stable @@ -39,7 +39,7 @@ sudo -u git -H git checkout 6-1-stable
39 ```bash 39 ```bash
40 cd /home/git/gitlab-shell 40 cd /home/git/gitlab-shell
41 sudo -u git -H git fetch 41 sudo -u git -H git fetch
42 -sudo -u git -H git checkout v1.7.4 42 +sudo -u git -H git checkout v1.7.9
43 ``` 43 ```
44 44
45 ### 4. Install libs, migrations, etc. 45 ### 4. Install libs, migrations, etc.
doc/update/6.0-to-6.2.md
@@ -47,7 +47,7 @@ sudo apt-get install logrotate @@ -47,7 +47,7 @@ sudo apt-get install logrotate
47 ```bash 47 ```bash
48 cd /home/git/gitlab-shell 48 cd /home/git/gitlab-shell
49 sudo -u git -H git fetch 49 sudo -u git -H git fetch
50 -sudo -u git -H git checkout v1.7.4 # Addresses CVE-2013-4490 50 +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
51 ``` 51 ```
52 52
53 ### 5. Install libs, migrations, etc. 53 ### 5. Install libs, migrations, etc.
@@ -74,7 +74,7 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production @@ -74,7 +74,7 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
74 TIP: to see what changed in gitlab.yml.example in this release use next command: 74 TIP: to see what changed in gitlab.yml.example in this release use next command:
75 75
76 ``` 76 ```
77 -git diff 6-1-stable:config/gitlab.yml.example 6-2-stable:config/gitlab.yml.example 77 +git diff 6-0-stable:config/gitlab.yml.example 6-2-stable:config/gitlab.yml.example
78 ``` 78 ```
79 79
80 * 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. 80 * 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.
doc/update/6.1-to-6.2.md
@@ -32,7 +32,7 @@ sudo -u git -H git checkout 6-2-stable # Latest version of 6-2-stable addresses @@ -32,7 +32,7 @@ sudo -u git -H git checkout 6-2-stable # Latest version of 6-2-stable addresses
32 ```bash 32 ```bash
33 cd /home/git/gitlab-shell 33 cd /home/git/gitlab-shell
34 sudo -u git -H git fetch 34 sudo -u git -H git fetch
35 -sudo -u git -H git checkout v1.7.4 # Addresses CVE-2013-4490 35 +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
36 ``` 36 ```
37 37
38 ### 4. Install additional packages 38 ### 4. Install additional packages
doc/update/6.2-to-6.3.md 0 → 100644
@@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
  1 +# From 6.2 to 6.3
  2 +
  3 +## Requires version: 6.1 or 6.2
  4 +
  5 +### 0. Backup
  6 +
  7 +It's useful to make a backup just in case things go south:
  8 +(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
  9 +
  10 +```bash
  11 +cd /home/git/gitlab
  12 +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
  13 +```
  14 +
  15 +### 1. Stop server
  16 +
  17 + sudo service gitlab stop
  18 +
  19 +### 2. Get latest code
  20 +
  21 +```bash
  22 +cd /home/git/gitlab
  23 +sudo -u git -H git fetch
  24 +sudo -u git -H git checkout 6-3-stable
  25 +```
  26 +
  27 +### 3. Update gitlab-shell (and its config)
  28 +
  29 +```bash
  30 +cd /home/git/gitlab-shell
  31 +sudo -u git -H git fetch
  32 +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
  33 +```
  34 +
  35 +The Gitlab-shell config changed recently, so check for config file changes and make `/home/git/gitlab-shell/config.yml` the same as https://github.com/gitlabhq/gitlab-shell/blob/master/config.yml.example
  36 +
  37 +### 4. Install libs, migrations, etc.
  38 +
  39 +```bash
  40 +cd /home/git/gitlab
  41 +
  42 +# MySQL
  43 +sudo -u git -H bundle install --without development test postgres --deployment
  44 +
  45 +# PostgreSQL
  46 +sudo -u git -H bundle install --without development test mysql --deployment
  47 +
  48 +
  49 +# Run database migrations
  50 +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
  51 +
  52 +# Clean up assets and cache
  53 +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
  54 +```
  55 +
  56 +### 5. Update config files
  57 +
  58 +TIP: to see what changed in gitlab.yml.example in this release use next command:
  59 +
  60 +```
  61 +git diff 6-2-stable:config/gitlab.yml.example 6-3-stable:config/gitlab.yml.example
  62 +```
  63 +
  64 +* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-3-stable/config/gitlab.yml.example but with your settings.
  65 +* Make `/home/git/gitlab/config/unicorn.rb` same as https://github.com/gitlabhq/gitlabhq/blob/6-3-stable/config/unicorn.rb.example but with your settings.
  66 +* Copy rack attack middleware config
  67 +
  68 +### 6. Update Init script
  69 +
  70 +```bash
  71 +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
  72 +sudo chmod +x /etc/init.d/gitlab
  73 +```
  74 +
  75 +### 7. Start application
  76 +
  77 + sudo service gitlab start
  78 + sudo service nginx restart
  79 +
  80 +### 8. Check application status
  81 +
  82 +Check if GitLab and its environment are configured correctly:
  83 +
  84 + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
  85 +
  86 +To make sure you didn't miss anything run a more thorough check with:
  87 +
  88 + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
  89 +
  90 +If all items are green, then congratulations upgrade complete!
  91 +
  92 +## Things went south? Revert to previous version (6.2)
  93 +
  94 +### 1. Revert the code to the previous version
  95 +Follow the [`upgrade guide from 6.1 to 6.2`](6.1-to-6.2.md), except for the database migration
  96 +(The backup is already migrated to the previous version)
  97 +
  98 +### 2. Restore from the backup:
  99 +
  100 +```bash
  101 +cd /home/git/gitlab
  102 +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
  103 +```
doc/update/patch_versions.md
1 -# Universal update guide for patch versions. Ex. from From 6.2.0 to 6.2.1 1 +# Universal update guide for patch versions. For example from 6.2.0 to 6.2.1, also see the [semantic versioning specification](http://semver.org/).
2 2
3 ### 0. Backup 3 ### 0. Backup
4 4
@@ -14,21 +14,25 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production @@ -14,21 +14,25 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
14 14
15 sudo service gitlab stop 15 sudo service gitlab stop
16 16
17 -### 2. Get latest code for your current stable branch 17 +### 2. Get latest code for the stable branch
18 18
19 ```bash 19 ```bash
20 cd /home/git/gitlab 20 cd /home/git/gitlab
21 -sudo -u git -H git pull origin 6-2-stable 21 +sudo -u git -H git pull origin STABLE_BRANCH
22 ``` 22 ```
23 23
24 -### 3. Update gitlab-shell if necessary 24 +Replace STABLE_BRANCH with the minor version you want to upgrade to, for example `6-3-stable`.
  25 +
  26 +### 3. Update gitlab-shell if it is not the latest version
25 27
26 ```bash 28 ```bash
27 cd /home/git/gitlab-shell 29 cd /home/git/gitlab-shell
28 sudo -u git -H git fetch 30 sudo -u git -H git fetch
29 -sudo -u git -H git checkout v1.7.4 31 +sudo -u git -H git checkout LATEST_TAG
30 ``` 32 ```
31 33
  34 +Replace LATEST_TAG with the latest GitLab Shell tag you want to upgrade to, for example `v1.7.9`.
  35 +
32 ### 4. Install libs, migrations, etc. 36 ### 4. Install libs, migrations, etc.
33 37
34 ```bash 38 ```bash
doc/update/ruby.md 0 → 100644
@@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
  1 +# Updating Ruby from source
  2 +
  3 +This guide explains how to update Ruby in case you installed it from source according to the instructions in https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md#2-ruby .
  4 +
  5 +### 1. Look for Ruby versions
  6 +This guide will only update `/usr/local/bin/ruby`. You can see which Ruby binaries are installed on your system by running:
  7 +
  8 +```bash
  9 +ls -l $(which -a ruby)
  10 +```
  11 +
  12 +### 2. Stop GitLab
  13 +
  14 +```bash
  15 +sudo service gitlab stop
  16 +```
  17 +
  18 +### 3. Install or update dependencies
  19 +Here we are assuming you are using Debian/Ubuntu.
  20 +
  21 +```bash
  22 +sudo apt-get install build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl
  23 +```
  24 +
  25 +### 4. Download, compile and install Ruby
  26 +Find the latest stable version of Ruby 1.9 or 2.0 at https://www.ruby-lang.org/en/downloads/ . We recommend at least 2.0.0-p353, which is patched against [CVE-2013-4164](https://www.ruby-lang.org/en/news/2013/11/22/heap-overflow-in-floating-point-parsing-cve-2013-4164/).
  27 +
  28 +```bash
  29 +cd /tmp
  30 +curl --progress http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz
  31 +cd ruby-2.0.0-p353
  32 +./configure --disable-install-rdoc
  33 +make
  34 +sudo make install # overwrite the existing Ruby in /usr/local/bin
  35 +sudo gem install bundler
  36 +```
  37 +
  38 +### 5. Reinstall GitLab gem bundle
  39 +Just to be sure we will reinstall the gems used by GitLab. Note that the `bundle install` command [depends on your choice of database](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md#install-gems).
  40 +
  41 +```bash
  42 +cd /home/git/gitlab
  43 +sudo -u git -H rm -rf vendor/bundle # remove existing Gem bundle
  44 +sudo -u git -H bundle install --deployment --without development test postgres aws # Assuming MySQL
  45 +```
  46 +
  47 +### 6. Start GitLab
  48 +We are now ready to restart GitLab.
  49 +
  50 +```bash
  51 +sudo service gitlab start
  52 +```
  53 +
  54 +### Done
features/admin/active_tab.feature
@@ -27,6 +27,11 @@ Feature: Admin active tab @@ -27,6 +27,11 @@ Feature: Admin active tab
27 Then the active main tab should be Logs 27 Then the active main tab should be Logs
28 And no other main tabs should be active 28 And no other main tabs should be active
29 29
  30 + Scenario: On Admin Messages
  31 + Given I visit admin messages page
  32 + Then the active main tab should be Messages
  33 + And no other main tabs should be active
  34 +
30 Scenario: On Admin Hooks 35 Scenario: On Admin Hooks
31 Given I visit admin hooks page 36 Given I visit admin hooks page
32 Then the active main tab should be Hooks 37 Then the active main tab should be Hooks
features/admin/broadcast_messages.feature 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +Feature: Admin Broadcast Messages
  2 + Background:
  3 + Given I sign in as an admin
  4 + And application already has admin messages
  5 + And I visit admin messages page
  6 +
  7 + Scenario: See broadcast messages list
  8 + Then I should be all broadcast messages
  9 +
  10 + Scenario: Create a broadcast message
  11 + When submit form with new broadcast message
  12 + Then I should be redirected to admin messages page
  13 + And I should see newly created broadcast message
features/profile/profile.feature
@@ -26,6 +26,14 @@ Feature: Profile @@ -26,6 +26,14 @@ Feature: Profile
26 Given I visit profile page 26 Given I visit profile page
27 Then I change my avatar 27 Then I change my avatar
28 And I should see new avatar 28 And I should see new avatar
  29 + And I should see the "Remove avatar" button
  30 +
  31 + Scenario: I remove my avatar
  32 + Given I visit profile page
  33 + And I have an avatar
  34 + When I remove my avatar
  35 + Then I should see my gravatar
  36 + And I should not see the "Remove avatar" button
29 37
30 Scenario: My password is expired 38 Scenario: My password is expired
31 Given my password is expired 39 Given my password is expired
features/project/commits/commits.feature
@@ -14,6 +14,12 @@ Feature: Project Browse commits @@ -14,6 +14,12 @@ Feature: Project Browse commits
14 Scenario: I browse commit from list 14 Scenario: I browse commit from list
15 Given I click on commit link 15 Given I click on commit link
16 Then I see commit info 16 Then I see commit info
  17 + And I see side-by-side diff button
  18 +
  19 + Scenario: I browse commit with side-by-side diff view
  20 + Given I click on commit link
  21 + And I click side-by-side diff button
  22 + Then I see inline diff button
17 23
18 Scenario: I compare refs 24 Scenario: I compare refs
19 Given I visit compare refs page 25 Given I visit compare refs page
features/project/network.feature
@@ -29,11 +29,11 @@ Feature: Project Network Graph @@ -29,11 +29,11 @@ Feature: Project Network Graph
29 @javascript 29 @javascript
30 Scenario: I should filter selected tag 30 Scenario: I should filter selected tag
31 When I switch ref to "v2.1.0" 31 When I switch ref to "v2.1.0"
32 - Then page should have content not cotaining "v2.1.0" 32 + Then page should have content not containing "v2.1.0"
33 When click "Show only selected branch" checkbox 33 When click "Show only selected branch" checkbox
34 - Then page should not have content not cotaining "v2.1.0" 34 + Then page should not have content not containing "v2.1.0"
35 When click "Show only selected branch" checkbox 35 When click "Show only selected branch" checkbox
36 - Then page should have content not cotaining "v2.1.0" 36 + Then page should have content not containing "v2.1.0"
37 37
38 Scenario: I should fail to look for a commit 38 Scenario: I should fail to look for a commit
39 When I look for a commit by ";" 39 When I look for a commit by ";"
features/project/redirects.feature 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +Feature: Project Redirects
  2 + Background:
  3 + Given public project "Community"
  4 + And private project "Enterprise"
  5 +
  6 + Scenario: I visit public project page
  7 + When I visit project "Community" page
  8 + Then I should see project "Community" home page
  9 +
  10 + Scenario: I visit private project page
  11 + When I visit project "Enterprise" page
  12 + Then I should be redirected to sign in page
  13 +
  14 + Scenario: I visit a non-existent project page
  15 + When I visit project "CommunityDoesNotExist" page
  16 + Then I should be redirected to sign in page
  17 +
  18 + Scenario: I visit a non-existent project page as user
  19 + Given I sign in as a user
  20 + When I visit project "CommunityDoesNotExist" page
  21 + Then page status code should be 404
  22 +
  23 + Scenario: I visit unauthorized project page as user
  24 + Given I sign in as a user
  25 + When I visit project "Enterprise" page
  26 + Then page status code should be 404
features/project/service.feature
@@ -30,3 +30,9 @@ Feature: Project Services @@ -30,3 +30,9 @@ Feature: Project Services
30 And I click Flowdock service link 30 And I click Flowdock service link
31 And I fill Flowdock settings 31 And I fill Flowdock settings
32 Then I should see Flowdock service settings saved 32 Then I should see Flowdock service settings saved
  33 +
  34 + Scenario: Activate Assembla service
  35 + When I visit project "Shop" services page
  36 + And I click Assembla service link
  37 + And I fill Assembla settings
  38 + Then I should see Assembla service settings saved
33 \ No newline at end of file 39 \ No newline at end of file
features/project/source/multiselect_blob.feature 0 → 100644
@@ -0,0 +1,86 @@ @@ -0,0 +1,86 @@
  1 +Feature: Project Multiselect Blob
  2 + Background:
  3 + Given I sign in as a user
  4 + And I own project "Shop"
  5 + And I visit project source page
  6 + And I click on "Gemfile.lock" file in repo
  7 +
  8 + @javascript
  9 + Scenario: I click line 1 in file
  10 + When I click line 1 in file
  11 + Then I should see "L1" as URI fragment
  12 + And I should see line 1 highlighted
  13 +
  14 + @javascript
  15 + Scenario: I shift-click line 1 in file
  16 + When I shift-click line 1 in file
  17 + Then I should see "L1" as URI fragment
  18 + And I should see line 1 highlighted
  19 +
  20 + @javascript
  21 + Scenario: I click line 1 then click line 2 in file
  22 + When I click line 1 in file
  23 + Then I should see "L1" as URI fragment
  24 + And I should see line 1 highlighted
  25 + Then I click line 2 in file
  26 + Then I should see "L2" as URI fragment
  27 + And I should see line 2 highlighted
  28 +
  29 + @javascript
  30 + Scenario: I click various line numbers to test multiselect
  31 + Then I click line 1 in file
  32 + Then I should see "L1" as URI fragment
  33 + And I should see line 1 highlighted
  34 + Then I shift-click line 2 in file
  35 + Then I should see "L1-2" as URI fragment
  36 + And I should see lines 1-2 highlighted
  37 + Then I shift-click line 3 in file
  38 + Then I should see "L1-3" as URI fragment
  39 + And I should see lines 1-3 highlighted
  40 + Then I click line 3 in file
  41 + Then I should see "L3" as URI fragment
  42 + And I should see line 3 highlighted
  43 + Then I shift-click line 1 in file
  44 + Then I should see "L1-3" as URI fragment
  45 + And I should see lines 1-3 highlighted
  46 + Then I shift-click line 5 in file
  47 + Then I should see "L1-5" as URI fragment
  48 + And I should see lines 1-5 highlighted
  49 + Then I shift-click line 4 in file
  50 + Then I should see "L1-4" as URI fragment
  51 + And I should see lines 1-4 highlighted
  52 + Then I click line 5 in file
  53 + Then I should see "L5" as URI fragment
  54 + And I should see line 5 highlighted
  55 + Then I shift-click line 3 in file
  56 + Then I should see "L3-5" as URI fragment
  57 + And I should see lines 3-5 highlighted
  58 + Then I shift-click line 1 in file
  59 + Then I should see "L1-3" as URI fragment
  60 + And I should see lines 1-3 highlighted
  61 + Then I shift-click line 1 in file
  62 + Then I should see "L1" as URI fragment
  63 + And I should see line 1 highlighted
  64 +
  65 + @javascript
  66 + Scenario: I multiselect lines 1-5 and then go back and forward in history
  67 + When I click line 1 in file
  68 + And I shift-click line 3 in file
  69 + And I shift-click line 2 in file
  70 + And I shift-click line 5 in file
  71 + Then I should see "L1-5" as URI fragment
  72 + And I should see lines 1-5 highlighted
  73 + Then I go back in history
  74 + Then I should see "L1-2" as URI fragment
  75 + And I should see lines 1-2 highlighted
  76 + Then I go back in history
  77 + Then I should see "L1-3" as URI fragment
  78 + And I should see lines 1-3 highlighted
  79 + Then I go back in history
  80 + Then I should see "L1" as URI fragment
  81 + And I should see line 1 highlighted
  82 + Then I go forward in history
  83 + And I go forward in history
  84 + And I go forward in history
  85 + Then I should see "L1-5" as URI fragment
  86 + And I should see lines 1-5 highlighted
0 \ No newline at end of file 87 \ No newline at end of file
features/public/public_projects.feature
1 Feature: Public Projects Feature 1 Feature: Public Projects Feature
2 Background: 2 Background:
3 Given public project "Community" 3 Given public project "Community"
  4 + And internal project "Internal"
4 And private project "Enterprise" 5 And private project "Enterprise"
5 6
6 Scenario: I visit public area 7 Scenario: I visit public area
7 When I visit the public projects area 8 When I visit the public projects area
8 Then I should see project "Community" 9 Then I should see project "Community"
  10 + And I should not see project "Internal"
9 And I should not see project "Enterprise" 11 And I should not see project "Enterprise"
10 12
11 Scenario: I visit public project page 13 Scenario: I visit public project page
12 When I visit project "Community" page 14 When I visit project "Community" page
13 Then I should see project "Community" home page 15 Then I should see project "Community" home page
14 16
  17 + Scenario: I visit internal project page
  18 + When I visit project "Internal" page
  19 + Then I should be redirected to sign in page
  20 +
  21 + Scenario: I visit private project page
  22 + When I visit project "Enterprise" page
  23 + Then I should be redirected to sign in page
  24 +
15 Scenario: I visit an empty public project page 25 Scenario: I visit an empty public project page
16 Given public empty project "Empty Public Project" 26 Given public empty project "Empty Public Project"
17 When I visit empty project page 27 When I visit empty project page
18 Then I should see empty public project details 28 Then I should see empty public project details
  29 +
  30 + Scenario: I visit public area as user
  31 + Given I sign in as a user
  32 + When I visit the public projects area
  33 + Then I should see project "Community"
  34 + And I should see project "Internal"
  35 + And I should not see project "Enterprise"
  36 +
  37 + Scenario: I visit internal project page as user
  38 + Given I sign in as a user
  39 + When I visit project "Internal" page
  40 + Then I should see project "Internal" home page
  41 +
  42 + Scenario: I visit public project page
  43 + When I visit project "Community" page
  44 + Then I should see project "Community" home page
  45 + And I should see a http link to the repository
  46 +
  47 + Scenario: I visit public area as user
  48 + Given I sign in as a user
  49 + When I visit project "Community" page
  50 + Then I should see project "Community" home page
  51 + And I should see a ssh link to the repository
features/steps/admin/admin_active_tab.rb
@@ -30,4 +30,8 @@ class AdminActiveTab &lt; Spinach::FeatureSteps @@ -30,4 +30,8 @@ class AdminActiveTab &lt; Spinach::FeatureSteps
30 Then 'the active main tab should be Resque' do 30 Then 'the active main tab should be Resque' do
31 ensure_active_main_tab('Background Jobs') 31 ensure_active_main_tab('Background Jobs')
32 end 32 end
  33 +
  34 + Then 'the active main tab should be Messages' do
  35 + ensure_active_main_tab('Messages')
  36 + end
33 end 37 end
features/steps/admin/admin_broadcast_messages.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
  2 + include SharedAuthentication
  3 + include SharedPaths
  4 + include SharedAdmin
  5 +
  6 + step 'application already has admin messages' do
  7 + FactoryGirl.create(:broadcast_message, message: "Migration to new server")
  8 + end
  9 +
  10 + step 'I should be all broadcast messages' do
  11 + page.should have_content "Migration to new server"
  12 + end
  13 +
  14 + step 'submit form with new broadcast message' do
  15 + fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST'
  16 + select '2018', from: "broadcast_message_ends_at_1i"
  17 + click_button "Add broadcast message"
  18 + end
  19 +
  20 + step 'I should be redirected to admin messages page' do
  21 + current_path.should == admin_broadcast_messages_path
  22 + end
  23 +
  24 + step 'I should see newly created broadcast message' do
  25 + page.should have_content 'Application update from 4:00 CST to 5:00 CST'
  26 + end
  27 +end
features/steps/profile/profile.rb
@@ -31,26 +31,49 @@ class Profile &lt; Spinach::FeatureSteps @@ -31,26 +31,49 @@ class Profile &lt; Spinach::FeatureSteps
31 @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png" 31 @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png"
32 end 32 end
33 33
  34 + step 'I should see the "Remove avatar" button' do
  35 + page.should have_link("Remove avatar")
  36 + end
  37 +
  38 + step 'I have an avatar' do
  39 + attach_file(:user_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png'))
  40 + click_button "Save changes"
  41 + @user.reload
  42 + end
  43 +
  44 + step 'I remove my avatar' do
  45 + click_link "Remove avatar"
  46 + @user.reload
  47 + end
  48 +
  49 + step 'I should see my gravatar' do
  50 + @user.avatar?.should be_false
  51 + end
  52 +
  53 + step 'I should not see the "Remove avatar" button' do
  54 + page.should_not have_link("Remove avatar")
  55 + end
  56 +
34 step 'I try change my password w/o old one' do 57 step 'I try change my password w/o old one' do
35 within '.update-password' do 58 within '.update-password' do
36 - fill_in "user_password", with: "222333"  
37 - fill_in "user_password_confirmation", with: "222333" 59 + fill_in "user_password", with: "22233344"
  60 + fill_in "user_password_confirmation", with: "22233344"
38 click_button "Save" 61 click_button "Save"
39 end 62 end
40 end 63 end
41 64
42 step 'I change my password' do 65 step 'I change my password' do
43 within '.update-password' do 66 within '.update-password' do
44 - fill_in "user_current_password", with: "123456"  
45 - fill_in "user_password", with: "222333"  
46 - fill_in "user_password_confirmation", with: "222333" 67 + fill_in "user_current_password", with: "12345678"
  68 + fill_in "user_password", with: "22233344"
  69 + fill_in "user_password_confirmation", with: "22233344"
47 click_button "Save" 70 click_button "Save"
48 end 71 end
49 end 72 end
50 73
51 step 'I unsuccessfully change my password' do 74 step 'I unsuccessfully change my password' do
52 within '.update-password' do 75 within '.update-password' do
53 - fill_in "user_current_password", with: "123456" 76 + fill_in "user_current_password", with: "12345678"
54 fill_in "user_password", with: "password" 77 fill_in "user_password", with: "password"
55 fill_in "user_password_confirmation", with: "confirmation" 78 fill_in "user_password_confirmation", with: "confirmation"
56 click_button "Save" 79 click_button "Save"
@@ -65,10 +88,6 @@ class Profile &lt; Spinach::FeatureSteps @@ -65,10 +88,6 @@ class Profile &lt; Spinach::FeatureSteps
65 page.should have_content "Password doesn't match confirmation" 88 page.should have_content "Password doesn't match confirmation"
66 end 89 end
67 90
68 - step 'I should be redirected to sign in page' do  
69 - current_path.should == new_user_session_path  
70 - end  
71 -  
72 step 'I reset my token' do 91 step 'I reset my token' do
73 within '.update-token' do 92 within '.update-token' do
74 @old_token = @user.private_token 93 @old_token = @user.private_token
features/steps/project/project_browse_commits.rb
@@ -88,4 +88,17 @@ class ProjectBrowseCommits &lt; Spinach::FeatureSteps @@ -88,4 +88,17 @@ class ProjectBrowseCommits &lt; Spinach::FeatureSteps
88 links[0]['href'].should =~ %r{blob/bc3735004cb45cec5e0e4fa92710897a910a5957} 88 links[0]['href'].should =~ %r{blob/bc3735004cb45cec5e0e4fa92710897a910a5957}
89 links[1]['href'].should =~ %r{blob/cc1ba255d6c5ffdce87a357ba7ccc397a4f4026b} 89 links[1]['href'].should =~ %r{blob/cc1ba255d6c5ffdce87a357ba7ccc397a4f4026b}
90 end 90 end
  91 +
  92 + Given 'I click side-by-side diff button' do
  93 + click_link "Side-by-side Diff"
  94 + end
  95 +
  96 + Then 'I see side-by-side diff button' do
  97 + page.should have_content "Side-by-side Diff"
  98 + end
  99 +
  100 + Then 'I see inline diff button' do
  101 + page.should have_content "Inline Diff"
  102 + end
  103 +
91 end 104 end
features/steps/project/project_multiselect_blob.rb 0 → 100644
@@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
  1 +class ProjectMultiselectBlob < Spinach::FeatureSteps
  2 + include SharedAuthentication
  3 + include SharedProject
  4 + include SharedPaths
  5 +
  6 + class << self
  7 + def click_line_steps(*line_numbers)
  8 + line_numbers.each do |line_number|
  9 + step "I click line #{line_number} in file" do
  10 + find("#L#{line_number}").click
  11 + end
  12 +
  13 + step "I shift-click line #{line_number} in file" do
  14 + script = "$('#L#{line_number}').trigger($.Event('click', { shiftKey: true }));"
  15 + page.evaluate_script(script)
  16 + end
  17 + end
  18 + end
  19 +
  20 + def check_state_steps(*ranges)
  21 + ranges.each do |range|
  22 + fragment = range.kind_of?(Array) ? "L#{range.first}-#{range.last}" : "L#{range}"
  23 + pluralization = range.kind_of?(Array) ? "s" : ""
  24 +
  25 + step "I should see \"#{fragment}\" as URI fragment" do
  26 + URI.parse(current_url).fragment.should == fragment
  27 + end
  28 +
  29 + step "I should see line#{pluralization} #{fragment[1..-1]} highlighted" do
  30 + ids = Array(range).map { |n| "LC#{n}" }
  31 + extra = false
  32 +
  33 + highlighted = all("#tree-content-holder .highlight .line.hll")
  34 + highlighted.each do |element|
  35 + extra ||= ids.delete(element[:id]).nil?
  36 + end
  37 +
  38 + extra.should be_false and ids.should be_empty
  39 + end
  40 + end
  41 + end
  42 + end
  43 +
  44 + click_line_steps *Array(1..5)
  45 + check_state_steps *Array(1..5), Array(1..2), Array(1..3), Array(1..4), Array(1..5), Array(3..5)
  46 +
  47 + step 'I go back in history' do
  48 + page.evaluate_script("window.history.back()")
  49 + end
  50 +
  51 + step 'I go forward in history' do
  52 + page.evaluate_script("window.history.forward()")
  53 + end
  54 +
  55 + step 'I click on "Gemfile.lock" file in repo' do
  56 + click_link "Gemfile.lock"
  57 + end
  58 +end
0 \ No newline at end of file 59 \ No newline at end of file
features/steps/project/project_network_graph.rb
@@ -43,13 +43,13 @@ class ProjectNetworkGraph &lt; Spinach::FeatureSteps @@ -43,13 +43,13 @@ class ProjectNetworkGraph &lt; Spinach::FeatureSteps
43 sleep 2 43 sleep 2
44 end 44 end
45 45
46 - Then 'page should have content not cotaining "v2.1.0"' do 46 + Then 'page should have content not containing "v2.1.0"' do
47 within '.network-graph' do 47 within '.network-graph' do
48 page.should have_content 'cleaning' 48 page.should have_content 'cleaning'
49 end 49 end
50 end 50 end
51 51
52 - Then 'page should not have content not cotaining "v2.1.0"' do 52 + Then 'page should not have content not containing "v2.1.0"' do
53 within '.network-graph' do 53 within '.network-graph' do
54 page.should_not have_content 'cleaning' 54 page.should_not have_content 'cleaning'
55 end 55 end
features/steps/project/project_services.rb
@@ -12,6 +12,7 @@ class ProjectServices &lt; Spinach::FeatureSteps @@ -12,6 +12,7 @@ class ProjectServices &lt; Spinach::FeatureSteps
12 page.should have_content 'Campfire' 12 page.should have_content 'Campfire'
13 page.should have_content 'Hipchat' 13 page.should have_content 'Hipchat'
14 page.should have_content 'GitLab CI' 14 page.should have_content 'GitLab CI'
  15 + page.should have_content 'Assembla'
15 end 16 end
16 17
17 And 'I click gitlab-ci service link' do 18 And 'I click gitlab-ci service link' do
@@ -72,4 +73,18 @@ class ProjectServices &lt; Spinach::FeatureSteps @@ -72,4 +73,18 @@ class ProjectServices &lt; Spinach::FeatureSteps
72 Then 'I should see Flowdock service settings saved' do 73 Then 'I should see Flowdock service settings saved' do
73 find_field('Token').value.should == 'verySecret' 74 find_field('Token').value.should == 'verySecret'
74 end 75 end
  76 +
  77 + And 'I click Assembla service link' do
  78 + click_link 'Assembla'
  79 + end
  80 +
  81 + And 'I fill Assembla settings' do
  82 + check 'Active'
  83 + fill_in 'Token', with: 'verySecret'
  84 + click_button 'Save'
  85 + end
  86 +
  87 + Then 'I should see Assembla service settings saved' do
  88 + find_field('Token').value.should == 'verySecret'
  89 + end
75 end 90 end
features/steps/project/redirects.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
  2 + include SharedAuthentication
  3 + include SharedPaths
  4 + include SharedProject
  5 +
  6 + step 'public project "Community"' do
  7 + create :project_with_code, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
  8 + end
  9 +
  10 + step 'private project "Enterprise"' do
  11 + create :project, name: 'Enterprise'
  12 + end
  13 +
  14 + step 'I visit project "Community" page' do
  15 + project = Project.find_by_name('Community')
  16 + visit project_path(project)
  17 + end
  18 +
  19 + step 'I should see project "Community" home page' do
  20 + within '.project-home-title' do
  21 + page.should have_content 'Community'
  22 + end
  23 + end
  24 +
  25 + step 'I visit project "Enterprise" page' do
  26 + project = Project.find_by_name('Enterprise')
  27 + visit project_path(project)
  28 + end
  29 +
  30 + step 'I visit project "CommunityDoesNotExist" page' do
  31 + project = Project.find_by_name('Community')
  32 + visit project_path(project) + 'DoesNotExist'
  33 + end
  34 +end
  35 +
features/steps/public/projects_feature.rb
1 class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps 1 class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
  2 + include SharedAuthentication
2 include SharedPaths 3 include SharedPaths
  4 + include SharedProject
3 5
4 step 'I should see project "Community"' do 6 step 'I should see project "Community"' do
5 page.should have_content "Community" 7 page.should have_content "Community"
@@ -23,11 +25,11 @@ class Spinach::Features::PublicProjectsFeature &lt; Spinach::FeatureSteps @@ -23,11 +25,11 @@ class Spinach::Features::PublicProjectsFeature &lt; Spinach::FeatureSteps
23 end 25 end
24 26
25 step 'public project "Community"' do 27 step 'public project "Community"' do
26 - create :project_with_code, name: 'Community', public: true, default_branch: 'master' 28 + create :project_with_code, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
27 end 29 end
28 30
29 step 'public empty project "Empty Public Project"' do 31 step 'public empty project "Empty Public Project"' do
30 - create :project, name: 'Empty Public Project', public: true 32 + create :project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC
31 end 33 end
32 34
33 step 'I visit empty project page' do 35 step 'I visit empty project page' do
@@ -48,16 +50,48 @@ class Spinach::Features::PublicProjectsFeature &lt; Spinach::FeatureSteps @@ -48,16 +50,48 @@ class Spinach::Features::PublicProjectsFeature &lt; Spinach::FeatureSteps
48 create :project, name: 'Enterprise' 50 create :project, name: 'Enterprise'
49 end 51 end
50 52
  53 + step 'I visit project "Enterprise" page' do
  54 + project = Project.find_by_name('Enterprise')
  55 + visit project_path(project)
  56 + end
  57 +
51 step 'I should see project "Community" home page' do 58 step 'I should see project "Community" home page' do
52 within '.project-home-title' do 59 within '.project-home-title' do
53 page.should have_content 'Community' 60 page.should have_content 'Community'
54 end 61 end
55 end 62 end
56 63
57 - private 64 + step 'internal project "Internal"' do
  65 + create :project_with_code, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
  66 + end
  67 +
  68 + step 'I should see project "Internal"' do
  69 + page.should have_content "Internal"
  70 + end
  71 +
  72 + step 'I should not see project "Internal"' do
  73 + page.should_not have_content "Internal"
  74 + end
  75 +
  76 + step 'I visit project "Internal" page' do
  77 + project = Project.find_by_name('Internal')
  78 + visit project_path(project)
  79 + end
  80 +
  81 + step 'I should see project "Internal" home page' do
  82 + within '.project-home-title' do
  83 + page.should have_content 'Internal'
  84 + end
  85 + end
  86 +
  87 + Then 'I should see a http link to the repository' do
  88 + project = Project.find_by_name 'Community'
  89 + page.should have_field('project_clone', with: project.http_url_to_repo)
  90 + end
58 91
59 - def project  
60 - @project ||= Project.find_by_name("Community") 92 + Then 'I should see a ssh link to the repository' do
  93 + project = Project.find_by_name 'Community'
  94 + page.should have_field('project_clone', with: project.url_to_repo)
61 end 95 end
62 end 96 end
63 97
features/steps/shared/authentication.rb
@@ -12,6 +12,10 @@ module SharedAuthentication @@ -12,6 +12,10 @@ module SharedAuthentication
12 login_as :admin 12 login_as :admin
13 end 13 end
14 14
  15 + step 'I should be redirected to sign in page' do
  16 + current_path.should == new_user_session_path
  17 + end
  18 +
15 def current_user 19 def current_user
16 @user || User.first 20 @user || User.first
17 end 21 end
features/steps/shared/paths.rb
@@ -105,6 +105,10 @@ module SharedPaths @@ -105,6 +105,10 @@ module SharedPaths
105 visit admin_logs_path 105 visit admin_logs_path
106 end 106 end
107 107
  108 + step 'I visit admin messages page' do
  109 + visit admin_broadcast_messages_path
  110 + end
  111 +
108 step 'I visit admin hooks page' do 112 step 'I visit admin hooks page' do
109 visit admin_hooks_path 113 visit admin_hooks_path
110 end 114 end
features/steps/snippets/snippets.rb
@@ -19,7 +19,7 @@ class SnippetsFeature &lt; Spinach::FeatureSteps @@ -19,7 +19,7 @@ class SnippetsFeature &lt; Spinach::FeatureSteps
19 end 19 end
20 20
21 And 'I click link "Destroy"' do 21 And 'I click link "Destroy"' do
22 - click_link "Destroy" 22 + click_link "Remove"
23 end 23 end
24 24
25 And 'I submit new snippet "Personal snippet three"' do 25 And 'I submit new snippet "Personal snippet three"' do
@@ -46,7 +46,7 @@ class SnippetsFeature &lt; Spinach::FeatureSteps @@ -46,7 +46,7 @@ class SnippetsFeature &lt; Spinach::FeatureSteps
46 end 46 end
47 47
48 And 'I uncheck "Private" checkbox' do 48 And 'I uncheck "Private" checkbox' do
49 - find(:xpath, "//input[@id='personal_snippet_private']").set true 49 + choose "Public"
50 click_button "Save" 50 click_button "Save"
51 end 51 end
52 52
lib/api/api.rb
@@ -39,5 +39,7 @@ module API @@ -39,5 +39,7 @@ module API
39 mount DeployKeys 39 mount DeployKeys
40 mount ProjectHooks 40 mount ProjectHooks
41 mount Services 41 mount Services
  42 + mount Files
  43 + mount Namespaces
42 end 44 end
43 end 45 end
lib/api/entities.rb
@@ -24,6 +24,10 @@ module API @@ -24,6 +24,10 @@ module API
24 expose :id, :url, :created_at 24 expose :id, :url, :created_at
25 end 25 end
26 26
  27 + class ProjectHook < Hook
  28 + expose :project_id, :push_events, :issues_events, :merge_requests_events
  29 + end
  30 +
27 class ForkedFromProject < Grape::Entity 31 class ForkedFromProject < Grape::Entity
28 expose :id 32 expose :id
29 expose :name, :name_with_namespace 33 expose :name, :name_with_namespace
@@ -31,11 +35,13 @@ module API @@ -31,11 +35,13 @@ module API
31 end 35 end
32 36
33 class Project < Grape::Entity 37 class Project < Grape::Entity
34 - expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url 38 + expose :id, :description, :default_branch
  39 + expose :public?, as: :public
  40 + expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
35 expose :owner, using: Entities::UserBasic 41 expose :owner, using: Entities::UserBasic
36 expose :name, :name_with_namespace 42 expose :name, :name_with_namespace
37 expose :path, :path_with_namespace 43 expose :path, :path_with_namespace
38 - expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at, :public 44 + expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at
39 expose :namespace 45 expose :namespace
40 expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? } 46 expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? }
41 end 47 end
@@ -136,5 +142,9 @@ module API @@ -136,5 +142,9 @@ module API
136 expose :target_id, :target_type, :author_id 142 expose :target_id, :target_type, :author_id
137 expose :data, :target_title 143 expose :data, :target_title
138 end 144 end
  145 +
  146 + class Namespace < Grape::Entity
  147 + expose :id, :path, :kind
  148 + end
139 end 149 end
140 end 150 end
lib/api/files.rb 0 → 100644
@@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
  1 +module API
  2 + # Projects API
  3 + class Files < Grape::API
  4 + before { authenticate! }
  5 + before { authorize! :push_code, user_project }
  6 +
  7 + resource :projects do
  8 + # Create new file in repository
  9 + #
  10 + # Parameters:
  11 + # file_path (optional) - The path to new file. Ex. lib/class.rb
  12 + # branch_name (required) - The name of branch
  13 + # content (required) - File content
  14 + # commit_message (required) - Commit message
  15 + #
  16 + # Example Request:
  17 + # POST /projects/:id/repository/files
  18 + #
  19 + post ":id/repository/files" do
  20 + required_attributes! [:file_path, :branch_name, :content, :commit_message]
  21 + attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message]
  22 + branch_name = attrs.delete(:branch_name)
  23 + file_path = attrs.delete(:file_path)
  24 + result = ::Files::CreateContext.new(user_project, current_user, attrs, branch_name, file_path).execute
  25 +
  26 + if result[:status] == :success
  27 + status(201)
  28 +
  29 + {
  30 + file_path: file_path,
  31 + branch_name: branch_name
  32 + }
  33 + else
  34 + render_api_error!(result[:error], 400)
  35 + end
  36 + end
  37 +
  38 + # Update existing file in repository
  39 + #
  40 + # Parameters:
  41 + # file_path (optional) - The path to file. Ex. lib/class.rb
  42 + # branch_name (required) - The name of branch
  43 + # content (required) - File content
  44 + # commit_message (required) - Commit message
  45 + #
  46 + # Example Request:
  47 + # PUT /projects/:id/repository/files
  48 + #
  49 + put ":id/repository/files" do
  50 + required_attributes! [:file_path, :branch_name, :content, :commit_message]
  51 + attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message]
  52 + branch_name = attrs.delete(:branch_name)
  53 + file_path = attrs.delete(:file_path)
  54 + result = ::Files::UpdateContext.new(user_project, current_user, attrs, branch_name, file_path).execute
  55 +
  56 + if result[:status] == :success
  57 + status(200)
  58 +
  59 + {
  60 + file_path: file_path,
  61 + branch_name: branch_name
  62 + }
  63 + else
  64 + render_api_error!(result[:error], 400)
  65 + end
  66 + end
  67 +
  68 + # Delete existing file in repository
  69 + #
  70 + # Parameters:
  71 + # file_path (optional) - The path to file. Ex. lib/class.rb
  72 + # branch_name (required) - The name of branch
  73 + # content (required) - File content
  74 + # commit_message (required) - Commit message
  75 + #
  76 + # Example Request:
  77 + # DELETE /projects/:id/repository/files
  78 + #
  79 + delete ":id/repository/files" do
  80 + required_attributes! [:file_path, :branch_name, :commit_message]
  81 + attrs = attributes_for_keys [:file_path, :branch_name, :commit_message]
  82 + branch_name = attrs.delete(:branch_name)
  83 + file_path = attrs.delete(:file_path)
  84 + result = ::Files::DeleteContext.new(user_project, current_user, attrs, branch_name, file_path).execute
  85 +
  86 + if result[:status] == :success
  87 + status(200)
  88 +
  89 + {
  90 + file_path: file_path,
  91 + branch_name: branch_name
  92 + }
  93 + else
  94 + render_api_error!(result[:error], 400)
  95 + end
  96 + end
  97 + end
  98 + end
  99 +end
lib/api/helpers.rb
@@ -6,19 +6,23 @@ module API @@ -6,19 +6,23 @@ module API
6 SUDO_PARAM = :sudo 6 SUDO_PARAM = :sudo
7 7
8 def current_user 8 def current_user
9 - @current_user ||= User.find_by_authentication_token(params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]) 9 + private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
  10 + @current_user ||= User.find_by_authentication_token(private_token)
10 identifier = sudo_identifier() 11 identifier = sudo_identifier()
  12 +
11 # If the sudo is the current user do nothing 13 # If the sudo is the current user do nothing
12 if (identifier && !(@current_user.id == identifier || @current_user.username == identifier)) 14 if (identifier && !(@current_user.id == identifier || @current_user.username == identifier))
13 render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin? 15 render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin?
14 @current_user = User.by_username_or_id(identifier) 16 @current_user = User.by_username_or_id(identifier)
15 not_found!("No user id or username for: #{identifier}") if @current_user.nil? 17 not_found!("No user id or username for: #{identifier}") if @current_user.nil?
16 end 18 end
  19 +
17 @current_user 20 @current_user
18 end 21 end
19 22
20 def sudo_identifier() 23 def sudo_identifier()
21 identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER] 24 identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER]
  25 +
22 # Regex for integers 26 # Regex for integers
23 if (!!(identifier =~ /^[0-9]+$/)) 27 if (!!(identifier =~ /^[0-9]+$/))
24 identifier.to_i 28 identifier.to_i
@@ -29,6 +33,7 @@ module API @@ -29,6 +33,7 @@ module API
29 33
30 def set_current_user_for_thread 34 def set_current_user_for_thread
31 Thread.current[:current_user] = current_user 35 Thread.current[:current_user] = current_user
  36 +
32 begin 37 begin
33 yield 38 yield
34 ensure 39 ensure
lib/api/namespaces.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +module API
  2 + # namespaces API
  3 + class Namespaces < Grape::API
  4 + before {
  5 + authenticate!
  6 + authenticated_as_admin!
  7 + }
  8 +
  9 + resource :namespaces do
  10 + # Get a namespaces list
  11 + #
  12 + # Example Request:
  13 + # GET /namespaces
  14 + get do
  15 + @namespaces = Namespace.scoped
  16 + @namespaces = @namespaces.search(params[:search]) if params[:search].present?
  17 + @namespaces = paginate @namespaces
  18 +
  19 + present @namespaces, with: Entities::Namespace
  20 + end
  21 + end
  22 + end
  23 +end
lib/api/project_hooks.rb
@@ -22,7 +22,7 @@ module API @@ -22,7 +22,7 @@ module API
22 # GET /projects/:id/hooks 22 # GET /projects/:id/hooks
23 get ":id/hooks" do 23 get ":id/hooks" do
24 @hooks = paginate user_project.hooks 24 @hooks = paginate user_project.hooks
25 - present @hooks, with: Entities::Hook 25 + present @hooks, with: Entities::ProjectHook
26 end 26 end
27 27
28 # Get a project hook 28 # Get a project hook
@@ -34,7 +34,7 @@ module API @@ -34,7 +34,7 @@ module API
34 # GET /projects/:id/hooks/:hook_id 34 # GET /projects/:id/hooks/:hook_id
35 get ":id/hooks/:hook_id" do 35 get ":id/hooks/:hook_id" do
36 @hook = user_project.hooks.find(params[:hook_id]) 36 @hook = user_project.hooks.find(params[:hook_id])
37 - present @hook, with: Entities::Hook 37 + present @hook, with: Entities::ProjectHook
38 end 38 end
39 39
40 40
@@ -47,10 +47,11 @@ module API @@ -47,10 +47,11 @@ module API
47 # POST /projects/:id/hooks 47 # POST /projects/:id/hooks
48 post ":id/hooks" do 48 post ":id/hooks" do
49 required_attributes! [:url] 49 required_attributes! [:url]
  50 + attrs = attributes_for_keys [:url, :push_events, :issues_events, :merge_requests_events]
  51 + @hook = user_project.hooks.new(attrs)
50 52
51 - @hook = user_project.hooks.new({"url" => params[:url]})  
52 if @hook.save 53 if @hook.save
53 - present @hook, with: Entities::Hook 54 + present @hook, with: Entities::ProjectHook
54 else 55 else
55 if @hook.errors[:url].present? 56 if @hook.errors[:url].present?
56 error!("Invalid url given", 422) 57 error!("Invalid url given", 422)
@@ -70,10 +71,10 @@ module API @@ -70,10 +71,10 @@ module API
70 put ":id/hooks/:hook_id" do 71 put ":id/hooks/:hook_id" do
71 @hook = user_project.hooks.find(params[:hook_id]) 72 @hook = user_project.hooks.find(params[:hook_id])
72 required_attributes! [:url] 73 required_attributes! [:url]
  74 + attrs = attributes_for_keys [:url, :push_events, :issues_events, :merge_requests_events]
73 75
74 - attrs = attributes_for_keys [:url]  
75 if @hook.update_attributes attrs 76 if @hook.update_attributes attrs
76 - present @hook, with: Entities::Hook 77 + present @hook, with: Entities::ProjectHook
77 else 78 else
78 if @hook.errors[:url].present? 79 if @hook.errors[:url].present?
79 error!("Invalid url given", 422) 80 error!("Invalid url given", 422)
lib/api/projects.rb
@@ -11,6 +11,13 @@ module API @@ -11,6 +11,13 @@ module API
11 end 11 end
12 not_found! 12 not_found!
13 end 13 end
  14 +
  15 + def map_public_to_visibility_level(attrs)
  16 + publik = attrs.delete(:public)
  17 + publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik)
  18 + attrs[:visibility_level] = Gitlab::VisibilityLevel::PUBLIC if !attrs[:visibility_level].present? && publik == true
  19 + attrs
  20 + end
14 end 21 end
15 22
16 # Get a projects list for authenticated user 23 # Get a projects list for authenticated user
@@ -31,6 +38,16 @@ module API @@ -31,6 +38,16 @@ module API
31 present @projects, with: Entities::Project 38 present @projects, with: Entities::Project
32 end 39 end
33 40
  41 + # Get all projects for admin user
  42 + #
  43 + # Example Request:
  44 + # GET /projects/all
  45 + get '/all' do
  46 + authenticated_as_admin!
  47 + @projects = paginate Project
  48 + present @projects, with: Entities::Project
  49 + end
  50 +
34 # Get a single project 51 # Get a single project
35 # 52 #
36 # Parameters: 53 # Parameters:
@@ -60,14 +77,14 @@ module API @@ -60,14 +77,14 @@ module API
60 # Parameters: 77 # Parameters:
61 # name (required) - name for new project 78 # name (required) - name for new project
62 # description (optional) - short project description 79 # description (optional) - short project description
63 - # default_branch (optional) - 'master' by default  
64 # issues_enabled (optional) 80 # issues_enabled (optional)
65 # wall_enabled (optional) 81 # wall_enabled (optional)
66 # merge_requests_enabled (optional) 82 # merge_requests_enabled (optional)
67 # wiki_enabled (optional) 83 # wiki_enabled (optional)
68 # snippets_enabled (optional) 84 # snippets_enabled (optional)
69 # namespace_id (optional) - defaults to user namespace 85 # namespace_id (optional) - defaults to user namespace
70 - # public (optional) - false by default 86 + # public (optional) - if true same as setting visibility_level = 20
  87 + # visibility_level (optional) - 0 by default
71 # Example Request 88 # Example Request
72 # POST /projects 89 # POST /projects
73 post do 90 post do
@@ -75,14 +92,15 @@ module API @@ -75,14 +92,15 @@ module API
75 attrs = attributes_for_keys [:name, 92 attrs = attributes_for_keys [:name,
76 :path, 93 :path,
77 :description, 94 :description,
78 - :default_branch,  
79 :issues_enabled, 95 :issues_enabled,
80 :wall_enabled, 96 :wall_enabled,
81 :merge_requests_enabled, 97 :merge_requests_enabled,
82 :wiki_enabled, 98 :wiki_enabled,
83 :snippets_enabled, 99 :snippets_enabled,
84 :namespace_id, 100 :namespace_id,
85 - :public] 101 + :public,
  102 + :visibility_level]
  103 + attrs = map_public_to_visibility_level(attrs)
86 @project = ::Projects::CreateContext.new(current_user, attrs).execute 104 @project = ::Projects::CreateContext.new(current_user, attrs).execute
87 if @project.saved? 105 if @project.saved?
88 present @project, with: Entities::Project 106 present @project, with: Entities::Project
@@ -106,7 +124,8 @@ module API @@ -106,7 +124,8 @@ module API
106 # merge_requests_enabled (optional) 124 # merge_requests_enabled (optional)
107 # wiki_enabled (optional) 125 # wiki_enabled (optional)
108 # snippets_enabled (optional) 126 # snippets_enabled (optional)
109 - # public (optional) 127 + # public (optional) - if true same as setting visibility_level = 20
  128 + # visibility_level (optional)
110 # Example Request 129 # Example Request
111 # POST /projects/user/:user_id 130 # POST /projects/user/:user_id
112 post "user/:user_id" do 131 post "user/:user_id" do
@@ -120,7 +139,9 @@ module API @@ -120,7 +139,9 @@ module API
120 :merge_requests_enabled, 139 :merge_requests_enabled,
121 :wiki_enabled, 140 :wiki_enabled,
122 :snippets_enabled, 141 :snippets_enabled,
123 - :public] 142 + :public,
  143 + :visibility_level]
  144 + attrs = map_public_to_visibility_level(attrs)
124 @project = ::Projects::CreateContext.new(user, attrs).execute 145 @project = ::Projects::CreateContext.new(user, attrs).execute
125 if @project.saved? 146 if @project.saved?
126 present @project, with: Entities::Project 147 present @project, with: Entities::Project
@@ -282,7 +303,8 @@ module API @@ -282,7 +303,8 @@ module API
282 # GET /projects/search/:query 303 # GET /projects/search/:query
283 get "/search/:query" do 304 get "/search/:query" do
284 ids = current_user.authorized_projects.map(&:id) 305 ids = current_user.authorized_projects.map(&:id)
285 - projects = Project.where("(id in (?) OR public = true) AND (name LIKE (?))", ids, "%#{params[:query]}%") 306 + visibility_levels = [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ]
  307 + projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%")
286 present paginate(projects), with: Entities::Project 308 present paginate(projects), with: Entities::Project
287 end 309 end
288 end 310 end
lib/backup/database.rb
@@ -13,20 +13,20 @@ module Backup @@ -13,20 +13,20 @@ module Backup
13 def dump 13 def dump
14 case config["adapter"] 14 case config["adapter"]
15 when /^mysql/ then 15 when /^mysql/ then
16 - system("mysqldump #{mysql_args} #{config['database']} > #{db_file_name}") 16 + system('mysqldump', *mysql_args, config['database'], out: db_file_name)
17 when "postgresql" then 17 when "postgresql" then
18 pg_env 18 pg_env
19 - system("pg_dump #{config['database']} > #{db_file_name}") 19 + system('pg_dump', config['database'], out: db_file_name)
20 end 20 end
21 end 21 end
22 22
23 def restore 23 def restore
24 case config["adapter"] 24 case config["adapter"]
25 when /^mysql/ then 25 when /^mysql/ then
26 - system("mysql #{mysql_args} #{config['database']} < #{db_file_name}") 26 + system('mysql', *mysql_args, config['database'], in: db_file_name)
27 when "postgresql" then 27 when "postgresql" then
28 pg_env 28 pg_env
29 - system("psql #{config['database']} -f #{db_file_name}") 29 + system('psql', config['database'], '-f', db_file_name)
30 end 30 end
31 end 31 end
32 32
@@ -45,7 +45,7 @@ module Backup @@ -45,7 +45,7 @@ module Backup
45 'encoding' => '--default-character-set', 45 'encoding' => '--default-character-set',
46 'password' => '--password' 46 'password' => '--password'
47 } 47 }
48 - args.map { |opt, arg| "#{arg}='#{config[opt]}'" if config[opt] }.compact.join(' ') 48 + args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
49 end 49 end
50 50
51 def pg_env 51 def pg_env
lib/backup/manager.rb
1 module Backup 1 module Backup
2 class Manager 2 class Manager
  3 + BACKUP_CONTENTS = %w{repositories/ db/ uploads/ backup_information.yml}
  4 +
3 def pack 5 def pack
4 # saving additional informations 6 # saving additional informations
5 s = {} 7 s = {}
@@ -16,7 +18,7 @@ module Backup @@ -16,7 +18,7 @@ module Backup
16 18
17 # create archive 19 # create archive
18 print "Creating backup archive: #{s[:backup_created_at].to_i}_gitlab_backup.tar ... " 20 print "Creating backup archive: #{s[:backup_created_at].to_i}_gitlab_backup.tar ... "
19 - if Kernel.system("tar -cf #{s[:backup_created_at].to_i}_gitlab_backup.tar repositories/ db/ uploads/ backup_information.yml") 21 + if Kernel.system('tar', '-cf', "#{s[:backup_created_at].to_i}_gitlab_backup.tar", *BACKUP_CONTENTS)
20 puts "done".green 22 puts "done".green
21 else 23 else
22 puts "failed".red 24 puts "failed".red
@@ -25,7 +27,7 @@ module Backup @@ -25,7 +27,7 @@ module Backup
25 27
26 def cleanup 28 def cleanup
27 print "Deleting tmp directories ... " 29 print "Deleting tmp directories ... "
28 - if Kernel.system("rm -rf repositories/ db/ uploads/ backup_information.yml") 30 + if Kernel.system('rm', '-rf', *BACKUP_CONTENTS)
29 puts "done".green 31 puts "done".green
30 else 32 else
31 puts "failed".red 33 puts "failed".red
@@ -44,7 +46,7 @@ module Backup @@ -44,7 +46,7 @@ module Backup
44 file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ } 46 file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
45 file_list.sort.each do |timestamp| 47 file_list.sort.each do |timestamp|
46 if Time.at(timestamp) < (Time.now - keep_time) 48 if Time.at(timestamp) < (Time.now - keep_time)
47 - if system("rm #{timestamp}_gitlab_backup.tar") 49 + if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar))
48 removed += 1 50 removed += 1
49 end 51 end
50 end 52 end
@@ -75,7 +77,7 @@ module Backup @@ -75,7 +77,7 @@ module Backup
75 end 77 end
76 78
77 print "Unpacking backup ... " 79 print "Unpacking backup ... "
78 - unless Kernel.system("tar -xf #{tar_file}") 80 + unless Kernel.system(*%W(tar -xf #{tar_file}))
79 puts "failed".red 81 puts "failed".red
80 exit 1 82 exit 1
81 else 83 else
lib/backup/repository.rb
@@ -18,7 +18,7 @@ module Backup @@ -18,7 +18,7 @@ module Backup
18 # Create namespace dir if missing 18 # Create namespace dir if missing
19 FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace 19 FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
20 20
21 - if system("cd #{path_to_repo(project)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(project)} --all > /dev/null 2>&1") 21 + if system(*%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all), silent)
22 puts "[DONE]".green 22 puts "[DONE]".green
23 else 23 else
24 puts "[FAILED]".red 24 puts "[FAILED]".red
@@ -30,7 +30,7 @@ module Backup @@ -30,7 +30,7 @@ module Backup
30 print " * #{wiki.path_with_namespace} ... " 30 print " * #{wiki.path_with_namespace} ... "
31 if wiki.empty? 31 if wiki.empty?
32 puts " [SKIPPED]".cyan 32 puts " [SKIPPED]".cyan
33 - elsif system("cd #{path_to_repo(wiki)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(wiki)} --all > /dev/null 2>&1") 33 + elsif system(*%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all), silent)
34 puts " [DONE]".green 34 puts " [DONE]".green
35 else 35 else
36 puts " [FAILED]".red 36 puts " [FAILED]".red
@@ -53,7 +53,7 @@ module Backup @@ -53,7 +53,7 @@ module Backup
53 53
54 project.namespace.ensure_dir_exist if project.namespace 54 project.namespace.ensure_dir_exist if project.namespace
55 55
56 - if system("git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)} > /dev/null 2>&1") 56 + if system(*%W(git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)}), silent)
57 puts "[DONE]".green 57 puts "[DONE]".green
58 else 58 else
59 puts "[FAILED]".red 59 puts "[FAILED]".red
@@ -63,7 +63,7 @@ module Backup @@ -63,7 +63,7 @@ module Backup
63 63
64 if File.exists?(path_to_bundle(wiki)) 64 if File.exists?(path_to_bundle(wiki))
65 print " * #{wiki.path_with_namespace} ... " 65 print " * #{wiki.path_with_namespace} ... "
66 - if system("git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)} > /dev/null 2>&1") 66 + if system(*%W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)}), silent)
67 puts " [DONE]".green 67 puts " [DONE]".green
68 else 68 else
69 puts " [FAILED]".red 69 puts " [FAILED]".red
@@ -73,7 +73,7 @@ module Backup @@ -73,7 +73,7 @@ module Backup
73 73
74 print 'Put GitLab hooks in repositories dirs'.yellow 74 print 'Put GitLab hooks in repositories dirs'.yellow
75 gitlab_shell_user_home = File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") 75 gitlab_shell_user_home = File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}")
76 - if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh #{Gitlab.config.gitlab_shell.repos_path}") 76 + if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh", Gitlab.config.gitlab_shell.repos_path)
77 puts " [DONE]".green 77 puts " [DONE]".green
78 else 78 else
79 puts " [FAILED]".red 79 puts " [FAILED]".red
@@ -103,5 +103,9 @@ module Backup @@ -103,5 +103,9 @@ module Backup
103 FileUtils.rm_rf(backup_repos_path) 103 FileUtils.rm_rf(backup_repos_path)
104 FileUtils.mkdir_p(backup_repos_path) 104 FileUtils.mkdir_p(backup_repos_path)
105 end 105 end
  106 +
  107 + def silent
  108 + {err: '/dev/null', out: '/dev/null'}
  109 + end
106 end 110 end
107 end 111 end
lib/backup/uploads.rb
@@ -19,7 +19,7 @@ module Backup @@ -19,7 +19,7 @@ module Backup
19 19
20 FileUtils.cp_r(backup_uploads_dir, app_uploads_dir) 20 FileUtils.cp_r(backup_uploads_dir, app_uploads_dir)
21 end 21 end
22 - 22 +
23 def backup_existing_uploads_dir 23 def backup_existing_uploads_dir
24 if File.exists?(app_uploads_dir) 24 if File.exists?(app_uploads_dir)
25 FileUtils.mv(app_uploads_dir, Rails.root.join('public', "uploads.#{Time.now.to_i}")) 25 FileUtils.mv(app_uploads_dir, Rails.root.join('public', "uploads.#{Time.now.to_i}"))
lib/gitlab/backend/grack_auth.rb
@@ -58,7 +58,7 @@ module Grack @@ -58,7 +58,7 @@ module Grack
58 end 58 end
59 59
60 else 60 else
61 - return unauthorized unless project.public 61 + return unauthorized unless project.public?
62 end 62 end
63 63
64 if authorized_git_request? 64 if authorized_git_request?
@@ -80,15 +80,19 @@ module Grack @@ -80,15 +80,19 @@ module Grack
80 def authorize_request(service) 80 def authorize_request(service)
81 case service 81 case service
82 when 'git-upload-pack' 82 when 'git-upload-pack'
83 - project.public || can?(user, :download_code, project) 83 + can?(user, :download_code, project)
84 when'git-receive-pack' 84 when'git-receive-pack'
85 - action = if project.protected_branch?(ref)  
86 - :push_code_to_protected_branches  
87 - else  
88 - :push_code  
89 - end 85 + refs.each do |ref|
  86 + action = if project.protected_branch?(ref)
  87 + :push_code_to_protected_branches
  88 + else
  89 + :push_code
  90 + end
  91 +
  92 + return false unless can?(user, action, project)
  93 + end
90 94
91 - can?(user, action, project) 95 + true
92 else 96 else
93 false 97 false
94 end 98 end
@@ -108,11 +112,11 @@ module Grack @@ -108,11 +112,11 @@ module Grack
108 @project ||= project_by_path(@request.path_info) 112 @project ||= project_by_path(@request.path_info)
109 end 113 end
110 114
111 - def ref  
112 - @ref ||= parse_ref 115 + def refs
  116 + @refs ||= parse_refs
113 end 117 end
114 118
115 - def parse_ref 119 + def parse_refs
116 input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/ 120 input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
117 Zlib::GzipReader.new(@request.body).read 121 Zlib::GzipReader.new(@request.body).read
118 else 122 else
@@ -121,7 +125,15 @@ module Grack @@ -121,7 +125,15 @@ module Grack
121 125
122 # Need to reset seek point 126 # Need to reset seek point
123 @request.body.rewind 127 @request.body.rewind
124 - /refs\/heads\/([\/\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last 128 +
  129 + # Parse refs
  130 + refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact
  131 +
  132 + # Cleanup grabare from refs
  133 + # if push to multiple branches
  134 + refs.map do |ref|
  135 + ref.gsub(/00.*/, "")
  136 + end
125 end 137 end
126 end 138 end
127 end 139 end
lib/gitlab/backend/shell.rb
@@ -196,6 +196,15 @@ module Gitlab @@ -196,6 +196,15 @@ module Gitlab
196 Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" 196 Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git"
197 end 197 end
198 198
  199 + # Return GitLab shell version
  200 + def version
  201 + gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION"
  202 +
  203 + if File.readable?(gitlab_shell_version_file)
  204 + File.read(gitlab_shell_version_file)
  205 + end
  206 + end
  207 +
199 protected 208 protected
200 209
201 def gitlab_shell_user_home 210 def gitlab_shell_user_home
lib/gitlab/ldap/user.rb
@@ -23,8 +23,8 @@ module Gitlab @@ -23,8 +23,8 @@ module Gitlab
23 # Look for user with same emails 23 # Look for user with same emails
24 # 24 #
25 # Possible cases: 25 # Possible cases:
26 - # * When user already has account and need to link his LDAP account.  
27 - # * LDAP uid changed for user with same email and we need to update his uid 26 + # * When user already has account and need to link their LDAP account.
  27 + # * LDAP uid changed for user with same email and we need to update their uid
28 # 28 #
29 user = find_user(email) 29 user = find_user(email)
30 30
@@ -47,7 +47,7 @@ module Gitlab @@ -47,7 +47,7 @@ module Gitlab
47 user = model.find_by_email(email) 47 user = model.find_by_email(email)
48 48
49 # If no user found and allow_username_or_email_login is true 49 # If no user found and allow_username_or_email_login is true
50 - # we look for user by extracting part of his email 50 + # we look for user by extracting part of their email
51 if !user && email && ldap_conf['allow_username_or_email_login'] 51 if !user && email && ldap_conf['allow_username_or_email_login']
52 uname = email.partition('@').first 52 uname = email.partition('@').first
53 user = model.find_by_username(uname) 53 user = model.find_by_username(uname)
lib/gitlab/regex.rb
@@ -44,7 +44,7 @@ module Gitlab @@ -44,7 +44,7 @@ module Gitlab
44 protected 44 protected
45 45
46 def default_regex 46 def default_regex
47 - /\A[a-zA-Z0-9][a-zA-Z0-9_\-\.]*\z/ 47 + /\A[a-zA-Z0-9][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
48 end 48 end
49 end 49 end
50 end 50 end
lib/gitlab/satellite/files/delete_file_action.rb 0 → 100644
@@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
  1 +require_relative 'file_action'
  2 +
  3 +module Gitlab
  4 + module Satellite
  5 + class DeleteFileAction < FileAction
  6 + # Deletes file and creates a new commit for it
  7 + #
  8 + # Returns false if committing the change fails
  9 + # Returns false if pushing from the satellite to bare repo failed or was rejected
  10 + # Returns true otherwise
  11 + def commit!(content, commit_message)
  12 + in_locked_and_timed_satellite do |repo|
  13 + prepare_satellite!(repo)
  14 +
  15 + # create target branch in satellite at the corresponding commit from bare repo
  16 + repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
  17 +
  18 + # update the file in the satellite's working dir
  19 + file_path_in_satellite = File.join(repo.working_dir, file_path)
  20 +
  21 + # Prevent relative links
  22 + unless safe_path?(file_path_in_satellite)
  23 + Gitlab::GitLogger.error("FileAction: Relative path not allowed")
  24 + return false
  25 + end
  26 +
  27 + File.delete(file_path_in_satellite)
  28 +
  29 + # add removed file
  30 + repo.remove(file_path_in_satellite)
  31 +
  32 + # commit the changes
  33 + # will raise CommandFailed when commit fails
  34 + repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
  35 +
  36 +
  37 + # push commit back to bare repo
  38 + # will raise CommandFailed when push fails
  39 + repo.git.push({raise: true, timeout: true}, :origin, ref)
  40 +
  41 + # everything worked
  42 + true
  43 + end
  44 + rescue Grit::Git::CommandFailed => ex
  45 + Gitlab::GitLogger.error(ex.message)
  46 + false
  47 + end
  48 + end
  49 + end
  50 +end
lib/gitlab/satellite/files/edit_file_action.rb
@@ -8,19 +8,24 @@ module Gitlab @@ -8,19 +8,24 @@ module Gitlab
8 # 8 #
9 # Returns false if the ref has been updated while editing the file 9 # Returns false if the ref has been updated while editing the file
10 # Returns false if committing the change fails 10 # Returns false if committing the change fails
11 - # Returns false if pushing from the satellite to Gitolite failed or was rejected 11 + # Returns false if pushing from the satellite to bare repo failed or was rejected
12 # Returns true otherwise 12 # Returns true otherwise
13 - def commit!(content, commit_message, last_commit)  
14 - return false unless can_edit?(last_commit)  
15 - 13 + def commit!(content, commit_message)
16 in_locked_and_timed_satellite do |repo| 14 in_locked_and_timed_satellite do |repo|
17 prepare_satellite!(repo) 15 prepare_satellite!(repo)
18 16
19 - # create target branch in satellite at the corresponding commit from Gitolite 17 + # create target branch in satellite at the corresponding commit from bare repo
20 repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}") 18 repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
21 19
22 # update the file in the satellite's working dir 20 # update the file in the satellite's working dir
23 file_path_in_satellite = File.join(repo.working_dir, file_path) 21 file_path_in_satellite = File.join(repo.working_dir, file_path)
  22 +
  23 + # Prevent relative links
  24 + unless safe_path?(file_path_in_satellite)
  25 + Gitlab::GitLogger.error("FileAction: Relative path not allowed")
  26 + return false
  27 + end
  28 +
24 File.open(file_path_in_satellite, 'w') { |f| f.write(content) } 29 File.open(file_path_in_satellite, 'w') { |f| f.write(content) }
25 30
26 # commit the changes 31 # commit the changes
@@ -28,7 +33,7 @@ module Gitlab @@ -28,7 +33,7 @@ module Gitlab
28 repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) 33 repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
29 34
30 35
31 - # push commit back to Gitolite 36 + # push commit back to bare repo
32 # will raise CommandFailed when push fails 37 # will raise CommandFailed when push fails
33 repo.git.push({raise: true, timeout: true}, :origin, ref) 38 repo.git.push({raise: true, timeout: true}, :origin, ref)
34 39
lib/gitlab/satellite/files/file_action.rb
@@ -9,11 +9,8 @@ module Gitlab @@ -9,11 +9,8 @@ module Gitlab
9 @ref = ref 9 @ref = ref
10 end 10 end
11 11
12 - protected  
13 -  
14 - def can_edit?(last_commit)  
15 - current_last_commit = Gitlab::Git::Commit.last_for_path(@project.repository, ref, file_path).sha  
16 - last_commit == current_last_commit 12 + def safe_path?(path)
  13 + File.absolute_path(path) == path
17 end 14 end
18 end 15 end
19 end 16 end
lib/gitlab/satellite/files/new_file_action.rb
@@ -7,17 +7,28 @@ module Gitlab @@ -7,17 +7,28 @@ module Gitlab
7 # 7 #
8 # Returns false if the ref has been updated while editing the file 8 # Returns false if the ref has been updated while editing the file
9 # Returns false if committing the change fails 9 # Returns false if committing the change fails
10 - # Returns false if pushing from the satellite to Gitolite failed or was rejected 10 + # Returns false if pushing from the satellite to bare repo failed or was rejected
11 # Returns true otherwise 11 # Returns true otherwise
12 - def commit!(content, commit_message, file_name) 12 + def commit!(content, commit_message)
13 in_locked_and_timed_satellite do |repo| 13 in_locked_and_timed_satellite do |repo|
14 prepare_satellite!(repo) 14 prepare_satellite!(repo)
15 15
16 - # create target branch in satellite at the corresponding commit from Gitolite 16 + # create target branch in satellite at the corresponding commit from bare repo
17 repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}") 17 repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
18 18
19 - # update the file in the satellite's working dir  
20 - file_path_in_satellite = File.join(repo.working_dir, file_path, file_name) 19 + file_path_in_satellite = File.join(repo.working_dir, file_path)
  20 + dir_name_in_satellite = File.dirname(file_path_in_satellite)
  21 +
  22 + # Prevent relative links
  23 + unless safe_path?(file_path_in_satellite)
  24 + Gitlab::GitLogger.error("FileAction: Relative path not allowed")
  25 + return false
  26 + end
  27 +
  28 + # Create dir if not exists
  29 + FileUtils.mkdir_p(dir_name_in_satellite)
  30 +
  31 + # Write file
21 File.open(file_path_in_satellite, 'w') { |f| f.write(content) } 32 File.open(file_path_in_satellite, 'w') { |f| f.write(content) }
22 33
23 # add new file 34 # add new file
@@ -28,7 +39,7 @@ module Gitlab @@ -28,7 +39,7 @@ module Gitlab
28 repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) 39 repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
29 40
30 41
31 - # push commit back to Gitolite 42 + # push commit back to bare repo
32 # will raise CommandFailed when push fails 43 # will raise CommandFailed when push fails
33 repo.git.push({raise: true, timeout: true}, :origin, ref) 44 repo.git.push({raise: true, timeout: true}, :origin, ref)
34 45
lib/gitlab/satellite/merge_action.rb
@@ -28,7 +28,7 @@ module Gitlab @@ -28,7 +28,7 @@ module Gitlab
28 in_locked_and_timed_satellite do |merge_repo| 28 in_locked_and_timed_satellite do |merge_repo|
29 prepare_satellite!(merge_repo) 29 prepare_satellite!(merge_repo)
30 if merge_in_satellite!(merge_repo) 30 if merge_in_satellite!(merge_repo)
31 - # push merge back to Gitolite 31 + # push merge back to bare repo
32 # will raise CommandFailed when push fails 32 # will raise CommandFailed when push fails
33 merge_repo.git.push(default_options, :origin, merge_request.target_branch) 33 merge_repo.git.push(default_options, :origin, merge_request.target_branch)
34 # remove source branch 34 # remove source branch
lib/gitlab/satellite/satellite.rb
@@ -123,7 +123,7 @@ module Gitlab @@ -123,7 +123,7 @@ module Gitlab
123 remotes.each { |name| repo.git.remote(default_options,'rm', name)} 123 remotes.each { |name| repo.git.remote(default_options,'rm', name)}
124 end 124 end
125 125
126 - # Updates the satellite from Gitolite 126 + # Updates the satellite from bare repo
127 # 127 #
128 # Note: this will only update remote branches (i.e. origin/*) 128 # Note: this will only update remote branches (i.e. origin/*)
129 def update_from_source! 129 def update_from_source!
lib/gitlab/visibility_level.rb 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  1 +# Gitlab::VisibilityLevel module
  2 +#
  3 +# Define allowed public modes that can be used for
  4 +# GitLab projects to determine project public mode
  5 +#
  6 +module Gitlab
  7 + module VisibilityLevel
  8 + PRIVATE = 0
  9 + INTERNAL = 10
  10 + PUBLIC = 20
  11 +
  12 + class << self
  13 + def values
  14 + options.values
  15 + end
  16 +
  17 + def options
  18 + {
  19 + 'Private' => PRIVATE,
  20 + 'Internal' => INTERNAL,
  21 + 'Public' => PUBLIC
  22 + }
  23 + end
  24 +
  25 + def allowed_for?(user, level)
  26 + user.is_admin? || !Gitlab.config.gitlab.restricted_visibility_levels.include?(level)
  27 + end
  28 + end
  29 +
  30 + def private?
  31 + visibility_level_field == PRIVATE
  32 + end
  33 +
  34 + def internal?
  35 + visibility_level_field == INTERNAL
  36 + end
  37 +
  38 + def public?
  39 + visibility_level_field == PUBLIC
  40 + end
  41 + end
  42 +end
lib/redcarpet/render/gitlab_html.rb
@@ -36,7 +36,7 @@ class Redcarpet::Render::GitlabHTML &lt; Redcarpet::Render::HTML @@ -36,7 +36,7 @@ class Redcarpet::Render::GitlabHTML &lt; Redcarpet::Render::HTML
36 36
37 def preprocess(full_document) 37 def preprocess(full_document)
38 if @project 38 if @project
39 - h.create_relative_links(full_document, @project.path_with_namespace, @ref, @request_path, is_wiki?) 39 + h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?)
40 else 40 else
41 full_document 41 full_document
42 end 42 end
lib/support/init.d/gitlab
@@ -31,6 +31,8 @@ sidekiq_pid_path=&quot;$pid_path/sidekiq.pid&quot; @@ -31,6 +31,8 @@ sidekiq_pid_path=&quot;$pid_path/sidekiq.pid&quot;
31 31
32 ### Here ends user configuration ### 32 ### Here ends user configuration ###
33 33
  34 +# Read configuration variable file if it is present
  35 +test -f /etc/default/gitlab && . /etc/default/gitlab
34 36
35 # Switch to the app_user if it is not he/she who is running the script. 37 # Switch to the app_user if it is not he/she who is running the script.
36 if [ "$USER" != "$app_user" ]; then 38 if [ "$USER" != "$app_user" ]; then
lib/support/nginx/gitlab
@@ -11,6 +11,9 @@ server { @@ -11,6 +11,9 @@ server {
11 server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com; 11 server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com;
12 server_tokens off; # don't show the version number, a security best practice 12 server_tokens off; # don't show the version number, a security best practice
13 root /home/git/gitlab/public; 13 root /home/git/gitlab/public;
  14 +
  15 + # Set value of client_max_body_size to at least the value of git.max_size in gitlab.yml
  16 + client_max_body_size 5m;
14 17
15 # individual nginx logs for this gitlab vhost 18 # individual nginx logs for this gitlab vhost
16 access_log /var/log/nginx/gitlab_access.log; 19 access_log /var/log/nginx/gitlab_access.log;
lib/tasks/gitlab/check.rake
@@ -3,6 +3,7 @@ namespace :gitlab do @@ -3,6 +3,7 @@ namespace :gitlab do
3 task check: %w{gitlab:env:check 3 task check: %w{gitlab:env:check
4 gitlab:gitlab_shell:check 4 gitlab:gitlab_shell:check
5 gitlab:sidekiq:check 5 gitlab:sidekiq:check
  6 + gitlab:ldap:check
6 gitlab:app:check} 7 gitlab:app:check}
7 8
8 9
@@ -611,10 +612,7 @@ namespace :gitlab do @@ -611,10 +612,7 @@ namespace :gitlab do
611 end 612 end
612 613
613 def gitlab_shell_version 614 def gitlab_shell_version
614 - gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION"  
615 - if File.readable?(gitlab_shell_version_file)  
616 - File.read(gitlab_shell_version_file)  
617 - end 615 + Gitlab::Shell.new.version
618 end 616 end
619 617
620 def has_gitlab_shell3? 618 def has_gitlab_shell3?
@@ -682,6 +680,44 @@ namespace :gitlab do @@ -682,6 +680,44 @@ namespace :gitlab do
682 end 680 end
683 end 681 end
684 682
  683 + namespace :ldap do
  684 + task :check, [:limit] => :environment do |t, args|
  685 + args.with_defaults(limit: 100)
  686 + warn_user_is_not_gitlab
  687 + start_checking "LDAP"
  688 +
  689 + if ldap_config.enabled
  690 + print_users(args.limit)
  691 + else
  692 + puts 'LDAP is disabled in config/gitlab.yml'
  693 + end
  694 +
  695 + finished_checking "LDAP"
  696 + end
  697 +
  698 + def print_users(limit)
  699 + puts "LDAP users with access to your GitLab server (limit: #{limit}):"
  700 + ldap.search(attributes: attributes, filter: filter, size: limit, return_result: false) do |entry|
  701 + puts "DN: #{entry.dn}\t#{ldap_config.uid}: #{entry[ldap_config.uid]}"
  702 + end
  703 + end
  704 +
  705 + def attributes
  706 + [ldap_config.uid]
  707 + end
  708 +
  709 + def filter
  710 + Net::LDAP::Filter.present?(ldap_config.uid)
  711 + end
  712 +
  713 + def ldap
  714 + @ldap ||= OmniAuth::LDAP::Adaptor.new(ldap_config).connection
  715 + end
  716 +
  717 + def ldap_config
  718 + @ldap_config ||= Gitlab.config.ldap
  719 + end
  720 + end
685 721
686 # Helper methods 722 # Helper methods
687 ########################## 723 ##########################
@@ -736,7 +772,7 @@ namespace :gitlab do @@ -736,7 +772,7 @@ namespace :gitlab do
736 end 772 end
737 773
738 def check_gitlab_shell 774 def check_gitlab_shell
739 - required_version = Gitlab::VersionInfo.new(1, 7, 4) 775 + required_version = Gitlab::VersionInfo.new(1, 7, 9)
740 current_version = Gitlab::VersionInfo.parse(gitlab_shell_version) 776 current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
741 777
742 print "GitLab Shell version >= #{required_version} ? ... " 778 print "GitLab Shell version >= #{required_version} ? ... "
lib/tasks/gitlab/task_helpers.rake
@@ -2,6 +2,16 @@ module Gitlab @@ -2,6 +2,16 @@ module Gitlab
2 class TaskAbortedByUserError < StandardError; end 2 class TaskAbortedByUserError < StandardError; end
3 end 3 end
4 4
  5 +unless STDOUT.isatty
  6 + module Colored
  7 + extend self
  8 +
  9 + def colorize(string, options={})
  10 + string
  11 + end
  12 + end
  13 +end
  14 +
5 namespace :gitlab do 15 namespace :gitlab do
6 16
7 # Ask if the user wants to continue 17 # Ask if the user wants to continue
public/favicon.ico
No preview for this file type
spec/contexts/issues/list_context_spec.rb 0 → 100644
@@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Issues::ListContext do
  4 +
  5 + let(:user) { create(:user) }
  6 + let(:project) { create(:project, creator: user) }
  7 +
  8 + titles = ['foo','bar','baz']
  9 + titles.each_with_index do |title, index|
  10 + let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) }
  11 + end
  12 +
  13 + describe 'sorting' do
  14 + it 'sorts by newest' do
  15 + params = {sort: 'newest'}
  16 +
  17 + issues = Issues::ListContext.new(project, user, params).execute
  18 + issues.first.should eq foo
  19 + end
  20 +
  21 + it 'sorts by oldest' do
  22 + params = {sort: 'oldest'}
  23 +
  24 + issues = Issues::ListContext.new(project, user, params).execute
  25 + issues.first.should eq baz
  26 + end
  27 +
  28 + it 'sorts by recently updated' do
  29 + params = {sort: 'recently_updated'}
  30 + baz.updated_at = Time.now + 10
  31 + baz.save
  32 +
  33 + issues = Issues::ListContext.new(project, user, params).execute
  34 + issues.first.should eq baz
  35 + end
  36 +
  37 + it 'sorts by least recently updated' do
  38 + params = {sort: 'last_updated'}
  39 + bar.updated_at = Time.now - 10
  40 + bar.save
  41 +
  42 + issues = Issues::ListContext.new(project, user, params).execute
  43 + issues.first.should eq bar
  44 + end
  45 +
  46 + describe 'sorting by milestone' do
  47 + let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
  48 + let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
  49 +
  50 + before :each do
  51 + foo.milestone = newer_due_milestone
  52 + foo.save
  53 + bar.milestone = later_due_milestone
  54 + bar.save
  55 + end
  56 +
  57 + it 'sorts by most recently due milestone' do
  58 + params = {sort: 'milestone_due_soon'}
  59 +
  60 + issues = Issues::ListContext.new(project, user, params).execute
  61 + issues.first.should eq foo
  62 +
  63 + end
  64 +
  65 + it 'sorts by least recently due milestone' do
  66 + params = {sort: 'milestone_due_later'}
  67 +
  68 + issues = Issues::ListContext.new(project, user, params).execute
  69 + issues.first.should eq bar
  70 + end
  71 + end
  72 + end
  73 +end
spec/contexts/projects_create_context_spec.rb
@@ -7,6 +7,7 @@ describe Projects::CreateContext do @@ -7,6 +7,7 @@ describe Projects::CreateContext do
7 describe :create_by_user do 7 describe :create_by_user do
8 before do 8 before do
9 @user = create :user 9 @user = create :user
  10 + @admin = create :user, admin: true
10 @opts = { 11 @opts = {
11 name: "GitLab", 12 name: "GitLab",
12 namespace: @user.namespace 13 namespace: @user.namespace
@@ -37,7 +38,7 @@ describe Projects::CreateContext do @@ -37,7 +38,7 @@ describe Projects::CreateContext do
37 it { @project.namespace.should == @group } 38 it { @project.namespace.should == @group }
38 end 39 end
39 40
40 - context 'respect configured public setting' do 41 + context 'respect configured visibility setting' do
41 before(:each) do 42 before(:each) do
42 @settings = double("settings") 43 @settings = double("settings")
43 @settings.stub(:issues) { true } 44 @settings.stub(:issues) { true }
@@ -46,25 +47,90 @@ describe Projects::CreateContext do @@ -46,25 +47,90 @@ describe Projects::CreateContext do
46 @settings.stub(:wall) { true } 47 @settings.stub(:wall) { true }
47 @settings.stub(:snippets) { true } 48 @settings.stub(:snippets) { true }
48 stub_const("Settings", Class.new) 49 stub_const("Settings", Class.new)
  50 + @restrictions = double("restrictions")
  51 + @restrictions.stub(:restricted_visibility_levels) { [] }
  52 + Settings.stub_chain(:gitlab).and_return(@restrictions)
49 Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings) 53 Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
50 end 54 end
51 55
52 context 'should be public when setting is public' do 56 context 'should be public when setting is public' do
53 before do 57 before do
54 - @settings.stub(:public) { true } 58 + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
55 @project = create_project(@user, @opts) 59 @project = create_project(@user, @opts)
56 end 60 end
57 61
58 - it { @project.public.should be_true } 62 + it { @project.public?.should be_true }
59 end 63 end
60 64
61 - context 'should be private when setting is not public' do 65 + context 'should be private when setting is private' do
62 before do 66 before do
63 - @settings.stub(:public) { false } 67 + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
64 @project = create_project(@user, @opts) 68 @project = create_project(@user, @opts)
65 end 69 end
66 70
67 - it { @project.public.should be_false } 71 + it { @project.private?.should be_true }
  72 + end
  73 +
  74 + context 'should be internal when setting is internal' do
  75 + before do
  76 + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::INTERNAL }
  77 + @project = create_project(@user, @opts)
  78 + end
  79 +
  80 + it { @project.internal?.should be_true }
  81 + end
  82 + end
  83 +
  84 + context 'respect configured visibility restrictions setting' do
  85 + before(:each) do
  86 + @settings = double("settings")
  87 + @settings.stub(:issues) { true }
  88 + @settings.stub(:merge_requests) { true }
  89 + @settings.stub(:wiki) { true }
  90 + @settings.stub(:wall) { true }
  91 + @settings.stub(:snippets) { true }
  92 + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
  93 + stub_const("Settings", Class.new)
  94 + @restrictions = double("restrictions")
  95 + @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
  96 + Settings.stub_chain(:gitlab).and_return(@restrictions)
  97 + Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
  98 + end
  99 +
  100 + context 'should be private when option is public' do
  101 + before do
  102 + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  103 + @project = create_project(@user, @opts)
  104 + end
  105 +
  106 + it { @project.private?.should be_true }
  107 + end
  108 +
  109 + context 'should be public when option is public for admin' do
  110 + before do
  111 + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  112 + @project = create_project(@admin, @opts)
  113 + end
  114 +
  115 + it { @project.public?.should be_true }
  116 + end
  117 +
  118 + context 'should be private when option is private' do
  119 + before do
  120 + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
  121 + @project = create_project(@user, @opts)
  122 + end
  123 +
  124 + it { @project.private?.should be_true }
  125 + end
  126 +
  127 + context 'should be internal when option is internal' do
  128 + before do
  129 + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
  130 + @project = create_project(@user, @opts)
  131 + end
  132 +
  133 + it { @project.internal?.should be_true }
68 end 134 end
69 end 135 end
70 end 136 end
@@ -73,3 +139,4 @@ describe Projects::CreateContext do @@ -73,3 +139,4 @@ describe Projects::CreateContext do
73 Projects::CreateContext.new(user, opts).execute 139 Projects::CreateContext.new(user, opts).execute
74 end 140 end
75 end 141 end
  142 +
spec/contexts/projects_update_context_spec.rb 0 → 100644
@@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Projects::UpdateContext do
  4 + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
  5 + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
  6 +
  7 + describe :update_by_user do
  8 + before do
  9 + @user = create :user
  10 + @admin = create :user, admin: true
  11 + @project = create :project, creator_id: @user.id, namespace: @user.namespace
  12 + @opts = { project: {} }
  13 + end
  14 +
  15 + context 'should be private when updated to private' do
  16 + before do
  17 + @created_private = @project.private?
  18 +
  19 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
  20 + update_project(@project, @user, @opts)
  21 + end
  22 +
  23 + it { @created_private.should be_true }
  24 + it { @project.private?.should be_true }
  25 + end
  26 +
  27 + context 'should be internal when updated to internal' do
  28 + before do
  29 + @created_private = @project.private?
  30 +
  31 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
  32 + update_project(@project, @user, @opts)
  33 + end
  34 +
  35 + it { @created_private.should be_true }
  36 + it { @project.internal?.should be_true }
  37 + end
  38 +
  39 + context 'should be public when updated to public' do
  40 + before do
  41 + @created_private = @project.private?
  42 +
  43 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  44 + update_project(@project, @user, @opts)
  45 + end
  46 +
  47 + it { @created_private.should be_true }
  48 + it { @project.public?.should be_true }
  49 + end
  50 +
  51 + context 'respect configured visibility restrictions setting' do
  52 + before(:each) do
  53 + @restrictions = double("restrictions")
  54 + @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
  55 + Settings.stub_chain(:gitlab).and_return(@restrictions)
  56 + end
  57 +
  58 + context 'should be private when updated to private' do
  59 + before do
  60 + @created_private = @project.private?
  61 +
  62 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
  63 + update_project(@project, @user, @opts)
  64 + end
  65 +
  66 + it { @created_private.should be_true }
  67 + it { @project.private?.should be_true }
  68 + end
  69 +
  70 + context 'should be internal when updated to internal' do
  71 + before do
  72 + @created_private = @project.private?
  73 +
  74 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
  75 + update_project(@project, @user, @opts)
  76 + end
  77 +
  78 + it { @created_private.should be_true }
  79 + it { @project.internal?.should be_true }
  80 + end
  81 +
  82 + context 'should be private when updated to public' do
  83 + before do
  84 + @created_private = @project.private?
  85 +
  86 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  87 + update_project(@project, @user, @opts)
  88 + end
  89 +
  90 + it { @created_private.should be_true }
  91 + it { @project.private?.should be_true }
  92 + end
  93 +
  94 + context 'should be public when updated to public by admin' do
  95 + before do
  96 + @created_private = @project.private?
  97 +
  98 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  99 + update_project(@project, @admin, @opts)
  100 + end
  101 +
  102 + it { @created_private.should be_true }
  103 + it { @project.public?.should be_true }
  104 + end
  105 + end
  106 + end
  107 +
  108 + def update_project(project, user, opts)
  109 + Projects::UpdateContext.new(project, user, opts).execute
  110 + end
  111 +end
0 \ No newline at end of file 112 \ No newline at end of file
spec/contexts/search_context_spec.rb
@@ -3,23 +3,39 @@ require &#39;spec_helper&#39; @@ -3,23 +3,39 @@ require &#39;spec_helper&#39;
3 describe SearchContext do 3 describe SearchContext do
4 let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') } 4 let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
5 let(:user) { create(:user, namespace: found_namespace) } 5 let(:user) { create(:user, namespace: found_namespace) }
6 - let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, public: false) } 6 + let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
7 7
8 let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') } 8 let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') }
9 - let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, public: false) }  
10 - let(:public_namespace) { create(:namespace, path: 'something_else',name: 'searchable public namespace') }  
11 - let(:other_user) { create(:user, namespace: public_namespace) }  
12 - let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: other_user.id, namespace: public_namespace, public: true) } 9 + let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
  10 +
  11 + let(:internal_namespace) { create(:namespace, path: 'something_internal',name: 'searchable internal namespace') }
  12 + let(:internal_user) { create(:user, namespace: internal_namespace) }
  13 + let!(:internal_project) { create(:project, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
  14 +
  15 + let(:public_namespace) { create(:namespace, path: 'something_public',name: 'searchable public namespace') }
  16 + let(:public_user) { create(:user, namespace: public_namespace) }
  17 + let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
13 18
14 describe '#execute' do 19 describe '#execute' do
15 it 'public projects should be searchable' do 20 it 'public projects should be searchable' do
16 - context = SearchContext.new([found_project.id], {search_code: false, search: "searchable"}) 21 + context = SearchContext.new([found_project.id], nil, {search_code: false, search: "searchable"})
17 results = context.execute 22 results = context.execute
18 results[:projects].should == [found_project, public_project] 23 results[:projects].should == [found_project, public_project]
19 end 24 end
20 25
  26 + it 'internal projects should be searchable' do
  27 + context = SearchContext.new([found_project.id], user, {search_code: false, search: "searchable"})
  28 + results = context.execute
  29 + # can't seem to rely on the return order, so check this way
  30 + #subject { results[:projects] }
  31 + results[:projects].should have(3).items
  32 + results[:projects].should include(found_project)
  33 + results[:projects].should include(internal_project)
  34 + results[:projects].should include(public_project)
  35 + end
  36 +
21 it 'namespace name should be searchable' do 37 it 'namespace name should be searchable' do
22 - context = SearchContext.new([found_project.id], {search_code: false, search: "searchable namespace"}) 38 + context = SearchContext.new([found_project.id], user, {search_code: false, search: "searchable namespace"})
23 results = context.execute 39 results = context.execute
24 results[:projects].should == [found_project] 40 results[:projects].should == [found_project]
25 end 41 end
spec/factories.rb
@@ -15,7 +15,7 @@ FactoryGirl.define do @@ -15,7 +15,7 @@ FactoryGirl.define do
15 email { Faker::Internet.email } 15 email { Faker::Internet.email }
16 name 16 name
17 sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" } 17 sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" }
18 - password "123456" 18 + password "12345678"
19 password_confirmation { password } 19 password_confirmation { password }
20 confirmed_at { Time.now } 20 confirmed_at { Time.now }
21 confirmation_token { nil } 21 confirmation_token { nil }
@@ -66,6 +66,7 @@ FactoryGirl.define do @@ -66,6 +66,7 @@ FactoryGirl.define do
66 66
67 after :create do |project| 67 after :create do |project|
68 TestEnv.clear_repo_dir(project.namespace, project.path) 68 TestEnv.clear_repo_dir(project.namespace, project.path)
  69 + TestEnv.reset_satellite_dir
69 TestEnv.create_repo(project.namespace, project.path) 70 TestEnv.create_repo(project.namespace, project.path)
70 end 71 end
71 end 72 end
spec/factories/broadcast_messages.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +# == Schema Information
  2 +#
  3 +# Table name: broadcast_messages
  4 +#
  5 +# id :integer not null, primary key
  6 +# message :text default(""), not null
  7 +# starts_at :datetime
  8 +# ends_at :datetime
  9 +# alert_type :integer
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +#
  13 +
  14 +# Read about factories at https://github.com/thoughtbot/factory_girl
  15 +
  16 +FactoryGirl.define do
  17 + factory :broadcast_message do
  18 + message "MyText"
  19 + starts_at "2013-11-12 13:43:25"
  20 + ends_at "2013-11-12 13:43:25"
  21 + alert_type 1
  22 + end
  23 +end
spec/features/issues_spec.rb
@@ -95,4 +95,91 @@ describe &quot;Issues&quot; do @@ -95,4 +95,91 @@ describe &quot;Issues&quot; do
95 page.should have_content 'gitlab' 95 page.should have_content 'gitlab'
96 end 96 end
97 end 97 end
  98 +
  99 + describe 'filter issue' do
  100 + titles = ['foo','bar','baz']
  101 + titles.each_with_index do |title, index|
  102 + let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) }
  103 + end
  104 + let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
  105 + let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
  106 +
  107 + it 'sorts by newest' do
  108 + visit project_issues_path(project, sort: 'newest')
  109 +
  110 + first_issue.should include("foo")
  111 + last_issue.should include("baz")
  112 + end
  113 +
  114 + it 'sorts by oldest' do
  115 + visit project_issues_path(project, sort: 'oldest')
  116 +
  117 + first_issue.should include("baz")
  118 + last_issue.should include("foo")
  119 + end
  120 +
  121 + it 'sorts by most recently updated' do
  122 + baz.updated_at = Time.now + 100
  123 + baz.save
  124 + visit project_issues_path(project, sort: 'recently_updated')
  125 +
  126 + first_issue.should include("baz")
  127 + end
  128 +
  129 + it 'sorts by least recently updated' do
  130 + baz.updated_at = Time.now - 100
  131 + baz.save
  132 + visit project_issues_path(project, sort: 'last_updated')
  133 +
  134 + first_issue.should include("baz")
  135 + end
  136 +
  137 + describe 'sorting by milestone' do
  138 + before :each do
  139 + foo.milestone = newer_due_milestone
  140 + foo.save
  141 + bar.milestone = later_due_milestone
  142 + bar.save
  143 + end
  144 +
  145 + it 'sorts by recently due milestone' do
  146 + visit project_issues_path(project, sort: 'milestone_due_soon')
  147 +
  148 + first_issue.should include("foo")
  149 + end
  150 +
  151 + it 'sorts by least recently due milestone' do
  152 + visit project_issues_path(project, sort: 'milestone_due_later')
  153 +
  154 + first_issue.should include("bar")
  155 + end
  156 + end
  157 +
  158 + describe 'combine filter and sort' do
  159 + let(:user2) { create(:user) }
  160 +
  161 + before :each do
  162 + foo.assignee = user2
  163 + foo.save
  164 + bar.assignee = user2
  165 + bar.save
  166 + end
  167 +
  168 + it 'sorts with a filter applied' do
  169 + visit project_issues_path(project, sort: 'oldest', assignee_id: user2.id)
  170 +
  171 + first_issue.should include("bar")
  172 + last_issue.should include("foo")
  173 + page.should_not have_content 'baz'
  174 + end
  175 + end
  176 + end
  177 +
  178 + def first_issue
  179 + all("ul.issues-list li").first.text
  180 + end
  181 +
  182 + def last_issue
  183 + all("ul.issues-list li").last.text
  184 + end
98 end 185 end
spec/features/security/project/internal_access_spec.rb 0 → 100644
@@ -0,0 +1,251 @@ @@ -0,0 +1,251 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Internal Project Access" do
  4 + let(:project) { create(:project_with_code) }
  5 +
  6 + let(:master) { create(:user) }
  7 + let(:guest) { create(:user) }
  8 + let(:reporter) { create(:user) }
  9 +
  10 + before do
  11 + # internal project
  12 + project.visibility_level = Gitlab::VisibilityLevel::INTERNAL
  13 + project.save!
  14 +
  15 + # full access
  16 + project.team << [master, :master]
  17 +
  18 + # readonly
  19 + project.team << [reporter, :reporter]
  20 +
  21 + end
  22 +
  23 + describe "Project should be internal" do
  24 + subject { project }
  25 +
  26 + its(:internal?) { should be_true }
  27 + end
  28 +
  29 + describe "GET /:project_path" do
  30 + subject { project_path(project) }
  31 +
  32 + it { should be_allowed_for master }
  33 + it { should be_allowed_for reporter }
  34 + it { should be_allowed_for :admin }
  35 + it { should be_allowed_for guest }
  36 + it { should be_allowed_for :user }
  37 + it { should be_denied_for :visitor }
  38 + end
  39 +
  40 + describe "GET /:project_path/tree/master" do
  41 + subject { project_tree_path(project, project.repository.root_ref) }
  42 +
  43 + it { should be_allowed_for master }
  44 + it { should be_allowed_for reporter }
  45 + it { should be_allowed_for :admin }
  46 + it { should be_allowed_for guest }
  47 + it { should be_allowed_for :user }
  48 + it { should be_denied_for :visitor }
  49 + end
  50 +
  51 + describe "GET /:project_path/commits/master" do
  52 + subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
  53 +
  54 + it { should be_allowed_for master }
  55 + it { should be_allowed_for reporter }
  56 + it { should be_allowed_for :admin }
  57 + it { should be_allowed_for guest }
  58 + it { should be_allowed_for :user }
  59 + it { should be_denied_for :visitor }
  60 + end
  61 +
  62 + describe "GET /:project_path/commit/:sha" do
  63 + subject { project_commit_path(project, project.repository.commit) }
  64 +
  65 + it { should be_allowed_for master }
  66 + it { should be_allowed_for reporter }
  67 + it { should be_allowed_for :admin }
  68 + it { should be_allowed_for guest }
  69 + it { should be_allowed_for :user }
  70 + it { should be_denied_for :visitor }
  71 + end
  72 +
  73 + describe "GET /:project_path/compare" do
  74 + subject { project_compare_index_path(project) }
  75 +
  76 + it { should be_allowed_for master }
  77 + it { should be_allowed_for reporter }
  78 + it { should be_allowed_for :admin }
  79 + it { should be_allowed_for guest }
  80 + it { should be_allowed_for :user }
  81 + it { should be_denied_for :visitor }
  82 + end
  83 +
  84 + describe "GET /:project_path/team" do
  85 + subject { project_team_index_path(project) }
  86 +
  87 + it { should be_allowed_for master }
  88 + it { should be_denied_for reporter }
  89 + it { should be_allowed_for :admin }
  90 + it { should be_denied_for guest }
  91 + it { should be_denied_for :user }
  92 + it { should be_denied_for :visitor }
  93 + end
  94 +
  95 + describe "GET /:project_path/wall" do
  96 + subject { project_wall_path(project) }
  97 +
  98 + it { should be_allowed_for master }
  99 + it { should be_allowed_for reporter }
  100 + it { should be_allowed_for :admin }
  101 + it { should be_allowed_for guest }
  102 + it { should be_allowed_for :user }
  103 + it { should be_denied_for :visitor }
  104 + end
  105 +
  106 + describe "GET /:project_path/blob" do
  107 + before do
  108 + commit = project.repository.commit
  109 + path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
  110 + @blob_path = project_blob_path(project, File.join(commit.id, path))
  111 + end
  112 +
  113 + it { @blob_path.should be_allowed_for master }
  114 + it { @blob_path.should be_allowed_for reporter }
  115 + it { @blob_path.should be_allowed_for :admin }
  116 + it { @blob_path.should be_allowed_for guest }
  117 + it { @blob_path.should be_allowed_for :user }
  118 + it { @blob_path.should be_denied_for :visitor }
  119 + end
  120 +
  121 + describe "GET /:project_path/edit" do
  122 + subject { edit_project_path(project) }
  123 +
  124 + it { should be_allowed_for master }
  125 + it { should be_denied_for reporter }
  126 + it { should be_allowed_for :admin }
  127 + it { should be_denied_for guest }
  128 + it { should be_denied_for :user }
  129 + it { should be_denied_for :visitor }
  130 + end
  131 +
  132 + describe "GET /:project_path/deploy_keys" do
  133 + subject { project_deploy_keys_path(project) }
  134 +
  135 + it { should be_allowed_for master }
  136 + it { should be_denied_for reporter }
  137 + it { should be_allowed_for :admin }
  138 + it { should be_denied_for guest }
  139 + it { should be_denied_for :user }
  140 + it { should be_denied_for :visitor }
  141 + end
  142 +
  143 + describe "GET /:project_path/issues" do
  144 + subject { project_issues_path(project) }
  145 +
  146 + it { should be_allowed_for master }
  147 + it { should be_allowed_for reporter }
  148 + it { should be_allowed_for :admin }
  149 + it { should be_allowed_for guest }
  150 + it { should be_allowed_for :user }
  151 + it { should be_denied_for :visitor }
  152 + end
  153 +
  154 + describe "GET /:project_path/snippets" do
  155 + subject { project_snippets_path(project) }
  156 +
  157 + it { should be_allowed_for master }
  158 + it { should be_allowed_for reporter }
  159 + it { should be_allowed_for :admin }
  160 + it { should be_allowed_for guest }
  161 + it { should be_allowed_for :user }
  162 + it { should be_denied_for :visitor }
  163 + end
  164 +
  165 + describe "GET /:project_path/snippets/new" do
  166 + subject { new_project_snippet_path(project) }
  167 +
  168 + it { should be_allowed_for master }
  169 + it { should be_allowed_for reporter }
  170 + it { should be_allowed_for :admin }
  171 + it { should be_denied_for guest }
  172 + it { should be_denied_for :user }
  173 + it { should be_denied_for :visitor }
  174 + end
  175 +
  176 + describe "GET /:project_path/merge_requests" do
  177 + subject { project_merge_requests_path(project) }
  178 +
  179 + it { should be_allowed_for master }
  180 + it { should be_allowed_for reporter }
  181 + it { should be_allowed_for :admin }
  182 + it { should be_allowed_for guest }
  183 + it { should be_allowed_for :user }
  184 + it { should be_denied_for :visitor }
  185 + end
  186 +
  187 + describe "GET /:project_path/merge_requests/new" do
  188 + subject { new_project_merge_request_path(project) }
  189 +
  190 + it { should be_allowed_for master }
  191 + it { should be_denied_for reporter }
  192 + it { should be_allowed_for :admin }
  193 + it { should be_denied_for guest }
  194 + it { should be_denied_for :user }
  195 + it { should be_denied_for :visitor }
  196 + end
  197 +
  198 + describe "GET /:project_path/branches/recent" do
  199 + subject { recent_project_branches_path(project) }
  200 +
  201 + it { should be_allowed_for master }
  202 + it { should be_allowed_for reporter }
  203 + it { should be_allowed_for :admin }
  204 + it { should be_allowed_for guest }
  205 + it { should be_allowed_for :user }
  206 + it { should be_denied_for :visitor }
  207 + end
  208 +
  209 + describe "GET /:project_path/branches" do
  210 + subject { project_branches_path(project) }
  211 +
  212 + before do
  213 + # Speed increase
  214 + Project.any_instance.stub(:branches).and_return([])
  215 + end
  216 +
  217 + it { should be_allowed_for master }
  218 + it { should be_allowed_for reporter }
  219 + it { should be_allowed_for :admin }
  220 + it { should be_allowed_for guest }
  221 + it { should be_allowed_for :user }
  222 + it { should be_denied_for :visitor }
  223 + end
  224 +
  225 + describe "GET /:project_path/tags" do
  226 + subject { project_tags_path(project) }
  227 +
  228 + before do
  229 + # Speed increase
  230 + Project.any_instance.stub(:tags).and_return([])
  231 + end
  232 +
  233 + it { should be_allowed_for master }
  234 + it { should be_allowed_for reporter }
  235 + it { should be_allowed_for :admin }
  236 + it { should be_allowed_for guest }
  237 + it { should be_allowed_for :user }
  238 + it { should be_denied_for :visitor }
  239 + end
  240 +
  241 + describe "GET /:project_path/hooks" do
  242 + subject { project_hooks_path(project) }
  243 +
  244 + it { should be_allowed_for master }
  245 + it { should be_denied_for reporter }
  246 + it { should be_allowed_for :admin }
  247 + it { should be_denied_for guest }
  248 + it { should be_denied_for :user }
  249 + it { should be_denied_for :visitor }
  250 + end
  251 +end
spec/features/security/project/private_access_spec.rb
@@ -15,6 +15,12 @@ describe &quot;Private Project Access&quot; do @@ -15,6 +15,12 @@ describe &quot;Private Project Access&quot; do
15 project.team << [reporter, :reporter] 15 project.team << [reporter, :reporter]
16 end 16 end
17 17
  18 + describe "Project should be private" do
  19 + subject { project }
  20 +
  21 + its(:private?) { should be_true }
  22 + end
  23 +
18 describe "GET /:project_path" do 24 describe "GET /:project_path" do
19 subject { project_path(project) } 25 subject { project_path(project) }
20 26
spec/features/security/project/public_access_spec.rb
@@ -9,7 +9,7 @@ describe &quot;Public Project Access&quot; do @@ -9,7 +9,7 @@ describe &quot;Public Project Access&quot; do
9 9
10 before do 10 before do
11 # public project 11 # public project
12 - project.public = true 12 + project.visibility_level = Gitlab::VisibilityLevel::PUBLIC
13 project.save! 13 project.save!
14 14
15 # full access 15 # full access
spec/helpers/gitlab_markdown_helper_spec.rb
@@ -378,9 +378,10 @@ describe GitlabMarkdownHelper do @@ -378,9 +378,10 @@ describe GitlabMarkdownHelper do
378 it "should leave code blocks untouched" do 378 it "should leave code blocks untouched" do
379 helper.stub(:user_color_scheme_class).and_return(:white) 379 helper.stub(:user_color_scheme_class).and_return(:white)
380 380
381 - helper.markdown("\n some code from $#{snippet.id}\n here too\n").should include("<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre></div></div>") 381 + target_html = "<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> <span class=\"err\">$</span><span class=\"mi\">#{snippet.id}</span>"
382 382
383 - helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should include("<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre></div></div>") 383 + helper.markdown("\n some code from $#{snippet.id}\n here too\n").should include(target_html)
  384 + helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should include(target_html)
384 end 385 end
385 386
386 it "should leave inline code untouched" do 387 it "should leave inline code untouched" do
spec/lib/auth_spec.rb
@@ -8,21 +8,21 @@ describe Gitlab::Auth do @@ -8,21 +8,21 @@ describe Gitlab::Auth do
8 @user = create( 8 @user = create(
9 :user, 9 :user,
10 username: 'john', 10 username: 'john',
11 - password: '888777',  
12 - password_confirmation: '888777' 11 + password: '88877711',
  12 + password_confirmation: '88877711'
13 ) 13 )
14 end 14 end
15 15
16 it "should find user by valid login/password" do 16 it "should find user by valid login/password" do
17 - gl_auth.find('john', '888777').should == @user 17 + gl_auth.find('john', '88877711').should == @user
18 end 18 end
19 19
20 it "should not find user with invalid password" do 20 it "should not find user with invalid password" do
21 - gl_auth.find('john', 'invalid').should_not == @user 21 + gl_auth.find('john', 'invalid11').should_not == @user
22 end 22 end
23 23
24 it "should not find user with invalid login and password" do 24 it "should not find user with invalid login and password" do
25 - gl_auth.find('jon', 'invalid').should_not == @user 25 + gl_auth.find('jon', 'invalid11').should_not == @user
26 end 26 end
27 end 27 end
28 end 28 end
spec/mailers/notify_spec.rb
@@ -110,7 +110,7 @@ describe Notify do @@ -110,7 +110,7 @@ describe Notify do
110 it_behaves_like 'an assignee email' 110 it_behaves_like 'an assignee email'
111 111
112 it 'has the correct subject' do 112 it 'has the correct subject' do
113 - should have_subject /#{project.name} \| new issue ##{issue.iid} \| #{issue.title}/ 113 + should have_subject /#{project.name} \| New issue ##{issue.iid} \| #{issue.title}/
114 end 114 end
115 115
116 it 'contains a link to the new issue' do 116 it 'contains a link to the new issue' do
@@ -126,7 +126,7 @@ describe Notify do @@ -126,7 +126,7 @@ describe Notify do
126 it_behaves_like 'a multiple recipients email' 126 it_behaves_like 'a multiple recipients email'
127 127
128 it 'has the correct subject' do 128 it 'has the correct subject' do
129 - should have_subject /changed issue ##{issue.iid} \| #{issue.title}/ 129 + should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/
130 end 130 end
131 131
132 it 'contains the name of the previous assignee' do 132 it 'contains the name of the previous assignee' do
@@ -148,7 +148,7 @@ describe Notify do @@ -148,7 +148,7 @@ describe Notify do
148 subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } 148 subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) }
149 149
150 it 'has the correct subject' do 150 it 'has the correct subject' do
151 - should have_subject /changed issue ##{issue.iid} \| #{issue.title}/i 151 + should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/i
152 end 152 end
153 153
154 it 'contains the new status' do 154 it 'contains the new status' do
@@ -175,7 +175,7 @@ describe Notify do @@ -175,7 +175,7 @@ describe Notify do
175 it_behaves_like 'an assignee email' 175 it_behaves_like 'an assignee email'
176 176
177 it 'has the correct subject' do 177 it 'has the correct subject' do
178 - should have_subject /new merge request !#{merge_request.iid}/ 178 + should have_subject /New merge request ##{merge_request.iid}/
179 end 179 end
180 180
181 it 'contains a link to the new merge request' do 181 it 'contains a link to the new merge request' do
@@ -199,7 +199,7 @@ describe Notify do @@ -199,7 +199,7 @@ describe Notify do
199 it_behaves_like 'a multiple recipients email' 199 it_behaves_like 'a multiple recipients email'
200 200
201 it 'has the correct subject' do 201 it 'has the correct subject' do
202 - should have_subject /changed merge request !#{merge_request.iid}/ 202 + should have_subject /Changed merge request ##{merge_request.iid}/
203 end 203 end
204 204
205 it 'contains the name of the previous assignee' do 205 it 'contains the name of the previous assignee' do
@@ -224,7 +224,7 @@ describe Notify do @@ -224,7 +224,7 @@ describe Notify do
224 subject { Notify.project_was_moved_email(project.id, user.id) } 224 subject { Notify.project_was_moved_email(project.id, user.id) }
225 225
226 it 'has the correct subject' do 226 it 'has the correct subject' do
227 - should have_subject /project was moved/ 227 + should have_subject /Project was moved/
228 end 228 end
229 229
230 it 'contains name of project' do 230 it 'contains name of project' do
@@ -244,7 +244,7 @@ describe Notify do @@ -244,7 +244,7 @@ describe Notify do
244 user: user) } 244 user: user) }
245 subject { Notify.project_access_granted_email(users_project.id) } 245 subject { Notify.project_access_granted_email(users_project.id) }
246 it 'has the correct subject' do 246 it 'has the correct subject' do
247 - should have_subject /access to project was granted/ 247 + should have_subject /Access to project was granted/
248 end 248 end
249 it 'contains name of project' do 249 it 'contains name of project' do
250 should have_body_text /#{project.name}/ 250 should have_body_text /#{project.name}/
@@ -302,7 +302,7 @@ describe Notify do @@ -302,7 +302,7 @@ describe Notify do
302 it_behaves_like 'a note email' 302 it_behaves_like 'a note email'
303 303
304 it 'has the correct subject' do 304 it 'has the correct subject' do
305 - should have_subject /note for commit #{commit.short_id}/ 305 + should have_subject /Note for commit #{commit.short_id}/
306 end 306 end
307 307
308 it 'contains a link to the commit' do 308 it 'contains a link to the commit' do
@@ -320,7 +320,7 @@ describe Notify do @@ -320,7 +320,7 @@ describe Notify do
320 it_behaves_like 'a note email' 320 it_behaves_like 'a note email'
321 321
322 it 'has the correct subject' do 322 it 'has the correct subject' do
323 - should have_subject /note for merge request ##{merge_request.iid}/ 323 + should have_subject /Note for merge request ##{merge_request.iid}/
324 end 324 end
325 325
326 it 'contains a link to the merge request note' do 326 it 'contains a link to the merge request note' do
@@ -338,7 +338,7 @@ describe Notify do @@ -338,7 +338,7 @@ describe Notify do
338 it_behaves_like 'a note email' 338 it_behaves_like 'a note email'
339 339
340 it 'has the correct subject' do 340 it 'has the correct subject' do
341 - should have_subject /note for issue ##{issue.iid}/ 341 + should have_subject /Note for issue ##{issue.iid}/
342 end 342 end
343 343
344 it 'contains a link to the issue note' do 344 it 'contains a link to the issue note' do
@@ -356,7 +356,7 @@ describe Notify do @@ -356,7 +356,7 @@ describe Notify do
356 subject { Notify.group_access_granted_email(membership.id) } 356 subject { Notify.group_access_granted_email(membership.id) }
357 357
358 it 'has the correct subject' do 358 it 'has the correct subject' do
359 - should have_subject /access to group was granted/ 359 + should have_subject /Access to group was granted/
360 end 360 end
361 361
362 it 'contains name of project' do 362 it 'contains name of project' do
@@ -367,4 +367,28 @@ describe Notify do @@ -367,4 +367,28 @@ describe Notify do
367 should have_body_text /#{membership.human_access}/ 367 should have_body_text /#{membership.human_access}/
368 end 368 end
369 end 369 end
  370 +
  371 + describe 'confirmation if email changed' do
  372 + let(:example_site_path) { root_path }
  373 + let(:user) { create(:user, email: 'old-email@mail.com') }
  374 +
  375 + before do
  376 + user.email = "new-email@mail.com"
  377 + user.save
  378 + end
  379 +
  380 + subject { ActionMailer::Base.deliveries.last }
  381 +
  382 + it 'is sent to the new user' do
  383 + should deliver_to 'new-email@mail.com'
  384 + end
  385 +
  386 + it 'has the correct subject' do
  387 + should have_subject "Confirmation instructions"
  388 + end
  389 +
  390 + it 'includes a link to the site' do
  391 + should have_body_text /#{example_site_path}/
  392 + end
  393 + end
370 end 394 end
spec/models/assembla_service_spec.rb 0 → 100644
@@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
  1 +# == Schema Information
  2 +#
  3 +# Table name: services
  4 +#
  5 +# id :integer not null, primary key
  6 +# type :string(255)
  7 +# title :string(255)
  8 +# token :string(255)
  9 +# project_id :integer not null
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +# active :boolean default(FALSE), not null
  13 +# project_url :string(255)
  14 +# subdomain :string(255)
  15 +# room :string(255)
  16 +#
  17 +
  18 +require 'spec_helper'
  19 +
  20 +describe AssemblaService do
  21 + describe "Associations" do
  22 + it { should belong_to :project }
  23 + it { should have_one :service_hook }
  24 + end
  25 +
  26 + describe "Execute" do
  27 + let(:user) { create(:user) }
  28 + let(:project) { create(:project_with_code) }
  29 +
  30 + before do
  31 + @assembla_service = AssemblaService.new
  32 + @assembla_service.stub(
  33 + project_id: project.id,
  34 + project: project,
  35 + service_hook: true,
  36 + token: 'verySecret'
  37 + )
  38 + @sample_data = GitPushService.new.sample_data(project, user)
  39 + @api_url = 'https://atlas.assembla.com/spaces/ouposp/github_tool?secret_key=verySecret'
  40 + WebMock.stub_request(:post, @api_url)
  41 + end
  42 +
  43 + it "should call FlowDock API" do
  44 + @assembla_service.execute(@sample_data)
  45 + WebMock.should have_requested(:post, @api_url).with(
  46 + body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/
  47 + ).once
  48 + end
  49 + end
  50 +end
spec/models/broadcast_message_spec.rb 0 → 100644
@@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
  1 +# == Schema Information
  2 +#
  3 +# Table name: broadcast_messages
  4 +#
  5 +# id :integer not null, primary key
  6 +# message :text default(""), not null
  7 +# starts_at :datetime
  8 +# ends_at :datetime
  9 +# alert_type :integer
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +#
  13 +
  14 +require 'spec_helper'
  15 +
  16 +describe BroadcastMessage do
  17 + subject { create(:broadcast_message) }
  18 +
  19 + it { should be_valid }
  20 +
  21 + describe :current do
  22 + it "should return last message if time match" do
  23 + broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow)
  24 + BroadcastMessage.current.should == broadcast_message
  25 + end
  26 +
  27 + it "should return nil if time not come" do
  28 + broadcast_message = create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days)
  29 + BroadcastMessage.current.should be_nil
  30 + end
  31 +
  32 + it "should return nil if time has passed" do
  33 + broadcast_message = create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday)
  34 + BroadcastMessage.current.should be_nil
  35 + end
  36 + end
  37 +end
spec/models/flowdock_service_spec.rb
@@ -11,6 +11,8 @@ @@ -11,6 +11,8 @@
11 # updated_at :datetime not null 11 # updated_at :datetime not null
12 # active :boolean default(FALSE), not null 12 # active :boolean default(FALSE), not null
13 # project_url :string(255) 13 # project_url :string(255)
  14 +# subdomain :string(255)
  15 +# room :string(255)
14 # 16 #
15 17
16 require 'spec_helper' 18 require 'spec_helper'
spec/models/merge_request_spec.rb
@@ -116,13 +116,13 @@ describe MergeRequest do @@ -116,13 +116,13 @@ describe MergeRequest do
116 end 116 end
117 117
118 it 'accesses the set of issues that will be closed on acceptance' do 118 it 'accesses the set of issues that will be closed on acceptance' do
119 - subject.project.default_branch = subject.target_branch 119 + subject.project.stub(default_branch: subject.target_branch)
120 120
121 subject.closes_issues.should == [issue0, issue1].sort_by(&:id) 121 subject.closes_issues.should == [issue0, issue1].sort_by(&:id)
122 end 122 end
123 123
124 it 'only lists issues as to be closed if it targets the default branch' do 124 it 'only lists issues as to be closed if it targets the default branch' do
125 - subject.project.default_branch = 'master' 125 + subject.project.stub(default_branch: 'master')
126 subject.target_branch = 'something-else' 126 subject.target_branch = 'something-else'
127 127
128 subject.closes_issues.should be_empty 128 subject.closes_issues.should be_empty
spec/models/note_spec.rb
@@ -61,6 +61,11 @@ describe Note do @@ -61,6 +61,11 @@ describe Note do
61 note.should be_upvote 61 note.should be_upvote
62 end 62 end
63 63
  64 + it "recognizes a thumbsup emoji as a vote" do
  65 + note = build(:votable_note, note: ":thumbsup: for this")
  66 + note.should be_upvote
  67 + end
  68 +
64 it "recognizes a -1 note" do 69 it "recognizes a -1 note" do
65 note = create(:votable_note, note: "-1 for this") 70 note = create(:votable_note, note: "-1 for this")
66 note.should be_downvote 71 note.should be_downvote
@@ -70,6 +75,11 @@ describe Note do @@ -70,6 +75,11 @@ describe Note do
70 note = build(:votable_note, note: ":-1: for this") 75 note = build(:votable_note, note: ":-1: for this")
71 note.should be_downvote 76 note.should be_downvote
72 end 77 end
  78 +
  79 + it "recognizes a thumbsdown emoji as a vote" do
  80 + note = build(:votable_note, note: ":thumbsdown: for this")
  81 + note.should be_downvote
  82 + end
73 end 83 end
74 84
75 let(:project) { create(:project) } 85 let(:project) { create(:project) }
spec/models/project_spec.rb
@@ -9,19 +9,18 @@ @@ -9,19 +9,18 @@
9 # created_at :datetime not null 9 # created_at :datetime not null
10 # updated_at :datetime not null 10 # updated_at :datetime not null
11 # creator_id :integer 11 # creator_id :integer
12 -# default_branch :string(255)  
13 # issues_enabled :boolean default(TRUE), not null 12 # issues_enabled :boolean default(TRUE), not null
14 # wall_enabled :boolean default(TRUE), not null 13 # wall_enabled :boolean default(TRUE), not null
15 # merge_requests_enabled :boolean default(TRUE), not null 14 # merge_requests_enabled :boolean default(TRUE), not null
16 # wiki_enabled :boolean default(TRUE), not null 15 # wiki_enabled :boolean default(TRUE), not null
17 # namespace_id :integer 16 # namespace_id :integer
18 -# public :boolean default(FALSE), not null  
19 # issues_tracker :string(255) default("gitlab"), not null 17 # issues_tracker :string(255) default("gitlab"), not null
20 # issues_tracker_id :string(255) 18 # issues_tracker_id :string(255)
21 # snippets_enabled :boolean default(TRUE), not null 19 # snippets_enabled :boolean default(TRUE), not null
22 # last_activity_at :datetime 20 # last_activity_at :datetime
23 # imported :boolean default(FALSE), not null 21 # imported :boolean default(FALSE), not null
24 # import_url :string(255) 22 # import_url :string(255)
  23 +# visibility_level :integer default(0), not null
25 # 24 #
26 25
27 require 'spec_helper' 26 require 'spec_helper'
spec/models/service_hook_spec.rb
@@ -2,13 +2,16 @@ @@ -2,13 +2,16 @@
2 # 2 #
3 # Table name: web_hooks 3 # Table name: web_hooks
4 # 4 #
5 -# id :integer not null, primary key  
6 -# url :string(255)  
7 -# project_id :integer  
8 -# created_at :datetime not null  
9 -# updated_at :datetime not null  
10 -# type :string(255) default("ProjectHook")  
11 -# service_id :integer 5 +# id :integer not null, primary key
  6 +# url :string(255)
  7 +# project_id :integer
  8 +# created_at :datetime not null
  9 +# updated_at :datetime not null
  10 +# type :string(255) default("ProjectHook")
  11 +# service_id :integer
  12 +# push_events :boolean default(TRUE), not null
  13 +# issues_events :boolean default(FALSE), not null
  14 +# merge_requests_events :boolean default(FALSE), not null
12 # 15 #
13 16
14 require "spec_helper" 17 require "spec_helper"
spec/models/system_hook_spec.rb
@@ -2,13 +2,16 @@ @@ -2,13 +2,16 @@
2 # 2 #
3 # Table name: web_hooks 3 # Table name: web_hooks
4 # 4 #
5 -# id :integer not null, primary key  
6 -# url :string(255)  
7 -# project_id :integer  
8 -# created_at :datetime not null  
9 -# updated_at :datetime not null  
10 -# type :string(255) default("ProjectHook")  
11 -# service_id :integer 5 +# id :integer not null, primary key
  6 +# url :string(255)
  7 +# project_id :integer
  8 +# created_at :datetime not null
  9 +# updated_at :datetime not null
  10 +# type :string(255) default("ProjectHook")
  11 +# service_id :integer
  12 +# push_events :boolean default(TRUE), not null
  13 +# issues_events :boolean default(FALSE), not null
  14 +# merge_requests_events :boolean default(FALSE), not null
12 # 15 #
13 16
14 require "spec_helper" 17 require "spec_helper"
spec/models/user_spec.rb
@@ -36,6 +36,11 @@ @@ -36,6 +36,11 @@
36 # notification_level :integer default(1), not null 36 # notification_level :integer default(1), not null
37 # password_expires_at :datetime 37 # password_expires_at :datetime
38 # created_by_id :integer 38 # created_by_id :integer
  39 +# avatar :string(255)
  40 +# confirmation_token :string(255)
  41 +# confirmed_at :datetime
  42 +# confirmation_sent_at :datetime
  43 +# unconfirmed_email :string(255)
39 # 44 #
40 45
41 require 'spec_helper' 46 require 'spec_helper'
@@ -85,8 +90,8 @@ describe User do @@ -85,8 +90,8 @@ describe User do
85 end 90 end
86 91
87 it "should not generate password by default" do 92 it "should not generate password by default" do
88 - user = create(:user, password: 'abcdefg')  
89 - user.password.should == 'abcdefg' 93 + user = create(:user, password: 'abcdefghe')
  94 + user.password.should == 'abcdefghe'
90 end 95 end
91 96
92 it "should generate password when forcing random password" do 97 it "should generate password when forcing random password" do
spec/models/web_hook_spec.rb
@@ -2,13 +2,16 @@ @@ -2,13 +2,16 @@
2 # 2 #
3 # Table name: web_hooks 3 # Table name: web_hooks
4 # 4 #
5 -# id :integer not null, primary key  
6 -# url :string(255)  
7 -# project_id :integer  
8 -# created_at :datetime not null  
9 -# updated_at :datetime not null  
10 -# type :string(255) default("ProjectHook")  
11 -# service_id :integer 5 +# id :integer not null, primary key
  6 +# url :string(255)
  7 +# project_id :integer
  8 +# created_at :datetime not null
  9 +# updated_at :datetime not null
  10 +# type :string(255) default("ProjectHook")
  11 +# service_id :integer
  12 +# push_events :boolean default(TRUE), not null
  13 +# issues_events :boolean default(FALSE), not null
  14 +# merge_requests_events :boolean default(FALSE), not null
12 # 15 #
13 16
14 require 'spec_helper' 17 require 'spec_helper'
spec/observers/merge_request_observer_spec.rb
@@ -4,7 +4,7 @@ describe MergeRequestObserver do @@ -4,7 +4,7 @@ describe MergeRequestObserver do
4 let(:some_user) { create :user } 4 let(:some_user) { create :user }
5 let(:assignee) { create :user } 5 let(:assignee) { create :user }
6 let(:author) { create :user } 6 let(:author) { create :user }
7 - let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) } 7 + let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author).as_null_object }
8 let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author, target_project: create(:project)) } 8 let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author, target_project: create(:project)) }
9 let(:unassigned_mr) { create(:merge_request, author: author, target_project: create(:project)) } 9 let(:unassigned_mr) { create(:merge_request, author: author, target_project: create(:project)) }
10 let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author, target_project: create(:project)) } 10 let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author, target_project: create(:project)) }
spec/observers/users_group_observer_spec.rb
@@ -23,5 +23,10 @@ describe UsersGroupObserver do @@ -23,5 +23,10 @@ describe UsersGroupObserver do
23 subject.should_receive(:notification) 23 subject.should_receive(:notification)
24 @membership.update_attribute(:group_access, UsersGroup::MASTER) 24 @membership.update_attribute(:group_access, UsersGroup::MASTER)
25 end 25 end
  26 +
  27 + it "does not send an email when the access level has not changed" do
  28 + subject.should_not_receive(:notification)
  29 + @membership.update_attribute(:group_access, UsersGroup::OWNER)
  30 + end
26 end 31 end
27 end 32 end
spec/requests/api/files_spec.rb 0 → 100644
@@ -0,0 +1,115 @@ @@ -0,0 +1,115 @@
  1 +require 'spec_helper'
  2 +
  3 +describe API::API do
  4 + include ApiHelpers
  5 + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
  6 + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
  7 +
  8 + let(:user) { create(:user) }
  9 + let!(:project) { create(:project_with_code, namespace: user.namespace ) }
  10 + before { project.team << [user, :developer] }
  11 +
  12 + describe "POST /projects/:id/repository/files" do
  13 + let(:valid_params) {
  14 + {
  15 + file_path: 'newfile.rb',
  16 + branch_name: 'master',
  17 + content: 'puts 8',
  18 + commit_message: 'Added newfile'
  19 + }
  20 + }
  21 +
  22 + it "should create a new file in project repo" do
  23 + Gitlab::Satellite::NewFileAction.any_instance.stub(
  24 + commit!: true,
  25 + )
  26 +
  27 + post api("/projects/#{project.id}/repository/files", user), valid_params
  28 + response.status.should == 201
  29 + json_response['file_path'].should == 'newfile.rb'
  30 + end
  31 +
  32 + it "should return a 400 bad request if no params given" do
  33 + post api("/projects/#{project.id}/repository/files", user)
  34 + response.status.should == 400
  35 + end
  36 +
  37 + it "should return a 400 if satellite fails to create file" do
  38 + Gitlab::Satellite::NewFileAction.any_instance.stub(
  39 + commit!: false,
  40 + )
  41 +
  42 + post api("/projects/#{project.id}/repository/files", user), valid_params
  43 + response.status.should == 400
  44 + end
  45 + end
  46 +
  47 + describe "PUT /projects/:id/repository/files" do
  48 + let(:valid_params) {
  49 + {
  50 + file_path: 'spec/spec_helper.rb',
  51 + branch_name: 'master',
  52 + content: 'puts 8',
  53 + commit_message: 'Changed file'
  54 + }
  55 + }
  56 +
  57 + it "should update existing file in project repo" do
  58 + Gitlab::Satellite::EditFileAction.any_instance.stub(
  59 + commit!: true,
  60 + )
  61 +
  62 + put api("/projects/#{project.id}/repository/files", user), valid_params
  63 + response.status.should == 200
  64 + json_response['file_path'].should == 'spec/spec_helper.rb'
  65 + end
  66 +
  67 + it "should return a 400 bad request if no params given" do
  68 + put api("/projects/#{project.id}/repository/files", user)
  69 + response.status.should == 400
  70 + end
  71 +
  72 + it "should return a 400 if satellite fails to create file" do
  73 + Gitlab::Satellite::EditFileAction.any_instance.stub(
  74 + commit!: false,
  75 + )
  76 +
  77 + put api("/projects/#{project.id}/repository/files", user), valid_params
  78 + response.status.should == 400
  79 + end
  80 + end
  81 +
  82 + describe "DELETE /projects/:id/repository/files" do
  83 + let(:valid_params) {
  84 + {
  85 + file_path: 'spec/spec_helper.rb',
  86 + branch_name: 'master',
  87 + commit_message: 'Changed file'
  88 + }
  89 + }
  90 +
  91 + it "should delete existing file in project repo" do
  92 + Gitlab::Satellite::DeleteFileAction.any_instance.stub(
  93 + commit!: true,
  94 + )
  95 +
  96 + delete api("/projects/#{project.id}/repository/files", user), valid_params
  97 + response.status.should == 200
  98 + json_response['file_path'].should == 'spec/spec_helper.rb'
  99 + end
  100 +
  101 + it "should return a 400 bad request if no params given" do
  102 + delete api("/projects/#{project.id}/repository/files", user)
  103 + response.status.should == 400
  104 + end
  105 +
  106 + it "should return a 400 if satellite fails to create file" do
  107 + Gitlab::Satellite::DeleteFileAction.any_instance.stub(
  108 + commit!: false,
  109 + )
  110 +
  111 + delete api("/projects/#{project.id}/repository/files", user), valid_params
  112 + response.status.should == 400
  113 + end
  114 + end
  115 +end
spec/requests/api/namespaces_spec.rb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +require 'spec_helper'
  2 +
  3 +describe API::API do
  4 + include ApiHelpers
  5 + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
  6 + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
  7 +
  8 + let(:admin) { create(:admin) }
  9 + let!(:group1) { create(:group) }
  10 + let!(:group2) { create(:group) }
  11 +
  12 + describe "GET /namespaces" do
  13 + context "when unauthenticated" do
  14 + it "should return authentication error" do
  15 + get api("/namespaces")
  16 + response.status.should == 401
  17 + end
  18 + end
  19 +
  20 + context "when authenticated as admin" do
  21 + it "admin: should return an array of all namespaces" do
  22 + get api("/namespaces", admin)
  23 + response.status.should == 200
  24 + json_response.should be_an Array
  25 +
  26 + # Admin namespace + 2 group namespaces
  27 + json_response.length.should == 3
  28 + end
  29 + end
  30 + end
  31 +end
spec/requests/api/project_hooks_spec.rb 0 → 100644
@@ -0,0 +1,132 @@ @@ -0,0 +1,132 @@
  1 +require 'spec_helper'
  2 +
  3 +describe API::API, 'ProjectHooks' do
  4 + include ApiHelpers
  5 + before(:each) { enable_observers }
  6 + after(:each) { disable_observers }
  7 +
  8 + let(:user) { create(:user) }
  9 + let(:user3) { create(:user) }
  10 + let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) }
  11 + let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
  12 +
  13 + before do
  14 + project.team << [user, :master]
  15 + project.team << [user3, :developer]
  16 + end
  17 +
  18 + describe "GET /projects/:id/hooks" do
  19 + context "authorized user" do
  20 + it "should return project hooks" do
  21 + get api("/projects/#{project.id}/hooks", user)
  22 + response.status.should == 200
  23 +
  24 + json_response.should be_an Array
  25 + json_response.count.should == 1
  26 + json_response.first['url'].should == "http://example.com"
  27 + end
  28 + end
  29 +
  30 + context "unauthorized user" do
  31 + it "should not access project hooks" do
  32 + get api("/projects/#{project.id}/hooks", user3)
  33 + response.status.should == 403
  34 + end
  35 + end
  36 + end
  37 +
  38 + describe "GET /projects/:id/hooks/:hook_id" do
  39 + context "authorized user" do
  40 + it "should return a project hook" do
  41 + get api("/projects/#{project.id}/hooks/#{hook.id}", user)
  42 + response.status.should == 200
  43 + json_response['url'].should == hook.url
  44 + end
  45 +
  46 + it "should return a 404 error if hook id is not available" do
  47 + get api("/projects/#{project.id}/hooks/1234", user)
  48 + response.status.should == 404
  49 + end
  50 + end
  51 +
  52 + context "unauthorized user" do
  53 + it "should not access an existing hook" do
  54 + get api("/projects/#{project.id}/hooks/#{hook.id}", user3)
  55 + response.status.should == 403
  56 + end
  57 + end
  58 +
  59 + it "should return a 404 error if hook id is not available" do
  60 + get api("/projects/#{project.id}/hooks/1234", user)
  61 + response.status.should == 404
  62 + end
  63 + end
  64 +
  65 + describe "POST /projects/:id/hooks" do
  66 + it "should add hook to project" do
  67 + expect {
  68 + post api("/projects/#{project.id}/hooks", user),
  69 + url: "http://example.com", issues_events: true
  70 + }.to change {project.hooks.count}.by(1)
  71 + response.status.should == 201
  72 + end
  73 +
  74 + it "should return a 400 error if url not given" do
  75 + post api("/projects/#{project.id}/hooks", user)
  76 + response.status.should == 400
  77 + end
  78 +
  79 + it "should return a 422 error if url not valid" do
  80 + post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"
  81 + response.status.should == 422
  82 + end
  83 + end
  84 +
  85 + describe "PUT /projects/:id/hooks/:hook_id" do
  86 + it "should update an existing project hook" do
  87 + put api("/projects/#{project.id}/hooks/#{hook.id}", user),
  88 + url: 'http://example.org', push_events: false
  89 + response.status.should == 200
  90 + json_response['url'].should == 'http://example.org'
  91 + end
  92 +
  93 + it "should return 404 error if hook id not found" do
  94 + put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'
  95 + response.status.should == 404
  96 + end
  97 +
  98 + it "should return 400 error if url is not given" do
  99 + put api("/projects/#{project.id}/hooks/#{hook.id}", user)
  100 + response.status.should == 400
  101 + end
  102 +
  103 + it "should return a 422 error if url is not valid" do
  104 + put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com'
  105 + response.status.should == 422
  106 + end
  107 + end
  108 +
  109 + describe "DELETE /projects/:id/hooks/:hook_id" do
  110 + it "should delete hook from project" do
  111 + expect {
  112 + delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
  113 + }.to change {project.hooks.count}.by(-1)
  114 + response.status.should == 200
  115 + end
  116 +
  117 + it "should return success when deleting hook" do
  118 + delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
  119 + response.status.should == 200
  120 + end
  121 +
  122 + it "should return success when deleting non existent hook" do
  123 + delete api("/projects/#{project.id}/hooks/42", user)
  124 + response.status.should == 200
  125 + end
  126 +
  127 + it "should return a 405 error if hook id not given" do
  128 + delete api("/projects/#{project.id}/hooks", user)
  129 + response.status.should == 405
  130 + end
  131 + end
  132 +end
spec/requests/api/projects_spec.rb
@@ -10,7 +10,6 @@ describe API::API do @@ -10,7 +10,6 @@ describe API::API do
10 let(:user3) { create(:user) } 10 let(:user3) { create(:user) }
11 let(:admin) { create(:admin) } 11 let(:admin) { create(:admin) }
12 let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) } 12 let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) }
13 - let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }  
14 let!(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') } 13 let!(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
15 let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } 14 let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
16 let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } 15 let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
@@ -36,6 +35,32 @@ describe API::API do @@ -36,6 +35,32 @@ describe API::API do
36 end 35 end
37 end 36 end
38 37
  38 + describe "GET /projects/all" do
  39 + context "when unauthenticated" do
  40 + it "should return authentication error" do
  41 + get api("/projects/all")
  42 + response.status.should == 401
  43 + end
  44 + end
  45 +
  46 + context "when authenticated as regular user" do
  47 + it "should return authentication error" do
  48 + get api("/projects/all", user)
  49 + response.status.should == 403
  50 + end
  51 + end
  52 +
  53 + context "when authenticated as admin" do
  54 + it "should return an array of all projects" do
  55 + get api("/projects/all", admin)
  56 + response.status.should == 200
  57 + json_response.should be_an Array
  58 + json_response.first['name'].should == project.name
  59 + json_response.first['owner']['email'].should == user.email
  60 + end
  61 + end
  62 + end
  63 +
39 describe "POST /projects" do 64 describe "POST /projects" do
40 context "maximum number of projects reached" do 65 context "maximum number of projects reached" do
41 before do 66 before do
@@ -91,7 +116,6 @@ describe API::API do @@ -91,7 +116,6 @@ describe API::API do
91 it "should assign attributes to project" do 116 it "should assign attributes to project" do
92 project = attributes_for(:project, { 117 project = attributes_for(:project, {
93 description: Faker::Lorem.sentence, 118 description: Faker::Lorem.sentence,
94 - default_branch: 'stable',  
95 issues_enabled: false, 119 issues_enabled: false,
96 wall_enabled: false, 120 wall_enabled: false,
97 merge_requests_enabled: false, 121 merge_requests_enabled: false,
@@ -107,19 +131,46 @@ describe API::API do @@ -107,19 +131,46 @@ describe API::API do
107 end 131 end
108 132
109 it "should set a project as public" do 133 it "should set a project as public" do
  134 + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC })
  135 + post api("/projects", user), project
  136 + json_response['public'].should be_true
  137 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
  138 + end
  139 +
  140 + it "should set a project as public using :public" do
110 project = attributes_for(:project, { public: true }) 141 project = attributes_for(:project, { public: true })
111 post api("/projects", user), project 142 post api("/projects", user), project
112 json_response['public'].should be_true 143 json_response['public'].should be_true
  144 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
  145 + end
113 146
  147 + it "should set a project as internal" do
  148 + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL })
  149 + post api("/projects", user), project
  150 + json_response['public'].should be_false
  151 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
114 end 152 end
115 153
116 - it "should set a project as private" do  
117 - project = attributes_for(:project, { public: false }) 154 + it "should set a project as internal overriding :public" do
  155 + project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL })
118 post api("/projects", user), project 156 post api("/projects", user), project
119 json_response['public'].should be_false 157 json_response['public'].should be_false
  158 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
  159 + end
120 160
  161 + it "should set a project as private" do
  162 + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
  163 + post api("/projects", user), project
  164 + json_response['public'].should be_false
  165 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
121 end 166 end
122 167
  168 + it "should set a project as private using :public" do
  169 + project = attributes_for(:project, { public: false })
  170 + post api("/projects", user), project
  171 + json_response['public'].should be_false
  172 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
  173 + end
123 end 174 end
124 175
125 describe "POST /projects/user/:id" do 176 describe "POST /projects/user/:id" do
@@ -146,7 +197,6 @@ describe API::API do @@ -146,7 +197,6 @@ describe API::API do
146 it "should assign attributes to project" do 197 it "should assign attributes to project" do
147 project = attributes_for(:project, { 198 project = attributes_for(:project, {
148 description: Faker::Lorem.sentence, 199 description: Faker::Lorem.sentence,
149 - default_branch: 'stable',  
150 issues_enabled: false, 200 issues_enabled: false,
151 wall_enabled: false, 201 wall_enabled: false,
152 merge_requests_enabled: false, 202 merge_requests_enabled: false,
@@ -162,19 +212,46 @@ describe API::API do @@ -162,19 +212,46 @@ describe API::API do
162 end 212 end
163 213
164 it "should set a project as public" do 214 it "should set a project as public" do
  215 + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC })
  216 + post api("/projects/user/#{user.id}", admin), project
  217 + json_response['public'].should be_true
  218 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
  219 + end
  220 +
  221 + it "should set a project as public using :public" do
165 project = attributes_for(:project, { public: true }) 222 project = attributes_for(:project, { public: true })
166 post api("/projects/user/#{user.id}", admin), project 223 post api("/projects/user/#{user.id}", admin), project
167 json_response['public'].should be_true 224 json_response['public'].should be_true
  225 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
  226 + end
168 227
  228 + it "should set a project as internal" do
  229 + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL })
  230 + post api("/projects/user/#{user.id}", admin), project
  231 + json_response['public'].should be_false
  232 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
169 end 233 end
170 234
171 - it "should set a project as private" do  
172 - project = attributes_for(:project, { public: false }) 235 + it "should set a project as internal overriding :public" do
  236 + project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL })
173 post api("/projects/user/#{user.id}", admin), project 237 post api("/projects/user/#{user.id}", admin), project
174 json_response['public'].should be_false 238 json_response['public'].should be_false
  239 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
  240 + end
175 241
  242 + it "should set a project as private" do
  243 + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
  244 + post api("/projects/user/#{user.id}", admin), project
  245 + json_response['public'].should be_false
  246 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
176 end 247 end
177 248
  249 + it "should set a project as private using :public" do
  250 + project = attributes_for(:project, { public: false })
  251 + post api("/projects/user/#{user.id}", admin), project
  252 + json_response['public'].should be_false
  253 + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
  254 + end
178 end 255 end
179 256
180 describe "GET /projects/:id" do 257 describe "GET /projects/:id" do
@@ -361,121 +438,6 @@ describe API::API do @@ -361,121 +438,6 @@ describe API::API do
361 end 438 end
362 end 439 end
363 440
364 - describe "GET /projects/:id/hooks" do  
365 - context "authorized user" do  
366 - it "should return project hooks" do  
367 - get api("/projects/#{project.id}/hooks", user)  
368 - response.status.should == 200  
369 -  
370 - json_response.should be_an Array  
371 - json_response.count.should == 1  
372 - json_response.first['url'].should == "http://example.com"  
373 - end  
374 - end  
375 -  
376 - context "unauthorized user" do  
377 - it "should not access project hooks" do  
378 - get api("/projects/#{project.id}/hooks", user3)  
379 - response.status.should == 403  
380 - end  
381 - end  
382 - end  
383 -  
384 - describe "GET /projects/:id/hooks/:hook_id" do  
385 - context "authorized user" do  
386 - it "should return a project hook" do  
387 - get api("/projects/#{project.id}/hooks/#{hook.id}", user)  
388 - response.status.should == 200  
389 - json_response['url'].should == hook.url  
390 - end  
391 -  
392 - it "should return a 404 error if hook id is not available" do  
393 - get api("/projects/#{project.id}/hooks/1234", user)  
394 - response.status.should == 404  
395 - end  
396 - end  
397 -  
398 - context "unauthorized user" do  
399 - it "should not access an existing hook" do  
400 - get api("/projects/#{project.id}/hooks/#{hook.id}", user3)  
401 - response.status.should == 403  
402 - end  
403 - end  
404 -  
405 - it "should return a 404 error if hook id is not available" do  
406 - get api("/projects/#{project.id}/hooks/1234", user)  
407 - response.status.should == 404  
408 - end  
409 - end  
410 -  
411 - describe "POST /projects/:id/hooks" do  
412 - it "should add hook to project" do  
413 - expect {  
414 - post api("/projects/#{project.id}/hooks", user),  
415 - url: "http://example.com"  
416 - }.to change {project.hooks.count}.by(1)  
417 - response.status.should == 201  
418 - end  
419 -  
420 - it "should return a 400 error if url not given" do  
421 - post api("/projects/#{project.id}/hooks", user)  
422 - response.status.should == 400  
423 - end  
424 -  
425 - it "should return a 422 error if url not valid" do  
426 - post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"  
427 - response.status.should == 422  
428 - end  
429 - end  
430 -  
431 - describe "PUT /projects/:id/hooks/:hook_id" do  
432 - it "should update an existing project hook" do  
433 - put api("/projects/#{project.id}/hooks/#{hook.id}", user),  
434 - url: 'http://example.org'  
435 - response.status.should == 200  
436 - json_response['url'].should == 'http://example.org'  
437 - end  
438 -  
439 - it "should return 404 error if hook id not found" do  
440 - put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'  
441 - response.status.should == 404  
442 - end  
443 -  
444 - it "should return 400 error if url is not given" do  
445 - put api("/projects/#{project.id}/hooks/#{hook.id}", user)  
446 - response.status.should == 400  
447 - end  
448 -  
449 - it "should return a 422 error if url is not valid" do  
450 - put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com'  
451 - response.status.should == 422  
452 - end  
453 - end  
454 -  
455 - describe "DELETE /projects/:id/hooks/:hook_id" do  
456 - it "should delete hook from project" do  
457 - expect {  
458 - delete api("/projects/#{project.id}/hooks/#{hook.id}", user)  
459 - }.to change {project.hooks.count}.by(-1)  
460 - response.status.should == 200  
461 - end  
462 -  
463 - it "should return success when deleting hook" do  
464 - delete api("/projects/#{project.id}/hooks/#{hook.id}", user)  
465 - response.status.should == 200  
466 - end  
467 -  
468 - it "should return success when deleting non existent hook" do  
469 - delete api("/projects/#{project.id}/hooks/42", user)  
470 - response.status.should == 200  
471 - end  
472 -  
473 - it "should return a 405 error if hook id not given" do  
474 - delete api("/projects/#{project.id}/hooks", user)  
475 - response.status.should == 405  
476 - end  
477 - end  
478 -  
479 describe "GET /projects/:id/snippets" do 441 describe "GET /projects/:id/snippets" do
480 it "should return an array of project snippets" do 442 it "should return an array of project snippets" do
481 get api("/projects/#{project.id}/snippets", user) 443 get api("/projects/#{project.id}/snippets", user)
@@ -628,10 +590,10 @@ describe API::API do @@ -628,10 +590,10 @@ describe API::API do
628 590
629 describe :fork_admin do 591 describe :fork_admin do
630 let(:project_fork_target) { create(:project) } 592 let(:project_fork_target) { create(:project) }
631 - let(:project_fork_source) { create(:project, public: true) } 593 + let(:project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
632 594
633 describe "POST /projects/:id/fork/:forked_from_id" do 595 describe "POST /projects/:id/fork/:forked_from_id" do
634 - let(:new_project_fork_source) { create(:project, public: true) } 596 + let(:new_project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
635 597
636 it "shouldn't available for non admin users" do 598 it "shouldn't available for non admin users" do
637 post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) 599 post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
@@ -700,8 +662,10 @@ describe API::API do @@ -700,8 +662,10 @@ describe API::API do
700 let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) } 662 let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
701 let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) } 663 let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
702 let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) } 664 let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
703 - let!(:public) { create(:project, name: "another #{query}",public: true) }  
704 - let!(:unfound_public) { create(:project, name: 'unfound public', public: true) } 665 + let!(:internal) { create(:project, name: "internal #{query}", visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
  666 + let!(:unfound_internal) { create(:project, name: 'unfound internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
  667 + let!(:public) { create(:project, name: "public #{query}", visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
  668 + let!(:unfound_public) { create(:project, name: 'unfound public', visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
705 669
706 context "when unauthenticated" do 670 context "when unauthenticated" do
707 it "should return authentication error" do 671 it "should return authentication error" do
@@ -715,7 +679,7 @@ describe API::API do @@ -715,7 +679,7 @@ describe API::API do
715 get api("/projects/search/#{query}",user) 679 get api("/projects/search/#{query}",user)
716 response.status.should == 200 680 response.status.should == 200
717 json_response.should be_an Array 681 json_response.should be_an Array
718 - json_response.size.should == 5 682 + json_response.size.should == 6
719 json_response.each {|project| project['name'].should =~ /.*query.*/} 683 json_response.each {|project| project['name'].should =~ /.*query.*/}
720 end 684 end
721 end 685 end
@@ -725,8 +689,8 @@ describe API::API do @@ -725,8 +689,8 @@ describe API::API do
725 get api("/projects/search/#{query}", user2) 689 get api("/projects/search/#{query}", user2)
726 response.status.should == 200 690 response.status.should == 200
727 json_response.should be_an Array 691 json_response.should be_an Array
728 - json_response.size.should == 1  
729 - json_response.first['name'].should == "another #{query}" 692 + json_response.size.should == 2
  693 + json_response.each {|project| project['name'].should =~ /(internal|public) query/}
730 end 694 end
731 end 695 end
732 end 696 end
spec/requests/api/session_spec.rb
@@ -8,7 +8,7 @@ describe API::API do @@ -8,7 +8,7 @@ describe API::API do
8 describe "POST /session" do 8 describe "POST /session" do
9 context "when valid password" do 9 context "when valid password" do
10 it "should return private token" do 10 it "should return private token" do
11 - post api("/session"), email: user.email, password: '123456' 11 + post api("/session"), email: user.email, password: '12345678'
12 response.status.should == 201 12 response.status.should == 201
13 13
14 json_response['email'].should == user.email 14 json_response['email'].should == user.email
spec/routing/routing_spec.rb
@@ -185,6 +185,13 @@ describe Profiles::KeysController, &quot;routing&quot; do @@ -185,6 +185,13 @@ describe Profiles::KeysController, &quot;routing&quot; do
185 end 185 end
186 end 186 end
187 187
  188 +# profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy
  189 +describe Profiles::AvatarsController, "routing" do
  190 + it "to #destroy" do
  191 + delete("/profile/avatar").should route_to('profiles/avatars#destroy')
  192 + end
  193 +end
  194 +
188 # dashboard GET /dashboard(.:format) dashboard#show 195 # dashboard GET /dashboard(.:format) dashboard#show
189 # dashboard_issues GET /dashboard/issues(.:format) dashboard#issues 196 # dashboard_issues GET /dashboard/issues(.:format) dashboard#issues
190 # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests 197 # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests
spec/services/git_push_service_spec.rb
@@ -74,38 +74,19 @@ describe GitPushService do @@ -74,38 +74,19 @@ describe GitPushService do
74 end 74 end
75 75
76 describe "Web Hooks" do 76 describe "Web Hooks" do
77 - context "with web hooks" do  
78 - before do  
79 - @project_hook = create(:project_hook)  
80 - @project_hook_2 = create(:project_hook)  
81 - project.hooks << [@project_hook, @project_hook_2]  
82 -  
83 - stub_request(:post, @project_hook.url)  
84 - stub_request(:post, @project_hook_2.url)  
85 - end  
86 -  
87 - it "executes multiple web hook" do  
88 - @project_hook.should_receive(:async_execute).once  
89 - @project_hook_2.should_receive(:async_execute).once  
90 -  
91 - service.execute(project, user, @oldrev, @newrev, @ref)  
92 - end  
93 - end  
94 -  
95 context "execute web hooks" do 77 context "execute web hooks" do
96 - before do  
97 - @project_hook = create(:project_hook)  
98 - project.hooks << [@project_hook]  
99 - stub_request(:post, @project_hook.url)  
100 - end  
101 -  
102 it "when pushing a branch for the first time" do 78 it "when pushing a branch for the first time" do
103 - @project_hook.should_receive(:async_execute) 79 + project.should_receive(:execute_hooks)
104 service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master') 80 service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master')
105 end 81 end
106 82
  83 + it "when pushing new commits to existing branch" do
  84 + project.should_receive(:execute_hooks)
  85 + service.execute(project, user, 'oldrev', 'newrev', 'refs/heads/master')
  86 + end
  87 +
107 it "when pushing tags" do 88 it "when pushing tags" do
108 - @project_hook.should_not_receive(:async_execute) 89 + project.should_not_receive(:execute_hooks)
109 service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0') 90 service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0')
110 end 91 end
111 end 92 end
spec/support/login_helpers.rb
@@ -16,7 +16,7 @@ module LoginHelpers @@ -16,7 +16,7 @@ module LoginHelpers
16 def login_with(user) 16 def login_with(user)
17 visit new_user_session_path 17 visit new_user_session_path
18 fill_in "user_login", with: user.email 18 fill_in "user_login", with: user.email
19 - fill_in "user_password", with: "123456" 19 + fill_in "user_password", with: "12345678"
20 click_button "Sign in" 20 click_button "Sign in"
21 Thread.current[:current_user] = user 21 Thread.current[:current_user] = user
22 end 22 end
spec/support/test_env.rb
@@ -45,6 +45,7 @@ module TestEnv @@ -45,6 +45,7 @@ module TestEnv
45 def disable_mailer 45 def disable_mailer
46 NotificationService.any_instance.stub(mailer: double.as_null_object) 46 NotificationService.any_instance.stub(mailer: double.as_null_object)
47 end 47 end
  48 +
48 def enable_mailer 49 def enable_mailer
49 NotificationService.any_instance.unstub(:mailer) 50 NotificationService.any_instance.unstub(:mailer)
50 end 51 end
@@ -68,7 +69,8 @@ module TestEnv @@ -68,7 +69,8 @@ module TestEnv
68 remove_repository: true, 69 remove_repository: true,
69 update_repository_head: true, 70 update_repository_head: true,
70 add_key: true, 71 add_key: true,
71 - remove_key: true 72 + remove_key: true,
  73 + version: '6.3.0'
72 ) 74 )
73 75
74 Gitlab::Satellite::Satellite.any_instance.stub( 76 Gitlab::Satellite::Satellite.any_instance.stub(
@@ -96,6 +98,15 @@ module TestEnv @@ -96,6 +98,15 @@ module TestEnv
96 FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git") 98 FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git")
97 end 99 end
98 100
  101 + def reset_satellite_dir
  102 + setup_stubs
  103 + FileUtils.cd(seed_satellite_path) do
  104 + `git reset --hard --quiet`
  105 + `git clean -fx`
  106 + `git checkout --quiet origin/master`
  107 + end
  108 + end
  109 +
99 # Create a repo and it's satellite 110 # Create a repo and it's satellite
100 def create_repo(namespace, name) 111 def create_repo(namespace, name)
101 setup_stubs 112 setup_stubs
vendor/assets/javascripts/ace-src-noconflict/mode-diff.js
@@ -66,7 +66,7 @@ var DiffHighlightRules = function() { @@ -66,7 +66,7 @@ var DiffHighlightRules = function() {
66 "regex": "^(?:\\*{15}|={67}|-{3}|\\+{3})$", 66 "regex": "^(?:\\*{15}|={67}|-{3}|\\+{3})$",
67 "token": "punctuation.definition.separator.diff", 67 "token": "punctuation.definition.separator.diff",
68 "name": "keyword" 68 "name": "keyword"
69 - }, { //diff.range.unified 69 + }, { //diff.range.inline
70 "regex": "^(@@)(\\s*.+?\\s*)(@@)(.*)$", 70 "regex": "^(@@)(\\s*.+?\\s*)(@@)(.*)$",
71 "token": [ 71 "token": [
72 "constant", 72 "constant",