Commit 61cfa2a7a6e1d4557b69e17c537656e8a0201ac8

Authored by Sebastian Ziebell
2 parents d269d107 6beae84e

Merge branch 'master' into fixes/api

Conflicts:
	lib/api/projects.rb
Showing 147 changed files with 1924 additions and 1221 deletions   Show diff stats
CONTRIBUTING.md
1 -# Contact & support 1 +# Contribute to GitLab
2 2
3 -If you want quick help, head over to our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq).  
4 -Otherwise you can follow our [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) for a more systematic and thorough guide to solving your issues. 3 +If you have a question or want to contribute to GitLab this guide show you the appropriate channel to use.
5 4
  5 +## Ruling out common errors
6 6
  7 +Some errors are common and it may so happen, that you are not the only one who stumbled over a particular issue. We have [collected several of those and documented quick solutions](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) for them.
7 8
8 -# Contribute to GitLab 9 +## Support forum
  10 +
  11 +Please visit our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) for any kind of question regarding the usage or adiministration/configuration of GitLab.
  12 +
  13 +### Use the support forum if ...
9 14
10 -## Recipes 15 +* You get permission denied errors
  16 +* You can't see your repos
  17 +* You have issues cloning, pulling or pushing
  18 +* You have issues with web_hooks not firing
