Commit e6ce47291b3f08ebe18c2450fc4f21a2a3a2b8a9

Authored by Alex Denisov
2 parents 77bfc591 61049424

master merged

Showing 288 changed files with 4642 additions and 2548 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 288 files displayed.

CONTRIBUTING.md 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +## Contribute to GitLab
  2 +
  3 +If you want to contribute to GitLab, follow this process:
  4 +
  5 +1. Fork the project
  6 +2. Create a feature branch
  7 +3. Code
  8 +4. Create a pull request
  9 +
  10 +We only accept pull requests if:
  11 +
  12 +* Your code has proper tests and all tests pass
  13 +* Your code can be merged w/o problems
  14 +* It wont broke existing functionality
  15 +* Its a quality code
  16 +* We like it :)
  17 +
  18 +## [You may need a developer VM](https://github.com/gitlabhq/developer-vm)
  19 +
  20 +## Running tests
  21 +
  22 +To run the specs for GitLab, you need to run seeds for test db.
  23 +
  24 + cd gitlabhq
  25 + rake db:seed_fu RAILS_ENV=test
  26 +
  27 +Then you can run the test suite with rake:
  28 +
  29 + rake gitlab:test
  30 +
1 source "http://rubygems.org" 1 source "http://rubygems.org"
2 2
  3 +def darwin_only(require_as)
  4 + RUBY_PLATFORM.include?('darwin') && require_as
  5 +end
  6 +
  7 +def linux_only(require_as)
  8 + RUBY_PLATFORM.include?('linux') && require_as
  9 +end
  10 +
3 gem "rails", "3.2.8" 11 gem "rails", "3.2.8"
4 12
5 # Supported DBs 13 # Supported DBs
@@ -8,6 +16,10 @@ gem "mysql2" @@ -8,6 +16,10 @@ gem "mysql2"
8 16
9 # Auth 17 # Auth
10 gem "devise", "~> 2.1.0" 18 gem "devise", "~> 2.1.0"
  19 +gem 'omniauth'
  20 +gem 'omniauth-google-oauth2'
  21 +gem 'omniauth-twitter'
  22 +gem 'omniauth-github'
11 23
12 # GITLAB patched libs 24 # GITLAB patched libs
13 gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" 25 gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
@@ -98,21 +110,28 @@ group :development do @@ -98,21 +110,28 @@ group :development do
98 end 110 end
99 111
100 group :development, :test do 112 group :development, :test do
  113 + gem 'spinach-rails'
101 gem "rspec-rails" 114 gem "rspec-rails"
102 gem "capybara" 115 gem "capybara"
103 gem "capybara-webkit" 116 gem "capybara-webkit"
104 gem "headless" 117 gem "headless"
105 - gem "autotest"  
106 - gem "autotest-rails"  
107 gem "pry" 118 gem "pry"
108 gem "awesome_print" 119 gem "awesome_print"
109 gem "database_cleaner" 120 gem "database_cleaner"
110 gem "launchy" 121 gem "launchy"
111 gem 'factory_girl_rails' 122 gem 'factory_girl_rails'
  123 +
  124 + # Guard
  125 + gem 'guard-rspec'
  126 + gem 'guard-spinach'
  127 +
  128 + # Notification
  129 + gem 'rb-fsevent', :require => darwin_only('rb-fsevent')
  130 + gem 'growl', :require => darwin_only('growl')
  131 + gem 'rb-inotify', :require => linux_only('rb-inotify')
112 end 132 end
113 133
114 group :test do 134 group :test do
115 - gem 'cucumber-rails', :require => false  
116 gem "simplecov", :require => false 135 gem "simplecov", :require => false
117 gem "shoulda-matchers" 136 gem "shoulda-matchers"
118 gem 'email_spec' 137 gem 'email_spec'
@@ -68,7 +68,6 @@ GIT @@ -68,7 +68,6 @@ GIT
68 GEM 68 GEM
69 remote: http://rubygems.org/ 69 remote: http://rubygems.org/
70 specs: 70 specs:
71 - ZenTest (4.8.1)  
72 actionmailer (3.2.8) 71 actionmailer (3.2.8)
73 actionpack (= 3.2.8) 72 actionpack (= 3.2.8)
74 mail (~> 2.4.4) 73 mail (~> 2.4.4)
@@ -100,10 +99,6 @@ GEM @@ -100,10 +99,6 @@ GEM
100 rails (~> 3.0) 99 rails (~> 3.0)
101 addressable (2.2.8) 100 addressable (2.2.8)
102 arel (3.0.2) 101 arel (3.0.2)
103 - autotest (4.4.6)  
104 - ZenTest (>= 4.4.1)  
105 - autotest-rails (4.1.2)  
106 - ZenTest (~> 4.5)  
107 awesome_print (1.0.2) 102 awesome_print (1.0.2)
108 bcrypt-ruby (3.0.1) 103 bcrypt-ruby (3.0.1)
109 blankslate (2.1.2.4) 104 blankslate (2.1.2.4)
@@ -137,16 +132,8 @@ GEM @@ -137,16 +132,8 @@ GEM
137 execjs 132 execjs
138 coffee-script-source (1.3.3) 133 coffee-script-source (1.3.3)
139 colored (1.2) 134 colored (1.2)
  135 + colorize (0.5.8)
140 crack (0.3.1) 136 crack (0.3.1)
141 - cucumber (1.2.1)  
142 - builder (>= 2.1.2)  
143 - diff-lcs (>= 1.1.3)  
144 - gherkin (~> 2.11.0)  
145 - json (>= 1.4.6)  
146 - cucumber-rails (1.3.0)  
147 - capybara (>= 1.1.2)  
148 - cucumber (>= 1.1.8)  
149 - nokogiri (>= 1.5.0)  
150 daemons (1.1.8) 137 daemons (1.1.8)
151 database_cleaner (0.8.0) 138 database_cleaner (0.8.0)
152 devise (2.1.2) 139 devise (2.1.2)
@@ -171,12 +158,13 @@ GEM @@ -171,12 +158,13 @@ GEM
171 factory_girl_rails (4.0.0) 158 factory_girl_rails (4.0.0)
172 factory_girl (~> 4.0.0) 159 factory_girl (~> 4.0.0)
173 railties (>= 3.0.0) 160 railties (>= 3.0.0)
  161 + faraday (0.8.4)
  162 + multipart-post (~> 1.1)
174 ffaker (1.14.0) 163 ffaker (1.14.0)
175 ffi (1.0.11) 164 ffi (1.0.11)
176 foreman (0.47.0) 165 foreman (0.47.0)
177 thor (>= 0.13.6) 166 thor (>= 0.13.6)
178 - gherkin (2.11.0)  
179 - json (>= 1.4.6) 167 + gherkin-ruby (0.2.1)
180 git (1.2.5) 168 git (1.2.5)
181 github-markup (0.7.4) 169 github-markup (0.7.4)
182 gitlab_meta (2.9) 170 gitlab_meta (2.9)
@@ -186,6 +174,15 @@ GEM @@ -186,6 +174,15 @@ GEM
186 multi_xml 174 multi_xml
187 rack 175 rack
188 rack-mount 176 rack-mount
  177 + growl (1.0.3)
  178 + guard (1.3.2)
  179 + listen (>= 0.4.2)
  180 + thor (>= 0.14.6)
  181 + guard-rspec (1.2.1)
  182 + guard (>= 1.1)
  183 + guard-spinach (0.0.2)
  184 + guard (>= 1.1)
  185 + spinach
189 haml (3.1.6) 186 haml (3.1.6)
190 haml-rails (0.3.4) 187 haml-rails (0.3.4)
191 actionpack (~> 3.0) 188 actionpack (~> 3.0)
@@ -199,6 +196,7 @@ GEM @@ -199,6 +196,7 @@ GEM
199 httparty (0.8.3) 196 httparty (0.8.3)
200 multi_json (~> 1.0) 197 multi_json (~> 1.0)
201 multi_xml 198 multi_xml
  199 + httpauth (0.1)
202 i18n (0.6.1) 200 i18n (0.6.1)
203 journey (1.0.4) 201 journey (1.0.4)
204 jquery-rails (2.0.2) 202 jquery-rails (2.0.2)
@@ -208,6 +206,8 @@ GEM @@ -208,6 +206,8 @@ GEM
208 jquery-rails 206 jquery-rails
209 railties (>= 3.1.0) 207 railties (>= 3.1.0)
210 json (1.7.5) 208 json (1.7.5)
  209 + jwt (0.1.5)
  210 + multi_json (>= 1.0)
211 kaminari (0.14.0) 211 kaminari (0.14.0)
212 actionpack (>= 3.0.0) 212 actionpack (>= 3.0.0)
213 activesupport (>= 3.0.0) 213 activesupport (>= 3.0.0)
@@ -219,6 +219,7 @@ GEM @@ -219,6 +219,7 @@ GEM
219 libv8 (3.3.10.4) 219 libv8 (3.3.10.4)
220 libwebsocket (0.1.3) 220 libwebsocket (0.1.3)
221 addressable 221 addressable
  222 + listen (0.5.0)
222 mail (2.4.4) 223 mail (2.4.4)
223 i18n (>= 0.4.0) 224 i18n (>= 0.4.0)
224 mime-types (~> 1.16) 225 mime-types (~> 1.16)
@@ -229,12 +230,35 @@ GEM @@ -229,12 +230,35 @@ GEM
229 sprockets (~> 2.0) 230 sprockets (~> 2.0)
230 multi_json (1.3.6) 231 multi_json (1.3.6)
231 multi_xml (0.5.1) 232 multi_xml (0.5.1)
  233 + multipart-post (1.1.5)
232 mysql2 (0.3.11) 234 mysql2 (0.3.11)
233 net-ldap (0.2.2) 235 net-ldap (0.2.2)
234 nokogiri (1.5.3) 236 nokogiri (1.5.3)
  237 + oauth (0.4.7)
  238 + oauth2 (0.8.0)
  239 + faraday (~> 0.8)
  240 + httpauth (~> 0.1)
  241 + jwt (~> 0.1.4)
  242 + multi_json (~> 1.0)
  243 + rack (~> 1.2)
235 omniauth (1.1.0) 244 omniauth (1.1.0)
236 hashie (~> 1.2) 245 hashie (~> 1.2)
237 rack 246 rack
  247 + omniauth-github (1.0.3)
  248 + omniauth (~> 1.0)
  249 + omniauth-oauth2 (~> 1.1)
  250 + omniauth-google-oauth2 (0.1.13)
  251 + omniauth (~> 1.0)
  252 + omniauth-oauth2
  253 + omniauth-oauth (1.0.1)
  254 + oauth
  255 + omniauth (~> 1.0)
  256 + omniauth-oauth2 (1.1.0)
  257 + oauth2 (~> 0.8.0)
  258 + omniauth (~> 1.0)
  259 + omniauth-twitter (0.0.13)
  260 + multi_json (~> 1.3)
  261 + omniauth-oauth (~> 1.0)
238 orm_adapter (0.3.0) 262 orm_adapter (0.3.0)
239 polyglot (0.3.3) 263 polyglot (0.3.3)
240 posix-spawn (0.3.6) 264 posix-spawn (0.3.6)
@@ -274,6 +298,9 @@ GEM @@ -274,6 +298,9 @@ GEM
274 raindrops (0.9.0) 298 raindrops (0.9.0)
275 rake (0.9.2.2) 299 rake (0.9.2.2)
276 raphael-rails (1.5.2) 300 raphael-rails (1.5.2)
  301 + rb-fsevent (0.9.1)
  302 + rb-inotify (0.8.8)
  303 + ffi (>= 0.5.0)
277 rdoc (3.12) 304 rdoc (3.12)
278 json (~> 1.4) 305 json (~> 1.4)
279 redcarpet (2.1.1) 306 redcarpet (2.1.1)
@@ -336,6 +363,13 @@ GEM @@ -336,6 +363,13 @@ GEM
336 tilt (~> 1.3, >= 1.3.3) 363 tilt (~> 1.3, >= 1.3.3)
337 six (0.2.0) 364 six (0.2.0)
338 slop (2.4.4) 365 slop (2.4.4)
  366 + spinach (0.5.2)
  367 + colorize
  368 + gherkin-ruby (~> 0.2.0)
  369 + spinach-rails (0.1.8)
  370 + capybara (~> 1)
  371 + railties (>= 3)
  372 + spinach (>= 0.4)
339 sprockets (2.1.3) 373 sprockets (2.1.3)
340 hike (~> 1.2) 374 hike (~> 1.2)
341 rack (~> 1.0) 375 rack (~> 1.0)
@@ -378,8 +412,6 @@ PLATFORMS @@ -378,8 +412,6 @@ PLATFORMS
378 DEPENDENCIES 412 DEPENDENCIES
379 acts-as-taggable-on (= 2.3.1) 413 acts-as-taggable-on (= 2.3.1)
380 annotate! 414 annotate!
381 - autotest  
382 - autotest-rails  
383 awesome_print 415 awesome_print
384 bootstrap-sass (= 2.0.4) 416 bootstrap-sass (= 2.0.4)
385 capybara 417 capybara
@@ -389,7 +421,6 @@ DEPENDENCIES @@ -389,7 +421,6 @@ DEPENDENCIES
389 chosen-rails 421 chosen-rails
390 coffee-rails (= 3.2.2) 422 coffee-rails (= 3.2.2)
391 colored 423 colored
392 - cucumber-rails  
393 database_cleaner 424 database_cleaner
394 devise (~> 2.1.0) 425 devise (~> 2.1.0)
395 draper 426 draper
@@ -404,6 +435,9 @@ DEPENDENCIES @@ -404,6 +435,9 @@ DEPENDENCIES
404 grack! 435 grack!
405 grape (~> 0.2.1) 436 grape (~> 0.2.1)
406 grit! 437 grit!
  438 + growl
  439 + guard-rspec
  440 + guard-spinach
407 haml-rails 441 haml-rails
408 headless 442 headless
409 httparty 443 httparty
@@ -415,12 +449,18 @@ DEPENDENCIES @@ -415,12 +449,18 @@ DEPENDENCIES
415 linguist (~> 1.0.0)! 449 linguist (~> 1.0.0)!
416 modernizr (= 2.5.3) 450 modernizr (= 2.5.3)
417 mysql2 451 mysql2
  452 + omniauth
  453 + omniauth-github
  454 + omniauth-google-oauth2
418 omniauth-ldap! 455 omniauth-ldap!
  456 + omniauth-twitter
419 pry 457 pry
420 pygments.rb! 458 pygments.rb!
421 rack-mini-profiler 459 rack-mini-profiler
422 rails (= 3.2.8) 460 rails (= 3.2.8)
423 raphael-rails (= 1.5.2) 461 raphael-rails (= 1.5.2)
  462 + rb-fsevent
  463 + rb-inotify
424 redcarpet (~> 2.1.1) 464 redcarpet (~> 2.1.1)
425 resque (~> 1.20.0) 465 resque (~> 1.20.0)
426 resque_mailer 466 resque_mailer
@@ -432,6 +472,7 @@ DEPENDENCIES @@ -432,6 +472,7 @@ DEPENDENCIES
432 shoulda-matchers 472 shoulda-matchers
433 simplecov 473 simplecov
434 six 474 six
  475 + spinach-rails
