Commit 2e34a6d3c40a60ed689de5d7870fe663b1959e88
Exists in
master
and in
4 other branches
Merge branch 'master' into project_hooks_api
Showing
58 changed files
with
468 additions
and
335 deletions
Show diff stats
Gemfile
1 | source "http://rubygems.org" | 1 | source "http://rubygems.org" |
2 | 2 | ||
3 | +def darwin_only(require_as) | ||
4 | + RUBY_PLATFORM.include?('darwin') && require_as | ||
5 | +end | ||
6 | + | ||
7 | +def linux_only(require_as) | ||
8 | + RUBY_PLATFORM.include?('linux') && require_as | ||
9 | +end | ||
10 | + | ||
3 | gem "rails", "3.2.8" | 11 | gem "rails", "3.2.8" |
4 | 12 | ||
5 | # Supported DBs | 13 | # Supported DBs |
@@ -44,7 +52,8 @@ gem "ffaker" | @@ -44,7 +52,8 @@ gem "ffaker" | ||
44 | gem "seed-fu" | 52 | gem "seed-fu" |
45 | 53 | ||
46 | # Markdown to HTML | 54 | # Markdown to HTML |
47 | -gem "redcarpet", "~> 2.1.1" | 55 | +gem "redcarpet", "~> 2.1.1" |
56 | +gem "github-markup", "~> 0.7.4" | ||
48 | 57 | ||
49 | # Servers | 58 | # Servers |
50 | gem "thin" | 59 | gem "thin" |
@@ -101,13 +110,20 @@ group :development, :test do | @@ -101,13 +110,20 @@ group :development, :test do | ||
101 | gem "capybara" | 110 | gem "capybara" |
102 | gem "capybara-webkit" | 111 | gem "capybara-webkit" |
103 | gem "headless" | 112 | gem "headless" |
104 | - gem "autotest" | ||
105 | - gem "autotest-rails" | ||
106 | gem "pry" | 113 | gem "pry" |
107 | gem "awesome_print" | 114 | gem "awesome_print" |
108 | gem "database_cleaner" | 115 | gem "database_cleaner" |
109 | gem "launchy" | 116 | gem "launchy" |
110 | gem 'factory_girl_rails' | 117 | gem 'factory_girl_rails' |
118 | + | ||
119 | + # Guard | ||
120 | + gem 'guard-rspec' | ||
121 | + gem 'guard-cucumber' | ||
122 | + | ||
123 | + # Notification | ||
124 | + gem 'rb-fsevent', :require => darwin_only('rb-fsevent') | ||
125 | + gem 'growl', :require => darwin_only('growl') | ||
126 | + gem 'rb-inotify', :require => linux_only('rb-inotify') | ||
111 | end | 127 | end |
112 | 128 | ||
113 | group :test do | 129 | group :test do |
Gemfile.lock
@@ -68,7 +68,6 @@ GIT | @@ -68,7 +68,6 @@ GIT | ||
68 | GEM | 68 | GEM |
69 | remote: http://rubygems.org/ | 69 | remote: http://rubygems.org/ |
70 | specs: | 70 | specs: |
71 | - ZenTest (4.8.1) | ||
72 | actionmailer (3.2.8) | 71 | actionmailer (3.2.8) |
73 | actionpack (= 3.2.8) | 72 | actionpack (= 3.2.8) |
74 | mail (~> 2.4.4) | 73 | mail (~> 2.4.4) |
@@ -100,10 +99,6 @@ GEM | @@ -100,10 +99,6 @@ GEM | ||
100 | rails (~> 3.0) | 99 | rails (~> 3.0) |
101 | addressable (2.2.8) | 100 | addressable (2.2.8) |
102 | arel (3.0.2) | 101 | arel (3.0.2) |
103 | - autotest (4.4.6) | ||
104 | - ZenTest (>= 4.4.1) | ||
105 | - autotest-rails (4.1.2) | ||
106 | - ZenTest (~> 4.5) | ||
107 | awesome_print (1.0.2) | 102 | awesome_print (1.0.2) |
108 | bcrypt-ruby (3.0.1) | 103 | bcrypt-ruby (3.0.1) |
109 | blankslate (2.1.2.4) | 104 | blankslate (2.1.2.4) |
@@ -178,6 +173,7 @@ GEM | @@ -178,6 +173,7 @@ GEM | ||
178 | gherkin (2.11.0) | 173 | gherkin (2.11.0) |
179 | json (>= 1.4.6) | 174 | json (>= 1.4.6) |
180 | git (1.2.5) | 175 | git (1.2.5) |
176 | + github-markup (0.7.4) | ||
181 | gitlab_meta (2.9) | 177 | gitlab_meta (2.9) |
182 | grape (0.2.1) | 178 | grape (0.2.1) |
183 | hashie (~> 1.2) | 179 | hashie (~> 1.2) |
@@ -185,6 +181,15 @@ GEM | @@ -185,6 +181,15 @@ GEM | ||
185 | multi_xml | 181 | multi_xml |
186 | rack | 182 | rack |
187 | rack-mount | 183 | rack-mount |
184 | + growl (1.0.3) | ||
185 | + guard (1.3.2) | ||
186 | + listen (>= 0.4.2) | ||
187 | + thor (>= 0.14.6) | ||
188 | + guard-cucumber (1.2.0) | ||
189 | + cucumber (>= 1.2.0) | ||
190 | + guard (>= 1.1.0) | ||
191 | + guard-rspec (1.2.1) | ||
192 | + guard (>= 1.1) | ||
188 | haml (3.1.6) | 193 | haml (3.1.6) |
189 | haml-rails (0.3.4) | 194 | haml-rails (0.3.4) |
190 | actionpack (~> 3.0) | 195 | actionpack (~> 3.0) |
@@ -218,6 +223,7 @@ GEM | @@ -218,6 +223,7 @@ GEM | ||
218 | libv8 (3.3.10.4) | 223 | libv8 (3.3.10.4) |
219 | libwebsocket (0.1.3) | 224 | libwebsocket (0.1.3) |
220 | addressable | 225 | addressable |
226 | + listen (0.5.0) | ||
221 | mail (2.4.4) | 227 | mail (2.4.4) |
222 | i18n (>= 0.4.0) | 228 | i18n (>= 0.4.0) |
223 | mime-types (~> 1.16) | 229 | mime-types (~> 1.16) |
@@ -273,6 +279,9 @@ GEM | @@ -273,6 +279,9 @@ GEM | ||
273 | raindrops (0.9.0) | 279 | raindrops (0.9.0) |
274 | rake (0.9.2.2) | 280 | rake (0.9.2.2) |
275 | raphael-rails (1.5.2) | 281 | raphael-rails (1.5.2) |
282 | + rb-fsevent (0.9.1) | ||
283 | + rb-inotify (0.8.8) | ||
284 | + ffi (>= 0.5.0) | ||
276 | rdoc (3.12) | 285 | rdoc (3.12) |
277 | json (~> 1.4) | 286 | json (~> 1.4) |
278 | redcarpet (2.1.1) | 287 | redcarpet (2.1.1) |
@@ -376,8 +385,6 @@ PLATFORMS | @@ -376,8 +385,6 @@ PLATFORMS | ||
376 | DEPENDENCIES | 385 | DEPENDENCIES |
377 | acts-as-taggable-on (= 2.3.1) | 386 | acts-as-taggable-on (= 2.3.1) |
378 | annotate! | 387 | annotate! |
379 | - autotest | ||
380 | - autotest-rails | ||
381 | awesome_print | 388 | awesome_print |
382 | bootstrap-sass (= 2.0.4) | 389 | bootstrap-sass (= 2.0.4) |
383 | capybara | 390 | capybara |
@@ -396,11 +403,15 @@ DEPENDENCIES | @@ -396,11 +403,15 @@ DEPENDENCIES | ||
396 | ffaker | 403 | ffaker |
397 | foreman | 404 | foreman |
398 | git | 405 | git |
406 | + github-markup (~> 0.7.4) | ||
399 | gitlab_meta (= 2.9) | 407 | gitlab_meta (= 2.9) |
400 | gitolite! | 408 | gitolite! |
401 | grack! | 409 | grack! |
402 | grape (~> 0.2.1) | 410 | grape (~> 0.2.1) |
403 | grit! | 411 | grit! |
412 | + growl | ||
413 | + guard-cucumber | ||
414 | + guard-rspec | ||
404 | haml-rails | 415 | haml-rails |
405 | headless | 416 | headless |
406 | httparty | 417 | httparty |
@@ -418,6 +429,8 @@ DEPENDENCIES | @@ -418,6 +429,8 @@ DEPENDENCIES | ||
418 | rack-mini-profiler | 429 | rack-mini-profiler |
419 | rails (= 3.2.8) | 430 | rails (= 3.2.8) |
420 | raphael-rails (= 1.5.2) | 431 | raphael-rails (= 1.5.2) |
432 | + rb-fsevent | ||
433 | + rb-inotify | ||
421 | redcarpet (~> 2.1.1) | 434 | redcarpet (~> 2.1.1) |
422 | resque (~> 1.20.0) | 435 | resque (~> 1.20.0) |
423 | resque_mailer | 436 | resque_mailer |
@@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
1 | +# A sample Guardfile | ||
2 | +# More info at https://github.com/guard/guard#readme | ||
3 | + | ||
4 | +guard 'rspec', :version => 2 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 | + | ||
20 | + # Turnip features and steps | ||
21 | + watch(%r{^spec/acceptance/(.+)\.feature$}) | ||
22 | + watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } | ||
23 | +end | ||
24 | + | ||
25 | + | ||
26 | +guard 'cucumber' do | ||
27 | + watch(%r{^features/.+\.feature$}) | ||
28 | + watch(%r{^features/support/.+$}) { 'features' } | ||
29 | + watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' } | ||
30 | +end |
app/assets/javascripts/issues.js
@@ -5,7 +5,7 @@ function switchToNewIssue(form){ | @@ -5,7 +5,7 @@ function switchToNewIssue(form){ | ||
5 | $('select#issue_milestone_id').chosen(); | 5 | $('select#issue_milestone_id').chosen(); |
6 | $("#new_issue_dialog").show("fade", { direction: "right" }, 150); | 6 | $("#new_issue_dialog").show("fade", { direction: "right" }, 150); |
7 | $('.top-tabs .add_new').hide(); | 7 | $('.top-tabs .add_new').hide(); |
8 | - disableButtonIfEmtpyField("#issue_title", ".save-btn"); | 8 | + disableButtonIfEmptyField("#issue_title", ".save-btn"); |
9 | }); | 9 | }); |
10 | } | 10 | } |
11 | 11 | ||
@@ -16,7 +16,7 @@ function switchToEditIssue(form){ | @@ -16,7 +16,7 @@ function switchToEditIssue(form){ | ||
16 | $('select#issue_milestone_id').chosen(); | 16 | $('select#issue_milestone_id').chosen(); |
17 | $("#edit_issue_dialog").show("fade", { direction: "right" }, 150); | 17 | $("#edit_issue_dialog").show("fade", { direction: "right" }, 150); |
18 | $('.add_new').hide(); | 18 | $('.add_new').hide(); |
19 | - disableButtonIfEmtpyField("#issue_title", ".save-btn"); | 19 | + disableButtonIfEmptyField("#issue_title", ".save-btn"); |
20 | }); | 20 | }); |
21 | } | 21 | } |
22 | 22 |
app/assets/javascripts/main.js
@@ -1,130 +0,0 @@ | @@ -1,130 +0,0 @@ | ||
1 | -$(document).ready(function(){ | ||
2 | - | ||
3 | - $(".one_click_select").live("click", function(){ | ||
4 | - $(this).select(); | ||
5 | - }); | ||
6 | - | ||
7 | - $('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){ | ||
8 | - var buttons = $('[type="submit"]', this); | ||
9 | - switch( e.type ){ | ||
10 | - case 'ajax:beforeSend': | ||
11 | - case 'submit': | ||
12 | - buttons.attr('disabled', 'disabled'); | ||
13 | - break; | ||
14 | - case ' ajax:complete': | ||
15 | - default: | ||
16 | - buttons.removeAttr('disabled'); | ||
17 | - break; | ||
18 | - } | ||
19 | - }) | ||
20 | - | ||
21 | - $(".account-box").mouseenter(showMenu); | ||
22 | - $(".account-box").mouseleave(resetMenu); | ||
23 | - | ||
24 | - $("#projects-list .project").live('click', function(e){ | ||
25 | - if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { | ||
26 | - location.href = $(this).attr("url"); | ||
27 | - e.stopPropagation(); | ||
28 | - return false; | ||
29 | - } | ||
30 | - }); | ||
31 | - | ||
32 | - /** | ||
33 | - * Focus search field by pressing 's' key | ||
34 | - */ | ||
35 | - $(document).keypress(function(e) { | ||
36 | - if( $(e.target).is(":input") ) return; | ||
37 | - switch(e.which) { | ||
38 | - case 115: focusSearch(); | ||
39 | - e.preventDefault(); | ||
40 | - } | ||
41 | - }); | ||
42 | - | ||
43 | - /** | ||
44 | - * Commit show suppressed diff | ||
45 | - * | ||
46 | - */ | ||
47 | - $(".supp_diff_link").bind("click", function() { | ||
48 | - showDiff(this); | ||
49 | - }); | ||
50 | - | ||
51 | - /** | ||
52 | - * Note markdown preview | ||
53 | - * | ||
54 | - */ | ||
55 | - $(document).on('click', '#preview-link', function(e) { | ||
56 | - $('#preview-note').text('Loading...'); | ||
57 | - | ||
58 | - var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview'); | ||
59 | - $(this).text(previewLinkText); | ||
60 | - | ||
61 | - var note = $('#note_note').val(); | ||
62 | - if (note.trim().length === 0) { note = 'Nothing to preview'; } | ||
63 | - $.post($(this).attr('href'), {note: note}, function(data) { | ||
64 | - $('#preview-note').html(data); | ||
65 | - }); | ||
66 | - | ||
67 | - $('#preview-note, #note_note').toggle(); | ||
68 | - e.preventDefault(); | ||
69 | - }); | ||
70 | -}); | ||
71 | - | ||
72 | -function focusSearch() { | ||
73 | - $("#search").focus(); | ||
74 | -} | ||
75 | - | ||
76 | -function updatePage(data){ | ||
77 | - $.ajax({type: "GET", url: location.href, data: data, dataType: "script"}); | ||
78 | -} | ||
79 | - | ||
80 | -function showMenu() { | ||
81 | - $(this).toggleClass('hover'); | ||
82 | -} | ||
83 | - | ||
84 | -function resetMenu() { | ||
85 | - $(this).removeClass("hover"); | ||
86 | -} | ||
87 | - | ||
88 | -function slugify(text) { | ||
89 | - return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase(); | ||
90 | -} | ||
91 | - | ||
92 | -function showDiff(link) { | ||
93 | - $(link).next('table').show(); | ||
94 | - $(link).remove(); | ||
95 | -} | ||
96 | - | ||
97 | -(function($){ | ||
98 | - var _chosen = $.fn.chosen; | ||
99 | - $.fn.extend({ | ||
100 | - chosen: function(options) { | ||
101 | - var default_options = {'search_contains' : 'true'}; | ||
102 | - $.extend(default_options, options); | ||
103 | - return _chosen.apply(this, [default_options]); | ||
104 | - }}) | ||
105 | -})(jQuery); | ||
106 | - | ||
107 | - | ||
108 | -function ajaxGet(url) { | ||
109 | - $.ajax({type: "GET", url: url, dataType: "script"}); | ||
110 | -} | ||
111 | - | ||
112 | -/** | ||
113 | - * Disable button if text field is empty | ||
114 | - */ | ||
115 | -function disableButtonIfEmtpyField(field_selector, button_selector) { | ||
116 | - field = $(field_selector); | ||
117 | - if(field.val() == "") { | ||
118 | - field.closest("form").find(button_selector).attr("disabled", "disabled").addClass("disabled"); | ||
119 | - } | ||
120 | - | ||
121 | - field.on('keyup', function(){ | ||
122 | - var field = $(this); | ||
123 | - var closest_submit = field.closest("form").find(button_selector); | ||
124 | - if(field.val() == "") { | ||
125 | - closest_submit.attr("disabled", "disabled").addClass("disabled"); | ||
126 | - } else { | ||
127 | - closest_submit.removeAttr("disabled").removeClass("disabled"); | ||
128 | - } | ||
129 | - }) | ||
130 | -} |
@@ -0,0 +1,92 @@ | @@ -0,0 +1,92 @@ | ||
1 | +window.updatePage = (data) -> | ||
2 | + $.ajax({type: "GET", url: location.href, data: data, dataType: "script"}) | ||
3 | + | ||
4 | +window.slugify = (text) -> | ||
5 | + text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() | ||
6 | + | ||
7 | +window.ajaxGet = (url) -> | ||
8 | + $.ajax({type: "GET", url: url, dataType: "script"}) | ||
9 | + | ||
10 | + # Disable button if text field is empty | ||
11 | +window.disableButtonIfEmptyField = (field_selector, button_selector) -> | ||
12 | + field = $(field_selector) | ||
13 | + closest_submit = field.closest("form").find(button_selector) | ||
14 | + | ||
15 | + closest_submit.disable() if field.val() is "" | ||
16 | + | ||
17 | + field.on "keyup", -> | ||
18 | + if $(this).val() is "" | ||
19 | + closest_submit.disable() | ||
20 | + else | ||
21 | + closest_submit.enable() | ||
22 | + | ||
23 | +$ -> | ||
24 | + # Click a .one_click_select field, select the contents | ||
25 | + $(".one_click_select").live 'click', -> $(this).select() | ||
26 | + | ||
27 | + # Initialize chosen selects | ||
28 | + $('select.chosen').chosen() | ||
29 | + | ||
30 | + # Disable form buttons while a form is submitting | ||
31 | + $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> | ||
32 | + buttons = $('[type="submit"]', this) | ||
33 | + | ||
34 | + switch e.type | ||
35 | + when 'ajax:beforeSend', 'submit' | ||
36 | + buttons.disable() | ||
37 | + else | ||
38 | + buttons.enable() | ||
39 | + | ||
40 | + # Show/Hide the profile menu when hovering the account box | ||
41 | + $('.account-box').hover -> $(this).toggleClass('hover') | ||
42 | + | ||
43 | + # Focus search field by pressing 's' key | ||
44 | + $(document).keypress (e) -> | ||
45 | + # Don't do anything if typing in an input | ||
46 | + return if $(e.target).is(":input") | ||
47 | + | ||
48 | + switch e.which | ||
49 | + when 115 | ||
50 | + $("#search").focus() | ||
51 | + e.preventDefault() | ||
52 | + | ||
53 | + # Commit show suppressed diff | ||
54 | + $(".supp_diff_link").bind "click", -> | ||
55 | + $(this).next('table').show() | ||
56 | + $(this).remove() | ||
57 | + | ||
58 | + # Note markdown preview | ||
59 | + $(document).on 'click', '#preview-link', (e) -> | ||
60 | + $('#preview-note').text('Loading...') | ||
61 | + | ||
62 | + previewLinkText = if $(this).text() == 'Preview' then 'Edit' else 'Preview' | ||
63 | + $(this).text(previewLinkText) | ||
64 | + | ||
65 | + note = $('#note_note').val() | ||
66 | + | ||
67 | + if note.trim().length == 0 | ||
68 | + $('#preview-note').text("Nothing to preview.") | ||
69 | + else | ||
70 | + $.post $(this).attr('href'), {note: note}, (data) -> | ||
71 | + $('#preview-note').html(data) | ||
72 | + | ||
73 | + $('#preview-note, #note_note').toggle() | ||
74 | + e.preventDefault() | ||
75 | + false | ||
76 | + | ||
77 | +(($) -> | ||
78 | + _chosen = $.fn.chosen | ||
79 | + $.fn.extend chosen: (options) -> | ||
80 | + default_options = search_contains: "true" | ||
81 | + $.extend default_options, options | ||
82 | + _chosen.apply this, [default_options] | ||
83 | + | ||
84 | + # Disable an element and add the 'disabled' Bootstrap class | ||
85 | + $.fn.extend disable: -> | ||
86 | + $(this).attr('disabled', 'disabled').addClass('disabled') | ||
87 | + | ||
88 | + # Enable an element and remove the 'disabled' Bootstrap class | ||
89 | + $.fn.extend enable: -> | ||
90 | + $(this).removeAttr('disabled').removeClass('disabled') | ||
91 | + | ||
92 | +)(jQuery) |
app/assets/javascripts/note.js
@@ -25,14 +25,14 @@ var NoteList = { | @@ -25,14 +25,14 @@ var NoteList = { | ||
25 | $(this).closest('li').fadeOut(); }); | 25 | $(this).closest('li').fadeOut(); }); |
26 | 26 | ||
27 | $(".note-form-holder").live("ajax:before", function(){ | 27 | $(".note-form-holder").live("ajax:before", function(){ |
28 | - $(".submit_note").attr("disabled", "disabled"); | 28 | + $(".submit_note").disable() |
29 | }) | 29 | }) |
30 | 30 | ||
31 | $(".note-form-holder").live("ajax:complete", function(){ | 31 | $(".note-form-holder").live("ajax:complete", function(){ |
32 | - $(".submit_note").removeAttr("disabled"); | 32 | + $(".submit_note").enable() |
33 | }) | 33 | }) |
34 | 34 | ||
35 | - disableButtonIfEmtpyField(".note-text", ".submit_note"); | 35 | + disableButtonIfEmptyField(".note-text", ".submit_note"); |
36 | 36 | ||
37 | $(".note-text").live("focus", function(){ | 37 | $(".note-text").live("focus", function(){ |
38 | $(this).css("height", "80px"); | 38 | $(this).css("height", "80px"); |
@@ -177,6 +177,6 @@ var PerLineNotes = { | @@ -177,6 +177,6 @@ var PerLineNotes = { | ||
177 | form.show(); | 177 | form.show(); |
178 | return false; | 178 | return false; |
179 | }); | 179 | }); |
180 | - disableButtonIfEmtpyField(".line-note-text", ".submit_inline_note"); | 180 | + disableButtonIfEmptyField(".line-note-text", ".submit_inline_note"); |
181 | } | 181 | } |
182 | } | 182 | } |
app/assets/javascripts/projects.js.coffee
@@ -8,7 +8,7 @@ window.Projects = -> | @@ -8,7 +8,7 @@ window.Projects = -> | ||
8 | $('.save-project-loader').show() | 8 | $('.save-project-loader').show() |
9 | 9 | ||
10 | $('form #project_default_branch').chosen() | 10 | $('form #project_default_branch').chosen() |
11 | - disableButtonIfEmtpyField '#project_name', '.project-submit' | 11 | + disableButtonIfEmptyField '#project_name', '.project-submit' |
12 | 12 | ||
13 | # Git clone panel switcher | 13 | # Git clone panel switcher |
14 | $ -> | 14 | $ -> |
app/assets/stylesheets/common.scss
@@ -179,6 +179,15 @@ span.update-author { | @@ -179,6 +179,15 @@ span.update-author { | ||
179 | &.merged { | 179 | &.merged { |
180 | background-color: #2A2; | 180 | background-color: #2A2; |
181 | } | 181 | } |
182 | + | ||
183 | + &.joined { | ||
184 | + background-color: #1ca9dd; | ||
185 | + } | ||
186 | + | ||
187 | + &.left { | ||
188 | + background-color: #888; | ||
189 | + float:none; | ||
190 | + } | ||
182 | } | 191 | } |
183 | 192 | ||
184 | form { | 193 | form { |
app/controllers/refs_controller.rb
app/decorators/event_decorator.rb
@@ -8,7 +8,9 @@ class EventDecorator < ApplicationDecorator | @@ -8,7 +8,9 @@ class EventDecorator < ApplicationDecorator | ||
8 | "#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title | 8 | "#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title |
9 | elsif self.push? | 9 | elsif self.push? |
10 | "#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name | 10 | "#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name |
11 | - else | 11 | + elsif self.membership_changed? |
12 | + "#{self.author_name} #{self.action_name} #{self.project.name}" | ||
13 | + else | ||
12 | "" | 14 | "" |
13 | end | 15 | end |
14 | end | 16 | end |
app/helpers/projects_helper.rb
@@ -2,5 +2,9 @@ module ProjectsHelper | @@ -2,5 +2,9 @@ module ProjectsHelper | ||
2 | def grouper_project_members(project) | 2 | def grouper_project_members(project) |
3 | @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) | 3 | @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) |
4 | end | 4 | end |
5 | + | ||
6 | + def remove_from_team_message(project, member) | ||
7 | + "You are going to remove #{member.user_name} from #{project.name}. Are you sure?" | ||
8 | + end | ||
5 | end | 9 | end |
6 | 10 |
app/helpers/tree_helper.rb
@@ -24,4 +24,14 @@ module TreeHelper | @@ -24,4 +24,14 @@ module TreeHelper | ||
24 | content.name | 24 | content.name |
25 | end | 25 | end |
26 | end | 26 | end |
27 | + | ||
28 | + # Public: Determines if a given filename is compatible with GitHub::Markup. | ||
29 | + # | ||
30 | + # filename - Filename string to check | ||
31 | + # | ||
32 | + # Returns boolean | ||
33 | + def markup?(filename) | ||
34 | + filename.end_with?(*%w(.mdown .md .markdown .textile .rdoc .org .creole | ||
35 | + .mediawiki .rst .asciidoc .pod)) | ||
36 | + end | ||
27 | end | 37 | end |
app/models/event.rb
@@ -10,6 +10,8 @@ class Event < ActiveRecord::Base | @@ -10,6 +10,8 @@ class Event < ActiveRecord::Base | ||
10 | Pushed = 5 | 10 | Pushed = 5 |
11 | Commented = 6 | 11 | Commented = 6 |
12 | Merged = 7 | 12 | Merged = 7 |
13 | + Joined = 8 # User joined project | ||
14 | + Left = 9 # User left project | ||
13 | 15 | ||
14 | belongs_to :project | 16 | belongs_to :project |
15 | belongs_to :target, polymorphic: true | 17 | belongs_to :target, polymorphic: true |
@@ -37,7 +39,7 @@ class Event < ActiveRecord::Base | @@ -37,7 +39,7 @@ class Event < ActiveRecord::Base | ||
37 | # - new issue | 39 | # - new issue |
38 | # - merge request | 40 | # - merge request |
39 | def allowed? | 41 | def allowed? |
40 | - push? || issue? || merge_request? | 42 | + push? || issue? || merge_request? || membership_changed? |
41 | end | 43 | end |
42 | 44 | ||
43 | def push? | 45 | def push? |
@@ -84,6 +86,18 @@ class Event < ActiveRecord::Base | @@ -84,6 +86,18 @@ class Event < ActiveRecord::Base | ||
84 | [Closed, Reopened].include?(action) | 86 | [Closed, Reopened].include?(action) |
85 | end | 87 | end |
86 | 88 | ||
89 | + def joined? | ||
90 | + action == Joined | ||
91 | + end | ||
92 | + | ||
93 | + def left? | ||
94 | + action == Left | ||
95 | + end | ||
96 | + | ||
97 | + def membership_changed? | ||
98 | + joined? || left? | ||
99 | + end | ||
100 | + | ||
87 | def issue | 101 | def issue |
88 | target if target_type == "Issue" | 102 | target if target_type == "Issue" |
89 | end | 103 | end |
@@ -101,6 +115,10 @@ class Event < ActiveRecord::Base | @@ -101,6 +115,10 @@ class Event < ActiveRecord::Base | ||
101 | "closed" | 115 | "closed" |
102 | elsif merged? | 116 | elsif merged? |
103 | "merged" | 117 | "merged" |
118 | + elsif joined? | ||
119 | + 'joined' | ||
120 | + elsif left? | ||
121 | + 'left' | ||
104 | else | 122 | else |
105 | "opened" | 123 | "opened" |
106 | end | 124 | end |
app/models/merge_request.rb
@@ -162,7 +162,7 @@ class MergeRequest < ActiveRecord::Base | @@ -162,7 +162,7 @@ class MergeRequest < ActiveRecord::Base | ||
162 | end | 162 | end |
163 | 163 | ||
164 | def automerge!(current_user) | 164 | def automerge!(current_user) |
165 | - if Gitlab::Merge.new(self, current_user).merge | 165 | + if Gitlab::Merge.new(self, current_user).merge && self.unmerged_commits.empty? |
166 | self.merge!(current_user.id) | 166 | self.merge!(current_user.id) |
167 | true | 167 | true |
168 | end | 168 | end |
app/models/users_project.rb
@@ -23,7 +23,7 @@ class UsersProject < ActiveRecord::Base | @@ -23,7 +23,7 @@ class UsersProject < ActiveRecord::Base | ||
23 | def self.bulk_delete(project, user_ids) | 23 | def self.bulk_delete(project, user_ids) |
24 | UsersProject.transaction do | 24 | UsersProject.transaction do |
25 | UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| | 25 | UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| |
26 | - users_project.delete | 26 | + users_project.destroy |
27 | end | 27 | end |
28 | end | 28 | end |
29 | end | 29 | end |
app/observers/users_project_observer.rb
1 | class UsersProjectObserver < ActiveRecord::Observer | 1 | class UsersProjectObserver < ActiveRecord::Observer |
2 | def after_create(users_project) | 2 | def after_create(users_project) |
3 | Notify.project_access_granted_email(users_project.id).deliver | 3 | Notify.project_access_granted_email(users_project.id).deliver |
4 | + | ||
5 | + Event.create( | ||
6 | + project_id: users_project.project.id, | ||
7 | + action: Event::Joined, | ||
8 | + author_id: users_project.user.id | ||
9 | + ) | ||
4 | end | 10 | end |
5 | 11 | ||
6 | def after_update(users_project) | 12 | def after_update(users_project) |
7 | Notify.project_access_granted_email(users_project.id).deliver | 13 | Notify.project_access_granted_email(users_project.id).deliver |
8 | end | 14 | end |
15 | + | ||
16 | + def after_destroy(users_project) | ||
17 | + Event.create( | ||
18 | + project_id: users_project.project.id, | ||
19 | + action: Event::Left, | ||
20 | + author_id: users_project.user.id | ||
21 | + ) | ||
22 | + end | ||
23 | + | ||
9 | end | 24 | end |
app/roles/push_event.rb
@@ -90,6 +90,8 @@ module PushEvent | @@ -90,6 +90,8 @@ module PushEvent | ||
90 | 90 | ||
91 | def push_with_commits? | 91 | def push_with_commits? |
92 | md_ref? && commits.any? && parent_commit && last_commit | 92 | md_ref? && commits.any? && parent_commit && last_commit |
93 | + rescue Grit::NoSuchPathError | ||
94 | + false | ||
93 | end | 95 | end |
94 | 96 | ||
95 | def last_push_to_non_root? | 97 | def last_push_to_non_root? |
app/views/admin/projects/_form.html.haml
@@ -32,7 +32,7 @@ | @@ -32,7 +32,7 @@ | ||
32 | - unless project.new_record? | 32 | - unless project.new_record? |
33 | .clearfix | 33 | .clearfix |
34 | = f.label :owner_id | 34 | = f.label :owner_id |
35 | - .input= f.select :owner_id, User.all.map { |user| [user.name, user.id] } | 35 | + .input= f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'} |
36 | 36 | ||
37 | - if project.repo_exists? | 37 | - if project.repo_exists? |
38 | .clearfix | 38 | .clearfix |
@@ -69,7 +69,6 @@ | @@ -69,7 +69,6 @@ | ||
69 | 69 | ||
70 | :javascript | 70 | :javascript |
71 | $(function(){ | 71 | $(function(){ |
72 | - $('#project_owner_id').chosen(); | ||
73 | new Projects(); | 72 | new Projects(); |
74 | }) | 73 | }) |
75 | 74 |
app/views/admin/projects/show.html.haml
@@ -71,25 +71,11 @@ | @@ -71,25 +71,11 @@ | ||
71 | %th Project Access: | 71 | %th Project Access: |
72 | 72 | ||
73 | %tr | 73 | %tr |
74 | - %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true | ||
75 | - %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select" | 74 | + %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' |
75 | + %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"} | ||
76 | 76 | ||
77 | %tr | 77 | %tr |
78 | %td= submit_tag 'Add', class: "btn primary" | 78 | %td= submit_tag 'Add', class: "btn primary" |
79 | %td | 79 | %td |
80 | Read more about project permissions | 80 | Read more about project permissions |
81 | %strong= link_to "here", help_permissions_path, class: "vlink" | 81 | %strong= link_to "here", help_permissions_path, class: "vlink" |
82 | - | ||
83 | -:css | ||
84 | - form select { | ||
85 | - width:150px; | ||
86 | - } | ||
87 | - | ||
88 | - #user_ids { | ||
89 | - width:300px; | ||
90 | - } | ||
91 | - | ||
92 | -:javascript | ||
93 | - $('select#user_ids').chosen(); | ||
94 | - $('select#repo_access').chosen(); | ||
95 | - $('select#project_access').chosen(); |
app/views/admin/team_members/_form.html.haml
@@ -8,20 +8,9 @@ | @@ -8,20 +8,9 @@ | ||
8 | .clearfix | 8 | .clearfix |
9 | %label Project Access: | 9 | %label Project Access: |
10 | .input | 10 | .input |
11 | - = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select" | 11 | + = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3" |
12 | 12 | ||
13 | %br | 13 | %br |
14 | .actions | 14 | .actions |
15 | = f.submit 'Save', class: "btn primary" | 15 | = f.submit 'Save', class: "btn primary" |
16 | = link_to 'Cancel', :back, class: "btn" | 16 | = link_to 'Cancel', :back, class: "btn" |
17 | - | ||
18 | -:css | ||
19 | - form select { | ||
20 | - width:300px; | ||
21 | - } | ||
22 | - | ||
23 | -:javascript | ||
24 | - $('select#team_member_user_id').chosen(); | ||
25 | - $('select#team_member_project_id').chosen(); | ||
26 | - $('select#team_member_repo_access').chosen(); | ||
27 | - $('select#team_member_project_access').chosen(); |
app/views/admin/users/show.html.haml
@@ -68,8 +68,8 @@ | @@ -68,8 +68,8 @@ | ||
68 | %th Project Access: | 68 | %th Project Access: |
69 | 69 | ||
70 | %tr | 70 | %tr |
71 | - %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true | ||
72 | - %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select" | 71 | + %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' |
72 | + %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3" | ||
73 | 73 | ||
74 | %tr | 74 | %tr |
75 | %td= submit_tag 'Add', class: "btn primary" | 75 | %td= submit_tag 'Add', class: "btn primary" |
@@ -97,17 +97,3 @@ | @@ -97,17 +97,3 @@ | ||
97 | %td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled | 97 | %td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled |
98 | %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" | 98 | %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" |
99 | %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" | 99 | %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" |
100 | - | ||
101 | -:css | ||
102 | - form select { | ||
103 | - width:150px; | ||
104 | - } | ||
105 | - | ||
106 | - #project_ids { | ||
107 | - width:300px; | ||
108 | - } | ||
109 | - | ||
110 | -:javascript | ||
111 | - $('select#project_ids').chosen(); | ||
112 | - $('select#repo_access').chosen(); | ||
113 | - $('select#project_access').chosen(); |
app/views/commits/_head.html.haml
1 | %ul.nav.nav-tabs | 1 | %ul.nav.nav-tabs |
2 | %li | 2 | %li |
3 | = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do | 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" | 4 | + = select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select chosen" |
5 | = hidden_field_tag :destination, "commits" | 5 | = hidden_field_tag :destination, "commits" |
6 | 6 | ||
7 | %li{class: "#{'active' if current_page?(project_commits_path(@project)) }"} | 7 | %li{class: "#{'active' if current_page?(project_commits_path(@project)) }"} |
@@ -26,8 +26,3 @@ | @@ -26,8 +26,3 @@ | ||
26 | %span.rss-icon | 26 | %span.rss-icon |
27 | = link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do | 27 | = link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do |
28 | = image_tag "rss_ui.png", title: "feed" | 28 | = image_tag "rss_ui.png", title: "feed" |
29 | - | ||
30 | -:javascript | ||
31 | - $(function(){ | ||
32 | - $('.project-refs-select').chosen(); | ||
33 | - }); |
app/views/devise/sessions/_new_ldap.html.haml
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | $(function() { | 15 | $(function() { |
16 | $('#new_user').toggle(); | 16 | $('#new_user').toggle(); |
17 | }); | 17 | }); |
18 | - = form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| | 18 | += form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| |
19 | = f.text_field :email, :class => "text top", :placeholder => "Email" | 19 | = f.text_field :email, :class => "text top", :placeholder => "Email" |
20 | = f.password_field :password, :class => "text bottom", :placeholder => "Password" | 20 | = f.password_field :password, :class => "text bottom", :placeholder => "Password" |
21 | - if devise_mapping.rememberable? | 21 | - if devise_mapping.rememberable? |
app/views/events/_event.html.haml
@@ -0,0 +1,9 @@ | @@ -0,0 +1,9 @@ | ||
1 | += image_tag gravatar_icon(event.author_email), class: "avatar" | ||
2 | +%strong #{event.author_name} | ||
3 | +%span.event_label{class: event.action_name}= event.action_name | ||
4 | +project | ||
5 | +%strong= link_to event.project.name, event.project | ||
6 | +%span.cgray | ||
7 | + = time_ago_in_words(event.created_at) | ||
8 | + ago. | ||
9 | + |
app/views/issues/_form.html.haml
@@ -18,12 +18,12 @@ | @@ -18,12 +18,12 @@ | ||
18 | = f.label :assignee_id do | 18 | = f.label :assignee_id do |
19 | %i.icon-user | 19 | %i.icon-user |
20 | Assign to | 20 | Assign to |
21 | - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }) | 21 | + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'}) |
22 | .issue_milestone | 22 | .issue_milestone |
23 | = f.label :milestone_id do | 23 | = f.label :milestone_id do |
24 | %i.icon-time | 24 | %i.icon-time |
25 | Milestone | 25 | Milestone |
26 | - .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }) | 26 | + .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'}) |
27 | 27 | ||
28 | .issue_description | 28 | .issue_description |
29 | .clearfix | 29 | .clearfix |
app/views/issues/edit.html.haml
app/views/issues/new.html.haml
app/views/layouts/_head_panel.html.haml
@@ -34,12 +34,4 @@ | @@ -34,12 +34,4 @@ | ||
34 | source: #{raw search_autocomplete_source}, | 34 | source: #{raw search_autocomplete_source}, |
35 | select: function(event, ui) { location.href = ui.item.url } | 35 | select: function(event, ui) { location.href = ui.item.url } |
36 | }); | 36 | }); |
37 | - | ||
38 | - $(document).keypress(function(e) { | ||
39 | - if($(e.target).is(":input")) return; | ||
40 | - switch(e.which) { | ||
41 | - case 115: focusSearch(); | ||
42 | - e.preventDefault(); | ||
43 | - } | ||
44 | - }); | ||
45 | }); | 37 | }); |
app/views/merge_requests/_form.html.haml
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | .padded | 16 | .padded |
17 | = f.label :source_branch, "From", class: "control-label" | 17 | = f.label :source_branch, "From", class: "control-label" |
18 | .controls | 18 | .controls |
19 | - = f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, style: "width:250px") | 19 | + = f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span3'}) |
20 | .mr_source_commit | 20 | .mr_source_commit |
21 | 21 | ||
22 | .span2 | 22 | .span2 |
@@ -28,7 +28,7 @@ | @@ -28,7 +28,7 @@ | ||
28 | .padded | 28 | .padded |
29 | = f.label :target_branch, "To", class: "control-label" | 29 | = f.label :target_branch, "To", class: "control-label" |
30 | .controls | 30 | .controls |
31 | - = f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, style: "width:250px") | 31 | + = f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span3'}) |
32 | .mr_target_commit | 32 | .mr_target_commit |
33 | 33 | ||
34 | %h4.cdark 2. Fill info | 34 | %h4.cdark 2. Fill info |
@@ -43,7 +43,7 @@ | @@ -43,7 +43,7 @@ | ||
43 | = f.label :assignee_id do | 43 | = f.label :assignee_id do |
44 | %i.icon-user | 44 | %i.icon-user |
45 | Assign to | 45 | Assign to |
46 | - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, style: "width:250px") | 46 | + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'}) |
47 | 47 | ||
48 | .control-group | 48 | .control-group |
49 | 49 | ||
@@ -56,18 +56,12 @@ | @@ -56,18 +56,12 @@ | ||
56 | = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do | 56 | = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do |
57 | Cancel | 57 | Cancel |
58 | 58 | ||
59 | - | ||
60 | - | ||
61 | :javascript | 59 | :javascript |
62 | $(function(){ | 60 | $(function(){ |
63 | - disableButtonIfEmtpyField("#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(); | 61 | + disableButtonIfEmptyField("#merge_request_title", ".save-btn"); |
67 | var source_branch = $("#merge_request_source_branch"); | 62 | var source_branch = $("#merge_request_source_branch"); |
68 | var target_branch = $("#merge_request_target_branch"); | 63 | var target_branch = $("#merge_request_target_branch"); |
69 | 64 | ||
70 | - | ||
71 | $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); | 65 | $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); |
72 | $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); | 66 | $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); |
73 | 67 | ||
@@ -79,4 +73,3 @@ | @@ -79,4 +73,3 @@ | ||
79 | $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() }); | 73 | $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() }); |
80 | }); | 74 | }); |
81 | }); | 75 | }); |
82 | - |
app/views/milestones/_form.html.haml
@@ -41,7 +41,7 @@ | @@ -41,7 +41,7 @@ | ||
41 | 41 | ||
42 | :javascript | 42 | :javascript |
43 | $(function() { | 43 | $(function() { |
44 | - disableButtonIfEmtpyField("#milestone_title", ".save-btn"); | 44 | + disableButtonIfEmptyField("#milestone_title", ".save-btn"); |
45 | $( ".datepicker" ).datepicker({ | 45 | $( ".datepicker" ).datepicker({ |
46 | dateFormat: "yy-mm-dd", | 46 | dateFormat: "yy-mm-dd", |
47 | onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } | 47 | onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } |
app/views/milestones/edit.html.haml
app/views/projects/_refs.html.haml
1 | = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do | 1 | = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do |
2 | - = select_tag "ref", grouped_options_refs, onchange: "this.form.submit();", class: "project-refs-select" | 2 | + = select_tag "ref", grouped_options_refs, onchange: "this.form.submit();", class: "project-refs-select chosen" |
3 | = hidden_field_tag :destination, destination | 3 | = hidden_field_tag :destination, destination |
4 | - | ||
5 | -:javascript | ||
6 | - $(function(){ | ||
7 | - $('.project-refs-select').chosen(); | ||
8 | - }) |
app/views/protected_branches/index.html.haml
@@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
19 | .entry.clearfix | 19 | .entry.clearfix |
20 | = f.label :name, "Branch" | 20 | = f.label :name, "Branch" |
21 | .span3 | 21 | .span3 |
22 | - = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , { include_blank: "-- Select branch" }, { class: "span3" }) | 22 | + = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "chosen span3"}) |
23 | | 23 | |
24 | = f.submit 'Protect', class: "primary btn" | 24 | = f.submit 'Protect', class: "primary btn" |
25 | 25 | ||
@@ -46,6 +46,3 @@ | @@ -46,6 +46,3 @@ | ||
46 | %td | 46 | %td |
47 | - if can? current_user, :admin_project, @project | 47 | - if can? current_user, :admin_project, @project |
48 | = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "danger btn small" | 48 | = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "danger btn small" |
49 | - | ||
50 | -:javascript | ||
51 | - $('select#protected_branch_name').chosen(); |
app/views/refs/_head.html.haml
1 | %ul.nav.nav-tabs | 1 | %ul.nav.nav-tabs |
2 | %li | 2 | %li |
3 | = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form", remote: true do | 3 | = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form", remote: true do |
4 | - = select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select" | 4 | + = select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select chosen" |
5 | = hidden_field_tag :destination, "tree" | 5 | = hidden_field_tag :destination, "tree" |
6 | = hidden_field_tag :path, params[:path] | 6 | = hidden_field_tag :path, params[:path] |
7 | %li{class: "#{'active' if (controller.controller_name == "refs") }"} | 7 | %li{class: "#{'active' if (controller.controller_name == "refs") }"} |
app/views/refs/_tree.html.haml
@@ -43,18 +43,11 @@ | @@ -43,18 +43,11 @@ | ||
43 | %i.icon-file | 43 | %i.icon-file |
44 | = content.name | 44 | = content.name |
45 | .file_content.wiki | 45 | .file_content.wiki |
46 | - - if content.name =~ /\.(md|markdown)$/i | ||
47 | - = preserve do | ||
48 | - = markdown(content.data) | ||
49 | - - else | ||
50 | - = simple_format(content.data) | 46 | + = raw GitHub::Markup.render(content.name, content.data) |
51 | 47 | ||
52 | :javascript | 48 | :javascript |
53 | $(function(){ | 49 | $(function(){ |
54 | - $('.project-refs-select').chosen(); | ||
55 | - | ||
56 | history.pushState({ path: this.path }, '', "#{@history_path}"); | 50 | history.pushState({ path: this.path }, '', "#{@history_path}"); |
57 | - | ||
58 | }); | 51 | }); |
59 | 52 | ||
60 | // Load last commit log for each file in tree | 53 | // Load last commit log for each file in tree |
app/views/refs/_tree_file.html.haml
@@ -9,10 +9,9 @@ | @@ -9,10 +9,9 @@ | ||
9 | = link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small" | 9 | = link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small" |
10 | = link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small" | 10 | = link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small" |
11 | - if file.text? | 11 | - if file.text? |
12 | - - if name =~ /\.(md|markdown)$/i | 12 | + - if markup?(name) |
13 | .file_content.wiki | 13 | .file_content.wiki |
14 | - = preserve do | ||
15 | - = markdown(file.data) | 14 | + = raw GitHub::Markup.render(name, file.data) |
16 | - else | 15 | - else |
17 | .file_content.code | 16 | .file_content.code |
18 | - unless file.empty? | 17 | - unless file.empty? |
app/views/refs/blame.html.haml
app/views/snippets/_form.html.haml
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | .input= f.text_field :file_name, placeholder: "example.rb" | 16 | .input= f.text_field :file_name, placeholder: "example.rb" |
17 | .clearfix | 17 | .clearfix |
18 | = f.label "Lifetime" | 18 | = f.label "Lifetime" |
19 | - .input= f.select :expires_at, lifetime_select_options, {}, style: "width:200px;" | 19 | + .input= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'} |
20 | .clearfix | 20 | .clearfix |
21 | = f.label :content, "Code" | 21 | = f.label :content, "Code" |
22 | .input= f.text_area :content, class: "span8" | 22 | .input= f.text_area :content, class: "span8" |
@@ -26,11 +26,3 @@ | @@ -26,11 +26,3 @@ | ||
26 | = link_to "Cancel", project_snippets_path(@project), class: " btn" | 26 | = link_to "Cancel", project_snippets_path(@project), class: " btn" |
27 | - unless @snippet.new_record? | 27 | - unless @snippet.new_record? |
28 | .right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" | 28 | .right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" |
29 | - | ||
30 | - | ||
31 | - | ||
32 | -:javascript | ||
33 | - $(function(){ | ||
34 | - $('select#snippet_expires_at').chosen(); | ||
35 | - }); | ||
36 | - |
app/views/team_members/_form.html.haml
@@ -10,21 +10,14 @@ | @@ -10,21 +10,14 @@ | ||
10 | 10 | ||
11 | %h6 1. Choose people you want in the team | 11 | %h6 1. Choose people you want in the team |
12 | .clearfix | 12 | .clearfix |
13 | - = f.label :user_ids, "Peolpe" | ||
14 | - .input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).all, :id, :name), { class: "xxlarge", multiple: true }) | ||
15 | - | 13 | + = f.label :user_ids, "People" |
14 | + .input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).all, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true}) | ||
16 | 15 | ||
17 | %h6 2. Set access level for them | 16 | %h6 2. Set access level for them |
18 | .clearfix | 17 | .clearfix |
19 | = f.label :project_access, "Project Access" | 18 | = f.label :project_access, "Project Access" |
20 | - .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select" | ||
21 | - | 19 | + .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen" |
22 | 20 | ||
23 | .actions | 21 | .actions |
24 | = f.submit 'Save', class: "btn save-btn" | 22 | = f.submit 'Save', class: "btn save-btn" |
25 | = link_to "Cancel", team_project_path(@project), class: "btn cancel-btn" | 23 | = link_to "Cancel", team_project_path(@project), class: "btn cancel-btn" |
26 | - | ||
27 | - | ||
28 | -:javascript | ||
29 | - $('select#user_ids').chosen(); | ||
30 | - $('select#project_access').chosen(); |
app/views/team_members/_show.html.haml
1 | - user = member.user | 1 | - user = member.user |
2 | - allow_admin = can? current_user, :admin_project, @project | 2 | - allow_admin = can? current_user, :admin_project, @project |
3 | %tr{id: dom_id(member), class: "team_member_row user_#{user.id}"} | 3 | %tr{id: dom_id(member), class: "team_member_row user_#{user.id}"} |
4 | - %td | 4 | + %td.span6 |
5 | = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do | 5 | = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do |
6 | = image_tag gravatar_icon(user.email, 40), class: "avatar s32" | 6 | = image_tag gravatar_icon(user.email, 40), class: "avatar s32" |
7 | = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do | 7 | = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do |
8 | %strong= truncate(user.name, lenght: 40) | 8 | %strong= truncate(user.name, lenght: 40) |
9 | - %br | ||
10 | - %div.cgray= user.email | 9 | + %br |
10 | + %small.cgray= user.email | ||
11 | 11 | ||
12 | - %td | 12 | + %td.span5 |
13 | .right | 13 | .right |
14 | + - if current_user == user | ||
15 | + %span.btn.disabled This is you! | ||
14 | - if @project.owner == user | 16 | - if @project.owner == user |
15 | - %span.btn.disabled.success Project Owner | ||
16 | - - if user.blocked | 17 | + %span.btn.disabled.success Owner |
18 | + - elsif user.blocked | ||
17 | %span.btn.disabled.blocked Blocked | 19 | %span.btn.disabled.blocked Blocked |
20 | + - elsif allow_admin | ||
21 | + = link_to project_team_member_path(project_id: @project, id: member.id), confirm: remove_from_team_message(@project, member), method: :delete, class: "very_small btn danger" do | ||
22 | + %i.icon-minus.icon-white | ||
23 | + | ||
18 | - if allow_admin | 24 | - if allow_admin |
19 | = form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f| | 25 | = form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f| |
20 | - = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select" | 26 | + = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2" |
config/gitlab.yml.example
@@ -33,11 +33,12 @@ app: | @@ -33,11 +33,12 @@ app: | ||
33 | git_host: | 33 | git_host: |
34 | admin_uri: git@localhost:gitolite-admin | 34 | admin_uri: git@localhost:gitolite-admin |
35 | base_path: /home/git/repositories/ | 35 | base_path: /home/git/repositories/ |
36 | - # hooks_path: /var/lib/gitolite/.gitolite/hooks/ # only needed when gitolite is not installed according the manual | ||
37 | - # host: localhost | 36 | + hooks_path: /home/git/.gitolite/hooks/ |
37 | + gitolite_admin_key: gitlab | ||
38 | git_user: git | 38 | git_user: git |
39 | upload_pack: true | 39 | upload_pack: true |
40 | receive_pack: true | 40 | receive_pack: true |
41 | + # host: localhost | ||
41 | # port: 22 | 42 | # port: 22 |
42 | 43 | ||
43 | # Git settings | 44 | # Git settings |
config/initializers/1_settings.rb
@@ -102,6 +102,10 @@ class Settings < Settingslogic | @@ -102,6 +102,10 @@ class Settings < Settingslogic | ||
102 | git_host['admin_uri'] || 'git@localhost:gitolite-admin' | 102 | git_host['admin_uri'] || 'git@localhost:gitolite-admin' |
103 | end | 103 | end |
104 | 104 | ||
105 | + def gitolite_admin_key | ||
106 | + git_host['gitolite_admin_key'] || 'gitlab' | ||
107 | + end | ||
108 | + | ||
105 | def default_projects_limit | 109 | def default_projects_limit |
106 | app['default_projects_limit'] || 10 | 110 | app['default_projects_limit'] || 10 |
107 | end | 111 | end |
doc/installation.md
@@ -113,17 +113,20 @@ Generate key: | @@ -113,17 +113,20 @@ Generate key: | ||
113 | Clone GitLab's fork of the Gitolite source code: | 113 | Clone GitLab's fork of the Gitolite source code: |
114 | 114 | ||
115 | cd /home/git | 115 | cd /home/git |
116 | - sudo -H -u git git clone https://github.com/gitlabhq/gitolite.git /home/git/gitolite | 116 | + sudo -H -u git git clone -b gl-v304 https://github.com/gitlabhq/gitolite.git /home/git/gitolite |
117 | 117 | ||
118 | Setup: | 118 | Setup: |
119 | 119 | ||
120 | + cd /home/git | ||
121 | + sudo -u git -H mkdir bin | ||
120 | sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" >> /home/git/.profile' | 122 | sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" >> /home/git/.profile' |
121 | - sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; /home/git/gitolite/src/gl-system-install" | 123 | + sudo -u git sh -c 'gitolite/install -ln /home/git/bin' |
124 | + | ||
122 | sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub | 125 | sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub |
123 | sudo chmod 0444 /home/git/gitlab.pub | 126 | sudo chmod 0444 /home/git/gitlab.pub |
124 | 127 | ||
125 | - sudo -u git -H sed -i 's/0077/0007/g' /home/git/share/gitolite/conf/example.gitolite.rc | ||
126 | - sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gl-setup -q /home/git/gitlab.pub" | 128 | + sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gitolite setup -pk /home/git/gitlab.pub" |
129 | + sudo -u git -H sed -i 's/0077/0007/g' /home/git/.gitolite.rc | ||
127 | 130 | ||
128 | Permissions: | 131 | Permissions: |
129 | 132 | ||
@@ -189,8 +192,8 @@ and ensure you have followed all of the above steps carefully. | @@ -189,8 +192,8 @@ and ensure you have followed all of the above steps carefully. | ||
189 | 192 | ||
190 | #### Setup GitLab hooks | 193 | #### Setup GitLab hooks |
191 | 194 | ||
192 | - sudo cp ./lib/hooks/post-receive /home/git/share/gitolite/hooks/common/post-receive | ||
193 | - sudo chown git:git /home/git/share/gitolite/hooks/common/post-receive | 195 | + sudo cp ./lib/hooks/post-receive /home/git/.gitolite/hooks/common/post-receive |
196 | + sudo chown git:git /home/git/.gitolite/hooks/common/post-receive | ||
194 | 197 | ||
195 | #### Check application status | 198 | #### Check application status |
196 | 199 |
features/dashboard/dashboard.feature
@@ -15,4 +15,14 @@ Feature: Dashboard | @@ -15,4 +15,14 @@ Feature: Dashboard | ||
15 | And I click "Create Merge Request" link | 15 | And I click "Create Merge Request" link |
16 | Then I see prefilled new Merge Request page | 16 | Then I see prefilled new Merge Request page |
17 | 17 | ||
18 | + Scenario: I should see User joined Project event | ||
19 | + Given user with name "John Doe" joined project "Shop" | ||
20 | + When I visit dashboard page | ||
21 | + Then I should see "John Doe joined project Shop" event | ||
18 | 22 | ||
23 | + Scenario: I should see User left Project event | ||
24 | + Given user with name "John Doe" joined project "Shop" | ||
25 | + And user with name "John Doe" left project "Shop" | ||
26 | + When I visit dashboard page | ||
27 | + Then I should see "John Doe left project Shop" event | ||
28 | + |
features/step_definitions/dashboard_steps.rb
@@ -109,3 +109,28 @@ Given /^I have authored merge requests$/ do | @@ -109,3 +109,28 @@ Given /^I have authored merge requests$/ do | ||
109 | :author => @user, | 109 | :author => @user, |
110 | :project => project2 | 110 | :project => project2 |
111 | end | 111 | end |
112 | + | ||
113 | +Given /^user with name "(.*?)" joined project "(.*?)"$/ do |user_name, project_name| | ||
114 | + user = Factory.create(:user, {name: user_name}) | ||
115 | + project = Project.find_by_name project_name | ||
116 | + Event.create( | ||
117 | + project: project, | ||
118 | + author_id: user.id, | ||
119 | + action: Event::Joined | ||
120 | + ) | ||
121 | +end | ||
122 | + | ||
123 | +Given /^user with name "(.*?)" left project "(.*?)"$/ do |user_name, project_name| | ||
124 | + user = User.find_by_name user_name | ||
125 | + project = Project.find_by_name project_name | ||
126 | + Event.create( | ||
127 | + project: project, | ||
128 | + author_id: user.id, | ||
129 | + action: Event::Left | ||
130 | + ) | ||
131 | +end | ||
132 | + | ||
133 | +Then /^I should see "(.*?)" event$/ do |event_text| | ||
134 | + page.should have_content(event_text) | ||
135 | +end | ||
136 | + |
lib/api/helpers.rb
@@ -8,7 +8,7 @@ module Gitlab | @@ -8,7 +8,7 @@ module Gitlab | ||
8 | if @project ||= current_user.projects.find_by_id(params[:id]) || | 8 | if @project ||= current_user.projects.find_by_id(params[:id]) || |
9 | current_user.projects.find_by_code(params[:id]) | 9 | current_user.projects.find_by_code(params[:id]) |
10 | else | 10 | else |
11 | - error!({'message' => '404 Not found'}, 404) | 11 | + not_found! |
12 | end | 12 | end |
13 | 13 | ||
14 | @project | 14 | @project |
@@ -19,7 +19,48 @@ module Gitlab | @@ -19,7 +19,48 @@ module Gitlab | ||
19 | end | 19 | end |
20 | 20 | ||
21 | def authenticate! | 21 | def authenticate! |
22 | - error!({'message' => '401 Unauthorized'}, 401) unless current_user | 22 | + unauthorized! unless current_user |
23 | + end | ||
24 | + | ||
25 | + def authorize! action, subject | ||
26 | + unless abilities.allowed?(current_user, action, subject) | ||
27 | + forbidden! | ||
28 | + end | ||
29 | + end | ||
30 | + | ||
31 | + # error helpers | ||
32 | + | ||
33 | + def forbidden! | ||
34 | + render_api_error!('403 Forbidden', 403) | ||
35 | + end | ||
36 | + | ||
37 | + def not_found!(resource = nil) | ||
38 | + message = ["404"] | ||
39 | + message << resource if resource | ||
40 | + message << "Not Found" | ||
41 | + render_api_error!(message.join(' '), 404) | ||
42 | + end | ||
43 | + | ||
44 | + def unauthorized! | ||
45 | + render_api_error!('401 Unauthorized', 401) | ||
46 | + end | ||
47 | + | ||
48 | + def not_allowed! | ||
49 | + render_api_error!('Method Not Allowed', 405) | ||
50 | + end | ||
51 | + | ||
52 | + def render_api_error!(message, status) | ||
53 | + error!({'message' => message}, status) | ||
54 | + end | ||
55 | + | ||
56 | + private | ||
57 | + | ||
58 | + def abilities | ||
59 | + @abilities ||= begin | ||
60 | + abilities = Six.new | ||
61 | + abilities << Ability | ||
62 | + abilities | ||
63 | + end | ||
23 | end | 64 | end |
24 | end | 65 | end |
25 | end | 66 | end |
lib/api/issues.rb
@@ -60,7 +60,7 @@ module Gitlab | @@ -60,7 +60,7 @@ module Gitlab | ||
60 | if @issue.save | 60 | if @issue.save |
61 | present @issue, with: Entities::Issue | 61 | present @issue, with: Entities::Issue |
62 | else | 62 | else |
63 | - error!({'message' => '404 Not found'}, 404) | 63 | + not_found! |
64 | end | 64 | end |
65 | end | 65 | end |
66 | 66 | ||
@@ -79,6 +79,8 @@ module Gitlab | @@ -79,6 +79,8 @@ module Gitlab | ||
79 | # PUT /projects/:id/issues/:issue_id | 79 | # PUT /projects/:id/issues/:issue_id |
80 | put ":id/issues/:issue_id" do | 80 | put ":id/issues/:issue_id" do |
81 | @issue = user_project.issues.find(params[:issue_id]) | 81 | @issue = user_project.issues.find(params[:issue_id]) |
82 | + authorize! :modify_issue, @issue | ||
83 | + | ||
82 | parameters = { | 84 | parameters = { |
83 | title: (params[:title] || @issue.title), | 85 | title: (params[:title] || @issue.title), |
84 | description: (params[:description] || @issue.description), | 86 | description: (params[:description] || @issue.description), |
@@ -91,7 +93,7 @@ module Gitlab | @@ -91,7 +93,7 @@ module Gitlab | ||
91 | if @issue.update_attributes(parameters) | 93 | if @issue.update_attributes(parameters) |
92 | present @issue, with: Entities::Issue | 94 | present @issue, with: Entities::Issue |
93 | else | 95 | else |
94 | - error!({'message' => '404 Not found'}, 404) | 96 | + not_found! |
95 | end | 97 | end |
96 | end | 98 | end |
97 | 99 | ||
@@ -103,7 +105,7 @@ module Gitlab | @@ -103,7 +105,7 @@ module Gitlab | ||
103 | # Example Request: | 105 | # Example Request: |
104 | # DELETE /projects/:id/issues/:issue_id | 106 | # DELETE /projects/:id/issues/:issue_id |
105 | delete ":id/issues/:issue_id" do | 107 | delete ":id/issues/:issue_id" do |
106 | - error!({'message' => 'method not allowed'}, 405) | 108 | + not_allowed! |
107 | end | 109 | end |
108 | end | 110 | end |
109 | end | 111 | end |
lib/api/milestones.rb
@@ -45,7 +45,7 @@ module Gitlab | @@ -45,7 +45,7 @@ module Gitlab | ||
45 | if @milestone.save | 45 | if @milestone.save |
46 | present @milestone, with: Entities::Milestone | 46 | present @milestone, with: Entities::Milestone |
47 | else | 47 | else |
48 | - error!({'message' => '404 Not found'}, 404) | 48 | + not_found! |
49 | end | 49 | end |
50 | end | 50 | end |
51 | 51 | ||
@@ -61,6 +61,8 @@ module Gitlab | @@ -61,6 +61,8 @@ module Gitlab | ||
61 | # Example Request: | 61 | # Example Request: |
62 | # PUT /projects/:id/milestones/:milestone_id | 62 | # PUT /projects/:id/milestones/:milestone_id |
63 | put ":id/milestones/:milestone_id" do | 63 | put ":id/milestones/:milestone_id" do |
64 | + authorize! :admin_milestone, user_project | ||
65 | + | ||
64 | @milestone = user_project.milestones.find(params[:milestone_id]) | 66 | @milestone = user_project.milestones.find(params[:milestone_id]) |
65 | parameters = { | 67 | parameters = { |
66 | title: (params[:title] || @milestone.title), | 68 | title: (params[:title] || @milestone.title), |
@@ -72,7 +74,7 @@ module Gitlab | @@ -72,7 +74,7 @@ module Gitlab | ||
72 | if @milestone.update_attributes(parameters) | 74 | if @milestone.update_attributes(parameters) |
73 | present @milestone, with: Entities::Milestone | 75 | present @milestone, with: Entities::Milestone |
74 | else | 76 | else |
75 | - error!({'message' => '404 Not found'}, 404) | 77 | + not_found! |
76 | end | 78 | end |
77 | end | 79 | end |
78 | end | 80 | end |
lib/api/projects.rb
@@ -50,7 +50,7 @@ module Gitlab | @@ -50,7 +50,7 @@ module Gitlab | ||
50 | if @project.saved? | 50 | if @project.saved? |
51 | present @project, with: Entities::Project | 51 | present @project, with: Entities::Project |
52 | else | 52 | else |
53 | - error!({'message' => '404 Not found'}, 404) | 53 | + not_found! |
54 | end | 54 | end |
55 | end | 55 | end |
56 | 56 | ||
@@ -74,6 +74,7 @@ module Gitlab | @@ -74,6 +74,7 @@ module Gitlab | ||
74 | # Example Request: | 74 | # Example Request: |
75 | # POST /projects/:id/users | 75 | # POST /projects/:id/users |
76 | post ":id/users" do | 76 | post ":id/users" do |
77 | + authorize! :admin_project, user_project | ||
77 | user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access]) | 78 | user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access]) |
78 | nil | 79 | nil |
79 | end | 80 | end |
@@ -87,6 +88,7 @@ module Gitlab | @@ -87,6 +88,7 @@ module Gitlab | ||
87 | # Example Request: | 88 | # Example Request: |
88 | # PUT /projects/:id/add_users | 89 | # PUT /projects/:id/add_users |
89 | put ":id/users" do | 90 | put ":id/users" do |
91 | + authorize! :admin_project, user_project | ||
90 | user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access]) | 92 | user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access]) |
91 | nil | 93 | nil |
92 | end | 94 | end |
@@ -99,6 +101,7 @@ module Gitlab | @@ -99,6 +101,7 @@ module Gitlab | ||
99 | # Example Request: | 101 | # Example Request: |
100 | # DELETE /projects/:id/users | 102 | # DELETE /projects/:id/users |
101 | delete ":id/users" do | 103 | delete ":id/users" do |
104 | + authorize! :admin_project, user_project | ||
102 | user_project.delete_users_ids_from_team(params[:user_ids].values) | 105 | user_project.delete_users_ids_from_team(params[:user_ids].values) |
103 | nil | 106 | nil |
104 | end | 107 | end |
@@ -209,7 +212,7 @@ module Gitlab | @@ -209,7 +212,7 @@ module Gitlab | ||
209 | if @snippet.save | 212 | if @snippet.save |
210 | present @snippet, with: Entities::ProjectSnippet | 213 | present @snippet, with: Entities::ProjectSnippet |
211 | else | 214 | else |
212 | - error!({'message' => '404 Not found'}, 404) | 215 | + not_found! |
213 | end | 216 | end |
214 | end | 217 | end |
215 | 218 | ||
@@ -226,6 +229,8 @@ module Gitlab | @@ -226,6 +229,8 @@ module Gitlab | ||
226 | # PUT /projects/:id/snippets/:snippet_id | 229 | # PUT /projects/:id/snippets/:snippet_id |
227 | put ":id/snippets/:snippet_id" do | 230 | put ":id/snippets/:snippet_id" do |
228 | @snippet = user_project.snippets.find(params[:snippet_id]) | 231 | @snippet = user_project.snippets.find(params[:snippet_id]) |
232 | + authorize! :modify_snippet, @snippet | ||
233 | + | ||
229 | parameters = { | 234 | parameters = { |
230 | title: (params[:title] || @snippet.title), | 235 | title: (params[:title] || @snippet.title), |
231 | file_name: (params[:file_name] || @snippet.file_name), | 236 | file_name: (params[:file_name] || @snippet.file_name), |
@@ -236,7 +241,7 @@ module Gitlab | @@ -236,7 +241,7 @@ module Gitlab | ||
236 | if @snippet.update_attributes(parameters) | 241 | if @snippet.update_attributes(parameters) |
237 | present @snippet, with: Entities::ProjectSnippet | 242 | present @snippet, with: Entities::ProjectSnippet |
238 | else | 243 | else |
239 | - error!({'message' => '404 Not found'}, 404) | 244 | + not_found! |
240 | end | 245 | end |
241 | end | 246 | end |
242 | 247 | ||
@@ -249,6 +254,8 @@ module Gitlab | @@ -249,6 +254,8 @@ module Gitlab | ||
249 | # DELETE /projects/:id/snippets/:snippet_id | 254 | # DELETE /projects/:id/snippets/:snippet_id |
250 | delete ":id/snippets/:snippet_id" do | 255 | delete ":id/snippets/:snippet_id" do |
251 | @snippet = user_project.snippets.find(params[:snippet_id]) | 256 | @snippet = user_project.snippets.find(params[:snippet_id]) |
257 | + authorize! :modify_snippet, @snippet | ||
258 | + | ||
252 | @snippet.destroy | 259 | @snippet.destroy |
253 | end | 260 | end |
254 | 261 | ||
@@ -277,10 +284,10 @@ module Gitlab | @@ -277,10 +284,10 @@ module Gitlab | ||
277 | ref = params[:sha] | 284 | ref = params[:sha] |
278 | 285 | ||
279 | commit = user_project.commit ref | 286 | commit = user_project.commit ref |
280 | - error!('404 Commit Not Found', 404) unless commit | 287 | + not_found! "Commit" unless commit |
281 | 288 | ||
282 | tree = Tree.new commit.tree, user_project, ref, params[:filepath] | 289 | tree = Tree.new commit.tree, user_project, ref, params[:filepath] |
283 | - error!('404 File Not Found', 404) unless tree.try(:tree) | 290 | + not_found! "File" unless tree.try(:tree) |
284 | 291 | ||
285 | if tree.text? | 292 | if tree.text? |
286 | encoding = Gitlab::Encode.detect_encoding(tree.data) | 293 | encoding = Gitlab::Encode.detect_encoding(tree.data) |
lib/gitlab/backend/gitolite.rb
@@ -35,7 +35,7 @@ module Gitlab | @@ -35,7 +35,7 @@ module Gitlab | ||
35 | end | 35 | end |
36 | 36 | ||
37 | def enable_automerge | 37 | def enable_automerge |
38 | - config.admin_all_repo!(project) | 38 | + config.admin_all_repo! |
39 | end | 39 | end |
40 | 40 | ||
41 | alias_method :create_repository, :update_repository | 41 | alias_method :create_repository, :update_repository |
lib/gitlab/backend/gitolite_config.rb
@@ -148,18 +148,7 @@ module Gitlab | @@ -148,18 +148,7 @@ module Gitlab | ||
148 | # Enable access to all repos for gitolite admin. | 148 | # Enable access to all repos for gitolite admin. |
149 | # We use it for accept merge request feature | 149 | # We use it for accept merge request feature |
150 | def admin_all_repo | 150 | def admin_all_repo |
151 | - owner_name = "" | ||
152 | - | ||
153 | - # Read gitolite-admin user | ||
154 | - # | ||
155 | - begin | ||
156 | - repo = conf.get_repo("gitolite-admin") | ||
157 | - owner_name = repo.permissions[0]["RW+"][""][0] | ||
158 | - raise StandardError if owner_name.blank? | ||
159 | - rescue => ex | ||
160 | - puts "Can't determine gitolite-admin owner".red | ||
161 | - raise StandardError | ||
162 | - end | 151 | + owner_name = Gitlab.config.gitolite_admin_key |
163 | 152 | ||
164 | # @ALL repos premission for gitolite owner | 153 | # @ALL repos premission for gitolite owner |
165 | repo_name = "@all" | 154 | repo_name = "@all" |
lib/gitlab/merge.rb
@@ -21,8 +21,7 @@ module Gitlab | @@ -21,8 +21,7 @@ module Gitlab | ||
21 | if output =~ /CONFLICT/ | 21 | if output =~ /CONFLICT/ |
22 | false | 22 | false |
23 | else | 23 | else |
24 | - repo.git.push({}, "origin", merge_request.target_branch) | ||
25 | - true | 24 | + !!repo.git.push({}, "origin", merge_request.target_branch) |
26 | end | 25 | end |
27 | end | 26 | end |
28 | end | 27 | end |
@@ -0,0 +1,15 @@ | @@ -0,0 +1,15 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe TreeHelper do | ||
4 | + describe '#markup?' do | ||
5 | + %w(mdown md markdown textile rdoc org creole mediawiki rst asciidoc pod).each do |type| | ||
6 | + it "returns true for #{type} files" do | ||
7 | + markup?("README.#{type}").should be_true | ||
8 | + end | ||
9 | + end | ||
10 | + | ||
11 | + it "returns false when given a non-markup filename" do | ||
12 | + markup?('README.rb').should_not be_true | ||
13 | + end | ||
14 | + end | ||
15 | +end |
spec/models/event_spec.rb
@@ -49,4 +49,26 @@ describe Event do | @@ -49,4 +49,26 @@ describe Event do | ||
49 | it { @event.branch_name.should == "master" } | 49 | it { @event.branch_name.should == "master" } |
50 | it { @event.author.should == @user } | 50 | it { @event.author.should == @user } |
51 | end | 51 | end |
52 | + | ||
53 | + describe "Joined project team" do | ||
54 | + let(:project) {Factory.create :project} | ||
55 | + let(:new_user) {Factory.create :user} | ||
56 | + it "should create event" do | ||
57 | + UsersProject.observers.enable :users_project_observer | ||
58 | + expect{ | ||
59 | + UsersProject.bulk_import(project, [new_user.id], UsersProject::DEVELOPER) | ||
60 | + }.to change{Event.count}.by(1) | ||
61 | + end | ||
62 | + end | ||
63 | + describe "Left project team" do | ||
64 | + let(:project) {Factory.create :project} | ||
65 | + let(:new_user) {Factory.create :user} | ||
66 | + it "should create event" do | ||
67 | + UsersProject.bulk_import(project, [new_user.id], UsersProject::DEVELOPER) | ||
68 | + UsersProject.observers.enable :users_project_observer | ||
69 | + expect{ | ||
70 | + UsersProject.bulk_delete(project, [new_user.id]) | ||
71 | + }.to change{Event.count}.by(1) | ||
72 | + end | ||
73 | + end | ||
52 | end | 74 | end |
spec/observers/users_project_observer_spec.rb
@@ -23,6 +23,14 @@ describe UsersProjectObserver do | @@ -23,6 +23,14 @@ describe UsersProjectObserver do | ||
23 | Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true)) | 23 | Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true)) |
24 | subject.after_create(users_project) | 24 | subject.after_create(users_project) |
25 | end | 25 | end |
26 | + it "should create new event" do | ||
27 | + Event.should_receive(:create).with( | ||
28 | + project_id: users_project.project.id, | ||
29 | + action: Event::Joined, | ||
30 | + author_id: users_project.user.id | ||
31 | + ) | ||
32 | + subject.after_create(users_project) | ||
33 | + end | ||
26 | end | 34 | end |
27 | 35 | ||
28 | describe "#after_update" do | 36 | describe "#after_update" do |
@@ -37,4 +45,23 @@ describe UsersProjectObserver do | @@ -37,4 +45,23 @@ describe UsersProjectObserver do | ||
37 | subject.after_update(users_project) | 45 | subject.after_update(users_project) |
38 | end | 46 | end |
39 | end | 47 | end |
48 | + describe "#after_destroy" do | ||
49 | + it "should called when UsersProject destroyed" do | ||
50 | + subject.should_receive(:after_destroy) | ||
51 | + UsersProject.observers.enable :users_project_observer do | ||
52 | + UsersProject.bulk_delete( | ||
53 | + users_project.project, | ||
54 | + [users_project.user.id] | ||
55 | + ) | ||
56 | + end | ||
57 | + end | ||
58 | + it "should create new event" do | ||
59 | + Event.should_receive(:create).with( | ||
60 | + project_id: users_project.project.id, | ||
61 | + action: Event::Left, | ||
62 | + author_id: users_project.user.id | ||
63 | + ) | ||
64 | + subject.after_destroy(users_project) | ||
65 | + end | ||
66 | + end | ||
40 | end | 67 | end |
spec/requests/api/projects_spec.rb
@@ -86,7 +86,7 @@ describe Gitlab::API do | @@ -86,7 +86,7 @@ describe Gitlab::API do | ||
86 | it "should return a 404 error if not found" do | 86 | it "should return a 404 error if not found" do |
87 | get api("/projects/42", user) | 87 | get api("/projects/42", user) |
88 | response.status.should == 404 | 88 | response.status.should == 404 |
89 | - json_response['message'].should == '404 Not found' | 89 | + json_response['message'].should == '404 Not Found' |
90 | end | 90 | end |
91 | end | 91 | end |
92 | 92 |