Commit 30227869482bbbdbfea153f2b45ef3bb9a9fd218
Exists in
master
and in
4 other branches
Merge commit 'master' into discussions
Conflicts: app/assets/stylesheets/sections/notes.scss app/contexts/notes/load_context.rb app/models/project.rb app/observers/note_observer.rb app/roles/votes.rb app/views/commit/show.html.haml app/views/merge_requests/_show.html.haml app/views/merge_requests/diffs.js.haml app/views/merge_requests/show.js.haml app/views/notes/_note.html.haml features/steps/project/project_merge_requests.rb spec/models/note_spec.rb
Showing
938 changed files
with
81195 additions
and
104503 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 938 files displayed.
.gitignore
.travis.yml
... | ... | @@ -3,16 +3,12 @@ env: |
3 | 3 | - DB=mysql |
4 | 4 | before_install: |
5 | 5 | - sudo apt-get install libicu-dev -y |
6 | - - wget -P /tmp http://phantomjs.googlecode.com/files/phantomjs-1.7.0-linux-i686.tar.bz2 | |
7 | - - tar -xf /tmp/phantomjs-1.7.0-linux-i686.tar.bz2 -C /tmp/ | |
8 | - - sudo rm -rf /usr/local/phantomjs | |
9 | - - sudo mv /tmp/phantomjs-1.7.0-linux-i686 /usr/local/phantomjs | |
10 | 6 | - gem install charlock_holmes -v="0.6.9" |
11 | 7 | branches: |
12 | 8 | only: |
13 | 9 | - 'master' |
14 | 10 | rvm: |
15 | - - 1.9.3 | |
11 | + - 1.9.2 | |
16 | 12 | services: |
17 | 13 | - mysql |
18 | 14 | - postgresql | ... | ... |
CHANGELOG
Gemfile
... | ... | @@ -8,7 +8,7 @@ def linux_only(require_as) |
8 | 8 | RUBY_PLATFORM.include?('linux') && require_as |
9 | 9 | end |
10 | 10 | |
11 | -gem "rails", "3.2.9" | |
11 | +gem "rails", "3.2.11" | |
12 | 12 | |
13 | 13 | # Supported DBs |
14 | 14 | gem "mysql2", group: :mysql |
... | ... | @@ -23,11 +23,15 @@ gem 'omniauth-github' |
23 | 23 | |
24 | 24 | # GITLAB patched libs |
25 | 25 | gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '7f35cb98ff17d534a07e3ce6ec3d580f67402837' |
26 | -gem "omniauth-ldap", git: "https://github.com/gitlabhq/omniauth-ldap.git", ref: 'f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e' | |
27 | -gem 'yaml_db', git: "https://github.com/gitlabhq/yaml_db.git", ref: '98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd' | |
28 | 26 | gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8' |
29 | 27 | gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '8e6afc2da821354774aa4d1ee8a1aa2082f84a3e' |
30 | 28 | |
29 | +# LDAP Auth | |
30 | +gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap" | |
31 | + | |
32 | +# Dump db to yml file. Mostly used to migrate from sqlite to mysql | |
33 | +gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db" | |
34 | + | |
31 | 35 | # Gitolite client (for work with gitolite-admin repo) |
32 | 36 | gem "gitolite", '1.1.0' |
33 | 37 | |
... | ... | @@ -77,8 +81,9 @@ gem "acts-as-taggable-on", "2.3.3" |
77 | 81 | gem "draper", "~> 0.18.0" |
78 | 82 | |
79 | 83 | # Background jobs |
80 | -gem "resque", "~> 1.23.0" | |
81 | -gem 'resque_mailer' | |
84 | +gem 'slim' | |
85 | +gem 'sinatra', :require => nil | |
86 | +gem 'sidekiq', '2.6.4' | |
82 | 87 | |
83 | 88 | # HTTP requests |
84 | 89 | gem "httparty" |
... | ... | @@ -104,7 +109,7 @@ group :assets do |
104 | 109 | gem "jquery-rails", "2.1.3" |
105 | 110 | gem "jquery-ui-rails", "2.0.2" |
106 | 111 | gem "modernizr", "2.6.2" |
107 | - gem "raphael-rails", "1.5.2" | |
112 | + gem "raphael-rails", git: "https://github.com/gitlabhq/raphael-rails.git" | |
108 | 113 | gem 'bootstrap-sass', "2.2.1.1" |
109 | 114 | gem "font-awesome-sass-rails", "~> 2.0.0" |
110 | 115 | gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' |
... | ... | @@ -115,6 +120,14 @@ group :development do |
115 | 120 | gem "letter_opener" |
116 | 121 | gem 'quiet_assets', '~> 1.0.1' |
117 | 122 | gem 'rack-mini-profiler' |
123 | + # Better errors handler | |
124 | + gem 'better_errors' | |
125 | + gem 'binding_of_caller' | |
126 | + | |
127 | + gem 'rails_best_practices' | |
128 | + | |
129 | + # Docs generator | |
130 | + gem "sdoc" | |
118 | 131 | end |
119 | 132 | |
120 | 133 | group :development, :test do |
... | ... | @@ -145,7 +158,6 @@ group :test do |
145 | 158 | gem "simplecov", require: false |
146 | 159 | gem "shoulda-matchers", "1.3.0" |
147 | 160 | gem 'email_spec' |
148 | - gem 'resque_spec' | |
149 | 161 | gem "webmock" |
150 | 162 | gem 'test_after_commit' |
151 | 163 | end | ... | ... |
Gemfile.lock
... | ... | @@ -40,17 +40,6 @@ GIT |
40 | 40 | charlock_holmes (~> 0.6.9) |
41 | 41 | |
42 | 42 | GIT |
43 | - remote: https://github.com/gitlabhq/omniauth-ldap.git | |
44 | - revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e | |
45 | - ref: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e | |
46 | - specs: | |
47 | - omniauth-ldap (1.0.2) | |
48 | - net-ldap (~> 0.2.2) | |
49 | - omniauth (~> 1.0) | |
50 | - pyu-ruby-sasl (~> 0.0.3.1) | |
51 | - rubyntlm (~> 0.1.1) | |
52 | - | |
53 | -GIT | |
54 | 43 | remote: https://github.com/gitlabhq/pygments.rb.git |
55 | 44 | revision: db1da0343adf86b49bdc3add04d02d2e80438d38 |
56 | 45 | branch: master |
... | ... | @@ -60,11 +49,10 @@ GIT |
60 | 49 | yajl-ruby (~> 1.1.0) |
61 | 50 | |
62 | 51 | GIT |
63 | - remote: https://github.com/gitlabhq/yaml_db.git | |
64 | - revision: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd | |
65 | - ref: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd | |
52 | + remote: https://github.com/gitlabhq/raphael-rails.git | |
53 | + revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58 | |
66 | 54 | specs: |
67 | - yaml_db (0.2.2) | |
55 | + raphael-rails (2.1.0) | |
68 | 56 | |
69 | 57 | GIT |
70 | 58 | remote: https://github.com/jonleighton/poltergeist.git |
... | ... | @@ -81,12 +69,12 @@ GIT |
81 | 69 | GEM |
82 | 70 | remote: http://rubygems.org/ |
83 | 71 | specs: |
84 | - actionmailer (3.2.9) | |
85 | - actionpack (= 3.2.9) | |
72 | + actionmailer (3.2.11) | |
73 | + actionpack (= 3.2.11) | |
86 | 74 | mail (~> 2.4.4) |
87 | - actionpack (3.2.9) | |
88 | - activemodel (= 3.2.9) | |
89 | - activesupport (= 3.2.9) | |
75 | + actionpack (3.2.11) | |
76 | + activemodel (= 3.2.11) | |
77 | + activesupport (= 3.2.11) | |
90 | 78 | builder (~> 3.0.0) |
91 | 79 | erubis (~> 2.7.0) |
92 | 80 | journey (~> 1.0.4) |
... | ... | @@ -94,18 +82,18 @@ GEM |
94 | 82 | rack-cache (~> 1.2) |
95 | 83 | rack-test (~> 0.6.1) |
96 | 84 | sprockets (~> 2.2.1) |
97 | - activemodel (3.2.9) | |
98 | - activesupport (= 3.2.9) | |
85 | + activemodel (3.2.11) | |
86 | + activesupport (= 3.2.11) | |
99 | 87 | builder (~> 3.0.0) |
100 | - activerecord (3.2.9) | |
101 | - activemodel (= 3.2.9) | |
102 | - activesupport (= 3.2.9) | |
88 | + activerecord (3.2.11) | |
89 | + activemodel (= 3.2.11) | |
90 | + activesupport (= 3.2.11) | |
103 | 91 | arel (~> 3.0.2) |
104 | 92 | tzinfo (~> 0.3.29) |
105 | - activeresource (3.2.9) | |
106 | - activemodel (= 3.2.9) | |
107 | - activesupport (= 3.2.9) | |
108 | - activesupport (3.2.9) | |
93 | + activeresource (3.2.11) | |
94 | + activemodel (= 3.2.11) | |
95 | + activesupport (= 3.2.11) | |
96 | + activesupport (3.2.11) | |
109 | 97 | i18n (~> 0.6) |
110 | 98 | multi_json (~> 1.0) |
111 | 99 | acts-as-taggable-on (2.3.3) |
... | ... | @@ -115,6 +103,10 @@ GEM |
115 | 103 | awesome_print (1.1.0) |
116 | 104 | backports (2.6.5) |
117 | 105 | bcrypt-ruby (3.0.1) |
106 | + better_errors (0.3.2) | |
107 | + coderay (>= 1.0.0) | |
108 | + erubis (>= 2.7.0) | |
109 | + binding_of_caller (0.6.8) | |
118 | 110 | blankslate (3.1.2) |
119 | 111 | bootstrap-sass (2.2.1.1) |
120 | 112 | sass (~> 3.2) |
... | ... | @@ -129,12 +121,17 @@ GEM |
129 | 121 | carrierwave (0.7.1) |
130 | 122 | activemodel (>= 3.2.0) |
131 | 123 | activesupport (>= 3.2.0) |
124 | + celluloid (0.12.4) | |
125 | + facter (>= 1.6.12) | |
126 | + timers (>= 1.0.0) | |
132 | 127 | charlock_holmes (0.6.9) |
133 | 128 | childprocess (0.3.6) |
134 | 129 | ffi (~> 1.0, >= 1.0.6) |
135 | 130 | chosen-rails (0.9.8) |
136 | 131 | railties (~> 3.0) |
137 | 132 | thor (~> 0.14) |
133 | + code_analyzer (0.3.1) | |
134 | + sexp_processor | |
138 | 135 | coderay (1.0.8) |
139 | 136 | coffee-rails (3.2.2) |
140 | 137 | coffee-script (>= 2.2.0) |
... | ... | @@ -145,6 +142,7 @@ GEM |
145 | 142 | coffee-script-source (1.4.0) |
146 | 143 | colored (1.2) |
147 | 144 | colorize (0.5.8) |
145 | + connection_pool (1.0.0) | |
148 | 146 | crack (0.3.1) |
149 | 147 | daemons (1.1.9) |
150 | 148 | devise (2.1.2) |
... | ... | @@ -164,6 +162,7 @@ GEM |
164 | 162 | eventmachine (1.0.0) |
165 | 163 | execjs (1.4.0) |
166 | 164 | multi_json (~> 1.0) |
165 | + facter (1.6.17) | |
167 | 166 | factory_girl (4.1.0) |
168 | 167 | activesupport (>= 3.0.0) |
169 | 168 | factory_girl_rails (4.1.0) |
... | ... | @@ -190,6 +189,12 @@ GEM |
190 | 189 | pygments.rb (>= 0.2.13) |
191 | 190 | github-markup (0.7.4) |
192 | 191 | gitlab_meta (4.0) |
192 | + gitlab_omniauth-ldap (1.0.2) | |
193 | + net-ldap (~> 0.2.2) | |
194 | + omniauth (~> 1.0) | |
195 | + pyu-ruby-sasl (~> 0.0.3.1) | |
196 | + rubyntlm (~> 0.1.1) | |
197 | + gitlab_yaml_db (1.0.0) | |
193 | 198 | gitolite (1.1.0) |
194 | 199 | gratr19 (~> 0.4.4.1) |
195 | 200 | grit (~> 2.5.0) |
... | ... | @@ -240,7 +245,7 @@ GEM |
240 | 245 | jquery-ui-rails (2.0.2) |
241 | 246 | jquery-rails |
242 | 247 | railties (>= 3.1.0) |
243 | - json (1.7.5) | |
248 | + json (1.7.6) | |
244 | 249 | jwt (0.1.5) |
245 | 250 | multi_json (>= 1.0) |
246 | 251 | kaminari (0.14.1) |
... | ... | @@ -264,7 +269,7 @@ GEM |
264 | 269 | mime-types (1.19) |
265 | 270 | modernizr (2.6.2) |
266 | 271 | sprockets (~> 2.0) |
267 | - multi_json (1.3.7) | |
272 | + multi_json (1.5.0) | |
268 | 273 | multi_xml (0.5.1) |
269 | 274 | multipart-post (1.1.5) |
270 | 275 | mysql2 (0.3.11) |
... | ... | @@ -299,6 +304,7 @@ GEM |
299 | 304 | pg (0.14.1) |
300 | 305 | polyglot (0.3.3) |
301 | 306 | posix-spawn (0.3.6) |
307 | + progressbar (0.12.0) | |
302 | 308 | pry (0.9.10) |
303 | 309 | coderay (~> 1.0.5) |
304 | 310 | method_source (~> 0.8) |
... | ... | @@ -306,7 +312,7 @@ GEM |
306 | 312 | pyu-ruby-sasl (0.0.3.3) |
307 | 313 | quiet_assets (1.0.1) |
308 | 314 | railties (~> 3.1) |
309 | - rack (1.4.1) | |
315 | + rack (1.4.3) | |
310 | 316 | rack-accept (0.4.5) |
311 | 317 | rack (>= 0.4) |
312 | 318 | rack-cache (1.2) |
... | ... | @@ -315,33 +321,40 @@ GEM |
315 | 321 | rack (>= 1.1.3) |
316 | 322 | rack-mount (0.8.3) |
317 | 323 | rack (>= 1.0.0) |
318 | - rack-protection (1.2.0) | |
324 | + rack-protection (1.3.2) | |
319 | 325 | rack |
320 | 326 | rack-ssl (1.3.2) |
321 | 327 | rack |
322 | 328 | rack-test (0.6.2) |
323 | 329 | rack (>= 1.0) |
324 | - rails (3.2.9) | |
325 | - actionmailer (= 3.2.9) | |
326 | - actionpack (= 3.2.9) | |
327 | - activerecord (= 3.2.9) | |
328 | - activeresource (= 3.2.9) | |
329 | - activesupport (= 3.2.9) | |
330 | + rails (3.2.11) | |
331 | + actionmailer (= 3.2.11) | |
332 | + actionpack (= 3.2.11) | |
333 | + activerecord (= 3.2.11) | |
334 | + activeresource (= 3.2.11) | |
335 | + activesupport (= 3.2.11) | |
330 | 336 | bundler (~> 1.0) |
331 | - railties (= 3.2.9) | |
337 | + railties (= 3.2.11) | |
332 | 338 | rails-dev-tweaks (0.6.1) |
333 | 339 | actionpack (~> 3.1) |
334 | 340 | railties (~> 3.1) |
335 | - railties (3.2.9) | |
336 | - actionpack (= 3.2.9) | |
337 | - activesupport (= 3.2.9) | |
341 | + rails_best_practices (1.13.2) | |
342 | + activesupport | |
343 | + awesome_print | |
344 | + code_analyzer | |
345 | + colored | |
346 | + erubis | |
347 | + i18n | |
348 | + progressbar | |
349 | + railties (3.2.11) | |
350 | + actionpack (= 3.2.11) | |
351 | + activesupport (= 3.2.11) | |
338 | 352 | rack-ssl (~> 1.3.2) |
339 | 353 | rake (>= 0.8.7) |
340 | 354 | rdoc (~> 3.4) |
341 | 355 | thor (>= 0.14.6, < 2.0) |
342 | 356 | raindrops (0.10.0) |
343 | - rake (10.0.1) | |
344 | - raphael-rails (1.5.2) | |
357 | + rake (10.0.3) | |
345 | 358 | rb-fsevent (0.9.2) |
346 | 359 | rb-inotify (0.8.8) |
347 | 360 | ffi (>= 0.5.0) |
... | ... | @@ -351,16 +364,6 @@ GEM |
351 | 364 | redis (3.0.2) |
352 | 365 | redis-namespace (1.2.1) |
353 | 366 | redis (~> 3.0.0) |
354 | - resque (1.23.0) | |
355 | - multi_json (~> 1.0) | |
356 | - redis-namespace (~> 1.0) | |
357 | - sinatra (>= 0.9.2) | |
358 | - vegas (~> 0.1.2) | |
359 | - resque_mailer (2.1.0) | |
360 | - actionmailer (~> 3.0) | |
361 | - resque_spec (0.12.5) | |
362 | - resque (>= 1.19.0) | |
363 | - rspec (>= 2.5.0) | |
364 | 367 | rspec (2.12.0) |
365 | 368 | rspec-core (~> 2.12.0) |
366 | 369 | rspec-expectations (~> 2.12.0) |
... | ... | @@ -383,6 +386,9 @@ GEM |
383 | 386 | railties (~> 3.2.0) |
384 | 387 | sass (>= 3.1.10) |
385 | 388 | tilt (~> 1.3) |
389 | + sdoc (0.3.20) | |
390 | + json (>= 1.1.3) | |
391 | + rdoc (~> 3.10) | |
386 | 392 | seed-fu (2.2.0) |
387 | 393 | activerecord (~> 3.1) |
388 | 394 | activesupport (~> 3.1) |
... | ... | @@ -392,8 +398,15 @@ GEM |
392 | 398 | multi_json (~> 1.0) |
393 | 399 | rubyzip |
394 | 400 | settingslogic (2.0.8) |
401 | + sexp_processor (4.1.3) | |
395 | 402 | shoulda-matchers (1.3.0) |
396 | 403 | activesupport (>= 3.0.0) |
404 | + sidekiq (2.6.4) | |
405 | + celluloid (~> 0.12.0) | |
406 | + connection_pool (~> 1.0) | |
407 | + multi_json (~> 1) | |
408 | + redis (~> 3) | |
409 | + redis-namespace | |
397 | 410 | simplecov (0.7.1) |
398 | 411 | multi_json (~> 1.0) |
399 | 412 | simplecov-html (~> 0.7.1) |
... | ... | @@ -403,6 +416,9 @@ GEM |
403 | 416 | rack-protection (~> 1.2) |
404 | 417 | tilt (~> 1.3, >= 1.3.3) |
405 | 418 | six (0.2.0) |
419 | + slim (1.3.6) | |
420 | + temple (~> 0.5.5) | |
421 | + tilt (~> 1.3.3) | |
406 | 422 | slop (3.3.3) |
407 | 423 | spinach (0.5.2) |
408 | 424 | colorize |
... | ... | @@ -411,12 +427,13 @@ GEM |
411 | 427 | capybara (~> 1) |
412 | 428 | railties (>= 3) |
413 | 429 | spinach (>= 0.4) |
414 | - sprockets (2.2.1) | |
430 | + sprockets (2.2.2) | |
415 | 431 | hike (~> 1.2) |
416 | 432 | multi_json (~> 1.0) |
417 | 433 | rack (~> 1.0) |
418 | 434 | tilt (~> 1.1, != 1.3.0) |
419 | 435 | stamp (0.3.0) |
436 | + temple (0.5.5) | |
420 | 437 | test_after_commit (0.0.1) |
421 | 438 | therubyracer (0.10.2) |
422 | 439 | libv8 (~> 3.3.10) |
... | ... | @@ -426,6 +443,7 @@ GEM |
426 | 443 | rack (>= 1.0.0) |
427 | 444 | thor (0.16.0) |
428 | 445 | tilt (1.3.3) |
446 | + timers (1.0.2) | |
429 | 447 | treetop (1.4.12) |
430 | 448 | polyglot |
431 | 449 | polyglot (>= 0.3.1) |
... | ... | @@ -437,8 +455,6 @@ GEM |
437 | 455 | kgio (~> 2.6) |
438 | 456 | rack |
439 | 457 | raindrops (~> 0.7) |
440 | - vegas (0.1.11) | |
441 | - rack (>= 1.0.0) | |
442 | 458 | virtus (0.5.2) |
443 | 459 | backports (~> 2.6.1) |
444 | 460 | warden (1.2.1) |
... | ... | @@ -458,6 +474,8 @@ DEPENDENCIES |
458 | 474 | acts-as-taggable-on (= 2.3.3) |
459 | 475 | annotate! |
460 | 476 | awesome_print |
477 | + better_errors | |
478 | + binding_of_caller | |
461 | 479 | bootstrap-sass (= 2.2.1.1) |
462 | 480 | capybara |
463 | 481 | carrierwave (~> 0.7.1) |
... | ... | @@ -477,6 +495,8 @@ DEPENDENCIES |
477 | 495 | github-linguist (~> 2.3.4) |
478 | 496 | github-markup (~> 0.7.4) |
479 | 497 | gitlab_meta (= 4.0) |
498 | + gitlab_omniauth-ldap (= 1.0.2) | |
499 | + gitlab_yaml_db (= 1.0.0) | |
480 | 500 | gitolite (= 1.1.0) |
481 | 501 | grack! |
482 | 502 | grape (~> 0.2.1) |
... | ... | @@ -498,7 +518,6 @@ DEPENDENCIES |
498 | 518 | omniauth (~> 1.1.1) |
499 | 519 | omniauth-github |
500 | 520 | omniauth-google-oauth2 |
501 | - omniauth-ldap! | |
502 | 521 | omniauth-twitter |
503 | 522 | pg |
504 | 523 | poltergeist! |
... | ... | @@ -506,22 +525,24 @@ DEPENDENCIES |
506 | 525 | pygments.rb! |
507 | 526 | quiet_assets (~> 1.0.1) |
508 | 527 | rack-mini-profiler |
509 | - rails (= 3.2.9) | |
528 | + rails (= 3.2.11) | |
510 | 529 | rails-dev-tweaks |
511 | - raphael-rails (= 1.5.2) | |
530 | + rails_best_practices | |
531 | + raphael-rails! | |
512 | 532 | rb-fsevent |
513 | 533 | rb-inotify |
514 | 534 | redcarpet (~> 2.2.2) |
515 | - resque (~> 1.23.0) | |
516 | - resque_mailer | |
517 | - resque_spec | |
518 | 535 | rspec-rails |
519 | 536 | sass-rails (~> 3.2.5) |
537 | + sdoc | |
520 | 538 | seed-fu |
521 | 539 | settingslogic |
522 | 540 | shoulda-matchers (= 1.3.0) |
541 | + sidekiq (= 2.6.4) | |
523 | 542 | simplecov |
543 | + sinatra | |
524 | 544 | six |
545 | + slim | |
525 | 546 | spinach-rails |
526 | 547 | stamp |
527 | 548 | test_after_commit |
... | ... | @@ -530,4 +551,3 @@ DEPENDENCIES |
530 | 551 | uglifier (~> 1.3.0) |
531 | 552 | unicorn (~> 4.4.0) |
532 | 553 | webmock |
533 | - yaml_db! | ... | ... |
Procfile
README.md
... | ... | @@ -2,6 +2,7 @@ |
2 | 2 | |
3 | 3 | GitLab is a free project and repository management application |
4 | 4 | |
5 | + | |
5 | 6 | |
6 | 7 | ## Application details |
7 | 8 | |
... | ... | @@ -39,6 +40,6 @@ Email |
39 | 40 | |
40 | 41 | ## Contribute |
41 | 42 | |
42 | -[Development Tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md) | |
43 | +[Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide) | |
43 | 44 | Want to help - send a pull request. |
44 | 45 | We'll accept good pull requests. | ... | ... |
VERSION
... | ... | @@ -0,0 +1,92 @@ |
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. | ... | ... |
No preview for this file type
app/assets/fonts/korolev-medium-compressed.otf
No preview for this file type
app/assets/images/download.png
674 Bytes
... | ... | @@ -0,0 +1,30 @@ |
1 | +$ -> | |
2 | + dashboardPage() | |
3 | + | |
4 | +dashboardPage = -> | |
5 | + Pager.init 20, true | |
6 | + $(".event_filter_link").bind "click", (event) -> | |
7 | + event.preventDefault() | |
8 | + toggleFilter $(this) | |
9 | + reloadActivities() | |
10 | + | |
11 | +reloadActivities = -> | |
12 | + $(".content_list").html '' | |
13 | + Pager.init 20, true | |
14 | + | |
15 | +toggleFilter = (sender) -> | |
16 | + sender.parent().toggleClass "inactive" | |
17 | + event_filters = $.cookie("event_filter") | |
18 | + filter = sender.attr("id").split("_")[0] | |
19 | + if event_filters | |
20 | + event_filters = event_filters.split(",") | |
21 | + else | |
22 | + event_filters = new Array() | |
23 | + | |
24 | + index = event_filters.indexOf(filter) | |
25 | + if index is -1 | |
26 | + event_filters.push filter | |
27 | + else | |
28 | + event_filters.splice index, 1 | |
29 | + | |
30 | + $.cookie "event_filter", event_filters.join(",") | ... | ... |
app/assets/javascripts/issues.js
... | ... | @@ -11,7 +11,7 @@ function initIssuesSearch() { |
11 | 11 | last_terms = terms; |
12 | 12 | |
13 | 13 | if (terms.length >= 2 || terms.length == 0) { |
14 | - $.get(href, { 'f': status, 'terms': terms, 'milestone_id': milestone_id }, function(response) { | |
14 | + $.get(href, { 'status': status, 'terms': terms, 'milestone_id': milestone_id }, function(response) { | |
15 | 15 | $('#issues-table').html(response); |
16 | 16 | }); |
17 | 17 | } | ... | ... |
app/assets/javascripts/merge_requests.js
... | ... | @@ -1,132 +0,0 @@ |
1 | -var MergeRequest = { | |
2 | - diffs_loaded: false, | |
3 | - commits_loaded: false, | |
4 | - opts: false, | |
5 | - | |
6 | - init: | |
7 | - function(opts) { | |
8 | - var self = this; | |
9 | - self.opts = opts; | |
10 | - | |
11 | - self.initTabs(); | |
12 | - self.initMergeWidget(); | |
13 | - | |
14 | - $(".mr_show_all_commits").bind("click", function() { | |
15 | - self.showAllCommits(); | |
16 | - }); | |
17 | - }, | |
18 | - | |
19 | - initMergeWidget: | |
20 | - function() { | |
21 | - var self = this; | |
22 | - self.showState(self.opts.current_state); | |
23 | - | |
24 | - if($(".automerge_widget").length && self.opts.check_enable){ | |
25 | - $.get(self.opts.url_to_automerge_check, function(data){ | |
26 | - self.showState(data.state); | |
27 | - }, "json"); | |
28 | - } | |
29 | - | |
30 | - if(self.opts.ci_enable){ | |
31 | - $.get(self.opts.url_to_ci_check, function(data){ | |
32 | - self.showCiState(data.status); | |
33 | - }, "json"); | |
34 | - } | |
35 | - }, | |
36 | - | |
37 | - initTabs: | |
38 | - function() { | |
39 | - $(".mr_nav_tabs a").live("click", function() { | |
40 | - $(".mr_nav_tabs a").parent().removeClass("active"); | |
41 | - $(this).parent().addClass("active"); | |
42 | - }); | |
43 | - | |
44 | - var current_tab; | |
45 | - if(this.opts.action == "diffs") { | |
46 | - current_tab = $(".mr_nav_tabs .merge-diffs-tab"); | |
47 | - } else { | |
48 | - current_tab = $(".mr_nav_tabs .merge-notes-tab"); | |
49 | - } | |
50 | - current_tab.parent().addClass("active"); | |
51 | - | |
52 | - this.initNotesTab(); | |
53 | - this.initDiffTab(); | |
54 | - }, | |
55 | - | |
56 | - initNotesTab: | |
57 | - function() { | |
58 | - $(".mr_nav_tabs a.merge-notes-tab").live("click", function(e) { | |
59 | - $(".merge-request-diffs").hide(); | |
60 | - $(".merge_request_notes").show(); | |
61 | - var mr_path = $(".merge-notes-tab").attr("data-url"); | |
62 | - history.pushState({ path: mr_path }, '', mr_path); | |
63 | - e.preventDefault(); | |
64 | - }); | |
65 | - }, | |
66 | - | |
67 | - initDiffTab: | |
68 | - function() { | |
69 | - $(".mr_nav_tabs a.merge-diffs-tab").live("click", function(e) { | |
70 | - if(!MergeRequest.diffs_loaded) { | |
71 | - MergeRequest.loadDiff(); | |
72 | - } | |
73 | - $(".merge_request_notes").hide(); | |
74 | - $(".merge-request-diffs").show(); | |
75 | - var mr_diff_path = $(".merge-diffs-tab").attr("data-url"); | |
76 | - history.pushState({ path: mr_diff_path }, '', mr_diff_path); | |
77 | - e.preventDefault(); | |
78 | - }); | |
79 | - | |
80 | - }, | |
81 | - | |
82 | - showState: | |
83 | - function(state){ | |
84 | - $(".automerge_widget").hide(); | |
85 | - $(".automerge_widget." + state).show(); | |
86 | - }, | |
87 | - | |
88 | - showCiState: | |
89 | - function(state){ | |
90 | - $(".ci_widget").hide(); | |
91 | - $(".ci_widget.ci-" + state).show(); | |
92 | - }, | |
93 | - | |
94 | - loadDiff: | |
95 | - function() { | |
96 | - $(".dashboard-loader").show(); | |
97 | - $.ajax({ | |
98 | - type: "GET", | |
99 | - url: $(".merge-diffs-tab").attr("data-url"), | |
100 | - beforeSend: function(){ $('.status').addClass("loading")}, | |
101 | - complete: function(){ | |
102 | - MergeRequest.diffs_loaded = true; | |
103 | - $(".merge_request_notes").hide(); | |
104 | - $('.status').removeClass("loading"); | |
105 | - }, | |
106 | - dataType: "script"}); | |
107 | - }, | |
108 | - | |
109 | - showAllCommits: | |
110 | - function() { | |
111 | - $(".first_mr_commits").remove(); | |
112 | - $(".all_mr_commits").removeClass("hide"); | |
113 | - }, | |
114 | - | |
115 | - already_cannot_be_merged: | |
116 | - function(){ | |
117 | - $(".automerge_widget").hide(); | |
118 | - $(".merge_in_progress").hide(); | |
119 | - $(".automerge_widget.already_cannot_be_merged").show(); | |
120 | - } | |
121 | -}; | |
122 | - | |
123 | -/* | |
124 | - * Filter merge requests | |
125 | - */ | |
126 | -function merge_requestsPage() { | |
127 | - $("#assignee_id").chosen(); | |
128 | - $("#milestone_id").chosen(); | |
129 | - $("#milestone_id, #assignee_id").on("change", function(){ | |
130 | - $(this).closest("form").submit(); | |
131 | - }); | |
132 | -} |
... | ... | @@ -0,0 +1,97 @@ |
1 | +# | |
2 | +# * Filter merge requests | |
3 | +# | |
4 | +@merge_requestsPage = -> | |
5 | + $('#assignee_id').chosen() | |
6 | + $('#milestone_id').chosen() | |
7 | + $('#milestone_id, #assignee_id').on 'change', -> | |
8 | + $(this).closest('form').submit() | |
9 | + | |
10 | +class MergeRequest | |
11 | + | |
12 | + constructor: (@opts) -> | |
13 | + this.$el = $('.merge-request') | |
14 | + @diffs_loaded = false | |
15 | + @commits_loaded = false | |
16 | + | |
17 | + this.activateTab(@opts.action) | |
18 | + | |
19 | + this.bindEvents() | |
20 | + | |
21 | + this.initMergeWidget() | |
22 | + this.$('.show-all-commits').on 'click', => | |
23 | + this.showAllCommits() | |
24 | + | |
25 | + # Local jQuery finder | |
26 | + $: (selector) -> | |
27 | + this.$el.find(selector) | |
28 | + | |
29 | + initMergeWidget: -> | |
30 | + this.showState( @opts.current_state ) | |
31 | + | |
32 | + if this.$('.automerge_widget').length and @opts.check_enable | |
33 | + $.get @opts.url_to_automerge_check, (data) => | |
34 | + this.showState( data.state ) | |
35 | + , 'json' | |
36 | + | |
37 | + if @opts.ci_enable | |
38 | + $.get @opts.url_to_ci_check, (data) => | |
39 | + this.showCiState data.status | |
40 | + , 'json' | |
41 | + | |
42 | + bindEvents: -> | |
43 | + this.$('.nav-tabs').on 'click', 'a', (event) => | |
44 | + a = $(event.currentTarget) | |
45 | + | |
46 | + href = a.attr('href') | |
47 | + History.replaceState {path: href}, document.title, href | |
48 | + | |
49 | + event.preventDefault() | |
50 | + | |
51 | + this.$('.nav-tabs').on 'click', 'li', (event) => | |
52 | + this.activateTab($(event.currentTarget).data('action')) | |
53 | + | |
54 | + activateTab: (action) -> | |
55 | + this.$('.nav-tabs li').removeClass 'active' | |
56 | + this.$('.tab-content').hide() | |
57 | + switch action | |
58 | + when 'diffs' | |
59 | + this.$('.nav-tabs .diffs-tab').addClass 'active' | |
60 | + this.loadDiff() unless @diffs_loaded | |
61 | + this.$('.diffs').show() | |
62 | + else | |
63 | + this.$('.nav-tabs .notes-tab').addClass 'active' | |
64 | + this.$('.notes').show() | |
65 | + | |
66 | + showState: (state) -> | |
67 | + $('.automerge_widget').hide() | |
68 | + $('.automerge_widget.' + state).show() | |
69 | + | |
70 | + showCiState: (state) -> | |
71 | + $('.ci_widget').hide() | |
72 | + $('.ci_widget.ci-' + state).show() | |
73 | + | |
74 | + loadDiff: (event) -> | |
75 | + $('.dashboard-loader').show() | |
76 | + $.ajax | |
77 | + type: 'GET' | |
78 | + url: this.$('.nav-tabs .diffs-tab a').attr('href') | |
79 | + beforeSend: => | |
80 | + this.$('.status').addClass 'loading' | |
81 | + | |
82 | + complete: => | |
83 | + @diffs_loaded = true | |
84 | + this.$('.status').removeClass 'loading' | |
85 | + | |
86 | + dataType: 'script' | |
87 | + | |
88 | + showAllCommits: -> | |
89 | + this.$('.first-commits').remove() | |
90 | + this.$('.all-commits').removeClass 'hide' | |
91 | + | |
92 | + alreadyOrCannotBeMerged: -> | |
93 | + this.$('.automerge_widget').hide() | |
94 | + this.$('.merge-in-progress').hide() | |
95 | + this.$('.automerge_widget.already_cannot_be_merged').show() | |
96 | + | |
97 | +this.MergeRequest = MergeRequest | ... | ... |
app/assets/javascripts/milestones.js.coffee
1 | 1 | $ -> |
2 | - $('.milestone-issue-filter tr[data-closed]').addClass('hide') | |
2 | + $('.milestone-issue-filter li[data-closed]').addClass('hide') | |
3 | 3 | |
4 | 4 | $('.milestone-issue-filter ul.nav li a').click -> |
5 | 5 | $('.milestone-issue-filter li').toggleClass('active') |
6 | - $('.milestone-issue-filter tr[data-closed]').toggleClass('hide') | |
6 | + $('.milestone-issue-filter li[data-closed]').toggleClass('hide') | |
7 | 7 | false |
8 | 8 | |
9 | - $('.milestone-merge-requests-filter tr[data-closed]').addClass('hide') | |
9 | + $('.milestone-merge-requests-filter li[data-closed]').addClass('hide') | |
10 | 10 | |
11 | 11 | $('.milestone-merge-requests-filter ul.nav li a').click -> |
12 | 12 | $('.milestone-merge-requests-filter li').toggleClass('active') |
13 | - $('.milestone-merge-requests-filter tr[data-closed]').toggleClass('hide') | |
13 | + $('.milestone-merge-requests-filter li[data-closed]').toggleClass('hide') | |
14 | 14 | false | ... | ... |
app/assets/javascripts/pager.js
... | ... | @@ -4,9 +4,16 @@ var Pager = { |
4 | 4 | disable:false, |
5 | 5 | |
6 | 6 | init: |
7 | - function(limit) { | |
7 | + function(limit, preload) { | |
8 | 8 | this.limit=limit; |
9 | - this.offset=limit; | |
9 | + | |
10 | + if(preload) { | |
11 | + this.offset = 0; | |
12 | + this.getOld(); | |
13 | + } else { | |
14 | + this.offset = limit; | |
15 | + } | |
16 | + | |
10 | 17 | this.initLoadMore(); |
11 | 18 | }, |
12 | 19 | ... | ... |
app/assets/stylesheets/common.scss
... | ... | @@ -117,34 +117,10 @@ span.update-author { |
117 | 117 | } |
118 | 118 | |
119 | 119 | .label { |
120 | - background-color: #474D57; | |
121 | - | |
122 | - &.label-tag { | |
123 | - background: none; | |
124 | - border: none; | |
125 | - padding: 4px 6px; | |
126 | - color: #444; | |
127 | - text-shadow: 0 0 1px #fff; | |
128 | - | |
129 | - &.grouped { | |
130 | - float: left; | |
131 | - margin-right: 6px; | |
132 | - padding: 6px; | |
133 | - } | |
134 | - } | |
135 | - &.label-issue { | |
136 | - background-color: #eee; | |
137 | - border: 1px solid #ccc; | |
138 | - padding: 4px 6px; | |
139 | - color: #444; | |
140 | - text-shadow: 0 0 1px #fff; | |
141 | - | |
142 | - &.grouped { | |
143 | - float: left; | |
144 | - margin-right: 6px; | |
145 | - padding: 6px; | |
146 | - } | |
147 | - } | |
120 | + padding: 0px 4px; | |
121 | + font-size: 10px; | |
122 | + font-style: normal; | |
123 | + background-color: $link_color; | |
148 | 124 | |
149 | 125 | &.label-success { |
150 | 126 | background-color: #8D8; |
... | ... | @@ -425,7 +401,7 @@ li.note { |
425 | 401 | |
426 | 402 | |
427 | 403 | .supp_diff_link, |
428 | -.mr_show_all_commits { | |
404 | +.show-all-commits { | |
429 | 405 | cursor: pointer; |
430 | 406 | } |
431 | 407 | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/blocks.scss
1 | 1 | /** |
2 | 2 | * =================================== |
3 | - * Contain 3 main UI block elements: | |
4 | - * .main_box - for show pages | |
5 | - * .ui-box - for simple block & widgets | |
3 | + * Contain UI block elements: | |
4 | + * .ui-box - for any block & widgets | |
6 | 5 | * =================================== |
7 | 6 | */ |
8 | 7 | |
9 | 8 | /** |
10 | - * UI box element | |
11 | - * contains top, middle, bottom blocks | |
9 | + * UI Block | |
12 | 10 | * |
13 | 11 | */ |
14 | -.main_box { | |
15 | - @extend .borders; | |
16 | - @extend .prepend-top-20; | |
17 | - @extend .append-bottom-20; | |
18 | - border-width: 1px; | |
12 | +.ui-box { | |
13 | + background: #F9F9F9; | |
14 | + margin-bottom: 25px; | |
15 | + border: 1px solid #CCC; | |
19 | 16 | @include solid-shade; |
20 | 17 | |
18 | + &.ui-box-show { | |
19 | + margin:20px 0; | |
20 | + background: #FFF; | |
21 | + } | |
21 | 22 | |
22 | 23 | img { max-width: 100%; } |
23 | 24 | |
... | ... | @@ -27,9 +28,9 @@ |
27 | 28 | } |
28 | 29 | } |
29 | 30 | |
30 | - .top_box_content, | |
31 | - .middle_box_content, | |
32 | - .bottom_box_content { | |
31 | + .ui-box-head, | |
32 | + .ui-box-body, | |
33 | + .ui-box-bottom { | |
33 | 34 | padding: 15px; |
34 | 35 | word-wrap: break-word; |
35 | 36 | |
... | ... | @@ -39,19 +40,25 @@ |
39 | 40 | border: none; |
40 | 41 | padding: 0; |
41 | 42 | } |
43 | + | |
44 | + .clearfix { | |
45 | + margin: 0; | |
46 | + } | |
42 | 47 | } |
43 | 48 | |
44 | - .top_box_content { | |
49 | + .ui-box-head { | |
45 | 50 | .box-title { |
46 | 51 | color: $style_color; |
47 | 52 | font-size: 18px; |
48 | 53 | font-weight: normal; |
49 | 54 | line-height: 28px; |
50 | 55 | } |
56 | + h3 { | |
57 | + margin: 0; | |
58 | + } | |
51 | 59 | } |
52 | 60 | |
53 | - .middle_box_content { | |
54 | - @include border-radius(0); | |
61 | + .ui-box-body { | |
55 | 62 | border: none; |
56 | 63 | font-size: 12px; |
57 | 64 | background-color: #f5f5f5; |
... | ... | @@ -59,24 +66,9 @@ |
59 | 66 | border-top: 1px solid #eee; |
60 | 67 | } |
61 | 68 | |
62 | - .bottom_box_content { | |
69 | + .ui-box-bottom { | |
63 | 70 | border-top: 1px solid #eee; |
64 | 71 | } |
65 | -} | |
66 | - | |
67 | -/** | |
68 | - * Big UI Block for show page content | |
69 | - * | |
70 | - */ | |
71 | -.ui-box { | |
72 | - background: #F9F9F9; | |
73 | - margin-bottom: 25px; | |
74 | - | |
75 | - border: 1px solid #eaeaea; | |
76 | - @include border-radius(4px); | |
77 | - | |
78 | - border-color: #CCC; | |
79 | - @include solid-shade; | |
80 | 72 | |
81 | 73 | &.white { |
82 | 74 | background: #fff; |
... | ... | @@ -86,47 +78,47 @@ |
86 | 78 | margin: 0; |
87 | 79 | } |
88 | 80 | |
89 | - h5, .title { | |
90 | - padding: 0 10px; | |
91 | - @include border-radius(4px 4px 0 0); | |
81 | + .title { | |
92 | 82 | @include bg-gray-gradient; |
93 | - border-top: 1px solid #eaeaea; | |
94 | - border-bottom: 1px solid #bbb; | |
83 | + border-bottom: 1px solid #CCC; | |
84 | + color: #456; | |
85 | + font-size: 16px; | |
86 | + text-shadow: 0 1px 1px #fff; | |
87 | + padding: 0px 10px; | |
88 | + line-height: 36px; | |
89 | + font-size: 14px; | |
90 | + font-weight: normal; | |
95 | 91 | |
96 | 92 | > a { |
97 | 93 | text-shadow: 0 1px 1px #fff; |
98 | 94 | } |
99 | 95 | |
100 | - &.small { | |
101 | - line-height: 28px; | |
102 | - font-size: 14px; | |
103 | - line-height: 28px; | |
104 | - text-shadow: 0 1px 1px white; | |
105 | - } | |
106 | - | |
107 | 96 | form { |
108 | - padding: 9px 0; | |
109 | - margin: 0px; | |
97 | + margin-bottom: 0; | |
98 | + margin-top: 3px; | |
110 | 99 | } |
111 | 100 | |
112 | 101 | .nav-pills { |
113 | - li { | |
114 | - padding: 3px 0; | |
115 | - &.active a { background-color: $style_color; } | |
116 | - a { | |
117 | - @include border-radius(7px); | |
102 | + > li { | |
103 | + > a { | |
104 | + padding: 13px; | |
105 | + margin: 0; | |
106 | + font-size: 13px; | |
107 | + } | |
108 | + &.active { | |
109 | + > a { | |
110 | + background: #D5D5D5; | |
111 | + color: $style_color; | |
112 | + @include border-radius(0); | |
113 | + border-radius: 0; | |
114 | + border-left: 1px solid #CCC; | |
115 | + border-right: 1px solid #CCC; | |
116 | + } | |
118 | 117 | } |
119 | 118 | } |
120 | 119 | } |
121 | 120 | } |
122 | 121 | |
123 | - .bottom { | |
124 | - @include bg-gray-gradient; | |
125 | - @include border-radius(0 0 4px 4px); | |
126 | - border-bottom: none; | |
127 | - border-top: 1px solid #bbb; | |
128 | - } | |
129 | - | |
130 | 122 | &.padded { |
131 | 123 | h5, .title { |
132 | 124 | margin: -20px; |
... | ... | @@ -143,6 +135,7 @@ |
143 | 135 | color: #777; |
144 | 136 | } |
145 | 137 | } |
138 | + | |
146 | 139 | .row_title { |
147 | 140 | font-weight: bold; |
148 | 141 | color: #444; |
... | ... | @@ -151,8 +144,4 @@ |
151 | 144 | text-decoration: underline; |
152 | 145 | } |
153 | 146 | } |
154 | - | |
155 | - .ui-box-body { | |
156 | - padding: 10px; | |
157 | - } | |
158 | 147 | } | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/buttons.scss
app/assets/stylesheets/gitlab_bootstrap/common.scss
... | ... | @@ -17,20 +17,43 @@ |
17 | 17 | .padded { padding:20px } |
18 | 18 | .ipadded { padding:20px!important } |
19 | 19 | .lborder { border-left:1px solid #eee } |
20 | -.no-padding { padding:0 !important; } | |
21 | -.underlined { border-bottom: 1px solid #CCC; } | |
22 | -.no-borders { border: none; } | |
23 | -.vlink { color: $link_color !important; } | |
24 | 20 | .underlined_link { text-decoration: underline; } |
25 | -.borders { border: 1px solid #ccc; @include shade; } | |
26 | 21 | .hint { font-style: italic; color: #999; } |
27 | 22 | .light { color: #888 } |
28 | 23 | .tiny { font-weight: normal } |
29 | 24 | |
30 | 25 | /** PILLS & TABS**/ |
31 | -.nav-pills a:hover { background-color: #888; } | |
32 | -.nav-pills .active a { background-color: $style_color; } | |
26 | +.nav-pills { | |
27 | + .active a { | |
28 | + background: $primary_color; | |
29 | + } | |
30 | + | |
31 | + > li > a { | |
32 | + @include border-radius(0); | |
33 | + } | |
34 | + &.nav-stacked { | |
35 | + > li > a { | |
36 | + border-left: 4px solid #EEE; | |
37 | + padding: 12px; | |
38 | + } | |
39 | + > .active > a { | |
40 | + border-color: #29B; | |
41 | + border-radius: 0; | |
42 | + background: #F1F1F1; | |
43 | + color: $style_color; | |
44 | + font-weight: bold; | |
45 | + } | |
46 | + } | |
47 | +} | |
48 | + | |
33 | 49 | .nav-pills > .active > a > i[class^="icon-"] { background: inherit; } |
50 | + | |
51 | + | |
52 | + | |
53 | +/** | |
54 | + * nav-tabs | |
55 | + * | |
56 | + */ | |
34 | 57 | .nav-tabs > li > a, .nav-pills > li > a { color: $style_color; } |
35 | 58 | .nav.nav-tabs { |
36 | 59 | li { | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/fonts.scss
1 | -@font-face{ | |
2 | - font-family: Korolev; | |
3 | - src: font-url('korolev-medium-compressed.otf'); | |
1 | +@font-face{ | |
2 | + font-family: Yanone; | |
3 | + src: font-url('YanoneKaffeesatz-Light.ttf'); | |
4 | 4 | } |
5 | 5 | |
6 | 6 | /** Typo **/ |
7 | -$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; | |
8 | 7 | \ No newline at end of file |
8 | +$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/lists.scss
... | ... | @@ -23,14 +23,12 @@ |
23 | 23 | border-bottom: 1px solid #ADF; |
24 | 24 | } |
25 | 25 | |
26 | - &:first-child { | |
27 | - @include border-radius(4px 4px 0 0); | |
28 | - border-top: none; | |
29 | - } | |
30 | - | |
31 | 26 | &:last-child { |
32 | - @include border-radius(0 0 4px 4px); | |
33 | - border: none; | |
27 | + border-bottom: none; | |
28 | + | |
29 | + &.bottom { | |
30 | + background: #f5f5f5; | |
31 | + } | |
34 | 32 | } |
35 | 33 | |
36 | 34 | .author { color: #999; } | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
... | ... | @@ -62,8 +62,8 @@ |
62 | 62 | @mixin header-font { |
63 | 63 | color: $style_color; |
64 | 64 | text-shadow: 0 1px 1px #FFF; |
65 | - font-family: 'Korolev', sans-serif; | |
66 | - font-size: 28px; | |
67 | - line-height: 48px; | |
65 | + font-family: 'Yanone', sans-serif; | |
66 | + font-size: 26px; | |
67 | + line-height: 42px; | |
68 | 68 | font-weight: normal; |
69 | 69 | } | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/tables.scss
app/assets/stylesheets/sections/commits.scss
1 | -.commit-box { | |
2 | - @extend .main_box; | |
3 | - | |
4 | - .commit-head { | |
5 | - @extend .top_box_content; | |
6 | - | |
7 | - .commit-title { | |
8 | - line-height: 26px; | |
9 | - margin: 0; | |
10 | - } | |
11 | - | |
12 | - .commit-description { | |
13 | - font-size: 14px; | |
14 | - border: none; | |
15 | - background-color: white; | |
16 | - padding-top: 10px; | |
17 | - } | |
18 | - | |
19 | - .browse-button { | |
20 | - @extend .btn; | |
21 | - @extend .btn-small; | |
22 | - float: right; | |
23 | - } | |
24 | - } | |
25 | - | |
26 | - .commit-info { | |
27 | - @extend .middle_box_content; | |
28 | - @extend .clearfix; | |
29 | - | |
30 | - .sha-block { | |
31 | - text-align: right; | |
32 | - &:first-child { | |
33 | - padding-bottom: 6px; | |
34 | - } | |
35 | - | |
36 | - a { | |
37 | - border-bottom: 1px solid #aaa; | |
38 | - margin-left: 9px; | |
39 | - } | |
40 | - } | |
41 | - | |
42 | - &.merge-commit .sha-block { | |
43 | - clear: right; | |
44 | - } | |
45 | - | |
46 | - .committer { | |
47 | - padding-left: 32px; | |
48 | - } | |
49 | - | |
50 | - .author a, | |
51 | - .committer a { | |
52 | - font-size: 14px; | |
53 | - line-height: 22px; | |
54 | - text-shadow: 0 1px 1px #fff; | |
55 | - color: #777; | |
56 | - &:hover { | |
57 | - color: #999; | |
58 | - } | |
59 | - } | |
60 | - | |
61 | - .avatar { | |
62 | - margin-right: 10px; | |
63 | - } | |
64 | - } | |
65 | -} | |
66 | - | |
67 | 1 | /** |
68 | 2 | * |
69 | 3 | * COMMIT SHOw |
70 | 4 | * |
71 | 5 | */ |
6 | +.commit-committer-link, | |
7 | +.commit-author-link { | |
8 | + font-size: 13px; | |
9 | + color: #555; | |
10 | + &:hover { | |
11 | + color: #999; | |
12 | + } | |
13 | +} | |
14 | + | |
72 | 15 | .diff_file { |
73 | 16 | border: 1px solid #CCC; |
74 | 17 | margin-bottom: 1em; |
... | ... | @@ -258,13 +201,6 @@ |
258 | 201 | min-width: 65px; |
259 | 202 | font-family: $monospace; |
260 | 203 | } |
261 | - | |
262 | - .commit-author-name { | |
263 | - color: #777; | |
264 | - &:hover { | |
265 | - color: #999; | |
266 | - } | |
267 | - } | |
268 | 204 | } |
269 | 205 | |
270 | 206 | .diff_file_header a, | ... | ... |
app/assets/stylesheets/sections/events.scss
app/assets/stylesheets/sections/header.scss
... | ... | @@ -33,22 +33,29 @@ header { |
33 | 33 | * |
34 | 34 | */ |
35 | 35 | .app_logo { |
36 | - width: 170px; | |
37 | 36 | float: left; |
37 | + margin-right: 15px; | |
38 | + position: relative; | |
39 | + top: -5px; | |
40 | + padding-top: 5px; | |
41 | + | |
38 | 42 | a { |
39 | 43 | float: left; |
40 | 44 | padding: 0px; |
45 | + margin: 0 10px; | |
41 | 46 | |
42 | 47 | h1 { |
43 | - width: 90px; | |
44 | 48 | background: url('logo_dark.png') no-repeat 0px 2px; |
45 | 49 | float: left; |
46 | - margin-left: 2px; | |
47 | - padding-left: 45px; | |
48 | 50 | height: 40px; |
51 | + width: 40px; | |
49 | 52 | @include header-font; |
53 | + text-indent: -9999px; | |
50 | 54 | } |
51 | 55 | } |
56 | + &:hover { | |
57 | + background-color: #EEE; | |
58 | + } | |
52 | 59 | } |
53 | 60 | |
54 | 61 | /** |
... | ... | @@ -60,7 +67,7 @@ header { |
60 | 67 | position: relative; |
61 | 68 | float: left; |
62 | 69 | margin: 0; |
63 | - margin-right: 30px; | |
70 | + margin-left: 15px; | |
64 | 71 | @include header-font; |
65 | 72 | } |
66 | 73 | |
... | ... | @@ -233,7 +240,7 @@ header { |
233 | 240 | .app_logo { |
234 | 241 | a { |
235 | 242 | h1 { |
236 | - background: url('logo_white.png') no-repeat 0px 2px; | |
243 | + background: url('logo_white.png') no-repeat center center; | |
237 | 244 | color: #fff; |
238 | 245 | text-shadow: 0 1px 1px #111; |
239 | 246 | } |
... | ... | @@ -244,5 +251,23 @@ header { |
244 | 251 | text-shadow: 0 1px 1px #111; |
245 | 252 | } |
246 | 253 | } |
254 | + | |
255 | + .app_logo { | |
256 | + .separator { | |
257 | + margin-left: 0; | |
258 | + margin-right: 0; | |
259 | + } | |
260 | + } | |
261 | + | |
262 | + .separator { | |
263 | + float: left; | |
264 | + height: 60px; | |
265 | + width: 1px; | |
266 | + background: white; | |
267 | + border-left: 1px solid #DDD; | |
268 | + margin-top: -10px; | |
269 | + margin-left: 10px; | |
270 | + margin-right: 10px; | |
271 | + } | |
247 | 272 | } |
248 | 273 | ... | ... |
app/assets/stylesheets/sections/issues.scss
1 | -.issue_form_box { | |
2 | - @extend .main_box; | |
3 | - .issue_title { | |
4 | - @extend .top_box_content; | |
5 | - .clearfix { | |
6 | - margin-bottom: 0px; | |
7 | - input { | |
8 | - @extend .span8; | |
9 | - } | |
10 | - } | |
11 | - } | |
12 | - .issue_middle_block { | |
13 | - @extend .middle_box_content; | |
14 | - height: 30px; | |
15 | - .issue_assignee { | |
16 | - @extend .span6; | |
17 | - float: left; | |
18 | - } | |
19 | - .issue_milestone { | |
20 | - @extend .span4; | |
21 | - float: left; | |
22 | - } | |
23 | - } | |
24 | - .issue_description { | |
25 | - @extend .bottom_box_content; | |
26 | - } | |
27 | -} | |
28 | - | |
29 | 1 | .issues_table { |
30 | 2 | .issue { |
31 | - padding: 7px 10px; | |
3 | + padding: 10px; | |
32 | 4 | |
33 | 5 | .issue_check { |
34 | 6 | float: left; |
... | ... | @@ -82,38 +54,34 @@ input.check_all_issues { |
82 | 54 | } |
83 | 55 | } |
84 | 56 | |
85 | -@media (min-width: 800px) { .issues_filters select { width: 160px; } } | |
86 | -@media (min-width: 1000px) { .issues_filters select { width: 200px; } } | |
57 | +@media (min-width: 800px) { .issues_filters select { width: 160px; } } | |
87 | 58 | @media (min-width: 1200px) { .issues_filters select { width: 220px; } } |
88 | 59 | |
60 | +@media (min-width: 800px) { .issues_bulk_update select { width: 120px; } } | |
61 | +@media (min-width: 1200px) { .issues_bulk_update select { width: 160px; } } | |
89 | 62 | |
90 | 63 | #issues-table-holder { |
91 | 64 | .issues_filters { |
92 | - form { | |
93 | - padding: 0; | |
94 | - margin: 0; | |
95 | - margin-top:7px | |
96 | - } | |
97 | 65 | } |
98 | 66 | |
99 | 67 | .issues_bulk_update { |
100 | 68 | margin: 0; |
101 | 69 | form { |
102 | - padding: 0; | |
103 | - margin: 0; | |
104 | - margin-top:7px | |
70 | + float:left; | |
105 | 71 | } |
72 | + | |
106 | 73 | .update_selected_issues { |
107 | 74 | position: relative; |
108 | - top:-2px; | |
75 | + top:5px; | |
109 | 76 | margin-left: 4px; |
110 | 77 | float: left; |
111 | 78 | } |
112 | 79 | |
113 | 80 | .update_issues_text { |
114 | 81 | padding: 3px; |
115 | - line-height: 18px; | |
82 | + line-height: 28px; | |
116 | 83 | float: left; |
84 | + color: #479; | |
117 | 85 | } |
118 | 86 | } |
119 | 87 | } | ... | ... |
app/assets/stylesheets/sections/merge_requests.scss
1 | -/** | |
2 | - * MR form | |
3 | - * | |
4 | - */ | |
5 | - | |
6 | -.mr_branch_box { | |
7 | - @extend .ui-box; | |
8 | - margin-bottom: 20px; | |
9 | - | |
10 | - .body { | |
11 | - background: #f1f1f1; | |
12 | - } | |
13 | - | |
14 | -} | |
15 | 1 | |
16 | 2 | /** |
17 | 3 | * MR -> show: Automerge widget |
... | ... | @@ -47,6 +33,7 @@ |
47 | 33 | } |
48 | 34 | label { |
49 | 35 | color: #444; |
36 | + text-align: left | |
50 | 37 | } |
51 | 38 | } |
52 | 39 | |
... | ... | @@ -56,7 +43,7 @@ |
56 | 43 | } |
57 | 44 | } |
58 | 45 | |
59 | -.mr_nav_tabs { | |
46 | +.merge-request .nav-tabs{ | |
60 | 47 | li { |
61 | 48 | a { |
62 | 49 | font-weight: bold; |
... | ... | @@ -67,7 +54,7 @@ |
67 | 54 | } |
68 | 55 | |
69 | 56 | li.merge_request { |
70 | - padding: 7px 10px; | |
57 | + padding: 10px; | |
71 | 58 | img.avatar { |
72 | 59 | width: 32px; |
73 | 60 | margin-top: 1px; |
... | ... | @@ -78,7 +65,7 @@ li.merge_request { |
78 | 65 | } |
79 | 66 | } |
80 | 67 | |
81 | -.merge_in_progress { | |
68 | +.merge-in-progress { | |
82 | 69 | @extend .padded; |
83 | 70 | @extend .append-bottom-10; |
84 | 71 | } |
... | ... | @@ -120,19 +107,3 @@ li.merge_request { |
120 | 107 | .mr_direction_tip { |
121 | 108 | margin-top:40px |
122 | 109 | } |
123 | - | |
124 | -.merge_requests_form_box { | |
125 | - @extend .main_box; | |
126 | - .merge_requests_middle_box { | |
127 | - @extend .middle_box_content; | |
128 | - height: 30px; | |
129 | - .merge_requests_assignee { | |
130 | - @extend .span6; | |
131 | - float: left; | |
132 | - } | |
133 | - .merge_requests_milestone { | |
134 | - @extend .span4; | |
135 | - float: left; | |
136 | - } | |
137 | - } | |
138 | -} | ... | ... |
app/assets/stylesheets/sections/notes.scss
app/assets/stylesheets/sections/projects.scss
... | ... | @@ -8,16 +8,12 @@ |
8 | 8 | |
9 | 9 | .groups_box, |
10 | 10 | .projects_box { |
11 | - > h5 { | |
12 | - color: $style_color; | |
13 | - font-size: 16px; | |
14 | - text-shadow: 0 1px 1px #fff; | |
15 | - padding: 2px 10px; | |
16 | - line-height: 32px; | |
17 | - font-size: 14px; | |
11 | + > .title { | |
12 | + padding: 2px 15px; | |
18 | 13 | } |
19 | 14 | .nav-projects-tabs li { padding: 0; } |
20 | 15 | .well-list { |
16 | + li { padding: 15px; } | |
21 | 17 | .arrow { |
22 | 18 | float: right; |
23 | 19 | padding: 10px; |
... | ... | @@ -109,7 +105,7 @@ ul.nav.nav-projects-tabs { |
109 | 105 | |
110 | 106 | li { |
111 | 107 | a { |
112 | - padding: 4px 20px; | |
108 | + padding: 6px 25px; | |
113 | 109 | margin-top: 2px; |
114 | 110 | border-color: #DDD; |
115 | 111 | background-color: #EEE; | ... | ... |
app/assets/stylesheets/themes/ui_basic.scss
... | ... | @@ -4,21 +4,8 @@ |
4 | 4 | * |
5 | 5 | */ |
6 | 6 | .ui_basic { |
7 | - .app_logo { | |
8 | - .separator { | |
9 | - margin-left: 0; | |
10 | - margin-right: 0; | |
11 | - } | |
12 | - } | |
13 | - | |
14 | 7 | .separator { |
15 | - float: left; | |
16 | - height: 60px; | |
17 | - width: 1px; | |
18 | 8 | background: white; |
19 | 9 | border-left: 1px solid #DDD; |
20 | - margin-top: -10px; | |
21 | - margin-left: 10px; | |
22 | - margin-right: 10px; | |
23 | 10 | } |
24 | 11 | } | ... | ... |
app/assets/stylesheets/themes/ui_color.scss
app/assets/stylesheets/themes/ui_gray.scss
app/assets/stylesheets/themes/ui_mars.scss
... | ... | @@ -46,21 +46,26 @@ |
46 | 46 | .app_logo { |
47 | 47 | a { |
48 | 48 | h1 { |
49 | - background: url('logo_white.png') no-repeat 0px 2px; | |
49 | + background: url('logo_white.png') no-repeat center center; | |
50 | 50 | color: #eee; |
51 | 51 | text-shadow: 0 1px 1px #111; |
52 | 52 | } |
53 | 53 | } |
54 | - .separator { | |
55 | - display: none; | |
54 | + &:hover { | |
55 | + background-color: #41464e; | |
56 | 56 | } |
57 | - | |
58 | 57 | } |
59 | 58 | .project_name { |
60 | 59 | color: #eee; |
61 | 60 | text-shadow: 0 1px 1px #111; |
62 | 61 | } |
63 | 62 | } |
63 | + | |
64 | + .separator { | |
65 | + background: #31363E; | |
66 | + border-left: 1px solid #666; | |
67 | + } | |
68 | + | |
64 | 69 | /* |
65 | 70 | * End of Application Header |
66 | 71 | * | ... | ... |
app/assets/stylesheets/themes/ui_modern.scss
app/contexts/commit_load_context.rb
... | ... | @@ -9,16 +9,16 @@ class CommitLoadContext < BaseContext |
9 | 9 | status: :ok |
10 | 10 | } |
11 | 11 | |
12 | - commit = project.commit(params[:id]) | |
12 | + commit = project.repository.commit(params[:id]) | |
13 | 13 | |
14 | 14 | if commit |
15 | 15 | commit = CommitDecorator.decorate(commit) |
16 | - line_notes = project.commit_line_notes(commit) | |
16 | + line_notes = project.notes.for_commit_id(commit.id).inline | |
17 | 17 | |
18 | 18 | result[:commit] = commit |
19 | 19 | result[:note] = project.build_commit_note(commit) |
20 | 20 | result[:line_notes] = line_notes |
21 | - result[:notes_count] = line_notes.count + project.commit_notes(commit).count | |
21 | + result[:notes_count] = project.notes.for_commit_id(commit.id).count | |
22 | 22 | |
23 | 23 | begin |
24 | 24 | result[:suppress_diff] = true if commit.diffs.size > Commit::DIFF_SAFE_SIZE && !params[:force_show_diff] | ... | ... |
... | ... | @@ -0,0 +1,31 @@ |
1 | +class FilterContext | |
2 | + attr_accessor :items, :params | |
3 | + | |
4 | + def initialize(items, params) | |
5 | + @items = items | |
6 | + @params = params | |
7 | + end | |
8 | + | |
9 | + def execute | |
10 | + apply_filter(items) | |
11 | + end | |
12 | + | |
13 | + def apply_filter items | |
14 | + if params[:project_id] | |
15 | + items = items.where(project_id: params[:project_id]) | |
16 | + end | |
17 | + | |
18 | + if params[:search].present? | |
19 | + items = items.search(params[:search]) | |
20 | + end | |
21 | + | |
22 | + case params[:status] | |
23 | + when 'closed' | |
24 | + items.closed | |
25 | + when 'all' | |
26 | + items | |
27 | + else | |
28 | + items.opened | |
29 | + end | |
30 | + end | |
31 | +end | ... | ... |
app/contexts/issues_list_context.rb
... | ... | @@ -4,7 +4,7 @@ class IssuesListContext < BaseContext |
4 | 4 | attr_accessor :issues |
5 | 5 | |
6 | 6 | def execute |
7 | - @issues = case params[:f] | |
7 | + @issues = case params[:status] | |
8 | 8 | when issues_filter[:all] then @project.issues |
9 | 9 | when issues_filter[:closed] then @project.issues.closed |
10 | 10 | when issues_filter[:to_me] then @project.issues.opened.assigned(current_user) | ... | ... |
app/contexts/notes/load_context.rb
... | ... | @@ -9,7 +9,7 @@ module Notes |
9 | 9 | |
10 | 10 | @notes = case target_type |
11 | 11 | when "commit" |
12 | - project.commit_notes(project.commit(target_id)).fresh | |
12 | + project.notes.for_commit_id(target_id).not_inline.fresh | |
13 | 13 | when "issue" |
14 | 14 | project.issues.find(target_id).notes.inc_author.fresh |
15 | 15 | when "merge_request" |
... | ... | @@ -18,7 +18,7 @@ module Notes |
18 | 18 | project.snippets.find(target_id).notes.fresh |
19 | 19 | when "wall" |
20 | 20 | # this is the only case, where the order is DESC |
21 | - project.common_notes.order("created_at DESC, id DESC").limit(50) | |
21 | + project.notes.common.inc_author_project.order("created_at DESC, id DESC").limit(50) | |
22 | 22 | end |
23 | 23 | |
24 | 24 | @notes = if after_id | ... | ... |
app/contexts/test_hook_context.rb
1 | 1 | class TestHookContext < BaseContext |
2 | 2 | def execute |
3 | 3 | hook = project.hooks.find(params[:id]) |
4 | - commits = project.commits(project.default_branch, nil, 3) | |
4 | + commits = project.repository.commits(project.default_branch, nil, 3) | |
5 | 5 | data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user) |
6 | 6 | hook.execute(data) |
7 | 7 | end | ... | ... |
app/controllers/admin/dashboard_controller.rb
... | ... | @@ -3,10 +3,6 @@ class Admin::DashboardController < AdminController |
3 | 3 | @projects = Project.order("created_at DESC").limit(10) |
4 | 4 | @users = User.order("created_at DESC").limit(10) |
5 | 5 | |
6 | - @resque_accessible = true | |
7 | - @workers = Resque.workers | |
8 | - @pending_jobs = Resque.size(:post_receive) | |
9 | - | |
10 | 6 | rescue Redis::InheritedError |
11 | 7 | @resque_accessible = false |
12 | 8 | end | ... | ... |
app/controllers/admin/groups_controller.rb
1 | 1 | class Admin::GroupsController < AdminController |
2 | - before_filter :group, only: [:edit, :show, :update, :destroy, :project_update] | |
2 | + before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update] | |
3 | 3 | |
4 | 4 | def index |
5 | 5 | @groups = Group.order('name ASC') |
... | ... | @@ -12,6 +12,8 @@ class Admin::GroupsController < AdminController |
12 | 12 | @projects = @projects.not_in_group(@group) if @group.projects.present? |
13 | 13 | @projects = @projects.all |
14 | 14 | @projects.reject!(&:empty_repo?) |
15 | + | |
16 | + @users = User.active | |
15 | 17 | end |
16 | 18 | |
17 | 19 | def new |
... | ... | @@ -65,7 +67,14 @@ class Admin::GroupsController < AdminController |
65 | 67 | redirect_to :back, notice: 'Group was successfully updated.' |
66 | 68 | end |
67 | 69 | |
70 | + def project_teams_update | |
71 | + @group.add_users_to_project_teams(params[:user_ids], params[:project_access]) | |
72 | + redirect_to [:admin, @group], notice: 'Users was successfully added.' | |
73 | + end | |
74 | + | |
68 | 75 | def destroy |
76 | + @group.truncate_teams | |
77 | + | |
69 | 78 | @group.destroy |
70 | 79 | |
71 | 80 | redirect_to admin_groups_path, notice: 'Group was successfully deleted.' | ... | ... |
app/controllers/admin/projects_controller.rb
... | ... | @@ -10,6 +10,7 @@ class Admin::ProjectsController < AdminController |
10 | 10 | end |
11 | 11 | |
12 | 12 | def show |
13 | + @repository = @project.repository | |
13 | 14 | @users = User.active |
14 | 15 | @users = @users.not_in_project(@project) if @project.users.present? |
15 | 16 | @users = @users.all |
... | ... | @@ -19,7 +20,7 @@ class Admin::ProjectsController < AdminController |
19 | 20 | end |
20 | 21 | |
21 | 22 | def team_update |
22 | - @project.add_users_ids_to_team(params[:user_ids], params[:project_access]) | |
23 | + @project.team.add_users_ids(params[:user_ids], params[:project_access]) | |
23 | 24 | |
24 | 25 | redirect_to [:admin, @project], notice: 'Project was successfully updated.' |
25 | 26 | end |
... | ... | @@ -35,6 +36,9 @@ class Admin::ProjectsController < AdminController |
35 | 36 | end |
36 | 37 | |
37 | 38 | def destroy |
39 | + # Delete team first in order to prevent multiple gitolite calls | |
40 | + @project.team.truncate | |
41 | + | |
38 | 42 | @project.destroy |
39 | 43 | |
40 | 44 | redirect_to admin_projects_path, notice: 'Project was successfully deleted.' | ... | ... |
app/controllers/admin/users_controller.rb
... | ... | @@ -3,13 +3,13 @@ class Admin::UsersController < AdminController |
3 | 3 | @admin_users = User.scoped |
4 | 4 | @admin_users = @admin_users.filter(params[:filter]) |
5 | 5 | @admin_users = @admin_users.search(params[:name]) if params[:name].present? |
6 | - @admin_users = @admin_users.order("name ASC").page(params[:page]) | |
6 | + @admin_users = @admin_users.alphabetically.page(params[:page]) | |
7 | 7 | end |
8 | 8 | |
9 | 9 | def show |
10 | 10 | @admin_user = User.find(params[:id]) |
11 | 11 | |
12 | - @projects = if @admin_user.projects.empty? | |
12 | + @projects = if @admin_user.authorized_projects.empty? | |
13 | 13 | Project |
14 | 14 | else |
15 | 15 | Project.without_user(@admin_user) |
... | ... | @@ -19,9 +19,9 @@ class Admin::UsersController < AdminController |
19 | 19 | def team_update |
20 | 20 | @admin_user = User.find(params[:id]) |
21 | 21 | |
22 | - UsersProject.user_bulk_import( | |
23 | - @admin_user, | |
22 | + UsersProject.add_users_into_projects( | |
24 | 23 | params[:project_ids], |
24 | + [@admin_user.id], | |
25 | 25 | params[:project_access] |
26 | 26 | ) |
27 | 27 | |
... | ... | @@ -98,7 +98,7 @@ class Admin::UsersController < AdminController |
98 | 98 | |
99 | 99 | def destroy |
100 | 100 | @admin_user = User.find(params[:id]) |
101 | - if @admin_user.my_own_projects.count > 0 | |
101 | + if @admin_user.personal_projects.count > 0 | |
102 | 102 | redirect_to admin_users_path, alert: "User is a project owner and can't be removed." and return |
103 | 103 | end |
104 | 104 | @admin_user.destroy | ... | ... |
app/controllers/application_controller.rb
app/controllers/commits_controller.rb
... | ... | @@ -9,10 +9,10 @@ class CommitsController < ProjectResourceController |
9 | 9 | before_filter :require_non_empty_project |
10 | 10 | |
11 | 11 | def show |
12 | - @repo = @project.repo | |
12 | + @repo = @project.repository | |
13 | 13 | @limit, @offset = (params[:limit] || 40), (params[:offset] || 0) |
14 | 14 | |
15 | - @commits = @project.commits(@ref, @path, @limit, @offset) | |
15 | + @commits = @repo.commits(@ref, @path, @limit, @offset) | |
16 | 16 | @commits = CommitDecorator.decorate(@commits) |
17 | 17 | |
18 | 18 | respond_to do |format| | ... | ... |
app/controllers/dashboard_controller.rb
... | ... | @@ -20,7 +20,7 @@ class DashboardController < ApplicationController |
20 | 20 | |
21 | 21 | @projects = @projects.page(params[:page]).per(30) |
22 | 22 | |
23 | - @events = Event.in_projects(current_user.project_ids) | |
23 | + @events = Event.in_projects(current_user.authorized_projects.pluck(:id)) | |
24 | 24 | @events = @event_filter.apply_filter(@events) |
25 | 25 | @events = @events.limit(20).offset(params[:offset] || 0) |
26 | 26 | |
... | ... | @@ -36,14 +36,14 @@ class DashboardController < ApplicationController |
36 | 36 | # Get authored or assigned open merge requests |
37 | 37 | def merge_requests |
38 | 38 | @merge_requests = current_user.cared_merge_requests |
39 | - @merge_requests = dashboard_filter(@merge_requests) | |
39 | + @merge_requests = FilterContext.new(@merge_requests, params).execute | |
40 | 40 | @merge_requests = @merge_requests.recent.page(params[:page]).per(20) |
41 | 41 | end |
42 | 42 | |
43 | 43 | # Get only assigned issues |
44 | 44 | def issues |
45 | 45 | @issues = current_user.assigned_issues |
46 | - @issues = dashboard_filter(@issues) | |
46 | + @issues = FilterContext.new(@issues, params).execute | |
47 | 47 | @issues = @issues.recent.page(params[:page]).per(20) |
48 | 48 | @issues = @issues.includes(:author, :project) |
49 | 49 | |
... | ... | @@ -60,25 +60,7 @@ class DashboardController < ApplicationController |
60 | 60 | end |
61 | 61 | |
62 | 62 | def event_filter |
63 | - @event_filter ||= EventFilter.new(params[:event_filter]) | |
64 | - end | |
65 | - | |
66 | - def dashboard_filter items | |
67 | - if params[:project_id] | |
68 | - items = items.where(project_id: params[:project_id]) | |
69 | - end | |
70 | - | |
71 | - if params[:search].present? | |
72 | - items = items.search(params[:search]) | |
73 | - end | |
74 | - | |
75 | - case params[:status] | |
76 | - when 'closed' | |
77 | - items.closed | |
78 | - when 'all' | |
79 | - items | |
80 | - else | |
81 | - items.opened | |
82 | - end | |
63 | + filters = cookies['event_filter'].split(',') if cookies['event_filter'] | |
64 | + @event_filter ||= EventFilter.new(filters) | |
83 | 65 | end |
84 | 66 | end | ... | ... |
app/controllers/groups_controller.rb
... | ... | @@ -21,15 +21,16 @@ class GroupsController < ApplicationController |
21 | 21 | |
22 | 22 | # Get authored or assigned open merge requests |
23 | 23 | def merge_requests |
24 | - @merge_requests = current_user.cared_merge_requests.opened | |
25 | - @merge_requests = @merge_requests.of_group(@group).recent.page(params[:page]).per(20) | |
24 | + @merge_requests = current_user.cared_merge_requests.of_group(@group) | |
25 | + @merge_requests = FilterContext.new(@merge_requests, params).execute | |
26 | + @merge_requests = @merge_requests.recent.page(params[:page]).per(20) | |
26 | 27 | end |
27 | 28 | |
28 | 29 | # Get only assigned issues |
29 | 30 | def issues |
30 | - @user = current_user | |
31 | - @issues = current_user.assigned_issues.opened | |
32 | - @issues = @issues.of_group(@group).recent.page(params[:page]).per(20) | |
31 | + @issues = current_user.assigned_issues.of_group(@group) | |
32 | + @issues = FilterContext.new(@issues, params).execute | |
33 | + @issues = @issues.recent.page(params[:page]).per(20) | |
33 | 34 | @issues = @issues.includes(:author, :project) |
34 | 35 | |
35 | 36 | respond_to do |format| |
... | ... | @@ -44,6 +45,7 @@ class GroupsController < ApplicationController |
44 | 45 | @projects = result[:projects] |
45 | 46 | @merge_requests = result[:merge_requests] |
46 | 47 | @issues = result[:issues] |
48 | + @wiki_pages = result[:wiki_pages] | |
47 | 49 | end |
48 | 50 | |
49 | 51 | def people |
... | ... | @@ -53,9 +55,16 @@ class GroupsController < ApplicationController |
53 | 55 | |
54 | 56 | if @project |
55 | 57 | @team_member = @project.users_projects.new |
58 | + else | |
59 | + @team_member = UsersProject.new | |
56 | 60 | end |
57 | 61 | end |
58 | 62 | |
63 | + def team_members | |
64 | + @group.add_users_to_project_teams(params[:user_ids], params[:project_access]) | |
65 | + redirect_to people_group_path(@group), notice: 'Users was successfully added.' | |
66 | + end | |
67 | + | |
59 | 68 | protected |
60 | 69 | |
61 | 70 | def group |
... | ... | @@ -63,7 +72,7 @@ class GroupsController < ApplicationController |
63 | 72 | end |
64 | 73 | |
65 | 74 | def projects |
66 | - @projects ||= group.projects.authorized_for(current_user).sorted_by_activity | |
75 | + @projects ||= current_user.authorized_projects.where(namespace_id: group.id).sorted_by_activity | |
67 | 76 | end |
68 | 77 | |
69 | 78 | def project_ids | ... | ... |
app/controllers/merge_requests_controller.rb
... | ... | @@ -74,6 +74,8 @@ class MergeRequestsController < ProjectResourceController |
74 | 74 | @merge_request.check_if_can_be_merged |
75 | 75 | end |
76 | 76 | render json: {state: @merge_request.human_state} |
77 | + rescue Gitlab::SatelliteNotExistError | |
78 | + render json: {state: :no_satellite} | |
77 | 79 | end |
78 | 80 | |
79 | 81 | def automerge |
... | ... | @@ -88,12 +90,12 @@ class MergeRequestsController < ProjectResourceController |
88 | 90 | end |
89 | 91 | |
90 | 92 | def branch_from |
91 | - @commit = project.commit(params[:ref]) | |
93 | + @commit = @repository.commit(params[:ref]) | |
92 | 94 | @commit = CommitDecorator.decorate(@commit) |
93 | 95 | end |
94 | 96 | |
95 | 97 | def branch_to |
96 | - @commit = project.commit(params[:ref]) | |
98 | + @commit = @repository.commit(params[:ref]) | |
97 | 99 | @commit = CommitDecorator.decorate(@commit) |
98 | 100 | end |
99 | 101 | ... | ... |
app/controllers/project_resource_controller.rb
app/controllers/projects_controller.rb
... | ... | @@ -2,6 +2,7 @@ require Rails.root.join('lib', 'gitlab', 'graph', 'json_builder') |
2 | 2 | |
3 | 3 | class ProjectsController < ProjectResourceController |
4 | 4 | skip_before_filter :project, only: [:new, :create] |
5 | + skip_before_filter :repository, only: [:new, :create] | |
5 | 6 | |
6 | 7 | # Authorize |
7 | 8 | before_filter :authorize_read_project!, except: [:index, :new, :create] |
... | ... | @@ -58,7 +59,7 @@ class ProjectsController < ProjectResourceController |
58 | 59 | |
59 | 60 | respond_to do |format| |
60 | 61 | format.html do |
61 | - unless @project.empty_repo? | |
62 | + if @project.repository && !@project.repository.empty? | |
62 | 63 | @last_push = current_user.recent_push(@project.id) |
63 | 64 | render :show |
64 | 65 | else |
... | ... | @@ -102,11 +103,10 @@ class ProjectsController < ProjectResourceController |
102 | 103 | def destroy |
103 | 104 | return access_denied! unless can?(current_user, :remove_project, project) |
104 | 105 | |
105 | - # Disable the UsersProject update_repository call, otherwise it will be | |
106 | - # called once for every person removed from the project | |
107 | - UsersProject.skip_callback(:destroy, :after, :update_repository) | |
106 | + # Delete team first in order to prevent multiple gitolite calls | |
107 | + project.team.truncate | |
108 | + | |
108 | 109 | project.destroy |
109 | - UsersProject.set_callback(:destroy, :after, :update_repository) | |
110 | 110 | |
111 | 111 | respond_to do |format| |
112 | 112 | format.html { redirect_to root_path } | ... | ... |
app/controllers/refs_controller.rb
... | ... | @@ -12,7 +12,7 @@ class RefsController < ProjectResourceController |
12 | 12 | respond_to do |format| |
13 | 13 | format.html do |
14 | 14 | new_path = if params[:destination] == "tree" |
15 | - project_tree_path(@project, @ref) | |
15 | + project_tree_path(@project, (@ref + "/" + params[:path])) | |
16 | 16 | else |
17 | 17 | project_commits_path(@project, @ref) |
18 | 18 | end |
... | ... | @@ -31,7 +31,7 @@ class RefsController < ProjectResourceController |
31 | 31 | contents = @tree.contents |
32 | 32 | @logs = contents.map do |content| |
33 | 33 | file = params[:path] ? File.join(params[:path], content.name) : content.name |
34 | - last_commit = @project.commits(@commit.id, file, 1).last | |
34 | + last_commit = @repo.commits(@commit.id, file, 1).last | |
35 | 35 | last_commit = CommitDecorator.decorate(last_commit) |
36 | 36 | { |
37 | 37 | file_name: content.name, |
... | ... | @@ -45,10 +45,10 @@ class RefsController < ProjectResourceController |
45 | 45 | def define_tree_vars |
46 | 46 | params[:path] = nil if params[:path].blank? |
47 | 47 | |
48 | - @repo = project.repo | |
49 | - @commit = project.commit(@ref) | |
48 | + @repo = project.repository | |
49 | + @commit = @repo.commit(@ref) | |
50 | 50 | @commit = CommitDecorator.decorate(@commit) |
51 | - @tree = Tree.new(@commit.tree, project, @ref, params[:path]) | |
51 | + @tree = Tree.new(@commit.tree, @ref, params[:path]) | |
52 | 52 | @tree = TreeDecorator.new(@tree) |
53 | 53 | @hex_path = Digest::SHA1.hexdigest(params[:path] || "") |
54 | 54 | ... | ... |
app/controllers/repositories_controller.rb
... | ... | @@ -5,19 +5,19 @@ class RepositoriesController < ProjectResourceController |
5 | 5 | before_filter :require_non_empty_project |
6 | 6 | |
7 | 7 | def show |
8 | - @activities = @project.commits_with_refs(20) | |
8 | + @activities = @repository.commits_with_refs(20) | |
9 | 9 | end |
10 | 10 | |
11 | 11 | def branches |
12 | - @branches = @project.branches | |
12 | + @branches = @repository.branches | |
13 | 13 | end |
14 | 14 | |
15 | 15 | def tags |
16 | - @tags = @project.tags | |
16 | + @tags = @repository.tags | |
17 | 17 | end |
18 | 18 | |
19 | 19 | def stats |
20 | - @stats = Gitlab::GitStats.new(@project.repo, @project.root_ref) | |
20 | + @stats = Gitlab::GitStats.new(@repository.raw, @repository.root_ref) | |
21 | 21 | @graph = @stats.graph |
22 | 22 | end |
23 | 23 | |
... | ... | @@ -27,7 +27,7 @@ class RepositoriesController < ProjectResourceController |
27 | 27 | end |
28 | 28 | |
29 | 29 | |
30 | - file_path = @project.archive_repo(params[:ref]) | |
30 | + file_path = @repository.archive_repo(params[:ref]) | |
31 | 31 | |
32 | 32 | if file_path |
33 | 33 | # Send file to user | ... | ... |
app/controllers/search_controller.rb
1 | 1 | class SearchController < ApplicationController |
2 | 2 | def show |
3 | - result = SearchContext.new(current_user.project_ids, params).execute | |
3 | + result = SearchContext.new(current_user.authorized_projects.map(&:id), params).execute | |
4 | 4 | |
5 | 5 | @projects = result[:projects] |
6 | 6 | @merge_requests = result[:merge_requests] | ... | ... |
app/controllers/services_controller.rb
... | ... | @@ -26,7 +26,7 @@ class ServicesController < ProjectResourceController |
26 | 26 | end |
27 | 27 | |
28 | 28 | def test |
29 | - commits = project.commits(project.default_branch, nil, 3) | |
29 | + commits = project.repository.commits(project.default_branch, nil, 3) | |
30 | 30 | data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user) |
31 | 31 | |
32 | 32 | @service = project.gitlab_ci_service | ... | ... |
app/controllers/snippets_controller.rb
app/controllers/team_members_controller.rb
... | ... | @@ -16,10 +16,9 @@ class TeamMembersController < ProjectResourceController |
16 | 16 | end |
17 | 17 | |
18 | 18 | def create |
19 | - @project.add_users_ids_to_team( | |
20 | - params[:user_ids], | |
21 | - params[:project_access] | |
22 | - ) | |
19 | + users = User.where(id: params[:user_ids]) | |
20 | + | |
21 | + @project.team << [users, params[:project_access]] | |
23 | 22 | |
24 | 23 | if params[:redirect_to] |
25 | 24 | redirect_to params[:redirect_to] |
... | ... | @@ -50,7 +49,7 @@ class TeamMembersController < ProjectResourceController |
50 | 49 | |
51 | 50 | def apply_import |
52 | 51 | giver = Project.find(params[:source_project_id]) |
53 | - status = UsersProject.import_team(giver, project) | |
52 | + status = @project.team.import(giver) | |
54 | 53 | notice = status ? "Succesfully imported" : "Import failed" |
55 | 54 | |
56 | 55 | redirect_to project_team_members_path(project), notice: notice | ... | ... |
app/controllers/tree_controller.rb
app/decorators/tree_decorator.rb
... | ... | @@ -6,16 +6,14 @@ class TreeDecorator < ApplicationDecorator |
6 | 6 | part_path = "" |
7 | 7 | parts = path.split("\/") |
8 | 8 | |
9 | - #parts = parts[0...-1] if is_blob? | |
10 | - | |
11 | - yield(h.link_to("..", "#")) if parts.count > max_links | |
9 | + yield('..', nil) if parts.count > max_links | |
12 | 10 | |
13 | 11 | parts.each do |part| |
14 | 12 | part_path = File.join(part_path, part) unless part_path.empty? |
15 | 13 | part_path = part if part_path.empty? |
16 | 14 | |
17 | 15 | next unless parts.last(2).include?(part) if parts.count > max_links |
18 | - yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path)))) | |
16 | + yield(part, h.tree_join(ref, part_path)) | |
19 | 17 | end |
20 | 18 | end |
21 | 19 | end |
... | ... | @@ -26,7 +24,7 @@ class TreeDecorator < ApplicationDecorator |
26 | 24 | |
27 | 25 | def up_dir_path |
28 | 26 | file = File.join(path, "..") |
29 | - h.project_tree_path(project, h.tree_join(ref, file)) | |
27 | + h.tree_join(ref, file) | |
30 | 28 | end |
31 | 29 | |
32 | 30 | def readme | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -53,7 +53,7 @@ module ApplicationHelper |
53 | 53 | |
54 | 54 | def last_commit(project) |
55 | 55 | if project.repo_exists? |
56 | - time_ago_in_words(project.commit.committed_date) + " ago" | |
56 | + time_ago_in_words(project.repository.commit.committed_date) + " ago" | |
57 | 57 | else |
58 | 58 | "Never" |
59 | 59 | end |
... | ... | @@ -62,9 +62,11 @@ module ApplicationHelper |
62 | 62 | end |
63 | 63 | |
64 | 64 | def grouped_options_refs(destination = :tree) |
65 | + repository = @project.repository | |
66 | + | |
65 | 67 | options = [ |
66 | - ["Branch", @project.branch_names ], | |
67 | - [ "Tag", @project.tag_names ] | |
68 | + ["Branch", repository.branch_names ], | |
69 | + [ "Tag", repository.tag_names ] | |
68 | 70 | ] |
69 | 71 | |
70 | 72 | # If reference is commit id - |
... | ... | @@ -78,7 +80,8 @@ module ApplicationHelper |
78 | 80 | end |
79 | 81 | |
80 | 82 | def search_autocomplete_source |
81 | - projects = current_user.projects.map{ |p| { label: p.name_with_namespace, url: project_path(p) } } | |
83 | + projects = current_user.authorized_projects.map { |p| { label: p.name_with_namespace, url: project_path(p) } } | |
84 | + groups = current_user.authorized_groups.map { |group| { label: "<group> #{group.name}", url: group_path(group) } } | |
82 | 85 | |
83 | 86 | default_nav = [ |
84 | 87 | { label: "My Profile", url: profile_path }, |
... | ... | @@ -99,21 +102,21 @@ module ApplicationHelper |
99 | 102 | ] |
100 | 103 | |
101 | 104 | project_nav = [] |
102 | - if @project && !@project.new_record? | |
105 | + if @project && @project.repository && @project.repository.root_ref | |
103 | 106 | project_nav = [ |
104 | 107 | { label: "#{@project.name} Issues", url: project_issues_path(@project) }, |
105 | - { label: "#{@project.name} Commits", url: project_commits_path(@project, @ref || @project.root_ref) }, | |
108 | + { label: "#{@project.name} Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) }, | |
106 | 109 | { label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) }, |
107 | 110 | { label: "#{@project.name} Milestones", url: project_milestones_path(@project) }, |
108 | 111 | { label: "#{@project.name} Snippets", url: project_snippets_path(@project) }, |
109 | 112 | { label: "#{@project.name} Team", url: project_team_index_path(@project) }, |
110 | - { label: "#{@project.name} Tree", url: project_tree_path(@project, @ref || @project.root_ref) }, | |
113 | + { label: "#{@project.name} Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) }, | |
111 | 114 | { label: "#{@project.name} Wall", url: wall_project_path(@project) }, |
112 | 115 | { label: "#{@project.name} Wiki", url: project_wikis_path(@project) }, |
113 | 116 | ] |
114 | 117 | end |
115 | 118 | |
116 | - [projects, default_nav, project_nav, help_nav].flatten.to_json | |
119 | + [groups, projects, default_nav, project_nav, help_nav].flatten.to_json | |
117 | 120 | end |
118 | 121 | |
119 | 122 | def emoji_autocomplete_source |
... | ... | @@ -139,6 +142,7 @@ module ApplicationHelper |
139 | 142 | event.last_push_to_non_root? && |
140 | 143 | !event.rm_ref? && |
141 | 144 | event.project && |
145 | + event.project.repository && | |
142 | 146 | event.project.merge_requests_enabled |
143 | 147 | end |
144 | 148 | ... | ... |
app/helpers/commits_helper.rb
app/helpers/events_helper.rb
... | ... | @@ -20,25 +20,8 @@ module EventsHelper |
20 | 20 | [event.action_name, target].join(" ") |
21 | 21 | end |
22 | 22 | |
23 | - def event_image event | |
24 | - event_image_path = if event.push? | |
25 | - "event_push.png" | |
26 | - elsif event.merged? | |
27 | - "event_mr_merged.png" | |
28 | - end | |
29 | - | |
30 | - return nil unless event_image_path | |
31 | - | |
32 | - content_tag :div, class: 'event_icon' do | |
33 | - image_tag event_image_path | |
34 | - end | |
35 | - end | |
36 | - | |
37 | 23 | def event_filter_link key, tooltip |
38 | 24 | key = key.to_s |
39 | - | |
40 | - filter = @event_filter.options key | |
41 | - | |
42 | 25 | inactive = if @event_filter.active? key |
43 | 26 | nil |
44 | 27 | else |
... | ... | @@ -46,7 +29,7 @@ module EventsHelper |
46 | 29 | end |
47 | 30 | |
48 | 31 | content_tag :div, class: "filter_icon #{inactive}" do |
49 | - link_to dashboard_path(event_filter: filter), class: 'has_tooltip', 'data-original-title' => tooltip do | |
32 | + link_to dashboard_path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do | |
50 | 33 | image_tag "event_filter_#{key}.png" |
51 | 34 | end |
52 | 35 | end | ... | ... |
... | ... | @@ -0,0 +1,17 @@ |
1 | +module GroupsHelper | |
2 | + def group_filter_path(entity, options={}) | |
3 | + exist_opts = { | |
4 | + status: params[:status], | |
5 | + project_id: params[:project_id], | |
6 | + } | |
7 | + | |
8 | + options = exist_opts.merge(options) | |
9 | + | |
10 | + case entity | |
11 | + when 'issue' then | |
12 | + issues_group_path(@group, options) | |
13 | + when 'merge_request' | |
14 | + merge_requests_group_path(@group, options) | |
15 | + end | |
16 | + end | |
17 | +end | ... | ... |
app/helpers/merge_requests_helper.rb
app/helpers/namespaces_helper.rb
app/mailers/notify.rb
1 | 1 | class Notify < ActionMailer::Base |
2 | - include Resque::Mailer | |
2 | + | |
3 | 3 | add_template_helper ApplicationHelper |
4 | 4 | add_template_helper GitlabMarkdownHelper |
5 | 5 | |
6 | 6 | default_url_options[:host] = Gitlab.config.gitlab.host |
7 | 7 | default_url_options[:protocol] = Gitlab.config.gitlab.protocol |
8 | 8 | default_url_options[:port] = Gitlab.config.gitlab.port if Gitlab.config.gitlab_on_non_standard_port? |
9 | + default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root | |
9 | 10 | |
10 | 11 | default from: Gitlab.config.gitlab.email_from |
11 | 12 | |
... | ... | @@ -87,7 +88,7 @@ class Notify < ActionMailer::Base |
87 | 88 | def note_wall_email(recipient_id, note_id) |
88 | 89 | @note = Note.find(note_id) |
89 | 90 | @project = @note.project |
90 | - mail(to: recipient(recipient_id), subject: subject) | |
91 | + mail(to: recipient(recipient_id), subject: subject("note on wall")) | |
91 | 92 | end |
92 | 93 | |
93 | 94 | |
... | ... | @@ -147,12 +148,15 @@ class Notify < ActionMailer::Base |
147 | 148 | # >> @project = Project.last |
148 | 149 | # => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> |
149 | 150 | # >> subject('Lorem ipsum') |
150 | - # => "GitLab | Lorem ipsum | Ruby on Rails" | |
151 | + # => "GitLab | Ruby on Rails | Lorem ipsum " | |
151 | 152 | # |
152 | 153 | # # Accepts multiple arguments |
153 | 154 | # >> subject('Lorem ipsum', 'Dolor sit amet') |
154 | 155 | # => "GitLab | Lorem ipsum | Dolor sit amet" |
155 | 156 | def subject(*extra) |
156 | - "GitLab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "") | |
157 | + subject = "GitLab" | |
158 | + subject << (@project ? " | #{@project.name_with_namespace}" : "") | |
159 | + subject << " | " + extra.join(' | ') if extra.present? | |
160 | + subject | |
157 | 161 | end |
158 | 162 | end | ... | ... |
app/models/ability.rb
... | ... | @@ -15,35 +15,26 @@ class Ability |
15 | 15 | def project_abilities(user, project) |
16 | 16 | rules = [] |
17 | 17 | |
18 | + team = project.team | |
19 | + | |
18 | 20 | # Rules based on role in project |
19 | - if project.master_access_for?(user) | |
21 | + if team.masters.include?(user) | |
20 | 22 | rules << project_master_rules |
21 | 23 | |
22 | - elsif project.dev_access_for?(user) | |
24 | + elsif team.developers.include?(user) | |
23 | 25 | rules << project_dev_rules |
24 | 26 | |
25 | - elsif project.report_access_for?(user) | |
27 | + elsif team.reporters.include?(user) | |
26 | 28 | rules << project_report_rules |
27 | 29 | |
28 | - elsif project.guest_access_for?(user) | |
30 | + elsif team.guests.include?(user) | |
29 | 31 | rules << project_guest_rules |
30 | 32 | end |
31 | 33 | |
32 | - if project.namespace | |
33 | - # If user own project namespace | |
34 | - # (Ex. group owner or account owner) | |
35 | - if project.namespace.owner == user | |
36 | - rules << project_admin_rules | |
37 | - end | |
38 | - else | |
39 | - # For compatibility with global projects | |
40 | - # use projects.owner_id | |
41 | - if project.owner == user | |
42 | - rules << project_admin_rules | |
43 | - end | |
34 | + if project.owner == user | |
35 | + rules << project_admin_rules | |
44 | 36 | end |
45 | 37 | |
46 | - | |
47 | 38 | rules.flatten |
48 | 39 | end |
49 | 40 | |
... | ... | @@ -107,9 +98,12 @@ class Ability |
107 | 98 | def group_abilities user, group |
108 | 99 | rules = [] |
109 | 100 | |
110 | - rules << [ | |
111 | - :manage_group | |
112 | - ] if group.owner == user | |
101 | + # Only group owner and administrators can manage group | |
102 | + if group.owner == user || user.admin? | |
103 | + rules << [ | |
104 | + :manage_group | |
105 | + ] | |
106 | + end | |
113 | 107 | |
114 | 108 | rules.flatten |
115 | 109 | end | ... | ... |
app/models/commit.rb
... | ... | @@ -11,7 +11,7 @@ class Commit |
11 | 11 | attr_accessor :commit, :head, :refs |
12 | 12 | |
13 | 13 | delegate :message, :authored_date, :committed_date, :parents, :sha, |
14 | - :date, :committer, :author, :message, :diffs, :tree, :id, | |
14 | + :date, :committer, :author, :diffs, :tree, :id, :stats, | |
15 | 15 | :to_patch, to: :commit |
16 | 16 | |
17 | 17 | class << self |
... | ... | @@ -83,8 +83,8 @@ class Commit |
83 | 83 | |
84 | 84 | return result unless from && to |
85 | 85 | |
86 | - first = project.commit(to.try(:strip)) | |
87 | - last = project.commit(from.try(:strip)) | |
86 | + first = project.repository.commit(to.try(:strip)) | |
87 | + last = project.repository.commit(from.try(:strip)) | |
88 | 88 | |
89 | 89 | if first && last |
90 | 90 | result[:same] = (first.id == last.id) |
... | ... | @@ -98,6 +98,8 @@ class Commit |
98 | 98 | end |
99 | 99 | |
100 | 100 | def initialize(raw_commit, head = nil) |
101 | + raise "Nil as raw commit passed" unless raw_commit | |
102 | + | |
101 | 103 | @commit = raw_commit |
102 | 104 | @head = head |
103 | 105 | end |
... | ... | @@ -136,17 +138,17 @@ class Commit |
136 | 138 | end |
137 | 139 | |
138 | 140 | def prev_commit |
139 | - parents.try :first | |
141 | + @prev_commit ||= if parents.present? | |
142 | + Commit.new(parents.first) | |
143 | + else | |
144 | + nil | |
145 | + end | |
140 | 146 | end |
141 | 147 | |
142 | 148 | def prev_commit_id |
143 | 149 | prev_commit.try :id |
144 | 150 | end |
145 | 151 | |
146 | - def parents_count | |
147 | - parents && parents.count || 0 | |
148 | - end | |
149 | - | |
150 | 152 | # Shows the diff between the commit's parent and the commit. |
151 | 153 | # |
152 | 154 | # Cuts out the header and stats from #to_patch and returns only the diff. | ... | ... |
... | ... | @@ -0,0 +1,106 @@ |
1 | +# == Issuable concern | |
2 | +# | |
3 | +# Contains common functionality shared between Issues and MergeRequests | |
4 | +# | |
5 | +# Used by Issue, MergeRequest | |
6 | +# | |
7 | +module Issuable | |
8 | + extend ActiveSupport::Concern | |
9 | + | |
10 | + included do | |
11 | + belongs_to :project | |
12 | + belongs_to :author, class_name: "User" | |
13 | + belongs_to :assignee, class_name: "User" | |
14 | + belongs_to :milestone | |
15 | + has_many :notes, as: :noteable, dependent: :destroy | |
16 | + | |
17 | + validates :project, presence: true | |
18 | + validates :author, presence: true | |
19 | + validates :title, presence: true, length: { within: 0..255 } | |
20 | + validates :closed, inclusion: { in: [true, false] } | |
21 | + | |
22 | + scope :opened, where(closed: false) | |
23 | + scope :closed, where(closed: true) | |
24 | + scope :of_group, ->(group) { where(project_id: group.project_ids) } | |
25 | + scope :assigned, ->(u) { where(assignee_id: u.id)} | |
26 | + scope :recent, order("created_at DESC") | |
27 | + | |
28 | + delegate :name, | |
29 | + :email, | |
30 | + to: :author, | |
31 | + prefix: true | |
32 | + | |
33 | + delegate :name, | |
34 | + :email, | |
35 | + to: :assignee, | |
36 | + allow_nil: true, | |
37 | + prefix: true | |
38 | + | |
39 | + attr_accessor :author_id_of_changes | |
40 | + end | |
41 | + | |
42 | + module ClassMethods | |
43 | + def search(query) | |
44 | + where("title like :query", query: "%#{query}%") | |
45 | + end | |
46 | + end | |
47 | + | |
48 | + def today? | |
49 | + Date.today == created_at.to_date | |
50 | + end | |
51 | + | |
52 | + def new? | |
53 | + today? && created_at == updated_at | |
54 | + end | |
55 | + | |
56 | + def is_assigned? | |
57 | + !!assignee_id | |
58 | + end | |
59 | + | |
60 | + def is_being_reassigned? | |
61 | + assignee_id_changed? | |
62 | + end | |
63 | + | |
64 | + def is_being_closed? | |
65 | + closed_changed? && closed | |
66 | + end | |
67 | + | |
68 | + def is_being_reopened? | |
69 | + closed_changed? && !closed | |
70 | + end | |
71 | + | |
72 | + # | |
73 | + # Votes | |
74 | + # | |
75 | + | |
76 | + # Return the number of -1 comments (downvotes) | |
77 | + def downvotes | |
78 | + notes.select(&:downvote?).size | |
79 | + end | |
80 | + | |
81 | + def downvotes_in_percent | |
82 | + if votes_count.zero? | |
83 | + 0 | |
84 | + else | |
85 | + 100.0 - upvotes_in_percent | |
86 | + end | |
87 | + end | |
88 | + | |
89 | + # Return the number of +1 comments (upvotes) | |
90 | + def upvotes | |
91 | + notes.select(&:upvote?).size | |
92 | + end | |
93 | + | |
94 | + def upvotes_in_percent | |
95 | + if votes_count.zero? | |
96 | + 0 | |
97 | + else | |
98 | + 100.0 / votes_count * upvotes | |
99 | + end | |
100 | + end | |
101 | + | |
102 | + # Return the total number of votes | |
103 | + def votes_count | |
104 | + upvotes + downvotes | |
105 | + end | |
106 | +end | ... | ... |
app/models/event.rb
... | ... | @@ -15,9 +15,6 @@ |
15 | 15 | # |
16 | 16 | |
17 | 17 | class Event < ActiveRecord::Base |
18 | - include NoteEvent | |
19 | - include PushEvent | |
20 | - | |
21 | 18 | attr_accessible :project, :action, :data, :author_id, :project_id, |
22 | 19 | :target_id, :target_type |
23 | 20 | |
... | ... | @@ -113,26 +110,6 @@ class Event < ActiveRecord::Base |
113 | 110 | target_type == "MergeRequest" |
114 | 111 | end |
115 | 112 | |
116 | - def new_issue? | |
117 | - target_type == "Issue" && | |
118 | - action == Created | |
119 | - end | |
120 | - | |
121 | - def new_merge_request? | |
122 | - target_type == "MergeRequest" && | |
123 | - action == Created | |
124 | - end | |
125 | - | |
126 | - def changed_merge_request? | |
127 | - target_type == "MergeRequest" && | |
128 | - [Closed, Reopened].include?(action) | |
129 | - end | |
130 | - | |
131 | - def changed_issue? | |
132 | - target_type == "Issue" && | |
133 | - [Closed, Reopened].include?(action) | |
134 | - end | |
135 | - | |
136 | 113 | def joined? |
137 | 114 | action == Joined |
138 | 115 | end |
... | ... | @@ -170,4 +147,143 @@ class Event < ActiveRecord::Base |
170 | 147 | "opened" |
171 | 148 | end |
172 | 149 | end |
150 | + | |
151 | + def valid_push? | |
152 | + data[:ref] | |
153 | + rescue => ex | |
154 | + false | |
155 | + end | |
156 | + | |
157 | + def tag? | |
158 | + data[:ref]["refs/tags"] | |
159 | + end | |
160 | + | |
161 | + def branch? | |
162 | + data[:ref]["refs/heads"] | |
163 | + end | |
164 | + | |
165 | + def new_branch? | |
166 | + commit_from =~ /^00000/ | |
167 | + end | |
168 | + | |
169 | + def new_ref? | |
170 | + commit_from =~ /^00000/ | |
171 | + end | |
172 | + | |
173 | + def rm_ref? | |
174 | + commit_to =~ /^00000/ | |
175 | + end | |
176 | + | |
177 | + def md_ref? | |
178 | + !(rm_ref? || new_ref?) | |
179 | + end | |
180 | + | |
181 | + def commit_from | |
182 | + data[:before] | |
183 | + end | |
184 | + | |
185 | + def commit_to | |
186 | + data[:after] | |
187 | + end | |
188 | + | |
189 | + def ref_name | |
190 | + if tag? | |
191 | + tag_name | |
192 | + else | |
193 | + branch_name | |
194 | + end | |
195 | + end | |
196 | + | |
197 | + def branch_name | |
198 | + @branch_name ||= data[:ref].gsub("refs/heads/", "") | |
199 | + end | |
200 | + | |
201 | + def tag_name | |
202 | + @tag_name ||= data[:ref].gsub("refs/tags/", "") | |
203 | + end | |
204 | + | |
205 | + # Max 20 commits from push DESC | |
206 | + def commits | |
207 | + @commits ||= data[:commits].map { |commit| repository.commit(commit[:id]) }.reverse | |
208 | + end | |
209 | + | |
210 | + def commits_count | |
211 | + data[:total_commits_count] || commits.count || 0 | |
212 | + end | |
213 | + | |
214 | + def ref_type | |
215 | + tag? ? "tag" : "branch" | |
216 | + end | |
217 | + | |
218 | + def push_action_name | |
219 | + if new_ref? | |
220 | + "pushed new" | |
221 | + elsif rm_ref? | |
222 | + "deleted" | |
223 | + else | |
224 | + "pushed to" | |
225 | + end | |
226 | + end | |
227 | + | |
228 | + def repository | |
229 | + project.repository | |
230 | + end | |
231 | + | |
232 | + def parent_commit | |
233 | + repository.commit(commit_from) | |
234 | + rescue => ex | |
235 | + nil | |
236 | + end | |
237 | + | |
238 | + def last_commit | |
239 | + repository.commit(commit_to) | |
240 | + rescue => ex | |
241 | + nil | |
242 | + end | |
243 | + | |
244 | + def push_with_commits? | |
245 | + md_ref? && commits.any? && parent_commit && last_commit | |
246 | + rescue Grit::NoSuchPathError | |
247 | + false | |
248 | + end | |
249 | + | |
250 | + def last_push_to_non_root? | |
251 | + branch? && project.default_branch != branch_name | |
252 | + end | |
253 | + | |
254 | + def note_commit_id | |
255 | + target.commit_id | |
256 | + end | |
257 | + | |
258 | + def note_short_commit_id | |
259 | + note_commit_id[0..8] | |
260 | + end | |
261 | + | |
262 | + def note_commit? | |
263 | + target.noteable_type == "Commit" | |
264 | + end | |
265 | + | |
266 | + def note_target | |
267 | + target.noteable | |
268 | + end | |
269 | + | |
270 | + def note_target_id | |
271 | + if note_commit? | |
272 | + target.commit_id | |
273 | + else | |
274 | + target.noteable_id.to_s | |
275 | + end | |
276 | + end | |
277 | + | |
278 | + def wall_note? | |
279 | + target.noteable_type.blank? | |
280 | + end | |
281 | + | |
282 | + def note_target_type | |
283 | + if target.noteable_type.present? | |
284 | + target.noteable_type.titleize | |
285 | + else | |
286 | + "Wall" | |
287 | + end.downcase | |
288 | + end | |
173 | 289 | end | ... | ... |
app/models/gitlab_ci_service.rb
... | ... | @@ -23,20 +23,12 @@ class GitlabCiService < Service |
23 | 23 | |
24 | 24 | after_save :compose_service_hook, if: :activated? |
25 | 25 | |
26 | - def activated? | |
27 | - active | |
28 | - end | |
29 | - | |
30 | 26 | def compose_service_hook |
31 | 27 | hook = service_hook || build_service_hook |
32 | 28 | hook.url = [project_url, "/build", "?token=#{token}"].join("") |
33 | 29 | hook.save |
34 | 30 | end |
35 | 31 | |
36 | - def commit_badge_path sha | |
37 | - project_url + "/status?sha=#{sha}" | |
38 | - end | |
39 | - | |
40 | 32 | def commit_status_path sha |
41 | 33 | project_url + "/builds/#{sha}/status.json?token=#{token}" |
42 | 34 | end | ... | ... |
app/models/group.rb
... | ... | @@ -12,6 +12,14 @@ |
12 | 12 | # |
13 | 13 | |
14 | 14 | class Group < Namespace |
15 | + def add_users_to_project_teams(user_ids, project_access) | |
16 | + UsersProject.add_users_into_projects( | |
17 | + projects.map(&:id), | |
18 | + user_ids, | |
19 | + project_access | |
20 | + ) | |
21 | + end | |
22 | + | |
15 | 23 | def users |
16 | 24 | users = User.joins(:users_projects).where(users_projects: {project_id: project_ids}) |
17 | 25 | users = users << owner |
... | ... | @@ -21,4 +29,8 @@ class Group < Namespace |
21 | 29 | def human_name |
22 | 30 | name |
23 | 31 | end |
32 | + | |
33 | + def truncate_teams | |
34 | + UsersProject.truncate_teams(project_ids) | |
35 | + end | |
24 | 36 | end | ... | ... |
app/models/issue.rb
app/models/key.rb
app/models/merge_request.rb
... | ... | @@ -20,11 +20,10 @@ |
20 | 20 | # |
21 | 21 | |
22 | 22 | require Rails.root.join("app/models/commit") |
23 | -require Rails.root.join("app/roles/static_model") | |
23 | +require Rails.root.join("lib/static_model") | |
24 | 24 | |
25 | 25 | class MergeRequest < ActiveRecord::Base |
26 | - include IssueCommonality | |
27 | - include Votes | |
26 | + include Issuable | |
28 | 27 | |
29 | 28 | attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id, |
30 | 29 | :author_id_of_changes | ... | ... |
app/models/milestone.rb
... | ... | @@ -29,7 +29,7 @@ class Milestone < ActiveRecord::Base |
29 | 29 | |
30 | 30 | def expired? |
31 | 31 | if due_date |
32 | - due_date < Date.today | |
32 | + due_date.past? | |
33 | 33 | else |
34 | 34 | false |
35 | 35 | end |
... | ... | @@ -58,7 +58,13 @@ class Milestone < ActiveRecord::Base |
58 | 58 | end |
59 | 59 | |
60 | 60 | def expires_at |
61 | - "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date | |
61 | + if due_date | |
62 | + if due_date.past? | |
63 | + "expired at #{due_date.stamp("Aug 21, 2011")}" | |
64 | + else | |
65 | + "expires at #{due_date.stamp("Aug 21, 2011")}" | |
66 | + end | |
67 | + end | |
62 | 68 | end |
63 | 69 | |
64 | 70 | def can_be_closed? | ... | ... |
app/models/namespace.rb
... | ... | @@ -27,10 +27,13 @@ class Namespace < ActiveRecord::Base |
27 | 27 | |
28 | 28 | after_create :ensure_dir_exist |
29 | 29 | after_update :move_dir |
30 | + after_commit :update_gitolite, on: :update, if: :require_update_gitolite | |
30 | 31 | after_destroy :rm_dir |
31 | 32 | |
32 | 33 | scope :root, where('type IS NULL') |
33 | 34 | |
35 | + attr_accessor :require_update_gitolite | |
36 | + | |
34 | 37 | def self.search query |
35 | 38 | where("name LIKE :query OR path LIKE :query", query: "%#{query}%") |
36 | 39 | end |
... | ... | @@ -48,8 +51,17 @@ class Namespace < ActiveRecord::Base |
48 | 51 | end |
49 | 52 | |
50 | 53 | def ensure_dir_exist |
51 | - namespace_dir_path = File.join(Gitlab.config.gitolite.repos_path, path) | |
52 | - system("mkdir -m 770 #{namespace_dir_path}") unless File.exists?(namespace_dir_path) | |
54 | + unless dir_exists? | |
55 | + FileUtils.mkdir( namespace_full_path, mode: 0770 ) | |
56 | + end | |
57 | + end | |
58 | + | |
59 | + def dir_exists? | |
60 | + File.exists?(namespace_full_path) | |
61 | + end | |
62 | + | |
63 | + def namespace_full_path | |
64 | + @namespace_full_path ||= File.join(Gitlab.config.gitolite.repos_path, path) | |
53 | 65 | end |
54 | 66 | |
55 | 67 | def move_dir |
... | ... | @@ -59,16 +71,25 @@ class Namespace < ActiveRecord::Base |
59 | 71 | if File.exists?(new_path) |
60 | 72 | raise "Already exists" |
61 | 73 | end |
62 | - | |
63 | - if system("mv #{old_path} #{new_path}") | |
74 | + | |
75 | + begin | |
76 | + FileUtils.mv( old_path, new_path ) | |
64 | 77 | send_update_instructions |
78 | + @require_update_gitolite = true | |
79 | + rescue Exception => e | |
80 | + raise "Namespace move error #{old_path} #{new_path}" | |
65 | 81 | end |
66 | 82 | end |
67 | 83 | end |
68 | 84 | |
85 | + def update_gitolite | |
86 | + @require_update_gitolite = false | |
87 | + projects.each(&:update_repository) | |
88 | + end | |
89 | + | |
69 | 90 | def rm_dir |
70 | 91 | dir_path = File.join(Gitlab.config.gitolite.repos_path, path) |
71 | - system("rm -rf #{dir_path}") | |
92 | + FileUtils.rm_r( dir_path, force: true ) | |
72 | 93 | end |
73 | 94 | |
74 | 95 | def send_update_instructions | ... | ... |
app/models/note.rb
... | ... | @@ -4,7 +4,6 @@ |
4 | 4 | # |
5 | 5 | # id :integer not null, primary key |
6 | 6 | # note :text |
7 | -# noteable_id :string(255) | |
8 | 7 | # noteable_type :string(255) |
9 | 8 | # author_id :integer |
10 | 9 | # created_at :datetime not null |
... | ... | @@ -12,6 +11,8 @@ |
12 | 11 | # project_id :integer |
13 | 12 | # attachment :string(255) |
14 | 13 | # line_code :string(255) |
14 | +# commit_id :string(255) | |
15 | +# noteable_id :integer | |
15 | 16 | # |
16 | 17 | |
17 | 18 | require 'carrierwave/orm/activerecord' |
... | ... | @@ -41,11 +42,11 @@ class Note < ActiveRecord::Base |
41 | 42 | mount_uploader :attachment, AttachmentUploader |
42 | 43 | |
43 | 44 | # Scopes |
44 | - scope :for_commits, ->{ where(noteable_type: "Commit") } | |
45 | - scope :common, ->{ where(noteable_id: nil, commit_id: nil) } | |
46 | - scope :today, ->{ where("created_at >= :date", date: Date.today) } | |
47 | - scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) } | |
48 | - scope :since, ->(day) { where("created_at >= :date", date: (day)) } | |
45 | + scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } | |
46 | + scope :inline, where("line_code IS NOT NULL") | |
47 | + scope :not_inline, where("line_code IS NULL") | |
48 | + | |
49 | + scope :common, ->{ where(noteable_type: ["", nil]) } | |
49 | 50 | scope :fresh, ->{ order("created_at ASC, id ASC") } |
50 | 51 | scope :inc_author_project, ->{ includes(:project, :author) } |
51 | 52 | scope :inc_author, ->{ includes(:author) } |
... | ... | @@ -126,7 +127,7 @@ class Note < ActiveRecord::Base |
126 | 127 | # override to return commits, which are not active record |
127 | 128 | def noteable |
128 | 129 | if for_commit? |
129 | - project.commit(commit_id) | |
130 | + project.repository.commit(commit_id) | |
130 | 131 | else |
131 | 132 | super |
132 | 133 | end | ... | ... |
app/models/project.rb
... | ... | @@ -9,7 +9,7 @@ |
9 | 9 | # created_at :datetime not null |
10 | 10 | # updated_at :datetime not null |
11 | 11 | # private_flag :boolean default(TRUE), not null |
12 | -# owner_id :integer | |
12 | +# creator_id :integer | |
13 | 13 | # default_branch :string(255) |
14 | 14 | # issues_enabled :boolean default(TRUE), not null |
15 | 15 | # wall_enabled :boolean default(TRUE), not null |
... | ... | @@ -21,18 +21,14 @@ |
21 | 21 | require "grit" |
22 | 22 | |
23 | 23 | class Project < ActiveRecord::Base |
24 | - include Repository | |
25 | - include PushObserver | |
26 | - include Authority | |
27 | - include Team | |
28 | - include NamespacedProject | |
24 | + include Gitolited | |
29 | 25 | |
30 | 26 | class TransferError < StandardError; end |
31 | 27 | |
32 | 28 | attr_accessible :name, :path, :description, :default_branch, :issues_enabled, |
33 | 29 | :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin] |
34 | 30 | |
35 | - attr_accessible :namespace_id, :owner_id, as: :admin | |
31 | + attr_accessible :namespace_id, :creator_id, as: :admin | |
36 | 32 | |
37 | 33 | attr_accessor :error_code |
38 | 34 | |
... | ... | @@ -40,10 +36,10 @@ class Project < ActiveRecord::Base |
40 | 36 | belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" |
41 | 37 | belongs_to :namespace |
42 | 38 | |
43 | - # TODO: replace owner with creator. | |
44 | - # With namespaces a project owner will be a namespace owner | |
45 | - # so this field makes sense only for global projects | |
46 | - belongs_to :owner, class_name: "User" | |
39 | + belongs_to :creator, | |
40 | + class_name: "User", | |
41 | + foreign_key: "creator_id" | |
42 | + | |
47 | 43 | has_many :users, through: :users_projects |
48 | 44 | has_many :events, dependent: :destroy |
49 | 45 | has_many :merge_requests, dependent: :destroy |
... | ... | @@ -62,9 +58,11 @@ class Project < ActiveRecord::Base |
62 | 58 | delegate :name, to: :owner, allow_nil: true, prefix: true |
63 | 59 | |
64 | 60 | # Validations |
65 | - validates :owner, presence: true | |
61 | + validates :creator, presence: true | |
66 | 62 | validates :description, length: { within: 0..2000 } |
67 | - validates :name, presence: true, length: { within: 0..255 } | |
63 | + validates :name, presence: true, length: { within: 0..255 }, | |
64 | + format: { with: Gitlab::Regex.project_name_regex, | |
65 | + message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter should be first" } | |
68 | 66 | validates :path, presence: true, length: { within: 0..255 }, |
69 | 67 | format: { with: Gitlab::Regex.path_regex, |
70 | 68 | message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } |
... | ... | @@ -77,19 +75,14 @@ class Project < ActiveRecord::Base |
77 | 75 | validate :check_limit, :repo_name |
78 | 76 | |
79 | 77 | # Scopes |
80 | - scope :public_only, where(private_flag: false) | |
81 | - scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.projects.map(&:id) ) } | |
78 | + scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) } | |
82 | 79 | scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) } |
80 | + scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) } | |
83 | 81 | scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") } |
84 | 82 | scope :personal, ->(user) { where(namespace_id: user.namespace_id) } |
85 | 83 | scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } |
86 | 84 | |
87 | 85 | class << self |
88 | - def authorized_for user | |
89 | - projects = includes(:users_projects, :namespace) | |
90 | - projects = projects.where("users_projects.user_id = :user_id or projects.owner_id = :user_id or namespaces.owner_id = :user_id", user_id: user.id) | |
91 | - end | |
92 | - | |
93 | 86 | def active |
94 | 87 | joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC") |
95 | 88 | end |
... | ... | @@ -101,8 +94,10 @@ class Project < ActiveRecord::Base |
101 | 94 | def find_with_namespace(id) |
102 | 95 | if id.include?("/") |
103 | 96 | id = id.split("/") |
104 | - namespace_id = Namespace.find_by_path(id.first).id | |
105 | - where(namespace_id: namespace_id).find_by_path(id.last) | |
97 | + namespace = Namespace.find_by_path(id.first) | |
98 | + return nil unless namespace | |
99 | + | |
100 | + where(namespace_id: namespace.id).find_by_path(id.second) | |
106 | 101 | else |
107 | 102 | where(path: id, namespace_id: nil).last |
108 | 103 | end |
... | ... | @@ -122,7 +117,7 @@ class Project < ActiveRecord::Base |
122 | 117 | # |
123 | 118 | project.path = project.name.dup.parameterize |
124 | 119 | |
125 | - project.owner = user | |
120 | + project.creator = user | |
126 | 121 | |
127 | 122 | # Apply namespace if user has access to it |
128 | 123 | # else fallback to user namespace |
... | ... | @@ -162,6 +157,20 @@ class Project < ActiveRecord::Base |
162 | 157 | end |
163 | 158 | end |
164 | 159 | |
160 | + def team | |
161 | + @team ||= Team.new(self) | |
162 | + end | |
163 | + | |
164 | + def repository | |
165 | + if path | |
166 | + @repository ||= Repository.new(path_with_namespace, default_branch) | |
167 | + else | |
168 | + nil | |
169 | + end | |
170 | + rescue Grit::NoSuchPathError | |
171 | + nil | |
172 | + end | |
173 | + | |
165 | 174 | def git_error? |
166 | 175 | error_code == :gitolite |
167 | 176 | end |
... | ... | @@ -171,8 +180,8 @@ class Project < ActiveRecord::Base |
171 | 180 | end |
172 | 181 | |
173 | 182 | def check_limit |
174 | - unless owner.can_create_project? | |
175 | - errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") | |
183 | + unless creator.can_create_project? | |
184 | + errors[:base] << ("Your own projects limit is #{creator.projects_limit}! Please contact administrator to increase it") | |
176 | 185 | end |
177 | 186 | rescue |
178 | 187 | errors[:base] << ("Can't check your ability to create project") |
... | ... | @@ -198,30 +207,10 @@ class Project < ActiveRecord::Base |
198 | 207 | [Gitlab.config.gitlab.url, path_with_namespace].join("/") |
199 | 208 | end |
200 | 209 | |
201 | - def common_notes | |
202 | - notes.where(noteable_type: ["", nil]).inc_author_project | |
203 | - end | |
204 | - | |
205 | 210 | def build_commit_note(commit) |
206 | 211 | notes.new(commit_id: commit.id, noteable_type: "Commit") |
207 | 212 | end |
208 | 213 | |
209 | - def commit_notes(commit) | |
210 | - notes.where(commit_id: commit.id, noteable_type: "Commit").where('line_code IS NULL OR line_code = ""') | |
211 | - end | |
212 | - | |
213 | - def commit_line_notes(commit) | |
214 | - notes.where(commit_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL") | |
215 | - end | |
216 | - | |
217 | - def public? | |
218 | - !private_flag | |
219 | - end | |
220 | - | |
221 | - def private? | |
222 | - private_flag | |
223 | - end | |
224 | - | |
225 | 214 | def last_activity |
226 | 215 | last_event |
227 | 216 | end |
... | ... | @@ -262,7 +251,282 @@ class Project < ActiveRecord::Base |
262 | 251 | |
263 | 252 | def send_move_instructions |
264 | 253 | self.users_projects.each do |member| |
265 | - Notify.project_was_moved_email(member.id).deliver | |
254 | + Notify.delay.project_was_moved_email(member.id) | |
255 | + end | |
256 | + end | |
257 | + | |
258 | + def owner | |
259 | + if namespace | |
260 | + namespace_owner | |
261 | + else | |
262 | + creator | |
263 | + end | |
264 | + end | |
265 | + | |
266 | + def team_member_by_name_or_email(name = nil, email = nil) | |
267 | + user = users.where("name like ? or email like ?", name, email).first | |
268 | + users_projects.where(user: user) if user | |
269 | + end | |
270 | + | |
271 | + # Get Team Member record by user id | |
272 | + def team_member_by_id(user_id) | |
273 | + users_projects.find_by_user_id(user_id) | |
274 | + end | |
275 | + | |
276 | + def transfer(new_namespace) | |
277 | + Project.transaction do | |
278 | + old_namespace = namespace | |
279 | + self.namespace = new_namespace | |
280 | + | |
281 | + old_dir = old_namespace.try(:path) || '' | |
282 | + new_dir = new_namespace.try(:path) || '' | |
283 | + | |
284 | + old_repo = if old_dir.present? | |
285 | + File.join(old_dir, self.path) | |
286 | + else | |
287 | + self.path | |
288 | + end | |
289 | + | |
290 | + if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present? | |
291 | + raise TransferError.new("Project with same path in target namespace already exists") | |
292 | + end | |
293 | + | |
294 | + Gitlab::ProjectMover.new(self, old_dir, new_dir).execute | |
295 | + | |
296 | + gitolite.move_repository(old_repo, self) | |
297 | + | |
298 | + save! | |
299 | + end | |
300 | + rescue Gitlab::ProjectMover::ProjectMoveError => ex | |
301 | + raise Project::TransferError.new(ex.message) | |
302 | + end | |
303 | + | |
304 | + def name_with_namespace | |
305 | + @name_with_namespace ||= begin | |
306 | + if namespace | |
307 | + namespace.human_name + " / " + name | |
308 | + else | |
309 | + name | |
310 | + end | |
311 | + end | |
312 | + end | |
313 | + | |
314 | + def namespace_owner | |
315 | + namespace.try(:owner) | |
316 | + end | |
317 | + | |
318 | + def path_with_namespace | |
319 | + if namespace | |
320 | + namespace.path + '/' + path | |
321 | + else | |
322 | + path | |
323 | + end | |
324 | + end | |
325 | + | |
326 | + # This method will be called after each post receive and only if the provided | |
327 | + # user is present in GitLab. | |
328 | + # | |
329 | + # All callbacks for post receive should be placed here. | |
330 | + def trigger_post_receive(oldrev, newrev, ref, user) | |
331 | + data = post_receive_data(oldrev, newrev, ref, user) | |
332 | + | |
333 | + # Create push event | |
334 | + self.observe_push(data) | |
335 | + | |
336 | + if push_to_branch? ref, oldrev | |
337 | + # Close merged MR | |
338 | + self.update_merge_requests(oldrev, newrev, ref, user) | |
339 | + | |
340 | + # Execute web hooks | |
341 | + self.execute_hooks(data.dup) | |
342 | + | |
343 | + # Execute project services | |
344 | + self.execute_services(data.dup) | |
345 | + end | |
346 | + | |
347 | + # Create satellite | |
348 | + self.satellite.create unless self.satellite.exists? | |
349 | + | |
350 | + # Discover the default branch, but only if it hasn't already been set to | |
351 | + # something else | |
352 | + if repository && default_branch.nil? | |
353 | + update_attributes(default_branch: self.repository.discover_default_branch) | |
354 | + end | |
355 | + end | |
356 | + | |
357 | + def push_to_branch? ref, oldrev | |
358 | + ref_parts = ref.split('/') | |
359 | + | |
360 | + # Return if this is not a push to a branch (e.g. new commits) | |
361 | + !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000") | |
362 | + end | |
363 | + | |
364 | + def observe_push(data) | |
365 | + Event.create( | |
366 | + project: self, | |
367 | + action: Event::Pushed, | |
368 | + data: data, | |
369 | + author_id: data[:user_id] | |
370 | + ) | |
371 | + end | |
372 | + | |
373 | + def execute_hooks(data) | |
374 | + hooks.each { |hook| hook.execute(data) } | |
375 | + end | |
376 | + | |
377 | + def execute_services(data) | |
378 | + services.each do |service| | |
379 | + | |
380 | + # Call service hook only if it is active | |
381 | + service.execute(data) if service.active | |
382 | + end | |
383 | + end | |
384 | + | |
385 | + # Produce a hash of post-receive data | |
386 | + # | |
387 | + # data = { | |
388 | + # before: String, | |
389 | + # after: String, | |
390 | + # ref: String, | |
391 | + # user_id: String, | |
392 | + # user_name: String, | |
393 | + # repository: { | |
394 | + # name: String, | |
395 | + # url: String, | |
396 | + # description: String, | |
397 | + # homepage: String, | |
398 | + # }, | |
399 | + # commits: Array, | |
400 | + # total_commits_count: Fixnum | |
401 | + # } | |
402 | + # | |
403 | + def post_receive_data(oldrev, newrev, ref, user) | |
404 | + | |
405 | + push_commits = repository.commits_between(oldrev, newrev) | |
406 | + | |
407 | + # Total commits count | |
408 | + push_commits_count = push_commits.size | |
409 | + | |
410 | + # Get latest 20 commits ASC | |
411 | + push_commits_limited = push_commits.last(20) | |
412 | + | |
413 | + # Hash to be passed as post_receive_data | |
414 | + data = { | |
415 | + before: oldrev, | |
416 | + after: newrev, | |
417 | + ref: ref, | |
418 | + user_id: user.id, | |
419 | + user_name: user.name, | |
420 | + repository: { | |
421 | + name: name, | |
422 | + url: url_to_repo, | |
423 | + description: description, | |
424 | + homepage: web_url, | |
425 | + }, | |
426 | + commits: [], | |
427 | + total_commits_count: push_commits_count | |
428 | + } | |
429 | + | |
430 | + # For perfomance purposes maximum 20 latest commits | |
431 | + # will be passed as post receive hook data. | |
432 | + # | |
433 | + push_commits_limited.each do |commit| | |
434 | + data[:commits] << { | |
435 | + id: commit.id, | |
436 | + message: commit.safe_message, | |
437 | + timestamp: commit.date.xmlschema, | |
438 | + url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}", | |
439 | + author: { | |
440 | + name: commit.author_name, | |
441 | + email: commit.author_email | |
442 | + } | |
443 | + } | |
266 | 444 | end |
445 | + | |
446 | + data | |
447 | + end | |
448 | + | |
449 | + def update_merge_requests(oldrev, newrev, ref, user) | |
450 | + return true unless ref =~ /heads/ | |
451 | + branch_name = ref.gsub("refs/heads/", "") | |
452 | + c_ids = self.repository.commits_between(oldrev, newrev).map(&:id) | |
453 | + | |
454 | + # Update code for merge requests | |
455 | + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all | |
456 | + mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } | |
457 | + | |
458 | + # Close merge requests | |
459 | + mrs = self.merge_requests.opened.where(target_branch: branch_name).all | |
460 | + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } | |
461 | + mrs.each { |merge_request| merge_request.merge!(user.id) } | |
462 | + | |
463 | + true | |
464 | + end | |
465 | + | |
466 | + def valid_repo? | |
467 | + repo | |
468 | + rescue | |
469 | + errors.add(:path, "Invalid repository path") | |
470 | + false | |
471 | + end | |
472 | + | |
473 | + def empty_repo? | |
474 | + !repository || repository.empty? | |
475 | + end | |
476 | + | |
477 | + def satellite | |
478 | + @satellite ||= Gitlab::Satellite::Satellite.new(self) | |
479 | + end | |
480 | + | |
481 | + def repo | |
482 | + repository.raw | |
483 | + end | |
484 | + | |
485 | + def url_to_repo | |
486 | + gitolite.url_to_repo(path_with_namespace) | |
487 | + end | |
488 | + | |
489 | + def namespace_dir | |
490 | + namespace.try(:path) || '' | |
491 | + end | |
492 | + | |
493 | + def update_repository | |
494 | + gitolite.update_repository(self) | |
495 | + end | |
496 | + | |
497 | + def destroy_repository | |
498 | + gitolite.remove_repository(self) | |
499 | + end | |
500 | + | |
501 | + def repo_exists? | |
502 | + @repo_exists ||= (repository && repository.branches.present?) | |
503 | + rescue | |
504 | + @repo_exists = false | |
505 | + end | |
506 | + | |
507 | + def open_branches | |
508 | + if protected_branches.empty? | |
509 | + self.repo.heads | |
510 | + else | |
511 | + pnames = protected_branches.map(&:name) | |
512 | + self.repo.heads.reject { |h| pnames.include?(h.name) } | |
513 | + end.sort_by(&:name) | |
514 | + end | |
515 | + | |
516 | + def root_ref?(branch) | |
517 | + repository.root_ref == branch | |
518 | + end | |
519 | + | |
520 | + def ssh_url_to_repo | |
521 | + url_to_repo | |
522 | + end | |
523 | + | |
524 | + def http_url_to_repo | |
525 | + http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') | |
526 | + end | |
527 | + | |
528 | + # Check if current branch name is marked as protected in the system | |
529 | + def protected_branch? branch_name | |
530 | + protected_branches.map(&:name).include?(branch_name) | |
267 | 531 | end |
268 | 532 | end | ... | ... |
app/models/protected_branch.rb
... | ... | @@ -10,7 +10,7 @@ |
10 | 10 | # |
11 | 11 | |
12 | 12 | class ProtectedBranch < ActiveRecord::Base |
13 | - include GitHost | |
13 | + include Gitolited | |
14 | 14 | |
15 | 15 | attr_accessible :name |
16 | 16 | |
... | ... | @@ -22,10 +22,10 @@ class ProtectedBranch < ActiveRecord::Base |
22 | 22 | after_destroy :update_repository |
23 | 23 | |
24 | 24 | def update_repository |
25 | - git_host.update_repository(project) | |
25 | + gitolite.update_repository(project) | |
26 | 26 | end |
27 | 27 | |
28 | 28 | def commit |
29 | - project.commit(self.name) | |
29 | + project.repository.commit(self.name) | |
30 | 30 | end |
31 | 31 | end | ... | ... |
... | ... | @@ -0,0 +1,169 @@ |
1 | +class Repository | |
2 | + # Repository directory name with namespace direcotry | |
3 | + # Examples: | |
4 | + # gitlab/gitolite | |
5 | + # diaspora | |
6 | + # | |
7 | + attr_accessor :path_with_namespace | |
8 | + | |
9 | + # Grit repo object | |
10 | + attr_accessor :repo | |
11 | + | |
12 | + # Default branch in the repository | |
13 | + attr_accessor :root_ref | |
14 | + | |
15 | + def initialize(path_with_namespace, root_ref = 'master') | |
16 | + @root_ref = root_ref || "master" | |
17 | + @path_with_namespace = path_with_namespace | |
18 | + | |
19 | + # Init grit repo object | |
20 | + repo | |
21 | + end | |
22 | + | |
23 | + def raw | |
24 | + repo | |
25 | + end | |
26 | + | |
27 | + def path_to_repo | |
28 | + @path_to_repo ||= File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git") | |
29 | + end | |
30 | + | |
31 | + def repo | |
32 | + @repo ||= Grit::Repo.new(path_to_repo) | |
33 | + end | |
34 | + | |
35 | + def commit(commit_id = nil) | |
36 | + Commit.find_or_first(repo, commit_id, root_ref) | |
37 | + end | |
38 | + | |
39 | + def fresh_commits(n = 10) | |
40 | + Commit.fresh_commits(repo, n) | |
41 | + end | |
42 | + | |
43 | + def commits_with_refs(n = 20) | |
44 | + Commit.commits_with_refs(repo, n) | |
45 | + end | |
46 | + | |
47 | + def commits_since(date) | |
48 | + Commit.commits_since(repo, date) | |
49 | + end | |
50 | + | |
51 | + def commits(ref, path = nil, limit = nil, offset = nil) | |
52 | + Commit.commits(repo, ref, path, limit, offset) | |
53 | + end | |
54 | + | |
55 | + def last_commit_for(ref, path = nil) | |
56 | + commits(ref, path, 1).first | |
57 | + end | |
58 | + | |
59 | + def commits_between(from, to) | |
60 | + Commit.commits_between(repo, from, to) | |
61 | + end | |
62 | + | |
63 | + def has_post_receive_file? | |
64 | + !!hook_file | |
65 | + end | |
66 | + | |
67 | + def valid_post_receive_file? | |
68 | + valid_hook_file == hook_file | |
69 | + end | |
70 | + | |
71 | + def valid_hook_file | |
72 | + @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive')) | |
73 | + end | |
74 | + | |
75 | + def hook_file | |
76 | + @hook_file ||= begin | |
77 | + hook_path = File.join(path_to_repo, 'hooks', 'post-receive') | |
78 | + File.read(hook_path) if File.exists?(hook_path) | |
79 | + end | |
80 | + end | |
81 | + | |
82 | + # Returns an Array of branch names | |
83 | + def branch_names | |
84 | + repo.branches.collect(&:name).sort | |
85 | + end | |
86 | + | |
87 | + # Returns an Array of Branches | |
88 | + def branches | |
89 | + repo.branches.sort_by(&:name) | |
90 | + end | |
91 | + | |
92 | + # Returns an Array of tag names | |
93 | + def tag_names | |
94 | + repo.tags.collect(&:name).sort.reverse | |
95 | + end | |
96 | + | |
97 | + # Returns an Array of Tags | |
98 | + def tags | |
99 | + repo.tags.sort_by(&:name).reverse | |
100 | + end | |
101 | + | |
102 | + # Returns an Array of branch and tag names | |
103 | + def ref_names | |
104 | + [branch_names + tag_names].flatten | |
105 | + end | |
106 | + | |
107 | + def heads | |
108 | + @heads ||= repo.heads | |
109 | + end | |
110 | + | |
111 | + def tree(fcommit, path = nil) | |
112 | + fcommit = commit if fcommit == :head | |
113 | + tree = fcommit.tree | |
114 | + path ? (tree / path) : tree | |
115 | + end | |
116 | + | |
117 | + def has_commits? | |
118 | + !!commit | |
119 | + rescue Grit::NoSuchPathError | |
120 | + false | |
121 | + end | |
122 | + | |
123 | + def empty? | |
124 | + !has_commits? | |
125 | + end | |
126 | + | |
127 | + # Discovers the default branch based on the repository's available branches | |
128 | + # | |
129 | + # - If no branches are present, returns nil | |
130 | + # - If one branch is present, returns its name | |
131 | + # - If two or more branches are present, returns the one that has a name | |
132 | + # matching root_ref (default_branch or 'master' if default_branch is nil) | |
133 | + def discover_default_branch | |
134 | + if branch_names.length == 0 | |
135 | + nil | |
136 | + elsif branch_names.length == 1 | |
137 | + branch_names.first | |
138 | + else | |
139 | + branch_names.select { |v| v == root_ref }.first | |
140 | + end | |
141 | + end | |
142 | + | |
143 | + # Archive Project to .tar.gz | |
144 | + # | |
145 | + # Already packed repo archives stored at | |
146 | + # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz | |
147 | + # | |
148 | + def archive_repo(ref) | |
149 | + ref = ref || self.root_ref | |
150 | + commit = self.commit(ref) | |
151 | + return nil unless commit | |
152 | + | |
153 | + # Build file path | |
154 | + file_name = self.path + "-" + commit.id.to_s + ".tar.gz" | |
155 | + storage_path = Rails.root.join("tmp", "repositories", self.path_with_namespace) | |
156 | + file_path = File.join(storage_path, file_name) | |
157 | + | |
158 | + # Put files into a directory before archiving | |
159 | + prefix = self.path + "/" | |
160 | + | |
161 | + # Create file if not exists | |
162 | + unless File.exists?(file_path) | |
163 | + FileUtils.mkdir_p storage_path | |
164 | + file = self.repo.archive_to_file(ref, prefix, file_path) | |
165 | + end | |
166 | + | |
167 | + file_path | |
168 | + end | |
169 | +end | ... | ... |
app/models/service.rb
app/models/system_hook.rb
... | ... | @@ -0,0 +1,118 @@ |
1 | +class Team | |
2 | + attr_accessor :project | |
3 | + | |
4 | + def initialize(project) | |
5 | + @project = project | |
6 | + end | |
7 | + | |
8 | + # Shortcut to add users | |
9 | + # | |
10 | + # Use: | |
11 | + # @team << [@user, :master] | |
12 | + # @team << [@users, :master] | |
13 | + # | |
14 | + def << args | |
15 | + users = args.first | |
16 | + | |
17 | + if users.respond_to?(:each) | |
18 | + add_users(users, args.second) | |
19 | + else | |
20 | + add_user(users, args.second) | |
21 | + end | |
22 | + end | |
23 | + | |
24 | + def add_user(user, access) | |
25 | + add_users_ids([user.id], access) | |
26 | + end | |
27 | + | |
28 | + def add_users(users, access) | |
29 | + add_users_ids(users.map(&:id), access) | |
30 | + end | |
31 | + | |
32 | + def add_users_ids(user_ids, access) | |
33 | + UsersProject.add_users_into_projects( | |
34 | + [project.id], | |
35 | + user_ids, | |
36 | + access | |
37 | + ) | |
38 | + end | |
39 | + | |
40 | + # Remove all users from project team | |
41 | + def truncate | |
42 | + UsersProject.truncate_team(project) | |
43 | + end | |
44 | + | |
45 | + def members | |
46 | + project.users_projects | |
47 | + end | |
48 | + | |
49 | + def guests | |
50 | + members.guests.map(&:user) | |
51 | + end | |
52 | + | |
53 | + def reporters | |
54 | + members.reporters.map(&:user) | |
55 | + end | |
56 | + | |
57 | + def developers | |
58 | + members.developers.map(&:user) | |
59 | + end | |
60 | + | |
61 | + def masters | |
62 | + members.masters.map(&:user) | |
63 | + end | |
64 | + | |
65 | + def repository_readers | |
66 | + repository_members[UsersProject::REPORTER] | |
67 | + end | |
68 | + | |
69 | + def repository_writers | |
70 | + repository_members[UsersProject::DEVELOPER] | |
71 | + end | |
72 | + | |
73 | + def repository_masters | |
74 | + repository_members[UsersProject::MASTER] | |
75 | + end | |
76 | + | |
77 | + def repository_members | |
78 | + keys = Hash.new {|h,k| h[k] = [] } | |
79 | + UsersProject.select("keys.identifier, project_access"). | |
80 | + joins(user: :keys).where(project_id: project.id). | |
81 | + each {|row| keys[row.project_access] << [row.identifier] } | |
82 | + | |
83 | + keys[UsersProject::REPORTER] += project.deploy_keys.pluck(:identifier) | |
84 | + keys | |
85 | + end | |
86 | + | |
87 | + def import(source_project) | |
88 | + target_project = project | |
89 | + | |
90 | + source_team = source_project.users_projects.all | |
91 | + target_team = target_project.users_projects.all | |
92 | + target_user_ids = target_team.map(&:user_id) | |
93 | + | |
94 | + source_team.reject! do |tm| | |
95 | + # Skip if user already present in team | |
96 | + target_user_ids.include?(tm.user_id) | |
97 | + end | |
98 | + | |
99 | + source_team.map! do |tm| | |
100 | + new_tm = tm.dup | |
101 | + new_tm.id = nil | |
102 | + new_tm.project_id = target_project.id | |
103 | + new_tm.skip_git = true | |
104 | + new_tm | |
105 | + end | |
106 | + | |
107 | + UsersProject.transaction do | |
108 | + source_team.each do |tm| | |
109 | + tm.save | |
110 | + end | |
111 | + target_project.update_repository | |
112 | + end | |
113 | + | |
114 | + true | |
115 | + rescue | |
116 | + false | |
117 | + end | |
118 | +end | ... | ... |
app/models/tree.rb
1 | 1 | class Tree |
2 | 2 | include Linguist::BlobHelper |
3 | - attr_accessor :path, :tree, :project, :ref | |
3 | + | |
4 | + attr_accessor :path, :tree, :ref | |
4 | 5 | |
5 | 6 | delegate :contents, :basename, :name, :data, :mime_type, |
6 | 7 | :mode, :size, :text?, :colorize, to: :tree |
7 | 8 | |
8 | - def initialize(raw_tree, project, ref = nil, path = nil) | |
9 | - @project, @ref, @path = project, ref, path | |
9 | + def initialize(raw_tree, ref = nil, path = nil) | |
10 | + @ref, @path = ref, path | |
10 | 11 | @tree = if path.present? |
11 | 12 | raw_tree / path |
12 | 13 | else | ... | ... |
app/models/user.rb
... | ... | @@ -34,8 +34,6 @@ |
34 | 34 | # |
35 | 35 | |
36 | 36 | class User < ActiveRecord::Base |
37 | - include Account | |
38 | - | |
39 | 37 | devise :database_authenticatable, :token_authenticatable, :lockable, |
40 | 38 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable |
41 | 39 | |
... | ... | @@ -51,7 +49,6 @@ class User < ActiveRecord::Base |
51 | 49 | has_many :groups, class_name: "Group", foreign_key: :owner_id |
52 | 50 | |
53 | 51 | has_many :keys, dependent: :destroy |
54 | - has_many :projects, through: :users_projects | |
55 | 52 | has_many :users_projects, dependent: :destroy |
56 | 53 | has_many :issues, foreign_key: :author_id, dependent: :destroy |
57 | 54 | has_many :notes, foreign_key: :author_id, dependent: :destroy |
... | ... | @@ -70,6 +67,8 @@ class User < ActiveRecord::Base |
70 | 67 | message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } |
71 | 68 | |
72 | 69 | |
70 | + validate :namespace_uniq, if: ->(user) { user.username_changed? } | |
71 | + | |
73 | 72 | before_validation :generate_password, on: :create |
74 | 73 | before_save :ensure_authentication_token |
75 | 74 | alias_attribute :private_token, :authentication_token |
... | ... | @@ -77,11 +76,14 @@ class User < ActiveRecord::Base |
77 | 76 | delegate :path, to: :namespace, allow_nil: true, prefix: true |
78 | 77 | |
79 | 78 | # Scopes |
80 | - scope :not_in_project, ->(project) { where("id not in (:ids)", ids: project.users.map(&:id) ) } | |
81 | 79 | scope :admins, where(admin: true) |
82 | 80 | scope :blocked, where(blocked: true) |
83 | 81 | scope :active, where(blocked: false) |
82 | + scope :alphabetically, order('name ASC') | |
84 | 83 | |
84 | + # | |
85 | + # Class methods | |
86 | + # | |
85 | 87 | class << self |
86 | 88 | def filter filter_name |
87 | 89 | case filter_name |
... | ... | @@ -93,6 +95,14 @@ class User < ActiveRecord::Base |
93 | 95 | end |
94 | 96 | end |
95 | 97 | |
98 | + def not_in_project(project) | |
99 | + if project.users.present? | |
100 | + where("id not in (:ids)", ids: project.users.map(&:id) ) | |
101 | + else | |
102 | + scoped | |
103 | + end | |
104 | + end | |
105 | + | |
96 | 106 | def without_projects |
97 | 107 | where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') |
98 | 108 | end |
... | ... | @@ -118,9 +128,158 @@ class User < ActiveRecord::Base |
118 | 128 | end |
119 | 129 | end |
120 | 130 | |
131 | + # | |
132 | + # Instance methods | |
133 | + # | |
121 | 134 | def generate_password |
122 | 135 | if self.force_random_password |
123 | 136 | self.password = self.password_confirmation = Devise.friendly_token.first(8) |
124 | 137 | end |
125 | 138 | end |
139 | + | |
140 | + def namespace_uniq | |
141 | + namespace_name = self.username | |
142 | + if Namespace.find_by_path(namespace_name) | |
143 | + self.errors.add :username, "already exist" | |
144 | + end | |
145 | + end | |
146 | + | |
147 | + # Namespaces user has access to | |
148 | + def namespaces | |
149 | + namespaces = [] | |
150 | + | |
151 | + # Add user account namespace | |
152 | + namespaces << self.namespace if self.namespace | |
153 | + | |
154 | + # Add groups you can manage | |
155 | + namespaces += if admin | |
156 | + Group.all | |
157 | + else | |
158 | + groups.all | |
159 | + end | |
160 | + namespaces | |
161 | + end | |
162 | + | |
163 | + # Groups where user is an owner | |
164 | + def owned_groups | |
165 | + groups | |
166 | + end | |
167 | + | |
168 | + # Groups user has access to | |
169 | + def authorized_groups | |
170 | + @authorized_groups ||= begin | |
171 | + groups = Group.where(id: self.authorized_projects.pluck(:namespace_id)).all | |
172 | + groups = groups + self.groups | |
173 | + groups.uniq | |
174 | + end | |
175 | + end | |
176 | + | |
177 | + | |
178 | + # Projects user has access to | |
179 | + def authorized_projects | |
180 | + project_ids = users_projects.pluck(:project_id) | |
181 | + project_ids = project_ids | owned_projects.pluck(:id) | |
182 | + Project.where(id: project_ids) | |
183 | + end | |
184 | + | |
185 | + # Projects in user namespace | |
186 | + def personal_projects | |
187 | + Project.personal(self) | |
188 | + end | |
189 | + | |
190 | + # Projects where user is an owner | |
191 | + def owned_projects | |
192 | + Project.where("(projects.namespace_id IN (:namespaces)) OR | |
193 | + (projects.namespace_id IS NULL AND projects.creator_id = :user_id)", | |
194 | + namespaces: namespaces.map(&:id), user_id: self.id) | |
195 | + end | |
196 | + | |
197 | + # Team membership in personal projects | |
198 | + def tm_in_personal_projects | |
199 | + UsersProject.where(project_id: personal_projects.map(&:id), user_id: self.id) | |
200 | + end | |
201 | + | |
202 | + # Returns a string for use as a Gitolite user identifier | |
203 | + # | |
204 | + # Note that Gitolite 2.x requires the following pattern for users: | |
205 | + # | |
206 | + # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$ | |
207 | + def identifier | |
208 | + # Replace non-word chars with underscores, then make sure it starts with | |
209 | + # valid chars | |
210 | + email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '') | |
211 | + end | |
212 | + | |
213 | + def is_admin? | |
214 | + admin | |
215 | + end | |
216 | + | |
217 | + def require_ssh_key? | |
218 | + keys.count == 0 | |
219 | + end | |
220 | + | |
221 | + def can_create_project? | |
222 | + projects_limit > personal_projects.count | |
223 | + end | |
224 | + | |
225 | + def can_create_group? | |
226 | + is_admin? | |
227 | + end | |
228 | + | |
229 | + def abilities | |
230 | + @abilities ||= begin | |
231 | + abilities = Six.new | |
232 | + abilities << Ability | |
233 | + abilities | |
234 | + end | |
235 | + end | |
236 | + | |
237 | + def can? action, subject | |
238 | + abilities.allowed?(self, action, subject) | |
239 | + end | |
240 | + | |
241 | + def first_name | |
242 | + name.split.first unless name.blank? | |
243 | + end | |
244 | + | |
245 | + def cared_merge_requests | |
246 | + MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id) | |
247 | + end | |
248 | + | |
249 | + # Remove user from all projects and | |
250 | + # set blocked attribute to true | |
251 | + def block | |
252 | + users_projects.find_each do |membership| | |
253 | + return false unless membership.destroy | |
254 | + end | |
255 | + | |
256 | + self.blocked = true | |
257 | + save | |
258 | + end | |
259 | + | |
260 | + def projects_limit_percent | |
261 | + return 100 if projects_limit.zero? | |
262 | + (personal_projects.count.to_f / projects_limit) * 100 | |
263 | + end | |
264 | + | |
265 | + def recent_push project_id = nil | |
266 | + # Get push events not earlier than 2 hours ago | |
267 | + events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours) | |
268 | + events = events.where(project_id: project_id) if project_id | |
269 | + | |
270 | + # Take only latest one | |
271 | + events = events.recent.limit(1).first | |
272 | + end | |
273 | + | |
274 | + def projects_sorted_by_activity | |
275 | + authorized_projects.sorted_by_activity | |
276 | + end | |
277 | + | |
278 | + def several_namespaces? | |
279 | + namespaces.size > 1 | |
280 | + end | |
281 | + | |
282 | + def namespace_id | |
283 | + namespace.try :id | |
284 | + end | |
126 | 285 | end | ... | ... |
app/models/users_project.rb
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | # |
12 | 12 | |
13 | 13 | class UsersProject < ActiveRecord::Base |
14 | - include GitHost | |
14 | + include Gitolited | |
15 | 15 | |
16 | 16 | GUEST = 10 |
17 | 17 | REPORTER = 20 |
... | ... | @@ -23,87 +23,96 @@ class UsersProject < ActiveRecord::Base |
23 | 23 | belongs_to :user |
24 | 24 | belongs_to :project |
25 | 25 | |
26 | - after_save :update_repository | |
27 | - after_destroy :update_repository | |
26 | + attr_accessor :skip_git | |
27 | + | |
28 | + after_save :update_repository, unless: :skip_git? | |
29 | + after_destroy :update_repository, unless: :skip_git? | |
28 | 30 | |
29 | 31 | validates :user, presence: true |
30 | - validates :user_id, uniqueness: { :scope => [:project_id], message: "already exists in project" } | |
32 | + validates :user_id, uniqueness: { scope: [:project_id], message: "already exists in project" } | |
31 | 33 | validates :project_access, inclusion: { in: [GUEST, REPORTER, DEVELOPER, MASTER] }, presence: true |
32 | 34 | validates :project, presence: true |
33 | 35 | |
34 | 36 | delegate :name, :email, to: :user, prefix: true |
35 | 37 | |
38 | + scope :guests, where(project_access: GUEST) | |
39 | + scope :reporters, where(project_access: REPORTER) | |
40 | + scope :developers, where(project_access: DEVELOPER) | |
41 | + scope :masters, where(project_access: MASTER) | |
42 | + scope :in_project, ->(project) { where(project_id: project.id) } | |
43 | + | |
36 | 44 | class << self |
37 | - def import_team(source_project, target_project) | |
38 | - UsersProject.without_repository_callback do | |
39 | - UsersProject.transaction do | |
40 | - team = source_project.users_projects.all | |
41 | - | |
42 | - team.each do |tm| | |
43 | - # Skip if user already present in team | |
44 | - next if target_project.users.include?(tm.user) | |
45 | - | |
46 | - new_tm = tm.dup | |
47 | - new_tm.id = nil | |
48 | - new_tm.project_id = target_project.id | |
49 | - new_tm.save | |
45 | + | |
46 | + # Add users to project teams with passed access option | |
47 | + # | |
48 | + # access can be an integer representing a access code | |
49 | + # or symbol like :master representing role | |
50 | + # | |
51 | + # Ex. | |
52 | + # add_users_into_projects( | |
53 | + # project_ids, | |
54 | + # user_ids, | |
55 | + # UsersProject::MASTER | |
56 | + # ) | |
57 | + # | |
58 | + # add_users_into_projects( | |
59 | + # project_ids, | |
60 | + # user_ids, | |
61 | + # :master | |
62 | + # ) | |
63 | + # | |
64 | + def add_users_into_projects(project_ids, user_ids, access) | |
65 | + project_access = if roles_hash.has_key?(access) | |
66 | + roles_hash[access] | |
67 | + elsif roles_hash.values.include?(access.to_i) | |
68 | + access | |
69 | + else | |
70 | + raise "Non valid access" | |
71 | + end | |
72 | + | |
73 | + UsersProject.transaction do | |
74 | + project_ids.each do |project_id| | |
75 | + user_ids.each do |user_id| | |
76 | + users_project = UsersProject.new(project_access: project_access, user_id: user_id) | |
77 | + users_project.project_id = project_id | |
78 | + users_project.skip_git = true | |
79 | + users_project.save | |
50 | 80 | end |
51 | 81 | end |
82 | + Gitlab::Gitolite.new.update_repositories(Project.where(id: project_ids)) | |
52 | 83 | end |
53 | 84 | |
54 | - target_project.update_repository | |
55 | 85 | true |
56 | 86 | rescue |
57 | 87 | false |
58 | 88 | end |
59 | 89 | |
60 | - def without_repository_callback | |
61 | - UsersProject.skip_callback(:destroy, :after, :update_repository) | |
62 | - yield | |
63 | - UsersProject.set_callback(:destroy, :after, :update_repository) | |
64 | - end | |
65 | - | |
66 | - def bulk_delete(project, user_ids) | |
90 | + def truncate_teams(project_ids) | |
67 | 91 | UsersProject.transaction do |
68 | - UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| | |
92 | + users_projects = UsersProject.where(project_id: project_ids) | |
93 | + users_projects.each do |users_project| | |
94 | + users_project.skip_git = true | |
69 | 95 | users_project.destroy |
70 | 96 | end |
97 | + Gitlab::Gitolite.new.update_repositories(Project.where(id: project_ids)) | |
71 | 98 | end |
72 | - end | |
73 | 99 | |
74 | - def bulk_update(project, user_ids, project_access) | |
75 | - UsersProject.transaction do | |
76 | - UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| | |
77 | - users_project.project_access = project_access | |
78 | - users_project.save | |
79 | - end | |
80 | - end | |
100 | + true | |
101 | + rescue | |
102 | + false | |
81 | 103 | end |
82 | 104 | |
83 | - def bulk_import(project, user_ids, project_access) | |
84 | - UsersProject.transaction do | |
85 | - user_ids.each do |user_id| | |
86 | - users_project = UsersProject.new( | |
87 | - project_access: project_access, | |
88 | - user_id: user_id | |
89 | - ) | |
90 | - users_project.project = project | |
91 | - users_project.save | |
92 | - end | |
93 | - end | |
105 | + def truncate_team project | |
106 | + truncate_teams [project.id] | |
94 | 107 | end |
95 | 108 | |
96 | - def user_bulk_import(user, project_ids, project_access) | |
97 | - UsersProject.transaction do | |
98 | - project_ids.each do |project_id| | |
99 | - users_project = UsersProject.new( | |
100 | - project_access: project_access, | |
101 | - ) | |
102 | - users_project.project_id = project_id | |
103 | - users_project.user_id = user.id | |
104 | - users_project.save | |
105 | - end | |
106 | - end | |
109 | + def roles_hash | |
110 | + { | |
111 | + guest: GUEST, | |
112 | + reporter: REPORTER, | |
113 | + developer: DEVELOPER, | |
114 | + master: MASTER | |
115 | + } | |
107 | 116 | end |
108 | 117 | |
109 | 118 | def access_roles |
... | ... | @@ -116,12 +125,8 @@ class UsersProject < ActiveRecord::Base |
116 | 125 | end |
117 | 126 | end |
118 | 127 | |
119 | - def role_access | |
120 | - project_access | |
121 | - end | |
122 | - | |
123 | 128 | def update_repository |
124 | - git_host.update_repository(project) | |
129 | + gitolite.update_repository(project) | |
125 | 130 | end |
126 | 131 | |
127 | 132 | def project_access_human |
... | ... | @@ -131,4 +136,8 @@ class UsersProject < ActiveRecord::Base |
131 | 136 | def repo_access_human |
132 | 137 | self.class.access_roles.invert[self.project_access] |
133 | 138 | end |
139 | + | |
140 | + def skip_git? | |
141 | + !!@skip_git | |
142 | + end | |
134 | 143 | end | ... | ... |
app/models/wiki.rb
app/observers/issue_observer.rb
... | ... | @@ -3,7 +3,7 @@ class IssueObserver < ActiveRecord::Observer |
3 | 3 | |
4 | 4 | def after_create(issue) |
5 | 5 | if issue.assignee && issue.assignee != current_user |
6 | - Notify.new_issue_email(issue.id).deliver | |
6 | + Notify.delay.new_issue_email(issue.id) | |
7 | 7 | end |
8 | 8 | end |
9 | 9 | |
... | ... | @@ -16,7 +16,7 @@ class IssueObserver < ActiveRecord::Observer |
16 | 16 | if status |
17 | 17 | Note.create_status_change_note(issue, current_user, status) |
18 | 18 | [issue.author, issue.assignee].compact.each do |recipient| |
19 | - Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id).deliver | |
19 | + Notify.delay.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) | |
20 | 20 | end |
21 | 21 | end |
22 | 22 | end |
... | ... | @@ -27,7 +27,7 @@ class IssueObserver < ActiveRecord::Observer |
27 | 27 | recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id } |
28 | 28 | |
29 | 29 | recipient_ids.each do |recipient_id| |
30 | - Notify.reassigned_issue_email(recipient_id, issue.id, issue.assignee_id_was).deliver | |
30 | + Notify.delay.reassigned_issue_email(recipient_id, issue.id, issue.assignee_id_was) | |
31 | 31 | end |
32 | 32 | end |
33 | 33 | end | ... | ... |
app/observers/key_observer.rb
1 | 1 | class KeyObserver < ActiveRecord::Observer |
2 | - include GitHost | |
2 | + include Gitolited | |
3 | 3 | |
4 | 4 | def after_save(key) |
5 | - git_host.set_key(key.identifier, key.key, key.projects) | |
5 | + gitolite.set_key(key.identifier, key.key, key.projects) | |
6 | 6 | end |
7 | 7 | |
8 | 8 | def after_destroy(key) |
9 | 9 | return if key.is_deploy_key && !key.last_deploy? |
10 | - git_host.remove_key(key.identifier, key.projects) | |
10 | + gitolite.remove_key(key.identifier, key.projects) | |
11 | 11 | end |
12 | 12 | end | ... | ... |
app/observers/merge_request_observer.rb
... | ... | @@ -3,7 +3,7 @@ class MergeRequestObserver < ActiveRecord::Observer |
3 | 3 | |
4 | 4 | def after_create(merge_request) |
5 | 5 | if merge_request.assignee && merge_request.assignee != current_user |
6 | - Notify.new_merge_request_email(merge_request.id).deliver | |
6 | + Notify.delay.new_merge_request_email(merge_request.id) | |
7 | 7 | end |
8 | 8 | end |
9 | 9 | |
... | ... | @@ -25,7 +25,7 @@ class MergeRequestObserver < ActiveRecord::Observer |
25 | 25 | recipients_ids.delete current_user.id |
26 | 26 | |
27 | 27 | recipients_ids.each do |recipient_id| |
28 | - Notify.reassigned_merge_request_email(recipient_id, merge_request.id, merge_request.assignee_id_was).deliver | |
28 | + Notify.delay.reassigned_merge_request_email(recipient_id, merge_request.id, merge_request.assignee_id_was) | |
29 | 29 | end |
30 | 30 | end |
31 | 31 | end | ... | ... |
app/observers/note_observer.rb
... | ... | @@ -11,7 +11,7 @@ class NoteObserver < ActiveRecord::Observer |
11 | 11 | notify_team(note) |
12 | 12 | elsif note.notify_author |
13 | 13 | # Notify only author of resource |
14 | - Notify.note_commit_email(note.noteable.author_email, note.id).deliver | |
14 | + Notify.delay.note_commit_email(note.noteable.author_email, note.id) | |
15 | 15 | else |
16 | 16 | # Otherwise ignore it |
17 | 17 | nil |
... | ... | @@ -26,7 +26,7 @@ class NoteObserver < ActiveRecord::Observer |
26 | 26 | |
27 | 27 | if Notify.respond_to? notify_method |
28 | 28 | team_without_note_author(note).map do |u| |
29 | - Notify.send(notify_method, u.id, note.id).deliver | |
29 | + Notify.delay.send(notify_method, u.id, note.id) | |
30 | 30 | end |
31 | 31 | end |
32 | 32 | end | ... | ... |
app/observers/user_observer.rb
... | ... | @@ -2,7 +2,7 @@ class UserObserver < ActiveRecord::Observer |
2 | 2 | def after_create(user) |
3 | 3 | log_info("User \"#{user.name}\" (#{user.email}) was created") |
4 | 4 | |
5 | - Notify.new_user_email(user.id, user.password).deliver | |
5 | + Notify.delay.new_user_email(user.id, user.password) | |
6 | 6 | end |
7 | 7 | |
8 | 8 | def after_destroy user |
... | ... | @@ -14,7 +14,7 @@ class UserObserver < ActiveRecord::Observer |
14 | 14 | if user.namespace |
15 | 15 | user.namespace.update_attributes(path: user.username) |
16 | 16 | else |
17 | - user.create_namespace!(path: user.username, name: user.name) | |
17 | + user.create_namespace!(path: user.username, name: user.username) | |
18 | 18 | end |
19 | 19 | end |
20 | 20 | end | ... | ... |
app/observers/users_project_observer.rb
1 | 1 | class UsersProjectObserver < ActiveRecord::Observer |
2 | 2 | def after_commit(users_project) |
3 | 3 | return if users_project.destroyed? |
4 | - Notify.project_access_granted_email(users_project.id).deliver | |
4 | + Notify.delay.project_access_granted_email(users_project.id) | |
5 | 5 | end |
6 | 6 | |
7 | 7 | def after_create(users_project) | ... | ... |
app/roles/account.rb
... | ... | @@ -1,124 +0,0 @@ |
1 | -module Account | |
2 | - # Returns a string for use as a Gitolite user identifier | |
3 | - # | |
4 | - # Note that Gitolite 2.x requires the following pattern for users: | |
5 | - # | |
6 | - # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$ | |
7 | - def identifier | |
8 | - # Replace non-word chars with underscores, then make sure it starts with | |
9 | - # valid chars | |
10 | - email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '') | |
11 | - end | |
12 | - | |
13 | - def is_admin? | |
14 | - admin | |
15 | - end | |
16 | - | |
17 | - def require_ssh_key? | |
18 | - keys.count == 0 | |
19 | - end | |
20 | - | |
21 | - def can_create_project? | |
22 | - projects_limit > my_own_projects.count | |
23 | - end | |
24 | - | |
25 | - def can_create_group? | |
26 | - is_admin? | |
27 | - end | |
28 | - | |
29 | - def abilities | |
30 | - @abilities ||= begin | |
31 | - abilities = Six.new | |
32 | - abilities << Ability | |
33 | - abilities | |
34 | - end | |
35 | - end | |
36 | - | |
37 | - def can? action, subject | |
38 | - abilities.allowed?(self, action, subject) | |
39 | - end | |
40 | - | |
41 | - def last_activity_project | |
42 | - projects.first | |
43 | - end | |
44 | - | |
45 | - def first_name | |
46 | - name.split.first unless name.blank? | |
47 | - end | |
48 | - | |
49 | - def cared_merge_requests | |
50 | - MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id) | |
51 | - end | |
52 | - | |
53 | - def project_ids | |
54 | - projects.map(&:id) | |
55 | - end | |
56 | - | |
57 | - # Remove user from all projects and | |
58 | - # set blocked attribute to true | |
59 | - def block | |
60 | - users_projects.find_each do |membership| | |
61 | - return false unless membership.destroy | |
62 | - end | |
63 | - | |
64 | - self.blocked = true | |
65 | - save | |
66 | - end | |
67 | - | |
68 | - def projects_limit_percent | |
69 | - return 100 if projects_limit.zero? | |
70 | - (my_own_projects.count.to_f / projects_limit) * 100 | |
71 | - end | |
72 | - | |
73 | - def recent_push project_id = nil | |
74 | - # Get push events not earlier than 2 hours ago | |
75 | - events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours) | |
76 | - events = events.where(project_id: project_id) if project_id | |
77 | - | |
78 | - # Take only latest one | |
79 | - events = events.recent.limit(1).first | |
80 | - end | |
81 | - | |
82 | - def projects_sorted_by_activity | |
83 | - projects.sorted_by_activity | |
84 | - end | |
85 | - | |
86 | - def namespaces | |
87 | - namespaces = [] | |
88 | - | |
89 | - # Add user account namespace | |
90 | - namespaces << self.namespace if self.namespace | |
91 | - | |
92 | - # Add groups you can manage | |
93 | - namespaces += if admin | |
94 | - Group.all | |
95 | - else | |
96 | - groups.all | |
97 | - end | |
98 | - namespaces | |
99 | - end | |
100 | - | |
101 | - def several_namespaces? | |
102 | - namespaces.size > 1 | |
103 | - end | |
104 | - | |
105 | - def namespace_id | |
106 | - namespace.try :id | |
107 | - end | |
108 | - | |
109 | - def authorized_groups | |
110 | - @authorized_groups ||= begin | |
111 | - groups = Group.where(id: self.projects.pluck(:namespace_id)).all | |
112 | - groups = groups + self.groups | |
113 | - groups.uniq | |
114 | - end | |
115 | - end | |
116 | - | |
117 | - def authorized_projects | |
118 | - Project.authorized_for(self) | |
119 | - end | |
120 | - | |
121 | - def my_own_projects | |
122 | - Project.personal(self) | |
123 | - end | |
124 | -end |
app/roles/authority.rb
... | ... | @@ -1,58 +0,0 @@ |
1 | -module Authority | |
2 | - # Compatible with all access rights | |
3 | - # Should be rewrited for new access rights | |
4 | - def add_access(user, *access) | |
5 | - access = if access.include?(:admin) | |
6 | - { project_access: UsersProject::MASTER } | |
7 | - elsif access.include?(:write) | |
8 | - { project_access: UsersProject::DEVELOPER } | |
9 | - else | |
10 | - { project_access: UsersProject::REPORTER } | |
11 | - end | |
12 | - opts = { user: user } | |
13 | - opts.merge!(access) | |
14 | - users_projects.create(opts) | |
15 | - end | |
16 | - | |
17 | - def reset_access(user) | |
18 | - users_projects.where(project_id: self.id, user_id: user.id).destroy if self.id | |
19 | - end | |
20 | - | |
21 | - def repository_readers | |
22 | - keys = Key.joins({user: :users_projects}). | |
23 | - where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::REPORTER) | |
24 | - keys.map(&:identifier) + deploy_keys.map(&:identifier) | |
25 | - end | |
26 | - | |
27 | - def repository_writers | |
28 | - keys = Key.joins({user: :users_projects}). | |
29 | - where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::DEVELOPER) | |
30 | - keys.map(&:identifier) | |
31 | - end | |
32 | - | |
33 | - def repository_masters | |
34 | - keys = Key.joins({user: :users_projects}). | |
35 | - where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::MASTER) | |
36 | - keys.map(&:identifier) | |
37 | - end | |
38 | - | |
39 | - def allow_read_for?(user) | |
40 | - !users_projects.where(user_id: user.id).empty? | |
41 | - end | |
42 | - | |
43 | - def guest_access_for?(user) | |
44 | - !users_projects.where(user_id: user.id).empty? | |
45 | - end | |
46 | - | |
47 | - def report_access_for?(user) | |
48 | - !users_projects.where(user_id: user.id, project_access: [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty? | |
49 | - end | |
50 | - | |
51 | - def dev_access_for?(user) | |
52 | - !users_projects.where(user_id: user.id, project_access: [UsersProject::DEVELOPER, UsersProject::MASTER]).empty? | |
53 | - end | |
54 | - | |
55 | - def master_access_for?(user) | |
56 | - !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty? | |
57 | - end | |
58 | -end |