435 sqlite3 476 sqlite3
436 stamp 477 stamp
437 test_after_commit 478 test_after_commit
Guardfile 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +# A sample Guardfile
  2 +# More info at https://github.com/guard/guard#readme
  3 +
  4 +guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do
  5 + watch(%r{^spec/.+_spec\.rb$})
  6 + watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
  7 + watch('spec/spec_helper.rb') { "spec" }
  8 +
  9 + # Rails example
  10 + watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
  11 + watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
  12 + watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
  13 + watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
  14 + watch('config/routes.rb') { "spec/routing" }
  15 + watch('app/controllers/application_controller.rb') { "spec/controllers" }
  16 +
  17 + # Capybara request specs
  18 + watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
  19 +end
  20 +
  21 +guard 'spinach' do
  22 + watch(%r|^features/(.*)\.feature|)
  23 + watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
  24 + "features/#{m[1]}#{m[2]}.feature"
  25 + end
  26 +end
app/assets/javascripts/admin.js.coffee
@@ -6,3 +6,7 @@ $ -> @@ -6,3 +6,7 @@ $ ->
6 elems.val('').attr 'disabled', true 6 elems.val('').attr 'disabled', true
7 else 7 else
8 elems.removeAttr 'disabled' 8 elems.removeAttr 'disabled'
  9 +
  10 + $('.log-tabs a').click (e) ->
  11 + e.preventDefault()
  12 + $(this).tab('show')
app/assets/javascripts/application.js
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 //= require jquery.endless-scroll 11 //= require jquery.endless-scroll
12 //= require jquery.highlight 12 //= require jquery.highlight
13 //= require jquery.waitforimages 13 //= require jquery.waitforimages
14 -//= require bootstrap-modal 14 +//= require bootstrap
15 //= require modernizr 15 //= require modernizr
16 //= require chosen-jquery 16 //= require chosen-jquery
17 //= require raphael 17 //= require raphael
app/assets/javascripts/main.js.coffee
@@ -24,6 +24,9 @@ $ -> @@ -24,6 +24,9 @@ $ ->
24 # Click a .one_click_select field, select the contents 24 # Click a .one_click_select field, select the contents
25 $(".one_click_select").live 'click', -> $(this).select() 25 $(".one_click_select").live 'click', -> $(this).select()
26 26
  27 + # Initialize chosen selects
  28 + $('select.chosen').chosen()
  29 +
27 # Disable form buttons while a form is submitting 30 # Disable form buttons while a form is submitting
28 $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> 31 $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
29 buttons = $('[type="submit"]', this) 32 buttons = $('[type="submit"]', this)
app/assets/javascripts/note.js
@@ -1,182 +0,0 @@ @@ -1,182 +0,0 @@
1 -var NoteList = {  
2 -  
3 - notes_path: null,  
4 - target_params: null,  
5 - target_id: 0,  
6 - target_type: null,  
7 - first_id: 0,  
8 - last_id: 0,  
9 - disable:false,  
10 -  
11 - init:  
12 - function(tid, tt, path) {  
13 - this.notes_path = path + ".js";  
14 - this.target_id = tid;  
15 - this.target_type = tt;  
16 - this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;  
17 -  
18 - // get notes  
19 - this.getContent();  
20 -  
21 - // get new notes every n seconds  
22 - this.initRefresh();  
23 -  
24 - $('.delete-note').live('ajax:success', function() {  
25 - $(this).closest('li').fadeOut(); });  
26 -  
27 - $(".note-form-holder").live("ajax:before", function(){  
28 - $(".submit_note").disable()  
29 - })  
30 -  
31 - $(".note-form-holder").live("ajax:complete", function(){  
32 - $(".submit_note").enable()  
33 - })  
34 -  
35 - disableButtonIfEmptyField(".note-text", ".submit_note");  
36 -  
37 - $(".note-text").live("focus", function(){  
38 - $(this).css("height", "80px");  
39 - $('.note_advanced_opts').show();  
40 - });  
41 -  
42 - $("#note_attachment").change(function(e){  
43 - var val = $('.input-file').val();  
44 - var filename = val.replace(/^.*[\\\/]/, '');  
45 - $(".file_name").text(filename);  
46 - });  
47 -  
48 - },  
49 -  
50 -  
51 - /**  
52 - * Load new notes to fresh list called 'new_notes_list':  
53 - * - Replace 'new_notes_list' with new list every n seconds  
54 - * - Append new notes to this list after submit  
55 - */  
56 -  
57 - initRefresh:  
58 - function() {  
59 - // init timer  
60 - var intNew = setInterval("NoteList.getNew()", 10000);  
61 - },  
62 -  
63 - replace:  
64 - function(html) {  
65 - $("#new_notes_list").html(html);  
66 - },  
67 -  
68 - prepend:  
69 - function(id, html) {  
70 - if(id != this.last_id) {  
71 - $("#new_notes_list").prepend(html);  
72 - }  
73 - },  
74 -  
75 - getNew:  
76 - function() {  
77 - // refersh notes list  
78 - $.ajax({  
79 - type: "GET",  
80 - url: this.notes_path,  
81 - data: "last_id=" + this.last_id + this.target_params,  
82 - dataType: "script"});  
83 - },  
84 -  
85 - refresh:  
86 - function() {  
87 - // refersh notes list  
88 - $.ajax({  
89 - type: "GET",  
90 - url: this.notes_path,  
91 - data: "first_id=" + this.first_id + "&last_id=" + this.last_id + this.target_params,  
92 - dataType: "script"});  
93 - },  
94 -  
95 -  
96 - /**  
97 - * Init load of notes:  
98 - * 1. Get content with ajax call  
99 - * 2. Set content of notes list with loaded one  
100 - */  
101 -  
102 -  
103 - getContent:  
104 - function() {  
105 - $.ajax({  
106 - type: "GET",  
107 - url: this.notes_path,  
108 - data: "?" + this.target_params,  
109 - complete: function(){ $('.status').removeClass("loading")},  
110 - beforeSend: function() { $('.status').addClass("loading") },  
111 - dataType: "script"});  
112 - },  
113 -  
114 - setContent:  
115 - function(fid, lid, html) {  
116 - this.last_id = lid;  
117 - this.first_id = fid;  
118 - $("#notes-list").html(html);  
119 -  
120 - // Init infinite scrolling  
121 - this.initLoadMore();  
122 - },  
123 -  
124 -  
125 - /**  
126 - * Paging for old notes when scroll to bottom:  
127 - * 1. Init scroll events with 'initLoadMore'  
128 - * 2. Load onlder notes with 'getOld' method  
129 - * 3. append old notes to bottom of list with 'append'  
130 - *  
131 - */  
132 - getOld:  
133 - function() {  
134 - $('.loading').show();  
135 - $.ajax({  
136 - type: "GET",  
137 - url: this.notes_path,  
138 - data: "first_id=" + this.first_id + this.target_params,  
139 - complete: function(){ $('.status').removeClass("loading")},  
140 - beforeSend: function() { $('.status').addClass("loading") },  
141 - dataType: "script"});  
142 - },  
143 -  
144 - append:  
145 - function(id, html) {  
146 - if(this.first_id == id) {  
147 - this.disable = true;  
148 - } else {  
149 - this.first_id = id;  
150 - $("#notes-list").append(html);  
151 - }  
152 - },  
153 -  
154 - initLoadMore:  
155 - function() {  
156 - $(document).endlessScroll({  
157 - bottomPixels: 400,  
158 - fireDelay: 1000,  
159 - fireOnce:true,  
160 - ceaseFire: function() {  
161 - return NoteList.disable;  
162 - },  
163 - callback: function(i) {  
164 - NoteList.getOld();  
165 - }  
166 - });  
167 - }  
168 -};  
169 -  
170 -var PerLineNotes = {  
171 - init:  
172 - function() {  
173 - $(".line_note_link, .line_note_reply_link").live("click", function(e) {  
174 - var form = $(".per_line_form");  
175 - $(this).closest("tr").after(form);  
176 - form.find("#note_line_code").val($(this).attr("line_code"));  
177 - form.show();  
178 - return false;  
179 - });  
180 - disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");  
181 - }  
182 -}  
app/assets/javascripts/notes.js 0 → 100644
@@ -0,0 +1,293 @@ @@ -0,0 +1,293 @@
  1 +var NoteList = {
  2 +
  3 + notes_path: null,
  4 + target_params: null,
  5 + target_id: 0,
  6 + target_type: null,
  7 + top_id: 0,
  8 + bottom_id: 0,
  9 + loading_more_disabled: false,
  10 + reversed: false,
  11 +
  12 + init:
  13 + function(tid, tt, path) {
  14 + this.notes_path = path + ".js";
  15 + this.target_id = tid;
  16 + this.target_type = tt;
  17 + this.reversed = $("#notes-list").hasClass("reversed");
  18 + this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
  19 +
  20 + // get initial set of notes
  21 + this.getContent();
  22 +
  23 + $("#notes-list, #new-notes-list").on("ajax:success", ".delete-note", function() {
  24 + $(this).closest('li').fadeOut(function() {
  25 + $(this).remove();
  26 + NoteList.updateVotes();
  27 + });
  28 + });
  29 +
  30 + $(".note-form-holder").on("ajax:before", function(){
  31 + $(".submit_note").disable();
  32 + })
  33 +
  34 + $(".note-form-holder").on("ajax:complete", function(){
  35 + $(".submit_note").enable();
  36 + })
  37 +
  38 + disableButtonIfEmptyField(".note-text", ".submit_note");
  39 +
  40 + $("#note_attachment").change(function(e){
  41 + var val = $('.input-file').val();
  42 + var filename = val.replace(/^.*[\\\/]/, '');
  43 + $(".file_name").text(filename);
  44 + });
  45 +
  46 + if(this.reversed) {
  47 + var textarea = $(".note-text");
  48 + $('.note_advanced_opts').hide();
  49 + textarea.css("height", "40px");
  50 + textarea.on("focus", function(){
  51 + $(this).css("height", "80px");
  52 + $('.note_advanced_opts').show();
  53 + });
  54 + }
  55 + },
  56 +
  57 +
  58 + /**
  59 + * Handle loading the initial set of notes.
  60 + * And set up loading more notes when scrolling to the bottom of the page.
  61 + */
  62 +
  63 +
  64 + /**
  65 + * Gets an inital set of notes.
  66 + */
  67 + getContent:
  68 + function() {
  69 + $.ajax({
  70 + type: "GET",
  71 + url: this.notes_path,
  72 + data: "?" + this.target_params,
  73 + complete: function(){ $('.notes-status').removeClass("loading")},
  74 + beforeSend: function() { $('.notes-status').addClass("loading") },
  75 + dataType: "script"});
  76 + },
  77 +
  78 + /**
  79 + * Called in response to getContent().
  80 + * Replaces the content of #notes-list with the given html.
  81 + */
  82 + setContent:
  83 + function(first_id, last_id, html) {
  84 + this.top_id = first_id;
  85 + this.bottom_id = last_id;
  86 + $("#notes-list").html(html);
  87 +
  88 + // init infinite scrolling
  89 + this.initLoadMore();
  90 +
  91 + // init getting new notes
  92 + if (this.reversed) {
  93 + this.initRefreshNew();
  94 + }
  95 + },
  96 +
  97 +
  98 + /**
  99 + * Handle loading more notes when scrolling to the bottom of the page.
  100 + * The id of the last note in the list is in this.bottom_id.
  101 + *
  102 + * Set up refreshing only new notes after all notes have been loaded.
  103 + */
  104 +
  105 +
  106 + /**
  107 + * Initializes loading more notes when scrolling to the bottom of the page.
  108 + */
  109 + initLoadMore:
  110 + function() {
  111 + $(document).endlessScroll({
  112 + bottomPixels: 400,
  113 + fireDelay: 1000,
  114 + fireOnce:true,
  115 + ceaseFire: function() {
  116 + return NoteList.loading_more_disabled;
  117 + },
  118 + callback: function(i) {
  119 + NoteList.getMore();
  120 + }
  121 + });
  122 + },
  123 +
  124 + /**
  125 + * Gets an additional set of notes.
  126 + */
  127 + getMore:
  128 + function() {
  129 + // only load more notes if there are no "new" notes
  130 + $('.loading').show();
  131 + $.ajax({
  132 + type: "GET",
  133 + url: this.notes_path,
  134 + data: "loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id + this.target_params,
  135 + complete: function(){ $('.notes-status').removeClass("loading")},
  136 + beforeSend: function() { $('.notes-status').addClass("loading") },
  137 + dataType: "script"});
  138 + },
  139 +
  140 + /**
  141 + * Called in response to getMore().
  142 + * Append notes to #notes-list.
  143 + */
  144 + appendMoreNotes:
  145 + function(id, html) {
  146 + if(id != this.bottom_id) {
  147 + this.bottom_id = id;
  148 + $("#notes-list").append(html);
  149 + }
  150 + },
  151 +
  152 + /**
  153 + * Called in response to getMore().
  154 + * Disables loading more notes when scrolling to the bottom of the page.
  155 + * Initalizes refreshing new notes.
  156 + */
  157 + finishedLoadingMore:
  158 + function() {
  159 + this.loading_more_disabled = true;
  160 +
  161 + // from now on only get new notes
  162 + if (!this.reversed) {
  163 + this.initRefreshNew();
  164 + }
  165 + // make sure we are up to date
  166 + this.updateVotes();
  167 + },
  168 +
  169 +
  170 + /**
  171 + * Handle refreshing and adding of new notes.
  172 + *
  173 + * New notes are all notes that are created after the site has been loaded.
  174 + * The "old" notes are in #notes-list the "new" ones will be in #new-notes-list.
  175 + * The id of the last "old" note is in this.bottom_id.
  176 + */
  177 +
  178 +
  179 + /**
  180 + * Initializes getting new notes every n seconds.
  181 + */
  182 + initRefreshNew:
  183 + function() {
  184 + setInterval("NoteList.getNew()", 10000);
  185 + },
  186 +
  187 + /**
  188 + * Gets the new set of notes.
  189 + */
  190 + getNew:
  191 + function() {
  192 + $.ajax({
  193 + type: "GET",
  194 + url: this.notes_path,
  195 + data: "loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id) + this.target_params,
  196 + dataType: "script"});
  197 + },
  198 +
  199 + /**
  200 + * Called in response to getNew().
  201 + * Replaces the content of #new-notes-list with the given html.
  202 + */
  203 + replaceNewNotes:
  204 + function(html) {
  205 + $("#new-notes-list").html(html);
  206 + this.updateVotes();
  207 + },
  208 +
  209 + /**
  210 + * Adds a single note to #new-notes-list.
  211 + */
  212 + appendNewNote:
  213 + function(id, html) {
  214 + if (this.reversed) {
  215 + $("#new-notes-list").prepend(html);
  216 + } else {
  217 + $("#new-notes-list").append(html);
  218 + }
  219 + this.updateVotes();
  220 + },
  221 +
  222 + /**
  223 + * Recalculates the votes and updates them (if they are displayed at all).
  224 + *
  225 + * Assumes all relevant notes are displayed (i.e. there are no more notes to
  226 + * load via getMore()).
  227 + * Might produce inaccurate results when not all notes have been loaded and a
  228 + * recalculation is triggered (e.g. when deleting a note).
  229 + */
  230 + updateVotes:
  231 + function() {
  232 + var votes = $("#votes .votes");
  233 + var notes = $("#notes-list, #new-notes-list").find(".note.vote");
  234 +
  235 + // only update if there is a vote display
  236 + if (votes.size()) {
  237 + var upvotes = notes.filter(".upvote").size();
  238 + var downvotes = notes.filter(".downvote").size();
  239 + var votesCount = upvotes + downvotes;
  240 + var upvotesPercent = votesCount ? (100.0 / votesCount * upvotes) : 0;
  241 + var downvotesPercent = votesCount ? (100.0 - upvotesPercent) : 0;
  242 +
  243 + // change vote bar lengths
  244 + votes.find(".bar-success").css("width", upvotesPercent+"%");
  245 + votes.find(".bar-danger").css("width", downvotesPercent+"%");
  246 + // replace vote numbers
  247 + votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes));
  248 + votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes));
  249 + }
  250 + }
  251 +};
  252 +
  253 +var PerLineNotes = {
  254 + init:
  255 + function() {
  256 + /**
  257 + * Called when clicking on the "add note" or "reply" button for a diff line.
  258 + *
  259 + * Shows the note form below the line.
  260 + * Sets some hidden fields in the form.
  261 + */
  262 + $(".diff_file_content").on("click", ".line_note_link, .line_note_reply_link", function(e) {
  263 + var form = $(".per_line_form");
  264 + $(this).closest("tr").after(form);
  265 + form.find("#note_line_code").val($(this).data("lineCode"));
  266 + form.show();
  267 + return false;
  268 + });
  269 +
  270 + disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
  271 +
  272 + /**
  273 + * Called in response to successfully deleting a note on a diff line.
  274 + *
  275 + * Removes the actual note from view.
  276 + * Removes the reply button if the last note for that line has been removed.
  277 + */
  278 + $(".diff_file_content").on("ajax:success", ".delete-note", function() {
  279 + var trNote = $(this).closest("tr");
  280 + trNote.fadeOut(function() {
  281 + $(this).remove();
  282 + });
  283 +
  284 + // check if this is the last note for this line
  285 + // elements must really be removed for this to work reliably
  286 + var trLine = trNote.prev();
  287 + var trRpl = trNote.next();
  288 + if (trLine.hasClass("line_holder") && trRpl.hasClass("reply")) {
  289 + trRpl.fadeOut(function() { $(this).remove(); });
  290 + }
  291 + });
  292 + }
  293 +}
