Commit 4226458fafc0cd64422c77691a2c802d2347ea2d
Merge branch 'master' into project_users_api
@@ -9,6 +9,8 @@ branches: | @@ -9,6 +9,8 @@ branches: | ||
9 | - 'master' | 9 | - 'master' |
10 | rvm: | 10 | rvm: |
11 | - 1.9.3 | 11 | - 1.9.3 |
12 | +services: | ||
13 | + - mysql | ||
12 | before_script: | 14 | before_script: |
13 | - "cp config/database.yml.$DB config/database.yml" | 15 | - "cp config/database.yml.$DB config/database.yml" |
14 | - "cp config/gitlab.yml.example config/gitlab.yml" | 16 | - "cp config/gitlab.yml.example config/gitlab.yml" |
@@ -108,7 +108,7 @@ GEM | @@ -108,7 +108,7 @@ GEM | ||
108 | bcrypt-ruby (3.0.1) | 108 | bcrypt-ruby (3.0.1) |
109 | blankslate (2.1.2.4) | 109 | blankslate (2.1.2.4) |
110 | bootstrap-sass (2.0.4.0) | 110 | bootstrap-sass (2.0.4.0) |
111 | - builder (3.0.0) | 111 | + builder (3.0.2) |
112 | capybara (1.1.2) | 112 | capybara (1.1.2) |
113 | mime-types (>= 1.16) | 113 | mime-types (>= 1.16) |
114 | nokogiri (>= 1.3.3) | 114 | nokogiri (>= 1.3.3) |
@@ -125,7 +125,7 @@ GEM | @@ -125,7 +125,7 @@ GEM | ||
125 | charlock_holmes (0.6.8) | 125 | charlock_holmes (0.6.8) |
126 | childprocess (0.3.2) | 126 | childprocess (0.3.2) |
127 | ffi (~> 1.0.6) | 127 | ffi (~> 1.0.6) |
128 | - chosen-rails (0.9.8) | 128 | + chosen-rails (0.9.8.3) |
129 | railties (~> 3.0) | 129 | railties (~> 3.0) |
130 | thor (~> 0.14) | 130 | thor (~> 0.14) |
131 | coderay (1.0.6) | 131 | coderay (1.0.6) |
5.01 KB
4.95 KB
3.22 KB
3.61 KB
4.64 KB
4.04 KB
3.08 KB
3.77 KB
4.15 KB
4.37 KB
4.62 KB
3.41 KB
4.63 KB
6.9 KB
5.33 KB
3.62 KB
4.37 KB
6.52 KB
3.01 KB
4.92 KB
2.78 KB
5.5 KB
4.98 KB
4.24 KB
3.11 KB
3.1 KB
3.53 KB
2.94 KB
2.87 KB
3.13 KB
3.44 KB
3.44 KB
2.97 KB
3.26 KB
3.26 KB
2.95 KB
3.63 KB
3 KB
3.46 KB
3.12 KB
3.15 KB
3.16 KB
1.37 KB
4.71 KB
6.59 KB
2.87 KB
5.9 KB
3.98 KB
2.95 KB
5.78 KB
4.36 KB
3.87 KB
2.9 KB
3.42 KB
2.25 KB
1.79 KB
4.56 KB
3.82 KB
1.35 KB
5.45 KB
2.4 KB
4.15 KB
5.89 KB
6.24 KB
3.13 KB
2.72 KB
3.72 KB
5.43 KB
5.95 KB
6.44 KB
5.13 KB
2.7 KB
4.75 KB
5.6 KB
6.33 KB
4.61 KB
3.8 KB
4.76 KB
5.28 KB
2.31 KB
3.79 KB
2.3 KB
1.3 KB
4.13 KB
3.66 KB
4.97 KB
3.99 KB
4 KB
5.07 KB
4.73 KB
3.74 KB
5.09 KB
5.91 KB
4.59 KB
3.09 KB
6.39 KB
3.25 KB
6.75 KB
5.02 KB
4.09 KB
6.33 KB
5.81 KB
6.07 KB
8.32 KB
5.02 KB
2.63 KB
4.02 KB
5.81 KB
4.38 KB
4.88 KB
3.75 KB
3.97 KB
1.64 KB
1.96 KB
2.95 KB
4.4 KB
5.99 KB
2.85 KB
3.94 KB
4.38 KB
4.55 KB
5.26 KB
4.4 KB
5.02 KB
4.56 KB
4.18 KB
3.66 KB
5.75 KB
5.85 KB
5.59 KB
6.56 KB
4.23 KB
2.84 KB
2.87 KB
1.64 KB
5.47 KB
7.01 KB
5.74 KB
3.89 KB
3.38 KB
5.13 KB
4.61 KB
4.54 KB
3.49 KB
4.57 KB
4.21 KB
3.75 KB
3.41 KB
6.94 KB
4.09 KB
4.55 KB
2.53 KB
2.53 KB
2.8 KB
2.53 KB
2.79 KB
2.45 KB
2.73 KB
2.78 KB
2.53 KB
2.79 KB
2.43 KB
2.67 KB
2.56 KB
2.78 KB
2.56 KB
2.77 KB
2.52 KB
2.67 KB
2.55 KB
2.75 KB
2.54 KB
2.78 KB
2.43 KB
2.68 KB
4.73 KB
5.57 KB
3.78 KB
3.77 KB
1.65 KB
3.55 KB
2.88 KB
4.21 KB
5.83 KB
3.69 KB
1.67 KB
5.39 KB
5.72 KB
4.77 KB
3.61 KB
6.05 KB
3.98 KB
7.96 KB
4.08 KB
6.97 KB
1.54 KB
6.54 KB
7.44 KB
7.2 KB
7.05 KB
5.61 KB
5.18 KB
2.59 KB
5.98 KB
3.92 KB
5.52 KB
5.57 KB
6.53 KB
6.09 KB
5.29 KB
1.23 KB
1.91 KB
5.21 KB
5.67 KB
3.81 KB
4.78 KB
3.65 KB
7.73 KB
4.34 KB
5.33 KB
5.32 KB
2.91 KB
2.58 KB
7.2 KB
5.04 KB
5.56 KB
2.72 KB
4.65 KB
2.97 KB
6.13 KB
5.15 KB
5.81 KB
5.79 KB
4.51 KB
6.97 KB
4.24 KB
3.23 KB
5.09 KB
7.64 KB
6.58 KB
3.55 KB
5.02 KB
3.2 KB
6.83 KB
2.08 KB
4.23 KB
4.65 KB
7 KB
6.87 KB
7.13 KB
5.09 KB
4.56 KB
3.75 KB
3.21 KB
3.92 KB
2.75 KB
4.97 KB
2.63 KB
1.44 KB
1.62 KB
4.2 KB
3.85 KB
5.3 KB
4.7 KB
4.81 KB
1.15 KB
4.81 KB
4.29 KB
4.76 KB
5.43 KB
4.78 KB
7.04 KB
3.03 KB
4.54 KB
5.47 KB
1.12 KB
1.49 KB
6.07 KB
3.92 KB
1.16 KB
3.79 KB
4.75 KB
6.12 KB
5.83 KB
4.18 KB
4.61 KB
5.68 KB
4.37 KB
5.74 KB
3.51 KB
5.98 KB
4.91 KB
3.14 KB
3.35 KB
5.71 KB
4.51 KB
6.55 KB
3.52 KB
4.97 KB
3.1 KB
5.85 KB
3.32 KB
3.52 KB
7.37 KB
6.25 KB
4.71 KB
4.2 KB
6.31 KB
7.05 KB
2.89 KB
5.76 KB
4.74 KB
4.2 KB
4.41 KB
6.55 KB
5.87 KB
6.17 KB
5.7 KB
4.77 KB
1.3 KB
1.02 KB
3.46 KB
5.3 KB
6.06 KB
4.97 KB
4.33 KB
1.12 KB
1.03 KB
5.59 KB
3.5 KB
4.28 KB
3.09 KB
6.93 KB
5.57 KB
3.58 KB
7.05 KB
4.06 KB
5.32 KB
4.64 KB
3.65 KB
5.51 KB
5.79 KB
1.87 KB
6.4 KB
3.22 KB
3.42 KB
5.62 KB
6.03 KB
3.96 KB
6.12 KB
2.86 KB
1.08 KB
340 Bytes
1.38 KB
1.28 KB
197 Bytes
591 Bytes
315 Bytes
4 KB
5.75 KB
8.13 KB
3.96 KB
4.45 KB
2.45 KB
5.69 KB
5.71 KB
4.47 KB
5.77 KB
4.77 KB
5 KB
3.46 KB
4.39 KB
3.43 KB
1.42 KB
5.34 KB
4.5 KB
3.81 KB
3.02 KB
6.48 KB
3.61 KB
2.15 KB
6.45 KB
3.58 KB
6.84 KB
2.81 KB
3.42 KB
3.41 KB
3.97 KB
5.5 KB
3.99 KB
4.82 KB
5.04 KB
6.98 KB
3.39 KB
6.19 KB
7.02 KB
2.76 KB
3.37 KB
4 KB
4.82 KB
6.13 KB
6.64 KB
5.43 KB
5.63 KB
5.55 KB
2.79 KB
4.99 KB
4.53 KB
3.7 KB
3.81 KB
6.03 KB
4.23 KB
6.2 KB
5.44 KB
5.78 KB
3.93 KB
3.33 KB
3.69 KB
5.91 KB
4.8 KB
5.26 KB
4.13 KB
3.7 KB
2.59 KB
3.65 KB
3.3 KB
3.59 KB
4.85 KB
5.64 KB
3.34 KB
5.86 KB
5.8 KB
2.41 KB
2.44 KB
4.63 KB
2.97 KB
3.54 KB
3.23 KB
4.1 KB
4.26 KB
4.47 KB
3.03 KB
5.88 KB
5.2 KB
6.38 KB
4.64 KB
4.35 KB
5.11 KB
5.89 KB
5.4 KB
4.57 KB
8.04 KB
4.83 KB
3.29 KB
3.03 KB
3.32 KB
3.59 KB
4.04 KB
5.74 KB
3.04 KB
5.46 KB
3.44 KB
7.41 KB
5.37 KB
4.86 KB
5.22 KB
4.21 KB
3.46 KB
4.07 KB
4.89 KB
9.29 KB
4.3 KB
7.27 KB
6.47 KB
3.99 KB
3.99 KB
2.12 KB
4.56 KB
4.77 KB
1.9 KB
3.11 KB
1.54 KB
6.48 KB
5.68 KB
3.89 KB
6.27 KB
5.97 KB
3.76 KB
4.73 KB
3.83 KB
5.25 KB
6.59 KB
5.08 KB
4.1 KB
3.69 KB
5.8 KB
5.53 KB
3.43 KB
3.21 KB
6.87 KB
4.96 KB
4.62 KB
5.36 KB
4.11 KB
5.08 KB
3.62 KB
5.89 KB
5.2 KB
1.5 KB
2.12 KB
2.48 KB
3.42 KB
5.64 KB
3.84 KB
5.64 KB
5.41 KB
5.04 KB
4.06 KB
4.49 KB
7.35 KB
6.58 KB
5.84 KB
1.76 KB
7.29 KB
5.18 KB
5.55 KB
6.14 KB
2.76 KB
4.19 KB
4.83 KB
4.33 KB
4.97 KB
3.6 KB
5.94 KB
2.13 KB
3.64 KB
3.93 KB
3.58 KB
4.7 KB
2.5 KB
3.01 KB
2.62 KB
5.06 KB
3.92 KB
2.41 KB
5.78 KB
6.77 KB
4.83 KB
4.35 KB
4.63 KB
4.94 KB
6.14 KB
5.39 KB
4.71 KB
6.47 KB
5.3 KB
5.37 KB
5.86 KB
4.68 KB
4.65 KB
4.9 KB
5.5 KB
4.35 KB
5.15 KB
4.96 KB
3.15 KB
3.01 KB
3.01 KB
3.35 KB
3.11 KB
3.27 KB
6.69 KB
4.64 KB
5.02 KB
4.71 KB
3.31 KB
3.84 KB
4.58 KB
4.1 KB
4.14 KB
4.8 KB
6.06 KB
7.73 KB
4.72 KB
4.19 KB
4.92 KB
3.7 KB
4 KB
1.67 KB
5.54 KB
4.41 KB
4.62 KB
6.01 KB
2.15 KB
5.28 KB
1.06 KB
1.07 KB
1.09 KB
1.24 KB
3.56 KB
5.19 KB
6.03 KB
5.25 KB
6.38 KB
6.42 KB
5.31 KB
3.62 KB
4.18 KB
3.85 KB
1.58 KB
5.33 KB
5.52 KB
3.92 KB
4.19 KB
4.04 KB
5.34 KB
2.98 KB
5.45 KB
4.54 KB
5.28 KB
7.6 KB
6.11 KB
5.11 KB
5.26 KB
5.03 KB
6.02 KB
4.1 KB
6.46 KB
1.89 KB
5.31 KB
3.83 KB
7.6 KB
3.06 KB
3.14 KB
5.57 KB
3.47 KB
4.4 KB
3.74 KB
4.95 KB
3.88 KB
6.12 KB
4.75 KB
5.24 KB
4.15 KB
5.32 KB
5.61 KB
3.75 KB
4.46 KB
6.33 KB
6.68 KB
6.59 KB
5.92 KB
5.24 KB
6.67 KB
2.21 KB
2.98 KB
5.77 KB
4.62 KB
5 KB
4.13 KB
9.13 KB
4.57 KB
4.64 KB
4.69 KB
7.41 KB
3.16 KB
3.7 KB
4.75 KB
4.07 KB
2.37 KB
5.7 KB
4.5 KB
1.85 KB
1.9 KB
2.01 KB
2.11 KB
5.75 KB
5.97 KB
5.66 KB
5.94 KB
7.02 KB
5.18 KB
5.92 KB
2.81 KB
6.5 KB
3.97 KB
5.23 KB
5.5 KB
4.55 KB
5.58 KB
4.76 KB
1.87 KB
4.16 KB
4.91 KB
4.25 KB
1.68 KB
6.79 KB
5.56 KB
2.16 KB
5.84 KB
5.05 KB
2.08 KB
3.43 KB
9.13 KB
3.54 KB
3.97 KB
4.26 KB
4.72 KB
5.93 KB
5.04 KB
5.24 KB
3.74 KB
5.35 KB
7.77 KB
6.41 KB
5.61 KB
3.71 KB
3.82 KB
6.44 KB
6.11 KB
5.13 KB
1016 Bytes
3.84 KB
5.45 KB
4.67 KB
6.37 KB
5.55 KB
4.28 KB
5.31 KB
2.97 KB
5.81 KB
4.31 KB
6.49 KB
4.62 KB
3.66 KB
5.81 KB
5.37 KB
1.95 KB
3.24 KB
5.84 KB
4.38 KB
2.52 KB
3.67 KB
4.95 KB
4.96 KB
3.02 KB
5.91 KB
5.61 KB
6.03 KB
842 Bytes
1.69 KB
4.69 KB
5.61 KB
5.65 KB
3.58 KB
3.7 KB
2.94 KB
5.54 KB
3.46 KB
3.81 KB
4.7 KB
4.75 KB
1.37 KB
2.64 KB
4.72 KB
6.02 KB
4.33 KB
4.79 KB
5.39 KB
4.09 KB
5.71 KB
3.63 KB
4.27 KB
4.57 KB
5.92 KB
5.21 KB
5.12 KB
4.21 KB
3.44 KB
3.48 KB
6.83 KB
7.45 KB
4.43 KB
3.8 KB
3.33 KB
4.01 KB
2.94 KB
3.12 KB
4.32 KB
3.85 KB
2.98 KB
5.05 KB
4.08 KB
5.76 KB
4.63 KB
5.19 KB
5.59 KB
3.47 KB
3.63 KB
6.14 KB
4.56 KB
3.34 KB
3.07 KB
3.81 KB
4.97 KB
4.84 KB
4.9 KB
4.75 KB
6.02 KB
3.34 KB
2.41 KB
5.75 KB
6.36 KB
3.1 KB
5.07 KB
4.66 KB
5.37 KB
4.93 KB
872 Bytes
6.05 KB
6.3 KB
3.99 KB
6.13 KB
5.71 KB
4.82 KB
5.89 KB
4.13 KB
2.45 KB
4.29 KB
1.38 KB
3.41 KB
3.08 KB
5.13 KB
5.87 KB
4.73 KB
6.73 KB
3.98 KB
7.91 KB
3.8 KB
2.71 KB
2 KB
4.31 KB
4.87 KB
5.75 KB
2.18 KB
3.51 KB
1.98 KB
@@ -1,11 +0,0 @@ | @@ -1,11 +0,0 @@ | ||
1 | -$(document).ready(function(){ | ||
2 | - $('input#user_force_random_password').on('change', function(elem) { | ||
3 | - var elems = $('#user_password, #user_password_confirmation'); | ||
4 | - | ||
5 | - if ($(this).attr('checked')) { | ||
6 | - elems.val('').attr('disabled', true); | ||
7 | - } else { | ||
8 | - elems.removeAttr('disabled'); | ||
9 | - } | ||
10 | - }); | ||
11 | -}); |
@@ -17,134 +17,3 @@ | @@ -17,134 +17,3 @@ | ||
17 | //= require raphael | 17 | //= require raphael |
18 | //= require branch-graph | 18 | //= require branch-graph |
19 | //= require_tree . | 19 | //= require_tree . |
20 | - | ||
21 | -$(document).ready(function(){ | ||
22 | - | ||
23 | - $(".one_click_select").live("click", function(){ | ||
24 | - $(this).select(); | ||
25 | - }); | ||
26 | - | ||
27 | - $('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){ | ||
28 | - var buttons = $('[type="submit"]', this); | ||
29 | - switch( e.type ){ | ||
30 | - case 'ajax:beforeSend': | ||
31 | - case 'submit': | ||
32 | - buttons.attr('disabled', 'disabled'); | ||
33 | - break; | ||
34 | - case ' ajax:complete': | ||
35 | - default: | ||
36 | - buttons.removeAttr('disabled'); | ||
37 | - break; | ||
38 | - } | ||
39 | - }) | ||
40 | - | ||
41 | - $(".account-box").mouseenter(showMenu); | ||
42 | - $(".account-box").mouseleave(resetMenu); | ||
43 | - | ||
44 | - $("#projects-list .project").live('click', function(e){ | ||
45 | - if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { | ||
46 | - location.href = $(this).attr("url"); | ||
47 | - e.stopPropagation(); | ||
48 | - return false; | ||
49 | - } | ||
50 | - }); | ||
51 | - | ||
52 | - /** | ||
53 | - * Focus search field by pressing 's' key | ||
54 | - */ | ||
55 | - $(document).keypress(function(e) { | ||
56 | - if( $(e.target).is(":input") ) return; | ||
57 | - switch(e.which) { | ||
58 | - case 115: focusSearch(); | ||
59 | - e.preventDefault(); | ||
60 | - } | ||
61 | - }); | ||
62 | - | ||
63 | - /** | ||
64 | - * Commit show suppressed diff | ||
65 | - * | ||
66 | - */ | ||
67 | - $(".supp_diff_link").bind("click", function() { | ||
68 | - showDiff(this); | ||
69 | - }); | ||
70 | - | ||
71 | - /** | ||
72 | - * Note markdown preview | ||
73 | - * | ||
74 | - */ | ||
75 | - $(document).on('click', '#preview-link', function(e) { | ||
76 | - $('#preview-note').text('Loading...'); | ||
77 | - | ||
78 | - var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview'); | ||
79 | - $(this).text(previewLinkText); | ||
80 | - | ||
81 | - var note = $('#note_note').val(); | ||
82 | - if (note.trim().length === 0) { note = 'Nothing to preview'; } | ||
83 | - $.post($(this).attr('href'), {note: note}, function(data) { | ||
84 | - $('#preview-note').html(data); | ||
85 | - }); | ||
86 | - | ||
87 | - $('#preview-note, #note_note').toggle(); | ||
88 | - e.preventDefault(); | ||
89 | - }); | ||
90 | -}); | ||
91 | - | ||
92 | -function focusSearch() { | ||
93 | - $("#search").focus(); | ||
94 | -} | ||
95 | - | ||
96 | -function updatePage(data){ | ||
97 | - $.ajax({type: "GET", url: location.href, data: data, dataType: "script"}); | ||
98 | -} | ||
99 | - | ||
100 | -function showMenu() { | ||
101 | - $(this).toggleClass('hover'); | ||
102 | -} | ||
103 | - | ||
104 | -function resetMenu() { | ||
105 | - $(this).removeClass("hover"); | ||
106 | -} | ||
107 | - | ||
108 | -function slugify(text) { | ||
109 | - return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase(); | ||
110 | -} | ||
111 | - | ||
112 | -function showDiff(link) { | ||
113 | - $(link).next('table').show(); | ||
114 | - $(link).remove(); | ||
115 | -} | ||
116 | - | ||
117 | -(function($){ | ||
118 | - var _chosen = $.fn.chosen; | ||
119 | - $.fn.extend({ | ||
120 | - chosen: function(options) { | ||
121 | - var default_options = {'search_contains' : 'true'}; | ||
122 | - $.extend(default_options, options); | ||
123 | - return _chosen.apply(this, [default_options]); | ||
124 | - }}) | ||
125 | -})(jQuery); | ||
126 | - | ||
127 | - | ||
128 | -function ajaxGet(url) { | ||
129 | - $.ajax({type: "GET", url: url, dataType: "script"}); | ||
130 | -} | ||
131 | - | ||
132 | -/** | ||
133 | - * Disable button if text field is empty | ||
134 | - */ | ||
135 | -function disableButtonIfEmtpyField(field_selector, button_selector) { | ||
136 | - field = $(field_selector); | ||
137 | - if(field.val() == "") { | ||
138 | - field.closest("form").find(button_selector).attr("disabled", "disabled").addClass("disabled"); | ||
139 | - } | ||
140 | - | ||
141 | - field.on('keyup', function(){ | ||
142 | - var field = $(this); | ||
143 | - var closest_submit = field.closest("form").find(button_selector); | ||
144 | - if(field.val() == "") { | ||
145 | - closest_submit.attr("disabled", "disabled").addClass("disabled"); | ||
146 | - } else { | ||
147 | - closest_submit.removeAttr("disabled").removeClass("disabled"); | ||
148 | - } | ||
149 | - }) | ||
150 | -} |
@@ -1,10 +0,0 @@ | @@ -1,10 +0,0 @@ | ||
1 | -function initGraphNav() { | ||
2 | - $(".graph svg").css("position", "relative"); | ||
3 | - $("body").bind("keyup", function(e) { | ||
4 | - if(e.keyCode == 37) { // left | ||
5 | - $(".graph svg").animate({ left: "+=400" }); | ||
6 | - } else if(e.keyCode == 39) { // right | ||
7 | - $(".graph svg").animate({ left: "-=400" }); | ||
8 | - } | ||
9 | - }); | ||
10 | -} |
@@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
1 | +initGraphNav = -> | ||
2 | + $('.graph svg').css 'position', 'relative' | ||
3 | + | ||
4 | + $('body').bind 'keyup', (e) -> | ||
5 | + if e.keyCode is 37 # left | ||
6 | + $('.graph svg').animate left: '+=400' | ||
7 | + else if e.keyCode is 39 # right | ||
8 | + $('.graph svg').animate left: '-=400' | ||
9 | + | ||
10 | +window.initGraphNav = initGraphNav |
@@ -80,6 +80,10 @@ function issuesPage(){ | @@ -80,6 +80,10 @@ function issuesPage(){ | ||
80 | $(this).closest("form").submit(); | 80 | $(this).closest("form").submit(); |
81 | }); | 81 | }); |
82 | 82 | ||
83 | + $("#new_issue_link").click(function(){ | ||
84 | + updateNewIssueURL(); | ||
85 | + }); | ||
86 | + | ||
83 | $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){ | 87 | $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){ |
84 | var t = $(this), | 88 | var t = $(this), |
85 | totalIssues, | 89 | totalIssues, |
@@ -126,3 +130,20 @@ function issuesCheckChanged() { | @@ -126,3 +130,20 @@ function issuesCheckChanged() { | ||
126 | $('.issues_filters').show(); | 130 | $('.issues_filters').show(); |
127 | } | 131 | } |
128 | } | 132 | } |
133 | + | ||
134 | +function updateNewIssueURL(){ | ||
135 | + var new_issue_link = $("#new_issue_link"); | ||
136 | + var milestone_id = $("#milestone_id").val(); | ||
137 | + var assignee_id = $("#assignee_id").val(); | ||
138 | + var new_href = ""; | ||
139 | + if(milestone_id){ | ||
140 | + new_href = "issue[milestone_id]=" + milestone_id + "&"; | ||
141 | + } | ||
142 | + if(assignee_id){ | ||
143 | + new_href = new_href + "issue[assignee_id]=" + assignee_id; | ||
144 | + } | ||
145 | + if(new_href.length){ | ||
146 | + new_href = new_issue_link.attr("href") + "?" + new_href; | ||
147 | + new_issue_link.attr("href", new_href); | ||
148 | + } | ||
149 | +}; |
@@ -0,0 +1,130 @@ | @@ -0,0 +1,130 @@ | ||
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 | +} |
1 | window.Projects = -> | 1 | window.Projects = -> |
2 | - $("#project_name").live "change", -> | ||
3 | - slug = slugify($(this).val()) | ||
4 | - $("#project_code").val(slug) | ||
5 | - $("#project_path").val(slug) | 2 | + $('#project_name').on 'change', -> |
3 | + slug = slugify $(@).val() | ||
4 | + $('#project_code, #project_path').val slug | ||
6 | 5 | ||
7 | - $(".new_project, .edit_project").live "ajax:before", -> | ||
8 | - $(".project_new_holder, .project_edit_holder").hide() | ||
9 | - $(".save-project-loader").show() | 6 | + $('.new_project, .edit_project').on 'ajax:before', -> |
7 | + $('.project_new_holder, .project_edit_holder').hide() | ||
8 | + $('.save-project-loader').show() | ||
10 | 9 | ||
11 | - $("form #project_default_branch").chosen() | ||
12 | - disableButtonIfEmtpyField "#project_name", ".project-submit" | 10 | + $('form #project_default_branch').chosen() |
11 | + disableButtonIfEmtpyField '#project_name', '.project-submit' | ||
13 | 12 | ||
14 | # Git clone panel switcher | 13 | # Git clone panel switcher |
15 | $ -> | 14 | $ -> |
16 | - scope = $('.project_clone_holder') | 15 | + scope = $ '.project_clone_holder' |
17 | if scope.length > 0 | 16 | if scope.length > 0 |
18 | $('a, button', scope).click -> | 17 | $('a, button', scope).click -> |
19 | - $('a, button', scope).removeClass('active') | ||
20 | - $(this).addClass('active') | ||
21 | - $('#project_clone', scope).val($(this).data('clone')) | 18 | + $('a, button', scope).removeClass 'active' |
19 | + $(@).addClass 'active' | ||
20 | + $('#project_clone', scope).val $(@).data 'clone' |
@@ -1,8 +0,0 @@ | @@ -1,8 +0,0 @@ | ||
1 | -function backToMembers(){ | ||
2 | - $("#new_team_member").hide("slide", { direction: "right" }, 150, function(){ | ||
3 | - $("#team-table").show("slide", { direction: "left" }, 150, function() { | ||
4 | - $("#new_team_member").remove(); | ||
5 | - $(".add_new").show(); | ||
6 | - }); | ||
7 | - }); | ||
8 | -} |
1 | @import "bootstrap"; | 1 | @import "bootstrap"; |
2 | @import "bootstrap-responsive"; | 2 | @import "bootstrap-responsive"; |
3 | 3 | ||
4 | -/** GITLAB colors **/ | 4 | +/** GitLab colors **/ |
5 | $link_color:#3A89A3; | 5 | $link_color:#3A89A3; |
6 | $blue_link: #2fa0bb; | 6 | $blue_link: #2fa0bb; |
7 | $style_color: #474d57; | 7 | $style_color: #474d57; |
8 | $hover: #fdf5d9; | 8 | $hover: #fdf5d9; |
9 | 9 | ||
10 | -/** GITLAB Fonts **/ | 10 | +/** GitLab Fonts **/ |
11 | @font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); } | 11 | @font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); } |
12 | 12 | ||
13 | /** MIXINS **/ | 13 | /** MIXINS **/ |
@@ -113,9 +113,9 @@ $hover: #fdf5d9; | @@ -113,9 +113,9 @@ $hover: #fdf5d9; | ||
113 | @import "themes/ui_modern.scss"; | 113 | @import "themes/ui_modern.scss"; |
114 | 114 | ||
115 | /** | 115 | /** |
116 | - * Gitlab bootstrap. | 116 | + * GitLab bootstrap. |
117 | * Overrides some styles of twitter bootstrap. | 117 | * Overrides some styles of twitter bootstrap. |
118 | - * Also give some common classes for gitlab app | 118 | + * Also give some common classes for GitLab app |
119 | */ | 119 | */ |
120 | @import "gitlab_bootstrap/common.scss"; | 120 | @import "gitlab_bootstrap/common.scss"; |
121 | @import "gitlab_bootstrap/typography.scss"; | 121 | @import "gitlab_bootstrap/typography.scss"; |
@@ -11,15 +11,11 @@ class ApplicationController < ActionController::Base | @@ -11,15 +11,11 @@ class ApplicationController < ActionController::Base | ||
11 | helper_method :abilities, :can? | 11 | helper_method :abilities, :can? |
12 | 12 | ||
13 | rescue_from Gitlab::Gitolite::AccessDenied do |exception| | 13 | rescue_from Gitlab::Gitolite::AccessDenied do |exception| |
14 | - render "errors/gitolite", layout: "error" | ||
15 | - end | ||
16 | - | ||
17 | - rescue_from Gitlab::Gitolite::InvalidKey do |exception| | ||
18 | - render "errors/invalid_ssh_key", layout: "error" | 14 | + render "errors/gitolite", layout: "error", status: 500 |
19 | end | 15 | end |
20 | 16 | ||
21 | rescue_from Encoding::CompatibilityError do |exception| | 17 | rescue_from Encoding::CompatibilityError do |exception| |
22 | - render "errors/encoding", layout: "error", status: 404 | 18 | + render "errors/encoding", layout: "error", status: 500 |
23 | end | 19 | end |
24 | 20 | ||
25 | rescue_from ActiveRecord::RecordNotFound do |exception| | 21 | rescue_from ActiveRecord::RecordNotFound do |exception| |
@@ -64,19 +64,14 @@ class CommitsController < ApplicationController | @@ -64,19 +64,14 @@ class CommitsController < ApplicationController | ||
64 | @commit.to_patch, | 64 | @commit.to_patch, |
65 | type: "text/plain", | 65 | type: "text/plain", |
66 | disposition: 'attachment', | 66 | disposition: 'attachment', |
67 | - filename: (@commit.id.to_s + ".patch") | 67 | + filename: "#{@commit.id.patch}" |
68 | ) | 68 | ) |
69 | end | 69 | end |
70 | 70 | ||
71 | protected | 71 | protected |
72 | 72 | ||
73 | def load_refs | 73 | def load_refs |
74 | - if params[:ref].blank? | ||
75 | - @branch = params[:branch].blank? ? nil : params[:branch] | ||
76 | - @tag = params[:tag].blank? ? nil : params[:tag] | ||
77 | - @ref = @branch || @tag || @project.try(:default_branch) || 'master' | ||
78 | - else | ||
79 | - @ref = params[:ref] | ||
80 | - end | 74 | + @ref ||= params[:ref].presence || params[:branch].presence || params[:tag].presence |
75 | + @ref ||= @ref || @project.try(:default_branch) || 'master' | ||
81 | end | 76 | end |
82 | end | 77 | end |
@@ -37,7 +37,7 @@ class IssuesController < ApplicationController | @@ -37,7 +37,7 @@ class IssuesController < ApplicationController | ||
37 | end | 37 | end |
38 | 38 | ||
39 | def new | 39 | def new |
40 | - @issue = @project.issues.new | 40 | + @issue = @project.issues.new(params[:issue]) |
41 | respond_with(@issue) | 41 | respond_with(@issue) |
42 | end | 42 | end |
43 | 43 |
@@ -17,13 +17,12 @@ class TeamMembersController < ApplicationController | @@ -17,13 +17,12 @@ class TeamMembersController < ApplicationController | ||
17 | end | 17 | end |
18 | 18 | ||
19 | def create | 19 | def create |
20 | - @team_member = UsersProject.new(params[:team_member]) | ||
21 | - @team_member.project = project | ||
22 | - if @team_member.save | ||
23 | - redirect_to team_project_path(@project) | ||
24 | - else | ||
25 | - render "new" | ||
26 | - end | 20 | + @project.add_users_ids_to_team( |
21 | + params[:user_ids], | ||
22 | + params[:project_access] | ||
23 | + ) | ||
24 | + | ||
25 | + redirect_to team_project_path(@project) | ||
27 | end | 26 | end |
28 | 27 | ||
29 | def update | 28 | def update |
1 | module GitlabMarkdownHelper | 1 | module GitlabMarkdownHelper |
2 | - # Replaces references (i.e. @abc, #123, !456, ...) in the text with links to | ||
3 | - # the appropriate items in Gitlab. | ||
4 | - # | ||
5 | - # text - the source text | ||
6 | - # html_options - extra options for the reference links as given to link_to | ||
7 | - # | ||
8 | - # note: reference links will only be generated if @project is set | ||
9 | - # | ||
10 | - # see Gitlab::Markdown for details on the supported syntax | ||
11 | - def gfm(text, html_options = {}) | ||
12 | - return text if text.nil? | ||
13 | - return text if @project.nil? | ||
14 | - | ||
15 | - # Extract pre blocks so they are not altered | ||
16 | - # from http://github.github.com/github-flavored-markdown/ | ||
17 | - extractions = {} | ||
18 | - text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match| | ||
19 | - md5 = Digest::MD5.hexdigest(match) | ||
20 | - extractions[md5] = match | ||
21 | - "{gfm-extraction-#{md5}}" | ||
22 | - end | ||
23 | - | ||
24 | - # TODO: add popups with additional information | ||
25 | - | ||
26 | - parser = Gitlab::Markdown.new(@project, html_options) | ||
27 | - text = parser.parse(text) | ||
28 | - | ||
29 | - # Insert pre block extractions | ||
30 | - text.gsub!(/\{gfm-extraction-(\h{32})\}/) do | ||
31 | - extractions[$1] | ||
32 | - end | ||
33 | - | ||
34 | - sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class ) | ||
35 | - end | 2 | + include Gitlab::Markdown |
36 | 3 | ||
37 | # Use this in places where you would normally use link_to(gfm(...), ...). | 4 | # Use this in places where you would normally use link_to(gfm(...), ...). |
38 | # | 5 | # |
@@ -60,7 +27,7 @@ module GitlabMarkdownHelper | @@ -60,7 +27,7 @@ module GitlabMarkdownHelper | ||
60 | filter_html: true, | 27 | filter_html: true, |
61 | with_toc_data: true, | 28 | with_toc_data: true, |
62 | hard_wrap: true) | 29 | hard_wrap: true) |
63 | - @markdown ||= Redcarpet::Markdown.new(gitlab_renderer, | 30 | + @markdown = Redcarpet::Markdown.new(gitlab_renderer, |
64 | # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use | 31 | # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use |
65 | no_intra_emphasis: true, | 32 | no_intra_emphasis: true, |
66 | tables: true, | 33 | tables: true, |
@@ -111,18 +111,18 @@ class Notify < ActionMailer::Base | @@ -111,18 +111,18 @@ class Notify < ActionMailer::Base | ||
111 | # Examples | 111 | # Examples |
112 | # | 112 | # |
113 | # >> subject('Lorem ipsum') | 113 | # >> subject('Lorem ipsum') |
114 | - # => "gitlab | Lorem ipsum" | 114 | + # => "GitLab | Lorem ipsum" |
115 | # | 115 | # |
116 | # # Automatically inserts Project name when @project is set | 116 | # # Automatically inserts Project name when @project is set |
117 | # >> @project = Project.last | 117 | # >> @project = Project.last |
118 | # => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> | 118 | # => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> |
119 | # >> subject('Lorem ipsum') | 119 | # >> subject('Lorem ipsum') |
120 | - # => "gitlab | Lorem ipsum | Ruby on Rails" | 120 | + # => "GitLab | Lorem ipsum | Ruby on Rails" |
121 | # | 121 | # |
122 | # # Accepts multiple arguments | 122 | # # Accepts multiple arguments |
123 | # >> subject('Lorem ipsum', 'Dolor sit amet') | 123 | # >> subject('Lorem ipsum', 'Dolor sit amet') |
124 | - # => "gitlab | Lorem ipsum | Dolor sit amet" | 124 | + # => "GitLab | Lorem ipsum | Dolor sit amet" |
125 | def subject(*extra) | 125 | def subject(*extra) |
126 | - "gitlab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "") | 126 | + "GitLab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "") |
127 | end | 127 | end |
128 | end | 128 | end |
@@ -103,7 +103,7 @@ class Note < ActiveRecord::Base | @@ -103,7 +103,7 @@ class Note < ActiveRecord::Base | ||
103 | # Returns true if this is an upvote note, | 103 | # Returns true if this is an upvote note, |
104 | # otherwise false is returned | 104 | # otherwise false is returned |
105 | def upvote? | 105 | def upvote? |
106 | - note =~ /^\+1/ ? true : false | 106 | + note.start_with?('+1') || note.start_with?(':+1:') |
107 | end | 107 | end |
108 | end | 108 | end |
109 | # == Schema Information | 109 | # == Schema Information |
@@ -14,7 +14,7 @@ class UsersProject < ActiveRecord::Base | @@ -14,7 +14,7 @@ class UsersProject < ActiveRecord::Base | ||
14 | after_save :update_repository | 14 | after_save :update_repository |
15 | after_destroy :update_repository | 15 | after_destroy :update_repository |
16 | 16 | ||
17 | - validates_uniqueness_of :user_id, scope: [:project_id] | 17 | + validates_uniqueness_of :user_id, scope: [:project_id], message: "already exists in project" |
18 | validates_presence_of :user_id | 18 | validates_presence_of :user_id |
19 | validates_presence_of :project_id | 19 | validates_presence_of :project_id |
20 | 20 | ||
@@ -65,10 +65,10 @@ class UsersProject < ActiveRecord::Base | @@ -65,10 +65,10 @@ class UsersProject < ActiveRecord::Base | ||
65 | 65 | ||
66 | def self.access_roles | 66 | def self.access_roles |
67 | { | 67 | { |
68 | - "Guest" => GUEST, | ||
69 | - "Reporter" => REPORTER, | 68 | + "Guest" => GUEST, |
69 | + "Reporter" => REPORTER, | ||
70 | "Developer" => DEVELOPER, | 70 | "Developer" => DEVELOPER, |
71 | - "Master" => MASTER | 71 | + "Master" => MASTER |
72 | } | 72 | } |
73 | end | 73 | end |
74 | 74 |
@@ -30,7 +30,7 @@ | @@ -30,7 +30,7 @@ | ||
30 | %h5= link_to "API", help_api_path | 30 | %h5= link_to "API", help_api_path |
31 | 31 | ||
32 | %li | 32 | %li |
33 | - %h5= link_to "Gitlab Markdown", help_markdown_path | 33 | + %h5= link_to "GitLab Markdown", help_markdown_path |
34 | 34 | ||
35 | %li | 35 | %li |
36 | %h5= link_to "SSH keys", help_ssh_path | 36 | %h5= link_to "SSH keys", help_ssh_path |
1 | -%h3.page_title Gitlab Flavored Markdown | 1 | +%h3.page_title GitLab Flavored Markdown |
2 | .back_link | 2 | .back_link |
3 | = link_to help_path do | 3 | = link_to help_path do |
4 | ← to index | 4 | ← to index |
@@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
7 | .row | 7 | .row |
8 | .span8 | 8 | .span8 |
9 | %p | 9 | %p |
10 | - For Gitlab we developed something we call "Gitlab Flavored Markdown" (GFM). | 10 | + For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). |
11 | It extends the standard Markdown in a few significant ways adds some useful functionality. | 11 | It extends the standard Markdown in a few significant ways adds some useful functionality. |
12 | 12 | ||
13 | %p You can use GFM in: | 13 | %p You can use GFM in: |
@@ -20,6 +20,15 @@ | @@ -20,6 +20,15 @@ | ||
20 | %li milestones | 20 | %li milestones |
21 | %li wiki pages | 21 | %li wiki pages |
22 | 22 | ||
23 | + .span4 | ||
24 | + .alert.alert-info | ||
25 | + %p | ||
26 | + If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent | ||
27 | + %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax" | ||
28 | + at Daring Fireball. | ||
29 | + | ||
30 | +.row | ||
31 | + .span8 | ||
23 | %h3 Differences from traditional Markdown | 32 | %h3 Differences from traditional Markdown |
24 | 33 | ||
25 | %h4 Newlines | 34 | %h4 Newlines |
@@ -62,7 +71,30 @@ | @@ -62,7 +71,30 @@ | ||
62 | %p becomes | 71 | %p becomes |
63 | = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} | 72 | = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} |
64 | 73 | ||
65 | - %h4 Special Gitlab references | 74 | + %h4 Emoji |
75 | + | ||
76 | +.row | ||
77 | + .span8 | ||
78 | + :ruby | ||
79 | + puts markdown %Q{Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you: | ||
80 | + | ||
81 | + :exclamation: You can use emoji anywhere GFM is supported. :sunglasses: | ||
82 | + | ||
83 | + You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that. | ||
84 | + | ||
85 | + If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes. | ||
86 | + } | ||
87 | + | ||
88 | + .span4 | ||
89 | + .alert.alert-info | ||
90 | + %p | ||
91 | + Consult the | ||
92 | + %strong= link_to "Emoji Cheat Sheet", "http://www.emoji-cheat-sheet.com/" | ||
93 | + for a list of all supported emoji codes. | ||
94 | + | ||
95 | +.row | ||
96 | + .span8 | ||
97 | + %h4 Special GitLab references | ||
66 | 98 | ||
67 | %p | 99 | %p |
68 | GFM recognizes special references. | 100 | GFM recognizes special references. |
@@ -88,18 +120,10 @@ | @@ -88,18 +120,10 @@ | ||
88 | for commits | 120 | for commits |
89 | 121 | ||
90 | -# this example will only be shown if the user has a project with at least one issue | 122 | -# this example will only be shown if the user has a project with at least one issue |
91 | - - if project = current_user.projects.first | ||
92 | - - if issue = project.issues.first | ||
93 | - %p For example in your #{link_to project.name, project_path(project)} project something like | 123 | + - if @project = current_user.projects.first |
124 | + - if issue = @project.issues.first | ||
125 | + %p For example in your #{link_to @project.name, project_path(@project)} project, writing: | ||
94 | %pre= "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." | 126 | %pre= "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." |
95 | - %p becomes | 127 | + %p becomes: |
96 | = markdown "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." | 128 | = markdown "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." |
97 | - | ||
98 | - | ||
99 | - | ||
100 | - .span4.right | ||
101 | - .alert.alert-info | ||
102 | - %p | ||
103 | - If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent | ||
104 | - %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax" | ||
105 | - at Daring Fireball. | 129 | + - @project = nil # Prevent this from bubbling up to page title |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | %hr | 5 | %hr |
6 | 6 | ||
7 | %p.slead | 7 | %p.slead |
8 | - SSH key allows you to establish a secure connection between your computer and Gitlab | 8 | + SSH key allows you to establish a secure connection between your computer and GitLab |
9 | 9 | ||
10 | %p.slead | 10 | %p.slead |
11 | To generate a new SSH key just open your terminal and use code below. | 11 | To generate a new SSH key just open your terminal and use code below. |
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | \# Generating public/private rsa key pair... | 17 | \# Generating public/private rsa key pair... |
18 | 18 | ||
19 | %p.slead | 19 | %p.slead |
20 | - Next just use code below to dump your public key and add to GITLAB SSH Keys | 20 | + Next just use code below to dump your public key and add to GitLab SSH Keys |
21 | 21 | ||
22 | %pre.dark | 22 | %pre.dark |
23 | cat ~/.ssh/id_rsa.pub | 23 | cat ~/.ssh/id_rsa.pub |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | %hr | 5 | %hr |
6 | 6 | ||
7 | %p.slead | 7 | %p.slead |
8 | - Your Gitlab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member. | 8 | + Your GitLab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member. |
9 | %br | 9 | %br |
10 | System Hooks can be used for logging or change information in LDAP server. | 10 | System Hooks can be used for logging or change information in LDAP server. |
11 | %br | 11 | %br |
@@ -5,11 +5,11 @@ | @@ -5,11 +5,11 @@ | ||
5 | %hr | 5 | %hr |
6 | 6 | ||
7 | %p.slead | 7 | %p.slead |
8 | - Every Gitlab project can trigger a web server whenever the repo is pushed to. | 8 | + Every GitLab project can trigger a web server whenever the repo is pushed to. |
9 | %br | 9 | %br |
10 | Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. | 10 | Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. |
11 | %br | 11 | %br |
12 | - GITLAB will send POST request with commits information on every push. | 12 | + GitLab will send POST request with commits information on every push. |
13 | %h5 Hooks request example: | 13 | %h5 Hooks request example: |
14 | = render "hooks/data_ex" | 14 | = render "hooks/data_ex" |
15 | 15 |
@@ -24,7 +24,7 @@ | @@ -24,7 +24,7 @@ | ||
24 | git commit -am "My feature is ready" | 24 | git commit -am "My feature is ready" |
25 | 25 | ||
26 | %li | 26 | %li |
27 | - %p Push your branch to gitlabhq | 27 | + %p Push your branch to GitLab |
28 | .bash | 28 | .bash |
29 | %pre.dark | 29 | %pre.dark |
30 | git push origin $feature_name | 30 | git push origin $feature_name |
@@ -32,7 +32,7 @@ | @@ -32,7 +32,7 @@ | ||
32 | :timestamp => "2012-01-03T23:36:29+02:00", | 32 | :timestamp => "2012-01-03T23:36:29+02:00", |
33 | :url => "http://localhost/diaspora/commits/da1560886d...", | 33 | :url => "http://localhost/diaspora/commits/da1560886d...", |
34 | :author => { | 34 | :author => { |
35 | - :name => "gitlab dev user", | 35 | + :name => "GitLab dev user", |
36 | :email => "gitlabdev@dv6700.(none)" | 36 | :email => "gitlabdev@dv6700.(none)" |
37 | } | 37 | } |
38 | } | 38 | } |
@@ -38,7 +38,7 @@ | @@ -38,7 +38,7 @@ | ||
38 | = f.label :description, "Details" | 38 | = f.label :description, "Details" |
39 | .input | 39 | .input |
40 | = f.text_area :description, maxlength: 2000, class: "xxlarge", rows: 14 | 40 | = f.text_area :description, maxlength: 2000, class: "xxlarge", rows: 14 |
41 | - %p.hint Issues are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}. | 41 | + %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. |
42 | 42 | ||
43 | 43 | ||
44 | .actions | 44 | .actions |
@@ -6,7 +6,7 @@ | @@ -6,7 +6,7 @@ | ||
6 | .right | 6 | .right |
7 | .span5 | 7 | .span5 |
8 | - if can? current_user, :write_issue, @project | 8 | - if can? current_user, :write_issue, @project |
9 | - = link_to new_project_issue_path(@project), class: "right btn", title: "New Issue", remote: true do | 9 | + = link_to new_project_issue_path(@project), class: "right btn", title: "New Issue", remote: true, id: "new_issue_link" do |
10 | %i.icon-plus | 10 | %i.icon-plus |
11 | New Issue | 11 | New Issue |
12 | = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do | 12 | = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do |
@@ -3,8 +3,8 @@ | @@ -3,8 +3,8 @@ | ||
3 | = link_to "Add new", new_key_path, class: "btn right" | 3 | = link_to "Add new", new_key_path, class: "btn right" |
4 | 4 | ||
5 | %hr | 5 | %hr |
6 | -%p.slead | ||
7 | - SSH key allows you to establish a secure connection between your computer and Gitlab | 6 | +%p.slead |
7 | + SSH key allows you to establish a secure connection between your computer and GitLab | ||
8 | 8 | ||
9 | 9 | ||
10 | %table#keys-table | 10 | %table#keys-table |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | %th | 15 | %th |
16 | - @keys.each do |key| | 16 | - @keys.each do |key| |
17 | = render(partial: 'show', locals: {key: key}) | 17 | = render(partial: 'show', locals: {key: key}) |
18 | - - if @keys.blank? | 18 | + - if @keys.blank? |
19 | %tr | 19 | %tr |
20 | %td{colspan: 3} | 20 | %td{colspan: 3} |
21 | %h3.nothing_here_message There are no SSH keys with access to your account. | 21 | %h3.nothing_here_message There are no SSH keys with access to your account. |
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | %head | 2 | %head |
3 | %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} | 3 | %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} |
4 | %title | 4 | %title |
5 | - gitlabhq | 5 | + GitLab |
6 | :css | 6 | :css |
7 | .header h1 {color: #BBBBBB !important; font: bold 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;} | 7 | .header h1 {color: #BBBBBB !important; font: bold 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;} |
8 | .header p {color: #c6c6c6; font: normal 12px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 18px;} | 8 | .header p {color: #c6c6c6; font: normal 12px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 18px;} |
@@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
21 | \ | 21 | \ |
22 | %td{align: "left", style: "padding: 18px 0 10px;", width: "580"} | 22 | %td{align: "left", style: "padding: 18px 0 10px;", width: "580"} |
23 | %h1{style: "color: #BBBBBB; font: normal 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;"} | 23 | %h1{style: "color: #BBBBBB; font: normal 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;"} |
24 | - gitlab | 24 | + GITLAB |
25 | - if @project | 25 | - if @project |
26 | | #{@project.name} | 26 | | #{@project.name} |
27 | %table{align: "center", bgcolor: "#fff", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; background: #fff;", width: "600"} | 27 | %table{align: "center", bgcolor: "#fff", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; background: #fff;", width: "600"} |
@@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
22 | = f.label :description, "Description", class: "control-label" | 22 | = f.label :description, "Description", class: "control-label" |
23 | .controls | 23 | .controls |
24 | = f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10 | 24 | = f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10 |
25 | - %p.hint Milestones are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}. | 25 | + %p.hint Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. |
26 | .span6 | 26 | .span6 |
27 | .control-group | 27 | .control-group |
28 | = f.label :due_date, "Due Date", class: "control-label" | 28 | = f.label :due_date, "Due Date", class: "control-label" |
@@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
11 | = f.text_area :note, size: 255, class: 'note-text' | 11 | = f.text_area :note, size: 255, class: 'note-text' |
12 | #preview-note.preview_note.hide | 12 | #preview-note.preview_note.hide |
13 | .hint | 13 | .hint |
14 | - .right Comments are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}. | 14 | + .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. |
15 | .clearfix | 15 | .clearfix |
16 | 16 | ||
17 | .row.note_advanced_opts.hide | 17 | .row.note_advanced_opts.hide |
@@ -6,7 +6,7 @@ | @@ -6,7 +6,7 @@ | ||
6 | %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} | 6 | %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} |
7 | Hi #{@user['name']}! | 7 | Hi #{@user['name']}! |
8 | %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} | 8 | %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} |
9 | - Administrator created account for you. Now you are a member of company gitlab application. | 9 | + Administrator created account for you. Now you are a member of company GitLab application. |
10 | %td{style: "font-size: 1px; line-height: 1px;", width: "21"} | 10 | %td{style: "font-size: 1px; line-height: 1px;", width: "21"} |
11 | %tr | 11 | %tr |
12 | %td{style: "font-size: 1px; line-height: 1px;", width: "21"} | 12 | %td{style: "font-size: 1px; line-height: 1px;", width: "21"} |
1 | -%table | ||
2 | - %thead | ||
3 | - %tr | ||
4 | - %th User | ||
5 | - %th Permissions | ||
6 | - %tbody | ||
7 | - - @project.users_projects.each do |up| | ||
8 | - = render(partial: 'team_members/show', locals: {member: up}) | 1 | +- grouper_project_members(@project).each do |access, members| |
2 | + %table | ||
3 | + %thead | ||
4 | + %tr | ||
5 | + %th.span7 | ||
6 | + = Project.access_options.key(access).pluralize | ||
7 | + %th | ||
8 | + %tbody | ||
9 | + - members.each do |up| | ||
10 | + = render(partial: 'team_members/show', locals: {member: up}) | ||
9 | 11 | ||
10 | 12 | ||
11 | :javascript | 13 | :javascript |
1 | -%h3= "New Team member" | 1 | +%h3.page_title |
2 | + = "New Team member(s)" | ||
2 | %hr | 3 | %hr |
3 | = form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f| | 4 | = form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f| |
4 | -if @team_member.errors.any? | 5 | -if @team_member.errors.any? |
@@ -7,27 +8,23 @@ | @@ -7,27 +8,23 @@ | ||
7 | - @team_member.errors.full_messages.each do |msg| | 8 | - @team_member.errors.full_messages.each do |msg| |
8 | %li= msg | 9 | %li= msg |
9 | 10 | ||
11 | + %h6 1. Choose people you want in the team | ||
10 | .clearfix | 12 | .clearfix |
11 | - = f.label :user_id, "Name" | ||
12 | - .input= f.select(:user_id, User.not_in_project(@project).all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, { style: "width:300px" }) | 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 }) | ||
13 | 15 | ||
14 | 16 | ||
17 | + %h6 2. Set access level for them | ||
15 | .clearfix | 18 | .clearfix |
16 | = f.label :project_access, "Project Access" | 19 | = f.label :project_access, "Project Access" |
17 | - .input= f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select" | 20 | + .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select" |
18 | 21 | ||
19 | 22 | ||
20 | .actions | 23 | .actions |
21 | - = f.submit 'Save', class: "btn primary" | ||
22 | - = link_to "Cancel", team_project_path(@project), class: "btn" | 24 | + = f.submit 'Save', class: "btn save-btn" |
25 | + = link_to "Cancel", team_project_path(@project), class: "btn cancel-btn" | ||
23 | 26 | ||
24 | -:css | ||
25 | - form select { | ||
26 | - width:300px; | ||
27 | - } | ||
28 | 27 | ||
29 | :javascript | 28 | :javascript |
30 | - $('select#team_member_user_id').chosen(); | ||
31 | - $('select#team_member_project_access').chosen(); | ||
32 | - //$('select#team_member_repo_access').chosen(); | ||
33 | - //$('select#team_member_project_access').chosen(); | 29 | + $('select#user_ids').chosen(); |
30 | + $('select#project_access').chosen(); |
@@ -2,12 +2,6 @@ | @@ -2,12 +2,6 @@ | ||
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 |
5 | - .right | ||
6 | - - if @project.owner == user | ||
7 | - %span.label Project Owner | ||
8 | - - if user.blocked | ||
9 | - %span.label Blocked | ||
10 | - | ||
11 | = 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 |
12 | = image_tag gravatar_icon(user.email, 40), class: "avatar s32" | 6 | = image_tag gravatar_icon(user.email, 40), class: "avatar s32" |
13 | = 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 |
@@ -16,5 +10,11 @@ | @@ -16,5 +10,11 @@ | ||
16 | %div.cgray= user.email | 10 | %div.cgray= user.email |
17 | 11 | ||
18 | %td | 12 | %td |
19 | - = 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", disabled: !allow_admin | 13 | + .right |
14 | + - if @project.owner == user | ||
15 | + %span.btn.disabled.success Project Owner | ||
16 | + - if user.blocked | ||
17 | + %span.btn.disabled.blocked Blocked | ||
18 | + - if allow_admin | ||
19 | + = 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" |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | .middle_box_content | 14 | .middle_box_content |
15 | .input | 15 | .input |
16 | %span.cgray | 16 | %span.cgray |
17 | - Wiki content is parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}. | 17 | + Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. |
18 | To link to a (new) page you can just type | 18 | To link to a (new) page you can just type |
19 | %code [Link Title](page-slug) | 19 | %code [Link Title](page-slug) |
20 | \. | 20 | \. |
1 | -# Gitlab API | 1 | +# GitLab API |
2 | 2 | ||
3 | All API requests require authentication. You need to pass a `private_token` parameter to authenticate. You can find or reset your private token in your profile. | 3 | All API requests require authentication. You need to pass a `private_token` parameter to authenticate. You can find or reset your private token in your profile. |
4 | 4 | ||
@@ -10,7 +10,7 @@ If no, or an invalid, `private_token` is provided then an error message will be | @@ -10,7 +10,7 @@ If no, or an invalid, `private_token` is provided then an error message will be | ||
10 | } | 10 | } |
11 | ``` | 11 | ``` |
12 | 12 | ||
13 | -API requests should be prefixed with `api` and the API version. The API version is equal to the Gitlab major version number, which is defined in `lib/api.rb`. | 13 | +API requests should be prefixed with `api` and the API version. The API version is equal to the GitLab major version number, which is defined in `lib/api.rb`. |
14 | 14 | ||
15 | Example of a valid API request: | 15 | Example of a valid API request: |
16 | 16 |
@@ -102,6 +102,12 @@ Parameters: | @@ -102,6 +102,12 @@ Parameters: | ||
102 | + `name` (required) - new project name | 102 | + `name` (required) - new project name |
103 | + `code` (optional) - new project code, uses project name if not set | 103 | + `code` (optional) - new project code, uses project name if not set |
104 | + `path` (optional) - new project path, uses project name if not set | 104 | + `path` (optional) - new project path, uses project name if not set |
105 | ++ `description (optional) - short project description | ||
106 | ++ `default_branch` (optional) - 'master' by default | ||
107 | ++ `issues_enabled` (optional) - enabled by default | ||
108 | ++ `wall_enabled` (optional) - enabled by default | ||
109 | ++ `merge_requests_enabled` (optional) - enabled by default | ||
110 | ++ `wiki_enabled` (optional) - enabled by default | ||
105 | 111 | ||
106 | Will return created project with status `201 Created` on success, or `404 Not | 112 | Will return created project with status `201 Created` on success, or `404 Not |
107 | found` on fail. | 113 | found` on fail. |
@@ -167,7 +167,7 @@ and ensure you have followed all of the above steps carefully. | @@ -167,7 +167,7 @@ and ensure you have followed all of the above steps carefully. | ||
167 | # Login to MySQL | 167 | # Login to MySQL |
168 | $ mysql -u root -p | 168 | $ mysql -u root -p |
169 | 169 | ||
170 | - # Create the gitlabhq production database | 170 | + # Create the GitLab production database |
171 | mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; | 171 | mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; |
172 | 172 | ||
173 | # Create the MySQL User change $password to a real password | 173 | # Create the MySQL User change $password to a real password |
@@ -4,7 +4,7 @@ Feature: Issues | @@ -4,7 +4,7 @@ Feature: Issues | ||
4 | And I own project "Shop" | 4 | And I own project "Shop" |
5 | And project "Shop" have "Release 0.4" open issue | 5 | And project "Shop" have "Release 0.4" open issue |
6 | And project "Shop" have "Release 0.3" closed issue | 6 | And project "Shop" have "Release 0.3" closed issue |
7 | - And I visit project "Shop" issues page | 7 | + And I visit project "Shop" issues page |
8 | 8 | ||
9 | Scenario: I should see open issues | 9 | Scenario: I should see open issues |
10 | Given I should see "Release 0.4" in issues | 10 | Given I should see "Release 0.4" in issues |
@@ -36,3 +36,47 @@ Feature: Issues | @@ -36,3 +36,47 @@ Feature: Issues | ||
36 | Given I visit issue page "Release 0.4" | 36 | Given I visit issue page "Release 0.4" |
37 | And I leave a comment like "XML attached" | 37 | And I leave a comment like "XML attached" |
38 | Then I should see comment "XML attached" | 38 | Then I should see comment "XML attached" |
39 | + | ||
40 | + @javascript | ||
41 | + Scenario: I search issue | ||
42 | + Given I fill in issue search with "Release" | ||
43 | + Then I should see "Release 0.4" in issues | ||
44 | + And I should not see "Release 0.3" in issues | ||
45 | + | ||
46 | + @javascript | ||
47 | + Scenario: I search issue that not exist | ||
48 | + Given I fill in issue search with "Bug" | ||
49 | + Then I should not see "Release 0.4" in issues | ||
50 | + And I should not see "Release 0.3" in issues | ||
51 | + | ||
52 | + | ||
53 | + @javascript | ||
54 | + Scenario: I search all issues | ||
55 | + Given I click link "All" | ||
56 | + And I fill in issue search with "0.3" | ||
57 | + Then I should see "Release 0.3" in issues | ||
58 | + And I should not see "Release 0.4" in issues | ||
59 | + | ||
60 | + @javascript | ||
61 | + Scenario: I clear search | ||
62 | + Given I click link "All" | ||
63 | + And I fill in issue search with "Something" | ||
64 | + And I fill in issue search with "" | ||
65 | + Then I should see "Release 0.4" in issues | ||
66 | + And I should see "Release 0.3" in issues | ||
67 | + | ||
68 | + @javascript | ||
69 | + Scenario: I create Issue with pre-selected milestone | ||
70 | + Given project "Shop" has milestone "v2.2" | ||
71 | + And project "Shop" has milestone "v3.0" | ||
72 | + And I visit project "Shop" issues page | ||
73 | + When I select milestone "v3.0" | ||
74 | + And I click link "New Issue" | ||
75 | + Then I should see selected milestone with title "v3.0" | ||
76 | + | ||
77 | + @javascript | ||
78 | + Scenario: I create Issue with pre-selected assignee | ||
79 | + When I select first assignee from "Shop" project | ||
80 | + And I click link "New Issue" | ||
81 | + Then I should see first assignee from "Shop" as selected assignee | ||
82 | + |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +include LoginHelpers | ||
2 | + | ||
3 | +Given /^I signin as a user$/ do | ||
4 | + login_as :user | ||
5 | +end | ||
6 | + | ||
7 | +When /^I click link "(.*?)"$/ do |link| | ||
8 | + click_link link | ||
9 | +end | ||
10 | + | ||
11 | +When /^I click button "(.*?)"$/ do |button| | ||
12 | + click_button button | ||
13 | +end | ||
14 | + | ||
15 | +When /^I fill in "(.*?)" with "(.*?)"$/ do |field, value| | ||
16 | + fill_in field, :with => value | ||
17 | +end | ||
18 | + | ||
19 | +Given /^show me page$/ do | ||
20 | + save_and_open_page | ||
21 | +end |
1 | -Given /^I visit dashboard page$/ do | ||
2 | - visit dashboard_path | ||
3 | -end | ||
4 | - | ||
5 | Then /^I should see "(.*?)" link$/ do |arg1| | 1 | Then /^I should see "(.*?)" link$/ do |arg1| |
6 | page.should have_link(arg1) | 2 | page.should have_link(arg1) |
7 | end | 3 | end |
@@ -51,10 +47,10 @@ Then /^I click "(.*?)" link$/ do |arg1| | @@ -51,10 +47,10 @@ Then /^I click "(.*?)" link$/ do |arg1| | ||
51 | end | 47 | end |
52 | 48 | ||
53 | Then /^I see prefilled new Merge Request page$/ do | 49 | Then /^I see prefilled new Merge Request page$/ do |
54 | - current_path.should == new_project_merge_request_path(@project) | ||
55 | - find("#merge_request_source_branch").value.should == "new_design" | ||
56 | - find("#merge_request_target_branch").value.should == "master" | ||
57 | - find("#merge_request_title").value.should == "New Design" | 50 | + current_path.should == new_project_merge_request_path(@project) |
51 | + find("#merge_request_source_branch").value.should == "new_design" | ||
52 | + find("#merge_request_target_branch").value.should == "master" | ||
53 | + find("#merge_request_title").value.should == "New Design" | ||
58 | end | 54 | end |
59 | 55 | ||
60 | Given /^I visit dashboard search page$/ do | 56 | Given /^I visit dashboard search page$/ do |
@@ -66,10 +62,6 @@ Given /^I search for "(.*?)"$/ do |arg1| | @@ -66,10 +62,6 @@ Given /^I search for "(.*?)"$/ do |arg1| | ||
66 | click_button "Search" | 62 | click_button "Search" |
67 | end | 63 | end |
68 | 64 | ||
69 | -Given /^I visit dashboard issues page$/ do | ||
70 | - visit dashboard_issues_path | ||
71 | -end | ||
72 | - | ||
73 | Then /^I should see issues assigned to me$/ do | 65 | Then /^I should see issues assigned to me$/ do |
74 | issues = @user.issues | 66 | issues = @user.issues |
75 | issues.each do |issue| | 67 | issues.each do |issue| |
@@ -78,10 +70,6 @@ Then /^I should see issues assigned to me$/ do | @@ -78,10 +70,6 @@ Then /^I should see issues assigned to me$/ do | ||
78 | end | 70 | end |
79 | end | 71 | end |
80 | 72 | ||
81 | -Given /^I visit dashboard merge requests page$/ do | ||
82 | - visit dashboard_merge_requests_path | ||
83 | -end | ||
84 | - | ||
85 | Then /^I should see my merge requests$/ do | 73 | Then /^I should see my merge requests$/ do |
86 | merge_requests = @user.merge_requests | 74 | merge_requests = @user.merge_requests |
87 | merge_requests.each do |mr| | 75 | merge_requests.each do |mr| |
1 | -Given /^I visit profile page$/ do | ||
2 | - visit profile_path | ||
3 | -end | ||
4 | - | ||
5 | Then /^I should see my profile info$/ do | 1 | Then /^I should see my profile info$/ do |
6 | page.should have_content "Profile" | 2 | page.should have_content "Profile" |
7 | page.should have_content @user.name | 3 | page.should have_content @user.name |
8 | page.should have_content @user.email | 4 | page.should have_content @user.email |
9 | end | 5 | end |
10 | 6 | ||
11 | -Given /^I visit profile password page$/ do | ||
12 | - visit profile_password_path | ||
13 | -end | ||
14 | - | ||
15 | Then /^I change my password$/ do | 7 | Then /^I change my password$/ do |
16 | fill_in "user_password", :with => "222333" | 8 | fill_in "user_password", :with => "222333" |
17 | fill_in "user_password_confirmation", :with => "222333" | 9 | fill_in "user_password_confirmation", :with => "222333" |
@@ -22,10 +14,6 @@ Then /^I should be redirected to sign in page$/ do | @@ -22,10 +14,6 @@ Then /^I should be redirected to sign in page$/ do | ||
22 | current_path.should == new_user_session_path | 14 | current_path.should == new_user_session_path |
23 | end | 15 | end |
24 | 16 | ||
25 | -Given /^I visit profile token page$/ do | ||
26 | - visit profile_token_path | ||
27 | -end | ||
28 | - | ||
29 | Then /^I reset my token$/ do | 17 | Then /^I reset my token$/ do |
30 | @old_token = @user.private_token | 18 | @old_token = @user.private_token |
31 | click_button "Reset" | 19 | click_button "Reset" |
1 | -Given /^I visit project source page$/ do | ||
2 | - visit tree_project_ref_path(@project, @project.root_ref) | ||
3 | -end | ||
4 | - | ||
5 | Then /^I should see files from repository$/ do | 1 | Then /^I should see files from repository$/ do |
6 | page.should have_content("app") | 2 | page.should have_content("app") |
7 | page.should have_content("History") | 3 | page.should have_content("History") |
8 | page.should have_content("Gemfile") | 4 | page.should have_content("Gemfile") |
9 | end | 5 | end |
10 | 6 | ||
11 | -Given /^I visit project source page for "(.*?)"$/ do |arg1| | ||
12 | - visit tree_project_ref_path(@project, arg1) | ||
13 | -end | ||
14 | - | ||
15 | Then /^I should see files from repository for "(.*?)"$/ do |arg1| | 7 | Then /^I should see files from repository for "(.*?)"$/ do |arg1| |
16 | current_path.should == tree_project_ref_path(@project, arg1) | 8 | current_path.should == tree_project_ref_path(@project, arg1) |
17 | page.should have_content("app") | 9 | page.should have_content("app") |
@@ -31,10 +23,6 @@ Given /^I click on raw button$/ do | @@ -31,10 +23,6 @@ Given /^I click on raw button$/ do | ||
31 | click_link "raw" | 23 | click_link "raw" |
32 | end | 24 | end |
33 | 25 | ||
34 | -Given /^I visit blob file from repo$/ do | ||
35 | - visit tree_project_ref_path(@project, ValidCommit::ID, :path => ValidCommit::BLOB_FILE_PATH) | ||
36 | -end | ||
37 | - | ||
38 | Then /^I should see raw file content$/ do | 26 | Then /^I should see raw file content$/ do |
39 | page.source.should == ValidCommit::BLOB_FILE | 27 | page.source.should == ValidCommit::BLOB_FILE |
40 | end | 28 | end |
1 | -Given /^I visit project commits page$/ do | ||
2 | - visit project_commits_path(@project) | ||
3 | -end | ||
4 | - | ||
5 | Then /^I see project commits$/ do | 1 | Then /^I see project commits$/ do |
6 | current_path.should == project_commits_path(@project) | 2 | current_path.should == project_commits_path(@project) |
7 | 3 | ||
@@ -23,19 +19,11 @@ Then /^I see commits atom feed$/ do | @@ -23,19 +19,11 @@ Then /^I see commits atom feed$/ do | ||
23 | page.body.should have_selector("entry summary", :text => commit.description) | 19 | page.body.should have_selector("entry summary", :text => commit.description) |
24 | end | 20 | end |
25 | 21 | ||
26 | -Given /^I click on commit link$/ do | ||
27 | - visit project_commit_path(@project, ValidCommit::ID) | ||
28 | -end | ||
29 | - | ||
30 | Then /^I see commit info$/ do | 22 | Then /^I see commit info$/ do |
31 | page.should have_content ValidCommit::MESSAGE | 23 | page.should have_content ValidCommit::MESSAGE |
32 | page.should have_content "Showing 1 changed file" | 24 | page.should have_content "Showing 1 changed file" |
33 | end | 25 | end |
34 | 26 | ||
35 | -Given /^I visit compare refs page$/ do | ||
36 | - visit compare_project_commits_path(@project) | ||
37 | -end | ||
38 | - | ||
39 | Given /^I fill compare fields with refs$/ do | 27 | Given /^I fill compare fields with refs$/ do |
40 | fill_in "from", :with => "master" | 28 | fill_in "from", :with => "master" |
41 | fill_in "to", :with => "stable" | 29 | fill_in "to", :with => "stable" |
@@ -48,18 +36,6 @@ Given /^I see compared refs$/ do | @@ -48,18 +36,6 @@ Given /^I see compared refs$/ do | ||
48 | page.should have_content "Showing 73 changed files" | 36 | page.should have_content "Showing 73 changed files" |
49 | end | 37 | end |
50 | 38 | ||
51 | -Given /^I visit project branches page$/ do | ||
52 | - visit branches_project_repository_path(@project) | ||
53 | -end | ||
54 | - | ||
55 | -Given /^I visit project commit page$/ do | ||
56 | - visit project_commit_path(@project, ValidCommit::ID) | ||
57 | -end | ||
58 | - | ||
59 | -Given /^I visit project tags page$/ do | ||
60 | - visit tags_project_repository_path(@project) | ||
61 | -end | ||
62 | - | ||
63 | Then /^I should see "(.*?)" recent branches list$/ do |arg1| | 39 | Then /^I should see "(.*?)" recent branches list$/ do |arg1| |
64 | page.should have_content("Branches") | 40 | page.should have_content("Branches") |
65 | page.should have_content("master") | 41 | page.should have_content("master") |
@@ -76,7 +52,7 @@ Then /^I should see "(.*?)" all tags list$/ do |arg1| | @@ -76,7 +52,7 @@ Then /^I should see "(.*?)" all tags list$/ do |arg1| | ||
76 | end | 52 | end |
77 | 53 | ||
78 | Then /^I should see "(.*?)" protected branches list$/ do |arg1| | 54 | Then /^I should see "(.*?)" protected branches list$/ do |arg1| |
79 | - within "table" do | 55 | + within "table" do |
80 | page.should have_content "stable" | 56 | page.should have_content "stable" |
81 | page.should_not have_content "master" | 57 | page.should_not have_content "master" |
82 | end | 58 | end |
@@ -8,16 +8,12 @@ Given /^project "(.*?)" have "(.*?)" closed issue$/ do |arg1, arg2| | @@ -8,16 +8,12 @@ Given /^project "(.*?)" have "(.*?)" closed issue$/ do |arg1, arg2| | ||
8 | Factory.create(:issue, :title => arg2, :project => project, :author => project.users.first, :closed => true) | 8 | Factory.create(:issue, :title => arg2, :project => project, :author => project.users.first, :closed => true) |
9 | end | 9 | end |
10 | 10 | ||
11 | -Given /^I visit project "(.*?)" issues page$/ do |arg1| | ||
12 | - visit project_issues_path(Project.find_by_name(arg1)) | ||
13 | -end | ||
14 | - | ||
15 | Given /^I should see "(.*?)" in issues$/ do |arg1| | 11 | Given /^I should see "(.*?)" in issues$/ do |arg1| |
16 | - page.should have_content arg1 | 12 | + page.should have_content arg1 |
17 | end | 13 | end |
18 | 14 | ||
19 | Given /^I should not see "(.*?)" in issues$/ do |arg1| | 15 | Given /^I should not see "(.*?)" in issues$/ do |arg1| |
20 | - page.should_not have_content arg1 | 16 | + page.should_not have_content arg1 |
21 | end | 17 | end |
22 | 18 | ||
23 | Then /^I should see issue "(.*?)"$/ do |arg1| | 19 | Then /^I should see issue "(.*?)"$/ do |arg1| |
@@ -27,11 +23,6 @@ Then /^I should see issue "(.*?)"$/ do |arg1| | @@ -27,11 +23,6 @@ Then /^I should see issue "(.*?)"$/ do |arg1| | ||
27 | page.should have_content issue.project.name | 23 | page.should have_content issue.project.name |
28 | end | 24 | end |
29 | 25 | ||
30 | -Given /^I visit issue page "(.*?)"$/ do |arg1| | ||
31 | - issue = Issue.find_by_title(arg1) | ||
32 | - visit project_issue_path(issue.project, issue) | ||
33 | -end | ||
34 | - | ||
35 | Given /^I submit new issue "(.*?)"$/ do |arg1| | 26 | Given /^I submit new issue "(.*?)"$/ do |arg1| |
36 | fill_in "issue_title", with: arg1 | 27 | fill_in "issue_title", with: arg1 |
37 | click_button "Submit new issue" | 28 | click_button "Submit new issue" |
@@ -51,7 +42,40 @@ Given /^I visit project "(.*?)" labels page$/ do |arg1| | @@ -51,7 +42,40 @@ Given /^I visit project "(.*?)" labels page$/ do |arg1| | ||
51 | end | 42 | end |
52 | 43 | ||
53 | Then /^I should see label "(.*?)"$/ do |arg1| | 44 | Then /^I should see label "(.*?)"$/ do |arg1| |
54 | - within ".labels-table" do | 45 | + within ".labels-table" do |
55 | page.should have_content arg1 | 46 | page.should have_content arg1 |
56 | end | 47 | end |
57 | end | 48 | end |
49 | + | ||
50 | +Given /^I fill in issue search with "(.*?)"$/ do |arg1| | ||
51 | + # Because fill_in, with: "" triggers nothing | ||
52 | + # we need to trigger a keyup event | ||
53 | + if arg1 == '' | ||
54 | + page.execute_script("$('.issue_search').val('').keyup();"); | ||
55 | + end | ||
56 | + fill_in 'issue_search', with: arg1 | ||
57 | +end | ||
58 | + | ||
59 | +When /^I select milestone "(.*?)"$/ do |milestone_title| | ||
60 | + select milestone_title, from: "milestone_id" | ||
61 | +end | ||
62 | + | ||
63 | +Then /^I should see selected milestone with title "(.*?)"$/ do |milestone_title| | ||
64 | + issues_milestone_selector = "#issue_milestone_id_chzn/a" | ||
65 | + wait_until{ page.has_content?("Details") } | ||
66 | + page.find(issues_milestone_selector).should have_content(milestone_title) | ||
67 | +end | ||
68 | + | ||
69 | +When /^I select first assignee from "(.*?)" project$/ do |project_name| | ||
70 | + project = Project.find_by_name project_name | ||
71 | + first_assignee = project.users.first | ||
72 | + select first_assignee.name, from: "assignee_id" | ||
73 | +end | ||
74 | + | ||
75 | +Then /^I should see first assignee from "(.*?)" as selected assignee$/ do |project_name| | ||
76 | + issues_assignee_selector = "#issue_assignee_id_chzn/a" | ||
77 | + wait_until{ page.has_content?("Details") } | ||
78 | + project = Project.find_by_name project_name | ||
79 | + assignee_name = project.users.first.name | ||
80 | + page.find(issues_assignee_selector).should have_content(assignee_name) | ||
81 | +end |
@@ -8,21 +8,17 @@ Given /^project "(.*?)" have "(.*?)" closed merge request$/ do |arg1, arg2| | @@ -8,21 +8,17 @@ Given /^project "(.*?)" have "(.*?)" closed merge request$/ do |arg1, arg2| | ||
8 | Factory.create(:merge_request, :title => arg2, :project => project, :author => project.users.first, :closed => true) | 8 | Factory.create(:merge_request, :title => arg2, :project => project, :author => project.users.first, :closed => true) |
9 | end | 9 | end |
10 | 10 | ||
11 | -Given /^I visit project "(.*?)" merge requests page$/ do |arg1| | ||
12 | - visit project_merge_requests_path(Project.find_by_name(arg1)) | ||
13 | -end | ||
14 | - | ||
15 | Then /^I should see "(.*?)" in merge requests$/ do |arg1| | 11 | Then /^I should see "(.*?)" in merge requests$/ do |arg1| |
16 | - page.should have_content arg1 | 12 | + page.should have_content arg1 |
17 | end | 13 | end |
18 | 14 | ||
19 | Then /^I should not see "(.*?)" in merge requests$/ do |arg1| | 15 | Then /^I should not see "(.*?)" in merge requests$/ do |arg1| |
20 | - page.should_not have_content arg1 | 16 | + page.should_not have_content arg1 |
21 | end | 17 | end |
22 | 18 | ||
23 | Then /^I should see merge request "(.*?)"$/ do |arg1| | 19 | Then /^I should see merge request "(.*?)"$/ do |arg1| |
24 | merge_request = MergeRequest.find_by_title(arg1) | 20 | merge_request = MergeRequest.find_by_title(arg1) |
25 | - page.should have_content(merge_request.title[0..10]) | 21 | + page.should have_content(merge_request.title[0..10]) |
26 | page.should have_content(merge_request.target_branch) | 22 | page.should have_content(merge_request.target_branch) |
27 | page.should have_content(merge_request.source_branch) | 23 | page.should have_content(merge_request.source_branch) |
28 | end | 24 | end |
@@ -34,11 +30,6 @@ Given /^I submit new merge request "(.*?)"$/ do |arg1| | @@ -34,11 +30,6 @@ Given /^I submit new merge request "(.*?)"$/ do |arg1| | ||
34 | click_button "Save" | 30 | click_button "Save" |
35 | end | 31 | end |
36 | 32 | ||
37 | -Given /^I visit merge request page "(.*?)"$/ do |arg1| | ||
38 | - mr = MergeRequest.find_by_title(arg1) | ||
39 | - visit project_merge_request_path(mr.project, mr) | ||
40 | -end | ||
41 | - | ||
42 | Then /^I should see closed merge request "(.*?)"$/ do |arg1| | 33 | Then /^I should see closed merge request "(.*?)"$/ do |arg1| |
43 | mr = MergeRequest.find_by_title(arg1) | 34 | mr = MergeRequest.find_by_title(arg1) |
44 | mr.closed.should be_true | 35 | mr.closed.should be_true |
@@ -12,11 +12,6 @@ Given /^project "(.*?)" has milestone "(.*?)"$/ do |arg1, arg2| | @@ -12,11 +12,6 @@ Given /^project "(.*?)" has milestone "(.*?)"$/ do |arg1, arg2| | ||
12 | end | 12 | end |
13 | end | 13 | end |
14 | 14 | ||
15 | -Given /^I visit project "(.*?)" milestones page$/ do |arg1| | ||
16 | - @project = Project.find_by_name(arg1) | ||
17 | - visit project_milestones_path(@project) | ||
18 | -end | ||
19 | - | ||
20 | Then /^I should see active milestones$/ do | 15 | Then /^I should see active milestones$/ do |
21 | milestone = @project.milestones.first | 16 | milestone = @project.milestones.first |
22 | page.should have_content(milestone.title[0..10]) | 17 | page.should have_content(milestone.title[0..10]) |
@@ -8,10 +8,6 @@ Given /^"(.*?)" is "(.*?)" developer$/ do |arg1, arg2| | @@ -8,10 +8,6 @@ Given /^"(.*?)" is "(.*?)" developer$/ do |arg1, arg2| | ||
8 | project.add_access(user, :write) | 8 | project.add_access(user, :write) |
9 | end | 9 | end |
10 | 10 | ||
11 | -Given /^I visit project "(.*?)" team page$/ do |arg1| | ||
12 | - visit team_project_path(Project.find_by_name(arg1)) | ||
13 | -end | ||
14 | - | ||
15 | Then /^I should be able to see myself in team$/ do | 11 | Then /^I should be able to see myself in team$/ do |
16 | page.should have_content(@user.name) | 12 | page.should have_content(@user.name) |
17 | page.should have_content(@user.email) | 13 | page.should have_content(@user.email) |
@@ -23,15 +19,11 @@ Then /^I should see "(.*?)" in team list$/ do |arg1| | @@ -23,15 +19,11 @@ Then /^I should see "(.*?)" in team list$/ do |arg1| | ||
23 | page.should have_content(user.email) | 19 | page.should have_content(user.email) |
24 | end | 20 | end |
25 | 21 | ||
26 | -Given /^I click link "(.*?)"$/ do |arg1| | ||
27 | - click_link arg1 | ||
28 | -end | ||
29 | - | ||
30 | Given /^I select "(.*?)" as "(.*?)"$/ do |arg1, arg2| | 22 | Given /^I select "(.*?)" as "(.*?)"$/ do |arg1, arg2| |
31 | user = User.find_by_name(arg1) | 23 | user = User.find_by_name(arg1) |
32 | - within "#new_team_member" do | ||
33 | - select user.name, :from => "team_member_user_id" | ||
34 | - select arg2, :from => "team_member_project_access" | 24 | + within "#new_team_member" do |
25 | + select user.name, :from => "user_ids" | ||
26 | + select arg2, :from => "project_access" | ||
35 | end | 27 | end |
36 | click_button "Save" | 28 | click_button "Save" |
37 | end | 29 | end |
@@ -44,7 +36,7 @@ end | @@ -44,7 +36,7 @@ end | ||
44 | 36 | ||
45 | Given /^I change "(.*?)" role to "(.*?)"$/ do |arg1, arg2| | 37 | Given /^I change "(.*?)" role to "(.*?)"$/ do |arg1, arg2| |
46 | user = User.find_by_name(arg1) | 38 | user = User.find_by_name(arg1) |
47 | - within ".user_#{user.id}" do | 39 | + within ".user_#{user.id}" do |
48 | select arg2, :from => "team_member_project_access" | 40 | select arg2, :from => "team_member_project_access" |
49 | end | 41 | end |
50 | end | 42 | end |
1 | -Given /^I visit project wiki page$/ do | ||
2 | - visit project_wiki_path(@project, :index) | ||
3 | -end | ||
4 | - | ||
5 | Given /^I create Wiki page$/ do | 1 | Given /^I create Wiki page$/ do |
6 | fill_in "Title", :with => 'Test title' | 2 | fill_in "Title", :with => 'Test title' |
7 | fill_in "Content", :with => '[link test](test)' | 3 | fill_in "Content", :with => '[link test](test)' |
1 | -include LoginHelpers | ||
2 | - | ||
3 | -Given /^I signin as a user$/ do | ||
4 | - login_as :user | ||
5 | -end | ||
6 | - | ||
7 | When /^I visit new project page$/ do | 1 | When /^I visit new project page$/ do |
8 | visit new_project_path | 2 | visit new_project_path |
9 | end | 3 | end |
@@ -65,10 +59,6 @@ Given /^I visit project "(.*?)" network page$/ do |arg1| | @@ -65,10 +59,6 @@ Given /^I visit project "(.*?)" network page$/ do |arg1| | ||
65 | visit graph_project_path(project) | 59 | visit graph_project_path(project) |
66 | end | 60 | end |
67 | 61 | ||
68 | -Given /^show me page$/ do | ||
69 | - save_and_open_page | ||
70 | -end | ||
71 | - | ||
72 | Given /^page should have network graph$/ do | 62 | Given /^page should have network graph$/ do |
73 | page.should have_content "Project Network Graph" | 63 | page.should have_content "Project Network Graph" |
74 | within ".graph" do | 64 | within ".graph" do |
@@ -0,0 +1,91 @@ | @@ -0,0 +1,91 @@ | ||
1 | +Given /^I visit project "(.*?)" issues page$/ do |arg1| | ||
2 | + visit project_issues_path(Project.find_by_name(arg1)) | ||
3 | +end | ||
4 | + | ||
5 | +Given /^I visit issue page "(.*?)"$/ do |arg1| | ||
6 | + issue = Issue.find_by_title(arg1) | ||
7 | + visit project_issue_path(issue.project, issue) | ||
8 | +end | ||
9 | + | ||
10 | +Given /^I visit project "(.*?)" merge requests page$/ do |arg1| | ||
11 | + visit project_merge_requests_path(Project.find_by_name(arg1)) | ||
12 | +end | ||
13 | + | ||
14 | +Given /^I visit merge request page "(.*?)"$/ do |arg1| | ||
15 | + mr = MergeRequest.find_by_title(arg1) | ||
16 | + visit project_merge_request_path(mr.project, mr) | ||
17 | +end | ||
18 | + | ||
19 | +Given /^I visit project "(.*?)" milestones page$/ do |arg1| | ||
20 | + @project = Project.find_by_name(arg1) | ||
21 | + visit project_milestones_path(@project) | ||
22 | +end | ||
23 | + | ||
24 | +Given /^I visit project commits page$/ do | ||
25 | + visit project_commits_path(@project) | ||
26 | +end | ||
27 | + | ||
28 | +Given /^I visit compare refs page$/ do | ||
29 | + visit compare_project_commits_path(@project) | ||
30 | +end | ||
31 | + | ||
32 | +Given /^I visit project branches page$/ do | ||
33 | + visit branches_project_repository_path(@project) | ||
34 | +end | ||
35 | + | ||
36 | +Given /^I visit project commit page$/ do | ||
37 | + visit project_commit_path(@project, ValidCommit::ID) | ||
38 | +end | ||
39 | + | ||
40 | +Given /^I visit project tags page$/ do | ||
41 | + visit tags_project_repository_path(@project) | ||
42 | +end | ||
43 | + | ||
44 | +Given /^I click on commit link$/ do | ||
45 | + visit project_commit_path(@project, ValidCommit::ID) | ||
46 | +end | ||
47 | + | ||
48 | +Given /^I visit project source page$/ do | ||
49 | + visit tree_project_ref_path(@project, @project.root_ref) | ||
50 | +end | ||
51 | + | ||
52 | +Given /^I visit project source page for "(.*?)"$/ do |arg1| | ||
53 | + visit tree_project_ref_path(@project, arg1) | ||
54 | +end | ||
55 | + | ||
56 | +Given /^I visit blob file from repo$/ do | ||
57 | + visit tree_project_ref_path(@project, ValidCommit::ID, :path => ValidCommit::BLOB_FILE_PATH) | ||
58 | +end | ||
59 | + | ||
60 | +Given /^I visit project "(.*?)" team page$/ do |arg1| | ||
61 | + visit team_project_path(Project.find_by_name(arg1)) | ||
62 | +end | ||
63 | + | ||
64 | +Given /^I visit project wiki page$/ do | ||
65 | + visit project_wiki_path(@project, :index) | ||
66 | +end | ||
67 | + | ||
68 | +Given /^I visit profile page$/ do | ||
69 | + visit profile_path | ||
70 | +end | ||
71 | + | ||
72 | +Given /^I visit profile token page$/ do | ||
73 | + visit profile_token_path | ||
74 | +end | ||
75 | + | ||
76 | +Given /^I visit profile password page$/ do | ||
77 | + visit profile_password_path | ||
78 | +end | ||
79 | + | ||
80 | +Given /^I visit dashboard page$/ do | ||
81 | + visit dashboard_path | ||
82 | +end | ||
83 | + | ||
84 | +Given /^I visit dashboard issues page$/ do | ||
85 | + visit dashboard_issues_path | ||
86 | +end | ||
87 | + | ||
88 | +Given /^I visit dashboard merge requests page$/ do | ||
89 | + visit dashboard_merge_requests_path | ||
90 | +end | ||
91 | + |
@@ -29,14 +29,24 @@ module Gitlab | @@ -29,14 +29,24 @@ module Gitlab | ||
29 | # name (required) - name for new project | 29 | # name (required) - name for new project |
30 | # code (optional) - code for new project, uses project name if not set | 30 | # code (optional) - code for new project, uses project name if not set |
31 | # path (optional) - path for new project, uses project name if not set | 31 | # path (optional) - path for new project, uses project name if not set |
32 | + # description (optional) - short project description | ||
33 | + # default_branch (optional) - 'master' by default | ||
34 | + # issues_enabled (optional) - enabled by default | ||
35 | + # wall_enabled (optional) - enabled by default | ||
36 | + # merge_requests_enabled (optional) - enabled by default | ||
37 | + # wiki_enabled (optional) - enabled by default | ||
32 | # Example Request | 38 | # Example Request |
33 | # POST /projects | 39 | # POST /projects |
34 | post do | 40 | post do |
35 | - project = {} | ||
36 | - project[:name] = params[:name] | ||
37 | - project[:code] = params[:code] || project[:name] | ||
38 | - project[:path] = params[:path] || project[:name] | ||
39 | - @project = Project.create_by_user(project, current_user) | 41 | + params[:code] ||= params[:name] |
42 | + params[:path] ||= params[:name] | ||
43 | + project_attrs = {} | ||
44 | + params.each_pair do |k ,v| | ||
45 | + if Project.attribute_names.include? k | ||
46 | + project_attrs[k] = v | ||
47 | + end | ||
48 | + end | ||
49 | + @project = Project.create_by_user(project_attrs, current_user) | ||
40 | if @project.saved? | 50 | if @project.saved? |
41 | present @project, with: Entities::Project | 51 | present @project, with: Entities::Project |
42 | else | 52 | else |
1 | -require 'gitolite' | ||
2 | -require 'timeout' | ||
3 | -require 'fileutils' | 1 | +require_relative 'gitolite_config' |
4 | 2 | ||
5 | -# TODO: refactor & cleanup | ||
6 | module Gitlab | 3 | module Gitlab |
7 | class Gitolite | 4 | class Gitolite |
8 | class AccessDenied < StandardError; end | 5 | class AccessDenied < StandardError; end |
9 | - class InvalidKey < StandardError; end | 6 | + |
7 | + def config | ||
8 | + Gitlab::GitoliteConfig.new | ||
9 | + end | ||
10 | 10 | ||
11 | def set_key key_id, key_content, projects | 11 | def set_key key_id, key_content, projects |
12 | - configure do |c| | ||
13 | - c.update_keys(key_id, key_content) | ||
14 | - c.update_projects(projects) | 12 | + config.apply do |config| |
13 | + config.write_key(key_id, key_content) | ||
14 | + config.update_projects(projects) | ||
15 | end | 15 | end |
16 | end | 16 | end |
17 | 17 | ||
18 | def remove_key key_id, projects | 18 | def remove_key key_id, projects |
19 | - configure do |c| | ||
20 | - c.delete_key(key_id) | ||
21 | - c.update_projects(projects) | 19 | + config.apply do |config| |
20 | + config.rm_key(key_id) | ||
21 | + config.update_projects(projects) | ||
22 | end | 22 | end |
23 | end | 23 | end |
24 | 24 | ||
25 | def update_repository project | 25 | def update_repository project |
26 | - configure do |c| | ||
27 | - c.update_project(project.path, project) | ||
28 | - end | 26 | + config.update_project!(project.path, project) |
29 | end | 27 | end |
30 | 28 | ||
31 | - alias_method :create_repository, :update_repository | ||
32 | - | ||
33 | def remove_repository project | 29 | def remove_repository project |
34 | - configure do |c| | ||
35 | - c.destroy_project(project) | ||
36 | - end | 30 | + config.destroy_project!(project) |
37 | end | 31 | end |
38 | 32 | ||
39 | def url_to_repo path | 33 | def url_to_repo path |
40 | Gitlab.config.ssh_path + "#{path}.git" | 34 | Gitlab.config.ssh_path + "#{path}.git" |
41 | end | 35 | end |
42 | 36 | ||
43 | - def initialize | ||
44 | - # create tmp dir | ||
45 | - @local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}") | ||
46 | - end | ||
47 | - | ||
48 | def enable_automerge | 37 | def enable_automerge |
49 | - configure do |git| | ||
50 | - git.admin_all_repo | ||
51 | - end | ||
52 | - end | ||
53 | - | ||
54 | - protected | ||
55 | - | ||
56 | - def destroy_project(project) | ||
57 | - FileUtils.rm_rf(project.path_to_repo) | ||
58 | - | ||
59 | - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite')) | ||
60 | - conf = ga_repo.config | ||
61 | - conf.rm_repo(project.path) | ||
62 | - ga_repo.save | ||
63 | - end | ||
64 | - | ||
65 | - #update or create | ||
66 | - def update_keys(user, key) | ||
67 | - File.open(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"), 'w') {|f| f.write(key.gsub(/\n/,'')) } | ||
68 | - end | ||
69 | - | ||
70 | - def delete_key(user) | ||
71 | - File.unlink(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub")) | ||
72 | - `cd #{File.join(@local_dir,'gitolite')} ; git rm keydir/#{user}.pub` | ||
73 | - end | ||
74 | - | ||
75 | - # update or create | ||
76 | - def update_project(repo_name, project) | ||
77 | - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite')) | ||
78 | - conf = ga_repo.config | ||
79 | - repo = update_project_config(project, conf) | ||
80 | - conf.add_repo(repo, true) | ||
81 | - | ||
82 | - ga_repo.save | ||
83 | - end | ||
84 | - | ||
85 | - # Updates many projects and uses project.path as the repo path | ||
86 | - # An order of magnitude faster than update_project | ||
87 | - def update_projects(projects) | ||
88 | - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite')) | ||
89 | - conf = ga_repo.config | ||
90 | - | ||
91 | - projects.each do |project| | ||
92 | - repo = update_project_config(project, conf) | ||
93 | - conf.add_repo(repo, true) | ||
94 | - end | ||
95 | - | ||
96 | - ga_repo.save | ||
97 | - end | ||
98 | - | ||
99 | - def update_project_config(project, conf) | ||
100 | - repo_name = project.path | ||
101 | - | ||
102 | - repo = if conf.has_repo?(repo_name) | ||
103 | - conf.get_repo(repo_name) | ||
104 | - else | ||
105 | - ::Gitolite::Config::Repo.new(repo_name) | ||
106 | - end | ||
107 | - | ||
108 | - name_readers = project.repository_readers | ||
109 | - name_writers = project.repository_writers | ||
110 | - name_masters = project.repository_masters | ||
111 | - | ||
112 | - pr_br = project.protected_branches.map(&:name).join("$ ") | ||
113 | - | ||
114 | - repo.clean_permissions | ||
115 | - | ||
116 | - # Deny access to protected branches for writers | ||
117 | - unless name_writers.blank? || pr_br.blank? | ||
118 | - repo.add_permission("-", pr_br.strip + "$ ", name_writers) | ||
119 | - end | ||
120 | - | ||
121 | - # Add read permissions | ||
122 | - repo.add_permission("R", "", name_readers) unless name_readers.blank? | ||
123 | - | ||
124 | - # Add write permissions | ||
125 | - repo.add_permission("RW+", "", name_writers) unless name_writers.blank? | ||
126 | - repo.add_permission("RW+", "", name_masters) unless name_masters.blank? | ||
127 | - | ||
128 | - repo | ||
129 | - end | ||
130 | - | ||
131 | - def admin_all_repo | ||
132 | - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite')) | ||
133 | - conf = ga_repo.config | ||
134 | - owner_name = "" | ||
135 | - | ||
136 | - # Read gitolite-admin user | ||
137 | - # | ||
138 | - begin | ||
139 | - repo = conf.get_repo("gitolite-admin") | ||
140 | - owner_name = repo.permissions[0]["RW+"][""][0] | ||
141 | - raise StandardError if owner_name.blank? | ||
142 | - rescue => ex | ||
143 | - puts "Can't determine gitolite-admin owner".red | ||
144 | - raise StandardError | ||
145 | - end | ||
146 | - | ||
147 | - # @ALL repos premission for gitolite owner | ||
148 | - repo_name = "@all" | ||
149 | - repo = if conf.has_repo?(repo_name) | ||
150 | - conf.get_repo(repo_name) | ||
151 | - else | ||
152 | - ::Gitolite::Config::Repo.new(repo_name) | ||
153 | - end | ||
154 | - | ||
155 | - repo.add_permission("RW+", "", owner_name) | ||
156 | - conf.add_repo(repo, true) | ||
157 | - ga_repo.save | 38 | + config.admin_all_repo!(project) |
158 | end | 39 | end |
159 | 40 | ||
160 | - private | ||
161 | - | ||
162 | - def pull | ||
163 | - # create tmp dir | ||
164 | - @local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}") | ||
165 | - Dir.mkdir @local_dir | ||
166 | - | ||
167 | - `git clone #{Gitlab.config.gitolite_admin_uri} #{@local_dir}/gitolite` | ||
168 | - end | ||
169 | - | ||
170 | - def push | ||
171 | - Dir.chdir(File.join(@local_dir, "gitolite")) | ||
172 | - `git add -A` | ||
173 | - `git commit -am "Gitlab"` | ||
174 | - `git push` | ||
175 | - Dir.chdir(Rails.root) | ||
176 | - | ||
177 | - FileUtils.rm_rf(@local_dir) | ||
178 | - end | ||
179 | - | ||
180 | - def configure | ||
181 | - Timeout::timeout(30) do | ||
182 | - File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f| | ||
183 | - begin | ||
184 | - f.flock(File::LOCK_EX) | ||
185 | - pull | ||
186 | - yield(self) | ||
187 | - push | ||
188 | - ensure | ||
189 | - f.flock(File::LOCK_UN) | ||
190 | - end | ||
191 | - end | ||
192 | - end | ||
193 | - rescue Exception => ex | ||
194 | - if ex.message =~ /is not a valid SSH key string/ | ||
195 | - raise Gitolite::InvalidKey.new("ssh key is not valid") | ||
196 | - else | ||
197 | - Gitlab::Logger.error(ex.message) | ||
198 | - raise Gitolite::AccessDenied.new("gitolite timeout") | ||
199 | - end | ||
200 | - end | 41 | + alias_method :create_repository, :update_repository |
201 | end | 42 | end |
202 | end | 43 | end |
@@ -0,0 +1,203 @@ | @@ -0,0 +1,203 @@ | ||
1 | +require 'gitolite' | ||
2 | +require 'timeout' | ||
3 | +require 'fileutils' | ||
4 | + | ||
5 | +module Gitlab | ||
6 | + class GitoliteConfig | ||
7 | + class PullError < StandardError; end | ||
8 | + class PushError < StandardError; end | ||
9 | + | ||
10 | + attr_reader :config_tmp_dir, :ga_repo, :conf | ||
11 | + | ||
12 | + def config_tmp_dir | ||
13 | + @config_tmp_dir ||= File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}") | ||
14 | + end | ||
15 | + | ||
16 | + def ga_repo | ||
17 | + @ga_repo ||= ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite')) | ||
18 | + end | ||
19 | + | ||
20 | + def apply | ||
21 | + Timeout::timeout(30) do | ||
22 | + File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f| | ||
23 | + begin | ||
24 | + # Set exclusive lock | ||
25 | + # to prevent race condition | ||
26 | + f.flock(File::LOCK_EX) | ||
27 | + | ||
28 | + # Pull gitolite-admin repo | ||
29 | + # in tmp dir before do any changes | ||
30 | + pull(config_tmp_dir) | ||
31 | + | ||
32 | + # Build ga_repo object and @conf | ||
33 | + # to access gitolite-admin configuration | ||
34 | + @conf = ga_repo.config | ||
35 | + | ||
36 | + # Do any changes | ||
37 | + # in gitolite-admin | ||
38 | + # config here | ||
39 | + yield(self) | ||
40 | + | ||
41 | + # Save changes in | ||
42 | + # gitolite-admin repo | ||
43 | + # before pusht it | ||
44 | + ga_repo.save | ||
45 | + | ||
46 | + # Push gitolite-admin repo | ||
47 | + # to apply all changes | ||
48 | + push(config_tmp_dir) | ||
49 | + | ||
50 | + # Remove tmp dir | ||
51 | + # wiith gitolite-admin | ||
52 | + FileUtils.rm_rf(config_tmp_dir) | ||
53 | + ensure | ||
54 | + # unlock so other task cann access | ||
55 | + # gitolite configuration | ||
56 | + f.flock(File::LOCK_UN) | ||
57 | + end | ||
58 | + end | ||
59 | + end | ||
60 | + rescue PullError => ex | ||
61 | + Gitlab::Logger.error("Pull error -> " + ex.message) | ||
62 | + raise Gitolite::AccessDenied, ex.message | ||
63 | + | ||
64 | + rescue PushError => ex | ||
65 | + Gitlab::Logger.error("Push error -> " + " " + ex.message) | ||
66 | + raise Gitolite::AccessDenied, ex.message | ||
67 | + | ||
68 | + rescue Exception => ex | ||
69 | + Gitlab::Logger.error(ex.class.name + " " + ex.message) | ||
70 | + raise Gitolite::AccessDenied.new("gitolite timeout") | ||
71 | + end | ||
72 | + | ||
73 | + def destroy_project(project) | ||
74 | + FileUtils.rm_rf(project.path_to_repo) | ||
75 | + conf.rm_repo(project.path) | ||
76 | + end | ||
77 | + | ||
78 | + def destroy_project!(project) | ||
79 | + apply do |config| | ||
80 | + config.destroy_project(project) | ||
81 | + end | ||
82 | + end | ||
83 | + | ||
84 | + def write_key(id, key) | ||
85 | + File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f| | ||
86 | + f.write(key.gsub(/\n/,'')) | ||
87 | + end | ||
88 | + end | ||
89 | + | ||
90 | + def rm_key(user) | ||
91 | + File.unlink(File.join(config_tmp_dir, 'gitolite/keydir',"#{user}.pub")) | ||
92 | + `cd #{File.join(config_tmp_dir,'gitolite')} ; git rm keydir/#{user}.pub` | ||
93 | + end | ||
94 | + | ||
95 | + # update or create | ||
96 | + def update_project(repo_name, project) | ||
97 | + repo = update_project_config(project, conf) | ||
98 | + conf.add_repo(repo, true) | ||
99 | + end | ||
100 | + | ||
101 | + def update_project!(repo_name, project) | ||
102 | + apply do |config| | ||
103 | + config.update_project(repo_name, project) | ||
104 | + end | ||
105 | + end | ||
106 | + | ||
107 | + # Updates many projects and uses project.path as the repo path | ||
108 | + # An order of magnitude faster than update_project | ||
109 | + def update_projects(projects) | ||
110 | + projects.each do |project| | ||
111 | + repo = update_project_config(project, conf) | ||
112 | + conf.add_repo(repo, true) | ||
113 | + end | ||
114 | + end | ||
115 | + | ||
116 | + def update_project_config(project, conf) | ||
117 | + repo_name = project.path | ||
118 | + | ||
119 | + repo = if conf.has_repo?(repo_name) | ||
120 | + conf.get_repo(repo_name) | ||
121 | + else | ||
122 | + ::Gitolite::Config::Repo.new(repo_name) | ||
123 | + end | ||
124 | + | ||
125 | + name_readers = project.repository_readers | ||
126 | + name_writers = project.repository_writers | ||
127 | + name_masters = project.repository_masters | ||
128 | + | ||
129 | + pr_br = project.protected_branches.map(&:name).join("$ ") | ||
130 | + | ||
131 | + repo.clean_permissions | ||
132 | + | ||
133 | + # Deny access to protected branches for writers | ||
134 | + unless name_writers.blank? || pr_br.blank? | ||
135 | + repo.add_permission("-", pr_br.strip + "$ ", name_writers) | ||
136 | + end | ||
137 | + | ||
138 | + # Add read permissions | ||
139 | + repo.add_permission("R", "", name_readers) unless name_readers.blank? | ||
140 | + | ||
141 | + # Add write permissions | ||
142 | + repo.add_permission("RW+", "", name_writers) unless name_writers.blank? | ||
143 | + repo.add_permission("RW+", "", name_masters) unless name_masters.blank? | ||
144 | + | ||
145 | + repo | ||
146 | + end | ||
147 | + | ||
148 | + # Enable access to all repos for gitolite admin. | ||
149 | + # We use it for accept merge request feature | ||
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 | ||
163 | + | ||
164 | + # @ALL repos premission for gitolite owner | ||
165 | + repo_name = "@all" | ||
166 | + repo = if conf.has_repo?(repo_name) | ||
167 | + conf.get_repo(repo_name) | ||
168 | + else | ||
169 | + ::Gitolite::Config::Repo.new(repo_name) | ||
170 | + end | ||
171 | + | ||
172 | + repo.add_permission("RW+", "", owner_name) | ||
173 | + conf.add_repo(repo, true) | ||
174 | + end | ||
175 | + | ||
176 | + def admin_all_repo! | ||
177 | + apply { |config| config.admin_all_repo } | ||
178 | + end | ||
179 | + | ||
180 | + private | ||
181 | + | ||
182 | + def pull tmp_dir | ||
183 | + Dir.mkdir tmp_dir | ||
184 | + `git clone #{Gitlab.config.gitolite_admin_uri} #{tmp_dir}/gitolite` | ||
185 | + | ||
186 | + unless File.exists?(File.join(tmp_dir, 'gitolite', 'conf', 'gitolite.conf')) | ||
187 | + raise PullError, "unable to clone gitolite-admin repo" | ||
188 | + end | ||
189 | + end | ||
190 | + | ||
191 | + def push tmp_dir | ||
192 | + Dir.chdir(File.join(tmp_dir, "gitolite")) | ||
193 | + system('git add -A') | ||
194 | + system('git commit -am "GitLab"') | ||
195 | + if system('git push') | ||
196 | + Dir.chdir(Rails.root) | ||
197 | + else | ||
198 | + raise PushError, "unable to push gitolite-admin repo" | ||
199 | + end | ||
200 | + end | ||
201 | + end | ||
202 | +end | ||
203 | + |
1 | module Gitlab | 1 | module Gitlab |
2 | - # Custom parser for Gitlab-flavored Markdown | 2 | + # Custom parser for GitLab-flavored Markdown |
3 | # | 3 | # |
4 | - # It replaces references in the text with links to the appropriate items in Gitlab. | 4 | + # It replaces references in the text with links to the appropriate items in |
5 | + # GitLab. | ||
5 | # | 6 | # |
6 | # Supported reference formats are: | 7 | # Supported reference formats are: |
7 | # * @foo for team members | 8 | # * @foo for team members |
@@ -10,19 +11,20 @@ module Gitlab | @@ -10,19 +11,20 @@ module Gitlab | ||
10 | # * $123 for snippets | 11 | # * $123 for snippets |
11 | # * 123456 for commits | 12 | # * 123456 for commits |
12 | # | 13 | # |
13 | - # Examples | 14 | + # It also parses Emoji codes to insert images. See |
15 | + # http://www.emoji-cheat-sheet.com/ for a list of the supported icons. | ||
14 | # | 16 | # |
15 | - # >> m = Markdown.new(...) | 17 | + # Examples |
16 | # | 18 | # |
17 | - # >> m.parse("Hey @david, can you fix this?") | 19 | + # >> gfm("Hey @david, can you fix this?") |
18 | # => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?" | 20 | # => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?" |
19 | # | 21 | # |
20 | - # >> m.parse("Commit 35d5f7c closes #1234") | 22 | + # >> gfm("Commit 35d5f7c closes #1234") |
21 | # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>" | 23 | # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>" |
22 | - class Markdown | ||
23 | - include Rails.application.routes.url_helpers | ||
24 | - include ActionView::Helpers | ||
25 | - | 24 | + # |
25 | + # >> gfm(":trollface:") | ||
26 | + # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" /> | ||
27 | + module Markdown | ||
26 | REFERENCE_PATTERN = %r{ | 28 | REFERENCE_PATTERN = %r{ |
27 | ([^\w&;])? # Prefix (1) | 29 | ([^\w&;])? # Prefix (1) |
28 | ( # Reference (2) | 30 | ( # Reference (2) |
@@ -33,15 +35,57 @@ module Gitlab | @@ -33,15 +35,57 @@ module Gitlab | ||
33 | ([^\w&;])? # Suffix (6) | 35 | ([^\w&;])? # Suffix (6) |
34 | }x.freeze | 36 | }x.freeze |
35 | 37 | ||
38 | + EMOJI_PATTERN = %r{(:(\S+):)}.freeze | ||
39 | + | ||
36 | attr_reader :html_options | 40 | attr_reader :html_options |
37 | 41 | ||
38 | - def initialize(project, html_options = {}) | ||
39 | - @project = project | 42 | + # Public: Parse the provided text with GitLab-Flavored Markdown |
43 | + # | ||
44 | + # text - the source text | ||
45 | + # html_options - extra options for the reference links as given to link_to | ||
46 | + # | ||
47 | + # Note: reference links will only be generated if @project is set | ||
48 | + def gfm(text, html_options = {}) | ||
49 | + return text if text.nil? | ||
50 | + | ||
51 | + # prevents the string supplied through the _text_ argument to be altered | ||
52 | + text = text.dup | ||
53 | + | ||
40 | @html_options = html_options | 54 | @html_options = html_options |
55 | + | ||
56 | + # Extract pre blocks so they are not altered | ||
57 | + # from http://github.github.com/github-flavored-markdown/ | ||
58 | + extractions = {} | ||
59 | + text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match| | ||
60 | + md5 = Digest::MD5.hexdigest(match) | ||
61 | + extractions[md5] = match | ||
62 | + "{gfm-extraction-#{md5}}" | ||
63 | + end | ||
64 | + | ||
65 | + # TODO: add popups with additional information | ||
66 | + | ||
67 | + text = parse(text) | ||
68 | + | ||
69 | + # Insert pre block extractions | ||
70 | + text.gsub!(/\{gfm-extraction-(\h{32})\}/) do | ||
71 | + extractions[$1] | ||
72 | + end | ||
73 | + | ||
74 | + sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class) | ||
41 | end | 75 | end |
42 | 76 | ||
77 | + private | ||
78 | + | ||
79 | + # Private: Parses text for references and emoji | ||
80 | + # | ||
81 | + # text - Text to parse | ||
82 | + # | ||
83 | + # Note: reference links will only be generated if @project is set | ||
84 | + # | ||
85 | + # Returns parsed text | ||
43 | def parse(text) | 86 | def parse(text) |
44 | - text.gsub(REFERENCE_PATTERN) do |match| | 87 | + # parse reference links |
88 | + text.gsub!(REFERENCE_PATTERN) do |match| | ||
45 | prefix = $1 || '' | 89 | prefix = $1 || '' |
46 | reference = $2 | 90 | reference = $2 |
47 | identifier = $3 || $4 || $5 | 91 | identifier = $3 || $4 || $5 |
@@ -52,10 +96,28 @@ module Gitlab | @@ -52,10 +96,28 @@ module Gitlab | ||
52 | else | 96 | else |
53 | match | 97 | match |
54 | end | 98 | end |
99 | + end if @project | ||
100 | + | ||
101 | + # parse emoji | ||
102 | + text.gsub!(EMOJI_PATTERN) do |match| | ||
103 | + if valid_emoji?($2) | ||
104 | + image_tag("emoji/#{$2}.png", size: "20x20", class: 'emoji', title: $1, alt: $1) | ||
105 | + else | ||
106 | + match | ||
107 | + end | ||
55 | end | 108 | end |
109 | + | ||
110 | + text | ||
56 | end | 111 | end |
57 | 112 | ||
58 | - private | 113 | + # Private: Checks if an emoji icon exists in the image asset directory |
114 | + # | ||
115 | + # emoji - Identifier of the emoji as a string (e.g., "+1", "heart") | ||
116 | + # | ||
117 | + # Returns boolean | ||
118 | + def valid_emoji?(emoji) | ||
119 | + File.exists?(Rails.root.join('app', 'assets', 'images', 'emoji', "#{emoji}.png")) | ||
120 | + end | ||
59 | 121 | ||
60 | # Private: Dispatches to a dedicated processing method based on reference | 122 | # Private: Dispatches to a dedicated processing method based on reference |
61 | # | 123 | # |
1 | #!/usr/bin/env bash | 1 | #!/usr/bin/env bash |
2 | 2 | ||
3 | -# This file was placed here by Gitlab. It makes sure that your pushed commits | 3 | +# This file was placed here by GitLab. It makes sure that your pushed commits |
4 | # will be processed properly. | 4 | # will be processed properly. |
5 | 5 | ||
6 | while read oldrev newrev ref | 6 | while read oldrev newrev ref |
1 | -IMPORT_DIRECTORY = 'import_projects' | ||
2 | - | ||
3 | -desc "Imports existing Git repos into new projects from the import_projects folder" | ||
4 | -task :import_projects, [:email] => :environment do |t, args| | ||
5 | - REPOSITORY_DIRECTORY = Gitlab.config.git_base_path | ||
6 | 1 | ||
2 | +desc "Imports existing Git repos from a directory into new projects in git_base_path" | ||
3 | +task :import_projects, [:directory,:email] => :environment do |t, args| | ||
7 | user_email = args.email | 4 | user_email = args.email |
8 | - repos_to_import = Dir.glob("#{IMPORT_DIRECTORY}/*") | ||
9 | - | 5 | + import_directory = args.directory |
6 | + repos_to_import = Dir.glob("#{import_directory}/*") | ||
7 | + git_base_path = Gitlab.config.git_base_path | ||
10 | puts "Found #{repos_to_import.length} repos to import" | 8 | puts "Found #{repos_to_import.length} repos to import" |
11 | 9 | ||
12 | imported_count = 0 | 10 | imported_count = 0 |
@@ -14,11 +12,9 @@ task :import_projects, [:email] => :environment do |t, args| | @@ -14,11 +12,9 @@ task :import_projects, [:email] => :environment do |t, args| | ||
14 | failed_count = 0 | 12 | failed_count = 0 |
15 | repos_to_import.each do |repo_path| | 13 | repos_to_import.each do |repo_path| |
16 | repo_name = File.basename repo_path | 14 | repo_name = File.basename repo_path |
17 | - repo_full_path = File.join(Rails.root, repo_path) | ||
18 | 15 | ||
19 | puts " Processing #{repo_name}" | 16 | puts " Processing #{repo_name}" |
20 | - | ||
21 | - clone_path = "#{REPOSITORY_DIRECTORY}/#{repo_name}.git" | 17 | + clone_path = "#{git_base_path}#{repo_name}.git" |
22 | 18 | ||
23 | if Dir.exists? clone_path | 19 | if Dir.exists? clone_path |
24 | if Project.find_by_code(repo_name) | 20 | if Project.find_by_code(repo_name) |
@@ -30,7 +26,7 @@ task :import_projects, [:email] => :environment do |t, args| | @@ -30,7 +26,7 @@ task :import_projects, [:email] => :environment do |t, args| | ||
30 | end | 26 | end |
31 | else | 27 | else |
32 | # Clone the repo | 28 | # Clone the repo |
33 | - unless clone_bare_repo_as_git(repo_full_path, clone_path) | 29 | + unless clone_bare_repo_as_git(repo_path, clone_path) |
34 | failed_count += 1 | 30 | failed_count += 1 |
35 | next | 31 | next |
36 | end | 32 | end |
@@ -48,14 +44,17 @@ task :import_projects, [:email] => :environment do |t, args| | @@ -48,14 +44,17 @@ task :import_projects, [:email] => :environment do |t, args| | ||
48 | puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})." | 44 | puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})." |
49 | end | 45 | end |
50 | 46 | ||
51 | -# Clones a repo as bare git repo using the git user | 47 | +# Clones a repo as bare git repo using the git_user |
52 | def clone_bare_repo_as_git(existing_path, new_path) | 48 | def clone_bare_repo_as_git(existing_path, new_path) |
49 | + git_user = Gitlab.config.ssh_user | ||
53 | begin | 50 | begin |
54 | - sh "sudo -u git -i git clone --bare '#{existing_path}' #{new_path}" | 51 | + sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}" |
55 | true | 52 | true |
56 | - rescue | 53 | + rescue Exception=> msg |
57 | puts " ERROR: Faild to clone #{existing_path} to #{new_path}" | 54 | puts " ERROR: Faild to clone #{existing_path} to #{new_path}" |
58 | - false | 55 | + puts " Make sure #{git_user} can reach #{existing_path}" |
56 | + puts " Exception-MSG: #{msg}" | ||
57 | + false | ||
59 | end | 58 | end |
60 | end | 59 | end |
61 | 60 |
1 | namespace :gitlab do | 1 | namespace :gitlab do |
2 | namespace :gitolite do | 2 | namespace :gitolite do |
3 | - desc "GITLAB | Write GITLAB hook for gitolite" | 3 | + desc "GITLAB | Write GitLab hook for gitolite" |
4 | task :write_hooks => :environment do | 4 | task :write_hooks => :environment do |
5 | gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common") | 5 | gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common") |
6 | gitlab_hooks_path = Rails.root.join("lib", "hooks") | 6 | gitlab_hooks_path = Rails.root.join("lib", "hooks") |
@@ -11,6 +11,9 @@ module Factory | @@ -11,6 +11,9 @@ module Factory | ||
11 | def self.new(type, *args) | 11 | def self.new(type, *args) |
12 | FactoryGirl.build(type, *args) | 12 | FactoryGirl.build(type, *args) |
13 | end | 13 | end |
14 | + def self.attributes(type, *args) | ||
15 | + FactoryGirl.attributes_for(type, *args) | ||
16 | + end | ||
14 | end | 17 | end |
15 | 18 | ||
16 | FactoryGirl.define do | 19 | FactoryGirl.define do |
1 | require 'spec_helper' | 1 | require 'spec_helper' |
2 | 2 | ||
3 | -describe "Factories" do | ||
4 | - describe 'User' do | ||
5 | - it "builds a valid instance" do | ||
6 | - build(:user).should be_valid | ||
7 | - end | ||
8 | - | ||
9 | - it "builds a valid admin instance" do | ||
10 | - build(:admin).should be_valid | ||
11 | - end | ||
12 | - end | ||
13 | - | ||
14 | - describe 'Project' do | ||
15 | - it "builds a valid instance" do | ||
16 | - build(:project).should be_valid | ||
17 | - end | ||
18 | - end | ||
19 | - | ||
20 | - describe 'Issue' do | ||
21 | - it "builds a valid instance" do | ||
22 | - build(:issue).should be_valid | ||
23 | - end | ||
24 | - | ||
25 | - it "builds a valid closed instance" do | ||
26 | - build(:closed_issue).should be_valid | ||
27 | - end | ||
28 | - end | ||
29 | - | ||
30 | - describe 'MergeRequest' do | ||
31 | - it "builds a valid instance" do | ||
32 | - build(:merge_request).should be_valid | ||
33 | - end | ||
34 | - end | ||
35 | - | ||
36 | - describe 'Note' do | ||
37 | - it "builds a valid instance" do | ||
38 | - build(:note).should be_valid | ||
39 | - end | ||
40 | - end | ||
41 | - | ||
42 | - describe 'Event' do | ||
43 | - it "builds a valid instance" do | ||
44 | - build(:event).should be_valid | ||
45 | - end | ||
46 | - end | ||
47 | - | ||
48 | - describe 'Key' do | ||
49 | - it "builds a valid instance" do | ||
50 | - build(:key).should be_valid | ||
51 | - end | ||
52 | - | ||
53 | - it "builds a valid deploy key instance" do | ||
54 | - build(:deploy_key).should be_valid | ||
55 | - end | ||
56 | - | ||
57 | - it "builds a valid personal key instance" do | ||
58 | - build(:personal_key).should be_valid | ||
59 | - end | ||
60 | - end | ||
61 | - | ||
62 | - describe 'Milestone' do | ||
63 | - it "builds a valid instance" do | ||
64 | - build(:milestone).should be_valid | ||
65 | - end | ||
66 | - end | ||
67 | - | ||
68 | - describe 'SystemHook' do | ||
69 | - it "builds a valid instance" do | ||
70 | - build(:system_hook).should be_valid | ||
71 | - end | ||
72 | - end | ||
73 | - | ||
74 | - describe 'ProjectHook' do | ||
75 | - it "builds a valid instance" do | ||
76 | - build(:project_hook).should be_valid | ||
77 | - end | ||
78 | - end | ||
79 | - | ||
80 | - describe 'Wiki' do | ||
81 | - it "builds a valid instance" do | ||
82 | - build(:wiki).should be_valid | ||
83 | - end | ||
84 | - end | ||
85 | - | ||
86 | - describe 'Snippet' do | ||
87 | - it "builds a valid instance" do | ||
88 | - build(:snippet).should be_valid | 3 | +FactoryGirl.factories.map(&:name).each do |factory_name| |
4 | + describe "#{factory_name} factory" do | ||
5 | + it 'should be valid' do | ||
6 | + build(factory_name).should be_valid | ||
89 | end | 7 | end |
90 | end | 8 | end |
91 | end | 9 | end |
@@ -208,6 +208,51 @@ describe GitlabMarkdownHelper do | @@ -208,6 +208,51 @@ describe GitlabMarkdownHelper do | ||
208 | gfm(actual).should match(expected) | 208 | gfm(actual).should match(expected) |
209 | end | 209 | end |
210 | end | 210 | end |
211 | + | ||
212 | + describe "emoji" do | ||
213 | + it "matches at the start of a string" do | ||
214 | + gfm(":+1:").should match(/<img/) | ||
215 | + end | ||
216 | + | ||
217 | + it "matches at the end of a string" do | ||
218 | + gfm("This gets a :-1:").should match(/<img/) | ||
219 | + end | ||
220 | + | ||
221 | + it "matches with adjacent text" do | ||
222 | + gfm("+1 (:+1:)").should match(/<img/) | ||
223 | + end | ||
224 | + | ||
225 | + it "has a title attribute" do | ||
226 | + gfm(":-1:").should match(/title=":-1:"/) | ||
227 | + end | ||
228 | + | ||
229 | + it "has an alt attribute" do | ||
230 | + gfm(":-1:").should match(/alt=":-1:"/) | ||
231 | + end | ||
232 | + | ||
233 | + it "has an emoji class" do | ||
234 | + gfm(":+1:").should match('class="emoji"') | ||
235 | + end | ||
236 | + | ||
237 | + it "sets height and width" do | ||
238 | + actual = gfm(":+1:") | ||
239 | + actual.should match(/width="20"/) | ||
240 | + actual.should match(/height="20"/) | ||
241 | + end | ||
242 | + | ||
243 | + it "keeps whitespace intact" do | ||
244 | + gfm("This deserves a :+1: big time.").should match(/deserves a <img.+\/> big time/) | ||
245 | + end | ||
246 | + | ||
247 | + it "ignores invalid emoji" do | ||
248 | + gfm(":invalid-emoji:").should_not match(/<img/) | ||
249 | + end | ||
250 | + | ||
251 | + it "should work independet of reference links (i.e. without @project being set)" do | ||
252 | + @project = nil | ||
253 | + gfm(":+1:").should match(/<img/) | ||
254 | + end | ||
255 | + end | ||
211 | end | 256 | end |
212 | 257 | ||
213 | describe "#link_to_gfm" do | 258 | describe "#link_to_gfm" do |
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Gitlab::GitoliteConfig do | ||
4 | + let(:gitolite) { Gitlab::GitoliteConfig.new } | ||
5 | + | ||
6 | + it { should respond_to :write_key } | ||
7 | + it { should respond_to :rm_key } | ||
8 | + it { should respond_to :update_project } | ||
9 | + it { should respond_to :update_project! } | ||
10 | + it { should respond_to :update_projects } | ||
11 | + it { should respond_to :destroy_project } | ||
12 | + it { should respond_to :destroy_project! } | ||
13 | + it { should respond_to :apply } | ||
14 | + it { should respond_to :admin_all_repo } | ||
15 | + it { should respond_to :admin_all_repo! } | ||
16 | +end |
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Gitlab::Gitolite do | ||
4 | + let(:project) { double('Project', path: 'diaspora') } | ||
5 | + let(:gitolite_config) { double('Gitlab::GitoliteConfig') } | ||
6 | + let(:gitolite) { Gitlab::Gitolite.new } | ||
7 | + | ||
8 | + before do | ||
9 | + gitolite.stub(config: gitolite_config) | ||
10 | + end | ||
11 | + | ||
12 | + it { should respond_to :set_key } | ||
13 | + it { should respond_to :remove_key } | ||
14 | + | ||
15 | + it { should respond_to :update_repository } | ||
16 | + it { should respond_to :create_repository } | ||
17 | + it { should respond_to :remove_repository } | ||
18 | + | ||
19 | + it { gitolite.url_to_repo('diaspora').should == Gitlab.config.ssh_path + "diaspora.git" } | ||
20 | + | ||
21 | + it "should call config update" do | ||
22 | + gitolite_config.should_receive(:update_project!) | ||
23 | + gitolite.update_repository project | ||
24 | + end | ||
25 | +end |
@@ -24,7 +24,7 @@ describe Notify do | @@ -24,7 +24,7 @@ describe Notify do | ||
24 | end | 24 | end |
25 | 25 | ||
26 | it 'has the correct subject' do | 26 | it 'has the correct subject' do |
27 | - should have_subject /^gitlab \| Account was created for you$/ | 27 | + should have_subject /^gitlab \| Account was created for you$/i |
28 | end | 28 | end |
29 | 29 | ||
30 | it 'contains the new user\'s login name' do | 30 | it 'contains the new user\'s login name' do |
@@ -35,6 +35,16 @@ describe Note do | @@ -35,6 +35,16 @@ describe Note do | ||
35 | note = Factory(:note, note: "-1 for this") | 35 | note = Factory(:note, note: "-1 for this") |
36 | note.should_not be_upvote | 36 | note.should_not be_upvote |
37 | end | 37 | end |
38 | + | ||
39 | + it "recognizes a +1 emoji as a vote" do | ||
40 | + note = build(:note, note: ":+1: for this") | ||
41 | + note.should be_upvote | ||
42 | + end | ||
43 | + | ||
44 | + it "recognizes a neutral emoji note" do | ||
45 | + note = build(:note, note: "I would :+1: this, but I don't want to") | ||
46 | + note.should_not be_upvote | ||
47 | + end | ||
38 | end | 48 | end |
39 | 49 | ||
40 | let(:project) { create(:project) } | 50 | let(:project) { create(:project) } |
@@ -10,7 +10,7 @@ describe UsersProject do | @@ -10,7 +10,7 @@ describe UsersProject do | ||
10 | let!(:users_project) { create(:users_project) } | 10 | let!(:users_project) { create(:users_project) } |
11 | 11 | ||
12 | it { should validate_presence_of(:user_id) } | 12 | it { should validate_presence_of(:user_id) } |
13 | - it { should validate_uniqueness_of(:user_id).scoped_to(:project_id) } | 13 | + it { should validate_uniqueness_of(:user_id).scoped_to(:project_id).with_message(/already exists/) } |
14 | 14 | ||
15 | it { should validate_presence_of(:project_id) } | 15 | it { should validate_presence_of(:project_id) } |
16 | end | 16 | end |
@@ -30,38 +30,40 @@ describe Gitlab::API do | @@ -30,38 +30,40 @@ describe Gitlab::API do | ||
30 | 30 | ||
31 | describe "POST /projects" do | 31 | describe "POST /projects" do |
32 | it "should create new project without code and path" do | 32 | it "should create new project without code and path" do |
33 | - lambda { | ||
34 | - name = "foo" | ||
35 | - post api("/projects", user), { | ||
36 | - name: name | ||
37 | - } | ||
38 | - response.status.should == 201 | ||
39 | - json_response["name"].should == name | ||
40 | - json_response["code"].should == name | ||
41 | - json_response["path"].should == name | ||
42 | - }.should change{Project.count}.by(1) | ||
43 | - end | ||
44 | - it "should create new project" do | ||
45 | - lambda { | ||
46 | - name = "foo" | ||
47 | - path = "bar" | ||
48 | - code = "bazz" | ||
49 | - post api("/projects", user), { | ||
50 | - code: code, | ||
51 | - path: path, | ||
52 | - name: name | ||
53 | - } | ||
54 | - response.status.should == 201 | ||
55 | - json_response["name"].should == name | ||
56 | - json_response["path"].should == path | ||
57 | - json_response["code"].should == code | ||
58 | - }.should change{Project.count}.by(1) | ||
59 | - end | ||
60 | - it "should not create project without name" do | ||
61 | - lambda { | ||
62 | - post api("/projects", user) | ||
63 | - response.status.should == 404 | ||
64 | - }.should_not change{Project.count} | 33 | + expect { post api("/projects", user), name: 'foo' }.to change {Project.count}.by(1) |
34 | + end | ||
35 | + | ||
36 | + it "should not create new project without name" do | ||
37 | + expect { post api("/projects", user) }.to_not change {Project.count} | ||
38 | + end | ||
39 | + | ||
40 | + it "should respond with 201 on success" do | ||
41 | + post api("/projects", user), name: 'foo' | ||
42 | + response.status.should == 201 | ||
43 | + end | ||
44 | + | ||
45 | + it "should repsond with 404 on failure" do | ||
46 | + post api("/projects", user) | ||
47 | + response.status.should == 404 | ||
48 | + end | ||
49 | + | ||
50 | + it "should assign attributes to project" do | ||
51 | + project = Factory.attributes(:project, { | ||
52 | + path: 'path', | ||
53 | + code: 'code', | ||
54 | + description: Faker::Lorem.sentence, | ||
55 | + default_branch: 'stable', | ||
56 | + issues_enabled: false, | ||
57 | + wall_enabled: false, | ||
58 | + merge_requests_enabled: false, | ||
59 | + wiki_enabled: false | ||
60 | + }) | ||
61 | + | ||
62 | + post api("/projects", user), project | ||
63 | + | ||
64 | + project.each_pair do |k,v| | ||
65 | + json_response[k.to_s].should == v | ||
66 | + end | ||
65 | end | 67 | end |
66 | end | 68 | end |
67 | 69 |
@@ -17,7 +17,7 @@ module GitoliteStub | @@ -17,7 +17,7 @@ module GitoliteStub | ||
17 | ) | 17 | ) |
18 | 18 | ||
19 | gitolite_admin = double( | 19 | gitolite_admin = double( |
20 | - 'Gitolite::GitoliteAdmin', | 20 | + 'Gitolite::GitoliteAdmin', |
21 | config: gitolite_config, | 21 | config: gitolite_config, |
22 | save: true, | 22 | save: true, |
23 | ) | 23 | ) |
@@ -27,9 +27,21 @@ module GitoliteStub | @@ -27,9 +27,21 @@ module GitoliteStub | ||
27 | end | 27 | end |
28 | 28 | ||
29 | def stub_gitlab_gitolite | 29 | def stub_gitlab_gitolite |
30 | - gitlab_gitolite = Gitlab::Gitolite.new | ||
31 | - Gitlab::Gitolite.stub(new: gitlab_gitolite) | ||
32 | - gitlab_gitolite.stub(configure: ->() { yield(self) }) | ||
33 | - gitlab_gitolite.stub(update_keys: true) | 30 | + gitolite_config = double('Gitlab::GitoliteConfig') |
31 | + gitolite_config.stub( | ||
32 | + apply: ->() { yield(self) }, | ||
33 | + write_key: true, | ||
34 | + rm_key: true, | ||
35 | + update_projects: true, | ||
36 | + update_project: true, | ||
37 | + update_project!: true, | ||
38 | + destroy_project: true, | ||
39 | + destroy_project!: true, | ||
40 | + admin_all_repo: true, | ||
41 | + admin_all_repo!: true, | ||
42 | + | ||
43 | + ) | ||
44 | + | ||
45 | + Gitlab::GitoliteConfig.stub(new: gitolite_config) | ||
34 | end | 46 | end |
35 | end | 47 | end |