Commit e6ce47291b3f08ebe18c2450fc4f21a2a3a2b8a9
Exists in
master
and in
4 other branches
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.
| ... | ... | @@ -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 | + | ... | ... |
Gemfile
| 1 | 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 | 11 | gem "rails", "3.2.8" |
| 4 | 12 | |
| 5 | 13 | # Supported DBs |
| ... | ... | @@ -8,6 +16,10 @@ gem "mysql2" |
| 8 | 16 | |
| 9 | 17 | # Auth |
| 10 | 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 | 24 | # GITLAB patched libs |
| 13 | 25 | gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" |
| ... | ... | @@ -98,21 +110,28 @@ group :development do |
| 98 | 110 | end |
| 99 | 111 | |
| 100 | 112 | group :development, :test do |
| 113 | + gem 'spinach-rails' | |
| 101 | 114 | gem "rspec-rails" |
| 102 | 115 | gem "capybara" |
| 103 | 116 | gem "capybara-webkit" |
| 104 | 117 | gem "headless" |
| 105 | - gem "autotest" | |
| 106 | - gem "autotest-rails" | |
| 107 | 118 | gem "pry" |
| 108 | 119 | gem "awesome_print" |
| 109 | 120 | gem "database_cleaner" |
| 110 | 121 | gem "launchy" |
| 111 | 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 | 132 | end |
| 113 | 133 | |
| 114 | 134 | group :test do |
| 115 | - gem 'cucumber-rails', :require => false | |
| 116 | 135 | gem "simplecov", :require => false |
| 117 | 136 | gem "shoulda-matchers" |
| 118 | 137 | gem 'email_spec' | ... | ... |
Gemfile.lock
| ... | ... | @@ -68,7 +68,6 @@ GIT |
| 68 | 68 | GEM |
| 69 | 69 | remote: http://rubygems.org/ |
| 70 | 70 | specs: |
| 71 | - ZenTest (4.8.1) | |
| 72 | 71 | actionmailer (3.2.8) |
| 73 | 72 | actionpack (= 3.2.8) |
| 74 | 73 | mail (~> 2.4.4) |
| ... | ... | @@ -100,10 +99,6 @@ GEM |
| 100 | 99 | rails (~> 3.0) |
| 101 | 100 | addressable (2.2.8) |
| 102 | 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 | 102 | awesome_print (1.0.2) |
| 108 | 103 | bcrypt-ruby (3.0.1) |
| 109 | 104 | blankslate (2.1.2.4) |
| ... | ... | @@ -137,16 +132,8 @@ GEM |
| 137 | 132 | execjs |
| 138 | 133 | coffee-script-source (1.3.3) |
| 139 | 134 | colored (1.2) |
| 135 | + colorize (0.5.8) | |
| 140 | 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 | 137 | daemons (1.1.8) |
| 151 | 138 | database_cleaner (0.8.0) |
| 152 | 139 | devise (2.1.2) |
| ... | ... | @@ -171,12 +158,13 @@ GEM |
| 171 | 158 | factory_girl_rails (4.0.0) |
| 172 | 159 | factory_girl (~> 4.0.0) |
| 173 | 160 | railties (>= 3.0.0) |
| 161 | + faraday (0.8.4) | |
| 162 | + multipart-post (~> 1.1) | |
| 174 | 163 | ffaker (1.14.0) |
| 175 | 164 | ffi (1.0.11) |
| 176 | 165 | foreman (0.47.0) |
| 177 | 166 | thor (>= 0.13.6) |
| 178 | - gherkin (2.11.0) | |
| 179 | - json (>= 1.4.6) | |
| 167 | + gherkin-ruby (0.2.1) | |
| 180 | 168 | git (1.2.5) |
| 181 | 169 | github-markup (0.7.4) |
| 182 | 170 | gitlab_meta (2.9) |
| ... | ... | @@ -186,6 +174,15 @@ GEM |
| 186 | 174 | multi_xml |
| 187 | 175 | rack |
| 188 | 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 | 186 | haml (3.1.6) |
| 190 | 187 | haml-rails (0.3.4) |
| 191 | 188 | actionpack (~> 3.0) |
| ... | ... | @@ -199,6 +196,7 @@ GEM |
| 199 | 196 | httparty (0.8.3) |
| 200 | 197 | multi_json (~> 1.0) |
| 201 | 198 | multi_xml |
| 199 | + httpauth (0.1) | |
| 202 | 200 | i18n (0.6.1) |
| 203 | 201 | journey (1.0.4) |
| 204 | 202 | jquery-rails (2.0.2) |
| ... | ... | @@ -208,6 +206,8 @@ GEM |
| 208 | 206 | jquery-rails |
| 209 | 207 | railties (>= 3.1.0) |
| 210 | 208 | json (1.7.5) |
| 209 | + jwt (0.1.5) | |
| 210 | + multi_json (>= 1.0) | |
| 211 | 211 | kaminari (0.14.0) |
| 212 | 212 | actionpack (>= 3.0.0) |
| 213 | 213 | activesupport (>= 3.0.0) |
| ... | ... | @@ -219,6 +219,7 @@ GEM |
| 219 | 219 | libv8 (3.3.10.4) |
| 220 | 220 | libwebsocket (0.1.3) |
| 221 | 221 | addressable |
| 222 | + listen (0.5.0) | |
| 222 | 223 | mail (2.4.4) |
| 223 | 224 | i18n (>= 0.4.0) |
| 224 | 225 | mime-types (~> 1.16) |
| ... | ... | @@ -229,12 +230,35 @@ GEM |
| 229 | 230 | sprockets (~> 2.0) |
| 230 | 231 | multi_json (1.3.6) |
| 231 | 232 | multi_xml (0.5.1) |
| 233 | + multipart-post (1.1.5) | |
| 232 | 234 | mysql2 (0.3.11) |
| 233 | 235 | net-ldap (0.2.2) |
| 234 | 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 | 244 | omniauth (1.1.0) |
| 236 | 245 | hashie (~> 1.2) |
| 237 | 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 | 262 | orm_adapter (0.3.0) |
| 239 | 263 | polyglot (0.3.3) |
| 240 | 264 | posix-spawn (0.3.6) |
| ... | ... | @@ -274,6 +298,9 @@ GEM |
| 274 | 298 | raindrops (0.9.0) |
| 275 | 299 | rake (0.9.2.2) |
| 276 | 300 | raphael-rails (1.5.2) |
| 301 | + rb-fsevent (0.9.1) | |
| 302 | + rb-inotify (0.8.8) | |
| 303 | + ffi (>= 0.5.0) | |
| 277 | 304 | rdoc (3.12) |
| 278 | 305 | json (~> 1.4) |
| 279 | 306 | redcarpet (2.1.1) |
| ... | ... | @@ -336,6 +363,13 @@ GEM |
| 336 | 363 | tilt (~> 1.3, >= 1.3.3) |
| 337 | 364 | six (0.2.0) |
| 338 | 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 | 373 | sprockets (2.1.3) |
| 340 | 374 | hike (~> 1.2) |
| 341 | 375 | rack (~> 1.0) |
| ... | ... | @@ -378,8 +412,6 @@ PLATFORMS |
| 378 | 412 | DEPENDENCIES |
| 379 | 413 | acts-as-taggable-on (= 2.3.1) |
| 380 | 414 | annotate! |
| 381 | - autotest | |
| 382 | - autotest-rails | |
| 383 | 415 | awesome_print |
| 384 | 416 | bootstrap-sass (= 2.0.4) |
| 385 | 417 | capybara |
| ... | ... | @@ -389,7 +421,6 @@ DEPENDENCIES |
| 389 | 421 | chosen-rails |
| 390 | 422 | coffee-rails (= 3.2.2) |
| 391 | 423 | colored |
| 392 | - cucumber-rails | |
| 393 | 424 | database_cleaner |
| 394 | 425 | devise (~> 2.1.0) |
| 395 | 426 | draper |
| ... | ... | @@ -404,6 +435,9 @@ DEPENDENCIES |
| 404 | 435 | grack! |
| 405 | 436 | grape (~> 0.2.1) |
| 406 | 437 | grit! |
| 438 | + growl | |
| 439 | + guard-rspec | |
| 440 | + guard-spinach | |
| 407 | 441 | haml-rails |
| 408 | 442 | headless |
| 409 | 443 | httparty |
| ... | ... | @@ -415,12 +449,18 @@ DEPENDENCIES |
| 415 | 449 | linguist (~> 1.0.0)! |
| 416 | 450 | modernizr (= 2.5.3) |
| 417 | 451 | mysql2 |
| 452 | + omniauth | |
| 453 | + omniauth-github | |
| 454 | + omniauth-google-oauth2 | |
| 418 | 455 | omniauth-ldap! |
| 456 | + omniauth-twitter | |
| 419 | 457 | pry |
| 420 | 458 | pygments.rb! |
| 421 | 459 | rack-mini-profiler |
| 422 | 460 | rails (= 3.2.8) |
| 423 | 461 | raphael-rails (= 1.5.2) |
| 462 | + rb-fsevent | |
| 463 | + rb-inotify | |
| 424 | 464 | redcarpet (~> 2.1.1) |
| 425 | 465 | resque (~> 1.20.0) |
| 426 | 466 | resque_mailer |
| ... | ... | @@ -432,6 +472,7 @@ DEPENDENCIES |
| 432 | 472 | shoulda-matchers |
| 433 | 473 | simplecov |
| 434 | 474 | six |
| 475 | + spinach-rails | |
| 435 | 476 | sqlite3 |
| 436 | 477 | stamp |
| 437 | 478 | test_after_commit | ... | ... |
| ... | ... | @@ -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
app/assets/javascripts/application.js
app/assets/javascripts/main.js.coffee
| ... | ... | @@ -24,6 +24,9 @@ $ -> |
| 24 | 24 | # Click a .one_click_select field, select the contents |
| 25 | 25 | $(".one_click_select").live 'click', -> $(this).select() |
| 26 | 26 | |
| 27 | + # Initialize chosen selects | |
| 28 | + $('select.chosen').chosen() | |
| 29 | + | |
| 27 | 30 | # Disable form buttons while a form is submitting |
| 28 | 31 | $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> |
| 29 | 32 | buttons = $('[type="submit"]', this) | ... | ... |
app/assets/javascripts/note.js
| ... | ... | @@ -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 | -} |
| ... | ... | @@ -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 | 10 | $('form #project_default_branch').chosen() |
| 11 | 11 | disableButtonIfEmptyField '#project_name', '.project-submit' |
| 12 | 12 | |
| 13 | -# Git clone panel switcher | |
| 14 | 13 | $ -> |
| 14 | + # Git clone panel switcher | |
| 15 | 15 | scope = $ '.project_clone_holder' |
| 16 | 16 | if scope.length > 0 |
| 17 | 17 | $('a, button', scope).click -> |
| 18 | 18 | $('a, button', scope).removeClass 'active' |
| 19 | 19 | $(@).addClass 'active' |
| 20 | 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 | 145 | .label { |
| 146 | 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 | 161 | &.label-issue { |
| 149 | 162 | background-color: #eee; |
| 150 | 163 | border: 1px solid #ccc; |
| ... | ... | @@ -158,6 +171,18 @@ span.update-author { |
| 158 | 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 | 188 | .event_label { |
| ... | ... | @@ -181,11 +206,12 @@ span.update-author { |
| 181 | 206 | } |
| 182 | 207 | |
| 183 | 208 | &.joined { |
| 184 | - background-color: #1cb9ff; | |
| 209 | + background-color: #1ca9dd; | |
| 185 | 210 | } |
| 186 | 211 | |
| 187 | 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 | 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 | 487 | /* Fix for readme code (stopped it from being yellow) */ |
| ... | ... | @@ -624,7 +685,7 @@ li.note { |
| 624 | 685 | margin-right:40px; |
| 625 | 686 | |
| 626 | 687 | .prev { |
| 627 | - @extend .borders; | |
| 688 | + @extend .thumbnail; | |
| 628 | 689 | height:120px; |
| 629 | 690 | width:175px; |
| 630 | 691 | margin-bottom:10px; |
| ... | ... | @@ -653,3 +714,31 @@ li.note { |
| 653 | 714 | text-align:center; |
| 654 | 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 | 65 | border-color: #CCC; |
| 66 | 66 | @include solid_shade; |
| 67 | 67 | |
| 68 | + &.white { | |
| 69 | + background:#fff; | |
| 70 | + } | |
| 71 | + | |
| 68 | 72 | ul { |
| 69 | 73 | margin:0; |
| 70 | 74 | } |
| ... | ... | @@ -142,4 +146,8 @@ |
| 142 | 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 | 33 | .nav-pills a:hover { background-color:#888; } |
| 34 | 34 | .nav-pills .active a { background-color: $style_color; } |
| 35 | 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 | 60 | /** ALERT MESSAGES **/ |
| 39 | 61 | .alert-message { @extend .alert; } |
| ... | ... | @@ -50,3 +72,13 @@ img.lil_av { padding-left: 4px; padding-right:3px; } |
| 50 | 72 | /** HELPERS **/ |
| 51 | 73 | .nothing_here_message { text-align:center; padding:20px; color:#777; } |
| 52 | 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 | 135 | */ |
| 136 | 136 | @import "common.scss"; |
| 137 | 137 | |
| 138 | - | |
| 139 | 138 | /** |
| 140 | 139 | * Styles related to specific part of app |
| 141 | 140 | */ |
| ... | ... | @@ -162,6 +161,11 @@ $hover: #fdf5d9; |
| 162 | 161 | @import "sections/notes.scss"; |
| 163 | 162 | |
| 164 | 163 | /** |
| 164 | + * This file represent profile styles | |
| 165 | + */ | |
| 166 | +@import "sections/profile.scss"; | |
| 167 | + | |
| 168 | +/** | |
| 165 | 169 | * Devise styles |
| 166 | 170 | */ |
| 167 | 171 | @import "sections/login.scss"; | ... | ... |
app/assets/stylesheets/ref_select.scss
| ... | ... | @@ -12,35 +12,45 @@ |
| 12 | 12 | width:120px; |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | -.project-refs-form .chzn-container { | |
| 15 | +.project-refs-form .chzn-container { | |
| 16 | 16 | position: relative; |
| 17 | 17 | top: 0; |
| 18 | 18 | left: 0; |
| 19 | 19 | margin-right: 10px; |
| 20 | 20 | |
| 21 | - .chzn-drop { | |
| 21 | + .chzn-drop { | |
| 22 | 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 | 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 | 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 | 46 | @include bg-gray-gradient; |
| 37 | 47 | |
| 38 | - div { | |
| 48 | + div { | |
| 39 | 49 | background:transparent; |
| 40 | 50 | border-left:none; |
| 41 | 51 | } |
| 42 | 52 | |
| 43 | - span { | |
| 53 | + span { | |
| 44 | 54 | font-weight: normal; |
| 45 | 55 | } |
| 46 | 56 | } | ... | ... |
app/assets/stylesheets/sections/issues.scss
| 1 | -.issue_form_box { | |
| 1 | +.issue_form_box { | |
| 2 | 2 | @extend .main_box; |
| 3 | - .issue_title { | |
| 3 | + .issue_title { | |
| 4 | 4 | @extend .top_box_content; |
| 5 | - .clearfix { | |
| 6 | - margin-bottom:0px; | |
| 7 | - input { | |
| 5 | + .clearfix { | |
| 6 | + margin-bottom:0px; | |
| 7 | + input { | |
| 8 | 8 | @extend .span8; |
| 9 | 9 | } |
| 10 | 10 | } |
| 11 | 11 | } |
| 12 | - .issue_middle_block { | |
| 12 | + .issue_middle_block { | |
| 13 | 13 | @extend .middle_box_content; |
| 14 | 14 | height:30px; |
| 15 | - .issue_assignee { | |
| 15 | + .issue_assignee { | |
| 16 | 16 | @extend .span6; |
| 17 | 17 | float:left; |
| 18 | 18 | } |
| 19 | - .issue_milestone { | |
| 19 | + .issue_milestone { | |
| 20 | 20 | @extend .span4; |
| 21 | 21 | float:left; |
| 22 | 22 | } |
| 23 | 23 | } |
| 24 | - .issue_description { | |
| 24 | + .issue_description { | |
| 25 | 25 | @extend .bottom_box_content; |
| 26 | 26 | } |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | -.issues_table { | |
| 30 | - .issue { | |
| 29 | +.issues_table { | |
| 30 | + .issue { | |
| 31 | 31 | padding:7px 10px; |
| 32 | 32 | |
| 33 | - .issue_check { | |
| 33 | + .issue_check { | |
| 34 | 34 | float:left; |
| 35 | 35 | padding: 8px 0; |
| 36 | 36 | padding-right: 8px; |
| 37 | 37 | min-width: 15px; |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | - p { | |
| 40 | + p { | |
| 41 | 41 | padding-top:0; |
| 42 | 42 | padding-bottom:2px; |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | - img.avatar { | |
| 45 | + img.avatar { | |
| 46 | 46 | width:32px; |
| 47 | 47 | margin-top:4px; |
| 48 | 48 | } |
| 49 | 49 | } |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | -input.check_all_issues { | |
| 52 | +input.check_all_issues { | |
| 53 | 53 | float:left; |
| 54 | 54 | padding: 0; |
| 55 | 55 | margin:0; |
| ... | ... | @@ -59,8 +59,8 @@ input.check_all_issues { |
| 59 | 59 | height: 22px; |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | -.issues_content { | |
| 63 | - .title { | |
| 62 | +.issues_content { | |
| 63 | + .title { | |
| 64 | 64 | height: 40px; |
| 65 | 65 | } |
| 66 | 66 | } |
| ... | ... | @@ -70,30 +70,30 @@ input.check_all_issues { |
| 70 | 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 | 76 | padding:0; |
| 77 | 77 | margin:0; |
| 78 | 78 | margin-top:7px |
| 79 | 79 | } |
| 80 | - } | |
| 80 | + } | |
| 81 | 81 | |
| 82 | - .issues_bulk_update { | |
| 82 | + .issues_bulk_update { | |
| 83 | 83 | margin: 0; |
| 84 | - form { | |
| 84 | + form { | |
| 85 | 85 | padding:0; |
| 86 | 86 | margin:0; |
| 87 | 87 | margin-top:7px |
| 88 | 88 | } |
| 89 | - .update_selected_issues { | |
| 89 | + .update_selected_issues { | |
| 90 | 90 | position:relative; |
| 91 | 91 | top:-2px; |
| 92 | 92 | margin-left:4px; |
| 93 | 93 | float:left; |
| 94 | 94 | } |
| 95 | - | |
| 96 | - .update_issues_text { | |
| 95 | + | |
| 96 | + .update_issues_text { | |
| 97 | 97 | padding:3px; |
| 98 | 98 | line-height: 18px; |
| 99 | 99 | float:left; |
| ... | ... | @@ -101,10 +101,11 @@ input.check_all_issues { |
| 101 | 101 | } |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | -#update_status { | |
| 104 | +#update_status { | |
| 105 | 105 | width:100px; |
| 106 | 106 | } |
| 107 | 107 | |
| 108 | + | |
| 108 | 109 | /** |
| 109 | 110 | * Milestones list |
| 110 | 111 | * | ... | ... |
app/assets/stylesheets/sections/merge_requests.scss
| 1 | -/** | |
| 1 | +/** | |
| 2 | 2 | * MR form |
| 3 | 3 | * |
| 4 | 4 | */ |
| 5 | 5 | |
| 6 | -.mr_branch_box { | |
| 6 | +.mr_branch_box { | |
| 7 | 7 | @extend .ui-box; |
| 8 | 8 | margin-bottom:20px; |
| 9 | 9 | |
| 10 | - .body { | |
| 10 | + .body { | |
| 11 | 11 | background:#f1f1f1; |
| 12 | 12 | } |
| 13 | 13 | |
| ... | ... | @@ -17,19 +17,19 @@ |
| 17 | 17 | * MR -> show: Automerge widget |
| 18 | 18 | * |
| 19 | 19 | */ |
| 20 | -.automerge_widget { | |
| 21 | - &.can_be_merged { | |
| 20 | +.automerge_widget { | |
| 21 | + &.can_be_merged { | |
| 22 | 22 | background: #DFF0D8; |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | - form { | |
| 25 | + form { | |
| 26 | 26 | margin-bottom:0; |
| 27 | - .clearfix { | |
| 27 | + .clearfix { | |
| 28 | 28 | margin-bottom:0; |
| 29 | 29 | } |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | - .accept_group { | |
| 32 | + .accept_group { | |
| 33 | 33 | float:left; |
| 34 | 34 | border: 1px solid #ADA; |
| 35 | 35 | padding: 2px; |
| ... | ... | @@ -37,29 +37,29 @@ |
| 37 | 37 | border-radius: 5px; |
| 38 | 38 | background: #CEB; |
| 39 | 39 | |
| 40 | - .accept_merge_request { | |
| 40 | + .accept_merge_request { | |
| 41 | 41 | font-size:13px; |
| 42 | 42 | float:left; |
| 43 | 43 | } |
| 44 | - .remove_branch_holder { | |
| 44 | + .remove_branch_holder { | |
| 45 | 45 | margin-left:20px; |
| 46 | 46 | margin-right:10px; |
| 47 | 47 | float:left; |
| 48 | 48 | } |
| 49 | - label { | |
| 49 | + label { | |
| 50 | 50 | color:#444; |
| 51 | 51 | } |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | |
| 55 | - .how_to_merge_link { | |
| 55 | + .how_to_merge_link { | |
| 56 | 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 | 63 | font-weight:bold; |
| 64 | 64 | padding:8px 20px; |
| 65 | 65 | text-align:center; |
| ... | ... | @@ -67,19 +67,19 @@ |
| 67 | 67 | } |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | -li.merge_request { | |
| 70 | +li.merge_request { | |
| 71 | 71 | padding:7px 10px; |
| 72 | - img.avatar { | |
| 72 | + img.avatar { | |
| 73 | 73 | width: 32px; |
| 74 | 74 | margin-top: 4px; |
| 75 | 75 | } |
| 76 | - p { | |
| 76 | + p { | |
| 77 | 77 | padding: 0px; |
| 78 | 78 | padding-bottom: 2px; |
| 79 | 79 | } |
| 80 | 80 | } |
| 81 | 81 | |
| 82 | -.merge_in_progress { | |
| 82 | +.merge_in_progress { | |
| 83 | 83 | @extend .padded; |
| 84 | 84 | @extend .append-bottom-10; |
| 85 | 85 | } |
| ... | ... | @@ -88,22 +88,21 @@ li.merge_request { |
| 88 | 88 | @include round-borders-all(4px); |
| 89 | 89 | padding:2px 4px; |
| 90 | 90 | border:none; |
| 91 | - font-size:13px; | |
| 91 | + font-size:14px; | |
| 92 | 92 | background: #474D57; |
| 93 | 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 | 100 | margin:0; |
| 102 | 101 | padding:0; |
| 103 | 102 | padding: 5px; |
| 104 | 103 | margin-bottom: 5px; |
| 105 | 104 | .avatar { position:relative } |
| 106 | - .row_title { | |
| 105 | + .row_title { | |
| 107 | 106 | color:#444; |
| 108 | 107 | } |
| 109 | 108 | .commit-author-name, |
| ... | ... | @@ -113,12 +112,12 @@ li.merge_request { |
| 113 | 112 | display:none; |
| 114 | 113 | } |
| 115 | 114 | list-style:none; |
| 116 | - &:hover { | |
| 115 | + &:hover { | |
| 117 | 116 | background:none; |
| 118 | 117 | } |
| 119 | 118 | } |
| 120 | 119 | } |
| 121 | 120 | |
| 122 | -.mr_direction_tip { | |
| 121 | +.mr_direction_tip { | |
| 123 | 122 | margin-top:40px |
| 124 | 123 | } | ... | ... |
app/assets/stylesheets/sections/nav.scss
app/assets/stylesheets/sections/notes.scss
| ... | ... | @@ -3,17 +3,13 @@ |
| 3 | 3 | * |
| 4 | 4 | */ |
| 5 | 5 | #notes-list, |
| 6 | -#new_notes_list { | |
| 6 | +#new-notes-list { | |
| 7 | 7 | display:block; |
| 8 | 8 | list-style:none; |
| 9 | 9 | margin:0px; |
| 10 | 10 | padding:0px; |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | -#new_notes_list li:last-child{ | |
| 14 | - border-bottom:1px solid #aaa; | |
| 15 | -} | |
| 16 | - | |
| 17 | 13 | .issue_notes, |
| 18 | 14 | .wiki_notes { |
| 19 | 15 | .note_content { |
| ... | ... | @@ -30,9 +26,6 @@ |
| 30 | 26 | } |
| 31 | 27 | |
| 32 | 28 | #new_note { |
| 33 | - .note-text { | |
| 34 | - height:40px; | |
| 35 | - } | |
| 36 | 29 | .attach_holder { |
| 37 | 30 | display:none; |
| 38 | 31 | } |
| ... | ... | @@ -48,7 +41,6 @@ |
| 48 | 41 | |
| 49 | 42 | .note { |
| 50 | 43 | padding: 8px 0; |
| 51 | - border-bottom: 1px solid #eee; | |
| 52 | 44 | overflow: hidden; |
| 53 | 45 | display: block; |
| 54 | 46 | img {float: left; margin-right: 10px;} |
| ... | ... | @@ -70,6 +62,23 @@ |
| 70 | 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 | 84 | p.notify_controls input{ |
| ... | ... | @@ -213,7 +222,7 @@ td .line_note_link { |
| 213 | 222 | } |
| 214 | 223 | } |
| 215 | 224 | |
| 216 | -.note-text { | |
| 225 | +.note-text { | |
| 217 | 226 | border: 1px solid #aaa; |
| 218 | 227 | box-shadow:none; |
| 219 | 228 | } | ... | ... |
app/contexts/notes/load_context.rb
| ... | ... | @@ -3,30 +3,31 @@ module Notes |
| 3 | 3 | def execute |
| 4 | 4 | target_type = params[:target_type] |
| 5 | 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 | 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 | 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 | 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 | 22 | when "wiki" |
| 22 | - then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20] | |
| 23 | + project.wiki_notes.limit(20) | |
| 23 | 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 | 31 | @notes |
| 31 | 32 | end |
| 32 | 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 | 2 | def index |
| 7 | 3 | @workers = Resque.workers |
| 8 | 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 | 2 | def index |
| 7 | 3 | @hooks = SystemHook.all |
| 8 | 4 | @hook = SystemHook.new |
| ... | ... | @@ -15,7 +11,7 @@ class Admin::HooksController < ApplicationController |
| 15 | 11 | redirect_to admin_hooks_path, notice: 'Hook was successfully created.' |
| 16 | 12 | else |
| 17 | 13 | @hooks = SystemHook.all |
| 18 | - render :index | |
| 14 | + render :index | |
| 19 | 15 | end |
| 20 | 16 | end |
| 21 | 17 | ... | ... |
app/controllers/admin/logs_controller.rb
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 | 2 | before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update] |
| 6 | 3 | |
| 7 | 4 | def index |
| ... | ... | @@ -43,7 +40,7 @@ class Admin::ProjectsController < ApplicationController |
| 43 | 40 | def update |
| 44 | 41 | owner_id = params[:project].delete(:owner_id) |
| 45 | 42 | |
| 46 | - if owner_id | |
| 43 | + if owner_id | |
| 47 | 44 | @admin_project.owner = User.find(owner_id) |
| 48 | 45 | end |
| 49 | 46 | |
| ... | ... | @@ -60,7 +57,7 @@ class Admin::ProjectsController < ApplicationController |
| 60 | 57 | redirect_to admin_projects_url, notice: 'Project was successfully deleted.' |
| 61 | 58 | end |
| 62 | 59 | |
| 63 | - private | |
| 60 | + private | |
| 64 | 61 | |
| 65 | 62 | def admin_project |
| 66 | 63 | @admin_project = Project.find_by_code(params[:id]) | ... | ... |
app/controllers/admin/resque_controller.rb
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 | 2 | def edit |
| 7 | 3 | @admin_team_member = UsersProject.find(params[:id]) |
| 8 | 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 | 2 | def index |
| 7 | 3 | @admin_users = User.scoped |
| 8 | 4 | @admin_users = @admin_users.filter(params[:filter]) |
| ... | ... | @@ -24,7 +20,7 @@ class Admin::UsersController < ApplicationController |
| 24 | 20 | @admin_user = User.find(params[:id]) |
| 25 | 21 | |
| 26 | 22 | UsersProject.user_bulk_import( |
| 27 | - @admin_user, | |
| 23 | + @admin_user, | |
| 28 | 24 | params[:project_ids], |
| 29 | 25 | params[:project_access] |
| 30 | 26 | ) |
| ... | ... | @@ -41,22 +37,22 @@ class Admin::UsersController < ApplicationController |
| 41 | 37 | @admin_user = User.find(params[:id]) |
| 42 | 38 | end |
| 43 | 39 | |
| 44 | - def block | |
| 40 | + def block | |
| 45 | 41 | @admin_user = User.find(params[:id]) |
| 46 | 42 | |
| 47 | 43 | if @admin_user.block |
| 48 | 44 | redirect_to :back, alert: "Successfully blocked" |
| 49 | - else | |
| 45 | + else | |
| 50 | 46 | redirect_to :back, alert: "Error occured. User was not blocked" |
| 51 | 47 | end |
| 52 | 48 | end |
| 53 | 49 | |
| 54 | - def unblock | |
| 50 | + def unblock | |
| 55 | 51 | @admin_user = User.find(params[:id]) |
| 56 | 52 | |
| 57 | 53 | if @admin_user.update_attribute(:blocked, false) |
| 58 | 54 | redirect_to :back, alert: "Successfully unblocked" |
| 59 | - else | |
| 55 | + else | |
| 60 | 56 | redirect_to :back, alert: "Error occured. User was not unblocked" |
| 61 | 57 | end |
| 62 | 58 | end | ... | ... |
| ... | ... | @@ -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 < ActionController::Base |
| 84 | 84 | abilities << Ability |
| 85 | 85 | end |
| 86 | 86 | |
| 87 | - def authenticate_admin! | |
| 88 | - return render_404 unless current_user.is_admin? | |
| 89 | - end | |
| 90 | - | |
| 91 | 87 | def authorize_project!(action) |
| 92 | 88 | return access_denied! unless can?(current_user, action, project) |
| 93 | 89 | end | ... | ... |
app/controllers/commits_controller.rb
app/controllers/issues_controller.rb
| ... | ... | @@ -17,7 +17,7 @@ class IssuesController < ApplicationController |
| 17 | 17 | before_filter :authorize_write_issue!, only: [:new, :create] |
| 18 | 18 | |
| 19 | 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 | 22 | # Allow destroy issue |
| 23 | 23 | before_filter :authorize_admin_issue!, only: [:destroy] |
| ... | ... | @@ -87,8 +87,6 @@ class IssuesController < ApplicationController |
| 87 | 87 | end |
| 88 | 88 | |
| 89 | 89 | def destroy |
| 90 | - return access_denied! unless can?(current_user, :admin_issue, @issue) | |
| 91 | - | |
| 92 | 90 | @issue.destroy |
| 93 | 91 | |
| 94 | 92 | respond_to do |format| | ... | ... |
app/controllers/omniauth_callbacks_controller.rb
| 1 | 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 | 8 | # Extend the standard message generation to accept our custom exception |
| 4 | 9 | def failure_message |
| ... | ... | @@ -9,7 +14,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController |
| 9 | 14 | error ||= env["omniauth.error.type"].to_s |
| 10 | 15 | error.to_s.humanize if error |
| 11 | 16 | end |
| 12 | - | |
| 17 | + | |
| 13 | 18 | def ldap |
| 14 | 19 | # We only find ourselves here if the authentication to LDAP was successful. |
| 15 | 20 | @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user) |
| ... | ... | @@ -19,4 +24,27 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController |
| 19 | 24 | sign_in_and_redirect @user |
| 20 | 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 | 50 | end | ... | ... |
app/controllers/profile_controller.rb
| ... | ... | @@ -16,9 +16,6 @@ class ProfileController < ApplicationController |
| 16 | 16 | def token |
| 17 | 17 | end |
| 18 | 18 | |
| 19 | - def password | |
| 20 | - end | |
| 21 | - | |
| 22 | 19 | def password_update |
| 23 | 20 | params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"} |
| 24 | 21 | |
| ... | ... | @@ -32,10 +29,14 @@ class ProfileController < ApplicationController |
| 32 | 29 | |
| 33 | 30 | def reset_private_token |
| 34 | 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 | 37 | end |
| 37 | 38 | |
| 38 | - private | |
| 39 | + private | |
| 39 | 40 | |
| 40 | 41 | def user |
| 41 | 42 | @user = current_user | ... | ... |
app/controllers/team_members_controller.rb
| ... | ... | @@ -5,7 +5,10 @@ class TeamMembersController < ApplicationController |
| 5 | 5 | # Authorize |
| 6 | 6 | before_filter :add_project_abilities |
| 7 | 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 | 13 | def show |
| 11 | 14 | @team_member = project.users_projects.find(params[:id]) |
| ... | ... | @@ -22,7 +25,7 @@ class TeamMembersController < ApplicationController |
| 22 | 25 | params[:project_access] |
| 23 | 26 | ) |
| 24 | 27 | |
| 25 | - redirect_to team_project_path(@project) | |
| 28 | + redirect_to project_team_index_path(@project) | |
| 26 | 29 | end |
| 27 | 30 | |
| 28 | 31 | def update |
| ... | ... | @@ -32,7 +35,7 @@ class TeamMembersController < ApplicationController |
| 32 | 35 | unless @team_member.valid? |
| 33 | 36 | flash[:alert] = "User should have at least one role" |
| 34 | 37 | end |
| 35 | - redirect_to team_project_path(@project) | |
| 38 | + redirect_to project_team_index_path(@project) | |
| 36 | 39 | end |
| 37 | 40 | |
| 38 | 41 | def destroy |
| ... | ... | @@ -40,7 +43,7 @@ class TeamMembersController < ApplicationController |
| 40 | 43 | @team_member.destroy |
| 41 | 44 | |
| 42 | 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 | 47 | format.js { render nothing: true } |
| 45 | 48 | end |
| 46 | 49 | end | ... | ... |
app/decorators/commit_decorator.rb
| ... | ... | @@ -16,7 +16,7 @@ class CommitDecorator < ApplicationDecorator |
| 16 | 16 | # In case this first line is longer than 80 characters, it is cut off |
| 17 | 17 | # after 70 characters and ellipses (`&hellp;`) are appended. |
| 18 | 18 | def title |
| 19 | - return no_commit_message unless safe_message | |
| 19 | + return no_commit_message if safe_message.blank? | |
| 20 | 20 | |
| 21 | 21 | title_end = safe_message.index(/\n/) |
| 22 | 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 | 62 | { label: "#{@project.name} / Wall", url: wall_project_path(@project) }, |
| 63 | 63 | { label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) }, |
| 64 | 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 | 67 | end |
| 68 | 68 | |
| ... | ... | @@ -104,7 +104,8 @@ module ApplicationHelper |
| 104 | 104 | |
| 105 | 105 | # Profile Area |
| 106 | 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 | 109 | when :token; current_page?(controller: "profile", action: :token) |
| 109 | 110 | when :design; current_page?(controller: "profile", action: :design) |
| 110 | 111 | when :ssh_keys; controller.controller_name == "keys" |
| ... | ... | @@ -135,4 +136,10 @@ module ApplicationHelper |
| 135 | 136 | "Never" |
| 136 | 137 | end |
| 137 | 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 | 145 | end | ... | ... |
app/helpers/gitlab_markdown_helper.rb
| ... | ... | @@ -11,7 +11,9 @@ module GitlabMarkdownHelper |
| 11 | 11 | # explicitly produce the correct linking behavior (i.e. |
| 12 | 12 | # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>"). |
| 13 | 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 | 18 | gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match| |
| 17 | 19 | "</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1 | ... | ... |
| ... | ... | @@ -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/projects_helper.rb
| ... | ... | @@ -2,5 +2,9 @@ module ProjectsHelper |
| 2 | 2 | def grouper_project_members(project) |
| 3 | 3 | @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) |
| 4 | 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 | 9 | end |
| 6 | 10 | ... | ... |
app/helpers/tab_helper.rb
| ... | ... | @@ -8,7 +8,7 @@ module TabHelper |
| 8 | 8 | end |
| 9 | 9 | |
| 10 | 10 | def project_tab_class |
| 11 | - [:show, :files, :team, :edit, :update].each do |action| | |
| 11 | + [:show, :files, :edit, :update].each do |action| | |
| 12 | 12 | return "current" if current_page?(controller: "projects", action: action, id: @project) |
| 13 | 13 | end |
| 14 | 14 | ... | ... |
app/helpers/tree_helper.rb
app/models/event.rb
| ... | ... | @@ -35,13 +35,21 @@ class Event < ActiveRecord::Base |
| 35 | 35 | end |
| 36 | 36 | |
| 37 | 37 | # Next events currently enabled for system |
| 38 | - # - push | |
| 38 | + # - push | |
| 39 | 39 | # - new issue |
| 40 | 40 | # - merge request |
| 41 | 41 | def allowed? |
| 42 | 42 | push? || issue? || merge_request? || membership_changed? |
| 43 | 43 | end |
| 44 | 44 | |
| 45 | + def project_name | |
| 46 | + if project | |
| 47 | + project.name | |
| 48 | + else | |
| 49 | + "(deleted)" | |
| 50 | + end | |
| 51 | + end | |
| 52 | + | |
| 45 | 53 | def push? |
| 46 | 54 | action == self.class::Pushed && valid_push? |
| 47 | 55 | end |
| ... | ... | @@ -58,31 +66,31 @@ class Event < ActiveRecord::Base |
| 58 | 66 | action == self.class::Reopened |
| 59 | 67 | end |
| 60 | 68 | |
| 61 | - def issue? | |
| 69 | + def issue? | |
| 62 | 70 | target_type == "Issue" |
| 63 | 71 | end |
| 64 | 72 | |
| 65 | - def merge_request? | |
| 73 | + def merge_request? | |
| 66 | 74 | target_type == "MergeRequest" |
| 67 | 75 | end |
| 68 | 76 | |
| 69 | - def new_issue? | |
| 70 | - target_type == "Issue" && | |
| 77 | + def new_issue? | |
| 78 | + target_type == "Issue" && | |
| 71 | 79 | action == Created |
| 72 | 80 | end |
| 73 | 81 | |
| 74 | - def new_merge_request? | |
| 75 | - target_type == "MergeRequest" && | |
| 82 | + def new_merge_request? | |
| 83 | + target_type == "MergeRequest" && | |
| 76 | 84 | action == Created |
| 77 | 85 | end |
| 78 | 86 | |
| 79 | - def changed_merge_request? | |
| 80 | - target_type == "MergeRequest" && | |
| 87 | + def changed_merge_request? | |
| 88 | + target_type == "MergeRequest" && | |
| 81 | 89 | [Closed, Reopened].include?(action) |
| 82 | 90 | end |
| 83 | 91 | |
| 84 | - def changed_issue? | |
| 85 | - target_type == "Issue" && | |
| 92 | + def changed_issue? | |
| 93 | + target_type == "Issue" && | |
| 86 | 94 | [Closed, Reopened].include?(action) |
| 87 | 95 | end |
| 88 | 96 | |
| ... | ... | @@ -98,7 +106,7 @@ class Event < ActiveRecord::Base |
| 98 | 106 | joined? || left? |
| 99 | 107 | end |
| 100 | 108 | |
| 101 | - def issue | |
| 109 | + def issue | |
| 102 | 110 | target if target_type == "Issue" |
| 103 | 111 | end |
| 104 | 112 | |
| ... | ... | @@ -106,7 +114,7 @@ class Event < ActiveRecord::Base |
| 106 | 114 | target if target_type == "MergeRequest" |
| 107 | 115 | end |
| 108 | 116 | |
| 109 | - def author | |
| 117 | + def author | |
| 110 | 118 | @author ||= User.find(author_id) |
| 111 | 119 | end |
| 112 | 120 | |
| ... | ... | @@ -119,7 +127,7 @@ class Event < ActiveRecord::Base |
| 119 | 127 | 'joined' |
| 120 | 128 | elsif left? |
| 121 | 129 | 'left' |
| 122 | - else | |
| 130 | + else | |
| 123 | 131 | "opened" |
| 124 | 132 | end |
| 125 | 133 | end | ... | ... |
app/models/issue.rb
app/models/merge_request.rb
app/models/note.rb
| ... | ... | @@ -36,7 +36,7 @@ class Note < ActiveRecord::Base |
| 36 | 36 | scope :today, where("created_at >= :date", date: Date.today) |
| 37 | 37 | scope :last_week, where("created_at >= :date", date: (Date.today - 7.days)) |
| 38 | 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 | 40 | scope :inc_author_project, includes(:project, :author) |
| 41 | 41 | scope :inc_author, includes(:author) |
| 42 | 42 | |
| ... | ... | @@ -105,6 +105,12 @@ class Note < ActiveRecord::Base |
| 105 | 105 | def upvote? |
| 106 | 106 | note.start_with?('+1') || note.start_with?(':+1:') |
| 107 | 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 | 114 | end |
| 109 | 115 | # == Schema Information |
| 110 | 116 | # | ... | ... |
app/models/project.rb
app/models/tree.rb
app/models/user.rb
| ... | ... | @@ -86,33 +86,20 @@ class User < ActiveRecord::Base |
| 86 | 86 | where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') |
| 87 | 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 | 103 | end |
| 117 | 104 | |
| 118 | 105 | def self.search query |
| ... | ... | @@ -148,4 +135,3 @@ end |
| 148 | 135 | # bio :string(255) |
| 149 | 136 | # blocked :boolean(1) default(FALSE), not null |
| 150 | 137 | # |
| 151 | - | ... | ... |
app/models/wiki.rb
app/observers/project_observer.rb
| ... | ... | @@ -4,6 +4,18 @@ class ProjectObserver < ActiveRecord::Observer |
| 4 | 4 | end |
| 5 | 5 | |
| 6 | 6 | def after_destroy(project) |
| 7 | + log_info("Project \"#{project.name}\" was removed") | |
| 8 | + | |
| 7 | 9 | project.destroy_repository |
| 8 | 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 | 21 | end | ... | ... |
app/observers/user_observer.rb
| 1 | 1 | class UserObserver < ActiveRecord::Observer |
| 2 | 2 | def after_create(user) |
| 3 | + log_info("User \"#{user.name}\" (#{user.email}) was created") | |
| 4 | + | |
| 3 | 5 | Notify.new_user_email(user.id, user.password).deliver |
| 4 | 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 | 17 | end | ... | ... |
app/observers/users_project_observer.rb
| ... | ... | @@ -14,8 +14,8 @@ class UsersProjectObserver < ActiveRecord::Observer |
| 14 | 14 | |
| 15 | 15 | def after_destroy(users_project) |
| 16 | 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 | 19 | author_id: users_project.user.id |
| 20 | 20 | ) |
| 21 | 21 | end | ... | ... |
app/roles/upvote.rb
| ... | ... | @@ -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 | 32 | - unless project.new_record? |
| 33 | 33 | .clearfix |
| 34 | 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 | 37 | - if project.repo_exists? |
| 38 | 38 | .clearfix |
| ... | ... | @@ -69,7 +69,6 @@ |
| 69 | 69 | |
| 70 | 70 | :javascript |
| 71 | 71 | $(function(){ |
| 72 | - $('#project_owner_id').chosen(); | |
| 73 | 72 | new Projects(); |
| 74 | 73 | }) |
| 75 | 74 | ... | ... |
app/views/admin/projects/show.html.haml
| ... | ... | @@ -71,25 +71,11 @@ |
| 71 | 71 | %th Project Access: |
| 72 | 72 | |
| 73 | 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 | 77 | %tr |
| 78 | 78 | %td= submit_tag 'Add', class: "btn primary" |
| 79 | 79 | %td |
| 80 | 80 | Read more about project permissions |
| 81 | 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
app/views/admin/team_members/_form.html.haml
| ... | ... | @@ -8,20 +8,9 @@ |
| 8 | 8 | .clearfix |
| 9 | 9 | %label Project Access: |
| 10 | 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 | 13 | %br |
| 14 | 14 | .actions |
| 15 | 15 | = f.submit 'Save', class: "btn primary" |
| 16 | 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 | 68 | %th Project Access: |
| 69 | 69 | |
| 70 | 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 | 74 | %tr |
| 75 | 75 | %td= submit_tag 'Add', class: "btn primary" |
| ... | ... | @@ -97,17 +97,3 @@ |
| 97 | 97 | %td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled |
| 98 | 98 | %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" |
| 99 | 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 | 11 | = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do |
| 12 | 12 | %strong Browse Code » |
| 13 | 13 | %h3.commit-title.page_title |
| 14 | - = gfm @commit.title | |
| 14 | + = gfm escape_once(@commit.title) | |
| 15 | 15 | - if @commit.description.present? |
| 16 | 16 | %pre.commit-description |
| 17 | - = gfm @commit.description | |
| 17 | + = gfm escape_once(@commit.description) | |
| 18 | 18 | .commit-info |
| 19 | 19 | .row |
| 20 | 20 | .span4 | ... | ... |
app/views/commits/_head.html.haml
| 1 | 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 | 3 | %li{class: "#{'active' if current_page?(project_commits_path(@project)) }"} |
| 8 | 4 | = link_to project_commits_path(@project) do |
| 9 | 5 | Commits |
| ... | ... | @@ -20,14 +16,8 @@ |
| 20 | 16 | Tags |
| 21 | 17 | %span.badge= @project.repo.tag_count |
| 22 | 18 | |
| 23 | - | |
| 24 | 19 | - if current_page?(project_commits_path(@project)) && current_user.private_token |
| 25 | 20 | %li.right |
| 26 | 21 | %span.rss-icon |
| 27 | 22 | = link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do |
| 28 | 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 | 13 | %td.old_line |
| 14 | 14 | = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code |
| 15 | 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 | 17 | %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code |
| 18 | 18 | %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} " |
| 19 | 19 | |
| 20 | 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 | 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
app/views/dashboard/index.html.haml
| ... | ... | @@ -31,13 +31,19 @@ |
| 31 | 31 | %span= project_last_activity(project) |
| 32 | 32 | .bottom= paginate @projects, theme: "gitlab" |
| 33 | 33 | |
| 34 | - %hr | |
| 35 | 34 | %div |
| 36 | 35 | %span.rss-icon |
| 37 | 36 | = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do |
| 38 | 37 | = image_tag "rss_ui.png", title: "feed" |
| 39 | 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 | 47 | - else |
| 42 | 48 | %h3.nothing_here_message There are no projects you have access to. |
| 43 | 49 | %br | ... | ... |
app/views/devise/sessions/_new_ldap.html.haml
| ... | ... | @@ -15,7 +15,7 @@ |
| 15 | 15 | $(function() { |
| 16 | 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 | 19 | = f.text_field :email, :class => "text top", :placeholder => "Email" |
| 20 | 20 | = f.password_field :password, :class => "text bottom", :placeholder => "Password" |
| 21 | 21 | - if devise_mapping.rememberable? | ... | ... |
app/views/devise/sessions/new.html.haml
| ... | ... | @@ -15,7 +15,7 @@ |
| 15 | 15 | .right |
| 16 | 16 | = render :partial => "devise/shared/links" |
| 17 | 17 | - if devise_mapping.omniauthable? |
| 18 | + %hr/ | |
| 18 | 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 | 5 | %strong.cdark= commit.author_name |
| 6 | 6 | – |
| 7 | 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 | 2 | .event_lp |
| 3 | 3 | %div |
| 4 | 4 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
| 5 | - %span Your pushed to | |
| 5 | + %span Your pushed to | |
| 6 | 6 | = event.ref_type |
| 7 | 7 | = link_to project_commits_path(event.project, ref: event.ref_name) do |
| 8 | 8 | %strong= truncate(event.ref_name, length: 28) | ... | ... |
app/views/events/_event_membership_changed.html.haml
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | %strong #{event.author_name} |
| 3 | 3 | %span.event_label{class: event.action_name}= event.action_name |
| 4 | 4 | project |
| 5 | -%strong= link_to event.project.name, event.project | |
| 5 | +%strong= link_to event.project_name, event.project | |
| 6 | 6 | %span.cgray |
| 7 | 7 | = time_ago_in_words(event.created_at) |
| 8 | 8 | ago. | ... | ... |
app/views/issues/_form.html.haml
| ... | ... | @@ -18,12 +18,12 @@ |
| 18 | 18 | = f.label :assignee_id do |
| 19 | 19 | %i.icon-user |
| 20 | 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 | 22 | .issue_milestone |
| 23 | 23 | = f.label :milestone_id do |
| 24 | 24 | %i.icon-time |
| 25 | 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 | 28 | .issue_description |
| 29 | 29 | .clearfix | ... | ... |
app/views/issues/_show.html.haml
| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 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 | 5 | .right |
| 6 | 6 | - issue.labels.each do |label| |
| 7 | - %span.label.label-issue.grouped | |
| 7 | + %span.label.label-tag.grouped | |
| 8 | 8 | %i.icon-tag |
| 9 | 9 | = label.name |
| 10 | 10 | - if issue.notes.any? |
| ... | ... | @@ -34,5 +34,5 @@ |
| 34 | 34 | - else |
| 35 | 35 | |
| 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
app/views/issues/index.html.haml
| ... | ... | @@ -12,7 +12,7 @@ |
| 12 | 12 | = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do |
| 13 | 13 | = hidden_field_tag :project_id, @project.id, { id: 'project_id' } |
| 14 | 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 | 17 | .clearfix |
| 18 | 18 | ... | ... |
app/views/issues/new.html.haml
app/views/issues/show.html.haml
| ... | ... | @@ -8,22 +8,22 @@ |
| 8 | 8 | %span.right |
| 9 | 9 | - if can?(current_user, :admin_project, @project) || @issue.author == current_user |
| 10 | 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 | 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 | 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 | 16 | %i.icon-edit |
| 17 | 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 | 22 | .back_link |
| 24 | 23 | = link_to project_issues_path(@project) do |
| 25 | 24 | ← To issues list |
| 26 | 25 | |
| 26 | + | |
| 27 | 27 | .main_box |
| 28 | 28 | .top_box_content |
| 29 | 29 | %h4 |
| ... | ... | @@ -31,7 +31,7 @@ |
| 31 | 31 | .alert-message.error.status_info Closed |
| 32 | 32 | - else |
| 33 | 33 | .alert-message.success.status_info Open |
| 34 | - = gfm @issue.title | |
| 34 | + = gfm escape_once(@issue.title) | |
| 35 | 35 | |
| 36 | 36 | .middle_box_content |
| 37 | 37 | %cite.cgray Created by |
| ... | ... | @@ -61,4 +61,4 @@ |
| 61 | 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
app/views/layouts/profile.html.haml
| ... | ... | @@ -9,20 +9,20 @@ |
| 9 | 9 | %li.home{class: tab_class(:profile)} |
| 10 | 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 | 15 | %li{class: tab_class(:ssh_keys)} |
| 16 | 16 | = link_to keys_path do |
| 17 | 17 | SSH Keys |
| 18 | 18 | %span.count= current_user.keys.count |
| 19 | 19 | |
| 20 | - %li{class: tab_class(:token)} | |
| 21 | - = link_to "Token", profile_token_path | |
| 22 | - | |
| 23 | 20 | %li{class: tab_class(:design)} |
| 24 | 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 | 27 | .content |
| 28 | 28 | = yield | ... | ... |
app/views/merge_requests/_form.html.haml
| ... | ... | @@ -16,7 +16,7 @@ |
| 16 | 16 | .padded |
| 17 | 17 | = f.label :source_branch, "From", class: "control-label" |
| 18 | 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 | 20 | .mr_source_commit |
| 21 | 21 | |
| 22 | 22 | .span2 |
| ... | ... | @@ -28,7 +28,7 @@ |
| 28 | 28 | .padded |
| 29 | 29 | = f.label :target_branch, "To", class: "control-label" |
| 30 | 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 | 32 | .mr_target_commit |
| 33 | 33 | |
| 34 | 34 | %h4.cdark 2. Fill info |
| ... | ... | @@ -43,7 +43,7 @@ |
| 43 | 43 | = f.label :assignee_id do |
| 44 | 44 | %i.icon-user |
| 45 | 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 | 48 | .control-group |
| 49 | 49 | |
| ... | ... | @@ -56,18 +56,12 @@ |
| 56 | 56 | = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do |
| 57 | 57 | Cancel |
| 58 | 58 | |
| 59 | - | |
| 60 | - | |
| 61 | 59 | :javascript |
| 62 | 60 | $(function(){ |
| 63 | 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 | 62 | var source_branch = $("#merge_request_source_branch"); |
| 68 | 63 | var target_branch = $("#merge_request_target_branch"); |
| 69 | 64 | |
| 70 | - | |
| 71 | 65 | $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); |
| 72 | 66 | $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); |
| 73 | 67 | |
| ... | ... | @@ -79,4 +73,3 @@ |
| 79 | 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 | 23 | authored by #{merge_request.author_name} |
| 24 | 24 | = time_ago_in_words(merge_request.created_at) |
| 25 | 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 | 15 | %i.icon-list-alt |
| 16 | 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 | 20 | .merge-request-diffs |
| 21 | 21 | = render "merge_requests/show/diffs" if @diffs |
| 22 | 22 | .status | ... | ... |
app/views/merge_requests/diffs.html.haml
app/views/merge_requests/diffs.js.haml
app/views/merge_requests/show.js.haml
app/views/merge_requests/show/_mr_box.html.haml
app/views/merge_requests/show/_mr_title.html.haml
| ... | ... | @@ -23,10 +23,8 @@ |
| 23 | 23 | %i.icon-edit |
| 24 | 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 | 29 | .back_link |
| 32 | 30 | = link_to project_merge_requests_path(@project) do | ... | ... |
app/views/milestones/edit.html.haml
app/views/milestones/show.html.haml
| ... | ... | @@ -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 | -- 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 | - |
| ... | ... | @@ -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 | -- 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})}"); |
| ... | ... | @@ -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 | -.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 | -- 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]}, ""); |
| ... | ... | @@ -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