app/assets/javascripts/projects.js.coffee
@@ -10,11 +10,15 @@ window.Projects = -> @@ -10,11 +10,15 @@ window.Projects = ->
10 $('form #project_default_branch').chosen() 10 $('form #project_default_branch').chosen()
11 disableButtonIfEmptyField '#project_name', '.project-submit' 11 disableButtonIfEmptyField '#project_name', '.project-submit'
12 12
13 -# Git clone panel switcher  
14 $ -> 13 $ ->
  14 + # Git clone panel switcher
15 scope = $ '.project_clone_holder' 15 scope = $ '.project_clone_holder'
16 if scope.length > 0 16 if scope.length > 0
17 $('a, button', scope).click -> 17 $('a, button', scope).click ->
18 $('a, button', scope).removeClass 'active' 18 $('a, button', scope).removeClass 'active'
19 $(@).addClass 'active' 19 $(@).addClass 'active'
20 $('#project_clone', scope).val $(@).data 'clone' 20 $('#project_clone', scope).val $(@).data 'clone'
  21 +
  22 + # Ref switcher
  23 + $('.project-refs-select').on 'change', ->
  24 + $(@).parents('form').submit()
app/assets/stylesheets/common.scss
@@ -145,6 +145,19 @@ span.update-author { @@ -145,6 +145,19 @@ span.update-author {
145 .label { 145 .label {
146 background-color: #474D57; 146 background-color: #474D57;
147 147
  148 + &.label-tag {
  149 + background: none;
  150 + border: none;
  151 + padding:4px 6px;
  152 + color:#444;
  153 + text-shadow:0 0 1px #fff;
  154 +
  155 + &.grouped {
  156 + float: left;
  157 + margin-right: 6px;
  158 + padding: 6px;
  159 + }
  160 + }
148 &.label-issue { 161 &.label-issue {
149 background-color: #eee; 162 background-color: #eee;
150 border: 1px solid #ccc; 163 border: 1px solid #ccc;
@@ -158,6 +171,18 @@ span.update-author { @@ -158,6 +171,18 @@ span.update-author {
158 padding: 6px; 171 padding: 6px;
159 } 172 }
160 } 173 }
  174 +
  175 + &.label-success {
  176 + background-color: #8D8;
  177 + color: #333;
  178 + text-shadow: 0 1px 1px white;
  179 + }
  180 +
  181 + &.label-error {
  182 + background-color: #D88;
  183 + color: #333;
  184 + text-shadow: 0 1px 1px white;
  185 + }
161 } 186 }
162 187
163 .event_label { 188 .event_label {
@@ -181,11 +206,12 @@ span.update-author { @@ -181,11 +206,12 @@ span.update-author {
181 } 206 }
182 207
183 &.joined { 208 &.joined {
184 - background-color: #1cb9ff; 209 + background-color: #1ca9dd;
185 } 210 }
186 211
187 &.left { 212 &.left {
188 - background-color: #ff5057; 213 + background-color: #888;
  214 + float:none;
189 } 215 }
190 } 216 }
191 217
@@ -414,13 +440,48 @@ p.time { @@ -414,13 +440,48 @@ p.time {
414 } 440 }
415 } 441 }
416 442
417 -.upvotes {  
418 - font-size: 14px;  
419 - font-weight: bold;  
420 - color: #468847;  
421 - text-align: right;  
422 - padding: 4px;  
423 - margin: 2px; 443 +.votes {
  444 + font-size: 13px;
  445 + line-height: 15px;
  446 + .progress {
  447 + height: 4px;
  448 + margin: 0;
  449 + .bar {
  450 + float: left;
  451 + height: 100%;
  452 + }
  453 + .bar-success {
  454 + background-color: #468847;
  455 + @include bg-gradient(#62C462, #51A351);
  456 + }
  457 + .bar-danger {
  458 + background-color: #B94A48;
  459 + @include bg-gradient(#EE5F5B, #BD362F);
  460 + }
  461 + }
  462 + .upvotes {
  463 + display: inline-block;
  464 + color: #468847;
  465 + }
  466 + .downvotes {
  467 + display: inline-block;
  468 + color: #B94A48;
  469 + }
  470 +}
  471 +.votes-block {
  472 + margin: 14px 6px 6px 0;
  473 + .downvotes {
  474 + float: right;
  475 + }
  476 +}
  477 +.votes-inline {
  478 + display: inline-block;
  479 + margin: 0 8px;
  480 + .progress {
  481 + display: inline-block;
  482 + padding: 0 0 2px;
  483 + width: 45px;
  484 + }
424 } 485 }
425 486
426 /* Fix for readme code (stopped it from being yellow) */ 487 /* Fix for readme code (stopped it from being yellow) */
@@ -624,7 +685,7 @@ li.note { @@ -624,7 +685,7 @@ li.note {
624 margin-right:40px; 685 margin-right:40px;
625 686
626 .prev { 687 .prev {
627 - @extend .borders; 688 + @extend .thumbnail;
628 height:120px; 689 height:120px;
629 width:175px; 690 width:175px;
630 margin-bottom:10px; 691 margin-bottom:10px;
@@ -653,3 +714,31 @@ li.note { @@ -653,3 +714,31 @@ li.note {
653 text-align:center; 714 text-align:center;
654 margin-bottom:10px; 715 margin-bottom:10px;
655 } 716 }
  717 +
  718 +.oauth_select_holder {
  719 + padding:20px;
  720 + img {
  721 + padding:5px;
  722 + margin-right:10px;
  723 + }
  724 + .active {
  725 + img {
  726 + border:1px solid #ccc;
  727 + background:$hover;
  728 + @include border-radius(5px);
  729 + }
  730 + }
  731 +}
  732 +
  733 +.btn-build-token {
  734 + float: left;
  735 + padding: 6px 20px;
  736 + margin-right: 12px;
  737 +}
  738 +
  739 +.gitlab-promo {
  740 + a {
  741 + color:#aaa;
  742 + margin-right: 30px;
  743 + }
  744 +}
app/assets/stylesheets/gitlab_bootstrap/blocks.scss
@@ -65,6 +65,10 @@ @@ -65,6 +65,10 @@
65 border-color: #CCC; 65 border-color: #CCC;
66 @include solid_shade; 66 @include solid_shade;
67 67
  68 + &.white {
  69 + background:#fff;
  70 + }
  71 +
68 ul { 72 ul {
69 margin:0; 73 margin:0;
70 } 74 }
@@ -142,4 +146,8 @@ @@ -142,4 +146,8 @@
142 border:none; 146 border:none;
143 } 147 }
144 } 148 }
  149 +
  150 + .ui-box-body {
  151 + padding:10px;
  152 + }
145 } 153 }
app/assets/stylesheets/gitlab_bootstrap/common.scss
@@ -33,7 +33,29 @@ @@ -33,7 +33,29 @@
33 .nav-pills a:hover { background-color:#888; } 33 .nav-pills a:hover { background-color:#888; }
34 .nav-pills .active a { background-color: $style_color; } 34 .nav-pills .active a { background-color: $style_color; }
35 .nav-tabs > li > a, .nav-pills > li > a { color:$style_color; } 35 .nav-tabs > li > a, .nav-pills > li > a { color:$style_color; }
36 -.nav-tabs > .active > a { font-weight:bold; } 36 +.nav.nav-tabs {
  37 + li {
  38 + > a {
  39 + padding:8px 20px;
  40 + margin-right: 7px;
  41 + border-color: #EEE;
  42 + color:#888;
  43 + border-bottom: 1px solid #ddd;
  44 + .badge {
  45 + background-color: #eee;
  46 + color:#888;
  47 + text-shadow:0 1px 1px #fff;
  48 + }
  49 + }
  50 + &.active {
  51 + > a {
  52 + border-color: #CCC;
  53 + border-bottom: 1px solid #fff;
  54 + color:#333;
  55 + }
  56 + }
  57 + }
  58 +}
37 59
38 /** ALERT MESSAGES **/ 60 /** ALERT MESSAGES **/
39 .alert-message { @extend .alert; } 61 .alert-message { @extend .alert; }
@@ -50,3 +72,13 @@ img.lil_av { padding-left: 4px; padding-right:3px; } @@ -50,3 +72,13 @@ img.lil_av { padding-left: 4px; padding-right:3px; }
50 /** HELPERS **/ 72 /** HELPERS **/
51 .nothing_here_message { text-align:center; padding:20px; color:#777; } 73 .nothing_here_message { text-align:center; padding:20px; color:#777; }
52 p.slead { color:#456; font-size:16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; } 74 p.slead { color:#456; font-size:16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; }
  75 +
  76 +/** FORMS **/
  77 +input[type='search'].search-text-input {
  78 + background-image: url("icon-search.png");
  79 + background-repeat: no-repeat;
  80 + background-position: 10px;
  81 + padding-left:25px;
  82 + @include border-radius(4px);
  83 + border:1px solid #ccc;
  84 +}
app/assets/stylesheets/main.scss
@@ -135,7 +135,6 @@ $hover: #fdf5d9; @@ -135,7 +135,6 @@ $hover: #fdf5d9;
135 */ 135 */
136 @import "common.scss"; 136 @import "common.scss";
137 137
138 -  
139 /** 138 /**
140 * Styles related to specific part of app 139 * Styles related to specific part of app
141 */ 140 */
@@ -162,6 +161,11 @@ $hover: #fdf5d9; @@ -162,6 +161,11 @@ $hover: #fdf5d9;
162 @import "sections/notes.scss"; 161 @import "sections/notes.scss";
163 162
164 /** 163 /**
  164 + * This file represent profile styles
  165 + */
  166 +@import "sections/profile.scss";
  167 +
  168 +/**
165 * Devise styles 169 * Devise styles
166 */ 170 */
167 @import "sections/login.scss"; 171 @import "sections/login.scss";
app/assets/stylesheets/ref_select.scss
@@ -12,35 +12,45 @@ @@ -12,35 +12,45 @@
12 width:120px; 12 width:120px;
13 } 13 }
14 14
15 -.project-refs-form .chzn-container { 15 +.project-refs-form .chzn-container {
16 position: relative; 16 position: relative;
17 top: 0; 17 top: 0;
18 left: 0; 18 left: 0;
19 margin-right: 10px; 19 margin-right: 10px;
20 20
21 - .chzn-drop { 21 + .chzn-drop {
22 margin:7px 0; 22 margin:7px 0;
23 - border: 1px solid #CCC;  
24 - min-width: 300px; 23 + min-width: 400px;
  24 + border: 2px solid $blue_link;
  25 + @include border-radius(4px);
25 26
26 - .chzn-results { 27 + .chzn-results {
27 max-height:300px; 28 max-height:300px;
  29 +
  30 + .group-result {
  31 + color: $blue_link;
  32 + }
  33 + .active-result {
  34 + &.highlighted {
  35 + background: $blue_link;
  36 + }
  37 + }
28 } 38 }
29 39
30 .chzn-search input { 40 .chzn-search input {
31 - min-width:200px; 41 + min-width:365px;
32 } 42 }
33 } 43 }
34 44
35 - .chzn-single { 45 + .chzn-single {
36 @include bg-gray-gradient; 46 @include bg-gray-gradient;
37 47
38 - div { 48 + div {
39 background:transparent; 49 background:transparent;
40 border-left:none; 50 border-left:none;
41 } 51 }
42 52
43 - span { 53 + span {
44 font-weight: normal; 54 font-weight: normal;
45 } 55 }
46 } 56 }
app/assets/stylesheets/sections/issues.scss
1 -.issue_form_box { 1 +.issue_form_box {
2 @extend .main_box; 2 @extend .main_box;
3 - .issue_title { 3 + .issue_title {
4 @extend .top_box_content; 4 @extend .top_box_content;
5 - .clearfix {  
6 - margin-bottom:0px;  
7 - input { 5 + .clearfix {
  6 + margin-bottom:0px;
  7 + input {
8 @extend .span8; 8 @extend .span8;
9 } 9 }
10 } 10 }
11 } 11 }
12 - .issue_middle_block { 12 + .issue_middle_block {
13 @extend .middle_box_content; 13 @extend .middle_box_content;
14 height:30px; 14 height:30px;
15 - .issue_assignee { 15 + .issue_assignee {
16 @extend .span6; 16 @extend .span6;
17 float:left; 17 float:left;
18 } 18 }
19 - .issue_milestone { 19 + .issue_milestone {
20 @extend .span4; 20 @extend .span4;
21 float:left; 21 float:left;
22 } 22 }
23 } 23 }
24 - .issue_description { 24 + .issue_description {
25 @extend .bottom_box_content; 25 @extend .bottom_box_content;
26 } 26 }
27 } 27 }
28 28
29 -.issues_table {  
30 - .issue { 29 +.issues_table {
  30 + .issue {
31 padding:7px 10px; 31 padding:7px 10px;
32 32
33 - .issue_check { 33 + .issue_check {
34 float:left; 34 float:left;
35 padding: 8px 0; 35 padding: 8px 0;
36 padding-right: 8px; 36 padding-right: 8px;
37 min-width: 15px; 37 min-width: 15px;
38 } 38 }
39 39
40 - p { 40 + p {
41 padding-top:0; 41 padding-top:0;
42 padding-bottom:2px; 42 padding-bottom:2px;
43 } 43 }
44 44
45 - img.avatar { 45 + img.avatar {
46 width:32px; 46 width:32px;
47 margin-top:4px; 47 margin-top:4px;
48 } 48 }
49 } 49 }
50 } 50 }
51 51
52 -input.check_all_issues { 52 +input.check_all_issues {
53 float:left; 53 float:left;
54 padding: 0; 54 padding: 0;
55 margin:0; 55 margin:0;
@@ -59,8 +59,8 @@ input.check_all_issues { @@ -59,8 +59,8 @@ input.check_all_issues {
59 height: 22px; 59 height: 22px;
60 } 60 }
61 61
62 -.issues_content {  
63 - .title { 62 +.issues_content {
  63 + .title {
64 height: 40px; 64 height: 40px;
65 } 65 }
66 } 66 }
@@ -70,30 +70,30 @@ input.check_all_issues { @@ -70,30 +70,30 @@ input.check_all_issues {
70 @media (min-width: 1200px) { .issues_filters select { width:220px; } } 70 @media (min-width: 1200px) { .issues_filters select { width:220px; } }
71 71
72 72
73 -#issues-table-holder {  
74 - .issues_filters {  
75 - form { 73 +#issues-table-holder {
  74 + .issues_filters {
  75 + form {
76 padding:0; 76 padding:0;
77 margin:0; 77 margin:0;
78 margin-top:7px 78 margin-top:7px
79 } 79 }
80 - } 80 + }
81 81
82 - .issues_bulk_update { 82 + .issues_bulk_update {
83 margin: 0; 83 margin: 0;
84 - form { 84 + form {
85 padding:0; 85 padding:0;
86 margin:0; 86 margin:0;
87 margin-top:7px 87 margin-top:7px
88 } 88 }
89 - .update_selected_issues { 89 + .update_selected_issues {
90 position:relative; 90 position:relative;
91 top:-2px; 91 top:-2px;
92 margin-left:4px; 92 margin-left:4px;
93 float:left; 93 float:left;
94 } 94 }
95 -  
96 - .update_issues_text { 95 +
  96 + .update_issues_text {
97 padding:3px; 97 padding:3px;
98 line-height: 18px; 98 line-height: 18px;
99 float:left; 99 float:left;
@@ -101,10 +101,11 @@ input.check_all_issues { @@ -101,10 +101,11 @@ input.check_all_issues {
101 } 101 }
102 } 102 }
103 103
104 -#update_status { 104 +#update_status {
105 width:100px; 105 width:100px;
106 } 106 }
107 107
  108 +
108 /** 109 /**
109 * Milestones list 110 * Milestones list
110 * 111 *
app/assets/stylesheets/sections/merge_requests.scss
1 -/** 1 +/**
2 * MR form 2 * MR form
3 * 3 *
4 */ 4 */
5 5
6 -.mr_branch_box { 6 +.mr_branch_box {
7 @extend .ui-box; 7 @extend .ui-box;
8 margin-bottom:20px; 8 margin-bottom:20px;
9 9
10 - .body { 10 + .body {
11 background:#f1f1f1; 11 background:#f1f1f1;
12 } 12 }
13 13
@@ -17,19 +17,19 @@ @@ -17,19 +17,19 @@
17 * MR -> show: Automerge widget 17 * MR -> show: Automerge widget
18 * 18 *
19 */ 19 */
20 -.automerge_widget {  
21 - &.can_be_merged { 20 +.automerge_widget {
  21 + &.can_be_merged {
22 background: #DFF0D8; 22 background: #DFF0D8;
23 } 23 }
24 24
25 - form { 25 + form {
26 margin-bottom:0; 26 margin-bottom:0;
27 - .clearfix { 27 + .clearfix {
28 margin-bottom:0; 28 margin-bottom:0;
29 } 29 }
30 } 30 }
31 31
32 - .accept_group { 32 + .accept_group {
33 float:left; 33 float:left;
34 border: 1px solid #ADA; 34 border: 1px solid #ADA;
35 padding: 2px; 35 padding: 2px;
@@ -37,29 +37,29 @@ @@ -37,29 +37,29 @@
37 border-radius: 5px; 37 border-radius: 5px;
38 background: #CEB; 38 background: #CEB;
39 39
40 - .accept_merge_request { 40 + .accept_merge_request {
41 font-size:13px; 41 font-size:13px;
42 float:left; 42 float:left;
43 } 43 }
44 - .remove_branch_holder { 44 + .remove_branch_holder {
45 margin-left:20px; 45 margin-left:20px;
46 margin-right:10px; 46 margin-right:10px;
47 float:left; 47 float:left;
48 } 48 }
49 - label { 49 + label {
50 color:#444; 50 color:#444;
51 } 51 }
52 } 52 }
53 53
54 54
55 - .how_to_merge_link { 55 + .how_to_merge_link {
56 @extend .primary; 56 @extend .primary;
57 } 57 }
58 } 58 }
59 59
60 -.mr_nav_tabs {  
61 - li {  
62 - a { 60 +.mr_nav_tabs {
  61 + li {
  62 + a {
63 font-weight:bold; 63 font-weight:bold;
64 padding:8px 20px; 64 padding:8px 20px;
65 text-align:center; 65 text-align:center;
@@ -67,19 +67,19 @@ @@ -67,19 +67,19 @@
67 } 67 }
68 } 68 }
69 69
70 -li.merge_request { 70 +li.merge_request {
71 padding:7px 10px; 71 padding:7px 10px;
72 - img.avatar { 72 + img.avatar {
73 width: 32px; 73 width: 32px;
74 margin-top: 4px; 74 margin-top: 4px;
75 } 75 }
76 - p { 76 + p {
77 padding: 0px; 77 padding: 0px;
78 padding-bottom: 2px; 78 padding-bottom: 2px;
79 } 79 }
80 } 80 }
81 81
82 -.merge_in_progress { 82 +.merge_in_progress {
83 @extend .padded; 83 @extend .padded;
84 @extend .append-bottom-10; 84 @extend .append-bottom-10;
85 } 85 }
@@ -88,22 +88,21 @@ li.merge_request { @@ -88,22 +88,21 @@ li.merge_request {
88 @include round-borders-all(4px); 88 @include round-borders-all(4px);
89 padding:2px 4px; 89 padding:2px 4px;
90 border:none; 90 border:none;
91 - font-size:13px; 91 + font-size:14px;
92 background: #474D57; 92 background: #474D57;
93 color:#fff; 93 color:#fff;
94 - font-weight:bold;  
95 - font-family: monospace; 94 + font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
96 } 95 }
97 96
98 -.mr_source_commit,  
99 -.mr_target_commit {  
100 - .commit { 97 +.mr_source_commit,
  98 +.mr_target_commit {
  99 + .commit {
101 margin:0; 100 margin:0;
102 padding:0; 101 padding:0;
103 padding: 5px; 102 padding: 5px;
104 margin-bottom: 5px; 103 margin-bottom: 5px;
105 .avatar { position:relative } 104 .avatar { position:relative }
106 - .row_title { 105 + .row_title {
107 color:#444; 106 color:#444;
108 } 107 }
109 .commit-author-name, 108 .commit-author-name,
@@ -113,12 +112,12 @@ li.merge_request { @@ -113,12 +112,12 @@ li.merge_request {
113 display:none; 112 display:none;
114 } 113 }
115 list-style:none; 114 list-style:none;
116 - &:hover { 115 + &:hover {
117 background:none; 116 background:none;
118 } 117 }
119 } 118 }
120 } 119 }
121 120
122 -.mr_direction_tip { 121 +.mr_direction_tip {
123 margin-top:40px 122 margin-top:40px
124 } 123 }
app/assets/stylesheets/sections/nav.scss
@@ -55,7 +55,6 @@ ul.main_menu { @@ -55,7 +55,6 @@ ul.main_menu {
55 55
56 &.current { 56 &.current {
57 background-color:#D5D5D5; 57 background-color:#D5D5D5;
58 - border-bottom: 1px solid #AAA;  
59 border-right: 1px solid #BBB; 58 border-right: 1px solid #BBB;
60 border-left: 1px solid #BBB; 59 border-left: 1px solid #BBB;
61 border-radius: 0 0 1px 1px; 60 border-radius: 0 0 1px 1px;
app/assets/stylesheets/sections/notes.scss
@@ -3,17 +3,13 @@ @@ -3,17 +3,13 @@
3 * 3 *
4 */ 4 */
5 #notes-list, 5 #notes-list,
6 -#new_notes_list { 6 +#new-notes-list {
7 display:block; 7 display:block;
8 list-style:none; 8 list-style:none;
9 margin:0px; 9 margin:0px;
10 padding:0px; 10 padding:0px;
11 } 11 }
12 12
13 -#new_notes_list li:last-child{  
14 - border-bottom:1px solid #aaa;  
15 -}  
16 -  
17 .issue_notes, 13 .issue_notes,
18 .wiki_notes { 14 .wiki_notes {
19 .note_content { 15 .note_content {
@@ -30,9 +26,6 @@ @@ -30,9 +26,6 @@
30 } 26 }
31 27
32 #new_note { 28 #new_note {
33 - .note-text {  
34 - height:40px;  
35 - }  
36 .attach_holder { 29 .attach_holder {
37 display:none; 30 display:none;
38 } 31 }
@@ -48,7 +41,6 @@ @@ -48,7 +41,6 @@
48 41
49 .note { 42 .note {
50 padding: 8px 0; 43 padding: 8px 0;
51 - border-bottom: 1px solid #eee;  
52 overflow: hidden; 44 overflow: hidden;
53 display: block; 45 display: block;
54 img {float: left; margin-right: 10px;} 46 img {float: left; margin-right: 10px;}
@@ -70,6 +62,23 @@ @@ -70,6 +62,23 @@
70 .delete-note { display:block; } 62 .delete-note { display:block; }
71 } 63 }
72 } 64 }
  65 +#notes-list:not(.reversed) .note,
  66 +#new-notes-list:not(.reversed) .note {
  67 + border-bottom: 1px solid #eee;
  68 +}
  69 +#notes-list.reversed .note,
  70 +#new-notes-list.reversed .note {
  71 + border-top: 1px solid #eee;
  72 +}
  73 +
  74 +/* mark vote notes */
  75 +.voting_notes .note {
  76 + padding: 8px 0;
  77 +}
  78 +
  79 +.notes-status {
  80 + margin: 18px;
  81 +}
73 82
74 83
75 p.notify_controls input{ 84 p.notify_controls input{
@@ -213,7 +222,7 @@ td .line_note_link { @@ -213,7 +222,7 @@ td .line_note_link {
213 } 222 }
214 } 223 }
215 224
216 -.note-text { 225 +.note-text {
217 border: 1px solid #aaa; 226 border: 1px solid #aaa;
218 box-shadow:none; 227 box-shadow:none;
219 } 228 }
app/assets/stylesheets/sections/profile.scss 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +.profile_history {
  2 + .event_feed {
  3 + min-height:20px;
  4 + .avatar {
  5 + width:20px;
  6 + }
  7 + }
  8 +}
app/contexts/notes/load_context.rb
@@ -3,30 +3,31 @@ module Notes @@ -3,30 +3,31 @@ module Notes
3 def execute 3 def execute
4 target_type = params[:target_type] 4 target_type = params[:target_type]
5 target_id = params[:target_id] 5 target_id = params[:target_id]
6 - first_id = params[:first_id]  
7 - last_id = params[:last_id] 6 + after_id = params[:after_id]
  7 + before_id = params[:before_id]
8 8
9 9
10 @notes = case target_type 10 @notes = case target_type
11 - when "commit"  
12 - then project.commit_notes(project.commit(target_id)).fresh.limit(20)  
13 - when "snippet"  
14 - then project.snippets.find(target_id).notes  
15 - when "wall"  
16 - then project.common_notes.order("created_at DESC").fresh.limit(50) 11 + when "commit"
  12 + project.commit_notes(project.commit(target_id)).fresh.limit(20)
17 when "issue" 13 when "issue"
18 - then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20) 14 + project.issues.find(target_id).notes.inc_author.fresh.limit(20)
19 when "merge_request" 15 when "merge_request"
20 - then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20) 16 + project.merge_requests.find(target_id).notes.inc_author.fresh.limit(20)
  17 + when "snippet"
  18 + project.snippets.find(target_id).notes.fresh
  19 + when "wall"
  20 + # this is the only case, where the order is DESC
  21 + project.common_notes.order("created_at DESC, id DESC").limit(50)
21 when "wiki" 22 when "wiki"
22 - then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20] 23 + project.wiki_notes.limit(20)
23 end 24 end
24 25
25 - @notes = if last_id  
26 - @notes.where("id > ?", last_id)  
27 - elsif first_id  
28 - @notes.where("id < ?", first_id)  
29 - else 26 + @notes = if after_id
  27 + @notes.where("id > ?", after_id)
  28 + elsif before_id
  29 + @notes.where("id < ?", before_id)
  30 + else
30 @notes 31 @notes
31 end 32 end
32 end 33 end
app/controllers/admin/dashboard_controller.rb
1 -class Admin::DashboardController < ApplicationController  
2 - layout "admin"  
3 - before_filter :authenticate_user!  
4 - before_filter :authenticate_admin!  
5 - 1 +class Admin::DashboardController < AdminController
6 def index 2 def index
7 @workers = Resque.workers 3 @workers = Resque.workers
8 @pending_jobs = Resque.size(:post_receive) 4 @pending_jobs = Resque.size(:post_receive)
app/controllers/admin/hooks_controller.rb
1 -class Admin::HooksController < ApplicationController  
2 - layout "admin"  
3 - before_filter :authenticate_user!  
4 - before_filter :authenticate_admin!  
5 - 1 +class Admin::HooksController < AdminController
6 def index 2 def index
7 @hooks = SystemHook.all 3 @hooks = SystemHook.all
8 @hook = SystemHook.new 4 @hook = SystemHook.new
@@ -15,7 +11,7 @@ class Admin::HooksController &lt; ApplicationController @@ -15,7 +11,7 @@ class Admin::HooksController &lt; ApplicationController
15 redirect_to admin_hooks_path, notice: 'Hook was successfully created.' 11 redirect_to admin_hooks_path, notice: 'Hook was successfully created.'
16 else 12 else
17 @hooks = SystemHook.all 13 @hooks = SystemHook.all
18 - render :index 14 + render :index
19 end 15 end
20 end 16 end
21 17
app/controllers/admin/logs_controller.rb
1 -class Admin::LogsController < ApplicationController  
2 - layout "admin"  
3 - before_filter :authenticate_user!  
4 - before_filter :authenticate_admin! 1 +class Admin::LogsController < AdminController
5 end 2 end
6 -  
app/controllers/admin/projects_controller.rb
1 -class Admin::ProjectsController < ApplicationController  
2 - layout "admin"  
3 - before_filter :authenticate_user!  
4 - before_filter :authenticate_admin! 1 +class Admin::ProjectsController < AdminController
5 before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update] 2 before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update]
6 3
7 def index 4 def index
@@ -43,7 +40,7 @@ class Admin::ProjectsController &lt; ApplicationController @@ -43,7 +40,7 @@ class Admin::ProjectsController &lt; ApplicationController
43 def update 40 def update
44 owner_id = params[:project].delete(:owner_id) 41 owner_id = params[:project].delete(:owner_id)
45 42
46 - if owner_id 43 + if owner_id
47 @admin_project.owner = User.find(owner_id) 44 @admin_project.owner = User.find(owner_id)
48 end 45 end
49 46
@@ -60,7 +57,7 @@ class Admin::ProjectsController &lt; ApplicationController @@ -60,7 +57,7 @@ class Admin::ProjectsController &lt; ApplicationController
60 redirect_to admin_projects_url, notice: 'Project was successfully deleted.' 57 redirect_to admin_projects_url, notice: 'Project was successfully deleted.'
61 end 58 end
62 59
63 - private 60 + private
64 61
65 def admin_project 62 def admin_project
66 @admin_project = Project.find_by_code(params[:id]) 63 @admin_project = Project.find_by_code(params[:id])
app/controllers/admin/resque_controller.rb
1 -class Admin::ResqueController < ApplicationController  
2 - layout 'admin' 1 +class Admin::ResqueController < AdminController
3 def show 2 def show
4 end 3 end
5 -end  
6 \ No newline at end of file 4 \ No newline at end of file
  5 +end
app/controllers/admin/team_members_controller.rb
1 -class Admin::TeamMembersController < ApplicationController  
2 - layout "admin"  
3 - before_filter :authenticate_user!  
4 - before_filter :authenticate_admin!  
5 - 1 +class Admin::TeamMembersController < AdminController
6 def edit 2 def edit
7 @admin_team_member = UsersProject.find(params[:id]) 3 @admin_team_member = UsersProject.find(params[:id])
8 end 4 end
app/controllers/admin/users_controller.rb
1 -class Admin::UsersController < ApplicationController  
2 - layout "admin"  
3 - before_filter :authenticate_user!  
4 - before_filter :authenticate_admin!  
5 - 1 +class Admin::UsersController < AdminController
6 def index 2 def index
7 @admin_users = User.scoped 3 @admin_users = User.scoped
8 @admin_users = @admin_users.filter(params[:filter]) 4 @admin_users = @admin_users.filter(params[:filter])
@@ -24,7 +20,7 @@ class Admin::UsersController &lt; ApplicationController @@ -24,7 +20,7 @@ class Admin::UsersController &lt; ApplicationController
24 @admin_user = User.find(params[:id]) 20 @admin_user = User.find(params[:id])
25 21
26 UsersProject.user_bulk_import( 22 UsersProject.user_bulk_import(
27 - @admin_user, 23 + @admin_user,
28 params[:project_ids], 24 params[:project_ids],
29 params[:project_access] 25 params[:project_access]
30 ) 26 )
@@ -41,22 +37,22 @@ class Admin::UsersController &lt; ApplicationController @@ -41,22 +37,22 @@ class Admin::UsersController &lt; ApplicationController
41 @admin_user = User.find(params[:id]) 37 @admin_user = User.find(params[:id])
42 end 38 end
43 39
44 - def block 40 + def block
45 @admin_user = User.find(params[:id]) 41 @admin_user = User.find(params[:id])
46 42
47 if @admin_user.block 43 if @admin_user.block
48 redirect_to :back, alert: "Successfully blocked" 44 redirect_to :back, alert: "Successfully blocked"
49 - else 45 + else
50 redirect_to :back, alert: "Error occured. User was not blocked" 46 redirect_to :back, alert: "Error occured. User was not blocked"
51 end 47 end
52 end 48 end
53 49
54 - def unblock 50 + def unblock
55 @admin_user = User.find(params[:id]) 51 @admin_user = User.find(params[:id])
56 52
57 if @admin_user.update_attribute(:blocked, false) 53 if @admin_user.update_attribute(:blocked, false)
58 redirect_to :back, alert: "Successfully unblocked" 54 redirect_to :back, alert: "Successfully unblocked"
59 - else 55 + else
60 redirect_to :back, alert: "Error occured. User was not unblocked" 56 redirect_to :back, alert: "Error occured. User was not unblocked"
61 end 57 end
62 end 58 end
app/controllers/admin_controller.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +# Provides a base class for Admin controllers to subclass
  2 +#
  3 +# Automatically sets the layout and ensures an administrator is logged in
  4 +class AdminController < ApplicationController
  5 + layout 'admin'
  6 + before_filter :authenticate_admin!
  7 +
  8 + def authenticate_admin!
  9 + return render_404 unless current_user.is_admin?
  10 + end
  11 +end
app/controllers/application_controller.rb
@@ -84,10 +84,6 @@ class ApplicationController &lt; ActionController::Base @@ -84,10 +84,6 @@ class ApplicationController &lt; ActionController::Base
84 abilities << Ability 84 abilities << Ability
85 end 85 end
86 86
87 - def authenticate_admin!  
88 - return render_404 unless current_user.is_admin?  
89 - end  
90 -  
91 def authorize_project!(action) 87 def authorize_project!(action)
92 return access_denied! unless can?(current_user, action, project) 88 return access_denied! unless can?(current_user, action, project)
93 end 89 end
app/controllers/commits_controller.rb
@@ -64,7 +64,7 @@ class CommitsController &lt; ApplicationController @@ -64,7 +64,7 @@ class CommitsController &lt; ApplicationController
64 @commit.to_patch, 64 @commit.to_patch,
65 type: "text/plain", 65 type: "text/plain",
66 disposition: 'attachment', 66 disposition: 'attachment',
67 - filename: "#{@commit.id.patch}" 67 + filename: "#{@commit.id}.patch"
68 ) 68 )
69 end 69 end
70 70
app/controllers/issues_controller.rb
@@ -17,7 +17,7 @@ class IssuesController &lt; ApplicationController @@ -17,7 +17,7 @@ class IssuesController &lt; ApplicationController
17 before_filter :authorize_write_issue!, only: [:new, :create] 17 before_filter :authorize_write_issue!, only: [:new, :create]
18 18
19 # Allow modify issue 19 # Allow modify issue
20 - before_filter :authorize_modify_issue!, only: [:close, :edit, :update] 20 + before_filter :authorize_modify_issue!, only: [:edit, :update]
21 21
22 # Allow destroy issue 22 # Allow destroy issue
23 before_filter :authorize_admin_issue!, only: [:destroy] 23 before_filter :authorize_admin_issue!, only: [:destroy]
@@ -87,8 +87,6 @@ class IssuesController &lt; ApplicationController @@ -87,8 +87,6 @@ class IssuesController &lt; ApplicationController
87 end 87 end
88 88
89 def destroy 89 def destroy
90 - return access_denied! unless can?(current_user, :admin_issue, @issue)  
91 -  
92 @issue.destroy 90 @issue.destroy
93 91
94 respond_to do |format| 92 respond_to do |format|
app/controllers/omniauth_callbacks_controller.rb
1 class OmniauthCallbacksController < Devise::OmniauthCallbacksController 1 class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  2 + Gitlab.config.omniauth_providers.each do |provider|
  3 + define_method provider['name'] do
  4 + handle_omniauth
  5 + end
  6 + end
2 7
3 # Extend the standard message generation to accept our custom exception 8 # Extend the standard message generation to accept our custom exception
4 def failure_message 9 def failure_message
@@ -9,7 +14,7 @@ class OmniauthCallbacksController &lt; Devise::OmniauthCallbacksController @@ -9,7 +14,7 @@ class OmniauthCallbacksController &lt; Devise::OmniauthCallbacksController
9 error ||= env["omniauth.error.type"].to_s 14 error ||= env["omniauth.error.type"].to_s
10 error.to_s.humanize if error 15 error.to_s.humanize if error
11 end 16 end
12 - 17 +
13 def ldap 18 def ldap
14 # We only find ourselves here if the authentication to LDAP was successful. 19 # We only find ourselves here if the authentication to LDAP was successful.
15 @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user) 20 @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user)
@@ -19,4 +24,27 @@ class OmniauthCallbacksController &lt; Devise::OmniauthCallbacksController @@ -19,4 +24,27 @@ class OmniauthCallbacksController &lt; Devise::OmniauthCallbacksController
19 sign_in_and_redirect @user 24 sign_in_and_redirect @user
20 end 25 end
21 26
  27 + private
  28 +
  29 + def handle_omniauth
  30 + oauth = request.env['omniauth.auth']
  31 + provider, uid = oauth['provider'], oauth['uid']
  32 +
  33 + if current_user
  34 + # Change a logged-in user's authentication method:
  35 + current_user.extern_uid = uid
  36 + current_user.provider = provider
  37 + current_user.save
  38 + redirect_to profile_path
  39 + else
  40 + @user = User.find_or_new_for_omniauth(oauth)
  41 +
  42 + if @user
  43 + sign_in_and_redirect @user
  44 + else
  45 + flash[:notice] = "There's no such user!"
  46 + redirect_to new_user_session_path
  47 + end
  48 + end
  49 + end
22 end 50 end
app/controllers/profile_controller.rb
@@ -16,9 +16,6 @@ class ProfileController &lt; ApplicationController @@ -16,9 +16,6 @@ class ProfileController &lt; ApplicationController
16 def token 16 def token
17 end 17 end
18 18
19 - def password  
20 - end  
21 -  
22 def password_update 19 def password_update
23 params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"} 20 params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"}
24 21
@@ -32,10 +29,14 @@ class ProfileController &lt; ApplicationController @@ -32,10 +29,14 @@ class ProfileController &lt; ApplicationController
32 29
33 def reset_private_token 30 def reset_private_token
34 current_user.reset_authentication_token! 31 current_user.reset_authentication_token!
35 - redirect_to profile_token_path 32 + redirect_to profile_account_path
  33 + end
  34 +
  35 + def history
  36 + @events = current_user.recent_events.page(params[:page]).per(20)