11 19
12 -We collect user submitted installation scripts and config file templates for platforms we don't support officially.  
13 -We believe there is merit in allowing a certain amount of diversity.  
14 -You can get and submit your solution to running/configuring GitLab with your favorite OS/distro, database, web server, cloud hoster, configuration management tool, etc. 20 +**Search** for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and had it resolved.
15 21
16 -Help us improve the collection of [GitLab Recipes](https://github.com/gitlabhq/gitlab-recipes/) 22 +## Paid support
17 23
  24 +Community support in the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) is done by volunteers. Paid support is available from [GitLab.com](http://blog.gitlab.com/services/)
18 25
19 ## Feature suggestions 26 ## Feature suggestions
20 27
21 -Follow the [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) and support other peoples ideas or propose your own. 28 +Feature suggestions don't belong in issues but can go to [Feedback forum](http://gitlab.uservoice.com/forums/176466-general) where they can be voted on.
  29 +
  30 +## Pull requests
  31 +
  32 +Code speaks louder than words. If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows:
  33 +
  34 +1. Fork the project on GitHub
  35 +1. Create a feature branch
  36 +1. Write tests and code
  37 +1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
  38 +1. Push the commit to your fork
  39 +1. Submit a pull request
  40 +
  41 +We will accept pull requests if:
  42 +
  43 +* The code has proper tests and all tests pass
  44 +* It can be merged without problems (if not please use: git rebase master)
  45 +* It doesn't break any existing functionality
  46 +* It's quality code that conforms to the [Rails style guide](https://github.com/bbatsov/rails-style-guide) and best practices
  47 +* The description includes a motive for your change and the method you used to achieve it
  48 +* It keeps the GitLab code base clean and well structured
  49 +* We think other users will need the same functionality
  50 +* If it makes changes to the UI the pull request should include screenshots
  51 +
  52 +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).
  53 +
  54 +## Submitting via GitHub's issue tracker
  55 +
  56 +* For obvious bugs or misbehavior in GitLab in the master branch. Please include the revision id and a reproducible test case.
  57 +* For problematic or insufficient documentation. Please give a suggestion on how to improve it.
  58 +
  59 +If you're unsure where to post, post it to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) first.
  60 +There are a lot of helpful GitLab users there who may be able to help you quickly.
  61 +If your particular issue turns out to be a bug, it will find its way from there to the [issue tracker on GitHub](https://github.com/gitlabhq/gitlabhq/issues).
  62 +
  63 +### When submitting an issue
  64 +
  65 +**Search** for similar entries before submitting your own, there's a good chance somebody else had the same issue or idea. Show your support with `:+1:` and/or join the discussion.
  66 +
  67 +Please consider the following points when submitting an **issue**:
22 68
  69 +* Summarize your issue in one sentence (what happened wrong, when you did/expected something else)
  70 +* Describe your issue in detail (including steps to reproduce)
  71 +* Add logs or screen shots when possible
  72 +* Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`)
23 73
24 -## Code 74 +## Thank you!
25 75
26 -Follow our [Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide) to set you up for hacking on GitLab. 76 +By taking the time to use the right channel, you help the development team to organize and prioritize issues and suggestions in order to make GitLab a better product for us all.
@@ -15,16 +15,18 @@ gem "mysql2", group: :mysql @@ -15,16 +15,18 @@ gem "mysql2", group: :mysql
15 gem "pg", group: :postgres 15 gem "pg", group: :postgres
16 16
17 # Auth 17 # Auth
18 -gem "devise", "~> 2.1.0"  
19 -gem 'omniauth', "~> 1.1.1" 18 +gem "devise"
  19 +gem 'omniauth', "~> 1.1.3"
20 gem 'omniauth-google-oauth2' 20 gem 'omniauth-google-oauth2'
21 gem 'omniauth-twitter' 21 gem 'omniauth-twitter'
22 gem 'omniauth-github' 22 gem 'omniauth-github'
23 23
24 -# GITLAB patched libs  
25 -gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '9e98418ce2d654485b967003726aa2706a10060b'  
26 -gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8'  
27 -gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '8e6afc2da821354774aa4d1ee8a1aa2082f84a3e' 24 +# Extracting information from a git repository
  25 +gem "gitlab-grit", '~> 1.0.0', require: 'grit'
  26 +gem 'grit_ext', '~> 0.6.2'
  27 +
  28 +# Ruby/Rack Git Smart-HTTP Server Handler
  29 +gem 'gitlab-grack', '~> 1.0.0', require: 'grack'
28 30
29 # LDAP Auth 31 # LDAP Auth
30 gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap" 32 gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
@@ -33,7 +35,7 @@ gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap" @@ -33,7 +35,7 @@ gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
33 gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db" 35 gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db"
34 36
35 # Syntax highlighter 37 # Syntax highlighter
36 -gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", branch: "master" 38 +gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb'
37 39
38 # Language detection 40 # Language detection
39 gem "github-linguist", "~> 2.3.4" , require: "linguist" 41 gem "github-linguist", "~> 2.3.4" , require: "linguist"
@@ -46,14 +48,17 @@ gem "grape-entity", "~> 0.2.0" @@ -46,14 +48,17 @@ gem "grape-entity", "~> 0.2.0"
46 # based on human-friendly examples 48 # based on human-friendly examples
47 gem "stamp" 49 gem "stamp"
48 50
  51 +# Enumeration fields
  52 +gem 'enumerize'
  53 +
49 # Pagination 54 # Pagination
50 gem "kaminari", "~> 0.14.1" 55 gem "kaminari", "~> 0.14.1"
51 56
52 # HAML 57 # HAML
53 -gem "haml-rails", "~> 0.3.5" 58 +gem "haml-rails"
54 59
55 # Files attachments 60 # Files attachments
56 -gem "carrierwave", "~> 0.7.1" 61 +gem "carrierwave"
57 62
58 # Authorization 63 # Authorization
59 gem "six" 64 gem "six"
@@ -69,7 +74,7 @@ gem "redcarpet", "~> 2.2.2" @@ -69,7 +74,7 @@ gem "redcarpet", "~> 2.2.2"
69 gem "github-markup", "~> 0.7.4", require: 'github/markup' 74 gem "github-markup", "~> 0.7.4", require: 'github/markup'
70 75
71 # Servers 76 # Servers
72 -gem "unicorn", "~> 4.4.0" 77 +gem "unicorn"
73 78
74 # State machine 79 # State machine
75 gem "state_machine" 80 gem "state_machine"
@@ -78,12 +83,12 @@ gem "state_machine" @@ -78,12 +83,12 @@ gem "state_machine"
78 gem "acts-as-taggable-on", "2.3.3" 83 gem "acts-as-taggable-on", "2.3.3"
79 84
80 # Decorators 85 # Decorators
81 -gem "draper", "~> 0.18.0" 86 +gem "draper"
82 87
83 # Background jobs 88 # Background jobs
84 gem 'slim' 89 gem 'slim'
85 gem 'sinatra', require: nil 90 gem 'sinatra', require: nil
86 -gem 'sidekiq', '2.7.3' 91 +gem 'sidekiq'
87 92
88 # HTTP requests 93 # HTTP requests
89 gem "httparty" 94 gem "httparty"
@@ -113,6 +118,7 @@ group :assets do @@ -113,6 +118,7 @@ group :assets do
113 gem 'bootstrap-sass', "2.2.1.1" 118 gem 'bootstrap-sass', "2.2.1.1"
114 gem "font-awesome-sass-rails", "~> 3.0.0" 119 gem "font-awesome-sass-rails", "~> 3.0.0"
115 gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' 120 gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
  121 + gem "gon"
116 end 122 end
117 123
118 group :development do 124 group :development do
@@ -140,7 +146,7 @@ group :development, :test do @@ -140,7 +146,7 @@ group :development, :test do
140 gem "capybara", '2.0.2' 146 gem "capybara", '2.0.2'
141 gem "pry" 147 gem "pry"
142 gem "awesome_print" 148 gem "awesome_print"
143 - gem "database_cleaner", ref: "9f898fc50d87a5d51760f9dcf374bf5ffda21baf", git: "https://github.com/bmabey/database_cleaner.git" 149 + gem "database_cleaner"
144 gem "launchy" 150 gem "launchy"
145 gem 'factory_girl_rails' 151 gem 'factory_girl_rails'
146 152
1 GIT 1 GIT
2 - remote: https://github.com/bmabey/database_cleaner.git  
3 - revision: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf  
4 - ref: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf  
5 - specs:  
6 - database_cleaner (0.9.1)  
7 -  
8 -GIT  
9 remote: https://github.com/ctran/annotate_models.git 2 remote: https://github.com/ctran/annotate_models.git
10 revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e 3 revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
11 specs: 4 specs:
@@ -14,41 +7,6 @@ GIT @@ -14,41 +7,6 @@ GIT
14 rake (>= 0.8.7) 7 rake (>= 0.8.7)
15 8
16 GIT 9 GIT
17 - remote: https://github.com/gitlabhq/grack.git  
18 - revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8  
19 - ref: ba46f3b0845c6a09d488ae6abdce6ede37e227e8  
20 - specs:  
21 - grack (1.0.0)  
22 - rack (~> 1.4.1)  
23 -  
24 -GIT  
25 - remote: https://github.com/gitlabhq/grit.git  
26 - revision: 9e98418ce2d654485b967003726aa2706a10060b  
27 - ref: 9e98418ce2d654485b967003726aa2706a10060b  
28 - specs:  
29 - grit (2.5.0)  
30 - diff-lcs (~> 1.1)  
31 - mime-types (~> 1.15)  
32 - posix-spawn (~> 0.3.6)  
33 -  
34 -GIT  
35 - remote: https://github.com/gitlabhq/grit_ext.git  
36 - revision: 8e6afc2da821354774aa4d1ee8a1aa2082f84a3e  
37 - ref: 8e6afc2da821354774aa4d1ee8a1aa2082f84a3e  
38 - specs:  
39 - grit_ext (0.6.1)  
40 - charlock_holmes (~> 0.6.9)  
41 -  
42 -GIT  
43 - remote: https://github.com/gitlabhq/pygments.rb.git  
44 - revision: db1da0343adf86b49bdc3add04d02d2e80438d38  
45 - branch: master  
46 - specs:  
47 - pygments.rb (0.3.2)  
48 - posix-spawn (~> 0.3.6)  
49 - yajl-ruby (~> 1.1.0)  
50 -  
51 -GIT  
52 remote: https://github.com/gitlabhq/raphael-rails.git 10 remote: https://github.com/gitlabhq/raphael-rails.git
53 revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58 11 revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58
54 specs: 12 specs:
@@ -89,12 +47,13 @@ GEM @@ -89,12 +47,13 @@ GEM
89 addressable (2.3.2) 47 addressable (2.3.2)
90 arel (3.0.2) 48 arel (3.0.2)
91 awesome_print (1.1.0) 49 awesome_print (1.1.0)
92 - backports (2.6.5) 50 + backports (2.6.7)
93 bcrypt-ruby (3.0.1) 51 bcrypt-ruby (3.0.1)
94 better_errors (0.3.2) 52 better_errors (0.3.2)
95 coderay (>= 1.0.0) 53 coderay (>= 1.0.0)
96 erubis (>= 2.7.0) 54 erubis (>= 2.7.0)
97 - binding_of_caller (0.6.8) 55 + binding_of_caller (0.7.1)
  56 + debug_inspector (>= 0.0.1)
98 bootstrap-sass (2.2.1.1) 57 bootstrap-sass (2.2.1.1)
99 sass (~> 3.2) 58 sass (~> 3.2)
100 builder (3.0.4) 59 builder (3.0.4)
@@ -105,7 +64,7 @@ GEM @@ -105,7 +64,7 @@ GEM
105 rack-test (>= 0.5.4) 64 rack-test (>= 0.5.4)
106 selenium-webdriver (~> 2.0) 65 selenium-webdriver (~> 2.0)
107 xpath (~> 1.0.0) 66 xpath (~> 1.0.0)
108 - carrierwave (0.7.1) 67 + carrierwave (0.8.0)
109 activemodel (>= 3.2.0) 68 activemodel (>= 3.2.0)
110 activesupport (>= 3.2.0) 69 activesupport (>= 3.2.0)
111 celluloid (0.12.4) 70 celluloid (0.12.4)
@@ -132,18 +91,24 @@ GEM @@ -132,18 +91,24 @@ GEM
132 connection_pool (1.0.0) 91 connection_pool (1.0.0)
133 crack (0.3.1) 92 crack (0.3.1)
134 daemons (1.1.9) 93 daemons (1.1.9)
135 - devise (2.1.2) 94 + database_cleaner (0.9.1)
  95 + debug_inspector (0.0.2)
  96 + descendants_tracker (0.0.1)
  97 + devise (2.2.3)
136 bcrypt-ruby (~> 3.0) 98 bcrypt-ruby (~> 3.0)
137 orm_adapter (~> 0.1) 99 orm_adapter (~> 0.1)
138 railties (~> 3.1) 100 railties (~> 3.1)
139 warden (~> 1.2.1) 101 warden (~> 1.2.1)
140 diff-lcs (1.1.3) 102 diff-lcs (1.1.3)
141 - draper (0.18.0)  
142 - actionpack (~> 3.2)  
143 - activesupport (~> 3.2) 103 + draper (1.1.0)
  104 + actionpack (>= 3.0)
  105 + activesupport (>= 3.0)
  106 + request_store (~> 1.0.3)
144 email_spec (1.4.0) 107 email_spec (1.4.0)
145 launchy (~> 2.1) 108 launchy (~> 2.1)
146 mail (~> 2.2) 109 mail (~> 2.2)
  110 + enumerize (0.5.1)
  111 + activesupport (>= 3.2)
147 erubis (2.7.0) 112 erubis (2.7.0)
148 escape_utils (0.2.4) 113 escape_utils (0.2.4)
149 eventmachine (1.0.0) 114 eventmachine (1.0.0)
@@ -155,7 +120,7 @@ GEM @@ -155,7 +120,7 @@ GEM
155 factory_girl_rails (4.1.0) 120 factory_girl_rails (4.1.0)
156 factory_girl (~> 4.1.0) 121 factory_girl (~> 4.1.0)
157 railties (>= 3.0.0) 122 railties (>= 3.0.0)
158 - faraday (0.8.4) 123 + faraday (0.8.6)
159 multipart-post (~> 1.1) 124 multipart-post (~> 1.1)
160 faye-websocket (0.4.7) 125 faye-websocket (0.4.7)
161 eventmachine (>= 0.12.0) 126 eventmachine (>= 0.12.0)
@@ -164,7 +129,7 @@ GEM @@ -164,7 +129,7 @@ GEM
164 font-awesome-sass-rails (3.0.0.1) 129 font-awesome-sass-rails (3.0.0.1)
165 railties (>= 3.1.1) 130 railties (>= 3.1.1)
166 sass-rails (>= 3.1.1) 131 sass-rails (>= 3.1.1)
167 - foreman (0.60.2) 132 + foreman (0.61.0)
168 thor (>= 0.13.6) 133 thor (>= 0.13.6)
169 gemoji (1.2.1) 134 gemoji (1.2.1)
170 gherkin-ruby (0.2.1) 135 gherkin-ruby (0.2.1)
@@ -174,7 +139,16 @@ GEM @@ -174,7 +139,16 @@ GEM
174 escape_utils (~> 0.2.3) 139 escape_utils (~> 0.2.3)
175 mime-types (~> 1.19) 140 mime-types (~> 1.19)
176 pygments.rb (>= 0.2.13) 141 pygments.rb (>= 0.2.13)
177 - github-markup (0.7.4) 142 + github-markup (0.7.5)
  143 + gitlab-grack (1.0.0)
  144 + rack (~> 1.4.1)
  145 + gitlab-grit (1.0.0)
  146 + diff-lcs (~> 1.1)
  147 + mime-types (~> 1.15)
  148 + posix-spawn (~> 0.3.6)
  149 + gitlab-pygments.rb (0.3.2)
  150 + posix-spawn (~> 0.3.6)
  151 + yajl-ruby (~> 1.1.0)
178 gitlab_meta (5.0) 152 gitlab_meta (5.0)
179 gitlab_omniauth-ldap (1.0.2) 153 gitlab_omniauth-ldap (1.0.2)
180 net-ldap (~> 0.2.2) 154 net-ldap (~> 0.2.2)
@@ -182,17 +156,22 @@ GEM @@ -182,17 +156,22 @@ GEM
182 pyu-ruby-sasl (~> 0.0.3.1) 156 pyu-ruby-sasl (~> 0.0.3.1)
183 rubyntlm (~> 0.1.1) 157 rubyntlm (~> 0.1.1)
184 gitlab_yaml_db (1.0.0) 158 gitlab_yaml_db (1.0.0)
185 - grape (0.3.1) 159 + gon (4.0.2)
  160 + grape (0.3.2)
186 activesupport 161 activesupport
187 - grape-entity (~> 0.2.0)  
188 - hashie (~> 1.2) 162 + builder
  163 + hashie (>= 1.2.0)
189 multi_json (>= 1.3.2) 164 multi_json (>= 1.3.2)
190 - multi_xml 165 + multi_xml (>= 0.5.2)
191 rack 166 rack
192 rack-accept 167 rack-accept
193 rack-mount 168 rack-mount
194 virtus 169 virtus
195 grape-entity (0.2.0) 170 grape-entity (0.2.0)
  171 + activesupport
  172 + multi_json (>= 1.3.2)
  173 + grit_ext (0.6.2)
  174 + charlock_holmes (~> 0.6.9)
196 growl (1.0.3) 175 growl (1.0.3)
197 guard (1.5.4) 176 guard (1.5.4)
198 listen (>= 0.4.2) 177 listen (>= 0.4.2)
@@ -205,20 +184,21 @@ GEM @@ -205,20 +184,21 @@ GEM
205 guard-spinach (0.0.2) 184 guard-spinach (0.0.2)
206 guard (>= 1.1) 185 guard (>= 1.1)
207 spinach 186 spinach
208 - haml (3.1.7)  
209 - haml-rails (0.3.5) 187 + haml (4.0.0)
  188 + tilt
  189 + haml-rails (0.4)
210 actionpack (>= 3.1, < 4.1) 190 actionpack (>= 3.1, < 4.1)
211 activesupport (>= 3.1, < 4.1) 191 activesupport (>= 3.1, < 4.1)
212 - haml (~> 3.1) 192 + haml (>= 3.1, < 4.1)
213 railties (>= 3.1, < 4.1) 193 railties (>= 3.1, < 4.1)
214 hashie (1.2.0) 194 hashie (1.2.0)
215 hike (1.2.1) 195 hike (1.2.1)
216 http_parser.rb (0.5.3) 196 http_parser.rb (0.5.3)
217 - httparty (0.9.0) 197 + httparty (0.10.2)
218 multi_json (~> 1.0) 198 multi_json (~> 1.0)
219 - multi_xml 199 + multi_xml (>= 0.5.2)
220 httpauth (0.2.0) 200 httpauth (0.2.0)
221 - i18n (0.6.1) 201 + i18n (0.6.4)
222 journey (1.0.4) 202 journey (1.0.4)
223 jquery-atwho-rails (0.1.7) 203 jquery-atwho-rails (0.1.7)
224 jquery-rails (2.1.3) 204 jquery-rails (2.1.3)
@@ -233,7 +213,7 @@ GEM @@ -233,7 +213,7 @@ GEM
233 kaminari (0.14.1) 213 kaminari (0.14.1)
234 actionpack (>= 3.0.0) 214 actionpack (>= 3.0.0)
235 activesupport (>= 3.0.0) 215 activesupport (>= 3.0.0)
236 - kgio (2.7.4) 216 + kgio (2.8.0)
237 launchy (2.1.2) 217 launchy (2.1.2)
238 addressable (~> 2.3) 218 addressable (~> 2.3)
239 letter_opener (1.0.0) 219 letter_opener (1.0.0)
@@ -250,22 +230,22 @@ GEM @@ -250,22 +230,22 @@ GEM
250 modernizr (2.6.2) 230 modernizr (2.6.2)
251 sprockets (~> 2.0) 231 sprockets (~> 2.0)
252 multi_json (1.6.1) 232 multi_json (1.6.1)
253 - multi_xml (0.5.1)  
254 - multipart-post (1.1.5) 233 + multi_xml (0.5.3)
  234 + multipart-post (1.2.0)
255 mysql2 (0.3.11) 235 mysql2 (0.3.11)
256 net-ldap (0.2.2) 236 net-ldap (0.2.2)
257 nokogiri (1.5.6) 237 nokogiri (1.5.6)
258 oauth (0.4.7) 238 oauth (0.4.7)
259 - oauth2 (0.8.0) 239 + oauth2 (0.8.1)
260 faraday (~> 0.8) 240 faraday (~> 0.8)
261 httpauth (~> 0.1) 241 httpauth (~> 0.1)
262 jwt (~> 0.1.4) 242 jwt (~> 0.1.4)
263 multi_json (~> 1.0) 243 multi_json (~> 1.0)
264 rack (~> 1.2) 244 rack (~> 1.2)
265 - omniauth (1.1.1) 245 + omniauth (1.1.3)
266 hashie (~> 1.2) 246 hashie (~> 1.2)
267 rack 247 rack
268 - omniauth-github (1.0.3) 248 + omniauth-github (1.1.0)
269 omniauth (~> 1.0) 249 omniauth (~> 1.0)
270 omniauth-oauth2 (~> 1.1) 250 omniauth-oauth2 (~> 1.1)
271 omniauth-google-oauth2 (0.1.13) 251 omniauth-google-oauth2 (0.1.13)
@@ -293,6 +273,9 @@ GEM @@ -293,6 +273,9 @@ GEM
293 coderay (~> 1.0.5) 273 coderay (~> 1.0.5)
294 method_source (~> 0.8) 274 method_source (~> 0.8)
295 slop (~> 3.3.1) 275 slop (~> 3.3.1)
  276 + pygments.rb (0.4.2)
  277 + posix-spawn (~> 0.3.6)
  278 + yajl-ruby (~> 1.1.0)
296 pyu-ruby-sasl (0.0.3.3) 279 pyu-ruby-sasl (0.0.3.3)
297 quiet_assets (1.0.1) 280 quiet_assets (1.0.1)
298 railties (~> 3.1) 281 railties (~> 3.1)
@@ -305,7 +288,7 @@ GEM @@ -305,7 +288,7 @@ GEM
305 rack (>= 1.1.3) 288 rack (>= 1.1.3)
306 rack-mount (0.8.3) 289 rack-mount (0.8.3)
307 rack (>= 1.0.0) 290 rack (>= 1.0.0)
308 - rack-protection (1.3.2) 291 + rack-protection (1.4.0)
309 rack 292 rack
310 rack-ssl (1.3.3) 293 rack-ssl (1.3.3)
311 rack 294 rack
@@ -342,12 +325,13 @@ GEM @@ -342,12 +325,13 @@ GEM
342 rb-fsevent (0.9.2) 325 rb-fsevent (0.9.2)
343 rb-inotify (0.8.8) 326 rb-inotify (0.8.8)
344 ffi (>= 0.5.0) 327 ffi (>= 0.5.0)
345 - rdoc (3.12.1) 328 + rdoc (3.12.2)
346 json (~> 1.4) 329 json (~> 1.4)
347 redcarpet (2.2.2) 330 redcarpet (2.2.2)
348 redis (3.0.2) 331 redis (3.0.2)
349 redis-namespace (1.2.1) 332 redis-namespace (1.2.1)
350 redis (~> 3.0.0) 333 redis (~> 3.0.0)
  334 + request_store (1.0.5)
351 rspec (2.12.0) 335 rspec (2.12.0)
352 rspec-core (~> 2.12.0) 336 rspec-core (~> 2.12.0)
353 rspec-expectations (~> 2.12.0) 337 rspec-expectations (~> 2.12.0)
@@ -381,11 +365,11 @@ GEM @@ -381,11 +365,11 @@ GEM
381 multi_json (~> 1.0) 365 multi_json (~> 1.0)
382 rubyzip 366 rubyzip
383 websocket (~> 1.0.4) 367 websocket (~> 1.0.4)
384 - settingslogic (2.0.8) 368 + settingslogic (2.0.9)
385 sexp_processor (4.1.3) 369 sexp_processor (4.1.3)
386 shoulda-matchers (1.3.0) 370 shoulda-matchers (1.3.0)
387 activesupport (>= 3.0.0) 371 activesupport (>= 3.0.0)
388 - sidekiq (2.7.3) 372 + sidekiq (2.7.5)
389 celluloid (~> 0.12.0) 373 celluloid (~> 0.12.0)
390 connection_pool (~> 1.0) 374 connection_pool (~> 1.0)
391 multi_json (~> 1) 375 multi_json (~> 1)
@@ -395,9 +379,9 @@ GEM @@ -395,9 +379,9 @@ GEM
395 multi_json (~> 1.0) 379 multi_json (~> 1.0)
396 simplecov-html (~> 0.7.1) 380 simplecov-html (~> 0.7.1)
397 simplecov-html (0.7.1) 381 simplecov-html (0.7.1)
398 - sinatra (1.3.3)  
399 - rack (~> 1.3, >= 1.3.6)  
400 - rack-protection (~> 1.2) 382 + sinatra (1.3.5)
  383 + rack (~> 1.4)
  384 + rack-protection (~> 1.3)
401 tilt (~> 1.3, >= 1.3.3) 385 tilt (~> 1.3, >= 1.3.3)
402 six (0.2.0) 386 six (0.2.0)
403 slim (1.3.6) 387 slim (1.3.6)
@@ -416,7 +400,7 @@ GEM @@ -416,7 +400,7 @@ GEM
416 multi_json (~> 1.0) 400 multi_json (~> 1.0)
417 rack (~> 1.0) 401 rack (~> 1.0)
418 tilt (~> 1.1, != 1.3.0) 402 tilt (~> 1.1, != 1.3.0)
419 - stamp (0.3.0) 403 + stamp (0.5.0)
420 state_machine (1.1.2) 404 state_machine (1.1.2)
421 temple (0.5.5) 405 temple (0.5.5)
422 test_after_commit (0.0.1) 406 test_after_commit (0.0.1)
@@ -427,7 +411,7 @@ GEM @@ -427,7 +411,7 @@ GEM
427 eventmachine (>= 0.12.6) 411 eventmachine (>= 0.12.6)
428 rack (>= 1.0.0) 412 rack (>= 1.0.0)
429 thor (0.17.0) 413 thor (0.17.0)
430 - tilt (1.3.3) 414 + tilt (1.3.4)
431 timers (1.1.0) 415 timers (1.1.0)
432 treetop (1.4.12) 416 treetop (1.4.12)
433 polyglot 417 polyglot
@@ -436,12 +420,13 @@ GEM @@ -436,12 +420,13 @@ GEM
436 uglifier (1.3.0) 420 uglifier (1.3.0)
437 execjs (>= 0.3.0) 421 execjs (>= 0.3.0)
438 multi_json (~> 1.0, >= 1.0.2) 422 multi_json (~> 1.0, >= 1.0.2)
439 - unicorn (4.4.0) 423 + unicorn (4.6.2)
440 kgio (~> 2.6) 424 kgio (~> 2.6)
441 rack 425 rack
442 raindrops (~> 0.7) 426 raindrops (~> 0.7)
443 - virtus (0.5.2) 427 + virtus (0.5.4)
444 backports (~> 2.6.1) 428 backports (~> 2.6.1)
  429 + descendants_tracker (~> 0.0.1)
445 warden (1.2.1) 430 warden (1.2.1)
446 rack (>= 1.0) 431 rack (>= 1.0)
447 webmock (1.9.0) 432 webmock (1.9.0)
@@ -463,14 +448,15 @@ DEPENDENCIES @@ -463,14 +448,15 @@ DEPENDENCIES
463 binding_of_caller 448 binding_of_caller
464 bootstrap-sass (= 2.2.1.1) 449 bootstrap-sass (= 2.2.1.1)
465 capybara (= 2.0.2) 450 capybara (= 2.0.2)
466 - carrierwave (~> 0.7.1) 451 + carrierwave
467 chosen-rails (= 0.9.8) 452 chosen-rails (= 0.9.8)
468 coffee-rails (~> 3.2.2) 453 coffee-rails (~> 3.2.2)
469 colored 454 colored
470 - database_cleaner!  
471 - devise (~> 2.1.0)  
472 - draper (~> 0.18.0) 455 + database_cleaner
  456 + devise
  457 + draper
473 email_spec 458 email_spec
  459 + enumerize
474 factory_girl_rails 460 factory_girl_rails
475 ffaker 461 ffaker
476 font-awesome-sass-rails (~> 3.0.0) 462 font-awesome-sass-rails (~> 3.0.0)
@@ -479,18 +465,20 @@ DEPENDENCIES @@ -479,18 +465,20 @@ DEPENDENCIES
479 git 465 git
480 github-linguist (~> 2.3.4) 466 github-linguist (~> 2.3.4)
481 github-markup (~> 0.7.4) 467 github-markup (~> 0.7.4)
  468 + gitlab-grack (~> 1.0.0)
  469 + gitlab-grit (~> 1.0.0)
  470 + gitlab-pygments.rb (~> 0.3.2)
482 gitlab_meta (= 5.0) 471 gitlab_meta (= 5.0)
483 gitlab_omniauth-ldap (= 1.0.2) 472 gitlab_omniauth-ldap (= 1.0.2)
484 gitlab_yaml_db (= 1.0.0) 473 gitlab_yaml_db (= 1.0.0)
485 - grack! 474 + gon
486 grape (~> 0.3.1) 475 grape (~> 0.3.1)
487 grape-entity (~> 0.2.0) 476 grape-entity (~> 0.2.0)
488 - grit!  
489 - grit_ext! 477 + grit_ext (~> 0.6.2)
490 growl 478 growl
491 guard-rspec 479 guard-rspec
492 guard-spinach 480 guard-spinach
493 - haml-rails (~> 0.3.5) 481 + haml-rails
494 httparty 482 httparty
495 jquery-atwho-rails (= 0.1.7) 483 jquery-atwho-rails (= 0.1.7)
496 jquery-rails (= 2.1.3) 484 jquery-rails (= 2.1.3)
@@ -500,14 +488,13 @@ DEPENDENCIES @@ -500,14 +488,13 @@ DEPENDENCIES
500 letter_opener 488 letter_opener
501 modernizr (= 2.6.2) 489 modernizr (= 2.6.2)
502 mysql2 490 mysql2
503 - omniauth (~> 1.1.1) 491 + omniauth (~> 1.1.3)
504 omniauth-github 492 omniauth-github
505 omniauth-google-oauth2 493 omniauth-google-oauth2
506 omniauth-twitter 494 omniauth-twitter
507 pg 495 pg
508 poltergeist (= 1.1.0) 496 poltergeist (= 1.1.0)
509 pry 497 pry
510 - pygments.rb!  
511 quiet_assets (~> 1.0.1) 498 quiet_assets (~> 1.0.1)
512 rack-mini-profiler 499 rack-mini-profiler
513 rails (= 3.2.12) 500 rails (= 3.2.12)
@@ -523,7 +510,7 @@ DEPENDENCIES @@ -523,7 +510,7 @@ DEPENDENCIES
523 seed-fu 510 seed-fu
524 settingslogic 511 settingslogic
525 shoulda-matchers (= 1.3.0) 512 shoulda-matchers (= 1.3.0)
526 - sidekiq (= 2.7.3) 513 + sidekiq
527 simplecov 514 simplecov
528 sinatra 515 sinatra
529 six 516 six
@@ -535,5 +522,5 @@ DEPENDENCIES @@ -535,5 +522,5 @@ DEPENDENCIES
535 therubyracer 522 therubyracer
536 thin 523 thin
537 uglifier (~> 1.3.0) 524 uglifier (~> 1.3.0)
538 - unicorn (~> 4.4.0) 525 + unicorn
539 webmock 526 webmock
1 -# Welcome to GitLab! Self hosted Git management software 1 +## GitLab: self hosted Git management software
2 2
  3 +![logo](https://raw.github.com/gitlabhq/gitlabhq/master/public/gitlab_logo.png)
3 4
4 -## Badges: 5 +### GitLab allows you to
  6 + * keep your code secure on your own server
  7 + * manage repositories, users and access permissions
  8 + * communicate though issues, line-comments and wiki's
  9 + * perform code reviews with merge requests
  10 +
  11 +### GitLab is
  12 +
  13 +* powered by Ruby on Rails
  14 +* completely free and open source (MIT license)
  15 +* used by 10.000 organization to keep their code secure
  16 +
  17 +### Code status
  18 +
  19 +* [![build status](http://ci.gitlab.org/projects/1/status?ref=master)](http://ci.gitlab.org/projects/1?ref=master) ci.gitlab.org (master branch)
  20 +
  21 +* [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) travis-ci.org (master branch)
  22 +
  23 +* [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
5 24
6 -* master: travis-ci.org [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq)a  
7 -* master: ci.gitlab.org [![CI](http://ci.gitlab.org/projects/1/status?ref=master)](http://ci.gitlab.org/projects/1?ref=master)  
8 -* [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)  
9 * [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq) 25 * [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq)
10 26
11 -GitLab is a free project and repository management application 27 +### Resources
12 28
  29 +* GitLab.org community site: [Homepage](http://gitlab.org) [Screenshots](http://gitlab.org/screenshots/) [Blog](http://blog.gitlab.org/) [Demo](http://demo.gitlabhq.com/users/sign_in)
13 30
14 -## Application details 31 +* GitLab.com: [Homepage](http://blog.gitlab.com/) [Hosted pricing](http://blog.gitlab.com/pricing/) [Services](http://blog.gitlab.com/services/) [Blog](http://blog.gitlab.com/blog/)
15 32
16 -* powered by Ruby on Rails  
17 -* its completely free and open source  
18 -* distributed under the MIT License 33 +* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server
19 34
20 -## Requirements 35 +### Requirements
21 36
22 -* Ubuntu/Debian 37 +* Ubuntu/Debian*
23 * ruby 1.9.3+ 38 * ruby 1.9.3+
24 * MySQL 39 * MySQL
25 * git 40 * git
26 * gitlab-shell 41 * gitlab-shell
27 * redis 42 * redis
28 43
29 -## Install 44 +* More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md)
  45 +
  46 +### Installation
  47 +
  48 +You can either follow the "ordinary" Installation guide to install it on a machine or use the Vagrant virtual machine. The Installation guide is recommended to set up a production server. The Vargrant virtual machine is recommended for development since it makes it much easier to set up all the dependencies for integration testing.
  49 +
  50 +* [Installation guide for latest stable release](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md)
  51 +
  52 +* [Installation guide for the current master branch](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
  53 +
  54 +* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm)
  55 +
  56 +### Starting
  57 +
  58 +1. The Installation guide contains instructions to download an init script and run that on boot. With the init script you can also start GitLab with:
  59 +
  60 + sudo service gitlab start
  61 +
  62 + or
  63 +
  64 + sudo /etc/init.d/gitlab restart
  65 +
  66 +2. Start it with [Foreman](https://github.com/ddollar/foreman) in development model
  67 +
  68 + bundle exec foreman start -p 3000
  69 +
  70 +3. Start it manually in development mode
  71 +
  72 + bundle exec rails s
  73 + bundle exec rake sidekiq:start
  74 +
  75 +### Running the tests
  76 +
  77 +* Seed the database with
  78 +
  79 + bundle exec rake db:setup RAILS_ENV=test
  80 + bundle exec rake db:seed_fu RAILS_ENV=test
  81 +
  82 +* Run all tests
  83 +
  84 + bundle exec rake gitlab:test
  85 +
  86 +* Rspec unit and functional tests
  87 +
  88 + bundle exec rake spec
  89 +
  90 +* Spinach integration tests
  91 +
  92 + bundle exec rake spinach
  93 +
  94 +### Getting help
  95 +
  96 +* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide)
  97 +
  98 +* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq)
  99 +
  100 +* [Feedback and suggestions forum](http://gitlab.uservoice.com/forums/176466-general)
  101 +
  102 +* [Paid support](http://blog.gitlab.com/support/)
  103 +
  104 +* [Paid services](http://blog.gitlab.com/services/)
  105 +
  106 +### New versions and the API
  107 +
  108 +Each month on the 22th a new version is released together with an upgrade guide.
  109 +
  110 +* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki)
  111 +
  112 +* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md)
  113 +
  114 +### Other documentation
  115 +
  116 +* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md)
  117 +
  118 +* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks)
  119 +
  120 +* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes)
  121 +
  122 +### Getting in touch
30 123
31 -Checkout [wiki](https://github.com/gitlabhq/gitlabhq/wiki) pages for installation information, migration, etc. 124 +* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md)
32 125
33 -## [Community](http://gitlab.org/community/) 126 +* [Core team](https://github.com/gitlabhq?tab=members)
34 127
35 -## [Contact](http://gitlab.org/contact/) 128 +* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)
36 129
37 -## Contribute 130 +* [Leader](https://github.com/randx)
38 131
39 -[Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide)  
40 -Want to help - send a pull request.  
41 -We'll accept good pull requests. 132 +* [Contact page](http://gitlab.org/contact/)
@@ -4,9 +4,4 @@ @@ -4,9 +4,4 @@
4 4
5 * Replace gitolite with gitlab-shell 5 * Replace gitolite with gitlab-shell
6 * Usability improvements 6 * Usability improvements
7 -* Notification improvements  
8 -  
9 -### v4.2 February 22  
10 -  
11 -* Teams  
12 - 7 +* Notification improvements
13 \ No newline at end of file 8 \ No newline at end of file
app/assets/fonts/OFL.txt
@@ -1,92 +0,0 @@ @@ -1,92 +0,0 @@
1 -Copyright (c) 2010, Jan Gerner (post@yanone.de)  
2 -This Font Software is licensed under the SIL Open Font License, Version 1.1.  
3 -This license is copied below, and is also available with a FAQ at:  
4 -http://scripts.sil.org/OFL  
5 -  
6 -  
7 ------------------------------------------------------------  
8 -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007  
9 ------------------------------------------------------------  
10 -  
11 -PREAMBLE  
12 -The goals of the Open Font License (OFL) are to stimulate worldwide  
13 -development of collaborative font projects, to support the font creation  
14 -efforts of academic and linguistic communities, and to provide a free and  
15 -open framework in which fonts may be shared and improved in partnership  
16 -with others.  
17 -  
18 -The OFL allows the licensed fonts to be used, studied, modified and  
19 -redistributed freely as long as they are not sold by themselves. The  
20 -fonts, including any derivative works, can be bundled, embedded,  
21 -redistributed and/or sold with any software provided that any reserved  
22 -names are not used by derivative works. The fonts and derivatives,  
23 -however, cannot be released under any other type of license. The  
24 -requirement for fonts to remain under this license does not apply  
25 -to any document created using the fonts or their derivatives.  
26 -  
27 -DEFINITIONS  
28 -"Font Software" refers to the set of files released by the Copyright  
29 -Holder(s) under this license and clearly marked as such. This may  
30 -include source files, build scripts and documentation.  
31 -  
32 -"Reserved Font Name" refers to any names specified as such after the  
33 -copyright statement(s).  
34 -  
35 -"Original Version" refers to the collection of Font Software components as  
36 -distributed by the Copyright Holder(s).  
37 -  
38 -"Modified Version" refers to any derivative made by adding to, deleting,  
39 -or substituting -- in part or in whole -- any of the components of the  
40 -Original Version, by changing formats or by porting the Font Software to a  
41 -new environment.  
42 -  
43 -"Author" refers to any designer, engineer, programmer, technical  
44 -writer or other person who contributed to the Font Software.  
45 -  
46 -PERMISSION & CONDITIONS  
47 -Permission is hereby granted, free of charge, to any person obtaining  
48 -a copy of the Font Software, to use, study, copy, merge, embed, modify,  
49 -redistribute, and sell modified and unmodified copies of the Font  
50 -Software, subject to the following conditions:  
51 -  
52 -1) Neither the Font Software nor any of its individual components,  
53 -in Original or Modified Versions, may be sold by itself.  
54 -  
55 -2) Original or Modified Versions of the Font Software may be bundled,  
56 -redistributed and/or sold with any software, provided that each copy  
57 -contains the above copyright notice and this license. These can be  
58 -included either as stand-alone text files, human-readable headers or  
59 -in the appropriate machine-readable metadata fields within text or  
60 -binary files as long as those fields can be easily viewed by the user.  
61 -  
62 -3) No Modified Version of the Font Software may use the Reserved Font  
63 -Name(s) unless explicit written permission is granted by the corresponding  
64 -Copyright Holder. This restriction only applies to the primary font name as  
65 -presented to the users.  
66 -  
67 -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font  
68 -Software shall not be used to promote, endorse or advertise any  
69 -Modified Version, except to acknowledge the contribution(s) of the  
70 -Copyright Holder(s) and the Author(s) or with their explicit written  
71 -permission.  
72 -  
73 -5) The Font Software, modified or unmodified, in part or in whole,  
74 -must be distributed entirely under this license, and must not be  
75 -distributed under any other license. The requirement for fonts to  
76 -remain under this license does not apply to any document created  
77 -using the Font Software.  
78 -  
79 -TERMINATION  
80 -This license becomes null and void if any of the above conditions are  
81 -not met.  
82 -  
83 -DISCLAIMER  
84 -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,  
85 -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF  
86 -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT  
87 -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE  
88 -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,  
89 -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL  
90 -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  
91 -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM  
92 -OTHER DEALINGS IN THE FONT SOFTWARE.  
app/assets/fonts/YanoneKaffeesatz-Light.ttf
No preview for this file type
app/assets/javascripts/branch-graph.js 0 → 100644
@@ -0,0 +1,400 @@ @@ -0,0 +1,400 @@
  1 +!function(){
  2 +
  3 + var BranchGraph = function(element, options){
  4 + this.element = element;
  5 + this.options = options;
  6 +
  7 + this.preparedCommits = {};
  8 + this.mtime = 0;
  9 + this.mspace = 0;
  10 + this.parents = {};
  11 + this.colors = ["#000"];
  12 +
  13 + this.load();
  14 + };
  15 +
  16 + BranchGraph.prototype.load = function(){
  17 + $.ajax({
  18 + url: this.options.url,
  19 + method: 'get',
  20 + dataType: 'json',
  21 + success: $.proxy(function(data){
  22 + $('.loading', this.element).hide();
  23 + this.prepareData(data.days, data.commits);
  24 + this.buildGraph();
  25 + }, this)
  26 + });
  27 + };
  28 +
  29 + BranchGraph.prototype.prepareData = function(days, commits){
  30 + this.days = days;
  31 + this.dayCount = days.length;
  32 + this.commits = commits;
  33 + this.commitCount = commits.length;
  34 +
  35 + this.collectParents();
  36 +
  37 + this.mtime += 4;
  38 + this.mspace += 10;
  39 + for (var i = 0; i < this.commitCount; i++) {
  40 + if (this.commits[i].id in this.parents) {
  41 + this.commits[i].isParent = true;
  42 + }
  43 + this.preparedCommits[this.commits[i].id] = this.commits[i];
  44 + }
  45 + this.collectColors();
  46 + };
  47 +
  48 + BranchGraph.prototype.collectParents = function(){
  49 + for (var i = 0; i < this.commitCount; i++) {
  50 + for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
  51 + this.parents[this.commits[i].parents[j][0]] = true;
  52 + }
  53 + this.mtime = Math.max(this.mtime, this.commits[i].time);
  54 + this.mspace = Math.max(this.mspace, this.commits[i].space);
  55 + }
  56 + };
  57 +
  58 + BranchGraph.prototype.collectColors = function(){
  59 + for (var k = 0; k < this.mspace; k++) {
  60 + this.colors.push(Raphael.getColor(.8));
  61 + // Skipping a few colors in the spectrum to get more contrast between colors
  62 + Raphael.getColor();Raphael.getColor();
  63 + }
  64 + };
  65 +
  66 + BranchGraph.prototype.buildGraph = function(){
  67 + var graphWidth = $(this.element).width()
  68 + , ch = this.mspace * 20 + 100
  69 + , cw = Math.max(graphWidth, this.mtime * 20 + 260)
  70 + , r = Raphael(this.element.get(0), cw, ch)
  71 + , top = r.set()
  72 + , cuday = 0
  73 + , cumonth = ""
  74 + , offsetX = 20
  75 + , offsetY = 60
  76 + , barWidth = Math.max(graphWidth, this.dayCount * 20 + 320)
  77 + , scrollLeft = cw;
  78 +
  79 + this.raphael = r;
  80 +
  81 + r.rect(0, 0, barWidth, 20).attr({fill: "#222"});
  82 + r.rect(0, 20, barWidth, 20).attr({fill: "#444"});
  83 +
  84 + for (mm = 0; mm < this.dayCount; mm++) {
  85 + if(this.days[mm] != null){
  86 + if(cuday != this.days[mm][0]){
  87 + // Dates
  88 + r.text(offsetX + mm * 20, 31, this.days[mm][0]).attr({
  89 + font: "12px Monaco, monospace",
  90 + fill: "#DDD"
  91 + });
  92 + cuday = this.days[mm][0];
  93 + }
  94 + if(cumonth != this.days[mm][1]){
  95 + // Months
  96 + r.text(offsetX + mm * 20, 11, this.days[mm][1]).attr({
  97 + font: "12px Monaco, monospace",
  98 + fill: "#EEE"
  99 + });
  100 + cumonth = this.days[mm][1];
  101 + }
  102 + }
  103 + }
  104 +
  105 + for (i = 0; i < this.commitCount; i++) {
  106 + var x = offsetX + 20 * this.commits[i].time
  107 + , y = offsetY + 10 * this.commits[i].space
  108 + , c
  109 + , ps;
  110 +
  111 + // Draw dot
  112 + r.circle(x, y, 3).attr({
  113 + fill: this.colors[this.commits[i].space],
  114 + stroke: "none"
  115 + });
  116 +
  117 + // Draw lines
  118 + for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
  119 + c = this.preparedCommits[this.commits[i].parents[j][0]];
  120 + ps = this.commits[i].parent_spaces[j];
  121 + if (c) {
  122 + var cx = offsetX + 20 * c.time
  123 + , cy = offsetY + 10 * c.space
  124 + , psy = offsetY + 10 * ps;
  125 + if (c.space == this.commits[i].space && c.space == ps) {
  126 + r.path([
  127 + "M", x, y,
  128 + "L", cx, cy
  129 + ]).attr({
  130 + stroke: this.colors[c.space],
  131 + "stroke-width": 2
  132 + });
  133 +
  134 + } else if (c.space < this.commits[i].space) {
  135 + if (y == psy) {
  136 + r.path([
  137 + "M", x - 5, y,
  138 + "l-5,-2,0,4,5,-2",
  139 + "L", x - 10, y,
  140 + "L", x - 15, psy,
  141 + "L", cx + 5, psy,
  142 + "L", cx, cy])
  143 + .attr({
  144 + stroke: this.colors[this.commits[i].space],
  145 + "stroke-width": 2
  146 + });
  147 + } else {
  148 + r.path([
  149 + "M", x - 3, y - 6,
  150 + "l-4,-3,4,-2,0,5",
  151 + "L", x - 5, y - 10,
  152 + "L", x - 10, psy,
  153 + "L", cx + 5, psy,
  154 + "L", cx, cy])
  155 + .attr({
  156 + stroke: this.colors[this.commits[i].space],
  157 + "stroke-width": 2
  158 + });
  159 + }
  160 + } else {
  161 + r.path([
  162 + "M", x - 3, y + 6,
  163 + "l-4,3,4,2,0,-5",
  164 + "L", x - 5, y + 10,
  165 + "L", x - 10, psy,
  166 + "L", cx + 5, psy,
  167 + "L", cx, cy])
  168 + .attr({
  169 + stroke: this.colors[c.space],
  170 + "stroke-width": 2
  171 + });
  172 + }
  173 + }
  174 + }
  175 +
  176 + if (this.commits[i].refs) {
  177 + this.appendLabel(x, y, this.commits[i].refs);
  178 + }
  179 +
  180 + // mark commit and displayed in the center
  181 + if (this.commits[i].id == this.options.commit_id) {
  182 + r.path([
  183 + 'M', x, y - 5,
  184 + 'L', x + 4, y - 15,
  185 + 'L', x - 4, y - 15,
  186 + 'Z'
  187 + ]).attr({
  188 + "fill": "#000",
  189 + "fill-opacity": .7,
  190 + "stroke": "none"
  191 + });
  192 + scrollLeft = x - graphWidth / 2;
  193 + }
  194 +
  195 + this.appendAnchor(top, this.commits[i], x, y);
  196 + }
  197 + top.toFront();
  198 + this.element.scrollLeft(scrollLeft);
  199 + this.bindEvents();
  200 + };
  201 +
  202 + BranchGraph.prototype.bindEvents = function(){
  203 + var drag = {}
  204 + , element = this.element;
  205 +
  206 + var dragger = function(event){
  207 + element.scrollLeft(drag.sl - (event.clientX - drag.x));
  208 + element.scrollTop(drag.st - (event.clientY - drag.y));
  209 + };
  210 +
  211 + element.on({
  212 + mousedown: function (event) {
  213 + drag = {
  214 + x: event.clientX,
  215 + y: event.clientY,
  216 + st: element.scrollTop(),
  217 + sl: element.scrollLeft()
  218 + };
  219 + $(window).on('mousemove', dragger);
  220 + }
  221 + });
  222 + $(window).on({
  223 + mouseup: function(){
  224 + //bars.animate({opacity: 0}, 300);
  225 + $(window).off('mousemove', dragger);
  226 + },
  227 + keydown: function(event){
  228 + if(event.keyCode == 37){
  229 + // left
  230 + element.scrollLeft( element.scrollLeft() - 50);
  231 + }
  232 + if(event.keyCode == 38){
  233 + // top
  234 + element.scrollTop( element.scrollTop() - 50);
  235 + }
  236 + if(event.keyCode == 39){
  237 + // right
  238 + element.scrollLeft( element.scrollLeft() + 50);
  239 + }
  240 + if(event.keyCode == 40){
  241 + // bottom
  242 + element.scrollTop( element.scrollTop() + 50);
  243 + }
  244 + }
  245 + });
  246 + };
  247 +
  248 + BranchGraph.prototype.appendLabel = function(x, y, refs){
  249 + var r = this.raphael
  250 + , shortrefs = refs
  251 + , text, textbox, rect;
  252 +
  253 + if (shortrefs.length > 17){
  254 + // Truncate if longer than 15 chars
  255 + shortrefs = shortrefs.substr(0,15) + "…";
  256 + }
  257 +
  258 + text = r.text(x+5, y+8 + 10, shortrefs).attr({
  259 + font: "10px Monaco, monospace",
  260 + fill: "#FFF",
  261 + title: refs
  262 + });
  263 +
  264 + textbox = text.getBBox();
  265 + text.transform([
  266 + 't', textbox.height/-4, textbox.width/2 + 5,
  267 + 'r90'
  268 + ]);
  269 +
  270 + // Create rectangle based on the size of the textbox
  271 + rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr({
  272 + "fill": "#000",
  273 + "fill-opacity": .7,
  274 + "stroke": "none"
  275 + });
  276 +
  277 + triangle = r.path([
  278 + 'M', x, y + 5,
  279 + 'L', x + 4, y + 15,
  280 + 'L', x - 4, y + 15,
  281 + 'Z'
  282 + ]).attr({
  283 + "fill": "#000",
  284 + "fill-opacity": .7,
  285 + "stroke": "none"
  286 + });
  287 +
  288 + // Rotate and reposition rectangle over text
  289 + rect.transform([
  290 + 'r', 90, x, y,
  291 + 't', 15, -9
  292 + ]);
  293 +
  294 + // Set text to front
  295 + text.toFront();
  296 + };
  297 +
  298 + BranchGraph.prototype.appendAnchor = function(top, commit, x, y) {
  299 + var r = this.raphael
  300 + , options = this.options
  301 + , anchor;
  302 + anchor = r.circle(x, y, 10).attr({
  303 + fill: "#000",
  304 + opacity: 0,
  305 + cursor: "pointer"
  306 + })
  307 + .click(function(){
  308 + window.open(options.commit_url.replace('%s', commit.id), '_blank');
  309 + })
  310 + .hover(function(){
  311 + this.tooltip = r.commitTooltip(x, y + 5, commit);
  312 + top.push(this.tooltip.insertBefore(this));
  313 + }, function(){
  314 + this.tooltip && this.tooltip.remove() && delete this.tooltip;
  315 + });
  316 + top.push(anchor);
  317 + };
  318 +
  319 + this.BranchGraph = BranchGraph;
  320 +
  321 +}(this);
  322 +Raphael.fn.commitTooltip = function(x, y, commit){
  323 + var icon, nameText, idText, messageText
  324 + , boxWidth = 300
  325 + , boxHeight = 200;
  326 +
  327 + icon = this.image(commit.author.icon, x, y, 20, 20);
  328 + nameText = this.text(x + 25, y + 10, commit.author.name);
  329 + idText = this.text(x, y + 35, commit.id);
  330 + messageText = this.text(x, y + 50, commit.message);
  331 +
  332 + textSet = this.set(icon, nameText, idText, messageText).attr({
  333 + "text-anchor": "start",
  334 + "font": "12px Monaco, monospace"
  335 + });
  336 +
  337 + nameText.attr({
  338 + "font": "14px Arial",
  339 + "font-weight": "bold"
  340 + });
  341 +
  342 + idText.attr({
  343 + "fill": "#AAA"
  344 + });
  345 +
  346 + textWrap(messageText, boxWidth - 50);
  347 +
  348 + var rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({
  349 + "fill": "#FFF",
  350 + "stroke": "#000",
  351 + "stroke-linecap": "round",
  352 + "stroke-width": 2
  353 + });
  354 + var tooltip = this.set(rect, textSet);
  355 +
  356 + rect.attr({
  357 + "height" : tooltip.getBBox().height + 10,
  358 + "width" : tooltip.getBBox().width + 10
  359 + });
  360 +
  361 + tooltip.transform([
  362 + 't', 20, 20
  363 + ]);
  364 +
  365 + return tooltip;
  366 +};
  367 +
  368 +function textWrap(t, width) {
  369 + var content = t.attr("text");
  370 + var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  371 + t.attr({
  372 + "text" : abc
  373 + });
  374 + var letterWidth = t.getBBox().width / abc.length;
  375 +
  376 + t.attr({
  377 + "text" : content
  378 + });
  379 +
  380 + var words = content.split(" ");
  381 + var x = 0, s = [];
  382 + for ( var i = 0; i < words.length; i++) {
  383 +
  384 + var l = words[i].length;
  385 + if (x + (l * letterWidth) > width) {
  386 + s.push("\n");
  387 + x = 0;
  388 + }
  389 + x += l * letterWidth;
  390 + s.push(words[i] + " ");
  391 + }
  392 + t.attr({
  393 + "text" : s.join("")
  394 + });
  395 + var b = t.getBBox()
  396 + , h = Math.abs(b.y2) - Math.abs(b.y) + 1;
  397 + t.attr({
  398 + "y": b.y + h
  399 + });
  400 +}
app/assets/javascripts/main.js.coffee
@@ -54,10 +54,10 @@ $ -&gt; @@ -54,10 +54,10 @@ $ -&gt;
54 $(@).parents('form').submit() 54 $(@).parents('form').submit()
55 55
56 # Flash 56 # Flash
57 - if (flash = $("#flash-container")).length > 0  
58 - flash.click -> $(@).slideUp("slow")  
59 - flash.slideDown "slow"  
60 - setTimeout (-> flash.slideUp("slow")), 3000 57 + if (flash = $(".flash-container")).length > 0
  58 + flash.click -> $(@).fadeOut()
  59 + flash.show()
  60 + setTimeout (-> flash.fadeOut()), 3000
61 61
62 # Disable form buttons while a form is submitting 62 # Disable form buttons while a form is submitting
63 $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> 63 $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
app/assets/javascripts/projects.js.coffee
@@ -18,3 +18,18 @@ $ -&gt; @@ -18,3 +18,18 @@ $ -&gt;
18 # Ref switcher 18 # Ref switcher
19 $('.project-refs-select').on 'change', -> 19 $('.project-refs-select').on 'change', ->
20 $(@).parents('form').submit() 20 $(@).parents('form').submit()
  21 +
  22 + $('#project_issues_enabled').change ->
  23 + if ($(this).is(':checked') == true)
  24 + $('#project_issues_tracker').removeAttr('disabled')
  25 + else
  26 + $('#project_issues_tracker').attr('disabled', 'disabled')
  27 +
  28 + $('#project_issues_tracker').change()
  29 +
  30 + $('#project_issues_tracker').change ->
  31 + if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
  32 + $('#project_issues_tracker_id').attr('disabled', 'disabled')
  33 + else
  34 + $('#project_issues_tracker_id').removeAttr('disabled')
  35 +
app/assets/stylesheets/common.scss
@@ -67,27 +67,17 @@ table a code { @@ -67,27 +67,17 @@ table a code {
67 } 67 }
68 68
69 /** FLASH message **/ 69 /** FLASH message **/
70 -#flash-container {  
71 - height: 50px;  
72 - position: fixed;  
73 - z-index: 10001;  
74 - top: 0px;  
75 - width: 100%;  
76 - margin-bottom: 15px;  
77 - overflow: hidden;  
78 - background: white;  
79 - cursor: pointer;  
80 - border-bottom: 1px solid #ccc;  
81 - text-align: center; 70 +.flash-container {
82 display: none; 71 display: none;
  72 + .alert {
  73 + cursor: pointer;
  74 + margin: 0;
  75 + text-align: center;
  76 + border-radius: 0;
83 77
84 - h4 {  
85 - color: #666;  
86 - font-size: 18px;  
87 - line-height: 38px;  
88 - padding-top: 5px;  
89 - margin: 2px;  
90 - font-weight: normal; 78 + span {
  79 + font-size: 14px;
  80 + }
91 } 81 }
92 } 82 }
93 83
@@ -203,10 +193,6 @@ input[type=text] { @@ -203,10 +193,6 @@ input[type=text] {
203 } 193 }
204 } 194 }
205 195
206 -input.git_clone_url {  
207 - width: 325px;  
208 -}  
209 -  
210 .merge-request-form-holder { 196 .merge-request-form-holder {
211 select { 197 select {
212 width: 300px; 198 width: 300px;
app/assets/stylesheets/gitlab_bootstrap/common.scss
@@ -30,6 +30,8 @@ @@ -30,6 +30,8 @@
30 border-color: #DDD; 30 border-color: #DDD;
31 } 31 }
32 32
  33 +.well { padding: 15px; }
  34 +
33 /** HELPERS **/ 35 /** HELPERS **/
34 .nothing_here_message { 36 .nothing_here_message {
35 text-align: center; 37 text-align: center;
app/assets/stylesheets/gitlab_bootstrap/fonts.scss
1 -@font-face{  
2 - font-family: Yanone;  
3 - src: font-url('YanoneKaffeesatz-Light.ttf');  
4 -}  
5 -  
6 /** Typo **/ 1 /** Typo **/
7 $monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; 2 $monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
@@ -70,8 +70,19 @@ @@ -70,8 +70,19 @@
70 @mixin header-font { 70 @mixin header-font {
71 color: $style_color; 71 color: $style_color;
72 text-shadow: 0 1px 1px #FFF; 72 text-shadow: 0 1px 1px #FFF;
73 - font-family: 'Yanone', sans-serif;  
74 - font-size: 24px;  
75 - line-height: 36px; 73 + font-size: 18px;
  74 + line-height: 42px;
76 font-weight: normal; 75 font-weight: normal;
  76 + letter-spacing: -1px;
  77 +}
  78 +
  79 +@mixin md-typography {
  80 + code { padding: 0 4px; }
  81 + p { font-size: 13px; }
  82 + h1 { font-size: 26px; line-height: 40px; margin: 10px 0;}
  83 + h2 { font-size: 22px; line-height: 40px; margin: 10px 0;}
  84 + h3 { font-size: 18px; line-height: 40px; margin: 10px 0;}
  85 + h4 { font-size: 16px; line-height: 20px; margin: 10px 0;}
  86 + h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
  87 + h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
77 } 88 }
app/assets/stylesheets/gitlab_bootstrap/typography.scss
@@ -87,16 +87,15 @@ a:focus { @@ -87,16 +87,15 @@ a:focus {
87 * 87 *
88 */ 88 */
89 .wiki { 89 .wiki {
  90 + @include md-typography;
  91 +
90 font-size: 13px; 92 font-size: 13px;
  93 + line-height: 20px;
91 94
92 - code { padding: 0 4px; }  
93 - p { font-size: 13px; }  
94 - h1 { font-size: 32px; line-height: 40px; margin: 10px 0;}  
95 - h2 { font-size: 26px; line-height: 40px; margin: 10px 0;}  
96 - h3 { font-size: 22px; line-height: 40px; margin: 10px 0;}  
97 - h4 { font-size: 18px; line-height: 20px; margin: 10px 0;}  
98 - h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}  
99 - h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}  
100 .white .highlight pre { background: #f5f5f5; } 95 .white .highlight pre { background: #f5f5f5; }
101 ul { margin: 0 0 9px 25px !important; } 96 ul { margin: 0 0 9px 25px !important; }
102 } 97 }
  98 +
  99 +.md {
  100 + @include md-typography;
  101 +}
app/assets/stylesheets/highlight/dark.scss
1 .black .highlight { 1 .black .highlight {
2 - background-color: #333;  
3 pre { 2 pre {
  3 + background-color: #333;
4 color: #eee; 4 color: #eee;
5 - background: inherit;  
6 } 5 }
7 6
8 .hll { display: block; background-color: darken($hover, 65%) } 7 .hll { display: block; background-color: darken($hover, 65%) }
app/assets/stylesheets/sections/commits.scss
@@ -100,8 +100,9 @@ @@ -100,8 +100,9 @@
100 } 100 }
101 } 101 }
102 .line_content { 102 .line_content {
  103 + display: block;
103 white-space: pre; 104 white-space: pre;
104 - height: 14px; 105 + height: 18px;
105 margin: 0px; 106 margin: 0px;
106 padding: 0px; 107 padding: 0px;
107 border: none; 108 border: none;
app/assets/stylesheets/sections/events.scss
@@ -48,15 +48,13 @@ @@ -48,15 +48,13 @@
48 color: #666; 48 color: #666;
49 } 49 }
50 .event-note { 50 .event-note {
51 - padding-top: 5px;  
52 - padding-left: 5px;  
53 - display: inline-block;  
54 color: #555; 51 color: #555;
  52 + margin-top: 5px;
  53 + margin-left: 40px;
55 54
56 .note-file-attach { 55 .note-file-attach {
57 - margin-left: -25px;  
58 - float: left;  
59 .note-image-attach { 56 .note-image-attach {
  57 + margin-top: 4px;
60 margin-left: 0px; 58 margin-left: 0px;
61 max-width: 200px; 59 max-width: 200px;
62 } 60 }
@@ -66,8 +64,8 @@ @@ -66,8 +64,8 @@
66 color: #777; 64 color: #777;
67 float: left; 65 float: left;
68 font-size: 16px; 66 font-size: 16px;
69 - line-height: 18px;  
70 - margin: 5px; 67 + line-height: 16px;
  68 + margin-right: 5px;
71 } 69 }
72 } 70 }
73 .avatar { 71 .avatar {
app/assets/stylesheets/sections/header.scss
@@ -67,7 +67,7 @@ header { @@ -67,7 +67,7 @@ header {
67 position: relative; 67 position: relative;
68 float: left; 68 float: left;
69 margin: 0; 69 margin: 0;
70 - margin-left: 15px; 70 + margin-left: 10px;
71 @include header-font; 71 @include header-font;
72 } 72 }
73 73
app/assets/stylesheets/sections/login.scss
1 /* Login Page */ 1 /* Login Page */
2 body.login-page{ 2 body.login-page{
3 - padding-top: 7%;  
4 - background: #666; 3 + background: #EEE;
  4 + .container .content { padding-top: 5%; }
5 } 5 }
6 6
7 .login-box{ 7 .login-box{
app/assets/stylesheets/sections/notes.scss
@@ -83,6 +83,7 @@ ul.notes { @@ -83,6 +83,7 @@ ul.notes {
83 margin-top: -20px; 83 margin-top: -20px;
84 } 84 }
85 .note-body { 85 .note-body {
  86 + @include md-typography;
86 margin-left: 45px; 87 margin-left: 45px;
87 } 88 }
88 .note-header { 89 .note-header {
app/assets/stylesheets/sections/projects.scss
@@ -80,6 +80,7 @@ @@ -80,6 +80,7 @@
80 border: 1px solid #BBB; 80 border: 1px solid #BBB;
81 box-shadow: none; 81 box-shadow: none;
82 margin-left: -1px; 82 margin-left: -1px;
  83 + background: #FFF;
83 } 84 }
84 } 85 }
85 86
app/contexts/issues_list_context.rb
@@ -7,12 +7,13 @@ class IssuesListContext &lt; BaseContext @@ -7,12 +7,13 @@ class IssuesListContext &lt; BaseContext
7 @issues = case params[:status] 7 @issues = case params[:status]
8 when issues_filter[:all] then @project.issues 8 when issues_filter[:all] then @project.issues
9 when issues_filter[:closed] then @project.issues.closed 9 when issues_filter[:closed] then @project.issues.closed
10 - when issues_filter[:to_me] then @project.issues.opened.assigned(current_user) 10 + when issues_filter[:to_me] then @project.issues.assigned(current_user)
  11 + when issues_filter[:by_me] then @project.issues.authored(current_user)
11 else @project.issues.opened 12 else @project.issues.opened
12 end 13 end
13 14
14 @issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present? 15 @issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present?
15 - @issues = @issues.includes(:author, :project).order("updated_at") 16 + @issues = @issues.includes(:author, :project)
16 17
17 # Filter by specific assignee_id (or lack thereof)? 18 # Filter by specific assignee_id (or lack thereof)?
18 if params[:assignee_id].present? 19 if params[:assignee_id].present?
app/controllers/admin/teams/members_controller.rb
1 class Admin::Teams::MembersController < Admin::Teams::ApplicationController 1 class Admin::Teams::MembersController < Admin::Teams::ApplicationController
2 def new 2 def new
3 @users = User.potential_team_members(user_team) 3 @users = User.potential_team_members(user_team)
4 - @users = UserDecorator.decorate @users 4 + @users = UserDecorator.decorate_collection @users
5 end 5 end
6 6
7 def create 7 def create
app/controllers/admin/users_controller.rb
@@ -45,7 +45,7 @@ class Admin::UsersController &lt; Admin::ApplicationController @@ -45,7 +45,7 @@ class Admin::UsersController &lt; Admin::ApplicationController
45 end 45 end
46 46
47 def unblock 47 def unblock
48 - if admin_user.update_attribute(:blocked, false) 48 + if admin_user.activate
49 redirect_to :back, alert: "Successfully unblocked" 49 redirect_to :back, alert: "Successfully unblocked"
50 else 50 else
51 redirect_to :back, alert: "Error occured. User was not unblocked" 51 redirect_to :back, alert: "Error occured. User was not unblocked"
app/controllers/application_controller.rb
@@ -5,6 +5,7 @@ class ApplicationController &lt; ActionController::Base @@ -5,6 +5,7 @@ class ApplicationController &lt; ActionController::Base
5 before_filter :add_abilities 5 before_filter :add_abilities
6 before_filter :dev_tools if Rails.env == 'development' 6 before_filter :dev_tools if Rails.env == 'development'
7 before_filter :default_headers 7 before_filter :default_headers
  8 + before_filter :add_gon_variables
8 9
9 protect_from_forgery 10 protect_from_forgery
10 11
@@ -29,7 +30,7 @@ class ApplicationController &lt; ActionController::Base @@ -29,7 +30,7 @@ class ApplicationController &lt; ActionController::Base
29 end 30 end
30 31
31 def reject_blocked! 32 def reject_blocked!
32 - if current_user && current_user.blocked 33 + if current_user && current_user.blocked?
33 sign_out current_user 34 sign_out current_user
34 flash[:alert] = "Your account is blocked. Retry when an admin unblock it." 35 flash[:alert] = "Your account is blocked. Retry when an admin unblock it."
35 redirect_to new_user_session_path 36 redirect_to new_user_session_path
@@ -37,7 +38,7 @@ class ApplicationController &lt; ActionController::Base @@ -37,7 +38,7 @@ class ApplicationController &lt; ActionController::Base
37 end 38 end
38 39
39 def after_sign_in_path_for resource 40 def after_sign_in_path_for resource
40 - if resource.is_a?(User) && resource.respond_to?(:blocked) && resource.blocked 41 + if resource.is_a?(User) && resource.respond_to?(:blocked?) && resource.blocked?
41 sign_out resource 42 sign_out resource
42 flash[:alert] = "Your account is blocked. Retry when an admin unblock it." 43 flash[:alert] = "Your account is blocked. Retry when an admin unblock it."
43 new_user_session_path 44 new_user_session_path
@@ -148,4 +149,8 @@ class ApplicationController &lt; ActionController::Base @@ -148,4 +149,8 @@ class ApplicationController &lt; ActionController::Base
148 headers['X-Frame-Options'] = 'DENY' 149 headers['X-Frame-Options'] = 'DENY'
149 headers['X-XSS-Protection'] = '1; mode=block' 150 headers['X-XSS-Protection'] = '1; mode=block'
150 end 151 end
  152 +
  153 + def add_gon_variables
  154 + gon.default_issues_tracker = Project.issues_tracker.default_value
  155 + end
151 end 156 end
app/controllers/commits_controller.rb
@@ -13,7 +13,7 @@ class CommitsController &lt; ProjectResourceController @@ -13,7 +13,7 @@ class CommitsController &lt; ProjectResourceController
13 @limit, @offset = (params[:limit] || 40), (params[:offset] || 0) 13 @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
14 14
15 @commits = @repo.commits(@ref, @path, @limit, @offset) 15 @commits = @repo.commits(@ref, @path, @limit, @offset)
16 - @commits = CommitDecorator.decorate(@commits) 16 + @commits = CommitDecorator.decorate_collection(@commits)
17 17
18 respond_to do |format| 18 respond_to do |format|
19 format.html # index.html.erb 19 format.html # index.html.erb
app/controllers/compare_controller.rb
@@ -16,7 +16,7 @@ class CompareController &lt; ProjectResourceController @@ -16,7 +16,7 @@ class CompareController &lt; ProjectResourceController
16 @refs_are_same = result[:same] 16 @refs_are_same = result[:same]
17 @line_notes = [] 17 @line_notes = []
18 18
19 - @commits = CommitDecorator.decorate(@commits) 19 + @commits = CommitDecorator.decorate_collection(@commits)
20 end 20 end
21 21
22 def create 22 def create
app/controllers/graph_controller.rb
1 class GraphController < ProjectResourceController 1 class GraphController < ProjectResourceController
2 include ExtractsPath 2 include ExtractsPath
  3 + include ApplicationHelper
3 4
4 # Authorize 5 # Authorize
5 before_filter :authorize_read_project! 6 before_filter :authorize_read_project!
@@ -20,7 +21,10 @@ class GraphController &lt; ProjectResourceController @@ -20,7 +21,10 @@ class GraphController &lt; ProjectResourceController
20 respond_to do |format| 21 respond_to do |format|
21 format.html 22 format.html
22 format.json do 23 format.json do
23 - graph = Gitlab::Graph::JsonBuilder.new(project, @ref, @commit) 24 + graph = Graph::JsonBuilder.new(project, @ref, @commit)
  25 + graph.commits.each do |c|
  26 + c.icon = gravatar_icon(c.author.email)
  27 + end
24 render :json => graph.to_json 28 render :json => graph.to_json
25 end 29 end
26 end 30 end
app/controllers/merge_requests_controller.rb
@@ -81,7 +81,8 @@ class MergeRequestsController &lt; ProjectResourceController @@ -81,7 +81,8 @@ class MergeRequestsController &lt; ProjectResourceController
81 end 81 end
82 82
83 def automerge 83 def automerge
84 - return access_denied! unless can?(current_user, :accept_mr, @project) 84 + return access_denied! unless allowed_to_merge?
  85 +
85 if @merge_request.opened? && @merge_request.can_be_merged? 86 if @merge_request.opened? && @merge_request.can_be_merged?
86 @merge_request.should_remove_source_branch = params[:should_remove_source_branch] 87 @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
87 @merge_request.automerge!(current_user) 88 @merge_request.automerge!(current_user)
@@ -142,6 +143,19 @@ class MergeRequestsController &lt; ProjectResourceController @@ -142,6 +143,19 @@ class MergeRequestsController &lt; ProjectResourceController
142 # Get commits from repository 143 # Get commits from repository
143 # or from cache if already merged 144 # or from cache if already merged
144 @commits = @merge_request.commits 145 @commits = @merge_request.commits
145 - @commits = CommitDecorator.decorate(@commits) 146 + @commits = CommitDecorator.decorate_collection(@commits)
  147 +
  148 + @allowed_to_merge = allowed_to_merge?
  149 + @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
  150 + end
  151 +
  152 + def allowed_to_merge?
  153 + action = if project.protected_branch?(@merge_request.target_branch)
  154 + :push_code_to_protected_branches
  155 + else
  156 + :push_code
  157 + end
  158 +
  159 + can?(current_user, action, @project)
146 end 160 end
147 end 161 end
app/controllers/milestones_controller.rb
@@ -32,7 +32,7 @@ class MilestonesController &lt; ProjectResourceController @@ -32,7 +32,7 @@ class MilestonesController &lt; ProjectResourceController
32 32
33 def show 33 def show
34 @issues = @milestone.issues 34 @issues = @milestone.issues
35 - @users = UserDecorator.decorate(@milestone.participants) 35 + @users = UserDecorator.decorate_collection(@milestone.participants)
36 @merge_requests = @milestone.merge_requests 36 @merge_requests = @milestone.merge_requests
37 37
38 respond_to do |format| 38 respond_to do |format|
app/controllers/projects_controller.rb
1 -require Rails.root.join('lib', 'gitlab', 'graph', 'json_builder')  
2 -  
3 class ProjectsController < ProjectResourceController 1 class ProjectsController < ProjectResourceController
4 skip_before_filter :project, only: [:new, :create] 2 skip_before_filter :project, only: [:new, :create]
5 skip_before_filter :repository, only: [:new, :create] 3 skip_before_filter :repository, only: [:new, :create]
app/controllers/teams/members_controller.rb
@@ -8,7 +8,7 @@ class Teams::MembersController &lt; Teams::ApplicationController @@ -8,7 +8,7 @@ class Teams::MembersController &lt; Teams::ApplicationController
8 8
9 def new 9 def new
10 @users = User.potential_team_members(user_team) 10 @users = User.potential_team_members(user_team)
11 - @users = UserDecorator.decorate @users 11 + @users = UserDecorator.decorate_collection @users
12 end 12 end
13 13
14 def create 14 def create
app/decorators/application_decorator.rb
1 -class ApplicationDecorator < Draper::Base 1 +class ApplicationDecorator < Draper::Decorator
  2 + delegate_all
2 # Lazy Helpers 3 # Lazy Helpers
3 # PRO: Call Rails helpers without the h. proxy 4 # PRO: Call Rails helpers without the h. proxy
4 # ex: number_to_currency(model.price) 5 # ex: number_to_currency(model.price)
5 # CON: Add a bazillion methods into your decorator's namespace 6 # CON: Add a bazillion methods into your decorator's namespace
6 # and probably sacrifice performance/memory 7 # and probably sacrifice performance/memory
7 - # 8 + #
8 # Enable them by uncommenting this line: 9 # Enable them by uncommenting this line:
9 # lazy_helpers 10 # lazy_helpers
10 11
11 # Shared Decorations 12 # Shared Decorations
12 # Consider defining shared methods common to all your models. 13 # Consider defining shared methods common to all your models.
13 - # 14 + #
14 # Example: standardize the formatting of timestamps 15 # Example: standardize the formatting of timestamps
15 # 16 #
16 # def formatted_timestamp(time) 17 # def formatted_timestamp(time)
17 - # h.content_tag :span, time.strftime("%a %m/%d/%y"),  
18 - # class: 'timestamp' 18 + # h.content_tag :span, time.strftime("%a %m/%d/%y"),
  19 + # class: 'timestamp'
19 # end 20 # end
20 - # 21 + #
21 # def created_at 22 # def created_at
22 # formatted_timestamp(model.created_at) 23 # formatted_timestamp(model.created_at)
23 # end 24 # end
24 - # 25 + #
25 # def updated_at 26 # def updated_at
26 # formatted_timestamp(model.updated_at) 27 # formatted_timestamp(model.updated_at)
27 # end 28 # end
app/helpers/application_helper.rb
@@ -164,7 +164,8 @@ module ApplicationHelper @@ -164,7 +164,8 @@ module ApplicationHelper
164 end 164 end
165 165
166 def image_url(source) 166 def image_url(source)
167 - root_url + path_to_image(source) 167 + # prevent relative_root_path being added twice (it's part of root_url and path_to_image)
  168 + root_url.sub(/#{root_path}$/, path_to_image(source))
168 end 169 end
169 170
170 alias_method :url_to_image, :image_url 171 alias_method :url_to_image, :image_url
app/helpers/issues_helper.rb
@@ -27,6 +27,7 @@ module IssuesHelper @@ -27,6 +27,7 @@ module IssuesHelper
27 all: "all", 27 all: "all",
28 closed: "closed", 28 closed: "closed",
29 to_me: "assigned-to-me", 29 to_me: "assigned-to-me",
  30 + by_me: "created-by-me",
30 open: "open" 31 open: "open"
31 } 32 }
32 end 33 end
@@ -40,4 +41,39 @@ module IssuesHelper @@ -40,4 +41,39 @@ module IssuesHelper
40 def issues_active_milestones 41 def issues_active_milestones
41 @project.milestones.active.order("id desc").all 42 @project.milestones.active.order("id desc").all
42 end 43 end
  44 +
  45 + def url_for_project_issues
  46 + return "" if @project.nil?
  47 +
  48 + if @project.used_default_issues_tracker?
  49 + project_issues_filter_path(@project)
  50 + else
  51 + url = Settings[:issues_tracker][@project.issues_tracker]["project_url"]
  52 + url.gsub(':project_id', @project.id.to_s)
  53 + .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
  54 + end
  55 + end
  56 +
  57 + def url_for_issue(issue_id)
  58 + return "" if @project.nil?
  59 +
  60 + if @project.used_default_issues_tracker?
  61 + url = project_issue_url project_id: @project, id: issue_id
  62 + else
  63 + url = Settings[:issues_tracker][@project.issues_tracker]["issues_url"]
  64 + url.gsub(':id', issue_id.to_s)
  65 + .gsub(':project_id', @project.id.to_s)
  66 + .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
  67 + end
  68 + end
  69 +
  70 + def title_for_issue(issue_id)
  71 + return "" if @project.nil?
  72 +
  73 + if @project.used_default_issues_tracker? && issue = @project.issues.where(id: issue_id).first
  74 + issue.title
  75 + else
  76 + ""
  77 + end
  78 + end
43 end 79 end
app/helpers/tree_helper.rb
@@ -13,13 +13,15 @@ module TreeHelper @@ -13,13 +13,15 @@ module TreeHelper
13 tree += render partial: 'tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present? 13 tree += render partial: 'tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present?
14 14
15 files.each do |f| 15 files.each do |f|
16 - if f.respond_to?(:url)  
17 - # Object is a Submodule  
18 - tree += render partial: 'tree/submodule_item', object: f  
19 - else  
20 - # Object is a Blob  
21 - tree += render partial: 'tree/tree_item', object: f, locals: {type: 'file'}  
22 - end 16 + html = if f.respond_to?(:url)
  17 + # Object is a Submodule
  18 + render partial: 'tree/submodule_item', object: f
  19 + else
  20 + # Object is a Blob
  21 + render partial: 'tree/tree_item', object: f, locals: {type: 'file'}
  22 + end
  23 +
  24 + tree += html if html.present?
23 end 25 end
24 26
25 tree.html_safe 27 tree.html_safe
app/models/ability.rb
@@ -91,7 +91,6 @@ class Ability @@ -91,7 +91,6 @@ class Ability
91 :admin_team_member, 91 :admin_team_member,
92 :admin_merge_request, 92 :admin_merge_request,
93 :admin_note, 93 :admin_note,
94 - :accept_mr,  
95 :admin_wiki, 94 :admin_wiki,
96 :admin_project 95 :admin_project
97 ] 96 ]
app/models/graph/commit.rb 0 → 100644
@@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
  1 +require "grit"
  2 +
  3 +module Graph
  4 + class Commit
  5 + include ActionView::Helpers::TagHelper
  6 +
  7 + attr_accessor :time, :spaces, :refs, :parent_spaces, :icon
  8 +
  9 + def initialize(commit)
  10 + @_commit = commit
  11 + @time = -1
  12 + @spaces = []
  13 + @parent_spaces = []
  14 + end
  15 +
  16 + def method_missing(m, *args, &block)
  17 + @_commit.send(m, *args, &block)
  18 + end
  19 +
  20 + def to_graph_hash
  21 + h = {}
  22 + h[:parents] = self.parents.collect do |p|
  23 + [p.id,0,0]
  24 + end
  25 + h[:author] = {
  26 + name: author.name,
  27 + email: author.email,
  28 + icon: icon
  29 + }
  30 + h[:time] = time
  31 + h[:space] = spaces.first
  32 + h[:parent_spaces] = parent_spaces
  33 + h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
  34 + h[:id] = sha
  35 + h[:date] = date
  36 + h[:message] = message
  37 + h
  38 + end
  39 +
  40 + def add_refs(ref_cache, repo)
  41 + if ref_cache.empty?
  42 + repo.refs.each do |ref|
  43 + ref_cache[ref.commit.id] ||= []
  44 + ref_cache[ref.commit.id] << ref
  45 + end
  46 + end
  47 + @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)
  48 + @refs ||= []
  49 + end
  50 +
  51 + def space
  52 + if @spaces.size > 0
  53 + @spaces.first
  54 + else
  55 + 0
  56 + end
  57 + end
  58 + end
  59 +end
app/models/graph/json_builder.rb 0 → 100644
@@ -0,0 +1,291 @@ @@ -0,0 +1,291 @@
  1 +require "grit"
  2 +
  3 +module Graph
  4 + class JsonBuilder
  5 + attr_accessor :days, :commits, :ref_cache, :repo
  6 +
  7 + def self.max_count
  8 + @max_count ||= 650
  9 + end
  10 +
  11 + def initialize project, ref, commit
  12 + @project = project
  13 + @ref = ref
  14 + @commit = commit
  15 + @repo = project.repo
  16 + @ref_cache = {}
  17 +
  18 + @commits = collect_commits
  19 + @days = index_commits
  20 + end
  21 +
  22 + def to_json(*args)
  23 + {
  24 + days: @days.compact.map { |d| [d.day, d.strftime("%b")] },
  25 + commits: @commits.map(&:to_graph_hash)
  26 + }.to_json(*args)
  27 + end
  28 +
  29 + protected
  30 +
  31 + # Get commits from repository
  32 + #
  33 + def collect_commits
  34 +
  35 + @commits = Grit::Commit.find_all(repo, nil, {date_order: true, max_count: self.class.max_count, skip: to_commit}).dup
  36 +
  37 + # Decorate with app/models/commit.rb
  38 + @commits.map! { |commit| Commit.new(commit) }
  39 +
  40 + # Decorate with lib/gitlab/graph/commit.rb
  41 + @commits.map! { |commit| Graph::Commit.new(commit) }
  42 +
  43 + # add refs to each commit
  44 + @commits.each { |commit| commit.add_refs(ref_cache, repo) }
  45 +
  46 + @commits
  47 + end
  48 +
  49 + # Method is adding time and space on the
  50 + # list of commits. As well as returns date list
  51 + # corelated with time set on commits.
  52 + #
  53 + # @param [Array<Graph::Commit>] commits to index
  54 + #
  55 + # @return [Array<TimeDate>] list of commit dates corelated with time on commits
  56 + def index_commits
  57 + days, times = [], []
  58 + map = {}
  59 +
  60 + commits.reverse.each_with_index do |c,i|
  61 + c.time = i
  62 + days[i] = c.committed_date
  63 + map[c.id] = c
  64 + times[i] = c
  65 + end
  66 +
  67 + @_reserved = {}
  68 + days.each_index do |i|
  69 + @_reserved[i] = []
  70 + end
  71 +
  72 + commits_sort_by_ref.each do |commit|
  73 + if map.include? commit.id then
  74 + place_chain(map[commit.id], map)
  75 + end
  76 + end
  77 +
  78 + # find parent spaces for not overlap lines
  79 + times.each do |c|
  80 + c.parent_spaces.concat(find_free_parent_spaces(c, map, times))
  81 + end
  82 +
  83 + days
  84 + end
  85 +
  86 + # Skip count that the target commit is displayed in center.
  87 + def to_commit
  88 + commits = Grit::Commit.find_all(repo, nil, {date_order: true})
  89 + commit_index = commits.index do |c|
  90 + c.id == @commit.id
  91 + end
  92 +
  93 + if commit_index && (self.class.max_count / 2 < commit_index) then
  94 + # get max index that commit is displayed in the center.
  95 + commit_index - self.class.max_count / 2
  96 + else
  97 + 0
  98 + end
  99 + end
  100 +
  101 + def commits_sort_by_ref
  102 + commits.sort do |a,b|
  103 + if include_ref?(a)
  104 + -1
  105 + elsif include_ref?(b)
  106 + 1
  107 + else
  108 + b.committed_date <=> a.committed_date
  109 + end
  110 + end
  111 + end
  112 +
  113 + def include_ref?(commit)
  114 + heads = commit.refs.select do |ref|
  115 + ref.is_a?(Grit::Head) or ref.is_a?(Grit::Remote) or ref.is_a?(Grit::Tag)
  116 + end
  117 +
  118 + heads.map! do |head|
  119 + head.name
  120 + end
  121 +
  122 + heads.include?(@ref)
  123 + end
  124 +
  125 + def find_free_parent_spaces(commit, map, times)
  126 + spaces = []
  127 +
  128 + commit.parents.each do |p|
  129 + if map.include?(p.id) then
  130 + parent = map[p.id]
  131 +
  132 + range = if commit.time < parent.time then
  133 + commit.time..parent.time
  134 + else
  135 + parent.time..commit.time
  136 + end
  137 +
  138 + space = if commit.space >= parent.space then
  139 + find_free_parent_space(range, parent.space, -1, commit.space, times)
  140 + else
  141 + find_free_parent_space(range, commit.space, -1, parent.space, times)
  142 + end
  143 +
  144 + mark_reserved(range, space)
  145 + spaces << space
  146 + end
  147 + end
  148 +
  149 + spaces
  150 + end
  151 +
  152 + def find_free_parent_space(range, space_base, space_step, space_default, times)
  153 + if is_overlap?(range, times, space_default) then
  154 + find_free_space(range, space_step, space_base, space_default)
  155 + else
  156 + space_default
  157 + end
  158 + end
  159 +
  160 + def is_overlap?(range, times, overlap_space)
  161 + range.each do |i|
  162 + if i != range.first &&
  163 + i != range.last &&
  164 + times[i].spaces.include?(overlap_space) then
  165 +
  166 + return true;
  167 + end
  168 + end
  169 +
  170 + false
  171 + end
  172 +
  173 + # Add space mark on commit and its parents
  174 + #
  175 + # @param [Graph::Commit] the commit object.
  176 + # @param [Hash<String,Graph::Commit>] map of commits
  177 + def place_chain(commit, map, parent_time = nil)
  178 + leaves = take_left_leaves(commit, map)
  179 + if leaves.empty?
  180 + return
  181 + end
  182 +
  183 + time_range = leaves.last.time..leaves.first.time
  184 + space_base = get_space_base(leaves, map)
  185 + space = find_free_space(time_range, 2, space_base)
  186 + leaves.each do |l|
  187 + l.spaces << space
  188 + # Also add space to parent
  189 + l.parents.each do |p|
  190 + if map.include?(p.id)
  191 + parent = map[p.id]
  192 + if parent.space > 0
  193 + parent.spaces << space
  194 + end
  195 + end
  196 + end
  197 + end
  198 +
  199 + # and mark it as reserved
  200 + min_time = leaves.last.time
  201 + parents = leaves.last.parents.collect
  202 + parents.each do |p|
  203 + if map.include? p.id
  204 + parent = map[p.id]
  205 + if parent.time < min_time
  206 + min_time = parent.time
  207 + end
  208 + end
  209 + end
  210 +
  211 + if parent_time.nil?
  212 + max_time = leaves.first.time
  213 + else
  214 + max_time = parent_time - 1
  215 + end
  216 + mark_reserved(min_time..max_time, space)
  217 +
  218 + # Visit branching chains
  219 + leaves.each do |l|
  220 + parents = l.parents.collect.select{|p| map.include? p.id and map[p.id].space.zero?}
  221 + for p in parents
  222 + place_chain(map[p.id], map, l.time)
  223 + end
  224 + end
  225 + end
  226 +
  227 + def get_space_base(leaves, map)
  228 + space_base = 1
  229 + if leaves.last.parents.size > 0
  230 + first_parent = leaves.last.parents.first
  231 + if map.include?(first_parent.id)
  232 + first_p = map[first_parent.id]
  233 + if first_p.space > 0
  234 + space_base = first_p.space
  235 + end
  236 + end
  237 + end
  238 + space_base
  239 + end
  240 +
  241 + def mark_reserved(time_range, space)
  242 + for day in time_range
  243 + @_reserved[day].push(space)
  244 + end
  245 + end
  246 +
  247 + def find_free_space(time_range, space_step, space_base = 1, space_default = nil)
  248 + space_default ||= space_base
  249 +
  250 + reserved = []
  251 + for day in time_range
  252 + reserved += @_reserved[day]
  253 + end
  254 + reserved.uniq!
  255 +
  256 + space = space_default
  257 + while reserved.include?(space) do
  258 + space += space_step
  259 + if space < space_base then
  260 + space_step *= -1
  261 + space = space_base + space_step
  262 + end
  263 + end
  264 +
  265 + space
  266 + end
  267 +
  268 + # Takes most left subtree branch of commits
  269 + # which don't have space mark yet.
  270 + #
  271 + # @param [Graph::Commit] the commit object.
  272 + # @param [Hash<String,Graph::Commit>] map of commits
  273 + #
  274 + # @return [Array<Graph::Commit>] list of branch commits
  275 + def take_left_leaves(commit, map)
  276 + leaves = []
  277 + leaves.push(commit) if commit.space.zero?
  278 +
  279 + while true
  280 + return leaves if commit.parents.count.zero?
  281 + return leaves unless map.include? commit.parents.first.id
  282 +
  283 + commit = map[commit.parents.first.id]
  284 +
  285 + return leaves unless commit.space.zero?
  286 +
  287 + leaves.push(commit)
  288 + end
  289 + end
  290 + end
  291 +end
app/models/group.rb
@@ -2,13 +2,14 @@ @@ -2,13 +2,14 @@
2 # 2 #
3 # Table name: namespaces 3 # Table name: namespaces
4 # 4 #
5 -# id :integer not null, primary key  
6 -# name :string(255) not null  
7 -# path :string(255) not null  
8 -# owner_id :integer not null  
9 -# created_at :datetime not null  
10 -# updated_at :datetime not null  
11 -# type :string(255) 5 +# id :integer not null, primary key
  6 +# name :string(255) not null
  7 +# description :string(255) not null
  8 +# path :string(255) not null
  9 +# owner_id :integer not null
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +# type :string(255)
12 # 13 #
13 14
14 class Group < Namespace 15 class Group < Namespace
app/models/issue.rb
@@ -30,6 +30,10 @@ class Issue &lt; ActiveRecord::Base @@ -30,6 +30,10 @@ class Issue &lt; ActiveRecord::Base
30 where('assignee_id = :user', user: user.id) 30 where('assignee_id = :user', user: user.id)
31 end 31 end
32 32
  33 + def authored(user)
  34 + where('author_id = :user', user: user.id)
  35 + end
  36 +
33 def open_for(user) 37 def open_for(user)
34 opened.assigned(user) 38 opened.assigned(user)
35 end 39 end
app/models/namespace.rb
@@ -2,17 +2,18 @@ @@ -2,17 +2,18 @@
2 # 2 #
3 # Table name: namespaces 3 # Table name: namespaces
4 # 4 #
5 -# id :integer not null, primary key  
6 -# name :string(255) not null  
7 -# path :string(255) not null  
8 -# owner_id :integer not null  
9 -# created_at :datetime not null  
10 -# updated_at :datetime not null  
11 -# type :string(255) 5 +# id :integer not null, primary key
  6 +# name :string(255) not null
  7 +# description :string(255) not null
  8 +# path :string(255) not null
  9 +# owner_id :integer not null
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +# type :string(255)
12 # 13 #
13 14
14 class Namespace < ActiveRecord::Base 15 class Namespace < ActiveRecord::Base
15 - attr_accessible :name, :path 16 + attr_accessible :name, :description, :path
16 17
17 has_many :projects, dependent: :destroy 18 has_many :projects, dependent: :destroy
18 belongs_to :owner, class_name: "User" 19 belongs_to :owner, class_name: "User"
@@ -22,7 +23,7 @@ class Namespace &lt; ActiveRecord::Base @@ -22,7 +23,7 @@ class Namespace &lt; ActiveRecord::Base
22 length: { within: 0..255 }, 23 length: { within: 0..255 },
23 format: { with: Gitlab::Regex.name_regex, 24 format: { with: Gitlab::Regex.name_regex,
24 message: "only letters, digits, spaces & '_' '-' '.' allowed." } 25 message: "only letters, digits, spaces & '_' '-' '.' allowed." }
25 - 26 + validates :description, length: { within: 0..255 }
26 validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, 27 validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
27 format: { with: Gitlab::Regex.path_regex, 28 format: { with: Gitlab::Regex.path_regex,
28 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 29 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
app/models/project.rb
@@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
11 # creator_id :integer 11 # creator_id :integer
12 # default_branch :string(255) 12 # default_branch :string(255)
13 # issues_enabled :boolean default(TRUE), not null 13 # issues_enabled :boolean default(TRUE), not null
  14 +# issues_tracker :string not null
14 # wall_enabled :boolean default(TRUE), not null 15 # wall_enabled :boolean default(TRUE), not null
15 # merge_requests_enabled :boolean default(TRUE), not null 16 # merge_requests_enabled :boolean default(TRUE), not null
16 # wiki_enabled :boolean default(TRUE), not null 17 # wiki_enabled :boolean default(TRUE), not null
@@ -22,11 +23,12 @@ require &quot;grit&quot; @@ -22,11 +23,12 @@ require &quot;grit&quot;
22 23
23 class Project < ActiveRecord::Base 24 class Project < ActiveRecord::Base
24 include Gitolited 25 include Gitolited
  26 + extend Enumerize
25 27
26 class TransferError < StandardError; end 28 class TransferError < StandardError; end
27 29
28 - attr_accessible :name, :path, :description, :default_branch,  
29 - :issues_enabled, :wall_enabled, :merge_requests_enabled, 30 + attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
  31 + :issues_enabled, :wall_enabled, :merge_requests_enabled, :issues_tracker_id,
30 :wiki_enabled, :public, :import_url, as: [:default, :admin] 32 :wiki_enabled, :public, :import_url, as: [:default, :admin]
31 33
32 attr_accessible :namespace_id, :creator_id, as: :admin 34 attr_accessible :namespace_id, :creator_id, as: :admin
@@ -43,7 +45,7 @@ class Project &lt; ActiveRecord::Base @@ -43,7 +45,7 @@ class Project &lt; ActiveRecord::Base
43 45
44 has_many :events, dependent: :destroy 46 has_many :events, dependent: :destroy
45 has_many :merge_requests, dependent: :destroy 47 has_many :merge_requests, dependent: :destroy
46 - has_many :issues, dependent: :destroy, order: "state, created_at DESC" 48 + has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC"
47 has_many :milestones, dependent: :destroy 49 has_many :milestones, dependent: :destroy
48 has_many :users_projects, dependent: :destroy 50 has_many :users_projects, dependent: :destroy
49 has_many :notes, dependent: :destroy 51 has_many :notes, dependent: :destroy
@@ -72,6 +74,7 @@ class Project &lt; ActiveRecord::Base @@ -72,6 +74,7 @@ class Project &lt; ActiveRecord::Base
72 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 74 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
73 validates :issues_enabled, :wall_enabled, :merge_requests_enabled, 75 validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
74 :wiki_enabled, inclusion: { in: [true, false] } 76 :wiki_enabled, inclusion: { in: [true, false] }
  77 + validates :issues_tracker_id, length: { within: 0..255 }
75 78
76 validates_uniqueness_of :name, scope: :namespace_id 79 validates_uniqueness_of :name, scope: :namespace_id
77 validates_uniqueness_of :path, scope: :namespace_id 80 validates_uniqueness_of :path, scope: :namespace_id
@@ -93,6 +96,8 @@ class Project &lt; ActiveRecord::Base @@ -93,6 +96,8 @@ class Project &lt; ActiveRecord::Base
93 scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } 96 scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
94 scope :public_only, -> { where(public: true) } 97 scope :public_only, -> { where(public: true) }
95 98
  99 + enumerize :issues_tracker, :in => (Gitlab.config.issues_tracker.keys).append(:gitlab), :default => :gitlab
  100 +
96 class << self 101 class << self
97 def abandoned 102 def abandoned
98 project_ids = Event.select('max(created_at) as latest_date, project_id'). 103 project_ids = Event.select('max(created_at) as latest_date, project_id').
@@ -201,6 +206,22 @@ class Project &lt; ActiveRecord::Base @@ -201,6 +206,22 @@ class Project &lt; ActiveRecord::Base
201 issues.tag_counts_on(:labels) 206 issues.tag_counts_on(:labels)
202 end 207 end
203 208
  209 + def issue_exists?(issue_id)
  210 + if used_default_issues_tracker?
  211 + self.issues.where(id: issue_id).first.present?
  212 + else
  213 + true
  214 + end
  215 + end
  216 +
  217 + def used_default_issues_tracker?
  218 + self.issues_tracker == Project.issues_tracker.default_value
  219 + end
  220 +
  221 + def can_have_issues_tracker_id?
  222 + self.issues_enabled && !self.used_default_issues_tracker?
  223 + end
  224 +
204 def services 225 def services
205 [gitlab_ci_service].compact 226 [gitlab_ci_service].compact
206 end 227 end
app/models/repository.rb
@@ -137,7 +137,7 @@ class Repository @@ -137,7 +137,7 @@ class Repository
137 file_path = File.join(storage_path, self.path_with_namespace, file_name) 137 file_path = File.join(storage_path, self.path_with_namespace, file_name)
138 138
139 # Put files into a directory before archiving 139 # Put files into a directory before archiving
140 - prefix = self.path_with_namespace + "/" 140 + prefix = File.basename(self.path_with_namespace) + "/"
141 141
142 # Create file if not exists 142 # Create file if not exists
143 unless File.exists?(file_path) 143 unless File.exists?(file_path)
app/models/user.rb
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 # dark_scheme :boolean default(FALSE), not null 25 # dark_scheme :boolean default(FALSE), not null
26 # theme_id :integer default(1), not null 26 # theme_id :integer default(1), not null
27 # bio :string(255) 27 # bio :string(255)
28 -# blocked :boolean default(FALSE), not null 28 +# state :string(255)
29 # failed_attempts :integer default(0) 29 # failed_attempts :integer default(0)
30 # locked_at :datetime 30 # locked_at :datetime
31 # extern_uid :string(255) 31 # extern_uid :string(255)
@@ -46,10 +46,35 @@ class User &lt; ActiveRecord::Base @@ -46,10 +46,35 @@ class User &lt; ActiveRecord::Base
46 46
47 attr_accessor :force_random_password 47 attr_accessor :force_random_password
48 48
  49 + #
  50 + # Relations
  51 + #
  52 +
49 # Namespace for personal projects 53 # Namespace for personal projects
50 - has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL' 54 + has_one :namespace,
  55 + dependent: :destroy,
  56 + foreign_key: :owner_id,
  57 + class_name: "Namespace",
  58 + conditions: 'type IS NULL'
  59 +
  60 + # Profile
  61 + has_many :keys, dependent: :destroy
  62 +
  63 + # Groups
  64 + has_many :groups, class_name: "Group", foreign_key: :owner_id
  65 +
  66 + # Teams
  67 + has_many :own_teams,
  68 + class_name: "UserTeam",
  69 + foreign_key: :owner_id,
  70 + dependent: :destroy
  71 +
  72 + has_many :user_team_user_relationships, dependent: :destroy
  73 + has_many :user_teams, through: :user_team_user_relationships
  74 + has_many :user_team_project_relationships, through: :user_teams
  75 + has_many :team_projects, through: :user_team_project_relationships
51 76
52 - has_many :keys, dependent: :destroy 77 + # Projects
53 has_many :users_projects, dependent: :destroy 78 has_many :users_projects, dependent: :destroy
54 has_many :issues, dependent: :destroy, foreign_key: :author_id 79 has_many :issues, dependent: :destroy, foreign_key: :author_id
55 has_many :notes, dependent: :destroy, foreign_key: :author_id 80 has_many :notes, dependent: :destroy, foreign_key: :author_id
@@ -57,18 +82,16 @@ class User &lt; ActiveRecord::Base @@ -57,18 +82,16 @@ class User &lt; ActiveRecord::Base
57 has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event" 82 has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
58 has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue" 83 has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
59 has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest" 84 has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
  85 + has_many :projects, through: :users_projects
60 86
61 - has_many :groups, class_name: "Group", foreign_key: :owner_id  
62 - has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"  
63 -  
64 - has_many :projects, through: :users_projects  
65 -  
66 - has_many :user_team_user_relationships, dependent: :destroy  
67 -  
68 - has_many :user_teams, through: :user_team_user_relationships  
69 - has_many :user_team_project_relationships, through: :user_teams  
70 - has_many :team_projects, through: :user_team_project_relationships 87 + has_many :recent_events,
  88 + class_name: "Event",
  89 + foreign_key: :author_id,
  90 + order: "id DESC"
71 91
  92 + #
  93 + # Validations
  94 + #
72 validates :name, presence: true 95 validates :name, presence: true
73 validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ } 96 validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
74 validates :bio, length: { within: 0..255 } 97 validates :bio, length: { within: 0..255 }
@@ -87,10 +110,27 @@ class User &lt; ActiveRecord::Base @@ -87,10 +110,27 @@ class User &lt; ActiveRecord::Base
87 110
88 delegate :path, to: :namespace, allow_nil: true, prefix: true 111 delegate :path, to: :namespace, allow_nil: true, prefix: true
89 112
  113 + state_machine :state, initial: :active do
  114 + after_transition any => :blocked do |user, transition|
  115 + # Remove user from all projects and
  116 + user.users_projects.find_each do |membership|
  117 + return false unless membership.destroy
  118 + end
  119 + end
  120 +
  121 + event :block do
  122 + transition active: :blocked
  123 + end
  124 +
  125 + event :activate do
  126 + transition blocked: :active
  127 + end
  128 + end
  129 +
90 # Scopes 130 # Scopes
91 scope :admins, -> { where(admin: true) } 131 scope :admins, -> { where(admin: true) }
92 - scope :blocked, -> { where(blocked: true) }  
93 - scope :active, -> { where(blocked: false) } 132 + scope :blocked, -> { with_state(:blocked) }
  133 + scope :active, -> { with_state(:active) }
94 scope :alphabetically, -> { order('name ASC') } 134 scope :alphabetically, -> { order('name ASC') }
95 scope :in_team, ->(team){ where(id: team.member_ids) } 135 scope :in_team, ->(team){ where(id: team.member_ids) }
96 scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } 136 scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
@@ -260,17 +300,6 @@ class User &lt; ActiveRecord::Base @@ -260,17 +300,6 @@ class User &lt; ActiveRecord::Base
260 MergeRequest.cared(self) 300 MergeRequest.cared(self)
261 end 301 end
262 302
263 - # Remove user from all projects and  
264 - # set blocked attribute to true  
265 - def block  
266 - users_projects.find_each do |membership|  
267 - return false unless membership.destroy  
268 - end  
269 -  
270 - self.blocked = true  
271 - save  
272 - end  
273 -  
274 def projects_limit_percent 303 def projects_limit_percent
275 return 100 if projects_limit.zero? 304 return 100 if projects_limit.zero?
276 (personal_projects.count.to_f / projects_limit) * 100 305 (personal_projects.count.to_f / projects_limit) * 100
app/models/user_team.rb
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 12
13 class UserTeam < ActiveRecord::Base 13 class UserTeam < ActiveRecord::Base
14 - attr_accessible :name, :owner_id, :path 14 + attr_accessible :name, :description, :owner_id, :path
15 15
16 belongs_to :owner, class_name: User 16 belongs_to :owner, class_name: User
17 17
@@ -26,6 +26,7 @@ class UserTeam &lt; ActiveRecord::Base @@ -26,6 +26,7 @@ class UserTeam &lt; ActiveRecord::Base
26 length: { within: 0..255 }, 26 length: { within: 0..255 },
27 format: { with: Gitlab::Regex.name_regex, 27 format: { with: Gitlab::Regex.name_regex,
28 message: "only letters, digits, spaces & '_' '-' '.' allowed." } 28 message: "only letters, digits, spaces & '_' '-' '.' allowed." }
  29 + validates :description, length: { within: 0..255 }
29 validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, 30 validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
30 format: { with: Gitlab::Regex.path_regex, 31 format: { with: Gitlab::Regex.path_regex,
31 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 32 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
app/observers/issue_observer.rb
@@ -27,7 +27,7 @@ class IssueObserver &lt; ActiveRecord::Observer @@ -27,7 +27,7 @@ class IssueObserver &lt; ActiveRecord::Observer
27 27
28 def create_note(issue) 28 def create_note(issue)
29 Note.create_status_change_note(issue, current_user, issue.state) 29 Note.create_status_change_note(issue, current_user, issue.state)
30 - [issue.author, issue.assignee].compact.each do |recipient| 30 + [issue.author, issue.assignee].compact.uniq.each do |recipient|
31 Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id) 31 Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id)
32 end 32 end
33 end 33 end
app/observers/user_observer.rb
@@ -2,7 +2,8 @@ class UserObserver &lt; ActiveRecord::Observer @@ -2,7 +2,8 @@ class UserObserver &lt; ActiveRecord::Observer
2 def after_create(user) 2 def after_create(user)
3 log_info("User \"#{user.name}\" (#{user.email}) was created") 3 log_info("User \"#{user.name}\" (#{user.email}) was created")
4 4
5 - Notify.delay.new_user_email(user.id, user.password) 5 + # Dont email omniauth created users
  6 + Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid?
6 end 7 end
7 8
8 def after_destroy user 9 def after_destroy user
app/services/git_push_service.rb
@@ -19,6 +19,8 @@ class GitPushService @@ -19,6 +19,8 @@ class GitPushService
19 # Collect data for this git push 19 # Collect data for this git push
20 @push_data = post_receive_data(oldrev, newrev, ref) 20 @push_data = post_receive_data(oldrev, newrev, ref)
21 21
  22 + create_push_event
  23 +
22 project.ensure_satellite_exists 24 project.ensure_satellite_exists
23 project.discover_default_branch 25 project.discover_default_branch
24 26
@@ -27,8 +29,6 @@ class GitPushService @@ -27,8 +29,6 @@ class GitPushService
27 project.execute_hooks(@push_data.dup) 29 project.execute_hooks(@push_data.dup)
28 project.execute_services(@push_data.dup) 30 project.execute_services(@push_data.dup)
29 end 31 end
30 -  
31 - create_push_event  
32 end 32 end
33 33
34 # This method provide a sample data 34 # This method provide a sample data
app/services/project_transfer_service.rb
@@ -25,7 +25,7 @@ class ProjectTransferService @@ -25,7 +25,7 @@ class ProjectTransferService
25 25
26 Gitlab::ProjectMover.new(project, old_dir, new_dir).execute 26 Gitlab::ProjectMover.new(project, old_dir, new_dir).execute
27 27
28 - save! 28 + project.save!
29 end 29 end
30 rescue Gitlab::ProjectMover::ProjectMoveError => ex 30 rescue Gitlab::ProjectMover::ProjectMoveError => ex
31 raise Project::TransferError.new(ex.message) 31 raise Project::TransferError.new(ex.message)
app/views/admin/groups/edit.html.haml
1 -%h3.page_title Rename Group 1 +%h3.page_title Edit Group
2 %hr 2 %hr
3 = form_for [:admin, @group] do |f| 3 = form_for [:admin, @group] do |f|
4 - if @group.errors.any? 4 - if @group.errors.any?
@@ -10,7 +10,10 @@ @@ -10,7 +10,10 @@
10 .input 10 .input
11 = f.text_field :name, placeholder: "Example Group", class: "xxlarge" 11 = f.text_field :name, placeholder: "Example Group", class: "xxlarge"
12 12
13 - 13 + .clearfix.group-description-holder
  14 + = f.label :description, "Details"
  15 + .input
  16 + = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
14 17
15 .clearfix.group_name_holder 18 .clearfix.group_name_holder
16 = f.label :path do 19 = f.label :path do
@@ -24,5 +27,5 @@ @@ -24,5 +27,5 @@
24 %li It will change the git path to repositories under this group. 27 %li It will change the git path to repositories under this group.
25 28
26 .form-actions 29 .form-actions
27 - = f.submit 'Rename group', class: "btn btn-remove" 30 + = f.submit 'Edit group', class: "btn btn-remove"
28 = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel" 31 = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
app/views/admin/groups/index.html.haml
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 Name 17 Name
18 %i.icon-sort-down 18 %i.icon-sort-down
19 %th Path 19 %th Path
  20 + %th Description
20 %th Projects 21 %th Projects
21 %th Owner 22 %th Owner
22 %th.cred Danger Zone! 23 %th.cred Danger Zone!
@@ -25,11 +26,12 @@ @@ -25,11 +26,12 @@
25 %tr 26 %tr
26 %td 27 %td
27 %strong= link_to group.name, [:admin, group] 28 %strong= link_to group.name, [:admin, group]
  29 + %td= truncate group.description
28 %td= group.path 30 %td= group.path
29 %td= group.projects.count 31 %td= group.projects.count
30 %td 32 %td
31 = link_to group.owner_name, admin_user_path(group.owner) 33 = link_to group.owner_name, admin_user_path(group.owner)
32 %td.bgred 34 %td.bgred
33 - = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small" 35 + = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
34 = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" 36 = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
35 = paginate @groups, theme: "admin" 37 = paginate @groups, theme: "admin"
app/views/admin/groups/new.html.haml
@@ -9,8 +9,14 @@ @@ -9,8 +9,14 @@
9 Group name is 9 Group name is
10 .input 10 .input
11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" 11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
12 - &nbsp;  
13 - = f.submit 'Create group', class: "btn btn-primary" 12 + .clearfix.group-description-holder
  13 + = f.label :description, "Details"
  14 + .input
  15 + = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
  16 +
  17 + .form-actions
  18 + = f.submit 'Create group', class: "btn btn-primary"
  19 +
14 %hr 20 %hr
15 .padded 21 .padded
16 %ul 22 %ul
app/views/admin/groups/show.html.haml
@@ -16,7 +16,13 @@ @@ -16,7 +16,13 @@
16 &nbsp; 16 &nbsp;
17 = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do 17 = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do
18 %i.icon-edit 18 %i.icon-edit
19 - Rename 19 + Edit
  20 + %tr
  21 + %td
  22 + %b
  23 + Description:
  24 + %td
  25 + = @group.description
20 %tr 26 %tr
21 %td 27 %td
22 %b 28 %b
app/views/admin/projects/_form.html.haml
@@ -31,6 +31,15 @@ @@ -31,6 +31,15 @@
31 = f.label :issues_enabled, "Issues" 31 = f.label :issues_enabled, "Issues"
32 .input= f.check_box :issues_enabled 32 .input= f.check_box :issues_enabled
33 33
  34 + - if Project.issues_tracker.values.count > 1
  35 + .clearfix
  36 + = f.label :issues_tracker, "Issues tracker", class: 'control-label'
  37 + .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
  38 +
  39 + .clearfix
  40 + = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
  41 + .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
  42 +
34 .clearfix 43 .clearfix
35 = f.label :merge_requests_enabled, "Merge Requests" 44 = f.label :merge_requests_enabled, "Merge Requests"
36 .input= f.check_box :merge_requests_enabled 45 .input= f.check_box :merge_requests_enabled
app/views/admin/teams/edit.html.haml
1 -%h3.page_title Rename Team 1 +%h3.page_title Edit Team
2 %hr 2 %hr
3 = form_for @team, url: admin_team_path(@team), method: :put do |f| 3 = form_for @team, url: admin_team_path(@team), method: :put do |f|
4 - if @team.errors.any? 4 - if @team.errors.any?
@@ -10,6 +10,11 @@ @@ -10,6 +10,11 @@
10 .input 10 .input
11 = f.text_field :name, placeholder: "Example Team", class: "xxlarge" 11 = f.text_field :name, placeholder: "Example Team", class: "xxlarge"
12 12
  13 + .clearfix.team-description-holder
  14 + = f.label :description, "Details"
  15 + .input
  16 + = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
  17 +
13 .clearfix.team_name_holder 18 .clearfix.team_name_holder
14 = f.label :path do 19 = f.label :path do
15 %span.cred Team path is 20 %span.cred Team path is
@@ -19,5 +24,5 @@ @@ -19,5 +24,5 @@
19 %li It will change web url for access team and team projects. 24 %li It will change web url for access team and team projects.
20 25
21 .form-actions 26 .form-actions
22 - = f.submit 'Rename team', class: "btn btn-remove" 27 + = f.submit 'Edit team', class: "btn btn-remove"
23 = link_to 'Cancel', admin_teams_path, class: "btn btn-cancel" 28 = link_to 'Cancel', admin_teams_path, class: "btn btn-cancel"
app/views/admin/teams/index.html.haml
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 %th 16 %th
17 Name 17 Name
18 %i.icon-sort-down 18 %i.icon-sort-down
  19 + %th Description
19 %th Path 20 %th Path
20 %th Projects 21 %th Projects
21 %th Members 22 %th Members
@@ -26,13 +27,17 @@ @@ -26,13 +27,17 @@
26 %tr 27 %tr
27 %td 28 %td
28 %strong= link_to team.name, admin_team_path(team) 29 %strong= link_to team.name, admin_team_path(team)
  30 + %td= truncate team.description
29 %td= team.path 31 %td= team.path
30 %td= team.projects.count 32 %td= team.projects.count
31 %td= team.members.count 33 %td= team.members.count
32 %td 34 %td
33 - = link_to team.owner.name, admin_user_path(team.owner) 35 + - if team.owner
  36 + = link_to team.owner.name, admin_user_path(team.owner)
  37 + - else
  38 + (deleted)
34 %td.bgred 39 %td.bgred
35 - = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small" 40 + = link_to 'Edit', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
36 = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" 41 = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
37 42
38 = paginate @teams, theme: "admin" 43 = paginate @teams, theme: "admin"
app/views/admin/teams/new.html.haml
@@ -9,8 +9,15 @@ @@ -9,8 +9,15 @@
9 Team name is 9 Team name is
10 .input 10 .input
11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" 11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
12 - &nbsp;  
13 - = f.submit 'Create team', class: "btn btn-primary" 12 +
  13 + .clearfix.team-description-holder
  14 + = f.label :description, "Details"
  15 + .input
  16 + = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
  17 +
  18 + .form-actions
  19 + = f.submit 'Create team', class: "btn btn-primary"
  20 +
14 %hr 21 %hr
15 .padded 22 .padded
16 %ul 23 %ul
app/views/admin/teams/show.html.haml
@@ -16,7 +16,13 @@ @@ -16,7 +16,13 @@
16 &nbsp; 16 &nbsp;
17 = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do 17 = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do
18 %i.icon-edit 18 %i.icon-edit
19 - Rename 19 + Edit
  20 + %tr
  21 + %td
  22 + %b
  23 + Description:
  24 + %td
  25 + = @team.description
20 %tr 26 %tr
21 %td 27 %td
22 %b 28 %b
app/views/admin/users/_form.html.haml
@@ -61,7 +61,7 @@ @@ -61,7 +61,7 @@
61 .span4 61 .span4
62 - unless @admin_user.new_record? 62 - unless @admin_user.new_record?
63 .alert.alert-error 63 .alert.alert-error
64 - - if @admin_user.blocked 64 + - if @admin_user.blocked?
65 %p This user is blocked and is not able to login to GitLab 65 %p This user is blocked and is not able to login to GitLab
66 = link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small" 66 = link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small"
67 - else 67 - else
app/views/admin/users/index.html.haml
@@ -53,7 +53,7 @@ @@ -53,7 +53,7 @@
53 &nbsp; 53 &nbsp;
54 = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-small" 54 = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-small"
55 - unless user == current_user 55 - unless user == current_user
56 - - if user.blocked 56 + - if user.blocked?
57 = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-small success" 57 = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-small success"
58 - else 58 - else
59 = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove" 59 = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"
app/views/admin/users/show.html.haml
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 %h3.page_title 3 %h3.page_title
4 = image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90" 4 = image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90"
5 = @admin_user.name 5 = @admin_user.name
6 - - if @admin_user.blocked 6 + - if @admin_user.blocked?
7 %span.cred (Blocked) 7 %span.cred (Blocked)
8 - if @admin_user.admin 8 - if @admin_user.admin
9 %span.cred (Admin) 9 %span.cred (Admin)
app/views/commits/_commits.html.haml
1 -- @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits| 1 +- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
2 %div.ui-box 2 %div.ui-box
3 %h5.title 3 %h5.title
4 %i.icon-calendar 4 %i.icon-calendar
app/views/events/event/_note.html.haml
@@ -21,9 +21,10 @@ @@ -21,9 +21,10 @@
21 = event.project_name 21 = event.project_name
22 22
23 .event-body 23 .event-body
24 - %i.icon-comment-alt.event-note-icon  
25 - %span.event-note  
26 - = markdown truncate(event.target.note, length: 70) 24 + .event-note
  25 + .md
  26 + %i.icon-comment-alt.event-note-icon
  27 + = sanitize(markdown(truncate(event.target.note, length: 150)), tags: %w(a img b pre p))
27 - note = event.target 28 - note = event.target
28 - if note.attachment.url 29 - if note.attachment.url
29 = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do 30 = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do
app/views/groups/edit.html.haml
@@ -9,8 +9,15 @@ @@ -9,8 +9,15 @@
9 Group name is 9 Group name is
10 .input 10 .input
11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" 11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
12 - &nbsp;  
13 - = f.submit 'Save group', class: "btn btn-save" 12 +
  13 + .clearfix.group-description-holder
  14 + = f.label :description, "Details"
  15 + .input
  16 + = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
  17 +
  18 + .form-actions
  19 + = f.submit 'Save group', class: "btn btn-save"
  20 +
14 %hr 21 %hr
15 22
16 23
app/views/groups/new.html.haml
@@ -9,9 +9,16 @@ @@ -9,9 +9,16 @@
9 Group name is 9 Group name is
10 .input 10 .input
11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" 11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
12 - &nbsp;  
13 - = f.submit 'Create group', class: "btn btn-create"  
14 - %hr 12 +
  13 + .clearfix.group-description-holder
  14 + = f.label :description, "Details"
  15 + .input
  16 + = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
  17 +
  18 + .form-actions
  19 + = f.submit 'Create group', class: "btn btn-create"
  20 +
  21 +
15 .padded 22 .padded
16 %ul 23 %ul
17 %li Group is kind of directory for several projects 24 %li Group is kind of directory for several projects
app/views/groups/show.html.haml
@@ -12,6 +12,9 @@ @@ -12,6 +12,9 @@
12 %p.nothing_here_message Project activity will be displayed here 12 %p.nothing_here_message Project activity will be displayed here
13 .loading.hide 13 .loading.hide
14 .side.span4 14 .side.span4
  15 + - if @group.description.present?
  16 + .description.well.light
  17 + = @group.description
15 = render "projects", projects: @projects 18 = render "projects", projects: @projects
16 %div 19 %div
17 %span.rss-icon 20 %span.rss-icon
app/views/issues/_filter.html.haml
@@ -6,7 +6,10 @@ @@ -6,7 +6,10 @@
6 Open 6 Open
7 %li{class: ("active" if params[:status] == 'assigned-to-me')} 7 %li{class: ("active" if params[:status] == 'assigned-to-me')}
8 = link_to project_issues_path(@project, status: 'assigned-to-me') do 8 = link_to project_issues_path(@project, status: 'assigned-to-me') do
9 - Assigned To Me 9 + Assigned to me
  10 + %li{class: ("active" if params[:status] == 'created-by-me')}
  11 + = link_to project_issues_path(@project, status: 'created-by-me') do
  12 + Created by me
10 %li{class: ("active" if params[:status] == 'closed')} 13 %li{class: ("active" if params[:status] == 'closed')}
11 = link_to project_issues_path(@project, status: 'closed') do 14 = link_to project_issues_path(@project, status: 'closed') do
12 Closed 15 Closed
app/views/layouts/_flash.html.haml
1 -- if text = alert || notice  
2 - #flash-container  
3 - %h4= text 1 +.flash-container
  2 + - if alert
  3 + .alert
  4 + %span= alert
  5 +
  6 + - elsif notice
  7 + .alert.alert-info
  8 + %span= notice
app/views/layouts/_head.html.haml
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 = stylesheet_link_tag "application" 7 = stylesheet_link_tag "application"
8 = javascript_include_tag "application" 8 = javascript_include_tag "application"
9 = csrf_meta_tags 9 = csrf_meta_tags
  10 + = include_gon
10 11
11 -# Atom feed 12 -# Atom feed
12 - if current_user 13 - if current_user
app/views/layouts/_head_panel.html.haml
@@ -8,6 +8,9 @@ @@ -8,6 +8,9 @@
8 %span.separator 8 %span.separator
9 %h1.project_name= title 9 %h1.project_name= title
10 %ul.nav 10 %ul.nav
  11 + %li
  12 + = link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
  13 + %i.icon-globe
11 - if current_user.is_admin? 14 - if current_user.is_admin?
12 %li 15 %li
13 = link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do 16 = link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do
app/views/layouts/admin.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: "Admin area" 3 = render "layouts/head", title: "Admin area"
4 %body{class: "#{app_theme} admin"} 4 %body{class: "#{app_theme} admin"}
5 - = render "layouts/flash"  
6 = render "layouts/head_panel", title: "Admin area" 5 = render "layouts/head_panel", title: "Admin area"
  6 + = render "layouts/flash"
7 .container 7 .container
8 %ul.main_menu 8 %ul.main_menu
9 = nav_link(controller: :dashboard, html_options: {class: 'home'}) do 9 = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
app/views/layouts/application.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
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"} 4 %body{class: "#{app_theme} application"}
5 - = render "layouts/flash"  
6 = render "layouts/head_panel", title: "Dashboard" 5 = render "layouts/head_panel", title: "Dashboard"
  6 + = render "layouts/flash"
7 .container 7 .container
8 %ul.main_menu 8 %ul.main_menu
9 = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do 9 = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
app/views/layouts/devise.html.haml
@@ -3,4 +3,6 @@ @@ -3,4 +3,6 @@
3 = render "layouts/head" 3 = render "layouts/head"
4 %body.ui_basic.login-page 4 %body.ui_basic.login-page
5 = render "layouts/flash" 5 = render "layouts/flash"
6 - .container= yield 6 + .container
  7 + .content
  8 + = yield
app/views/layouts/errors.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: "Error" 3 = render "layouts/head", title: "Error"
4 %body{class: "#{app_theme} application"} 4 %body{class: "#{app_theme} application"}
5 - = render "layouts/flash"  
6 = render "layouts/head_panel", title: "" 5 = render "layouts/head_panel", title: ""
  6 + = render "layouts/flash"
7 .container 7 .container
8 .content 8 .content
9 %center.padded.prepend-top-20 9 %center.padded.prepend-top-20
app/views/layouts/group.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: "#{@group.name}" 3 = render "layouts/head", title: "#{@group.name}"
4 %body{class: "#{app_theme} application"} 4 %body{class: "#{app_theme} application"}
5 - = render "layouts/flash"  
6 = render "layouts/head_panel", title: "group: #{@group.name}" 5 = render "layouts/head_panel", title: "group: #{@group.name}"
  6 + = render "layouts/flash"
7 .container 7 .container
8 %ul.main_menu 8 %ul.main_menu
9 = nav_link(path: 'groups#show', html_options: {class: 'home'}) do 9 = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
app/views/layouts/profile.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
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"} 4 %body{class: "#{app_theme} profile"}
5 - = render "layouts/flash"  
6 = render "layouts/head_panel", title: "Profile" 5 = render "layouts/head_panel", title: "Profile"
  6 + = render "layouts/flash"
7 .container 7 .container
8 %ul.main_menu 8 %ul.main_menu
9 = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do 9 = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
app/views/layouts/project_resource.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
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"} 4 %body{class: "#{app_theme} project"}
5 - = render "layouts/flash"  
6 = render "layouts/head_panel", title: project_title(@project) 5 = render "layouts/head_panel", title: project_title(@project)
  6 + = render "layouts/flash"
7 - if can?(current_user, :download_code, @project) 7 - if can?(current_user, :download_code, @project)
8 = render 'shared/no_ssh' 8 = render 'shared/no_ssh'
9 9
@@ -22,11 +22,12 @@ @@ -22,11 +22,12 @@
22 = nav_link(controller: %w(graph)) do 22 = nav_link(controller: %w(graph)) do
23 = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref) 23 = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref)
24 24
25 - - if @project.issues_enabled 25 + - if @project.issues_enabled
26 = nav_link(controller: %w(issues milestones labels)) do 26 = nav_link(controller: %w(issues milestones labels)) do
27 - = link_to project_issues_filter_path(@project) do 27 + = link_to url_for_project_issues do
28 Issues 28 Issues
29 - %span.count.issue_counter= @project.issues.opened.count 29 + - if @project.used_default_issues_tracker?
  30 + %span.count.issue_counter= @project.issues.opened.count
30 31
31 - if @project.repo_exists? && @project.merge_requests_enabled 32 - if @project.repo_exists? && @project.merge_requests_enabled
32 = nav_link(controller: :merge_requests) do 33 = nav_link(controller: :merge_requests) do
app/views/layouts/user_team.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 %html{ lang: "en"} 2 %html{ lang: "en"}
3 = render "layouts/head", title: "#{@team.name}" 3 = render "layouts/head", title: "#{@team.name}"
4 %body{class: "#{app_theme} application"} 4 %body{class: "#{app_theme} application"}
5 - = render "layouts/flash"  
6 = render "layouts/head_panel", title: "team: #{@team.name}" 5 = render "layouts/head_panel", title: "team: #{@team.name}"
  6 + = render "layouts/flash"
7 .container 7 .container
8 %ul.main_menu 8 %ul.main_menu
9 = nav_link(path: 'teams#show', html_options: {class: 'home'}) do 9 = nav_link(path: 'teams#show', html_options: {class: 'home'}) do
app/views/merge_requests/show/_mr_accept.html.haml
1 -- unless can?(current_user, :accept_mr, @project) 1 +- unless @allowed_to_merge
2 .alert 2 .alert
3 - %strong Only masters can accept MR 3 + %strong You don't have enough permissions to merge this MR
4 4
5 5
6 -- if @merge_request.opened? && @commits.any? && can?(current_user, :accept_mr, @project) 6 +- if @show_merge_controls
7 .automerge_widget.can_be_merged{style: "display:none"} 7 .automerge_widget.can_be_merged{style: "display:none"}
8 .alert.alert-success 8 .alert.alert-success
9 %span 9 %span
app/views/notify/issue_status_changed_email.text.erb 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +Issue was <%= @issue_status %> by <%= @updated_by.name %>
  2 +
  3 +Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
  4 +
app/views/notify/new_issue_email.text.erb 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +New Issue was created and assigned to you.
  2 +
  3 +
  4 +Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
app/views/notify/new_merge_request_email.text.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +New Merge Request <%= @merge_request.id %>
  2 +
  3 +<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
  4 +
  5 +
  6 +Branches: <%= @merge_request.source_branch %> to <%= @merge_request.target_branch %>
  7 +Author: <%= @merge_request.author_name %>
  8 +Asignee: <%= @merge_request.assignee_name %>
  9 +
app/views/notify/new_user_email.text.erb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +Hi <%= @user.name %>!
  2 +
  3 +Administrator created account for you. Now you are a member of company GitLab application.
  4 +
  5 +login.................. <%= @user.email %>
  6 +<% unless Gitlab.config.gitlab.signup_enabled %>
  7 + password............... <%= @password %>
  8 +<% end %>
  9 +
  10 +Click here to login: <%= url_for(root_url) %>
app/views/notify/note_commit_email.text.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +New comment for Commit <%= @commit.short_id %>
  2 +
  3 +<%= url_for(project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
  4 +
  5 +
  6 +Author: <%= @note.author_name %>
  7 +
  8 +<%= @note.note %>
  9 +
app/views/notify/note_issue_email.text.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +New comment for Issue <%= @issue.id %>
  2 +
  3 +<%= url_for(project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")) %>
  4 +
  5 +
  6 +Author: <%= @note.author_name %>
  7 +
  8 +<%= @note.note %>
  9 +
app/views/notify/note_merge_request_email.text.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +New comment for Merge Request <%= @merge_request.id %>
  2 +
  3 +<%= url_for(project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}")) %>
  4 +
  5 +
  6 +<%= @note.author_name %>
  7 +
  8 +<%= @note.note %>
  9 +
app/views/notify/note_wall_email.text.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +New message on the project wall <%= @note.project %>
  2 +
  3 +<%= url_for(wall_project_url(@note.project, anchor: "note_#{@note.id}")) %>
  4 +
  5 +
  6 +<%= @note.author_name %>
  7 +
  8 +<%= @note.note %>
  9 +
app/views/notify/project_access_granted_email.text.erb 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +
  2 +You have been granted <%= @users_project.project_access_human %> access to project <%= @project.name_with_namespace %>
  3 +
  4 +<%= url_for(project_url(@project)) %>
app/views/notify/project_was_moved_email.text.erb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +Project was moved to another location
  2 +
  3 +The project is now located under
  4 +<%= url_for(link_to project_url(@project)) %>
  5 +
  6 +
  7 +To update the remote url in your local repository run:
  8 + git remote set-url origin <%= @project.ssh_url_to_repo %>
app/views/notify/reassigned_issue_email.text.erb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +Reassigned Issue <%= @issue.id %>
  2 +
  3 +<%= url_for(project_issue_url(@issue.project, @issue)) %>
  4 +
  5 +
  6 +Assignee changed from <%= @previous_assignee.name %> to <%= @issue.assignee_name %>
  7 +
app/views/notify/reassigned_merge_request_email.text.erb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +Reassigned Merge Request <%= @merge_request.id %>
  2 +
  3 +<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
  4 +
  5 +
  6 +Assignee changed from <%= @previous_assignee.name %> to <%= @merge_request.assignee_name %>
  7 +
app/views/projects/_form.html.haml
@@ -24,6 +24,15 @@ @@ -24,6 +24,15 @@
24 = f.check_box :issues_enabled 24 = f.check_box :issues_enabled
25 %span.descr Lightweight issue tracking system for this project 25 %span.descr Lightweight issue tracking system for this project
26 26
  27 + - if Project.issues_tracker.values.count > 1
  28 + .control-group
  29 + = f.label :issues_tracker, "Issues tracker", class: 'control-label'
  30 + .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
  31 +
  32 + .clearfix
  33 + = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
  34 + .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
  35 +
27 .control-group 36 .control-group
28 = f.label :merge_requests_enabled, "Merge Requests", class: 'control-label' 37 = f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
29 .controls 38 .controls
app/views/projects/empty.html.haml
@@ -5,14 +5,14 @@ @@ -5,14 +5,14 @@
5 %fieldset 5 %fieldset
6 %legend Git global setup: 6 %legend Git global setup:
7 %pre.dark 7 %pre.dark
8 - = preserve do 8 + :preserve
9 git config --global user.name "#{current_user.name}" 9 git config --global user.name "#{current_user.name}"
10 git config --global user.email "#{current_user.email}" 10 git config --global user.email "#{current_user.email}"
11 11
12 %fieldset 12 %fieldset
13 %legend Create Repository 13 %legend Create Repository
14 %pre.dark 14 %pre.dark
15 - = preserve do 15 + :preserve
16 mkdir #{@project.path} 16 mkdir #{@project.path}
17 cd #{@project.path} 17 cd #{@project.path}
18 git init 18 git init
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 %fieldset 25 %fieldset
26 %legend Existing Git Repo? 26 %legend Existing Git Repo?
27 %pre.dark 27 %pre.dark
28 - = preserve do 28 + :preserve
29 cd existing_git_repo 29 cd existing_git_repo
30 git remote add origin #{@project.url_to_repo} 30 git remote add origin #{@project.url_to_repo}
31 git push -u origin master 31 git push -u origin master
app/views/public/projects/index.html.haml
@@ -12,5 +12,7 @@ @@ -12,5 +12,7 @@
12 .pull-right 12 .pull-right
13 %pre.dark.tiny git clone #{project.http_url_to_repo} 13 %pre.dark.tiny git clone #{project.http_url_to_repo}
14 14
  15 + - unless @projects.present?
  16 + %h3.nothing_here_message No public projects
15 17
16 = paginate @projects, theme: "admin" 18 = paginate @projects, theme: "admin"
app/views/shared/_clone_panel.html.haml
1 .input-prepend.project_clone_holder 1 .input-prepend.project_clone_holder
2 %button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH 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.gitlab.protocol.upcase 3 %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
4 -  
5 - = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge" 4 + = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
app/views/team_members/_team_member.html.haml
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 %span.label This is you! 20 %span.label This is you!
21 - if @project.namespace_owner == user 21 - if @project.namespace_owner == user
22 %span.label Owner 22 %span.label Owner
23 - - elsif user.blocked 23 + - elsif user.blocked?
24 %span.label Blocked 24 %span.label Blocked
25 - elsif allow_admin 25 - elsif allow_admin
26 = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do 26 = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do
app/views/teams/edit.html.haml
@@ -12,13 +12,19 @@ @@ -12,13 +12,19 @@
12 .input 12 .input
13 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left" 13 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left"
14 14
  15 + .clearfix.team-description-holder
  16 + = f.label :description, "Details"
  17 + .input
  18 + = f.text_area :description, maxlength: 250, class: "xlarge js-gfm-input", rows: 4
  19 +
15 .clearfix 20 .clearfix
16 = f.label :path do 21 = f.label :path do
17 Team path is 22 Team path is
18 .input 23 .input
19 = f.text_field :path, placeholder: "opensource", class: "xlarge left" 24 = f.text_field :path, placeholder: "opensource", class: "xlarge left"
  25 +
20 .form-actions 26 .form-actions
21 - = f.submit 'Save team changes', class: "btn btn-save" 27 + = f.submit 'Save team changes', class: "btn btn-primary"
22 .span5 28 .span5
23 .ui-box 29 .ui-box
24 %h5.title Remove team 30 %h5.title Remove team
@@ -26,4 +32,3 @@ @@ -26,4 +32,3 @@
26 %p 32 %p
27 Removed team can not be restored! 33 Removed team can not be restored!
28 = link_to 'Remove team', team_path(@team), method: :delete, confirm: "You are sure?", class: "btn btn-remove btn-small" 34 = link_to 'Remove team', team_path(@team), method: :delete, confirm: "You are sure?", class: "btn btn-remove btn-small"
29 -  
app/views/teams/members/_show.html.haml
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 %span.btn.disabled This is you! 23 %span.btn.disabled This is you!
24 - if @team.owner == user 24 - if @team.owner == user
25 %span.btn.disabled Owner 25 %span.btn.disabled Owner
26 - - elsif user.blocked 26 + - elsif user.blocked?
27 %span.btn.disabled.blocked Blocked 27 %span.btn.disabled.blocked Blocked
28 - elsif allow_admin 28 - elsif allow_admin
29 = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove", title: "Remove from team" do 29 = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove", title: "Remove from team" do
app/views/teams/new.html.haml
@@ -9,9 +9,15 @@ @@ -9,9 +9,15 @@
9 Team name is 9 Team name is
10 .input 10 .input
11 = f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left" 11 = f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left"
12 - &nbsp;  
13 - = f.submit 'Create team', class: "btn btn-create"  
14 - %hr 12 +
  13 + .clearfix.team-description-holder
  14 + = f.label :description, "Details"
  15 + .input
  16 + = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
  17 +
  18 + .form-actions
  19 + = f.submit 'Create team', class: "btn btn-create"
  20 +
15 .padded 21 .padded
16 %ul 22 %ul
17 %li All created teams are public (users can view who enter into team and which project are assigned for this team) 23 %li All created teams are public (users can view who enter into team and which project are assigned for this team)
app/views/teams/show.html.haml
@@ -11,6 +11,9 @@ @@ -11,6 +11,9 @@
11 %p.nothing_here_message Projects activity will be displayed here 11 %p.nothing_here_message Projects activity will be displayed here
12 .loading.hide 12 .loading.hide
13 .side.span4 13 .side.span4
  14 + - if @team.description.present?
  15 + .description.well.light
  16 + = @team.description
14 = render "projects", projects: @projects 17 = render "projects", projects: @projects
15 %div 18 %div
16 %span.rss-icon 19 %span.rss-icon
config/gitlab.yml.example
1 # # # # # # # # # # # # # # # # # # 1 # # # # # # # # # # # # # # # # # #
2 -# Gitlab application config file # 2 +# GitLab application config file #
3 # # # # # # # # # # # # # # # # # # 3 # # # # # # # # # # # # # # # # # #
4 # 4 #
5 # How to use: 5 # How to use:
@@ -37,9 +37,25 @@ production: &amp;base @@ -37,9 +37,25 @@ production: &amp;base
37 # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled. 37 # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled.
38 # username_changing_enabled: false # default: true - User can change her username/namespace 38 # username_changing_enabled: false # default: true - User can change her username/namespace
39 39
  40 +
  41 + ## External issues trackers
  42 + issues_tracker:
  43 + redmine:
  44 + ## If not nil, link 'Issues' on project page will be replaced with this
  45 + ## Use placeholders:
  46 + ## :project_id - GitLab project identifier
  47 + ## :issues_tracker_id - Project Name or Id in external issue tracker
  48 + project_url: "http://redmine.sample/projects/:issues_tracker_id"
  49 + ## If not nil, links from /#\d/ entities from commit messages will replaced with this
  50 + ## Use placeholders:
  51 + ## :project_id - GitLab project identifier
  52 + ## :issues_tracker_id - Project Name or Id in external issue tracker
  53 + ## :id - Issue id (from commit messages)
  54 + issues_url: "http://redmine.sample/issues/:id"
  55 +
40 ## Gravatar 56 ## Gravatar
41 gravatar: 57 gravatar:
42 - enabled: true # Use user avatar images from Gravatar.com (default: true) 58 + enabled: true # Use user avatar image from Gravatar.com (default: true)
43 # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm 59 # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
44 # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm 60 # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
45 61
@@ -60,22 +76,21 @@ production: &amp;base @@ -60,22 +76,21 @@ production: &amp;base
60 bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' 76 bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
61 password: '_the_password_of_the_bind_user' 77 password: '_the_password_of_the_bind_user'
62 78
63 - ## Omniauth settings 79 + ## OmniAuth settings
64 omniauth: 80 omniauth:
65 - # Enable ability for users  
66 - # Allow logging in via Twitter, Google, etc. using Omniauth providers 81 + # Allow login via Twitter, Google, etc. using OmniAuth providers
67 enabled: false 82 enabled: false
68 83
69 # CAUTION! 84 # CAUTION!
70 - # This allows users to login without having a user account first (default: false) 85 + # This allows users to login without having a user account first (default: false).
71 # User accounts will be created automatically when authentication was successful. 86 # User accounts will be created automatically when authentication was successful.
72 allow_single_sign_on: false 87 allow_single_sign_on: false
73 - # Locks down those users until they have been cleared by the admin (default: true) 88 + # Locks down those users until they have been cleared by the admin (default: true).
74 block_auto_created_users: true 89 block_auto_created_users: true
75 90
76 ## Auth providers 91 ## Auth providers
77 - # Uncomment the lines and fill in the data of the auth provider you want to use  
78 - # If your favorite auth provider is not listed you can user others: 92 + # Uncomment the following lines and fill in the data of the auth provider you want to use
  93 + # If your favorite auth provider is not listed you can use others:
79 # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers 94 # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers
80 # The 'app_id' and 'app_secret' parameters are always passed as the first two 95 # The 'app_id' and 'app_secret' parameters are always passed as the first two
81 # arguments, followed by optional 'args' which can be either a hash or an array. 96 # arguments, followed by optional 'args' which can be either a hash or an array.
@@ -114,7 +129,7 @@ production: &amp;base @@ -114,7 +129,7 @@ production: &amp;base
114 upload_pack: true 129 upload_pack: true
115 receive_pack: true 130 receive_pack: true
116 131
117 - # If you use non-standart ssh port you need to specify it 132 + # If you use non-standard ssh port you need to specify it
118 # ssh_port: 22 133 # ssh_port: 22
119 134
120 ## Git settings 135 ## Git settings
@@ -122,10 +137,10 @@ production: &amp;base @@ -122,10 +137,10 @@ production: &amp;base
122 # Use the default values unless you really know what you are doing 137 # Use the default values unless you really know what you are doing
123 git: 138 git:
124 bin_path: /usr/bin/git 139 bin_path: /usr/bin/git
125 - # Max size of git object like commit, in bytes  
126 - # This value can be increased if you have a very large commits 140 + # Max size of a git object (e.g. a commit), in bytes
  141 + # This value can be increased if you have very large commits
127 max_size: 5242880 # 5.megabytes 142 max_size: 5242880 # 5.megabytes
128 - # Git timeout to read commit, in seconds 143 + # Git timeout to read a commit, in seconds
129 timeout: 10 144 timeout: 10
130 145
131 development: 146 development:
@@ -133,6 +148,10 @@ development: @@ -133,6 +148,10 @@ development:
133 148
134 test: 149 test:
135 <<: *base 150 <<: *base
  151 + issues_tracker:
  152 + redmine:
  153 + project_url: "http://redmine/projects/:issues_tracker_id"
  154 + issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
136 155
137 staging: 156 staging:
138 <<: *base 157 <<: *base
config/initializers/1_settings.rb
@@ -42,6 +42,8 @@ Settings[&#39;omniauth&#39;] ||= Settingslogic.new({}) @@ -42,6 +42,8 @@ Settings[&#39;omniauth&#39;] ||= Settingslogic.new({})
42 Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil? 42 Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
43 Settings.omniauth['providers'] ||= [] 43 Settings.omniauth['providers'] ||= []
44 44
  45 +Settings['issues_tracker'] ||= {}
  46 +
45 # 47 #
46 # GitLab 48 # GitLab
47 # 49 #
@@ -50,7 +52,7 @@ Settings.gitlab[&#39;default_projects_limit&#39;] ||= 10 @@ -50,7 +52,7 @@ Settings.gitlab[&#39;default_projects_limit&#39;] ||= 10
50 Settings.gitlab['host'] ||= 'localhost' 52 Settings.gitlab['host'] ||= 'localhost'
51 Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? 53 Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
52 Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 54 Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
53 -Settings.gitlab['relative_url_root'] ||= '' 55 +Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
54 Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http" 56 Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
55 Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}" 57 Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}"
56 Settings.gitlab['support_email'] ||= Settings.gitlab.email_from 58 Settings.gitlab['support_email'] ||= Settings.gitlab.email_from
config/initializers/devise.rb
@@ -99,7 +99,7 @@ Devise.setup do |config| @@ -99,7 +99,7 @@ Devise.setup do |config|
99 99
100 # ==> Configuration for :validatable 100 # ==> Configuration for :validatable
101 # Range for password length. Default is 6..128. 101 # Range for password length. Default is 6..128.
102 - # config.password_length = 6..128 102 + config.password_length = 6..128
103 103
104 # Email regex used to validate email formats. It simply asserts that 104 # Email regex used to validate email formats. It simply asserts that
105 # an one (and only one) @ exists in the given string. This is mainly 105 # an one (and only one) @ exists in the given string. This is mainly
config/locales/devise.en.yml
@@ -17,6 +17,7 @@ en: @@ -17,6 +17,7 @@ en:
17 unauthenticated: 'You need to sign in before continuing.' 17 unauthenticated: 'You need to sign in before continuing.'
18 unconfirmed: 'You have to confirm your account before continuing.' 18 unconfirmed: 'You have to confirm your account before continuing.'
19 locked: 'Your account is locked.' 19 locked: 'Your account is locked.'
  20 + not_found_in_database: 'Invalid email or password.'
20 invalid: 'Invalid email or password.' 21 invalid: 'Invalid email or password.'
21 invalid_token: 'Invalid authentication token.' 22 invalid_token: 'Invalid authentication token.'
22 timeout: 'Your session expired, please sign in again to continue.' 23 timeout: 'Your session expired, please sign in again to continue.'
config/routes.rb
@@ -49,7 +49,7 @@ Gitlab::Application.routes.draw do @@ -49,7 +49,7 @@ Gitlab::Application.routes.draw do
49 # 49 #
50 # Attachments serving 50 # Attachments serving
51 # 51 #
52 - get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /[a-zA-Z.0-9_\-\+]+/ } 52 + get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /.+/ }
53 53
54 # 54 #
55 # Admin Area 55 # Admin Area
db/migrate/20130123114545_add_issues_tracker_to_project.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddIssuesTrackerToProject < ActiveRecord::Migration
  2 + def change
  3 + add_column :projects, :issues_tracker, :string, default: :gitlab, null: false
  4 + end
  5 +end
db/migrate/20130206084024_add_description_to_namsespace.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddDescriptionToNamsespace < ActiveRecord::Migration
  2 + def change
  3 + add_column :namespaces, :description, :string, default: '', null: false
  4 + end
  5 +end
db/migrate/20130207104426_add_description_to_teams.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddDescriptionToTeams < ActiveRecord::Migration
  2 + def change
  3 + add_column :user_teams, :description, :string, default: '', null: false
  4 + end
  5 +end
db/migrate/20130211085435_add_issues_tracker_id_to_project.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddIssuesTrackerIdToProject < ActiveRecord::Migration
  2 + def change
  3 + add_column :projects, :issues_tracker_id, :string
  4 + end
  5 +end
db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
1 class ConvertClosedToStateInIssue < ActiveRecord::Migration 1 class ConvertClosedToStateInIssue < ActiveRecord::Migration
2 def up 2 def up
3 Issue.transaction do 3 Issue.transaction do
4 - Issue.where(closed: true).update_all("state = 'closed'")  
5 - Issue.where(closed: false).update_all("state = 'opened'") 4 + Issue.where(closed: true).update_all(state: :closed)
  5 + Issue.where(closed: false).update_all(state: :opened)
6 end 6 end
7 end 7 end
8 8
9 def down 9 def down
10 Issue.transaction do 10 Issue.transaction do
11 - Issue.where(state: :closed).update_all("closed = 1") 11 + Issue.where(state: :closed).update_all(closed: true)
12 end 12 end
13 end 13 end
14 end 14 end
db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
1 class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration 1 class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
2 def up 2 def up
3 MergeRequest.transaction do 3 MergeRequest.transaction do
4 - MergeRequest.where(closed: true, merged: true).update_all("state = 'merged'")  
5 - MergeRequest.where(closed: true, merged: true).update_all("state = 'closed'")  
6 - MergeRequest.where(closed: false).update_all("state = 'opened'") 4 + MergeRequest.where(closed: true, merged: true).update_all(state: :merged)
  5 + MergeRequest.where(closed: true, merged: false).update_all(state: :closed)
  6 + MergeRequest.where(closed: false).update_all(state: :opened)
7 end 7 end
8 end 8 end
9 9
db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
1 class ConvertClosedToStateInMilestone < ActiveRecord::Migration 1 class ConvertClosedToStateInMilestone < ActiveRecord::Migration
2 def up 2 def up
3 Milestone.transaction do 3 Milestone.transaction do
4 - Milestone.where(closed: false).update_all("state = 'opened'")  
5 - Milestone.where(closed: false).update_all("state = 'active'") 4 + Milestone.where(closed: true).update_all(state: :closed)
  5 + Milestone.where(closed: false).update_all(state: :active)
6 end 6 end
7 end 7 end
8 8
9 def down 9 def down
10 Milestone.transaction do 10 Milestone.transaction do
11 - Milestone.where(state: :closed).update_all("closed = 1") 11 + Milestone.where(state: :closed).update_all(closed: true)
12 end 12 end
13 end 13 end
14 end 14 end
db/migrate/20130304104623_add_state_to_user.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddStateToUser < ActiveRecord::Migration
  2 + def change
  3 + add_column :users, :state, :string
  4 + end
  5 +end
db/migrate/20130304104740_convert_blocked_to_state.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class ConvertBlockedToState < ActiveRecord::Migration
  2 + def up
  3 + User.transaction do
  4 + User.where(blocked: true).update_all(state: :blocked)
  5 + User.where(blocked: false).update_all(state: :active)
  6 + end
  7 + end
  8 +
  9 + def down
  10 + User.transaction do
  11 + User.where(state: :blocked).update_all(blocked: :true)
  12 + end
  13 + end
  14 +end
db/migrate/20130304105317_remove_blocked_from_user.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class RemoveBlockedFromUser < ActiveRecord::Migration
  2 + def up
  3 + remove_column :users, :blocked
  4 + end
  5 +
  6 + def down
  7 + add_column :users, :blocked, :boolean
  8 + end
  9 +end
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
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 => 20130220133245) do 14 +ActiveRecord::Schema.define(:version => 20130304105317) do
15 15
16 create_table "events", :force => true do |t| 16 create_table "events", :force => true do |t|
17 t.string "target_type" 17 t.string "target_type"
@@ -112,6 +112,7 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do @@ -112,6 +112,7 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do
112 t.datetime "created_at", :null => false 112 t.datetime "created_at", :null => false
113 t.datetime "updated_at", :null => false 113 t.datetime "updated_at", :null => false
114 t.string "type" 114 t.string "type"
  115 + t.string "description", :default => "", :null => false
115 end 116 end
116 117
117 add_index "namespaces", ["name"], :name => "index_namespaces_on_name" 118 add_index "namespaces", ["name"], :name => "index_namespaces_on_name"
@@ -152,6 +153,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do @@ -152,6 +153,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do
152 t.boolean "wiki_enabled", :default => true, :null => false 153 t.boolean "wiki_enabled", :default => true, :null => false
153 t.integer "namespace_id" 154 t.integer "namespace_id"
154 t.boolean "public", :default => false, :null => false 155 t.boolean "public", :default => false, :null => false
  156 + t.string "issues_tracker", :default => "gitlab", :null => false
  157 + t.string "issues_tracker_id"
155 end 158 end
156 159
157 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" 160 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
@@ -232,6 +235,7 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do @@ -232,6 +235,7 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do
232 t.integer "owner_id" 235 t.integer "owner_id"
233 t.datetime "created_at", :null => false 236 t.datetime "created_at", :null => false
234 t.datetime "updated_at", :null => false 237 t.datetime "updated_at", :null => false
  238 + t.string "description", :default => "", :null => false
235 end 239 end
236 240
237 create_table "users", :force => true do |t| 241 create_table "users", :force => true do |t|
@@ -257,7 +261,6 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do @@ -257,7 +261,6 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do
257 t.boolean "dark_scheme", :default => false, :null => false 261 t.boolean "dark_scheme", :default => false, :null => false
258 t.integer "theme_id", :default => 1, :null => false 262 t.integer "theme_id", :default => 1, :null => false
259 t.string "bio" 263 t.string "bio"
260 - t.boolean "blocked", :default => false, :null => false  
261 t.integer "failed_attempts", :default => 0 264 t.integer "failed_attempts", :default => 0
262 t.datetime "locked_at" 265 t.datetime "locked_at"
263 t.string "extern_uid" 266 t.string "extern_uid"
@@ -265,10 +268,10 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do @@ -265,10 +268,10 @@ ActiveRecord::Schema.define(:version =&gt; 20130220133245) do
265 t.string "username" 268 t.string "username"
266 t.boolean "can_create_group", :default => true, :null => false 269 t.boolean "can_create_group", :default => true, :null => false
267 t.boolean "can_create_team", :default => true, :null => false 270 t.boolean "can_create_team", :default => true, :null => false
  271 + t.string "state"
268 end 272 end
269 273
270 add_index "users", ["admin"], :name => "index_users_on_admin" 274 add_index "users", ["admin"], :name => "index_users_on_admin"
271 - add_index "users", ["blocked"], :name => "index_users_on_blocked"  
272 add_index "users", ["email"], :name => "index_users_on_email", :unique => true 275 add_index "users", ["email"], :name => "index_users_on_email", :unique => true
273 add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true 276 add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true
274 add_index "users", ["name"], :name => "index_users_on_name" 277 add_index "users", ["name"], :name => "index_users_on_name"
doc/install/databases.md
@@ -12,7 +12,7 @@ GitLab supports the following databases: @@ -12,7 +12,7 @@ GitLab supports the following databases:
12 sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev 12 sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
13 13
14 # Login to MySQL 14 # Login to MySQL
15 - $ mysql -u root -p 15 + mysql -u root -p
16 16
17 # Create a user for GitLab. (change $password to a real password) 17 # Create a user for GitLab. (change $password to a real password)
18 mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password'; 18 mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password';
doc/install/installation.md
1 -This installation guide was created for Debian/Ubuntu and tested on it.  
2 -  
3 -Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements. 1 +This installation guide was created for Debian/Ubuntu and tested on it. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements.
4 2
  3 +This installation guide is recommended to set up a production server. If you want a development environment please use the [Vargrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) since it makes it much easier to set up all the dependencies for integration testing.
5 4
6 **Important Note:** 5 **Important Note:**
7 The following steps have been known to work. 6 The following steps have been known to work.
@@ -92,21 +91,29 @@ Create a `git` user for Gitlab: @@ -92,21 +91,29 @@ Create a `git` user for Gitlab:
92 91
93 sudo adduser --disabled-login --gecos 'GitLab' git 92 sudo adduser --disabled-login --gecos 'GitLab' git
94 93
  94 +
95 # 4. GitLab shell 95 # 4. GitLab shell
96 96
97 - # Login as git 97 +GitLab Shell is a ssh access and repository management software developed specially for GitLab.
  98 +
  99 + # Login as git
98 sudo su git 100 sudo su git
99 101
100 - # Go to home directory 102 + # Go to home directory
101 cd /home/git 103 cd /home/git
102 104
103 # Clone gitlab shell 105 # Clone gitlab shell
104 git clone https://github.com/gitlabhq/gitlab-shell.git 106 git clone https://github.com/gitlabhq/gitlab-shell.git
105 107
106 - # Setup  
107 cd gitlab-shell 108 cd gitlab-shell
108 cp config.yml.example config.yml 109 cp config.yml.example config.yml
109 - ./bin/install 110 +
  111 + # Edit config and replace gitlab_url
  112 + # with something like 'http://domain.com/'
  113 + vim config.yml
  114 +
  115 + # Do setup
  116 + ./bin/install
110 117
111 118
112 # 5. Database 119 # 5. Database
@@ -124,9 +131,9 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install @@ -124,9 +131,9 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
124 # Clone GitLab repository 131 # Clone GitLab repository
125 sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab 132 sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab
126 133
127 - # Go to gitlab dir 134 + # Go to gitlab dir
128 cd /home/git/gitlab 135 cd /home/git/gitlab
129 - 136 +
130 # Checkout to stable release 137 # Checkout to stable release
131 sudo -u git -H git checkout 5-0-stable 138 sudo -u git -H git checkout 5-0-stable
132 139
@@ -157,7 +164,7 @@ do so with caution! @@ -157,7 +164,7 @@ do so with caution!
157 # Create directory for pids and make sure GitLab can write to it 164 # Create directory for pids and make sure GitLab can write to it
158 sudo -u git -H mkdir tmp/pids/ 165 sudo -u git -H mkdir tmp/pids/
159 sudo chmod -R u+rwX tmp/pids/ 166 sudo chmod -R u+rwX tmp/pids/
160 - 167 +
161 # Copy the example Unicorn config 168 # Copy the example Unicorn config
162 sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb 169 sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
163 170
@@ -188,7 +195,7 @@ Make sure to update username/password in config/database.yml. @@ -188,7 +195,7 @@ Make sure to update username/password in config/database.yml.
188 195
189 196
190 ## Initialise Database and Activate Advanced Features 197 ## Initialise Database and Activate Advanced Features
191 - 198 +
192 sudo -u git -H bundle exec rake db:setup RAILS_ENV=production 199 sudo -u git -H bundle exec rake db:setup RAILS_ENV=production
193 sudo -u git -H bundle exec rake db:seed_fu RAILS_ENV=production 200 sudo -u git -H bundle exec rake db:seed_fu RAILS_ENV=production
194 sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production 201 sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
@@ -286,7 +293,7 @@ a different host, you can configure its connection string via the @@ -286,7 +293,7 @@ a different host, you can configure its connection string via the
286 ## Custom SSH Connection 293 ## Custom SSH Connection
287 294
288 If you are running SSH on a non-standard port, you must change the gitlab user's SSH config. 295 If you are running SSH on a non-standard port, you must change the gitlab user's SSH config.
289 - 296 +
290 # Add to /home/git/.ssh/config 297 # Add to /home/git/.ssh/config
291 host localhost # Give your setup a name (here: override localhost) 298 host localhost # Give your setup a name (here: override localhost)
292 user git # Your remote git user 299 user git # Your remote git user
features/project/network.feature
@@ -7,3 +7,19 @@ Feature: Project Network Graph @@ -7,3 +7,19 @@ Feature: Project Network Graph
7 @javascript 7 @javascript
8 Scenario: I should see project network 8 Scenario: I should see project network
9 Then page should have network graph 9 Then page should have network graph
  10 + And page should select "master" in select box
  11 + And page should have "master" on graph
  12 +
  13 + @javascript
  14 + Scenario: I should switch ref to "stable"
  15 + When I switch ref to "stable"
  16 + Then page should have network graph
  17 + And page should select "stable" in select box
  18 + And page should have "stable" on graph
  19 +
  20 + @javascript
  21 + Scenario: I should looking for a commit by SHA of "v2.1.0"
  22 + When I looking for a commit by SHA of "v2.1.0"
  23 + Then page should have network graph
  24 + And page should select "master" in select box
  25 + And page should have "v2.1.0" on graph
features/steps/admin/admin_groups.rb
@@ -25,11 +25,13 @@ class AdminGroups &lt; Spinach::FeatureSteps @@ -25,11 +25,13 @@ class AdminGroups &lt; Spinach::FeatureSteps
25 25
26 And 'submit form with new group info' do 26 And 'submit form with new group info' do
27 fill_in 'group_name', :with => 'gitlab' 27 fill_in 'group_name', :with => 'gitlab'
  28 + fill_in 'group_description', :with => 'Group description'
28 click_button "Create group" 29 click_button "Create group"
29 end 30 end
30 31
31 Then 'I should see newly created group' do 32 Then 'I should see newly created group' do
32 page.should have_content "Group: gitlab" 33 page.should have_content "Group: gitlab"
  34 + page.should have_content "Group description"
33 end 35 end
34 36
35 Then 'I should be redirected to group page' do 37 Then 'I should be redirected to group page' do
features/steps/admin/admin_teams.rb
@@ -18,6 +18,7 @@ class AdminTeams &lt; Spinach::FeatureSteps @@ -18,6 +18,7 @@ class AdminTeams &lt; Spinach::FeatureSteps
18 18
19 And 'submit form with new team info' do 19 And 'submit form with new team info' do
20 fill_in 'user_team_name', with: 'gitlab' 20 fill_in 'user_team_name', with: 'gitlab'
  21 + fill_in 'user_team_description', with: 'description'
21 click_button 'Create team' 22 click_button 'Create team'
22 end 23 end
23 24
@@ -27,6 +28,7 @@ class AdminTeams &lt; Spinach::FeatureSteps @@ -27,6 +28,7 @@ class AdminTeams &lt; Spinach::FeatureSteps
27 28
28 And 'I should see newly created team' do 29 And 'I should see newly created team' do
29 page.should have_content "Team: gitlab" 30 page.should have_content "Team: gitlab"
  31 + page.should have_content "description"
30 end 32 end
31 33
32 When 'I visit admin teams page' do 34 When 'I visit admin teams page' do
features/steps/group/group.rb
@@ -28,7 +28,7 @@ class Groups &lt; Spinach::FeatureSteps @@ -28,7 +28,7 @@ class Groups &lt; Spinach::FeatureSteps
28 28
29 Then 'I should see merge requests from this group assigned to me' do 29 Then 'I should see merge requests from this group assigned to me' do
30 assigned_to_me(:merge_requests).each do |issue| 30 assigned_to_me(:merge_requests).each do |issue|
31 - page.should have_content issue.title 31 + page.should have_content issue.title[0..80]
32 end 32 end
33 end 33 end
34 34
@@ -69,12 +69,14 @@ class Groups &lt; Spinach::FeatureSteps @@ -69,12 +69,14 @@ class Groups &lt; Spinach::FeatureSteps
69 end 69 end
70 70
71 And 'submit form with new group info' do 71 And 'submit form with new group info' do
72 - fill_in 'group_name', :with => 'Samurai' 72 + fill_in 'group_name', with: 'Samurai'
  73 + fill_in 'group_description', with: 'Tokugawa Shogunate'
73 click_button "Create group" 74 click_button "Create group"
74 end 75 end
75 76
76 Then 'I should see newly created group' do 77 Then 'I should see newly created group' do
77 page.should have_content "Samurai" 78 page.should have_content "Samurai"
  79 + page.should have_content "Tokugawa Shogunate"
78 page.should have_content "You will only see events from projects in this group" 80 page.should have_content "You will only see events from projects in this group"
79 end 81 end
80 82
features/steps/project/project_merge_requests.rb
@@ -25,8 +25,8 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -25,8 +25,8 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
25 end 25 end
26 26
27 Then 'I should see closed merge request "Bug NS-04"' do 27 Then 'I should see closed merge request "Bug NS-04"' do
28 - mr = MergeRequest.find_by_title("Bug NS-04")  
29 - mr.closed?.should be_true 28 + merge_request = MergeRequest.find_by_title!("Bug NS-04")
  29 + merge_request.closed?.should be_true
30 page.should have_content "Closed by" 30 page.should have_content "Closed by"
31 end 31 end
32 32
@@ -63,7 +63,6 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -63,7 +63,6 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
63 end 63 end
64 64
65 And 'project "Shop" have "Bug NS-04" open merge request' do 65 And 'project "Shop" have "Bug NS-04" open merge request' do
66 - project = Project.find_by_name("Shop")  
67 create(:merge_request, 66 create(:merge_request,
68 title: "Bug NS-04", 67 title: "Bug NS-04",
69 project: project, 68 project: project,
@@ -71,7 +70,6 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -71,7 +70,6 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
71 end 70 end
72 71
73 And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do 72 And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
74 - project = Project.find_by_name("Shop")  
75 create(:merge_request_with_diffs, 73 create(:merge_request_with_diffs,
76 title: "Bug NS-05", 74 title: "Bug NS-05",
77 project: project, 75 project: project,
@@ -79,7 +77,6 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -79,7 +77,6 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
79 end 77 end
80 78
81 And 'project "Shop" have "Feature NS-03" closed merge request' do 79 And 'project "Shop" have "Feature NS-03" closed merge request' do
82 - project = Project.find_by_name("Shop")  
83 create(:closed_merge_request, 80 create(:closed_merge_request,
84 title: "Feature NS-03", 81 title: "Feature NS-03",
85 project: project, 82 project: project,
@@ -87,18 +84,16 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -87,18 +84,16 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
87 end 84 end
88 85
89 And 'I switch to the diff tab' do 86 And 'I switch to the diff tab' do
90 - mr = MergeRequest.find_by_title("Bug NS-05")  
91 - visit diffs_project_merge_request_path(mr.project, mr) 87 + visit diffs_project_merge_request_path(project, merge_request)
92 end 88 end
93 89
94 And 'I switch to the merge request\'s comments tab' do 90 And 'I switch to the merge request\'s comments tab' do
95 - mr = MergeRequest.find_by_title("Bug NS-05")  
96 - visit project_merge_request_path(mr.project, mr) 91 + visit project_merge_request_path(project, merge_request)
97 end 92 end
98 93
99 And 'I click on the first commit in the merge request' do 94 And 'I click on the first commit in the merge request' do
100 - mr = MergeRequest.find_by_title("Bug NS-05")  
101 - click_link mr.commits.first.short_id(8) 95 +
  96 + click_link merge_request.commits.first.short_id(8)
102 end 97 end
103 98
104 And 'I leave a comment on the diff page' do 99 And 'I leave a comment on the diff page' do
@@ -121,8 +116,7 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -121,8 +116,7 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
121 end 116 end
122 117
123 Then 'I should see a discussion has started on line 185' do 118 Then 'I should see a discussion has started on line 185' do
124 - mr = MergeRequest.find_by_title("Bug NS-05")  
125 - first_commit = mr.commits.first 119 + first_commit = merge_request.commits.first
126 first_diff = first_commit.diffs.first 120 first_diff = first_commit.diffs.first
127 page.should have_content "#{current_user.name} started a discussion on this merge request diff" 121 page.should have_content "#{current_user.name} started a discussion on this merge request diff"
128 page.should have_content "#{first_diff.b_path}:L185" 122 page.should have_content "#{first_diff.b_path}:L185"
@@ -130,8 +124,7 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -130,8 +124,7 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
130 end 124 end
131 125
132 Then 'I should see a discussion has started on commit bcf03b5de6c:L185' do 126 Then 'I should see a discussion has started on commit bcf03b5de6c:L185' do
133 - mr = MergeRequest.find_by_title("Bug NS-05")  
134 - first_commit = mr.commits.first 127 + first_commit = merge_request.commits.first
135 first_diff = first_commit.diffs.first 128 first_diff = first_commit.diffs.first
136 page.should have_content "#{current_user.name} started a discussion on commit" 129 page.should have_content "#{current_user.name} started a discussion on commit"
137 page.should have_content first_commit.short_id(8) 130 page.should have_content first_commit.short_id(8)
@@ -140,12 +133,19 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -140,12 +133,19 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
140 end 133 end
141 134
142 Then 'I should see a discussion has started on commit bcf03b5de6c' do 135 Then 'I should see a discussion has started on commit bcf03b5de6c' do
143 - mr = MergeRequest.find_by_title("Bug NS-05")  
144 - first_commit = mr.st_commits.first 136 + first_commit = merge_request.st_commits.first
145 first_diff = first_commit.diffs.first 137 first_diff = first_commit.diffs.first
146 page.should have_content "#{current_user.name} started a discussion on commit bcf03b5de6c" 138 page.should have_content "#{current_user.name} started a discussion on commit bcf03b5de6c"
147 page.should have_content first_commit.short_id(8) 139 page.should have_content first_commit.short_id(8)
148 page.should have_content "One comment to rule them all" 140 page.should have_content "One comment to rule them all"
149 page.should have_content "#{first_diff.b_path}:L185" 141 page.should have_content "#{first_diff.b_path}:L185"
150 end 142 end
  143 +
  144 + def project
  145 + @project ||= Project.find_by_name!("Shop")
  146 + end
  147 +
  148 + def merge_request
  149 + @merge_request ||= MergeRequest.find_by_title!("Bug NS-05")
  150 + end
151 end 151 end
features/steps/project/project_network_graph.rb
@@ -4,16 +4,51 @@ class ProjectNetworkGraph &lt; Spinach::FeatureSteps @@ -4,16 +4,51 @@ class ProjectNetworkGraph &lt; Spinach::FeatureSteps
4 4
5 Then 'page should have network graph' do 5 Then 'page should have network graph' do
6 page.should have_content "Project Network Graph" 6 page.should have_content "Project Network Graph"
7 - within ".graph" do  
8 - page.should have_content "master"  
9 - end 7 + page.should have_selector ".graph"
10 end 8 end
11 9
12 - And 'I visit project "Shop" network page' do 10 + When 'I visit project "Shop" network page' do
13 # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650) 11 # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650)
14 - Gitlab::Graph::JsonBuilder.stub(max_count: 10) 12 + Graph::JsonBuilder.stub(max_count: 10)
15 13
16 project = Project.find_by_name("Shop") 14 project = Project.find_by_name("Shop")
17 visit project_graph_path(project, "master") 15 visit project_graph_path(project, "master")
18 end 16 end
  17 +
  18 + And 'page should select "master" in select box' do
  19 + page.should have_selector '#ref_chzn span', :text => "master"
  20 + end
  21 +
  22 + And 'page should have "master" on graph' do
  23 + within '.graph' do
  24 + page.should have_content 'master'
  25 + end
  26 + end
  27 +
  28 + And 'I switch ref to "stable"' do
  29 + page.select 'stable', :from => 'ref'
  30 + end
  31 +
  32 + And 'page should select "stable" in select box' do
  33 + page.should have_selector '#ref_chzn span', :text => "stable"
  34 + end
  35 +
  36 + And 'page should have "stable" on graph' do
  37 + within '.graph' do
  38 + page.should have_content 'stable'
  39 + end
  40 + end
  41 +
  42 + And 'I looking for a commit by SHA of "v2.1.0"' do
  43 + within ".content .search" do
  44 + fill_in 'q', :with => '98d6492'
  45 + find('button').click
  46 + end
  47 + end
  48 +
  49 + And 'page should have "v2.1.0" on graph' do
  50 + within '.graph' do
  51 + page.should have_content 'v2.1.0'
  52 + end
  53 + end
19 end 54 end
features/steps/shared/paths.rb
@@ -143,7 +143,7 @@ module SharedPaths @@ -143,7 +143,7 @@ module SharedPaths
143 143
144 Given "I visit my project's network page" do 144 Given "I visit my project's network page" do
145 # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650) 145 # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650)
146 - Gitlab::Graph::JsonBuilder.stub(max_count: 10) 146 + Graph::JsonBuilder.stub(max_count: 10)
147 147
148 visit project_graph_path(@project, root_ref) 148 visit project_graph_path(@project, root_ref)
149 end 149 end
features/steps/userteams/userteams.rb
@@ -44,9 +44,16 @@ class Userteams &lt; Spinach::FeatureSteps @@ -44,9 +44,16 @@ class Userteams &lt; Spinach::FeatureSteps
44 44
45 And 'I submit form with new team info' do 45 And 'I submit form with new team info' do
46 fill_in 'name', with: 'gitlab' 46 fill_in 'name', with: 'gitlab'
  47 +
  48 + fill_in 'user_team_description', with: 'team description'
47 click_button 'Create team' 49 click_button 'Create team'
48 end 50 end
49 51
  52 + And 'I should see newly created team' do
  53 + page.should have_content "gitlab"
  54 + page.should have_content "team description"
  55 + end
  56 +
50 Then 'I should be redirected to new team page' do 57 Then 'I should be redirected to new team page' do
51 team = UserTeam.last 58 team = UserTeam.last
52 current_path.should == team_path(team) 59 current_path.should == team_path(team)
features/support/env.rb
@@ -34,6 +34,7 @@ Spinach.hooks.before_scenario do @@ -34,6 +34,7 @@ Spinach.hooks.before_scenario do
34 Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path')) 34 Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
35 FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path 35 FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
36 FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path 36 FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
  37 + DatabaseCleaner.start
37 end 38 end
38 39
39 Spinach.hooks.after_scenario do 40 Spinach.hooks.after_scenario do
features/teams/team.feature
@@ -20,6 +20,7 @@ Feature: UserTeams @@ -20,6 +20,7 @@ Feature: UserTeams
20 When I click to "New team" link 20 When I click to "New team" link
21 And I submit form with new team info 21 And I submit form with new team info
22 Then I should be redirected to new team page 22 Then I should be redirected to new team page
  23 + Then I should see newly created team
23 24
24 Scenario: I should see team dashboard list 25 Scenario: I should see team dashboard list
25 When I have teams with projects and members 26 When I have teams with projects and members
lib/api/entities.rb
@@ -2,11 +2,11 @@ module Gitlab @@ -2,11 +2,11 @@ module Gitlab
2 module Entities 2 module Entities
3 class User < Grape::Entity 3 class User < Grape::Entity
4 expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter, 4 expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter,
5 - :dark_scheme, :theme_id, :blocked, :created_at, :extern_uid, :provider 5 + :dark_scheme, :theme_id, :state, :created_at, :extern_uid, :provider
6 end 6 end
7 7
8 class UserBasic < Grape::Entity 8 class UserBasic < Grape::Entity
9 - expose :id, :username, :email, :name, :blocked, :created_at 9 + expose :id, :username, :email, :name, :state, :created_at
10 end 10 end
11 11
12 class UserLogin < UserBasic 12 class UserLogin < UserBasic
lib/api/projects.rb
@@ -52,8 +52,8 @@ module Gitlab @@ -52,8 +52,8 @@ module Gitlab
52 :issues_enabled, 52 :issues_enabled,
53 :wall_enabled, 53 :wall_enabled,
54 :merge_requests_enabled, 54 :merge_requests_enabled,
55 - :wiki_enabled]  
56 - 55 + :wiki_enabled,
  56 + :namespace_id]
57 @project = ::Projects::CreateContext.new(current_user, attrs).execute 57 @project = ::Projects::CreateContext.new(current_user, attrs).execute
58 if @project.saved? 58 if @project.saved?
59 present @project, with: Entities::Project 59 present @project, with: Entities::Project
lib/extracts_path.rb
@@ -126,7 +126,7 @@ module ExtractsPath @@ -126,7 +126,7 @@ module ExtractsPath
126 @tree = TreeDecorator.new(@tree) 126 @tree = TreeDecorator.new(@tree)
127 127
128 raise InvalidPathError if @tree.invalid? 128 raise InvalidPathError if @tree.invalid?
129 - rescue NoMethodError, InvalidPathError 129 + rescue RuntimeError, NoMethodError, InvalidPathError
130 not_found! 130 not_found!
131 end 131 end
132 end 132 end
lib/gitlab/auth.rb
@@ -41,10 +41,12 @@ module Gitlab @@ -41,10 +41,12 @@ module Gitlab
41 password_confirmation: password, 41 password_confirmation: password,
42 projects_limit: Gitlab.config.gitlab.default_projects_limit, 42 projects_limit: Gitlab.config.gitlab.default_projects_limit,
43 }, as: :admin) 43 }, as: :admin)
  44 + @user.save!
  45 +
44 if Gitlab.config.omniauth['block_auto_created_users'] && !ldap 46 if Gitlab.config.omniauth['block_auto_created_users'] && !ldap
45 - @user.blocked = true 47 + @user.block
46 end 48 end
47 - @user.save! 49 +
48 @user 50 @user
49 end 51 end
50 52
lib/gitlab/graph/commit.rb
@@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
1 -require "grit"  
2 -  
3 -module Gitlab  
4 - module Graph  
5 - class Commit  
6 - include ActionView::Helpers::TagHelper  
7 -  
8 - attr_accessor :time, :space, :refs, :parent_spaces  
9 -  
10 - def initialize(commit)  
11 - @_commit = commit  
12 - @time = -1  
13 - @space = 0  
14 - @parent_spaces = []  
15 - end  
16 -  
17 - def method_missing(m, *args, &block)  
18 - @_commit.send(m, *args, &block)  
19 - end  
20 -  
21 - def to_graph_hash  
22 - h = {}  
23 - h[:parents] = self.parents.collect do |p|  
24 - [p.id,0,0]  
25 - end  
26 - h[:author] = {  
27 - name: author.name,  
28 - email: author.email  
29 - }  
30 - h[:time] = time  
31 - h[:space] = space  
32 - h[:parent_spaces] = parent_spaces  
33 - h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?  
34 - h[:id] = sha  
35 - h[:date] = date  
36 - h[:message] = message  
37 - h  
38 - end  
39 -  
40 - def add_refs(ref_cache, repo)  
41 - if ref_cache.empty?  
42 - repo.refs.each do |ref|  
43 - ref_cache[ref.commit.id] ||= []  
44 - ref_cache[ref.commit.id] << ref  
45 - end  
46 - end  
47 - @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)  
48 - @refs ||= []  
49 - end  
50 - end  
51 - end  
52 -end  
lib/gitlab/graph/json_builder.rb
@@ -1,268 +0,0 @@ @@ -1,268 +0,0 @@
1 -require "grit"  
2 -  
3 -module Gitlab  
4 - module Graph  
5 - class JsonBuilder  
6 - attr_accessor :days, :commits, :ref_cache, :repo  
7 -  
8 - def self.max_count  
9 - @max_count ||= 650  
10 - end  
11 -  
12 - def initialize project, ref, commit  
13 - @project = project  
14 - @ref = ref  
15 - @commit = commit  
16 - @repo = project.repo  
17 - @ref_cache = {}  
18 -  
19 - @commits = collect_commits  
20 - @days = index_commits  
21 - end  
22 -  
23 - def to_json(*args)  
24 - {  
25 - days: @days.compact.map { |d| [d.day, d.strftime("%b")] },  
26 - commits: @commits.map(&:to_graph_hash)  
27 - }.to_json(*args)  
28 - end  
29 -  
30 - protected  
31 -  
32 - # Get commits from repository  
33 - #  
34 - def collect_commits  
35 -  
36 - @commits = Grit::Commit.find_all(repo, nil, {topo_order: true, max_count: self.class.max_count, skip: to_commit}).dup  
37 -  
38 - # Decorate with app/models/commit.rb  
39 - @commits.map! { |commit| ::Commit.new(commit) }  
40 -  
41 - # Decorate with lib/gitlab/graph/commit.rb  
42 - @commits.map! { |commit| Gitlab::Graph::Commit.new(commit) }  
43 -  
44 - # add refs to each commit  
45 - @commits.each { |commit| commit.add_refs(ref_cache, repo) }  
46 -  
47 - @commits  
48 - end  
49 -  
50 - # Method is adding time and space on the  
51 - # list of commits. As well as returns date list  
52 - # corelated with time set on commits.  
53 - #  
54 - # @param [Array<Graph::Commit>] commits to index  
55 - #  
56 - # @return [Array<TimeDate>] list of commit dates corelated with time on commits  
57 - def index_commits  
58 - days, times = [], []  
59 - map = {}  
60 -  
61 - commits.reverse.each_with_index do |c,i|  
62 - c.time = i  
63 - days[i] = c.committed_date  
64 - map[c.id] = c  
65 - times[i] = c  
66 - end  
67 -  
68 - @_reserved = {}  
69 - days.each_index do |i|  
70 - @_reserved[i] = []  
71 - end  
72 -  
73 - commits_sort_by_ref.each do |commit|  
74 - if map.include? commit.id then  
75 - place_chain(map[commit.id], map)  
76 - end  
77 - end  
78 -  
79 - # find parent spaces for not overlap lines  
80 - times.each do |c|  
81 - c.parent_spaces.concat(find_free_parent_spaces(c, map, times))  
82 - end  
83 -  
84 - days  
85 - end  
86 -  
87 - # Skip count that the target commit is displayed in center.  
88 - def to_commit  
89 - commits = Grit::Commit.find_all(repo, nil, {topo_order: true})  
90 - commit_index = commits.index do |c|  
91 - c.id == @commit.id  
92 - end  
93 -  
94 - if commit_index && (self.class.max_count / 2 < commit_index) then  
95 - # get max index that commit is displayed in the center.  
96 - commit_index - self.class.max_count / 2  
97 - else  
98 - 0  
99 - end  
100 - end  
101 -  
102 - def commits_sort_by_ref  
103 - commits.sort do |a,b|  
104 - if include_ref?(a)  
105 - -1  
106 - elsif include_ref?(b)  
107 - 1  
108 - else  
109 - b.committed_date <=> a.committed_date  
110 - end  
111 - end  
112 - end  
113 -  
114 - def include_ref?(commit)  
115 - heads = commit.refs.select do |ref|  
116 - ref.is_a?(Grit::Head) or ref.is_a?(Grit::Remote) or ref.is_a?(Grit::Tag)  
117 - end  
118 -  
119 - heads.map! do |head|  
120 - head.name  
121 - end  
122 -  
123 - heads.include?(@ref)  
124 - end  
125 -  
126 - def find_free_parent_spaces(commit, map, times)  
127 - spaces = []  
128 -  
129 - commit.parents.each do |p|  
130 - if map.include?(p.id) then  
131 - parent = map[p.id]  
132 -  
133 - range = if commit.time < parent.time then  
134 - commit.time..parent.time  
135 - else  
136 - parent.time..commit.time  
137 - end  
138 -  
139 - space = if commit.space >= parent.space then  
140 - find_free_parent_space(range, parent.space, 1, commit.space, times)  
141 - else  
142 - find_free_parent_space(range, parent.space, -1, parent.space, times)  
143 - end  
144 -  
145 - mark_reserved(range, space)  
146 - spaces << space  
147 - end  
148 - end  
149 -  
150 - spaces  
151 - end  
152 -  
153 - def find_free_parent_space(range, space_base, space_step, space_default, times)  
154 - if is_overlap?(range, times, space_default) then  
155 - find_free_space(range, space_base, space_step)  
156 - else  
157 - space_default  
158 - end  
159 - end  
160 -  
161 - def is_overlap?(range, times, overlap_space)  
162 - range.each do |i|  
163 - if i != range.first &&  
164 - i != range.last &&  
165 - times[i].space == overlap_space then  
166 -  
167 - return true;  
168 - end  
169 - end  
170 -  
171 - false  
172 - end  
173 -  
174 - # Add space mark on commit and its parents  
175 - #  
176 - # @param [Graph::Commit] the commit object.  
177 - # @param [Hash<String,Graph::Commit>] map of commits  
178 - def place_chain(commit, map, parent_time = nil)  
179 - leaves = take_left_leaves(commit, map)  
180 - if leaves.empty?  
181 - return  
182 - end  
183 - # and mark it as reserved  
184 - min_time = leaves.last.time  
185 - max_space = 1  
186 - parents = leaves.last.parents.collect  
187 - parents.each do |p|  
188 - if map.include? p.id  
189 - parent = map[p.id]  
190 - if parent.time < min_time  
191 - min_time = parent.time  
192 - end  
193 - if max_space < parent.space then  
194 - max_space = parent.space  
195 - end  
196 - end  
197 - end  
198 - if parent_time.nil?  
199 - max_time = leaves.first.time  
200 - else  
201 - max_time = parent_time - 1  
202 - end  
203 -  
204 - time_range = leaves.last.time..leaves.first.time  
205 - space = find_free_space(time_range, max_space, 2)  
206 - leaves.each{|l| l.space = space}  
207 -  
208 - mark_reserved(min_time..max_time, space)  
209 -  
210 - # Visit branching chains  
211 - leaves.each do |l|  
212 - parents = l.parents.collect.select{|p| map.include? p.id and map[p.id].space.zero?}  
213 - for p in parents  
214 - place_chain(map[p.id], map, l.time)  
215 - end  
216 - end  
217 - end  
218 -  
219 - def mark_reserved(time_range, space)  
220 - for day in time_range  
221 - @_reserved[day].push(space)  
222 - end  
223 - end  
224 -  
225 - def find_free_space(time_range, space_base, space_step)  
226 - reserved = []  
227 - for day in time_range  
228 - reserved += @_reserved[day]  
229 - end  
230 - reserved.uniq!  
231 -  
232 - space = space_base  
233 - while reserved.include?(space) do  
234 - space += space_step  
235 - if space <= 0 then  
236 - space_step *= -1  
237 - space = space_base + space_step  
238 - end  
239 - end  
240 -  
241 - space  
242 - end  
243 -  
244 - # Takes most left subtree branch of commits  
245 - # which don't have space mark yet.  
246 - #  
247 - # @param [Graph::Commit] the commit object.  
248 - # @param [Hash<String,Graph::Commit>] map of commits  
249 - #  
250 - # @return [Array<Graph::Commit>] list of branch commits  
251 - def take_left_leaves(commit, map)  
252 - leaves = []  
253 - leaves.push(commit) if commit.space.zero?  
254 -  
255 - while true  
256 - return leaves if commit.parents.count.zero?  
257 - return leaves unless map.include? commit.parents.first.id  
258 -  
259 - commit = map[commit.parents.first.id]  
260 -  
261 - return leaves unless commit.space.zero?  
262 -  
263 - leaves.push(commit)  
264 - end  
265 - end  
266 - end  
267 - end  
268 -end  
lib/gitlab/markdown.rb
@@ -25,6 +25,8 @@ module Gitlab @@ -25,6 +25,8 @@ module Gitlab
25 # >> gfm(":trollface:") 25 # >> gfm(":trollface:")
26 # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" /> 26 # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
27 module Markdown 27 module Markdown
  28 + include IssuesHelper
  29 +
28 attr_reader :html_options 30 attr_reader :html_options
29 31
30 # Public: Parse the provided text with GitLab-Flavored Markdown 32 # Public: Parse the provided text with GitLab-Flavored Markdown
@@ -163,8 +165,11 @@ module Gitlab @@ -163,8 +165,11 @@ module Gitlab
163 end 165 end
164 166
165 def reference_issue(identifier) 167 def reference_issue(identifier)
166 - if issue = @project.issues.where(id: identifier).first  
167 - link_to("##{identifier}", project_issue_url(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}")) 168 + if @project.issue_exists? identifier
  169 + url = url_for_issue(identifier)
  170 + title = title_for_issue(identifier)
  171 +
  172 + link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}"))
168 end 173 end
169 end 174 end
170 175
lib/tasks/sidekiq.rake
1 namespace :sidekiq do 1 namespace :sidekiq do
2 desc "GITLAB | Stop sidekiq" 2 desc "GITLAB | Stop sidekiq"
3 task :stop do 3 task :stop do
4 - run "bundle exec sidekiqctl stop #{pidfile}" 4 + system "bundle exec sidekiqctl stop #{pidfile}"
5 end 5 end
6 6
7 desc "GITLAB | Start sidekiq" 7 desc "GITLAB | Start sidekiq"
8 task :start do 8 task :start do
9 - run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &" 9 + system "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
10 end 10 end
11 - 11 +
12 desc "GITLAB | Start sidekiq with launchd on Mac OS X" 12 desc "GITLAB | Start sidekiq with launchd on Mac OS X"
13 task :launchd do 13 task :launchd do
14 - run "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1" 14 + system "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1"
15 end 15 end
16 - 16 +
17 def pidfile 17 def pidfile
18 Rails.root.join("tmp", "pids", "sidekiq.pid") 18 Rails.root.join("tmp", "pids", "sidekiq.pid")
19 end 19 end
spec/factories.rb
@@ -29,6 +29,11 @@ FactoryGirl.define do @@ -29,6 +29,11 @@ FactoryGirl.define do
29 creator 29 creator
30 end 30 end
31 31
  32 + factory :redmine_project, parent: :project do
  33 + issues_tracker { "redmine" }
  34 + issues_tracker_id { "project_name_in_redmine" }
  35 + end
  36 +
32 factory :group do 37 factory :group do
33 sequence(:name) { |n| "group#{n}" } 38 sequence(:name) { |n| "group#{n}" }
34 path { name.downcase.gsub(/\s/, '_') } 39 path { name.downcase.gsub(/\s/, '_') }
spec/factories/user_teams.rb
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 FactoryGirl.define do 15 FactoryGirl.define do
16 factory :user_team do 16 factory :user_team do
17 sequence(:name) { |n| "team#{n}" } 17 sequence(:name) { |n| "team#{n}" }
  18 + sequence(:description) { |n| "team_description#{n}" }
18 path { name.downcase.gsub(/\s/, '_') } 19 path { name.downcase.gsub(/\s/, '_') }
19 owner 20 owner
20 end 21 end
spec/features/admin/admin_users_spec.rb
@@ -55,8 +55,8 @@ describe &quot;Admin::Users&quot; do @@ -55,8 +55,8 @@ describe &quot;Admin::Users&quot; do
55 user = User.last 55 user = User.last
56 email = ActionMailer::Base.deliveries.last 56 email = ActionMailer::Base.deliveries.last
57 email.subject.should have_content("Account was created") 57 email.subject.should have_content("Account was created")
58 - email.body.should have_content(user.email)  
59 - email.body.should have_content(@password) 58 + email.text_part.body.should have_content(user.email)
  59 + email.text_part.body.should have_content(@password)
60 end 60 end
61 end 61 end
62 62
@@ -67,8 +67,8 @@ describe &quot;Admin::Users&quot; do @@ -67,8 +67,8 @@ describe &quot;Admin::Users&quot; do
67 user = User.last 67 user = User.last
68 email = ActionMailer::Base.deliveries.last 68 email = ActionMailer::Base.deliveries.last
69 email.subject.should have_content("Account was created") 69 email.subject.should have_content("Account was created")
70 - email.body.should have_content(user.email)  
71 - email.body.should_not have_content(@password) 70 + email.text_part.body.should have_content(user.email)
  71 + email.text_part.body.should_not have_content(@password)
72 end 72 end
73 end 73 end
74 end 74 end
spec/helpers/gitlab_markdown_helper_spec.rb
@@ -2,6 +2,7 @@ require &quot;spec_helper&quot; @@ -2,6 +2,7 @@ require &quot;spec_helper&quot;
2 2
3 describe GitlabMarkdownHelper do 3 describe GitlabMarkdownHelper do
4 include ApplicationHelper 4 include ApplicationHelper
  5 + include IssuesHelper
5 6
6 let!(:project) { create(:project) } 7 let!(:project) { create(:project) }
7 8
spec/helpers/issues_helper_spec.rb 0 → 100644
@@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
  1 +require "spec_helper"
  2 +
  3 +describe IssuesHelper do
  4 + let(:project) { create :project }
  5 + let(:issue) { create :issue, project: project }
  6 + let(:ext_project) { create :redmine_project }
  7 +
  8 + describe :title_for_issue do
  9 + it "should return issue title if used internal tracker" do
  10 + @project = project
  11 + title_for_issue(issue.id).should eq issue.title
  12 + end
  13 +
  14 + it "should always return empty string if used external tracker" do
  15 + @project = ext_project
  16 + title_for_issue(rand(100)).should eq ""
  17 + end
  18 +
  19 + it "should always return empty string if project nil" do
  20 + @project = nil
  21 +
  22 + title_for_issue(rand(100)).should eq ""
  23 + end
  24 + end
  25 +
  26 + describe :url_for_project_issues do
  27 + let(:project_url) { Gitlab.config.issues_tracker.redmine.project_url}
  28 + let(:ext_expected) do
  29 + project_url.gsub(':project_id', ext_project.id.to_s)
  30 + .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
  31 + end
  32 + let(:int_expected) { polymorphic_path([project]) }
  33 +
  34 + it "should return internal path if used internal tracker" do
  35 + @project = project
  36 + url_for_project_issues.should match(int_expected)
  37 + end
  38 +
  39 + it "should return path to external tracker" do
  40 + @project = ext_project
  41 +
  42 + url_for_project_issues.should match(ext_expected)
  43 + end
  44 +
  45 + it "should return empty string if project nil" do
  46 + @project = nil
  47 +
  48 + url_for_project_issues.should eq ""
  49 + end
  50 + end
  51 +
  52 + describe :url_for_issue do
  53 + let(:issue_id) { 3 }
  54 + let(:issues_url) { Gitlab.config.issues_tracker.redmine.issues_url}
  55 + let(:ext_expected) do
  56 + issues_url.gsub(':id', issue_id.to_s)
  57 + .gsub(':project_id', ext_project.id.to_s)
  58 + .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
  59 + end
  60 + let(:int_expected) { polymorphic_path([project, issue]) }
  61 +
  62 + it "should return internal path if used internal tracker" do
  63 + @project = project
  64 + url_for_issue(issue.id).should match(int_expected)
  65 + end
  66 +
  67 + it "should return path to external tracker" do
  68 + @project = ext_project
  69 +
  70 + url_for_issue(issue_id).should match(ext_expected)
  71 + end
  72 +
  73 + it "should return empty string if project nil" do
  74 + @project = nil
  75 +
  76 + url_for_issue(issue.id).should eq ""
  77 + end
  78 + end
  79 +end
spec/models/project_spec.rb
@@ -60,6 +60,7 @@ describe Project do @@ -60,6 +60,7 @@ describe Project do
60 it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) } 60 it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) }
61 it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) } 61 it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) }
62 it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) } 62 it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) }
  63 + it { should ensure_length_of(:issues_tracker_id).is_within(0..255) }
63 64
64 it "should not allow new projects beyond user limits" do 65 it "should not allow new projects beyond user limits" do
65 project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1)) 66 project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1))
@@ -190,4 +191,57 @@ describe Project do @@ -190,4 +191,57 @@ describe Project do
190 Project.new(path: "empty").repository.should be_nil 191 Project.new(path: "empty").repository.should be_nil
191 end 192 end
192 end 193 end
  194 +
  195 + describe :issue_exists? do
  196 + let(:project) { create(:project) }
  197 + let(:existed_issue) { create(:issue, project: project) }
  198 + let(:not_existed_issue) { create(:issue) }
  199 + let(:ext_project) { create(:redmine_project) }
  200 +
  201 + it "should be true or if used internal tracker and issue exists" do
  202 + project.issue_exists?(existed_issue.id).should be_true
  203 + end
  204 +
  205 + it "should be false or if used internal tracker and issue not exists" do
  206 + project.issue_exists?(not_existed_issue.id).should be_false
  207 + end
  208 +
  209 + it "should always be true if used other tracker" do
  210 + ext_project.issue_exists?(rand(100)).should be_true
  211 + end
  212 + end
  213 +
  214 + describe :used_default_issues_tracker? do
  215 + let(:project) { create(:project) }
  216 + let(:ext_project) { create(:redmine_project) }
  217 +
  218 + it "should be true if used internal tracker" do
  219 + project.used_default_issues_tracker?.should be_true
  220 + end
  221 +
  222 + it "should be false if used other tracker" do
  223 + ext_project.used_default_issues_tracker?.should be_false
  224 + end
  225 + end
  226 +
  227 + describe :can_have_issues_tracker_id? do
  228 + let(:project) { create(:project) }
  229 + let(:ext_project) { create(:redmine_project) }
  230 +
  231 + it "should be true for projects with external issues tracker if issues enabled" do
  232 + ext_project.can_have_issues_tracker_id?.should be_true
  233 + end
  234 +
  235 + it "should be false for projects with internal issue tracker if issues enabled" do
  236 + project.can_have_issues_tracker_id?.should be_false
  237 + end
  238 +
  239 + it "should be always false if issues disbled" do
  240 + project.issues_enabled = false
  241 + ext_project.issues_enabled = false
  242 +
  243 + project.can_have_issues_tracker_id?.should be_false
  244 + ext_project.can_have_issues_tracker_id?.should be_false
  245 + end
  246 + end
193 end 247 end
spec/models/user_spec.rb
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 # dark_scheme :boolean default(FALSE), not null 25 # dark_scheme :boolean default(FALSE), not null
26 # theme_id :integer default(1), not null 26 # theme_id :integer default(1), not null
27 # bio :string(255) 27 # bio :string(255)
28 -# blocked :boolean default(FALSE), not null 28 +# state :string(255) default(FALSE), not null
29 # failed_attempts :integer default(0) 29 # failed_attempts :integer default(0)
30 # locked_at :datetime 30 # locked_at :datetime
31 # extern_uid :string(255) 31 # extern_uid :string(255)
@@ -140,7 +140,7 @@ describe User do @@ -140,7 +140,7 @@ describe User do
140 140
141 it "should block user" do 141 it "should block user" do
142 user.block 142 user.block
143 - user.blocked.should be_true 143 + user.blocked?.should be_true
144 end 144 end
145 end 145 end
146 146
@@ -149,7 +149,7 @@ describe User do @@ -149,7 +149,7 @@ describe User do
149 User.delete_all 149 User.delete_all
150 @user = create :user 150 @user = create :user
151 @admin = create :user, admin: true 151 @admin = create :user, admin: true
152 - @blocked = create :user, blocked: true 152 + @blocked = create :user, state: :blocked
153 end 153 end
154 154
155 it { User.filter("admins").should == [@admin] } 155 it { User.filter("admins").should == [@admin] }
spec/observers/user_observer_spec.rb
@@ -15,7 +15,13 @@ describe UserObserver do @@ -15,7 +15,13 @@ describe UserObserver do
15 create(:user) 15 create(:user)
16 end 16 end
17 17
  18 + it 'no email for external' do
  19 + Notify.should_not_receive(:new_user_email)
  20 + create(:user, extern_uid: '32442eEfsafada')
  21 + end
  22 +
18 it 'trigger logger' do 23 it 'trigger logger' do
  24 + user = double(:user, id: 42, password: 'P@ssword!', name: 'John', email: 'u@mail.local', extern_uid?: false)
19 Gitlab::AppLogger.should_receive(:info) 25 Gitlab::AppLogger.should_receive(:info)
20 create(:user) 26 create(:user)
21 end 27 end
vendor/assets/javascripts/branch-graph.js
@@ -1,385 +0,0 @@ @@ -1,385 +0,0 @@
1 -!function(){  
2 -  
3 - var BranchGraph = function(element, options){  
4 - this.element = element;  
5 - this.options = options;  
6 -  
7 - this.preparedCommits = {};  
8 - this.mtime = 0;  
9 - this.mspace = 0;  
10 - this.parents = {};  
11 - this.colors = ["#000"];  
12 -  
13 - this.load();  
14 - };  
15 -  
16 - BranchGraph.prototype.load = function(){  
17 - $.ajax({  
18 - url: this.options.url,  
19 - method: 'get',  
20 - dataType: 'json',  
21 - success: $.proxy(function(data){  
22 - $('.loading', this.element).hide();  
23 - this.prepareData(data.days, data.commits);  
24 - this.buildGraph();  
25 - }, this)  
26 - });  
27 - };  
28 -  
29 - BranchGraph.prototype.prepareData = function(days, commits){  
30 - this.days = days;  
31 - this.dayCount = days.length;  
32 - this.commits = commits;  
33 - this.commitCount = commits.length;  
34 -  
35 - this.collectParents();  
36 -  
37 - this.mtime += 4;  
38 - this.mspace += 10;  
39 - for (var i = 0; i < this.commitCount; i++) {  
40 - if (this.commits[i].id in this.parents) {  
41 - this.commits[i].isParent = true;  
42 - }  
43 - this.preparedCommits[this.commits[i].id] = this.commits[i];  
44 - }  
45 - this.collectColors();  
46 - };  
47 -  
48 - BranchGraph.prototype.collectParents = function(){  
49 - for (var i = 0; i < this.commitCount; i++) {  
50 - for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {  
51 - this.parents[this.commits[i].parents[j][0]] = true;  
52 - }  
53 - this.mtime = Math.max(this.mtime, this.commits[i].time);  
54 - this.mspace = Math.max(this.mspace, this.commits[i].space);  
55 - }  
56 - };  
57 -  
58 - BranchGraph.prototype.collectColors = function(){  
59 - for (var k = 0; k < this.mspace; k++) {  
60 - this.colors.push(Raphael.getColor(.8));  
61 - // Skipping a few colors in the spectrum to get more contrast between colors  
62 - Raphael.getColor();Raphael.getColor();  
63 - }  
64 - };  
65 -  
66 - BranchGraph.prototype.buildGraph = function(){  
67 - var graphWidth = $(this.element).width()  
68 - , ch = this.mspace * 20 + 100  
69 - , cw = Math.max(graphWidth, this.mtime * 20 + 260)  
70 - , r = Raphael(this.element.get(0), cw, ch)  
71 - , top = r.set()  
72 - , cuday = 0  
73 - , cumonth = ""  
74 - , offsetX = 20  
75 - , offsetY = 60  
76 - , barWidth = Math.max(graphWidth, this.dayCount * 20 + 320)  
77 - , scrollLeft = cw;  
78 -  
79 - this.raphael = r;  
80 -  
81 - r.rect(0, 0, barWidth, 20).attr({fill: "#222"});  
82 - r.rect(0, 20, barWidth, 20).attr({fill: "#444"});  
83 -  
84 - for (mm = 0; mm < this.dayCount; mm++) {  
85 - if(this.days[mm] != null){  
86 - if(cuday != this.days[mm][0]){  
87 - // Dates  
88 - r.text(offsetX + mm * 20, 31, this.days[mm][0]).attr({  
89 - font: "12px Monaco, monospace",  
90 - fill: "#DDD"  
91 - });  
92 - cuday = this.days[mm][0];  
93 - }  
94 - if(cumonth != this.days[mm][1]){  
95 - // Months  
96 - r.text(offsetX + mm * 20, 11, this.days[mm][1]).attr({  
97 - font: "12px Monaco, monospace",  
98 - fill: "#EEE"  
99 - });  
100 - cumonth = this.days[mm][1];  
101 - }  
102 - }  
103 - }  
104 -  
105 - for (i = 0; i < this.commitCount; i++) {  
106 - var x = offsetX + 20 * this.commits[i].time  
107 - , y = offsetY + 10 * this.commits[i].space  
108 - , c  
109 - , ps;  
110 -  
111 - // Draw dot  
112 - r.circle(x, y, 3).attr({  
113 - fill: this.colors[this.commits[i].space],  
114 - stroke: "none"  
115 - });  
116 -  
117 - // Draw lines  
118 - for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {  
119 - c = this.preparedCommits[this.commits[i].parents[j][0]];  
120 - ps = this.commits[i].parent_spaces[j];  
121 - if (c) {  
122 - var cx = offsetX + 20 * c.time  
123 - , cy = offsetY + 10 * c.space  
124 - , psy = offsetY + 10 * ps;  
125 - if (c.space == this.commits[i].space && c.space == ps) {  
126 - r.path([  
127 - "M", x, y,  
128 - "L", cx, cy  
129 - ]).attr({  
130 - stroke: this.colors[c.space],  
131 - "stroke-width": 2  
132 - });  
133 -  
134 - } else if (c.space < this.commits[i].space) {  
135 - r.path([  
136 - "M", x - 5, y,  
137 - "l-5-2,0,4,5,-2",  
138 - "L", x - 10, y,  
139 - "L", x - 15, psy,  
140 - "L", cx + 5, psy,  
141 - "L", cx, cy])  
142 - .attr({  
143 - stroke: this.colors[this.commits[i].space],  
144 - "stroke-width": 2  
145 - });  
146 - } else {  
147 - r.path([  
148 - "M", x - 3, y + 6,  
149 - "l-4,3,4,2,0,-5",  
150 - "L", x - 5, y + 10,  
151 - "L", x - 10, psy,  
152 - "L", cx + 5, psy,  
153 - "L", cx, cy])  
154 - .attr({  
155 - stroke: this.colors[c.space],  
156 - "stroke-width": 2  
157 - });  
158 - }  
159 - }  
160 - }  
161 -  
162 - if (this.commits[i].refs) {  
163 - this.appendLabel(x, y, this.commits[i].refs);  
164 - }  
165 -  
166 - // mark commit and displayed in the center  
167 - if (this.commits[i].id == this.options.commit_id) {  
168 - r.path([  
169 - 'M', x, y - 5,  
170 - 'L', x + 4, y - 15,  
171 - 'L', x - 4, y - 15,  
172 - 'Z'  
173 - ]).attr({  
174 - "fill": "#000",  
175 - "fill-opacity": .7,  
176 - "stroke": "none"  
177 - });  
178 - scrollLeft = x - graphWidth / 2;  
179 - }  
180 -  
181 - this.appendAnchor(top, this.commits[i], x, y);  
182 - }  
183 - top.toFront();  
184 - this.element.scrollLeft(scrollLeft);  
185 - this.bindEvents();  
186 - };  
187 -  
188 - BranchGraph.prototype.bindEvents = function(){  
189 - var drag = {}  
190 - , element = this.element;  
191 -  
192 - var dragger = function(event){  
193 - element.scrollLeft(drag.sl - (event.clientX - drag.x));  
194 - element.scrollTop(drag.st - (event.clientY - drag.y));  
195 - };  
196 -  
197 - element.on({  
198 - mousedown: function (event) {  
199 - drag = {  
200 - x: event.clientX,  
201 - y: event.clientY,  
202 - st: element.scrollTop(),  
203 - sl: element.scrollLeft()  
204 - };  
205 - $(window).on('mousemove', dragger);  
206 - }  
207 - });  
208 - $(window).on({  
209 - mouseup: function(){  
210 - //bars.animate({opacity: 0}, 300);  
211 - $(window).off('mousemove', dragger);  
212 - },  
213 - keydown: function(event){  
214 - if(event.keyCode == 37){  
215 - // left  
216 - element.scrollLeft( element.scrollLeft() - 50);  
217 - }  
218 - if(event.keyCode == 38){  
219 - // top  
220 - element.scrollTop( element.scrollTop() - 50);  
221 - }  
222 - if(event.keyCode == 39){  
223 - // right  
224 - element.scrollLeft( element.scrollLeft() + 50);  
225 - }  
226 - if(event.keyCode == 40){  
227 - // bottom  
228 - element.scrollTop( element.scrollTop() + 50);  
229 - }  
230 - }  
231 - });  
232 - };  
233 -  
234 - BranchGraph.prototype.appendLabel = function(x, y, refs){  
235 - var r = this.raphael  
236 - , shortrefs = refs  
237 - , text, textbox, rect;  
238 -  
239 - if (shortrefs.length > 17){  
240 - // Truncate if longer than 15 chars  
241 - shortrefs = shortrefs.substr(0,15) + "…";  
242 - }  
243 -  
244 - text = r.text(x+5, y+8 + 10, shortrefs).attr({  
245 - font: "10px Monaco, monospace",  
246 - fill: "#FFF",  
247 - title: refs  
248 - });  
249 -  
250 - textbox = text.getBBox();  
251 - text.transform([  
252 - 't', textbox.height/-4, textbox.width/2 + 5,  
253 - 'r90'  
254 - ]);  
255 -  
256 - // Create rectangle based on the size of the textbox  
257 - rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr({  
258 - "fill": "#000",  
259 - "fill-opacity": .7,  
260 - "stroke": "none"  
261 - });  
262 -  
263 - triangle = r.path([  
264 - 'M', x, y + 5,  
265 - 'L', x + 4, y + 15,  
266 - 'L', x - 4, y + 15,  
267 - 'Z'  
268 - ]).attr({  
269 - "fill": "#000",  
270 - "fill-opacity": .7,  
271 - "stroke": "none"  
272 - });  
273 -  
274 - // Rotate and reposition rectangle over text  
275 - rect.transform([  
276 - 'r', 90, x, y,  
277 - 't', 15, -9  
278 - ]);  
279 -  
280 - // Set text to front  
281 - text.toFront();  
282 - };  
283 -  
284 - BranchGraph.prototype.appendAnchor = function(top, commit, x, y) {  
285 - var r = this.raphael  
286 - , options = this.options  
287 - , anchor;  
288 - anchor = r.circle(x, y, 10).attr({  
289 - fill: "#000",  
290 - opacity: 0,  
291 - cursor: "pointer"  
292 - })  
293 - .click(function(){  
294 - window.open(options.commit_url.replace('%s', commit.id), '_blank');  
295 - })  
296 - .hover(function(){  
297 - this.tooltip = r.commitTooltip(x, y + 5, commit);  
298 - top.push(this.tooltip.insertBefore(this));  
299 - }, function(){  
300 - this.tooltip && this.tooltip.remove() && delete this.tooltip;  
301 - });  
302 - top.push(anchor);  
303 - };  
304 -  
305 - this.BranchGraph = BranchGraph;  
306 -  
307 -}(this);  
308 -Raphael.fn.commitTooltip = function(x, y, commit){  
309 - var nameText, idText, messageText  
310 - , boxWidth = 300  
311 - , boxHeight = 200;  
312 -  
313 - nameText = this.text(x, y + 10, commit.author.name);  
314 - idText = this.text(x, y + 35, commit.id);  
315 - messageText = this.text(x, y + 50, commit.message);  
316 -  
317 - textSet = this.set(nameText, idText, messageText).attr({  
318 - "text-anchor": "start",  
319 - "font": "12px Monaco, monospace"  
320 - });  
321 -  
322 - nameText.attr({  
323 - "font": "14px Arial",  
324 - "font-weight": "bold"  
325 - });  
326 -  
327 - idText.attr({  
328 - "fill": "#AAA"  
329 - });  
330 -  
331 - textWrap(messageText, boxWidth - 50);  
332 -  
333 - var rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({  
334 - "fill": "#FFF",  
335 - "stroke": "#000",  
336 - "stroke-linecap": "round",  
337 - "stroke-width": 2  
338 - });  
339 - var tooltip = this.set(rect, textSet);  
340 -  
341 - rect.attr({  
342 - "height" : tooltip.getBBox().height + 10,  
343 - "width" : tooltip.getBBox().width + 10  
344 - });  
345 -  
346 - tooltip.transform([  
347 - 't', 20, 20  
348 - ]);  
349 -  
350 - return tooltip;  
351 -};  
352 -  
353 -function textWrap(t, width) {  
354 - var content = t.attr("text");  
355 - var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";  
356 - t.attr({  
357 - "text" : abc  
358 - });  
359 - var letterWidth = t.getBBox().width / abc.length;  
360 -  
361 - t.attr({  
362 - "text" : content  
363 - });  
364 -  
365 - var words = content.split(" ");  
366 - var x = 0, s = [];  
367 - for ( var i = 0; i < words.length; i++) {  
368 -  
369 - var l = words[i].length;  
370 - if (x + (l * letterWidth) > width) {  
371 - s.push("\n");  
372 - x = 0;  
373 - }  
374 - x += l * letterWidth;  
375 - s.push(words[i] + " ");  
376 - }  
377 - t.attr({  
378 - "text" : s.join("")  
379 - });  
380 - var b = t.getBBox()  
381 - , h = Math.abs(b.y2) - Math.abs(b.y) + 1;  
382 - t.attr({  
383 - "y": b.y + h  
384 - });  
385 -}