36 end 37 end
37 38
38 - private 39 + private
39 40
40 def user 41 def user
41 @user = current_user 42 @user = current_user
app/controllers/team_members_controller.rb
@@ -5,7 +5,10 @@ class TeamMembersController &lt; ApplicationController @@ -5,7 +5,10 @@ class TeamMembersController &lt; ApplicationController
5 # Authorize 5 # Authorize
6 before_filter :add_project_abilities 6 before_filter :add_project_abilities
7 before_filter :authorize_read_project! 7 before_filter :authorize_read_project!
8 - before_filter :authorize_admin_project!, except: [:show] 8 + before_filter :authorize_admin_project!, except: [:index, :show]
  9 +
  10 + def index
  11 + end
9 12
10 def show 13 def show
11 @team_member = project.users_projects.find(params[:id]) 14 @team_member = project.users_projects.find(params[:id])
@@ -22,7 +25,7 @@ class TeamMembersController &lt; ApplicationController @@ -22,7 +25,7 @@ class TeamMembersController &lt; ApplicationController
22 params[:project_access] 25 params[:project_access]
23 ) 26 )
24 27
25 - redirect_to team_project_path(@project) 28 + redirect_to project_team_index_path(@project)
26 end 29 end
27 30
28 def update 31 def update
@@ -32,7 +35,7 @@ class TeamMembersController &lt; ApplicationController @@ -32,7 +35,7 @@ class TeamMembersController &lt; ApplicationController
32 unless @team_member.valid? 35 unless @team_member.valid?
33 flash[:alert] = "User should have at least one role" 36 flash[:alert] = "User should have at least one role"
34 end 37 end
35 - redirect_to team_project_path(@project) 38 + redirect_to project_team_index_path(@project)
36 end 39 end
37 40
38 def destroy 41 def destroy
@@ -40,7 +43,7 @@ class TeamMembersController &lt; ApplicationController @@ -40,7 +43,7 @@ class TeamMembersController &lt; ApplicationController
40 @team_member.destroy 43 @team_member.destroy
41 44
42 respond_to do |format| 45 respond_to do |format|
43 - format.html { redirect_to team_project_path(@project) } 46 + format.html { redirect_to project_team_index_path(@project) }
44 format.js { render nothing: true } 47 format.js { render nothing: true }
45 end 48 end
46 end 49 end
app/decorators/commit_decorator.rb
@@ -16,7 +16,7 @@ class CommitDecorator &lt; ApplicationDecorator @@ -16,7 +16,7 @@ class CommitDecorator &lt; ApplicationDecorator
16 # In case this first line is longer than 80 characters, it is cut off 16 # In case this first line is longer than 80 characters, it is cut off
17 # after 70 characters and ellipses (`&hellp;`) are appended. 17 # after 70 characters and ellipses (`&hellp;`) are appended.
18 def title 18 def title
19 - return no_commit_message unless safe_message 19 + return no_commit_message if safe_message.blank?
20 20
21 title_end = safe_message.index(/\n/) 21 title_end = safe_message.index(/\n/)
22 if (!title_end && safe_message.length > 80) || (title_end && title_end > 80) 22 if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
app/helpers/application_helper.rb
@@ -62,7 +62,7 @@ module ApplicationHelper @@ -62,7 +62,7 @@ module ApplicationHelper
62 { label: "#{@project.name} / Wall", url: wall_project_path(@project) }, 62 { label: "#{@project.name} / Wall", url: wall_project_path(@project) },
63 { label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) }, 63 { label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) },
64 { label: "#{@project.name} / Commits", url: project_commits_path(@project) }, 64 { label: "#{@project.name} / Commits", url: project_commits_path(@project) },
65 - { label: "#{@project.name} / Team", url: team_project_path(@project) } 65 + { label: "#{@project.name} / Team", url: project_team_index_path(@project) }
66 ] 66 ]
67 end 67 end
68 68
@@ -104,7 +104,8 @@ module ApplicationHelper @@ -104,7 +104,8 @@ module ApplicationHelper
104 104
105 # Profile Area 105 # Profile Area
106 when :profile; current_page?(controller: "profile", action: :show) 106 when :profile; current_page?(controller: "profile", action: :show)
107 - when :password; current_page?(controller: "profile", action: :password) 107 + when :history; current_page?(controller: "profile", action: :history)
  108 + when :account; current_page?(controller: "profile", action: :account)
108 when :token; current_page?(controller: "profile", action: :token) 109 when :token; current_page?(controller: "profile", action: :token)
109 when :design; current_page?(controller: "profile", action: :design) 110 when :design; current_page?(controller: "profile", action: :design)
110 when :ssh_keys; controller.controller_name == "keys" 111 when :ssh_keys; controller.controller_name == "keys"
@@ -135,4 +136,10 @@ module ApplicationHelper @@ -135,4 +136,10 @@ module ApplicationHelper
135 "Never" 136 "Never"
136 end 137 end
137 end 138 end
  139 +
  140 + def authbutton(provider, size = 64)
  141 + file_name = "#{provider.to_s.split('_').first}_#{size}.png"
  142 + image_tag("authbuttons/#{file_name}",
  143 + alt: "Sign in with #{provider.to_s.titleize}")
  144 + end
138 end 145 end
app/helpers/gitlab_markdown_helper.rb
@@ -11,7 +11,9 @@ module GitlabMarkdownHelper @@ -11,7 +11,9 @@ module GitlabMarkdownHelper
11 # explicitly produce the correct linking behavior (i.e. 11 # explicitly produce the correct linking behavior (i.e.
12 # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>"). 12 # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
13 def link_to_gfm(body, url, html_options = {}) 13 def link_to_gfm(body, url, html_options = {})
14 - gfm_body = gfm(body, html_options) 14 + return "" if body.blank?
  15 +
  16 + gfm_body = gfm(escape_once(body), html_options)
15 17
16 gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match| 18 gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match|
17 "</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1 19 "</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1
app/helpers/notes_helper.rb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +module NotesHelper
  2 + def loading_more_notes?
  3 + params[:loading_more].present?
  4 + end
  5 +
  6 + def loading_new_notes?
  7 + params[:loading_new].present?
  8 + end
  9 +
  10 + def note_vote_class(note)
  11 + if note.upvote?
  12 + "vote upvote"
  13 + elsif note.downvote?
  14 + "vote downvote"
  15 + end
  16 + end
  17 +end
app/helpers/profile_helper.rb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +module ProfileHelper
  2 + def oauth_active_class provider
  3 + if current_user.provider == provider.to_s
  4 + 'active'
  5 + end
  6 + end
  7 +end
app/helpers/projects_helper.rb
@@ -2,5 +2,9 @@ module ProjectsHelper @@ -2,5 +2,9 @@ module ProjectsHelper
2 def grouper_project_members(project) 2 def grouper_project_members(project)
3 @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) 3 @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
4 end 4 end
  5 +
  6 + def remove_from_team_message(project, member)
  7 + "You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
  8 + end
5 end 9 end
6 10
app/helpers/tab_helper.rb
@@ -8,7 +8,7 @@ module TabHelper @@ -8,7 +8,7 @@ module TabHelper
8 end 8 end
9 9
10 def project_tab_class 10 def project_tab_class
11 - [:show, :files, :team, :edit, :update].each do |action| 11 + [:show, :files, :edit, :update].each do |action|
12 return "current" if current_page?(controller: "projects", action: action, id: @project) 12 return "current" if current_page?(controller: "projects", action: action, id: @project)
13 end 13 end
14 14
app/helpers/tree_helper.rb
@@ -18,7 +18,8 @@ module TreeHelper @@ -18,7 +18,8 @@ module TreeHelper
18 end 18 end
19 19
20 def tree_full_path(content) 20 def tree_full_path(content)
21 - if params[:path] 21 + content.name.force_encoding('utf-8')
  22 + if params[:path]
22 File.join(params[:path], content.name) 23 File.join(params[:path], content.name)
23 else 24 else
24 content.name 25 content.name
app/models/event.rb
@@ -35,13 +35,21 @@ class Event &lt; ActiveRecord::Base @@ -35,13 +35,21 @@ class Event &lt; ActiveRecord::Base
35 end 35 end
36 36
37 # Next events currently enabled for system 37 # Next events currently enabled for system
38 - # - push 38 + # - push
39 # - new issue 39 # - new issue
40 # - merge request 40 # - merge request
41 def allowed? 41 def allowed?
42 push? || issue? || merge_request? || membership_changed? 42 push? || issue? || merge_request? || membership_changed?
43 end 43 end
44 44
  45 + def project_name
  46 + if project
  47 + project.name
  48 + else
  49 + "(deleted)"
  50 + end
  51 + end
  52 +
45 def push? 53 def push?
46 action == self.class::Pushed && valid_push? 54 action == self.class::Pushed && valid_push?
47 end 55 end
@@ -58,31 +66,31 @@ class Event &lt; ActiveRecord::Base @@ -58,31 +66,31 @@ class Event &lt; ActiveRecord::Base
58 action == self.class::Reopened 66 action == self.class::Reopened
59 end 67 end
60 68
61 - def issue? 69 + def issue?
62 target_type == "Issue" 70 target_type == "Issue"
63 end 71 end
64 72
65 - def merge_request? 73 + def merge_request?
66 target_type == "MergeRequest" 74 target_type == "MergeRequest"
67 end 75 end
68 76
69 - def new_issue?  
70 - target_type == "Issue" && 77 + def new_issue?
  78 + target_type == "Issue" &&
71 action == Created 79 action == Created
72 end 80 end
73 81
74 - def new_merge_request?  
75 - target_type == "MergeRequest" && 82 + def new_merge_request?
  83 + target_type == "MergeRequest" &&
76 action == Created 84 action == Created
77 end 85 end
78 86
79 - def changed_merge_request?  
80 - target_type == "MergeRequest" && 87 + def changed_merge_request?
  88 + target_type == "MergeRequest" &&
81 [Closed, Reopened].include?(action) 89 [Closed, Reopened].include?(action)
82 end 90 end
83 91
84 - def changed_issue?  
85 - target_type == "Issue" && 92 + def changed_issue?
  93 + target_type == "Issue" &&
86 [Closed, Reopened].include?(action) 94 [Closed, Reopened].include?(action)
87 end 95 end
88 96
@@ -98,7 +106,7 @@ class Event &lt; ActiveRecord::Base @@ -98,7 +106,7 @@ class Event &lt; ActiveRecord::Base
98 joined? || left? 106 joined? || left?
99 end 107 end
100 108
101 - def issue 109 + def issue
102 target if target_type == "Issue" 110 target if target_type == "Issue"
103 end 111 end
104 112
@@ -106,7 +114,7 @@ class Event &lt; ActiveRecord::Base @@ -106,7 +114,7 @@ class Event &lt; ActiveRecord::Base
106 target if target_type == "MergeRequest" 114 target if target_type == "MergeRequest"
107 end 115 end
108 116
109 - def author 117 + def author
110 @author ||= User.find(author_id) 118 @author ||= User.find(author_id)
111 end 119 end
112 120
@@ -119,7 +127,7 @@ class Event &lt; ActiveRecord::Base @@ -119,7 +127,7 @@ class Event &lt; ActiveRecord::Base
119 'joined' 127 'joined'
120 elsif left? 128 elsif left?
121 'left' 129 'left'
122 - else 130 + else
123 "opened" 131 "opened"
124 end 132 end
125 end 133 end
app/models/issue.rb
1 class Issue < ActiveRecord::Base 1 class Issue < ActiveRecord::Base
2 include IssueCommonality 2 include IssueCommonality
3 - include Upvote 3 + include Votes
4 4
5 acts_as_taggable_on :labels 5 acts_as_taggable_on :labels
6 6
app/models/merge_request.rb
@@ -2,7 +2,7 @@ require File.join(Rails.root, &quot;app/models/commit&quot;) @@ -2,7 +2,7 @@ require File.join(Rails.root, &quot;app/models/commit&quot;)
2 2
3 class MergeRequest < ActiveRecord::Base 3 class MergeRequest < ActiveRecord::Base
4 include IssueCommonality 4 include IssueCommonality
5 - include Upvote 5 + include Votes
6 6
7 BROKEN_DIFF = "--broken-diff" 7 BROKEN_DIFF = "--broken-diff"
8 8
app/models/note.rb
@@ -36,7 +36,7 @@ class Note &lt; ActiveRecord::Base @@ -36,7 +36,7 @@ class Note &lt; ActiveRecord::Base
36 scope :today, where("created_at >= :date", date: Date.today) 36 scope :today, where("created_at >= :date", date: Date.today)
37 scope :last_week, where("created_at >= :date", date: (Date.today - 7.days)) 37 scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
38 scope :since, lambda { |day| where("created_at >= :date", date: (day)) } 38 scope :since, lambda { |day| where("created_at >= :date", date: (day)) }
39 - scope :fresh, order("created_at DESC") 39 + scope :fresh, order("created_at ASC, id ASC")
40 scope :inc_author_project, includes(:project, :author) 40 scope :inc_author_project, includes(:project, :author)
41 scope :inc_author, includes(:author) 41 scope :inc_author, includes(:author)
42 42
@@ -105,6 +105,12 @@ class Note &lt; ActiveRecord::Base @@ -105,6 +105,12 @@ class Note &lt; ActiveRecord::Base
105 def upvote? 105 def upvote?
106 note.start_with?('+1') || note.start_with?(':+1:') 106 note.start_with?('+1') || note.start_with?(':+1:')
107 end 107 end
  108 +
  109 + # Returns true if this is a downvote note,
  110 + # otherwise false is returned
  111 + def downvote?
  112 + note.start_with?('-1') || note.start_with?(':-1:')
  113 + end
108 end 114 end
109 # == Schema Information 115 # == Schema Information
110 # 116 #
app/models/project.rb
@@ -171,6 +171,10 @@ class Project &lt; ActiveRecord::Base @@ -171,6 +171,10 @@ class Project &lt; ActiveRecord::Base
171 end 171 end
172 end 172 end
173 173
  174 + def wiki_notes
  175 + Note.where(noteable_id: wikis.map(&:id), noteable_type: 'Wiki', project_id: self.id)
  176 + end
  177 +
174 def project_id 178 def project_id
175 self.id 179 self.id
176 end 180 end
app/models/tree.rb
@@ -16,7 +16,7 @@ class Tree @@ -16,7 +16,7 @@ class Tree
16 def initialize(raw_tree, project, ref = nil, path = nil) 16 def initialize(raw_tree, project, ref = nil, path = nil)
17 @project, @ref, @path = project, ref, path, 17 @project, @ref, @path = project, ref, path,
18 @tree = if path 18 @tree = if path
19 - raw_tree / path 19 + raw_tree / path.dup.force_encoding('ascii-8bit')
20 else 20 else
21 raw_tree 21 raw_tree
22 end 22 end
app/models/user.rb
@@ -86,33 +86,20 @@ class User &lt; ActiveRecord::Base @@ -86,33 +86,20 @@ class User &lt; ActiveRecord::Base
86 where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') 86 where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
87 end 87 end
88 88
89 - def self.find_for_ldap_auth(auth, signed_in_resource=nil)  
90 - uid = auth.info.uid  
91 - provider = auth.provider  
92 - name = auth.info.name.force_encoding("utf-8")  
93 - email = auth.info.email.downcase unless auth.info.email.nil?  
94 - raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil?  
95 -  
96 - if @user = User.find_by_extern_uid_and_provider(uid, provider)  
97 - @user  
98 - # workaround for backward compatibility  
99 - elsif @user = User.find_by_email(email)  
100 - logger.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}"  
101 - @user.update_attributes(:extern_uid => uid, :provider => provider)  
102 - @user  
103 - else  
104 - logger.info "Creating user from LDAP login {uid => #{uid}, name => #{name}, email => #{email}}"  
105 - password = Devise.friendly_token[0, 8].downcase  
106 - @user = User.create(  
107 - :extern_uid => uid,  
108 - :provider => provider,  
109 - :name => name,  
110 - :email => email,  
111 - :password => password,  
112 - :password_confirmation => password,  
113 - :projects_limit => Gitlab.config.default_projects_limit  
114 - )  
115 - end 89 + def self.create_from_omniauth(auth, ldap = false)
  90 + gitlab_auth.create_from_omniauth(auth, ldap)
  91 + end
  92 +
  93 + def self.find_or_new_for_omniauth(auth)
  94 + gitlab_auth.find_or_new_for_omniauth(auth)
  95 + end
  96 +
  97 + def self.find_for_ldap_auth(auth, signed_in_resource = nil)
  98 + gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
  99 + end
  100 +
  101 + def self.gitlab_auth
  102 + Gitlab::Auth.new
116 end 103 end
117 104
118 def self.search query 105 def self.search query
@@ -148,4 +135,3 @@ end @@ -148,4 +135,3 @@ end
148 # bio :string(255) 135 # bio :string(255)
149 # blocked :boolean(1) default(FALSE), not null 136 # blocked :boolean(1) default(FALSE), not null
150 # 137 #
151 -  
app/models/wiki.rb
@@ -28,7 +28,6 @@ class Wiki &lt; ActiveRecord::Base @@ -28,7 +28,6 @@ class Wiki &lt; ActiveRecord::Base
28 end 28 end
29 new_wiki 29 new_wiki
30 end 30 end
31 -  
32 end 31 end
33 end 32 end
34 # == Schema Information 33 # == Schema Information
app/observers/project_observer.rb
@@ -4,6 +4,18 @@ class ProjectObserver &lt; ActiveRecord::Observer @@ -4,6 +4,18 @@ class ProjectObserver &lt; ActiveRecord::Observer
4 end 4 end
5 5
6 def after_destroy(project) 6 def after_destroy(project)
  7 + log_info("Project \"#{project.name}\" was removed")
  8 +
7 project.destroy_repository 9 project.destroy_repository
8 end 10 end
  11 +
  12 + def after_create project
  13 + log_info("#{project.owner.name} created a new project \"#{project.name}\"")
  14 + end
  15 +
  16 + protected
  17 +
  18 + def log_info message
  19 + Gitlab::AppLogger.info message
  20 + end
9 end 21 end
app/observers/user_observer.rb
1 class UserObserver < ActiveRecord::Observer 1 class UserObserver < ActiveRecord::Observer
2 def after_create(user) 2 def after_create(user)
  3 + log_info("User \"#{user.name}\" (#{user.email}) was created")
  4 +
3 Notify.new_user_email(user.id, user.password).deliver 5 Notify.new_user_email(user.id, user.password).deliver
4 end 6 end
  7 +
  8 + def after_destroy user
  9 + log_info("User \"#{user.name}\" (#{user.email}) was removed")
  10 + end
  11 +
  12 + protected
  13 +
  14 + def log_info message
  15 + Gitlab::AppLogger.info message
  16 + end
5 end 17 end
app/observers/users_project_observer.rb
@@ -14,8 +14,8 @@ class UsersProjectObserver &lt; ActiveRecord::Observer @@ -14,8 +14,8 @@ class UsersProjectObserver &lt; ActiveRecord::Observer
14 14
15 def after_destroy(users_project) 15 def after_destroy(users_project)
16 Event.create( 16 Event.create(
17 - project_id: users_project.project.id,  
18 - action: Event::Left, 17 + project_id: users_project.project.id,
  18 + action: Event::Left,
19 author_id: users_project.user.id 19 author_id: users_project.user.id
20 ) 20 )
21 end 21 end
app/roles/upvote.rb
@@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
1 -module Upvote  
2 - # Return the number of +1 comments (upvotes)  
3 - def upvotes  
4 - notes.select(&:upvote?).size  
5 - end  
6 -end  
app/roles/votes.rb 0 → 100644
@@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
  1 +module Votes
  2 + # Return the number of +1 comments (upvotes)
  3 + def upvotes
  4 + notes.select(&:upvote?).size
  5 + end
  6 +
  7 + def upvotes_in_percent
  8 + if votes_count.zero?
  9 + 0
  10 + else
  11 + 100.0 / votes_count * upvotes
  12 + end
  13 + end
  14 +
  15 + # Return the number of -1 comments (downvotes)
  16 + def downvotes
  17 + notes.select(&:downvote?).size
  18 + end
  19 +
  20 + def downvotes_in_percent
  21 + if votes_count.zero?
  22 + 0
  23 + else
  24 + 100.0 - upvotes_in_percent
  25 + end
  26 + end
  27 +
  28 + # Return the total number of votes
  29 + def votes_count
  30 + upvotes + downvotes
  31 + end
  32 +end
app/views/admin/logs/show.html.haml
1 -.file_holder#README  
2 - .file_title  
3 - %i.icon-file  
4 - githost.log  
5 - .file_content.logs  
6 - %ol  
7 - - Gitlab::Logger.read_latest.each do |line|  
8 - %li  
9 - %p= line 1 +%ul.nav.nav-tabs.log-tabs
  2 + %li.active
  3 + = link_to "githost.log", "#githost", 'data-toggle' => 'tab'
  4 + %li
  5 + = link_to "application.log", "#application", 'data-toggle' => 'tab'
  6 +.tab-content
  7 + .tab-pane.active#githost
  8 + .file_holder#README
  9 + .file_title
  10 + %i.icon-file
  11 + githost.log
  12 + .file_content.logs
  13 + %ol
  14 + - Gitlab::GitLogger.read_latest.each do |line|
  15 + %li
  16 + %p= line
  17 + .tab-pane#application
  18 + .file_holder#README
  19 + .file_title
  20 + %i.icon-file
  21 + application.log
  22 + .file_content.logs
  23 + %ol
  24 + - Gitlab::AppLogger.read_latest.each do |line|
  25 + %li
  26 + %p= line
app/views/admin/projects/_form.html.haml
@@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
32 - unless project.new_record? 32 - unless project.new_record?
33 .clearfix 33 .clearfix
34 = f.label :owner_id 34 = f.label :owner_id
35 - .input= f.select :owner_id, User.all.map { |user| [user.name, user.id] } 35 + .input= f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
36 36
37 - if project.repo_exists? 37 - if project.repo_exists?
38 .clearfix 38 .clearfix
@@ -69,7 +69,6 @@ @@ -69,7 +69,6 @@
69 69
70 :javascript 70 :javascript
71 $(function(){ 71 $(function(){
72 - $('#project_owner_id').chosen();  
73 new Projects(); 72 new Projects();
74 }) 73 })
75 74
app/views/admin/projects/show.html.haml
@@ -71,25 +71,11 @@ @@ -71,25 +71,11 @@
71 %th Project Access: 71 %th Project Access:
72 72
73 %tr 73 %tr
74 - %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true  
75 - %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select" 74 + %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
  75 + %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
76 76
77 %tr 77 %tr
78 %td= submit_tag 'Add', class: "btn primary" 78 %td= submit_tag 'Add', class: "btn primary"
79 %td 79 %td
80 Read more about project permissions 80 Read more about project permissions
81 %strong= link_to "here", help_permissions_path, class: "vlink" 81 %strong= link_to "here", help_permissions_path, class: "vlink"
82 -  
83 -:css  
84 - form select {  
85 - width:150px;  
86 - }  
87 -  
88 - #user_ids {  
89 - width:300px;  
90 - }  
91 -  
92 -:javascript  
93 - $('select#user_ids').chosen();  
94 - $('select#repo_access').chosen();  
95 - $('select#project_access').chosen();  
app/views/admin/resque/show.html.haml
1 -%h3 Resque  
2 -%iframe{src: resque_url, width: 1168, height: 600, style: "border: none"} 1 +%h3.page_title Resque
  2 +%br
  3 +.ui-box
  4 + %iframe{src: resque_url, width: '100%', height: 600, style: "border: none"}
app/views/admin/team_members/_form.html.haml
@@ -8,20 +8,9 @@ @@ -8,20 +8,9 @@
8 .clearfix 8 .clearfix
9 %label Project Access: 9 %label Project Access:
10 .input 10 .input
11 - = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select" 11 + = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3"
12 12
13 %br 13 %br
14 .actions 14 .actions
15 = f.submit 'Save', class: "btn primary" 15 = f.submit 'Save', class: "btn primary"
16 = link_to 'Cancel', :back, class: "btn" 16 = link_to 'Cancel', :back, class: "btn"
17 -  
18 -:css  
19 - form select {  
20 - width:300px;  
21 - }  
22 -  
23 -:javascript  
24 - $('select#team_member_user_id').chosen();  
25 - $('select#team_member_project_id').chosen();  
26 - $('select#team_member_repo_access').chosen();  
27 - $('select#team_member_project_access').chosen();  
app/views/admin/users/show.html.haml
@@ -68,8 +68,8 @@ @@ -68,8 +68,8 @@
68 %th Project Access: 68 %th Project Access:
69 69
70 %tr 70 %tr
71 - %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true  
72 - %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select" 71 + %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
  72 + %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3"
73 73
74 %tr 74 %tr
75 %td= submit_tag 'Add', class: "btn primary" 75 %td= submit_tag 'Add', class: "btn primary"
@@ -97,17 +97,3 @@ @@ -97,17 +97,3 @@
97 %td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled 97 %td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled
98 %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" 98 %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
99 %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" 99 %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
100 -  
101 -:css  
102 - form select {  
103 - width:150px;  
104 - }  
105 -  
106 - #project_ids {  
107 - width:300px;  
108 - }  
109 -  
110 -:javascript  
111 - $('select#project_ids').chosen();  
112 - $('select#repo_access').chosen();  
113 - $('select#project_access').chosen();  
app/views/commits/_commit_box.html.haml
@@ -11,10 +11,10 @@ @@ -11,10 +11,10 @@
11 = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do 11 = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do
12 %strong Browse Code » 12 %strong Browse Code »
13 %h3.commit-title.page_title 13 %h3.commit-title.page_title
14 - = gfm @commit.title 14 + = gfm escape_once(@commit.title)
15 - if @commit.description.present? 15 - if @commit.description.present?
16 %pre.commit-description 16 %pre.commit-description
17 - = gfm @commit.description 17 + = gfm escape_once(@commit.description)
18 .commit-info 18 .commit-info
19 .row 19 .row
20 .span4 20 .span4
app/views/commits/_head.html.haml
1 %ul.nav.nav-tabs 1 %ul.nav.nav-tabs
2 - %li  
3 - = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do  
4 - = select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select"  
5 - = hidden_field_tag :destination, "commits"  
6 - 2 + %li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
7 %li{class: "#{'active' if current_page?(project_commits_path(@project)) }"} 3 %li{class: "#{'active' if current_page?(project_commits_path(@project)) }"}
8 = link_to project_commits_path(@project) do 4 = link_to project_commits_path(@project) do
9 Commits 5 Commits
@@ -20,14 +16,8 @@ @@ -20,14 +16,8 @@
20 Tags 16 Tags
21 %span.badge= @project.repo.tag_count 17 %span.badge= @project.repo.tag_count
22 18
23 -  
24 - if current_page?(project_commits_path(@project)) && current_user.private_token 19 - if current_page?(project_commits_path(@project)) && current_user.private_token
25 %li.right 20 %li.right
26 %span.rss-icon 21 %span.rss-icon
27 = link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do 22 = link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do
28 = image_tag "rss_ui.png", title: "feed" 23 = image_tag "rss_ui.png", title: "feed"
29 -  
30 -:javascript  
31 - $(function(){  
32 - $('.project-refs-select').chosen();  
33 - });  
app/views/commits/_text_file.html.haml
@@ -13,14 +13,11 @@ @@ -13,14 +13,11 @@
13 %td.old_line 13 %td.old_line
14 = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code 14 = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
15 - if @comments_allowed 15 - if @comments_allowed
16 - = link_to "", "#", class: "line_note_link", "line_code" => line_code, title: "Add note for this line" 16 + = render "notes/per_line_note_link", line_code: line_code
17 %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code 17 %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
18 %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;" 18 %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;"
19 19
20 - if @comments_allowed 20 - if @comments_allowed
21 - - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at).reverse 21 + - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
22 - unless comments.empty? 22 - unless comments.empty?
23 - - comments.each_with_index do |note, i|  
24 - = render "notes/reply_button", line_code: line_code if i.zero?  
25 - = render "notes/per_line_show", note: note  
26 - - @line_notes.reject!{ |n| n == note } 23 + = render "notes/per_line_notes_with_reply", notes: comments
app/views/commits/show.html.haml
1 = render "commits/commit_box" 1 = render "commits/commit_box"
2 = render "commits/diffs", diffs: @commit.diffs 2 = render "commits/diffs", diffs: @commit.diffs
3 -= render "notes/notes", tid: @commit.id, tt: "commit" 3 += render "notes/notes_with_form", tid: @commit.id, tt: "commit"
4 = render "notes/per_line_form" 4 = render "notes/per_line_form"
5 5
6 6
app/views/dashboard/index.html.haml
@@ -31,13 +31,19 @@ @@ -31,13 +31,19 @@
31 %span= project_last_activity(project) 31 %span= project_last_activity(project)
32 .bottom= paginate @projects, theme: "gitlab" 32 .bottom= paginate @projects, theme: "gitlab"
33 33
34 - %hr  
35 %div 34 %div
36 %span.rss-icon 35 %span.rss-icon
37 = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do 36 = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
38 = image_tag "rss_ui.png", title: "feed" 37 = image_tag "rss_ui.png", title: "feed"
39 %strong News Feed 38 %strong News Feed
40 39
  40 + %hr
  41 + .gitlab-promo
  42 + = link_to "Homepage", "http://gitlabhq.com"
  43 + = link_to "Blog", "http://blog.gitlabhq.com"
  44 + = link_to "@gitlabhq", "https://twitter.com/gitlabhq"
  45 +
  46 +
41 - else 47 - else
42 %h3.nothing_here_message There are no projects you have access to. 48 %h3.nothing_here_message There are no projects you have access to.
43 %br 49 %br
app/views/devise/sessions/_new_ldap.html.haml
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 $(function() { 15 $(function() {
16 $('#new_user').toggle(); 16 $('#new_user').toggle();
17 }); 17 });
18 - = form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| 18 += form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f|
19 = f.text_field :email, :class => "text top", :placeholder => "Email" 19 = f.text_field :email, :class => "text top", :placeholder => "Email"
20 = f.password_field :password, :class => "text bottom", :placeholder => "Password" 20 = f.password_field :password, :class => "text bottom", :placeholder => "Password"
21 - if devise_mapping.rememberable? 21 - if devise_mapping.rememberable?
app/views/devise/sessions/new.html.haml
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 .right 15 .right
16 = render :partial => "devise/shared/links" 16 = render :partial => "devise/shared/links"
17 - if devise_mapping.omniauthable? 17 - if devise_mapping.omniauthable?
  18 + %hr/
18 - resource_class.omniauth_providers.each do |provider| 19 - resource_class.omniauth_providers.each do |provider|
19 - %hr/  
20 - = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary"  
21 - %br/ 20 + %span
  21 + = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
app/views/events/_commit.html.haml
@@ -5,4 +5,4 @@ @@ -5,4 +5,4 @@
5 %strong.cdark= commit.author_name 5 %strong.cdark= commit.author_name
6 &ndash; 6 &ndash;
7 = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 7 = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
8 - = gfm truncate(commit.title, length: 50) rescue "--broken encoding" 8 + = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding"
app/views/events/_event_last_push.html.haml
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 .event_lp 2 .event_lp
3 %div 3 %div
4 = image_tag gravatar_icon(event.author_email), class: "avatar" 4 = image_tag gravatar_icon(event.author_email), class: "avatar"
5 - %span Your pushed to 5 + %span Your pushed to
6 = event.ref_type 6 = event.ref_type
7 = link_to project_commits_path(event.project, ref: event.ref_name) do 7 = link_to project_commits_path(event.project, ref: event.ref_name) do
8 %strong= truncate(event.ref_name, length: 28) 8 %strong= truncate(event.ref_name, length: 28)
app/views/events/_event_membership_changed.html.haml
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 %strong #{event.author_name} 2 %strong #{event.author_name}
3 %span.event_label{class: event.action_name}= event.action_name 3 %span.event_label{class: event.action_name}= event.action_name
4 project 4 project
5 -%strong= link_to event.project.name, event.project 5 +%strong= link_to event.project_name, event.project
6 %span.cgray 6 %span.cgray
7 = time_ago_in_words(event.created_at) 7 = time_ago_in_words(event.created_at)
8 ago. 8 ago.
app/views/issues/_form.html.haml
@@ -18,12 +18,12 @@ @@ -18,12 +18,12 @@
18 = f.label :assignee_id do 18 = f.label :assignee_id do
19 %i.icon-user 19 %i.icon-user
20 Assign to 20 Assign to
21 - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }) 21 + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'})
22 .issue_milestone 22 .issue_milestone
23 = f.label :milestone_id do 23 = f.label :milestone_id do
24 %i.icon-time 24 %i.icon-time
25 Milestone 25 Milestone
26 - .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }) 26 + .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
27 27
28 .issue_description 28 .issue_description
29 .clearfix 29 .clearfix
app/views/issues/_show.html.haml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) 4 = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
5 .right 5 .right
6 - issue.labels.each do |label| 6 - issue.labels.each do |label|
7 - %span.label.label-issue.grouped 7 + %span.label.label-tag.grouped
8 %i.icon-tag 8 %i.icon-tag
9 = label.name 9 = label.name
10 - if issue.notes.any? 10 - if issue.notes.any?
@@ -34,5 +34,5 @@ @@ -34,5 +34,5 @@
34 - else 34 - else
35 &nbsp; 35 &nbsp;
36 36
37 - - if issue.upvotes > 0  
38 - %span.badge.badge-success= "+#{issue.upvotes}" 37 + - if issue.votes_count > 0
  38 + = render 'votes/votes_inline', votable: issue
app/views/issues/edit.html.haml
1 = render "form" 1 = render "form"
2 -  
3 -:javascript  
4 - $(function(){  
5 - $('select#issue_assignee_id').chosen();  
6 - $('select#issue_milestone_id').chosen();  
7 - });  
8 -  
app/views/issues/index.html.haml
@@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
12 = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do 12 = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do
13 = hidden_field_tag :project_id, @project.id, { id: 'project_id' } 13 = hidden_field_tag :project_id, @project.id, { id: 'project_id' }
14 = hidden_field_tag :status, params[:f] 14 = hidden_field_tag :status, params[:f]
15 - = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib' } 15 + = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib search-text-input' }
16 16
17 .clearfix 17 .clearfix
18 18
app/views/issues/new.html.haml
1 = render "form" 1 = render "form"
2 -  
3 -:javascript  
4 - $(function(){  
5 - $('select#issue_assignee_id').chosen();  
6 - $('select#issue_milestone_id').chosen();  
7 - });  
8 -  
app/views/issues/show.html.haml
@@ -8,22 +8,22 @@ @@ -8,22 +8,22 @@
8 %span.right 8 %span.right
9 - if can?(current_user, :admin_project, @project) || @issue.author == current_user 9 - if can?(current_user, :admin_project, @project) || @issue.author == current_user
10 - if @issue.closed 10 - if @issue.closed
11 - = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small" 11 + = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped success"
12 - else 12 - else
13 - = link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small", title: "Close Issue" 13 + = link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn grouped danger", title: "Close Issue"
14 - if can?(current_user, :admin_project, @project) || @issue.author == current_user 14 - if can?(current_user, :admin_project, @project) || @issue.author == current_user
15 - = link_to edit_project_issue_path(@project, @issue), class: "btn small" do 15 + = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
16 %i.icon-edit 16 %i.icon-edit
17 Edit 17 Edit
18 18
19 - %br  
20 - - if @issue.upvotes > 0  
21 - .upvotes#upvotes= "+#{pluralize @issue.upvotes, 'upvote'}" 19 +.right
  20 + .span3#votes= render 'votes/votes_block', votable: @issue
22 21
23 .back_link 22 .back_link
24 = link_to project_issues_path(@project) do 23 = link_to project_issues_path(@project) do
25 &larr; To issues list 24 &larr; To issues list
26 25
  26 +
27 .main_box 27 .main_box
28 .top_box_content 28 .top_box_content
29 %h4 29 %h4
@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 .alert-message.error.status_info Closed 31 .alert-message.error.status_info Closed
32 - else 32 - else
33 .alert-message.success.status_info Open 33 .alert-message.success.status_info Open
34 - = gfm @issue.title 34 + = gfm escape_once(@issue.title)
35 35
36 .middle_box_content 36 .middle_box_content
37 %cite.cgray Created by 37 %cite.cgray Created by
@@ -61,4 +61,4 @@ @@ -61,4 +61,4 @@
61 = markdown @issue.description 61 = markdown @issue.description
62 62
63 63
64 -.issue_notes#notes= render "notes/notes", tid: @issue.id, tt: "issue" 64 +.issue_notes.voting_notes#notes= render "notes/notes_with_form", tid: @issue.id, tt: "issue"
app/views/labels/_label.html.haml
1 %li.wll 1 %li.wll
2 - %strong= label.name 2 + %strong
  3 + %i.icon-tag
  4 + = label.name
3 .right 5 .right
4 - %span= pluralize label.count, 'issue' 6 + = link_to project_issues_path(label_name: label.name) do
  7 + %strong
  8 + = pluralize(label.count, 'issue')
  9 + = "»"
app/views/layouts/profile.html.haml
@@ -9,20 +9,20 @@ @@ -9,20 +9,20 @@
9 %li.home{class: tab_class(:profile)} 9 %li.home{class: tab_class(:profile)}
10 = link_to "Profile", profile_path 10 = link_to "Profile", profile_path
11 11
12 - %li{class: tab_class(:password)}  
13 - = link_to "Password", profile_password_path 12 + %li{class: tab_class(:account)}
  13 + = link_to "Account", profile_account_path
14 14
15 %li{class: tab_class(:ssh_keys)} 15 %li{class: tab_class(:ssh_keys)}
16 = link_to keys_path do 16 = link_to keys_path do
17 SSH Keys 17 SSH Keys
18 %span.count= current_user.keys.count 18 %span.count= current_user.keys.count
19 19
20 - %li{class: tab_class(:token)}  
21 - = link_to "Token", profile_token_path  
22 -  
23 %li{class: tab_class(:design)} 20 %li{class: tab_class(:design)}
24 = link_to "Design", profile_design_path 21 = link_to "Design", profile_design_path
25 22
  23 + %li{class: tab_class(:history)}
  24 + = link_to "History", profile_history_path
  25 +
26 26
27 .content 27 .content
28 = yield 28 = yield
app/views/merge_requests/_form.html.haml
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 .padded 16 .padded
17 = f.label :source_branch, "From", class: "control-label" 17 = f.label :source_branch, "From", class: "control-label"
18 .controls 18 .controls
19 - = f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, style: "width:250px") 19 + = f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span3'})
20 .mr_source_commit 20 .mr_source_commit
21 21
22 .span2 22 .span2
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 .padded 28 .padded
29 = f.label :target_branch, "To", class: "control-label" 29 = f.label :target_branch, "To", class: "control-label"
30 .controls 30 .controls
31 - = f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, style: "width:250px") 31 + = f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span3'})
32 .mr_target_commit 32 .mr_target_commit
33 33
34 %h4.cdark 2. Fill info 34 %h4.cdark 2. Fill info
@@ -43,7 +43,7 @@ @@ -43,7 +43,7 @@
43 = f.label :assignee_id do 43 = f.label :assignee_id do
44 %i.icon-user 44 %i.icon-user
45 Assign to 45 Assign to
46 - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, style: "width:250px") 46 + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'})
47 47
48 .control-group 48 .control-group
49 49
@@ -56,18 +56,12 @@ @@ -56,18 +56,12 @@
56 = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do 56 = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do
57 Cancel 57 Cancel
58 58
59 -  
60 -  
61 :javascript 59 :javascript
62 $(function(){ 60 $(function(){
63 disableButtonIfEmptyField("#merge_request_title", ".save-btn"); 61 disableButtonIfEmptyField("#merge_request_title", ".save-btn");
64 - $('select#merge_request_assignee_id').chosen();  
65 - $('select#merge_request_source_branch').chosen();  
66 - $('select#merge_request_target_branch').chosen();  
67 var source_branch = $("#merge_request_source_branch"); 62 var source_branch = $("#merge_request_source_branch");
68 var target_branch = $("#merge_request_target_branch"); 63 var target_branch = $("#merge_request_target_branch");
69 64
70 -  
71 $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); 65 $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() });
72 $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); 66 $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() });
73 67
@@ -79,4 +73,3 @@ @@ -79,4 +73,3 @@
79 $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() }); 73 $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() });
80 }); 74 });
81 }); 75 });
82 -  
app/views/merge_requests/_merge_request.html.haml
@@ -23,5 +23,6 @@ @@ -23,5 +23,6 @@
23 authored by #{merge_request.author_name} 23 authored by #{merge_request.author_name}
24 = time_ago_in_words(merge_request.created_at) 24 = time_ago_in_words(merge_request.created_at)
25 ago 25 ago
26 - - if merge_request.upvotes > 0  
27 - %span.badge.badge-success= "+#{merge_request.upvotes}" 26 +
  27 + - if merge_request.votes_count > 0
  28 + = render 'votes/votes_inline', votable: merge_request
app/views/merge_requests/_show.html.haml
@@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
15 %i.icon-list-alt 15 %i.icon-list-alt
16 Diff 16 Diff
17 17
18 -.merge_request_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }  
19 - = render("notes/notes", tid: @merge_request.id, tt: "merge_request") 18 +.merge_request_notes.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
  19 + = render("notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")
20 .merge-request-diffs 20 .merge-request-diffs
21 = render "merge_requests/show/diffs" if @diffs 21 = render "merge_requests/show/diffs" if @diffs
22 .status 22 .status
app/views/merge_requests/diffs.html.haml
1 = render "show" 1 = render "show"
2 2
  3 +:javascript
  4 + $(function(){
  5 + PerLineNotes.init();
  6 + });
app/views/merge_requests/diffs.js.haml
1 :plain 1 :plain
2 $(".merge-request-diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}"); 2 $(".merge-request-diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}");
3 3
  4 + $(function(){
  5 + PerLineNotes.init();
  6 + });
4 7
app/views/merge_requests/show.js.haml
1 :plain 1 :plain
2 - $(".merge-request-notes").html("#{escape_javascript(render("notes/notes", tid: @merge_request.id, tt: "merge_request"))}"); 2 + $(".merge-request-notes").html("#{escape_javascript(render notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")}");
app/views/merge_requests/show/_mr_box.html.haml
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 .alert-message.error.status_info Closed 5 .alert-message.error.status_info Closed
6 - else 6 - else
7 .alert-message.success.status_info Open 7 .alert-message.success.status_info Open
8 - = gfm @merge_request.title 8 + = gfm escape_once(@merge_request.title)
9 9
10 .middle_box_content 10 .middle_box_content
11 %div 11 %div
app/views/merge_requests/show/_mr_title.html.haml
@@ -23,10 +23,8 @@ @@ -23,10 +23,8 @@
23 %i.icon-edit 23 %i.icon-edit
24 Edit 24 Edit
25 25
26 - %br  
27 - - if @merge_request.upvotes > 0  
28 - .upvotes#upvotes= "+#{pluralize @merge_request.upvotes, 'upvote'}"  
29 - 26 +.right
  27 + .span3#votes= render 'votes/votes_block', votable: @merge_request
30 28
31 .back_link 29 .back_link
32 = link_to project_merge_requests_path(@project) do 30 = link_to project_merge_requests_path(@project) do
app/views/milestones/edit.html.haml
1 = render "form" 1 = render "form"
2 -  
3 -:javascript  
4 - $(function(){  
5 - $('select#issue_assignee_id').chosen();  
6 - });  
7 -  
app/views/milestones/show.html.haml
@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 .alert-message.error.status_info Closed 21 .alert-message.error.status_info Closed
22 - else 22 - else
23 .alert-message.success.status_info Open 23 .alert-message.success.status_info Open
24 - = gfm @milestone.title 24 + = gfm escape_once(@milestone.title)
25 %small.right= @milestone.expires_at 25 %small.right= @milestone.expires_at
26 26
27 .middle_box_content 27 .middle_box_content
app/views/notes/_common_form.html.haml 0 → 100644
@@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
  1 +.note-form-holder
  2 + = form_for [@project, @note], remote: "true", multipart: true do |f|
  3 + %h3.page_title Leave a comment
  4 + -if @note.errors.any?
  5 + .alert-message.block-message.error
  6 + - @note.errors.full_messages.each do |msg|
  7 + %div= msg
  8 +
  9 + = f.hidden_field :noteable_id
  10 + = f.hidden_field :noteable_type
  11 + = f.text_area :note, size: 255, class: 'note-text'
  12 + #preview-note.preview_note.hide
  13 + .hint
  14 + .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  15 + .clearfix
  16 +
  17 + .row.note_advanced_opts
  18 + .span3
  19 + = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note"
  20 + = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link'
  21 + .span4.notify_opts
  22 + %h6.left Notify via email:
  23 + = label_tag :notify do
  24 + = check_box_tag :notify, 1, @note.noteable_type != "Commit"
  25 + %span Project team
  26 +
  27 + - if @note.notify_only_author?(current_user)
  28 + = label_tag :notify_author do
  29 + = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
  30 + %span Commit author
  31 + .span5.attachments
  32 + %h6.left Attachment:
  33 + %span.file_name File name...
  34 +
  35 + .input.input_file
  36 + %a.file_upload.btn.small Upload File
  37 + = f.file_field :attachment, class: "input-file"
  38 + %span.hint Any file less than 10 MB
  39 +
app/views/notes/_create_common.js.haml
@@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
1 -- if note.valid?  
2 - :plain  
3 - $(".note-form-holder .error").remove();  
4 - $('.note-form-holder textarea').val("");  
5 - $('.note-form-holder #preview-link').text('Preview');  
6 - $('.note-form-holder #preview-note').hide();  
7 - $('.note-form-holder').show();  
8 - NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}");  
9 -- else  
10 - :plain  
11 - $(".note-form-holder").replaceWith("#{escape_javascript(render('form'))}");  
12 -  
app/views/notes/_create_common_note.js.haml 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +- if note.valid?
  2 + :plain
  3 + $(".note-form-holder .error").remove();
  4 + $('.note-form-holder textarea').val("");
  5 + $('.note-form-holder #preview-link').text('Preview');
  6 + $('.note-form-holder #preview-note').hide();
  7 + $('.note-form-holder').show();
  8 + NoteList.appendNewNote(#{note.id}, "#{escape_javascript(render "notes/note", note: note)}");
  9 +
  10 +- else
  11 + :plain
  12 + $(".note-form-holder").replaceWith("#{escape_javascript(render 'form')}");
  13 +
app/views/notes/_create_line.js.haml
@@ -1,8 +0,0 @@ @@ -1,8 +0,0 @@
1 -- if note.valid?  
2 - :plain  
3 - $(".per_line_form").hide();  
4 - $('.line-note-form-holder textarea').val("");  
5 - $("a.line_note_reply_link[line_code='#{note.line_code}']").closest("tr").remove();  
6 - var trEl = $(".#{note.line_code}").parent();  
7 - trEl.after("#{escape_javascript(render partial: "notes/per_line_show", locals: {note: note})}");  
8 - trEl.after("#{escape_javascript(render partial: "notes/reply_button", locals: {line_code: note.line_code})}");  
app/views/notes/_create_per_line_note.js.haml 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +- if note.valid?
  2 + :plain
  3 + // hide and reset the form
  4 + $(".per_line_form").hide();
  5 + $('.line-note-form-holder textarea').val("");
  6 +
  7 + // find the reply button for this line
  8 + // (might not be there if this is the first note)
  9 + var trRpl = $("a.line_note_reply_link[data-line-code='#{note.line_code}']").closest("tr");
  10 + if (trRpl.size() == 0) {
  11 + // find the commented line ...
  12 + var trEl = $(".#{note.line_code}").parent();
  13 + // ... and insert the note and the reply button after it
  14 + trEl.after("#{escape_javascript(render "notes/per_line_reply_button", line_code: note.line_code)}");
  15 + trEl.after("#{escape_javascript(render "notes/per_line_note", note: note)}");
  16 + } else {
  17 + // instert new note before reply button
  18 + trRpl.before("#{escape_javascript(render "notes/per_line_note", note: note)}");
  19 + }
app/views/notes/_form.html.haml
@@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
1 -.note-form-holder  
2 - = form_for [@project, @note], remote: "true", multipart: true do |f|  
3 - %h3.page_title Leave a comment  
4 - -if @note.errors.any?  
5 - .alert-message.block-message.error  
6 - - @note.errors.full_messages.each do |msg|  
7 - %div= msg  
8 -  
9 - = f.hidden_field :noteable_id  
10 - = f.hidden_field :noteable_type  
11 - = f.text_area :note, size: 255, class: 'note-text'  
12 - #preview-note.preview_note.hide  
13 - .hint  
14 - .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.  
15 - .clearfix  
16 -  
17 - .row.note_advanced_opts.hide  
18 - .span3  
19 - = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note"  
20 - = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link'  
21 - .span4.notify_opts  
22 - %h6.left Notify via email:  
23 - = label_tag :notify do  
24 - = check_box_tag :notify, 1, @note.noteable_type != "Commit"  
25 - %span Project team  
26 -  
27 - - if @note.notify_only_author?(current_user)  
28 - = label_tag :notify_author do  
29 - = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"  
30 - %span Commit author  
31 - .span5.attachments  
32 - %h6.left Attachment:  
33 - %span.file_name File name...  
34 -  
35 - .input.input_file  
36 - %a.file_upload.btn.small Upload File  
37 - = f.file_field :attachment, class: "input-file"  
38 - %span.hint Any file less than 10 MB  
39 -  
app/views/notes/_load.js.haml
@@ -1,17 +0,0 @@ @@ -1,17 +0,0 @@
1 -- unless @notes.blank?  
2 - - if params[:last_id]  
3 - :plain  
4 - NoteList.replace("#{escape_javascript(render(partial: 'notes/notes_list'))}");  
5 -  
6 - - elsif params[:first_id]  
7 - :plain  
8 - NoteList.append(#{@notes.last.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");  
9 -  
10 - - else  
11 - :plain  
12 - NoteList.setContent(#{@notes.last.id}, #{@notes.first.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");  
13 -  
14 -- else  
15 - - if params[:first_id]  
16 - :plain  
17 - NoteList.append(#{params[:first_id]}, "");  
app/views/notes/_note.html.haml 0 → 100644
@@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
  1 +%li{id: dom_id(note), class: "note #{note_vote_class(note)}"}
  2 + = image_tag gravatar_icon(note.author.email), class: "avatar s32"
  3 + %div.note-author
  4 + %strong= note.author_name
  5 + = link_to "##{dom_id(note)}", name: dom_id(note) do
  6 + %cite.cgray
  7 + = time_ago_in_words(note.updated_at)
  8 + ago
  9 + - if note.upvote?
  10 + %span.label.label-success
  11 + %i.icon-thumbs-up
  12 + \+1
  13 + - if note.downvote?
  14 + %span.label.label-error
  15 + %i.icon-thumbs-down
  16 + \-1
  17 + - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
  18 + = link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do
  19 + %i.icon-trash
  20 + Remove
  21 +
  22 + %div.note-title
  23 + = preserve do
  24 + = markdown(note.note)
  25 + - if note.attachment.url
  26 + .right
  27 + %div.file
  28 + = link_to note.attachment_identifier, note.attachment.url, target: "_blank"
  29 + .clear
app/views/notes/_notes.html.haml
1 -- if can? current_user, :write_note, @project  
2 - = render "notes/form"  
3 -.clear  
4 -%hr  
5 -%ul#new_notes_list  
6 -%ul#notes-list  
7 -.status 1 +- @notes.each do |note|
  2 + - next unless note.author
  3 + = render "note", note: note
8 4
9 -  
10 -:javascript  
11 - $(function(){  
12 - NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");  
13 - });  
app/views/notes/_notes_list.html.haml
@@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
1 -- @notes.each do |note|  
2 - - next unless note.author  
3 - = render partial: "notes/show", locals: {note: note}  
4 -