Commit c09d233611e00328f0e8d493a106737f0638d9a2
Exists in
master
and in
4 other branches
Merge branch 'master' into fix_project_access_notification
Showing
74 changed files
with
1150 additions
and
838 deletions
Show diff stats
CHANGELOG
1 | +v 2.9.1 | ||
2 | + - Fixed resque custom config init | ||
3 | + | ||
1 | v 2.9.0 | 4 | v 2.9.0 |
2 | - fixed inline notes bugs | 5 | - fixed inline notes bugs |
3 | - refactored rspecs | 6 | - refactored rspecs |
@@ -9,8 +12,10 @@ v 2.9.0 | @@ -9,8 +12,10 @@ v 2.9.0 | ||
9 | - scss refactoring. gitlab_bootstrap/ dir | 12 | - scss refactoring. gitlab_bootstrap/ dir |
10 | - fix git push http body bigger than 112k problem | 13 | - fix git push http body bigger than 112k problem |
11 | - list of labels page under issues tab | 14 | - list of labels page under issues tab |
12 | - - API for milestones | 15 | + - API for milestones, keys |
13 | - restyled buttons | 16 | - restyled buttons |
17 | + - OAuth | ||
18 | + - Comment order changed | ||
14 | 19 | ||
15 | v 2.8.1 | 20 | v 2.8.1 |
16 | - ability to disable gravatars | 21 | - ability to disable gravatars |
Gemfile
@@ -96,6 +96,7 @@ group :assets do | @@ -96,6 +96,7 @@ group :assets do | ||
96 | gem "therubyracer" | 96 | gem "therubyracer" |
97 | 97 | ||
98 | gem 'chosen-rails' | 98 | gem 'chosen-rails' |
99 | + gem 'jquery-atwho-rails', '0.1.6' | ||
99 | gem "jquery-rails", "2.0.2" | 100 | gem "jquery-rails", "2.0.2" |
100 | gem "jquery-ui-rails", "0.5.0" | 101 | gem "jquery-ui-rails", "0.5.0" |
101 | gem "modernizr", "2.5.3" | 102 | gem "modernizr", "2.5.3" |
Gemfile.lock
@@ -199,6 +199,7 @@ GEM | @@ -199,6 +199,7 @@ GEM | ||
199 | httpauth (0.1) | 199 | httpauth (0.1) |
200 | i18n (0.6.1) | 200 | i18n (0.6.1) |
201 | journey (1.0.4) | 201 | journey (1.0.4) |
202 | + jquery-atwho-rails (0.1.6) | ||
202 | jquery-rails (2.0.2) | 203 | jquery-rails (2.0.2) |
203 | railties (>= 3.2.0, < 5.0) | 204 | railties (>= 3.2.0, < 5.0) |
204 | thor (~> 0.14) | 205 | thor (~> 0.14) |
@@ -441,6 +442,7 @@ DEPENDENCIES | @@ -441,6 +442,7 @@ DEPENDENCIES | ||
441 | haml-rails | 442 | haml-rails |
442 | headless | 443 | headless |
443 | httparty | 444 | httparty |
445 | + jquery-atwho-rails (= 0.1.6) | ||
444 | jquery-rails (= 2.0.2) | 446 | jquery-rails (= 2.0.2) |
445 | jquery-ui-rails (= 0.5.0) | 447 | jquery-ui-rails (= 0.5.0) |
446 | kaminari | 448 | kaminari |
Guardfile
@@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
4 | guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do | 4 | guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do |
5 | watch(%r{^spec/.+_spec\.rb$}) | 5 | watch(%r{^spec/.+_spec\.rb$}) |
6 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } | 6 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } |
7 | + watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" } | ||
7 | watch('spec/spec_helper.rb') { "spec" } | 8 | watch('spec/spec_helper.rb') { "spec" } |
8 | 9 | ||
9 | # Rails example | 10 | # Rails example |
VERSION
app/assets/javascripts/application.js
@@ -11,6 +11,7 @@ | @@ -11,6 +11,7 @@ | ||
11 | //= require jquery.endless-scroll | 11 | //= require jquery.endless-scroll |
12 | //= require jquery.highlight | 12 | //= require jquery.highlight |
13 | //= require jquery.waitforimages | 13 | //= require jquery.waitforimages |
14 | +//= require jquery.atwho | ||
14 | //= require bootstrap | 15 | //= require bootstrap |
15 | //= require modernizr | 16 | //= require modernizr |
16 | //= require chosen-jquery | 17 | //= require chosen-jquery |
app/assets/stylesheets/application.css
@@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
4 | * the top of the compiled file, but it's generally better to create a new file per style scope. | 4 | * the top of the compiled file, but it's generally better to create a new file per style scope. |
5 | *= require jquery.ui.all | 5 | *= require jquery.ui.all |
6 | *= require jquery.ui.aristo | 6 | *= require jquery.ui.aristo |
7 | + *= require jquery.atwho | ||
7 | *= require chosen | 8 | *= require chosen |
8 | *= require_self | 9 | *= require_self |
9 | *= require main | 10 | *= require main |
app/assets/stylesheets/common.scss
@@ -185,36 +185,6 @@ span.update-author { | @@ -185,36 +185,6 @@ span.update-author { | ||
185 | } | 185 | } |
186 | } | 186 | } |
187 | 187 | ||
188 | -.event_label { | ||
189 | - @extend .label; | ||
190 | - background-color: #999; | ||
191 | - | ||
192 | - &.pushed { | ||
193 | - background-color: #4A97BD; | ||
194 | - } | ||
195 | - | ||
196 | - &.opened { | ||
197 | - background-color: #469847; | ||
198 | - } | ||
199 | - | ||
200 | - &.closed { | ||
201 | - background-color: #B94A48; | ||
202 | - } | ||
203 | - | ||
204 | - &.merged { | ||
205 | - background-color: #2A2; | ||
206 | - } | ||
207 | - | ||
208 | - &.joined { | ||
209 | - background-color: #1ca9dd; | ||
210 | - } | ||
211 | - | ||
212 | - &.left { | ||
213 | - background-color: #888; | ||
214 | - float:none; | ||
215 | - } | ||
216 | -} | ||
217 | - | ||
218 | form { | 188 | form { |
219 | @extend .form-horizontal; | 189 | @extend .form-horizontal; |
220 | 190 | ||
@@ -355,41 +325,6 @@ p.time { | @@ -355,41 +325,6 @@ p.time { | ||
355 | border:2px solid #ddd; | 325 | border:2px solid #ddd; |
356 | } | 326 | } |
357 | 327 | ||
358 | -.event_feed { | ||
359 | - min-height:40px; | ||
360 | - border-bottom:1px solid #ddd; | ||
361 | - .avatar { | ||
362 | - width:32px; | ||
363 | - } | ||
364 | - .event_icon { | ||
365 | - float:right; | ||
366 | - margin-right:2px; | ||
367 | - img { | ||
368 | - width:20px; | ||
369 | - } | ||
370 | - } | ||
371 | - ul { | ||
372 | - margin-left:50px; | ||
373 | - margin-bottom:5px; | ||
374 | - .avatar { | ||
375 | - width:24px; | ||
376 | - } | ||
377 | - } | ||
378 | - | ||
379 | - padding: 15px 5px; | ||
380 | - &:last-child { border:none } | ||
381 | - .wll:hover { background:none } | ||
382 | - | ||
383 | - .event_commits { | ||
384 | - margin-top: 5px; | ||
385 | - | ||
386 | - li.commit { | ||
387 | - background: transparent; | ||
388 | - padding:5px; | ||
389 | - border:none; | ||
390 | - } | ||
391 | - } | ||
392 | -} | ||
393 | 328 | ||
394 | .ico { | 329 | .ico { |
395 | background: url("images.png") no-repeat -85px -77px; | 330 | background: url("images.png") no-repeat -85px -77px; |
@@ -639,22 +574,6 @@ li.note { | @@ -639,22 +574,6 @@ li.note { | ||
639 | background:#fff; | 574 | background:#fff; |
640 | } | 575 | } |
641 | 576 | ||
642 | -/** | ||
643 | - * Push event widget | ||
644 | - * | ||
645 | - */ | ||
646 | -.event_lp { | ||
647 | - @extend .ui-box; | ||
648 | - color:#777; | ||
649 | - margin-bottom:20px; | ||
650 | - padding:8px; | ||
651 | - @include border-radius(4px); | ||
652 | - min-height:22px; | ||
653 | - | ||
654 | - .avatar { | ||
655 | - width:24px; | ||
656 | - } | ||
657 | -} | ||
658 | 577 | ||
659 | .supp_diff_link, | 578 | .supp_diff_link, |
660 | .mr_show_all_commits { | 579 | .mr_show_all_commits { |
app/assets/stylesheets/gitlab_bootstrap/lists.scss
1 | /** LISTS **/ | 1 | /** LISTS **/ |
2 | 2 | ||
3 | -ul { | 3 | +ul { |
4 | /** | 4 | /** |
5 | * List li block element #1 | 5 | * List li block element #1 |
6 | * | 6 | * |
@@ -18,7 +18,7 @@ ul { | @@ -18,7 +18,7 @@ ul { | ||
18 | .author { color: #999; } | 18 | .author { color: #999; } |
19 | 19 | ||
20 | p { | 20 | p { |
21 | - padding-top:5px; | 21 | + padding-top:5px; |
22 | margin:0; | 22 | margin:0; |
23 | color:#222; | 23 | color:#222; |
24 | img { | 24 | img { |
app/assets/stylesheets/main.scss
@@ -143,6 +143,7 @@ $hover: #fdf5d9; | @@ -143,6 +143,7 @@ $hover: #fdf5d9; | ||
143 | @import "sections/projects.scss"; | 143 | @import "sections/projects.scss"; |
144 | @import "sections/merge_requests.scss"; | 144 | @import "sections/merge_requests.scss"; |
145 | @import "sections/graph.scss"; | 145 | @import "sections/graph.scss"; |
146 | +@import "sections/events.scss"; | ||
146 | 147 | ||
147 | /** | 148 | /** |
148 | * This scss file redefine chozen selectbox styles for | 149 | * This scss file redefine chozen selectbox styles for |
app/assets/stylesheets/sections/commits.scss
1 | .commit-box { | 1 | .commit-box { |
2 | @extend .main_box; | 2 | @extend .main_box; |
3 | 3 | ||
4 | - .commit-head { | 4 | + .commit-head { |
5 | @extend .top_box_content; | 5 | @extend .top_box_content; |
6 | 6 | ||
7 | .commit-title { | 7 | .commit-title { |
@@ -29,11 +29,11 @@ | @@ -29,11 +29,11 @@ | ||
29 | 29 | ||
30 | .sha-block { | 30 | .sha-block { |
31 | text-align:right; | 31 | text-align:right; |
32 | - &:first-child { | 32 | + &:first-child { |
33 | padding-bottom:6px; | 33 | padding-bottom:6px; |
34 | } | 34 | } |
35 | 35 | ||
36 | - a { | 36 | + a { |
37 | border-bottom: 1px solid #aaa; | 37 | border-bottom: 1px solid #aaa; |
38 | margin-left: 9px; | 38 | margin-left: 9px; |
39 | } | 39 | } |
@@ -54,7 +54,7 @@ | @@ -54,7 +54,7 @@ | ||
54 | } | 54 | } |
55 | 55 | ||
56 | /** | 56 | /** |
57 | - * | 57 | + * |
58 | * COMMIT SHOw | 58 | * COMMIT SHOw |
59 | * | 59 | * |
60 | */ | 60 | */ |
@@ -71,7 +71,7 @@ | @@ -71,7 +71,7 @@ | ||
71 | background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); | 71 | background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); |
72 | background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); | 72 | background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); |
73 | 73 | ||
74 | - span { | 74 | + span { |
75 | font-size:14px; | 75 | font-size:14px; |
76 | } | 76 | } |
77 | } | 77 | } |
@@ -111,8 +111,8 @@ | @@ -111,8 +111,8 @@ | ||
111 | } | 111 | } |
112 | } | 112 | } |
113 | 113 | ||
114 | - &.img_compared { | ||
115 | - img { | 114 | + &.img_compared { |
115 | + img { | ||
116 | max-width:300px; | 116 | max-width:300px; |
117 | } | 117 | } |
118 | } | 118 | } |
@@ -120,12 +120,12 @@ | @@ -120,12 +120,12 @@ | ||
120 | } | 120 | } |
121 | 121 | ||
122 | .diff_file_content{ | 122 | .diff_file_content{ |
123 | - table { | 123 | + table { |
124 | border:none; | 124 | border:none; |
125 | margin:0px; | 125 | margin:0px; |
126 | padding:0px; | 126 | padding:0px; |
127 | tr { | 127 | tr { |
128 | - td { | 128 | + td { |
129 | font-size:12px; | 129 | font-size:12px; |
130 | } | 130 | } |
131 | } | 131 | } |
@@ -145,29 +145,29 @@ | @@ -145,29 +145,29 @@ | ||
145 | moz-user-select: none; | 145 | moz-user-select: none; |
146 | -khtml-user-select: none; | 146 | -khtml-user-select: none; |
147 | user-select: none; | 147 | user-select: none; |
148 | - a { | 148 | + a { |
149 | float:left; | 149 | float:left; |
150 | width:35px; | 150 | width:35px; |
151 | font-weight:normal; | 151 | font-weight:normal; |
152 | color:#666; | 152 | color:#666; |
153 | - &:hover { | 153 | + &:hover { |
154 | text-decoration:underline; | 154 | text-decoration:underline; |
155 | } | 155 | } |
156 | } | 156 | } |
157 | } | 157 | } |
158 | - .line_content { | ||
159 | - white-space:pre; | 158 | + .line_content { |
159 | + white-space:pre; | ||
160 | height:14px; | 160 | height:14px; |
161 | margin:0px; | 161 | margin:0px; |
162 | padding:0px; | 162 | padding:0px; |
163 | border:none; | 163 | border:none; |
164 | - &.new { | 164 | + &.new { |
165 | background: #CFD; | 165 | background: #CFD; |
166 | } | 166 | } |
167 | - &.old { | 167 | + &.old { |
168 | background: #FDD; | 168 | background: #FDD; |
169 | } | 169 | } |
170 | - &.matched { | 170 | + &.matched { |
171 | color:#ccc; | 171 | color:#ccc; |
172 | background:#fafafa; | 172 | background:#fafafa; |
173 | } | 173 | } |
@@ -182,32 +182,32 @@ | @@ -182,32 +182,32 @@ | ||
182 | 182 | ||
183 | 183 | ||
184 | /** COMMIT ROW **/ | 184 | /** COMMIT ROW **/ |
185 | -.commit { | 185 | +.commit { |
186 | @extend .wll; | 186 | @extend .wll; |
187 | 187 | ||
188 | - .browse_code_link_holder { | 188 | + .browse_code_link_holder { |
189 | @extend .span2; | 189 | @extend .span2; |
190 | float:right; | 190 | float:right; |
191 | } | 191 | } |
192 | 192 | ||
193 | - .committed_ago { | 193 | + .committed_ago { |
194 | float:right; | 194 | float:right; |
195 | @extend .cgray; | 195 | @extend .cgray; |
196 | } | 196 | } |
197 | 197 | ||
198 | - code { | 198 | + code { |
199 | background:#FCEEC1; | 199 | background:#FCEEC1; |
200 | color:$style_color; | 200 | color:$style_color; |
201 | } | 201 | } |
202 | 202 | ||
203 | - .commit_short_id { | 203 | + .commit_short_id { |
204 | float:left; | 204 | float:left; |
205 | @extend .lined; | 205 | @extend .lined; |
206 | min-width:65px; | 206 | min-width:65px; |
207 | font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; | 207 | font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; |
208 | } | 208 | } |
209 | - | ||
210 | - .commit-author-name { | 209 | + |
210 | + .commit-author-name { | ||
211 | color: #777; | 211 | color: #777; |
212 | } | 212 | } |
213 | } | 213 | } |
@@ -0,0 +1,118 @@ | @@ -0,0 +1,118 @@ | ||
1 | +/** | ||
2 | + * Events labels | ||
3 | + * | ||
4 | + */ | ||
5 | +.event_label { | ||
6 | + &.pushed { | ||
7 | + padding:0 2px; | ||
8 | + @extend .alert; | ||
9 | + @extend .alert-info; | ||
10 | + } | ||
11 | + | ||
12 | + &.opened { | ||
13 | + padding:0 2px; | ||
14 | + @extend .alert; | ||
15 | + @extend .alert-success; | ||
16 | + } | ||
17 | + | ||
18 | + &.closed { | ||
19 | + padding:0 2px; | ||
20 | + @extend .alert; | ||
21 | + @extend .alert-error; | ||
22 | + } | ||
23 | + | ||
24 | + &.merged { | ||
25 | + padding:0 2px; | ||
26 | + @extend .alert; | ||
27 | + @extend .alert-success; | ||
28 | + } | ||
29 | + | ||
30 | + &.left, | ||
31 | + &.joined { | ||
32 | + padding:0 2px; | ||
33 | + @extend .alert; | ||
34 | + } | ||
35 | +} | ||
36 | + | ||
37 | +/** | ||
38 | + * Dashboard events feed | ||
39 | + * | ||
40 | + */ | ||
41 | +.event-item { | ||
42 | + min-height:40px; | ||
43 | + border-bottom:1px solid #eee; | ||
44 | + .event-title { | ||
45 | + color:#333; | ||
46 | + font-weight: bold; | ||
47 | + .author_name { | ||
48 | + color:#333; | ||
49 | + } | ||
50 | + } | ||
51 | + .event-body { | ||
52 | + p { | ||
53 | + color:#555; | ||
54 | + } | ||
55 | + .event-info { | ||
56 | + color:#666; | ||
57 | + } | ||
58 | + } | ||
59 | + .avatar { | ||
60 | + width:32px; | ||
61 | + } | ||
62 | + .event_icon { | ||
63 | + float: right; | ||
64 | + border: 1px solid #EEE; | ||
65 | + padding: 5px; | ||
66 | + @include border-radius(5px); | ||
67 | + background: #F9F9F9; | ||
68 | + img { | ||
69 | + width:20px; | ||
70 | + } | ||
71 | + } | ||
72 | + ul { | ||
73 | + margin-left:50px; | ||
74 | + margin-bottom:5px; | ||
75 | + .avatar { | ||
76 | + width:18px; | ||
77 | + margin-top:3px; | ||
78 | + } | ||
79 | + } | ||
80 | + | ||
81 | + padding: 15px 5px; | ||
82 | + &:last-child { border:none } | ||
83 | + .wll:hover { background:none } | ||
84 | + | ||
85 | + .event_commits { | ||
86 | + margin-top: 5px; | ||
87 | + | ||
88 | + li { | ||
89 | + &.commit { | ||
90 | + background: transparent; | ||
91 | + padding:3px; | ||
92 | + border:none; | ||
93 | + font-size:12px; | ||
94 | + } | ||
95 | + &.commits-stat { | ||
96 | + display: block; | ||
97 | + margin-top: 5px; | ||
98 | + } | ||
99 | + } | ||
100 | + } | ||
101 | +} | ||
102 | + | ||
103 | +/** | ||
104 | + * Push event widget | ||
105 | + * | ||
106 | + */ | ||
107 | +.event_lp { | ||
108 | + @extend .ui-box; | ||
109 | + color:#777; | ||
110 | + margin-bottom:20px; | ||
111 | + padding:8px; | ||
112 | + @include border-radius(4px); | ||
113 | + min-height:22px; | ||
114 | + | ||
115 | + .avatar { | ||
116 | + width:24px; | ||
117 | + } | ||
118 | +} |
app/assets/stylesheets/sections/notes.scss
@@ -43,7 +43,9 @@ | @@ -43,7 +43,9 @@ | ||
43 | padding: 8px 0; | 43 | padding: 8px 0; |
44 | overflow: hidden; | 44 | overflow: hidden; |
45 | display: block; | 45 | display: block; |
46 | + position:relative; | ||
46 | img {float: left; margin-right: 10px;} | 47 | img {float: left; margin-right: 10px;} |
48 | + img.emoji {float:none;margin:0;} | ||
47 | .note-author cite{font-style: italic;} | 49 | .note-author cite{font-style: italic;} |
48 | p { color:$style_color; } | 50 | p { color:$style_color; } |
49 | .note-author { color: $style_color;} | 51 | .note-author { color: $style_color;} |
@@ -55,7 +57,9 @@ | @@ -55,7 +57,9 @@ | ||
55 | 57 | ||
56 | .delete-note { | 58 | .delete-note { |
57 | display:none; | 59 | display:none; |
58 | - float:right; | 60 | + position:absolute; |
61 | + right:0; | ||
62 | + top:0; | ||
59 | } | 63 | } |
60 | 64 | ||
61 | &:hover { | 65 | &:hover { |
app/controllers/admin/users_controller.rb
@@ -30,7 +30,7 @@ class Admin::UsersController < AdminController | @@ -30,7 +30,7 @@ class Admin::UsersController < AdminController | ||
30 | 30 | ||
31 | 31 | ||
32 | def new | 32 | def new |
33 | - @admin_user = User.new(projects_limit: Gitlab.config.default_projects_limit) | 33 | + @admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin) |
34 | end | 34 | end |
35 | 35 | ||
36 | def edit | 36 | def edit |
@@ -60,7 +60,7 @@ class Admin::UsersController < AdminController | @@ -60,7 +60,7 @@ class Admin::UsersController < AdminController | ||
60 | def create | 60 | def create |
61 | admin = params[:user].delete("admin") | 61 | admin = params[:user].delete("admin") |
62 | 62 | ||
63 | - @admin_user = User.new(params[:user]) | 63 | + @admin_user = User.new(params[:user], as: :admin) |
64 | @admin_user.admin = (admin && admin.to_i > 0) | 64 | @admin_user.admin = (admin && admin.to_i > 0) |
65 | 65 | ||
66 | respond_to do |format| | 66 | respond_to do |format| |
@@ -86,7 +86,7 @@ class Admin::UsersController < AdminController | @@ -86,7 +86,7 @@ class Admin::UsersController < AdminController | ||
86 | @admin_user.admin = (admin && admin.to_i > 0) | 86 | @admin_user.admin = (admin && admin.to_i > 0) |
87 | 87 | ||
88 | respond_to do |format| | 88 | respond_to do |format| |
89 | - if @admin_user.update_attributes(params[:user]) | 89 | + if @admin_user.update_attributes(params[:user], as: :admin) |
90 | format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } | 90 | format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } |
91 | format.json { head :ok } | 91 | format.json { head :ok } |
92 | else | 92 | else |
app/controllers/commits_controller.rb
@@ -52,6 +52,7 @@ class CommitsController < ApplicationController | @@ -52,6 +52,7 @@ class CommitsController < ApplicationController | ||
52 | @commits = result[:commits] | 52 | @commits = result[:commits] |
53 | @commit = result[:commit] | 53 | @commit = result[:commit] |
54 | @diffs = result[:diffs] | 54 | @diffs = result[:diffs] |
55 | + @refs_are_same = result[:same] | ||
55 | @line_notes = [] | 56 | @line_notes = [] |
56 | 57 | ||
57 | @commits = CommitDecorator.decorate(@commits) | 58 | @commits = CommitDecorator.decorate(@commits) |
app/controllers/hooks_controller.rb
app/controllers/issues_controller.rb
1 | class IssuesController < ApplicationController | 1 | class IssuesController < ApplicationController |
2 | - before_filter :authenticate_user! | ||
3 | before_filter :project | 2 | before_filter :project |
4 | before_filter :module_enabled | 3 | before_filter :module_enabled |
5 | before_filter :issue, only: [:edit, :update, :destroy, :show] | 4 | before_filter :issue, only: [:edit, :update, :destroy, :show] |
app/controllers/labels_controller.rb
app/controllers/merge_requests_controller.rb
1 | class MergeRequestsController < ApplicationController | 1 | class MergeRequestsController < ApplicationController |
2 | - before_filter :authenticate_user! | ||
3 | before_filter :project | 2 | before_filter :project |
4 | before_filter :module_enabled | 3 | before_filter :module_enabled |
5 | before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw] | 4 | before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw] |
app/controllers/milestones_controller.rb
1 | class MilestonesController < ApplicationController | 1 | class MilestonesController < ApplicationController |
2 | - before_filter :authenticate_user! | ||
3 | before_filter :project | 2 | before_filter :project |
4 | before_filter :module_enabled | 3 | before_filter :module_enabled |
5 | before_filter :milestone, only: [:edit, :update, :destroy, :show] | 4 | before_filter :milestone, only: [:edit, :update, :destroy, :show] |
app/controllers/snippets_controller.rb
1 | class SnippetsController < ApplicationController | 1 | class SnippetsController < ApplicationController |
2 | - before_filter :authenticate_user! | ||
3 | before_filter :project | 2 | before_filter :project |
4 | before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw] | 3 | before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw] |
5 | layout "project" | 4 | layout "project" |
app/helpers/tree_helper.rb
@@ -32,7 +32,11 @@ module TreeHelper | @@ -32,7 +32,11 @@ module TreeHelper | ||
32 | # | 32 | # |
33 | # Returns boolean | 33 | # Returns boolean |
34 | def markup?(filename) | 34 | def markup?(filename) |
35 | - filename.end_with?(*%w(.mdown .md .markdown .textile .rdoc .org .creole | ||
36 | - .mediawiki .rst .asciidoc .pod)) | 35 | + filename.end_with?(*%w(.textile .rdoc .org .creole |
36 | + .mediawiki .rst .asciidoc .pod)) | ||
37 | + end | ||
38 | + | ||
39 | + def gitlab_markdown?(filename) | ||
40 | + filename.end_with?(*%w(.mdown .md .markdown)) | ||
37 | end | 41 | end |
38 | end | 42 | end |
app/models/commit.rb
@@ -82,20 +82,24 @@ class Commit | @@ -82,20 +82,24 @@ class Commit | ||
82 | end | 82 | end |
83 | 83 | ||
84 | def compare(project, from, to) | 84 | def compare(project, from, to) |
85 | - first = project.commit(to.try(:strip)) | ||
86 | - last = project.commit(from.try(:strip)) | ||
87 | - | ||
88 | result = { | 85 | result = { |
89 | commits: [], | 86 | commits: [], |
90 | diffs: [], | 87 | diffs: [], |
91 | - commit: nil | 88 | + commit: nil, |
89 | + same: false | ||
92 | } | 90 | } |
93 | 91 | ||
92 | + return result unless from && to | ||
93 | + | ||
94 | + first = project.commit(to.try(:strip)) | ||
95 | + last = project.commit(from.try(:strip)) | ||
96 | + | ||
94 | if first && last | 97 | if first && last |
95 | commits = [first, last].sort_by(&:created_at) | 98 | commits = [first, last].sort_by(&:created_at) |
96 | younger = commits.first | 99 | younger = commits.first |
97 | older = commits.last | 100 | older = commits.last |
98 | 101 | ||
102 | + result[:same] = (younger.id == older.id) | ||
99 | result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)} | 103 | result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)} |
100 | result[:diffs] = project.repo.diff(younger.id, older.id) rescue [] | 104 | result[:diffs] = project.repo.diff(younger.id, older.id) rescue [] |
101 | result[:commit] = Commit.new(older) | 105 | result[:commit] = Commit.new(older) |
app/models/event.rb
@@ -132,6 +132,7 @@ class Event < ActiveRecord::Base | @@ -132,6 +132,7 @@ class Event < ActiveRecord::Base | ||
132 | end | 132 | end |
133 | end | 133 | end |
134 | 134 | ||
135 | + | ||
135 | delegate :name, :email, to: :author, prefix: true, allow_nil: true | 136 | delegate :name, :email, to: :author, prefix: true, allow_nil: true |
136 | delegate :title, to: :issue, prefix: true, allow_nil: true | 137 | delegate :title, to: :issue, prefix: true, allow_nil: true |
137 | delegate :title, to: :merge_request, prefix: true, allow_nil: true | 138 | delegate :title, to: :merge_request, prefix: true, allow_nil: true |
app/models/user.rb
@@ -6,8 +6,9 @@ class User < ActiveRecord::Base | @@ -6,8 +6,9 @@ class User < ActiveRecord::Base | ||
6 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable | 6 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable |
7 | 7 | ||
8 | attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, | 8 | attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, |
9 | - :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, | ||
10 | - :theme_id, :force_random_password, :extern_uid, :provider | 9 | + :name, :skype, :linkedin, :twitter, :dark_scheme, |
10 | + :theme_id, :force_random_password, :extern_uid, :provider, :as => [:default, :admin] | ||
11 | + attr_accessible :projects_limit, :as => :admin | ||
11 | 12 | ||
12 | attr_accessor :force_random_password | 13 | attr_accessor :force_random_password |
13 | 14 |
app/roles/repository.rb
@@ -79,6 +79,14 @@ module Repository | @@ -79,6 +79,14 @@ module Repository | ||
79 | @heads ||= repo.heads | 79 | @heads ||= repo.heads |
80 | end | 80 | end |
81 | 81 | ||
82 | + def branches_names | ||
83 | + heads.map(&:name) | ||
84 | + end | ||
85 | + | ||
86 | + def ref_names | ||
87 | + [branches_names + tags].flatten | ||
88 | + end | ||
89 | + | ||
82 | def tree(fcommit, path = nil) | 90 | def tree(fcommit, path = nil) |
83 | fcommit = commit if fcommit == :head | 91 | fcommit = commit if fcommit == :head |
84 | tree = fcommit.tree | 92 | tree = fcommit.tree |
app/views/commits/compare.html.haml
1 | = render "head" | 1 | = render "head" |
2 | 2 | ||
3 | -%h3 | 3 | +%h3.page_title |
4 | Compare View | 4 | Compare View |
5 | %hr | 5 | %hr |
6 | 6 | ||
7 | %div | 7 | %div |
8 | - %p | 8 | + %p.slead |
9 | Fill input field with commit id like | 9 | Fill input field with commit id like |
10 | - %code '4eedf23' | 10 | + %code.label_branch 4eedf23 |
11 | or branch/tag name like | 11 | or branch/tag name like |
12 | - %code master | ||
13 | - & press compare button for commits list, code diff. | 12 | + %code.label_branch master |
13 | + and press compare button for commits list, code diff. | ||
14 | 14 | ||
15 | %br | 15 | %br |
16 | 16 | ||
@@ -19,22 +19,24 @@ | @@ -19,22 +19,24 @@ | ||
19 | = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" | 19 | = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" |
20 | = "..." | 20 | = "..." |
21 | = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" | 21 | = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" |
22 | + - if @refs_are_same | ||
23 | + .alert | ||
24 | + %span Refs are the same | ||
22 | .actions | 25 | .actions |
23 | - = submit_tag "Compare", class: "btn primary" | 26 | + = submit_tag "Compare", class: "btn primary wide commits-compare-btn" |
24 | 27 | ||
25 | - | ||
26 | -- unless @commits.empty? | 28 | +- if @commits.present? |
27 | %div.ui-box | 29 | %div.ui-box |
28 | %h5.small Commits (#{@commits.count}) | 30 | %h5.small Commits (#{@commits.count}) |
29 | %ul.unstyled= render @commits | 31 | %ul.unstyled= render @commits |
30 | 32 | ||
31 | -- unless @diffs.empty? | ||
32 | - %h4 Diff | ||
33 | - = render "commits/diffs", diffs: @diffs | 33 | + - unless @diffs.empty? |
34 | + %h4 Diff | ||
35 | + = render "commits/diffs", diffs: @diffs | ||
34 | 36 | ||
35 | :javascript | 37 | :javascript |
36 | $(function() { | 38 | $(function() { |
37 | - var availableTags = #{@project.heads.map(&:name).to_json}; | 39 | + var availableTags = #{@project.ref_names.to_json}; |
38 | 40 | ||
39 | $("#from").autocomplete({ | 41 | $("#from").autocomplete({ |
40 | source: availableTags, | 42 | source: availableTags, |
@@ -45,5 +47,7 @@ | @@ -45,5 +47,7 @@ | ||
45 | source: availableTags, | 47 | source: availableTags, |
46 | minLength: 1 | 48 | minLength: 1 |
47 | }); | 49 | }); |
50 | + | ||
51 | + disableButtonIfEmptyField('#to', '.commits-compare-btn'); | ||
48 | }); | 52 | }); |
49 | 53 |
app/views/events/_commit.html.haml
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | %li.commit | 2 | %li.commit |
3 | %p | 3 | %p |
4 | = link_to commit.short_id(8), project_commit_path(project, id: commit.id), class: "commit_short_id" | 4 | = link_to commit.short_id(8), project_commit_path(project, id: commit.id), class: "commit_short_id" |
5 | - %strong.cdark= commit.author_name | 5 | + %span= commit.author_name |
6 | – | 6 | – |
7 | = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 | 7 | = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 |
8 | = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding" | 8 | = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding" |
app/views/events/_event.html.haml
1 | - if event.allowed? | 1 | - if event.allowed? |
2 | - - if event.issue? | ||
3 | - .event_feed | 2 | + %div.event-item |
3 | + - if event.issue? | ||
4 | = render "events/event_issue", event: event | 4 | = render "events/event_issue", event: event |
5 | 5 | ||
6 | - - elsif event.merge_request? | ||
7 | - .event_feed | 6 | + - elsif event.merge_request? |
8 | = render "events/event_merge_request", event: event | 7 | = render "events/event_merge_request", event: event |
9 | 8 | ||
10 | - - elsif event.push? | ||
11 | - .event_feed | 9 | + - elsif event.push? |
12 | = render "events/event_push", event: event | 10 | = render "events/event_push", event: event |
13 | 11 | ||
14 | - - elsif event.membership_changed? | ||
15 | - .event_feed | 12 | + - elsif event.membership_changed? |
16 | = render "events/event_membership_changed", event: event | 13 | = render "events/event_membership_changed", event: event |
17 | 14 | ||
15 | + %span.cgray.right | ||
16 | + = time_ago_in_words(event.created_at) | ||
17 | + ago. | ||
18 | + .clearfix |
app/views/events/_event_issue.html.haml
1 | = image_tag gravatar_icon(event.author_email), class: "avatar" | 1 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
2 | -%strong #{event.author_name} | ||
3 | -%span.event_label{class: event.action_name}= event.action_name | ||
4 | -issue | ||
5 | -= link_to project_issue_path(event.project, event.issue) do | ||
6 | - %strong= truncate event.issue_title | ||
7 | -at | ||
8 | -%strong= link_to event.project.name, event.project | ||
9 | -%span.cgray | ||
10 | - = time_ago_in_words(event.created_at) | ||
11 | - ago. | 2 | +.event-title |
3 | + %strong.author_name #{event.author_name} | ||
4 | + %span.event_label{class: event.action_name} #{event.action_name} issue | ||
5 | + = link_to project_issue_path(event.project, event.issue) do | ||
6 | + %strong= truncate event.issue_title | ||
7 | + at | ||
8 | + %strong= link_to event.project.name, event.project |
app/views/events/_event_last_push.html.haml
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | .event_lp | 2 | .event_lp |
3 | %div | 3 | %div |
4 | = image_tag gravatar_icon(event.author_email), class: "avatar" | 4 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
5 | - %span Your pushed to | 5 | + %span You pushed to |
6 | = event.ref_type | 6 | = event.ref_type |
7 | = link_to project_commits_path(event.project, ref: event.ref_name) do | 7 | = link_to project_commits_path(event.project, ref: event.ref_name) do |
8 | %strong= truncate(event.ref_name, length: 28) | 8 | %strong= truncate(event.ref_name, length: 28) |
app/views/events/_event_membership_changed.html.haml
1 | = image_tag gravatar_icon(event.author_email), class: "avatar" | 1 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
2 | -%strong #{event.author_name} | ||
3 | -%span.event_label{class: event.action_name}= event.action_name | ||
4 | -project | ||
5 | -%strong= link_to event.project_name, event.project | ||
6 | -%span.cgray | ||
7 | - = time_ago_in_words(event.created_at) | ||
8 | - ago. | 2 | +.event-title |
3 | + %strong.author_name #{event.author_name} | ||
4 | + %span.event_label{class: event.action_name} #{event.action_name} project | ||
5 | + %strong= link_to event.project_name, event.project | ||
6 | + %span.cgray | ||
7 | + = time_ago_in_words(event.created_at) | ||
8 | + ago. | ||
9 | 9 |
app/views/events/_event_merge_request.html.haml
1 | - if event.action_name == "merged" | 1 | - if event.action_name == "merged" |
2 | .event_icon= image_tag "event_mr_merged.png" | 2 | .event_icon= image_tag "event_mr_merged.png" |
3 | = image_tag gravatar_icon(event.author_email), class: "avatar" | 3 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
4 | -%strong #{event.author_name} | ||
5 | -%span.event_label{class: event.action_name}= event.action_name | ||
6 | -merge request | ||
7 | -= link_to project_merge_request_path(event.project, event.merge_request) do | ||
8 | - %strong= truncate event.merge_request_title | ||
9 | -at | ||
10 | -%strong= link_to event.project.name, event.project | ||
11 | -%span.cgray | ||
12 | - = time_ago_in_words(event.created_at) | ||
13 | - ago. | ||
14 | -%br | ||
15 | -%span= event.merge_request.source_branch | ||
16 | -→ | ||
17 | -%span= event.merge_request.target_branch | 4 | +.event-title |
5 | + %strong.author_name #{event.author_name} | ||
6 | + %span.event_label{class: event.action_name} #{event.action_name} merge request | ||
7 | + = link_to project_merge_request_path(event.project, event.merge_request) do | ||
8 | + %strong= truncate event.merge_request_title | ||
9 | + at | ||
10 | + %strong= link_to event.project.name, event.project | ||
11 | +.event-body | ||
12 | + .event-info | ||
13 | + %span= event.merge_request.source_branch | ||
14 | + → | ||
15 | + %span= event.merge_request.target_branch | ||
18 | 16 |
app/views/events/_event_push.html.haml
1 | %div | 1 | %div |
2 | .event_icon= image_tag "event_push.png" | 2 | .event_icon= image_tag "event_push.png" |
3 | = image_tag gravatar_icon(event.author_email), class: "avatar" | 3 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
4 | - %strong #{event.author_name} | ||
5 | - %span.event_label.pushed= event.push_action_name | ||
6 | - = event.ref_type | ||
7 | - = link_to project_commits_path(event.project, ref: event.ref_name) do | ||
8 | - %strong= event.ref_name | ||
9 | - at | ||
10 | - %strong= link_to event.project.name, event.project | ||
11 | - %span.cgray | ||
12 | - = time_ago_in_words(event.created_at) | ||
13 | - ago. | 4 | + |
5 | + .event-title | ||
6 | + %strong.author_name #{event.author_name} | ||
7 | + %span.event_label.pushed #{event.push_action_name} #{event.ref_type} | ||
8 | + = link_to project_commits_path(event.project, ref: event.ref_name) do | ||
9 | + %strong= event.ref_name | ||
10 | + at | ||
11 | + %strong= link_to event.project.name, event.project | ||
14 | 12 | ||
15 | - if event.push_with_commits? | 13 | - if event.push_with_commits? |
16 | - - if event.commits_count > 1 | ||
17 | - = link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do | ||
18 | - %strong #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]} | ||
19 | - project = event.project | 14 | - project = event.project |
20 | - %ul.unstyled.event_commits | ||
21 | - - if event.commits_count > 3 | ||
22 | - - event.commits[0...2].each do |commit| | ||
23 | - = render "events/commit", commit: commit, project: project | ||
24 | - %li | ||
25 | - %br | ||
26 | - \... and #{event.commits_count - 2} more commits | ||
27 | - - else | ||
28 | - - event.commits.each do |commit| | 15 | + .event-body |
16 | + %ul.unstyled.event_commits | ||
17 | + - few_commits = event.commits[0...2] | ||
18 | + - few_commits.each do |commit| | ||
29 | = render "events/commit", commit: commit, project: project | 19 | = render "events/commit", commit: commit, project: project |
30 | 20 | ||
21 | + %li.commits-stat | ||
22 | + - if event.commits_count > 2 | ||
23 | + %span ... and #{event.commits_count - 2} more commits. | ||
24 | + = link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do | ||
25 | + %strong Compare → #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]} | ||
26 | + .clearfix |
app/views/help/api.html.haml
1 | -%h3 API | 1 | +%h3.page_title API |
2 | .back_link | 2 | .back_link |
3 | = link_to help_path do | 3 | = link_to help_path do |
4 | ← to index | 4 | ← to index |
5 | -%hr | 5 | +%br |
6 | 6 | ||
7 | -%ol | 7 | +%ul.nav.nav-tabs.log-tabs |
8 | + %li.active | ||
9 | + = link_to "README", "#README", 'data-toggle' => 'tab' | ||
8 | %li | 10 | %li |
9 | - %a{href: "#README"} README | 11 | + = link_to "Projects", "#projects", 'data-toggle' => 'tab' |
10 | %li | 12 | %li |
11 | - %a{href: "#projects"} Projects | 13 | + = link_to "Snippets", "#snippets", 'data-toggle' => 'tab' |
12 | %li | 14 | %li |
13 | - %a{href: "#snippets"} Snippets | 15 | + = link_to "Repositories", "#repositories", 'data-toggle' => 'tab' |
14 | %li | 16 | %li |
15 | - %a{href: "#users"} Users | 17 | + = link_to "Users", "#users", 'data-toggle' => 'tab' |
16 | %li | 18 | %li |
17 | - %a{href: "#issues"} Issues | 19 | + = link_to "Session", "#session", 'data-toggle' => 'tab' |
18 | %li | 20 | %li |
19 | - %a{href: "#milestones"} Milestones | ||
20 | - | ||
21 | -.file_holder#README | ||
22 | - .file_title | ||
23 | - %i.icon-file | ||
24 | - README | ||
25 | - .file_content.wiki | ||
26 | - = preserve do | ||
27 | - = markdown File.read(Rails.root.join("doc", "api", "README.md")) | ||
28 | - | ||
29 | -%br | ||
30 | - | ||
31 | -.file_holder#projects | ||
32 | - .file_title | ||
33 | - %i.icon-file | ||
34 | - Projects | ||
35 | - .file_content.wiki | ||
36 | - = preserve do | ||
37 | - = markdown File.read(Rails.root.join("doc", "api", "projects.md")) | 21 | + = link_to "Issues", "#issues", 'data-toggle' => 'tab' |
22 | + %li | ||
23 | + = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' | ||
38 | 24 | ||
39 | -%br | 25 | +.tab-content |
26 | + .tab-pane.active#README | ||
27 | + .file_holder | ||
28 | + .file_title | ||
29 | + %i.icon-file | ||
30 | + README | ||
31 | + .file_content.wiki | ||
32 | + = preserve do | ||
33 | + = markdown File.read(Rails.root.join("doc", "api", "README.md")) | ||
40 | 34 | ||
41 | -.file_holder#snippets | ||
42 | - .file_title | ||
43 | - %i.icon-file | ||
44 | - Projects Snippets | ||
45 | - .file_content.wiki | ||
46 | - = preserve do | ||
47 | - = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) | 35 | + .tab-pane#projects |
36 | + .file_holder | ||
37 | + .file_title | ||
38 | + %i.icon-file | ||
39 | + Projects | ||
40 | + .file_content.wiki | ||
41 | + = preserve do | ||
42 | + = markdown File.read(Rails.root.join("doc", "api", "projects.md")) | ||
48 | 43 | ||
49 | -%br | 44 | + .tab-pane#snippets |
45 | + .file_holder | ||
46 | + .file_title | ||
47 | + %i.icon-file | ||
48 | + Projects Snippets | ||
49 | + .file_content.wiki | ||
50 | + = preserve do | ||
51 | + = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) | ||
50 | 52 | ||
51 | -.file_holder#users | ||
52 | - .file_title | ||
53 | - %i.icon-file | ||
54 | - Users | ||
55 | - .file_content.wiki | ||
56 | - = preserve do | ||
57 | - = markdown File.read(Rails.root.join("doc", "api", "users.md")) | 53 | + .tab-pane#repositories |
54 | + .file_holder | ||
55 | + .file_title | ||
56 | + %i.icon-file | ||
57 | + Projects | ||
58 | + .file_content.wiki | ||
59 | + = preserve do | ||
60 | + = markdown File.read(Rails.root.join("doc", "api", "repositories.md")) | ||
58 | 61 | ||
59 | -%br | 62 | + .tab-pane#users |
63 | + .file_holder | ||
64 | + .file_title | ||
65 | + %i.icon-file | ||
66 | + Users | ||
67 | + .file_content.wiki | ||
68 | + = preserve do | ||
69 | + = markdown File.read(Rails.root.join("doc", "api", "users.md")) | ||
60 | 70 | ||
61 | -.file_holder#issues | ||
62 | - .file_title | ||
63 | - %i.icon-file | ||
64 | - Issues | ||
65 | - .file_content.wiki | ||
66 | - = preserve do | ||
67 | - = markdown File.read(Rails.root.join("doc", "api", "issues.md")) | 71 | + .tab-pane#session |
72 | + .file_holder | ||
73 | + .file_title | ||
74 | + %i.icon-file | ||
75 | + Session | ||
76 | + .file_content.wiki | ||
77 | + = preserve do | ||
78 | + = markdown File.read(Rails.root.join("doc", "api", "session.md")) | ||
68 | 79 | ||
69 | -%br | 80 | + .tab-pane#issues |
81 | + .file_holder | ||
82 | + .file_title | ||
83 | + %i.icon-file | ||
84 | + Issues | ||
85 | + .file_content.wiki | ||
86 | + = preserve do | ||
87 | + = markdown File.read(Rails.root.join("doc", "api", "issues.md")) | ||
70 | 88 | ||
71 | -.file_holder#milestones | ||
72 | - .file_title | ||
73 | - %i.icon-file | ||
74 | - Milestones | ||
75 | - .file_content.wiki | ||
76 | - = preserve do | ||
77 | - = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) | 89 | + .tab-pane#milestones |
90 | + .file_holder | ||
91 | + .file_title | ||
92 | + %i.icon-file | ||
93 | + Milestones | ||
94 | + .file_content.wiki | ||
95 | + = preserve do | ||
96 | + = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) |
app/views/notes/_common_form.html.haml
@@ -37,3 +37,14 @@ | @@ -37,3 +37,14 @@ | ||
37 | = f.file_field :attachment, class: "input-file" | 37 | = f.file_field :attachment, class: "input-file" |
38 | %span.hint Any file less than 10 MB | 38 | %span.hint Any file less than 10 MB |
39 | 39 | ||
40 | +:javascript | ||
41 | + $(function(){ | ||
42 | + var names = #{@project.users.pluck(:name)}, emoji = ['+1', '-1']; | ||
43 | + var emoji = $.map(emoji, function(value, i) {return {key:value + ':', name:value}}); | ||
44 | + $('#note_note'). | ||
45 | + atWho('@', { data: names }). | ||
46 | + atWho(':', { | ||
47 | + data: emoji, | ||
48 | + tpl: "<li data-value='${key}'>${name} #{escape_javascript image_tag('emoji/${name}.png', :size => '20x20')}</li>" | ||
49 | + }); | ||
50 | + }); |
app/views/refs/_tree.html.haml
@@ -43,7 +43,11 @@ | @@ -43,7 +43,11 @@ | ||
43 | %i.icon-file | 43 | %i.icon-file |
44 | = content.name | 44 | = content.name |
45 | .file_content.wiki | 45 | .file_content.wiki |
46 | - = raw GitHub::Markup.render(content.name, content.data) | 46 | + - if gitlab_markdown?(content.name) |
47 | + = preserve do | ||
48 | + = markdown(content.data) | ||
49 | + - else | ||
50 | + = raw GitHub::Markup.render(content.name, content.data) | ||
47 | 51 | ||
48 | :javascript | 52 | :javascript |
49 | $(function(){ | 53 | $(function(){ |
app/views/refs/_tree_file.html.haml
@@ -9,7 +9,11 @@ | @@ -9,7 +9,11 @@ | ||
9 | = link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small" | 9 | = link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small" |
10 | = link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small" | 10 | = link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small" |
11 | - if file.text? | 11 | - if file.text? |
12 | - - if markup?(name) | 12 | + - if gitlab_markdown?(name) |
13 | + .file_content.wiki | ||
14 | + = preserve do | ||
15 | + = markdown(file.data) | ||
16 | + - elsif markup?(name) | ||
13 | .file_content.wiki | 17 | .file_content.wiki |
14 | = raw GitHub::Markup.render(name, file.data) | 18 | = raw GitHub::Markup.render(name, file.data) |
15 | - else | 19 | - else |
@@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
1 | +# Custom Redis configuration | ||
2 | +rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' | ||
3 | +rails_env = ENV['RAILS_ENV'] || 'development' | ||
4 | +config_file = File.join(rails_root, 'config', 'resque.yml') | ||
5 | + | ||
6 | +if File.exists?(config_file) | ||
7 | + resque_config = YAML.load_file(config_file) | ||
8 | + Resque.redis = resque_config[rails_env] | ||
9 | +end | ||
10 | + | ||
11 | +# Queues | ||
12 | +Resque.watch_queue(PostReceive.instance_variable_get("@queue")) | ||
13 | + | ||
14 | +# Authentication | ||
15 | +require 'resque/server' | ||
16 | +class Authentication | ||
17 | + def initialize(app) | ||
18 | + @app = app | ||
19 | + end | ||
20 | + | ||
21 | + def call(env) | ||
22 | + account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user) | ||
23 | + raise "Access denied" if !account.admin? | ||
24 | + @app.call(env) | ||
25 | + end | ||
26 | +end | ||
27 | + | ||
28 | +Resque::Server.use Authentication | ||
29 | + | ||
30 | +# Mailer | ||
31 | +Resque::Mailer.excluded_environments = [] |
config/initializers/4_resque_queues.rb
@@ -1 +0,0 @@ | @@ -1 +0,0 @@ | ||
1 | -Resque.watch_queue(PostReceive.instance_variable_get("@queue")) |
config/initializers/resque.rb
@@ -1,8 +0,0 @@ | @@ -1,8 +0,0 @@ | ||
1 | -rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' | ||
2 | -rails_env = ENV['RAILS_ENV'] || 'development' | ||
3 | -config_file = File.join(rails_root, 'config', 'resque.yml') | ||
4 | - | ||
5 | -if File.exists?(config_file) | ||
6 | - resque_config = YAML.load_file(config_file) | ||
7 | - Resque.redis = resque_config[rails_env] | ||
8 | -end |
config/initializers/resque_authentication.rb
@@ -1,14 +0,0 @@ | @@ -1,14 +0,0 @@ | ||
1 | -require 'resque/server' | ||
2 | -class Authentication | ||
3 | - def initialize(app) | ||
4 | - @app = app | ||
5 | - end | ||
6 | - | ||
7 | - def call(env) | ||
8 | - account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user) | ||
9 | - raise "Access denied" if !account.admin? | ||
10 | - @app.call(env) | ||
11 | - end | ||
12 | -end | ||
13 | - | ||
14 | -Resque::Server.use Authentication | ||
15 | \ No newline at end of file | 0 | \ No newline at end of file |
config/initializers/resque_mailer.rb
@@ -1 +0,0 @@ | @@ -1 +0,0 @@ | ||
1 | -Resque::Mailer.excluded_environments = [] |
config/unicorn.rb.example
@@ -6,7 +6,7 @@ working_directory app_dir | @@ -6,7 +6,7 @@ working_directory app_dir | ||
6 | # worker spawn times | 6 | # worker spawn times |
7 | preload_app true | 7 | preload_app true |
8 | 8 | ||
9 | -# nuke workers after 60 seconds (the default) | 9 | +# nuke workers after 30 seconds (60 is the default) |
10 | timeout 30 | 10 | timeout 30 |
11 | 11 | ||
12 | # listen on a Unix domain socket and/or a TCP port, | 12 | # listen on a Unix domain socket and/or a TCP port, |
doc/api/README.md
@@ -30,8 +30,9 @@ When listing resources you can pass the following parameters: | @@ -30,8 +30,9 @@ When listing resources you can pass the following parameters: | ||
30 | ## Contents | 30 | ## Contents |
31 | 31 | ||
32 | + [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md) | 32 | + [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md) |
33 | ++ [Session](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/session.md) | ||
33 | + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md) | 34 | + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md) |
34 | + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) | 35 | + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) |
36 | ++ [Repositories](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/repositories.md) | ||
35 | + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) | 37 | + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) |
36 | + [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) | 38 | + [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) |
37 | -+ [SSH Keys](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/keys.md) |
doc/api/keys.md
@@ -1,79 +0,0 @@ | @@ -1,79 +0,0 @@ | ||
1 | -## List keys | ||
2 | - | ||
3 | -Get a list of currently authenticated user's keys. | ||
4 | - | ||
5 | -``` | ||
6 | -GET /keys | ||
7 | -``` | ||
8 | - | ||
9 | -```json | ||
10 | -[ | ||
11 | - { | ||
12 | - "id": 1, | ||
13 | - "title" : "Public key" | ||
14 | - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
15 | - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
16 | - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", | ||
17 | - }, | ||
18 | - { | ||
19 | - "id": 3, | ||
20 | - "title" : "Another Public key" | ||
21 | - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
22 | - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
23 | - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | ||
24 | - } | ||
25 | -] | ||
26 | -``` | ||
27 | - | ||
28 | -## Single key | ||
29 | - | ||
30 | -Get a single key. | ||
31 | - | ||
32 | -``` | ||
33 | -GET /keys/:id | ||
34 | -``` | ||
35 | - | ||
36 | -Parameters: | ||
37 | - | ||
38 | -+ `id` (required) - The ID of a key | ||
39 | - | ||
40 | -```json | ||
41 | -{ | ||
42 | - "id": 1, | ||
43 | - "title" : "Public key" | ||
44 | - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
45 | - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
46 | - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | ||
47 | -} | ||
48 | -``` | ||
49 | -## Add key | ||
50 | - | ||
51 | -Create new key owned by currently authenticated user | ||
52 | - | ||
53 | -``` | ||
54 | -POST /keys | ||
55 | -``` | ||
56 | - | ||
57 | -Parameters: | ||
58 | - | ||
59 | -+ `title` (required) - new SSH Key's title | ||
60 | -+ `key` (required) - new SSH key | ||
61 | - | ||
62 | -Will return created key with status `201 Created` on success, or `404 Not | ||
63 | -found` on fail. | ||
64 | - | ||
65 | -## Delete key | ||
66 | - | ||
67 | -Delete key owned by currently authenticated user | ||
68 | - | ||
69 | -``` | ||
70 | -DELETE /keys/:id | ||
71 | -``` | ||
72 | - | ||
73 | -Parameters: | ||
74 | - | ||
75 | -+ `id` (required) - key ID | ||
76 | - | ||
77 | -Will return `200 OK` on success, or `404 Not Found` on fail. | ||
78 | - | ||
79 | - |
doc/api/projects.md
@@ -102,7 +102,7 @@ Parameters: | @@ -102,7 +102,7 @@ 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 | 105 | ++ `description` (optional) - short project description |
106 | + `default_branch` (optional) - 'master' by default | 106 | + `default_branch` (optional) - 'master' by default |
107 | + `issues_enabled` (optional) - enabled by default | 107 | + `issues_enabled` (optional) - enabled by default |
108 | + `wall_enabled` (optional) - enabled by default | 108 | + `wall_enabled` (optional) - enabled by default |
@@ -112,66 +112,89 @@ Parameters: | @@ -112,66 +112,89 @@ Parameters: | ||
112 | 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 |
113 | found` on fail. | 113 | found` on fail. |
114 | 114 | ||
115 | -## Get project users | 115 | +## List project team members |
116 | 116 | ||
117 | -Get users and access roles for existing project | 117 | +Get a list of project team members. |
118 | 118 | ||
119 | ``` | 119 | ``` |
120 | -GET /projects/:id/users | 120 | +GET /projects/:id/members |
121 | ``` | 121 | ``` |
122 | 122 | ||
123 | Parameters: | 123 | Parameters: |
124 | 124 | ||
125 | + `id` (required) - The ID or code name of a project | 125 | + `id` (required) - The ID or code name of a project |
126 | 126 | ||
127 | -Will return users and their access roles with status `200 OK` on success, or `404 Not found` on fail. | 127 | +## Get project team member |
128 | 128 | ||
129 | -## Add project users | 129 | +Get a project team member. |
130 | 130 | ||
131 | -Add users to exiting project | 131 | +``` |
132 | +GET /projects/:id/members/:user_id | ||
133 | +``` | ||
134 | + | ||
135 | +Parameters: | ||
136 | + | ||
137 | ++ `id` (required) - The ID or code name of a project | ||
138 | ++ `user_id` (required) - The ID of a user | ||
132 | 139 | ||
140 | +```json | ||
141 | +{ | ||
142 | + | ||
143 | + "id": 1, | ||
144 | + "email": "john@example.com", | ||
145 | + "name": "John Smith", | ||
146 | + "blocked": false, | ||
147 | + "created_at": "2012-05-23T08:00:58Z", | ||
148 | + "access_level": 40 | ||
149 | +} | ||
133 | ``` | 150 | ``` |
134 | -POST /projects/:id/users | 151 | + |
152 | +## Add project team member | ||
153 | + | ||
154 | +Add a user to a project team. | ||
155 | + | ||
156 | +``` | ||
157 | +POST /projects/:id/members | ||
135 | ``` | 158 | ``` |
136 | 159 | ||
137 | Parameters: | 160 | Parameters: |
138 | 161 | ||
139 | + `id` (required) - The ID or code name of a project | 162 | + `id` (required) - The ID or code name of a project |
140 | -+ `user_ids` (required) - The ID list of users to add | ||
141 | -+ `project_access` (required) - Project access level | 163 | ++ `user_id` (required) - The ID of a user to add |
164 | ++ `access_level` (required) - Project access level | ||
142 | 165 | ||
143 | Will return status `201 Created` on success, or `404 Not found` on fail. | 166 | Will return status `201 Created` on success, or `404 Not found` on fail. |
144 | 167 | ||
145 | -## Update project users access level | 168 | +## Edit project team member |
146 | 169 | ||
147 | -Update existing users to specified access level | 170 | +Update project team member to specified access level. |
148 | 171 | ||
149 | ``` | 172 | ``` |
150 | -PUT /projects/:id/users | 173 | +PUT /projects/:id/members/:user_id |
151 | ``` | 174 | ``` |
152 | 175 | ||
153 | Parameters: | 176 | Parameters: |
154 | 177 | ||
155 | + `id` (required) - The ID or code name of a project | 178 | + `id` (required) - The ID or code name of a project |
156 | -+ `user_ids` (required) - The ID list of users to add | ||
157 | -+ `project_access` (required) - Project access level | 179 | ++ `user_id` (required) - The ID of a team member |
180 | ++ `access_level` (required) - Project access level | ||
158 | 181 | ||
159 | Will return status `200 OK` on success, or `404 Not found` on fail. | 182 | Will return status `200 OK` on success, or `404 Not found` on fail. |
160 | 183 | ||
161 | -## Delete project users | 184 | +## Remove project team member |
162 | 185 | ||
163 | -Delete users from exiting project | 186 | +Removes user from project team. |
164 | 187 | ||
165 | ``` | 188 | ``` |
166 | -DELETE /projects/:id/users | 189 | +DELETE /projects/:id/members/:user_id |
167 | ``` | 190 | ``` |
168 | 191 | ||
169 | Parameters: | 192 | Parameters: |
170 | 193 | ||
171 | + `id` (required) - The ID or code name of a project | 194 | + `id` (required) - The ID or code name of a project |
172 | -+ `user_ids` (required) - The ID list of users to add | 195 | ++ `user_id` (required) - The ID of a team member |
173 | 196 | ||
174 | -Will return status `200 OK` on success, or `404 Not found` on fail. | 197 | +Status code `200` will be returned on success. |
175 | 198 | ||
176 | ## Get project hooks | 199 | ## Get project hooks |
177 | 200 | ||
@@ -216,135 +239,3 @@ Parameters: | @@ -216,135 +239,3 @@ Parameters: | ||
216 | + `hook_id` (required) - The ID of hook to delete | 239 | + `hook_id` (required) - The ID of hook to delete |
217 | 240 | ||
218 | Will return status `200 OK` on success, or `404 Not found` on fail. | 241 | Will return status `200 OK` on success, or `404 Not found` on fail. |
219 | - | ||
220 | -## Project repository branches | ||
221 | - | ||
222 | -Get a list of repository branches from a project, sorted by name alphabetically. | ||
223 | - | ||
224 | -``` | ||
225 | -GET /projects/:id/repository/branches | ||
226 | -``` | ||
227 | - | ||
228 | -Parameters: | ||
229 | - | ||
230 | -+ `id` (required) - The ID or code name of a project | ||
231 | - | ||
232 | -```json | ||
233 | -[ | ||
234 | - { | ||
235 | - "name": "master", | ||
236 | - "commit": { | ||
237 | - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
238 | - "parents": [ | ||
239 | - { | ||
240 | - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
241 | - } | ||
242 | - ], | ||
243 | - "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
244 | - "message": "add projects API", | ||
245 | - "author": { | ||
246 | - "name": "John Smith", | ||
247 | - "email": "john@example.com" | ||
248 | - }, | ||
249 | - "committer": { | ||
250 | - "name": "John Smith", | ||
251 | - "email": "john@example.com" | ||
252 | - }, | ||
253 | - "authored_date": "2012-06-27T05:51:39-07:00", | ||
254 | - "committed_date": "2012-06-28T03:44:20-07:00" | ||
255 | - } | ||
256 | - } | ||
257 | -] | ||
258 | -``` | ||
259 | - | ||
260 | -Get a single project repository branch. | ||
261 | - | ||
262 | -``` | ||
263 | -GET /projects/:id/repository/branches/:branch | ||
264 | -``` | ||
265 | - | ||
266 | -Parameters: | ||
267 | - | ||
268 | -+ `id` (required) - The ID or code name of a project | ||
269 | -+ `branch` (required) - The name of the branch | ||
270 | - | ||
271 | -```json | ||
272 | -{ | ||
273 | - "name": "master", | ||
274 | - "commit": { | ||
275 | - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
276 | - "parents": [ | ||
277 | - { | ||
278 | - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
279 | - } | ||
280 | - ], | ||
281 | - "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
282 | - "message": "add projects API", | ||
283 | - "author": { | ||
284 | - "name": "John Smith", | ||
285 | - "email": "john@example.com" | ||
286 | - }, | ||
287 | - "committer": { | ||
288 | - "name": "John Smith", | ||
289 | - "email": "john@example.com" | ||
290 | - }, | ||
291 | - "authored_date": "2012-06-27T05:51:39-07:00", | ||
292 | - "committed_date": "2012-06-28T03:44:20-07:00" | ||
293 | - } | ||
294 | -} | ||
295 | -``` | ||
296 | - | ||
297 | -## Project repository tags | ||
298 | - | ||
299 | -Get a list of repository tags from a project, sorted by name in reverse alphabetical order. | ||
300 | - | ||
301 | -``` | ||
302 | -GET /projects/:id/repository/tags | ||
303 | -``` | ||
304 | - | ||
305 | -Parameters: | ||
306 | - | ||
307 | -+ `id` (required) - The ID or code name of a project | ||
308 | - | ||
309 | -```json | ||
310 | -[ | ||
311 | - { | ||
312 | - "name": "v1.0.0", | ||
313 | - "commit": { | ||
314 | - "id": "2695effb5807a22ff3d138d593fd856244e155e7", | ||
315 | - "parents": [ | ||
316 | - | ||
317 | - ], | ||
318 | - "tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d", | ||
319 | - "message": "Initial commit", | ||
320 | - "author": { | ||
321 | - "name": "John Smith", | ||
322 | - "email": "john@example.com" | ||
323 | - }, | ||
324 | - "committer": { | ||
325 | - "name": "Jack Smith", | ||
326 | - "email": "jack@example.com" | ||
327 | - }, | ||
328 | - "authored_date": "2012-05-28T04:42:42-07:00", | ||
329 | - "committed_date": "2012-05-28T04:42:42-07:00" | ||
330 | - } | ||
331 | - } | ||
332 | -] | ||
333 | -``` | ||
334 | - | ||
335 | -## Raw blob content | ||
336 | - | ||
337 | -Get the raw file contents for a file. | ||
338 | - | ||
339 | -``` | ||
340 | -GET /projects/:id/repository/commits/:sha/blob | ||
341 | -``` | ||
342 | - | ||
343 | -Parameters: | ||
344 | - | ||
345 | -+ `id` (required) - The ID or code name of a project | ||
346 | -+ `sha` (required) - The commit or branch name | ||
347 | -+ `filepath` (required) - The path the file | ||
348 | - | ||
349 | -Will return the raw file contents. | ||
350 | - |
@@ -0,0 +1,166 @@ | @@ -0,0 +1,166 @@ | ||
1 | +## Project repository branches | ||
2 | + | ||
3 | +Get a list of repository branches from a project, sorted by name alphabetically. | ||
4 | + | ||
5 | +``` | ||
6 | +GET /projects/:id/repository/branches | ||
7 | +``` | ||
8 | + | ||
9 | +Parameters: | ||
10 | + | ||
11 | ++ `id` (required) - The ID or code name of a project | ||
12 | + | ||
13 | +```json | ||
14 | +[ | ||
15 | + { | ||
16 | + "name": "master", | ||
17 | + "commit": { | ||
18 | + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
19 | + "parents": [ | ||
20 | + { | ||
21 | + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
22 | + } | ||
23 | + ], | ||
24 | + "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
25 | + "message": "add projects API", | ||
26 | + "author": { | ||
27 | + "name": "John Smith", | ||
28 | + "email": "john@example.com" | ||
29 | + }, | ||
30 | + "committer": { | ||
31 | + "name": "John Smith", | ||
32 | + "email": "john@example.com" | ||
33 | + }, | ||
34 | + "authored_date": "2012-06-27T05:51:39-07:00", | ||
35 | + "committed_date": "2012-06-28T03:44:20-07:00" | ||
36 | + } | ||
37 | + } | ||
38 | +] | ||
39 | +``` | ||
40 | + | ||
41 | +## Project repository branch | ||
42 | + | ||
43 | +Get a single project repository branch. | ||
44 | + | ||
45 | +``` | ||
46 | +GET /projects/:id/repository/branches/:branch | ||
47 | +``` | ||
48 | + | ||
49 | +Parameters: | ||
50 | + | ||
51 | ++ `id` (required) - The ID or code name of a project | ||
52 | ++ `branch` (required) - The name of the branch | ||
53 | + | ||
54 | +```json | ||
55 | +{ | ||
56 | + "name": "master", | ||
57 | + "commit": { | ||
58 | + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
59 | + "parents": [ | ||
60 | + { | ||
61 | + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
62 | + } | ||
63 | + ], | ||
64 | + "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
65 | + "message": "add projects API", | ||
66 | + "author": { | ||
67 | + "name": "John Smith", | ||
68 | + "email": "john@example.com" | ||
69 | + }, | ||
70 | + "committer": { | ||
71 | + "name": "John Smith", | ||
72 | + "email": "john@example.com" | ||
73 | + }, | ||
74 | + "authored_date": "2012-06-27T05:51:39-07:00", | ||
75 | + "committed_date": "2012-06-28T03:44:20-07:00" | ||
76 | + } | ||
77 | +} | ||
78 | +``` | ||
79 | + | ||
80 | +## Project repository tags | ||
81 | + | ||
82 | +Get a list of repository tags from a project, sorted by name in reverse alphabetical order. | ||
83 | + | ||
84 | +``` | ||
85 | +GET /projects/:id/repository/tags | ||
86 | +``` | ||
87 | + | ||
88 | +Parameters: | ||
89 | + | ||
90 | ++ `id` (required) - The ID or code name of a project | ||
91 | + | ||
92 | +```json | ||
93 | +[ | ||
94 | + { | ||
95 | + "name": "v1.0.0", | ||
96 | + "commit": { | ||
97 | + "id": "2695effb5807a22ff3d138d593fd856244e155e7", | ||
98 | + "parents": [ | ||
99 | + | ||
100 | + ], | ||
101 | + "tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d", | ||
102 | + "message": "Initial commit", | ||
103 | + "author": { | ||
104 | + "name": "John Smith", | ||
105 | + "email": "john@example.com" | ||
106 | + }, | ||
107 | + "committer": { | ||
108 | + "name": "Jack Smith", | ||
109 | + "email": "jack@example.com" | ||
110 | + }, | ||
111 | + "authored_date": "2012-05-28T04:42:42-07:00", | ||
112 | + "committed_date": "2012-05-28T04:42:42-07:00" | ||
113 | + } | ||
114 | + } | ||
115 | +] | ||
116 | +``` | ||
117 | + | ||
118 | +## Project repository commits | ||
119 | + | ||
120 | +Get a list of repository commits in a project. | ||
121 | + | ||
122 | +``` | ||
123 | +GET /projects/:id/repository/commits | ||
124 | +``` | ||
125 | + | ||
126 | +Parameters: | ||
127 | + | ||
128 | ++ `id` (required) - The ID or code name of a project | ||
129 | ++ `ref_name` (optional) - The name of a repository branch or tag | ||
130 | + | ||
131 | +```json | ||
132 | +[ | ||
133 | + { | ||
134 | + "id": "ed899a2f4b50b4370feeea94676502b42383c746", | ||
135 | + "short_id": "ed899a2f4b5", | ||
136 | + "title": "Replace sanitize with escape once", | ||
137 | + "author_name": "Dmitriy Zaporozhets", | ||
138 | + "author_email": "dzaporozhets@sphereconsultinginc.com", | ||
139 | + "created_at": "2012-09-20T11:50:22+03:00" | ||
140 | + }, | ||
141 | + { | ||
142 | + "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", | ||
143 | + "short_id": "6104942438c", | ||
144 | + "title": "Sanitize for network graph", | ||
145 | + "author_name": "randx", | ||
146 | + "author_email": "dmitriy.zaporozhets@gmail.com", | ||
147 | + "created_at": "2012-09-20T09:06:12+03:00" | ||
148 | + } | ||
149 | +] | ||
150 | +``` | ||
151 | + | ||
152 | +## Raw blob content | ||
153 | + | ||
154 | +Get the raw file contents for a file. | ||
155 | + | ||
156 | +``` | ||
157 | +GET /projects/:id/repository/commits/:sha/blob | ||
158 | +``` | ||
159 | + | ||
160 | +Parameters: | ||
161 | + | ||
162 | ++ `id` (required) - The ID or code name of a project | ||
163 | ++ `sha` (required) - The commit or branch name | ||
164 | ++ `filepath` (required) - The path the file | ||
165 | + | ||
166 | +Will return the raw file contents. |
@@ -0,0 +1,22 @@ | @@ -0,0 +1,22 @@ | ||
1 | +Login to get private token | ||
2 | + | ||
3 | +``` | ||
4 | +POST /session | ||
5 | +``` | ||
6 | + | ||
7 | +Parameters: | ||
8 | + | ||
9 | ++ `email` (required) - The email of user | ||
10 | ++ `password` (required) - Valid password | ||
11 | + | ||
12 | + | ||
13 | +```json | ||
14 | +{ | ||
15 | + "id": 1, | ||
16 | + "email": "john@example.com", | ||
17 | + "name": "John Smith", | ||
18 | + "private_token": "dd34asd13as", | ||
19 | + "created_at": "2012-05-23T08:00:58Z", | ||
20 | + "blocked": true | ||
21 | +} | ||
22 | +``` |
doc/api/users.md
@@ -88,3 +88,81 @@ GET /user | @@ -88,3 +88,81 @@ GET /user | ||
88 | "theme_id": 1 | 88 | "theme_id": 1 |
89 | } | 89 | } |
90 | ``` | 90 | ``` |
91 | + | ||
92 | +## List SSH keys | ||
93 | + | ||
94 | +Get a list of currently authenticated user's SSH keys. | ||
95 | + | ||
96 | +``` | ||
97 | +GET /user/keys | ||
98 | +``` | ||
99 | + | ||
100 | +```json | ||
101 | +[ | ||
102 | + { | ||
103 | + "id": 1, | ||
104 | + "title" : "Public key" | ||
105 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
106 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
107 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", | ||
108 | + }, | ||
109 | + { | ||
110 | + "id": 3, | ||
111 | + "title" : "Another Public key" | ||
112 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
113 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
114 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | ||
115 | + } | ||
116 | +] | ||
117 | +``` | ||
118 | + | ||
119 | +## Single SSH key | ||
120 | + | ||
121 | +Get a single key. | ||
122 | + | ||
123 | +``` | ||
124 | +GET /user/keys/:id | ||
125 | +``` | ||
126 | + | ||
127 | +Parameters: | ||
128 | + | ||
129 | ++ `id` (required) - The ID of an SSH key | ||
130 | + | ||
131 | +```json | ||
132 | +{ | ||
133 | + "id": 1, | ||
134 | + "title" : "Public key" | ||
135 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
136 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
137 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | ||
138 | +} | ||
139 | +``` | ||
140 | +## Add SSH key | ||
141 | + | ||
142 | +Create new key owned by currently authenticated user | ||
143 | + | ||
144 | +``` | ||
145 | +POST /user/keys | ||
146 | +``` | ||
147 | + | ||
148 | +Parameters: | ||
149 | + | ||
150 | ++ `title` (required) - new SSH Key's title | ||
151 | ++ `key` (required) - new SSH key | ||
152 | + | ||
153 | +Will return created key with status `201 Created` on success, or `404 Not | ||
154 | +found` on fail. | ||
155 | + | ||
156 | +## Delete SSH key | ||
157 | + | ||
158 | +Delete key owned by currently authenticated user | ||
159 | + | ||
160 | +``` | ||
161 | +DELETE /user/keys/:id | ||
162 | +``` | ||
163 | + | ||
164 | +Parameters: | ||
165 | + | ||
166 | ++ `id` (required) - SSH key ID | ||
167 | + | ||
168 | +Will return `200 OK` on success, or `404 Not Found` on fail. |
features/steps/dashboard/dashboard.rb
@@ -16,7 +16,7 @@ class Dashboard < Spinach::FeatureSteps | @@ -16,7 +16,7 @@ class Dashboard < Spinach::FeatureSteps | ||
16 | end | 16 | end |
17 | 17 | ||
18 | Then 'I should see last push widget' do | 18 | Then 'I should see last push widget' do |
19 | - page.should have_content "Your pushed to branch new_design" | 19 | + page.should have_content "You pushed to branch new_design" |
20 | page.should have_link "Create Merge Request" | 20 | page.should have_link "Create Merge Request" |
21 | end | 21 | end |
22 | 22 |
lib/api.rb
lib/api/entities.rb
@@ -9,6 +9,10 @@ module Gitlab | @@ -9,6 +9,10 @@ module Gitlab | ||
9 | expose :id, :email, :name, :blocked, :created_at | 9 | expose :id, :email, :name, :blocked, :created_at |
10 | end | 10 | end |
11 | 11 | ||
12 | + class UserLogin < UserBasic | ||
13 | + expose :private_token | ||
14 | + end | ||
15 | + | ||
12 | class Hook < Grape::Entity | 16 | class Hook < Grape::Entity |
13 | expose :id, :url | 17 | expose :id, :url |
14 | end | 18 | end |
@@ -20,15 +24,20 @@ module Gitlab | @@ -20,15 +24,20 @@ module Gitlab | ||
20 | expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at | 24 | expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at |
21 | end | 25 | end |
22 | 26 | ||
23 | - class UsersProject < Grape::Entity | ||
24 | - expose :user, using: Entities::UserBasic | ||
25 | - expose :project_access | 27 | + class ProjectMember < UserBasic |
28 | + expose :project_access, :as => :access_level do |user, options| | ||
29 | + options[:project].users_projects.find_by_user_id(user.id).project_access | ||
30 | + end | ||
26 | end | 31 | end |
27 | 32 | ||
28 | class RepoObject < Grape::Entity | 33 | class RepoObject < Grape::Entity |
29 | expose :name, :commit | 34 | expose :name, :commit |
30 | end | 35 | end |
31 | 36 | ||
37 | + class RepoCommit < Grape::Entity | ||
38 | + expose :id, :short_id, :title, :author_name, :author_email, :created_at | ||
39 | + end | ||
40 | + | ||
32 | class ProjectSnippet < Grape::Entity | 41 | class ProjectSnippet < Grape::Entity |
33 | expose :id, :title, :file_name | 42 | expose :id, :title, :file_name |
34 | expose :author, using: Entities::UserBasic | 43 | expose :author, using: Entities::UserBasic |
@@ -36,7 +45,9 @@ module Gitlab | @@ -36,7 +45,9 @@ module Gitlab | ||
36 | end | 45 | end |
37 | 46 | ||
38 | class Milestone < Grape::Entity | 47 | class Milestone < Grape::Entity |
39 | - expose :id, :title, :description, :due_date, :closed, :updated_at, :created_at | 48 | + expose :id |
49 | + expose (:project_id) {|milestone| milestone.project.id} | ||
50 | + expose :title, :description, :due_date, :closed, :updated_at, :created_at | ||
40 | end | 51 | end |
41 | 52 | ||
42 | class Issue < Grape::Entity | 53 | class Issue < Grape::Entity |
@@ -49,10 +60,8 @@ module Gitlab | @@ -49,10 +60,8 @@ module Gitlab | ||
49 | expose :closed, :updated_at, :created_at | 60 | expose :closed, :updated_at, :created_at |
50 | end | 61 | end |
51 | 62 | ||
52 | - class Key < Grape::Entity | ||
53 | - expose :id, | ||
54 | - :title, | ||
55 | - :key | 63 | + class SSHKey < Grape::Entity |
64 | + expose :id, :title, :key | ||
56 | end | 65 | end |
57 | end | 66 | end |
58 | end | 67 | end |
lib/api/helpers.rb
lib/api/keys.rb
@@ -1,50 +0,0 @@ | @@ -1,50 +0,0 @@ | ||
1 | -module Gitlab | ||
2 | - # Keys API | ||
3 | - class Keys < Grape::API | ||
4 | - before { authenticate! } | ||
5 | - resource :keys do | ||
6 | - # Get currently authenticated user's keys | ||
7 | - # | ||
8 | - # Example Request: | ||
9 | - # GET /keys | ||
10 | - get do | ||
11 | - present current_user.keys, with: Entities::Key | ||
12 | - end | ||
13 | - # Get single key owned by currently authenticated user | ||
14 | - # | ||
15 | - # Example Request: | ||
16 | - # GET /keys/:id | ||
17 | - get "/:id" do | ||
18 | - key = current_user.keys.find params[:id] | ||
19 | - present key, with: Entities::Key | ||
20 | - end | ||
21 | - # Add new ssh key to currently authenticated user | ||
22 | - # | ||
23 | - # Parameters: | ||
24 | - # key (required) - New SSH Key | ||
25 | - # title (required) - New SSH Key's title | ||
26 | - # Example Request: | ||
27 | - # POST /keys | ||
28 | - post do | ||
29 | - attrs = attributes_for_keys [:title, :key] | ||
30 | - key = current_user.keys.new attrs | ||
31 | - if key.save | ||
32 | - present key, with: Entities::Key | ||
33 | - else | ||
34 | - not_found! | ||
35 | - end | ||
36 | - end | ||
37 | - # Delete existed ssh key of currently authenticated user | ||
38 | - # | ||
39 | - # Parameters: | ||
40 | - # id (required) - SSH Key ID | ||
41 | - # Example Request: | ||
42 | - # DELETE /keys/:id | ||
43 | - delete "/:id" do | ||
44 | - key = current_user.keys.find params[:id] | ||
45 | - key.delete | ||
46 | - end | ||
47 | - end | ||
48 | - end | ||
49 | -end | ||
50 | - |
lib/api/milestones.rb
@@ -11,6 +11,8 @@ module Gitlab | @@ -11,6 +11,8 @@ module Gitlab | ||
11 | # Example Request: | 11 | # Example Request: |
12 | # GET /projects/:id/milestones | 12 | # GET /projects/:id/milestones |
13 | get ":id/milestones" do | 13 | get ":id/milestones" do |
14 | + authorize! :read_milestone, user_project | ||
15 | + | ||
14 | present paginate(user_project.milestones), with: Entities::Milestone | 16 | present paginate(user_project.milestones), with: Entities::Milestone |
15 | end | 17 | end |
16 | 18 | ||
@@ -22,6 +24,8 @@ module Gitlab | @@ -22,6 +24,8 @@ module Gitlab | ||
22 | # Example Request: | 24 | # Example Request: |
23 | # GET /projects/:id/milestones/:milestone_id | 25 | # GET /projects/:id/milestones/:milestone_id |
24 | get ":id/milestones/:milestone_id" do | 26 | get ":id/milestones/:milestone_id" do |
27 | + authorize! :read_milestone, user_project | ||
28 | + | ||
25 | @milestone = user_project.milestones.find(params[:milestone_id]) | 29 | @milestone = user_project.milestones.find(params[:milestone_id]) |
26 | present @milestone, with: Entities::Milestone | 30 | present @milestone, with: Entities::Milestone |
27 | end | 31 | end |
@@ -36,6 +40,8 @@ module Gitlab | @@ -36,6 +40,8 @@ module Gitlab | ||
36 | # Example Request: | 40 | # Example Request: |
37 | # POST /projects/:id/milestones | 41 | # POST /projects/:id/milestones |
38 | post ":id/milestones" do | 42 | post ":id/milestones" do |
43 | + authorize! :admin_milestone, user_project | ||
44 | + | ||
39 | attrs = attributes_for_keys [:title, :description, :due_date] | 45 | attrs = attributes_for_keys [:title, :description, :due_date] |
40 | @milestone = user_project.milestones.new attrs | 46 | @milestone = user_project.milestones.new attrs |
41 | if @milestone.save | 47 | if @milestone.save |
lib/api/projects.rb
@@ -40,14 +40,14 @@ module Gitlab | @@ -40,14 +40,14 @@ module Gitlab | ||
40 | post do | 40 | post do |
41 | params[:code] ||= params[:name] | 41 | params[:code] ||= params[:name] |
42 | params[:path] ||= params[:name] | 42 | params[:path] ||= params[:name] |
43 | - attrs = attributes_for_keys [:code, | ||
44 | - :path, | ||
45 | - :name, | ||
46 | - :description, | ||
47 | - :default_branch, | ||
48 | - :issues_enabled, | ||
49 | - :wall_enabled, | ||
50 | - :merge_requests_enabled, | 43 | + attrs = attributes_for_keys [:code, |
44 | + :path, | ||
45 | + :name, | ||
46 | + :description, | ||
47 | + :default_branch, | ||
48 | + :issues_enabled, | ||
49 | + :wall_enabled, | ||
50 | + :merge_requests_enabled, | ||
51 | :wiki_enabled] | 51 | :wiki_enabled] |
52 | @project = Project.create_by_user(attrs, current_user) | 52 | @project = Project.create_by_user(attrs, current_user) |
53 | if @project.saved? | 53 | if @project.saved? |
@@ -57,56 +57,83 @@ module Gitlab | @@ -57,56 +57,83 @@ module Gitlab | ||
57 | end | 57 | end |
58 | end | 58 | end |
59 | 59 | ||
60 | - # Get project users | 60 | + # Get a project team members |
61 | # | 61 | # |
62 | # Parameters: | 62 | # Parameters: |
63 | # id (required) - The ID or code name of a project | 63 | # id (required) - The ID or code name of a project |
64 | # Example Request: | 64 | # Example Request: |
65 | - # GET /projects/:id/users | ||
66 | - get ":id/users" do | ||
67 | - @users_projects = paginate user_project.users_projects | ||
68 | - present @users_projects, with: Entities::UsersProject | 65 | + # GET /projects/:id/members |
66 | + get ":id/members" do | ||
67 | + @members = paginate user_project.users | ||
68 | + present @members, with: Entities::ProjectMember, project: user_project | ||
69 | end | 69 | end |
70 | 70 | ||
71 | - # Add users to project with specified access level | 71 | + # Get a project team members |
72 | # | 72 | # |
73 | # Parameters: | 73 | # Parameters: |
74 | # id (required) - The ID or code name of a project | 74 | # id (required) - The ID or code name of a project |
75 | - # user_ids (required) - The ID list of users to add | ||
76 | - # project_access (required) - Project access level | 75 | + # user_id (required) - The ID of a user |
77 | # Example Request: | 76 | # Example Request: |
78 | - # POST /projects/:id/users | ||
79 | - post ":id/users" do | 77 | + # GET /projects/:id/members/:user_id |
78 | + get ":id/members/:user_id" do | ||
79 | + @member = user_project.users.find params[:user_id] | ||
80 | + present @member, with: Entities::ProjectMember, project: user_project | ||
81 | + end | ||
82 | + | ||
83 | + # Add a new project team member | ||
84 | + # | ||
85 | + # Parameters: | ||
86 | + # id (required) - The ID or code name of a project | ||
87 | + # user_id (required) - The ID of a user | ||
88 | + # access_level (required) - Project access level | ||
89 | + # Example Request: | ||
90 | + # POST /projects/:id/members | ||
91 | + post ":id/members" do | ||
80 | authorize! :admin_project, user_project | 92 | authorize! :admin_project, user_project |
81 | - user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access]) | ||
82 | - nil | 93 | + users_project = user_project.users_projects.new( |
94 | + user_id: params[:user_id], | ||
95 | + project_access: params[:access_level] | ||
96 | + ) | ||
97 | + | ||
98 | + if users_project.save | ||
99 | + @member = users_project.user | ||
100 | + present @member, with: Entities::ProjectMember, project: user_project | ||
101 | + else | ||
102 | + not_found! | ||
103 | + end | ||
83 | end | 104 | end |
84 | 105 | ||
85 | - # Update users to specified access level | 106 | + # Update project team member |
86 | # | 107 | # |
87 | # Parameters: | 108 | # Parameters: |
88 | # id (required) - The ID or code name of a project | 109 | # id (required) - The ID or code name of a project |
89 | - # user_ids (required) - The ID list of users to add | ||
90 | - # project_access (required) - New project access level to | 110 | + # user_id (required) - The ID of a team member |
111 | + # access_level (required) - Project access level | ||
91 | # Example Request: | 112 | # Example Request: |
92 | - # PUT /projects/:id/add_users | ||
93 | - put ":id/users" do | 113 | + # PUT /projects/:id/members/:user_id |
114 | + put ":id/members/:user_id" do | ||
94 | authorize! :admin_project, user_project | 115 | authorize! :admin_project, user_project |
95 | - user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access]) | ||
96 | - nil | 116 | + users_project = user_project.users_projects.find_by_user_id params[:user_id] |
117 | + | ||
118 | + if users_project.update_attributes(project_access: params[:access_level]) | ||
119 | + @member = users_project.user | ||
120 | + present @member, with: Entities::ProjectMember, project: user_project | ||
121 | + else | ||
122 | + not_found! | ||
123 | + end | ||
97 | end | 124 | end |
98 | 125 | ||
99 | - # Delete project users | 126 | + # Remove a team member from project |
100 | # | 127 | # |
101 | # Parameters: | 128 | # Parameters: |
102 | # id (required) - The ID or code name of a project | 129 | # id (required) - The ID or code name of a project |
103 | - # user_ids (required) - The ID list of users to delete | 130 | + # user_id (required) - The ID of a team member |
104 | # Example Request: | 131 | # Example Request: |
105 | - # DELETE /projects/:id/users | ||
106 | - delete ":id/users" do | 132 | + # DELETE /projects/:id/members/:user_id |
133 | + delete ":id/members/:user_id" do | ||
107 | authorize! :admin_project, user_project | 134 | authorize! :admin_project, user_project |
108 | - user_project.delete_users_ids_from_team(params[:user_ids].values) | ||
109 | - nil | 135 | + users_project = user_project.users_projects.find_by_user_id params[:user_id] |
136 | + users_project.destroy | ||
110 | end | 137 | end |
111 | 138 | ||
112 | # Get project hooks | 139 | # Get project hooks |
@@ -184,6 +211,24 @@ module Gitlab | @@ -184,6 +211,24 @@ module Gitlab | ||
184 | present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject | 211 | present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject |
185 | end | 212 | end |
186 | 213 | ||
214 | + # Get a project repository commits | ||
215 | + # | ||
216 | + # Parameters: | ||
217 | + # id (required) - The ID or code name of a project | ||
218 | + # ref_name (optional) - The name of a repository branch or tag | ||
219 | + # Example Request: | ||
220 | + # GET /projects/:id/repository/commits | ||
221 | + get ":id/repository/commits" do | ||
222 | + authorize! :download_code, user_project | ||
223 | + | ||
224 | + page = params[:page] || 0 | ||
225 | + per_page = params[:per_page] || 20 | ||
226 | + ref = params[:ref_name] || user_project.try(:default_branch) || 'master' | ||
227 | + | ||
228 | + commits = user_project.commits(ref, nil, per_page, page * per_page) | ||
229 | + present CommitDecorator.decorate(commits), with: Entities::RepoCommit | ||
230 | + end | ||
231 | + | ||
187 | # Get a project snippet | 232 | # Get a project snippet |
188 | # | 233 | # |
189 | # Parameters: | 234 | # Parameters: |
@@ -207,6 +252,8 @@ module Gitlab | @@ -207,6 +252,8 @@ module Gitlab | ||
207 | # Example Request: | 252 | # Example Request: |
208 | # POST /projects/:id/snippets | 253 | # POST /projects/:id/snippets |
209 | post ":id/snippets" do | 254 | post ":id/snippets" do |
255 | + authorize! :write_snippet, user_project | ||
256 | + | ||
210 | attrs = attributes_for_keys [:title, :file_name] | 257 | attrs = attributes_for_keys [:title, :file_name] |
211 | attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? | 258 | attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? |
212 | attrs[:content] = params[:code] if params[:code].present? | 259 | attrs[:content] = params[:code] if params[:code].present? |
@@ -282,6 +329,8 @@ module Gitlab | @@ -282,6 +329,8 @@ module Gitlab | ||
282 | # Example Request: | 329 | # Example Request: |
283 | # GET /projects/:id/repository/commits/:sha/blob | 330 | # GET /projects/:id/repository/commits/:sha/blob |
284 | get ":id/repository/commits/:sha/blob" do | 331 | get ":id/repository/commits/:sha/blob" do |
332 | + authorize! :download_code, user_project | ||
333 | + | ||
285 | ref = params[:sha] | 334 | ref = params[:sha] |
286 | 335 | ||
287 | commit = user_project.commit ref | 336 | commit = user_project.commit ref |
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +module Gitlab | ||
2 | + # Users API | ||
3 | + class Session < Grape::API | ||
4 | + # Login to get token | ||
5 | + # | ||
6 | + # Example Request: | ||
7 | + # POST /session | ||
8 | + post "/session" do | ||
9 | + resource = User.find_for_database_authentication(email: params[:email]) | ||
10 | + | ||
11 | + return unauthorized! unless resource | ||
12 | + | ||
13 | + if resource.valid_password?(params[:password]) | ||
14 | + present resource, with: Entities::UserLogin | ||
15 | + else | ||
16 | + unauthorized! | ||
17 | + end | ||
18 | + end | ||
19 | + end | ||
20 | +end |
lib/api/users.rb
@@ -25,12 +25,59 @@ module Gitlab | @@ -25,12 +25,59 @@ module Gitlab | ||
25 | end | 25 | end |
26 | end | 26 | end |
27 | 27 | ||
28 | - # Get currently authenticated user | ||
29 | - # | ||
30 | - # Example Request: | ||
31 | - # GET /user | ||
32 | - get "/user" do | ||
33 | - present @current_user, with: Entities::User | 28 | + resource :user do |
29 | + # Get currently authenticated user | ||
30 | + # | ||
31 | + # Example Request: | ||
32 | + # GET /user | ||
33 | + get do | ||
34 | + present @current_user, with: Entities::User | ||
35 | + end | ||
36 | + | ||
37 | + # Get currently authenticated user's keys | ||
38 | + # | ||
39 | + # Example Request: | ||
40 | + # GET /user/keys | ||
41 | + get "keys" do | ||
42 | + present current_user.keys, with: Entities::SSHKey | ||
43 | + end | ||
44 | + | ||
45 | + # Get single key owned by currently authenticated user | ||
46 | + # | ||
47 | + # Example Request: | ||
48 | + # GET /user/keys/:id | ||
49 | + get "keys/:id" do | ||
50 | + key = current_user.keys.find params[:id] | ||
51 | + present key, with: Entities::SSHKey | ||
52 | + end | ||
53 | + | ||
54 | + # Add new ssh key to currently authenticated user | ||
55 | + # | ||
56 | + # Parameters: | ||
57 | + # key (required) - New SSH Key | ||
58 | + # title (required) - New SSH Key's title | ||
59 | + # Example Request: | ||
60 | + # POST /user/keys | ||
61 | + post "keys" do | ||
62 | + attrs = attributes_for_keys [:title, :key] | ||
63 | + key = current_user.keys.new attrs | ||
64 | + if key.save | ||
65 | + present key, with: Entities::SSHKey | ||
66 | + else | ||
67 | + not_found! | ||
68 | + end | ||
69 | + end | ||
70 | + | ||
71 | + # Delete existed ssh key of currently authenticated user | ||
72 | + # | ||
73 | + # Parameters: | ||
74 | + # id (required) - SSH Key ID | ||
75 | + # Example Request: | ||
76 | + # DELETE /user/keys/:id | ||
77 | + delete "keys/:id" do | ||
78 | + key = current_user.keys.find params[:id] | ||
79 | + key.delete | ||
80 | + end | ||
34 | end | 81 | end |
35 | end | 82 | end |
36 | end | 83 | end |
lib/gitlab/graph_commit.rb
@@ -5,7 +5,7 @@ module Gitlab | @@ -5,7 +5,7 @@ module Gitlab | ||
5 | attr_accessor :time, :space | 5 | attr_accessor :time, :space |
6 | attr_accessor :refs | 6 | attr_accessor :refs |
7 | 7 | ||
8 | - include ActionView::Helpers::SanitizeHelper | 8 | + include ActionView::Helpers::TagHelper |
9 | 9 | ||
10 | def self.to_graph(project) | 10 | def self.to_graph(project) |
11 | @repo = project.repo | 11 | @repo = project.repo |
@@ -166,7 +166,7 @@ module Gitlab | @@ -166,7 +166,7 @@ module Gitlab | ||
166 | h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? | 166 | h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? |
167 | h[:id] = sha | 167 | h[:id] = sha |
168 | h[:date] = date | 168 | h[:date] = date |
169 | - h[:message] = sanitize(Gitlab::Encode.utf8(message)) | 169 | + h[:message] = escape_once(Gitlab::Encode.utf8(message)) |
170 | h[:login] = author.email | 170 | h[:login] = author.email |
171 | h | 171 | h |
172 | end | 172 | end |
lib/tasks/bulk_add_permission.rake
1 | -desc "Add all users to all projects, system administratos are added as masters" | 1 | +desc "Add all users to all projects (admin users are added as masters)" |
2 | task :add_users_to_project_teams => :environment do |t, args| | 2 | task :add_users_to_project_teams => :environment do |t, args| |
3 | - users = User.find_all_by_admin(false, :select => 'id').map(&:id) | ||
4 | - admins = User.find_all_by_admin(true, :select => 'id').map(&:id) | 3 | + user_ids = User.where(:admin => false).pluck(:id) |
4 | + admin_ids = User.where(:admin => true).pluck(:id) | ||
5 | 5 | ||
6 | - users.each do |user| | ||
7 | - puts "#{user}" | ||
8 | - end | ||
9 | - | ||
10 | - Project.all.each do |project| | ||
11 | - puts "Importing #{users.length} users into #{project.path}" | ||
12 | - UsersProject.bulk_import(project, users, UsersProject::DEVELOPER) | ||
13 | - puts "Importing #{admins.length} admins into #{project.path}" | ||
14 | - UsersProject.bulk_import(project, admins, UsersProject::MASTER) | 6 | + Project.find_each do |project| |
7 | + puts "Importing #{user_ids.size} users into #{project.code}" | ||
8 | + UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER) | ||
9 | + puts "Importing #{admin_ids.size} admins into #{project.code}" | ||
10 | + UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER) | ||
15 | end | 11 | end |
16 | end | 12 | end |
17 | 13 | ||
18 | desc "Add user to as a developer to all projects" | 14 | desc "Add user to as a developer to all projects" |
19 | task :add_user_to_project_teams, [:email] => :environment do |t, args| | 15 | task :add_user_to_project_teams, [:email] => :environment do |t, args| |
20 | - user_email = args.email | ||
21 | - user = User.find_by_email(user_email) | ||
22 | - | ||
23 | - project_ids = Project.all.map(&:id) | 16 | + user = User.find_by_email args.email |
17 | + project_ids = Project.pluck(:id) | ||
24 | 18 | ||
25 | - UsersProject.user_bulk_import(user,project_ids,UsersProject::DEVELOPER) | 19 | + UsersProject.user_bulk_import(user, project_ids, UsersProject::DEVELOPER) |
26 | end | 20 | end |
lib/tasks/bulk_import.rake
1 | - | ||
2 | desc "Imports existing Git repos from a directory into new projects in git_base_path" | 1 | 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| | 2 | task :import_projects, [:directory,:email] => :environment do |t, args| |
4 | - user_email = args.email | ||
5 | - import_directory = args.directory | 3 | + user_email, import_directory = args.email, args.directory |
6 | repos_to_import = Dir.glob("#{import_directory}/*") | 4 | repos_to_import = Dir.glob("#{import_directory}/*") |
7 | git_base_path = Gitlab.config.git_base_path | 5 | git_base_path = Gitlab.config.git_base_path |
8 | - puts "Found #{repos_to_import.length} repos to import" | 6 | + imported_count, skipped_count, failed_count = 0 |
7 | + | ||
8 | + puts "Found #{repos_to_import.size} repos to import" | ||
9 | 9 | ||
10 | - imported_count = 0 | ||
11 | - skipped_count = 0 | ||
12 | - failed_count = 0 | ||
13 | repos_to_import.each do |repo_path| | 10 | repos_to_import.each do |repo_path| |
14 | repo_name = File.basename repo_path | 11 | repo_name = File.basename repo_path |
12 | + clone_path = "#{git_base_path}#{repo_name}.git" | ||
15 | 13 | ||
16 | puts " Processing #{repo_name}" | 14 | puts " Processing #{repo_name}" |
17 | - clone_path = "#{git_base_path}#{repo_name}.git" | ||
18 | 15 | ||
19 | if Dir.exists? clone_path | 16 | if Dir.exists? clone_path |
20 | if Project.find_by_code(repo_name) | 17 | if Project.find_by_code(repo_name) |
@@ -38,7 +35,6 @@ task :import_projects, [:directory,:email] => :environment do |t, args| | @@ -38,7 +35,6 @@ task :import_projects, [:directory,:email] => :environment do |t, args| | ||
38 | else | 35 | else |
39 | failed_count += 1 | 36 | failed_count += 1 |
40 | end | 37 | end |
41 | - | ||
42 | end | 38 | end |
43 | 39 | ||
44 | puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})." | 40 | puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})." |
@@ -49,63 +45,39 @@ def clone_bare_repo_as_git(existing_path, new_path) | @@ -49,63 +45,39 @@ def clone_bare_repo_as_git(existing_path, new_path) | ||
49 | git_user = Gitlab.config.ssh_user | 45 | git_user = Gitlab.config.ssh_user |
50 | begin | 46 | begin |
51 | sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}" | 47 | sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}" |
52 | - true | ||
53 | - rescue Exception=> msg | ||
54 | - puts " ERROR: Faild to clone #{existing_path} to #{new_path}" | ||
55 | - puts " Make sure #{git_user} can reach #{existing_path}" | ||
56 | - puts " Exception-MSG: #{msg}" | ||
57 | - false | 48 | + rescue Exception => msg |
49 | + puts " ERROR: Failed to clone #{existing_path} to #{new_path}" | ||
50 | + puts " Make sure #{git_user} can reach #{existing_path}" | ||
51 | + puts " Exception-MSG: #{msg}" | ||
58 | end | 52 | end |
59 | end | 53 | end |
60 | 54 | ||
61 | -# Creats a project in Gitlag given a @project_name@ to use (for name, web url, and code | ||
62 | -# url) and a @user_email@ that will be assigned as the owner of the project. | 55 | +# Creates a project in GitLab given a `project_name` to use |
56 | +# (for name, web url, and code url) and a `user_email` that will be | ||
57 | +# assigned as the owner of the project. | ||
63 | def create_repo_project(project_name, user_email) | 58 | def create_repo_project(project_name, user_email) |
64 | - user = User.find_by_email(user_email) | ||
65 | - if user | 59 | + if user = User.find_by_email(user_email) |
66 | # Using find_by_code since that's the most important identifer to be unique | 60 | # Using find_by_code since that's the most important identifer to be unique |
67 | if Project.find_by_code(project_name) | 61 | if Project.find_by_code(project_name) |
68 | puts " INFO: Project #{project_name} already exists in Gitlab, skipping." | 62 | puts " INFO: Project #{project_name} already exists in Gitlab, skipping." |
69 | - false | ||
70 | else | 63 | else |
71 | - project = nil | ||
72 | - if Project.find_by_code(project_name) | ||
73 | - puts " ERROR: Project already exists #{project_name}" | ||
74 | - return false | ||
75 | - project = Project.find_by_code(project_name) | ||
76 | - else | ||
77 | - project = Project.create( | ||
78 | - name: project_name, | ||
79 | - code: project_name, | ||
80 | - path: project_name, | ||
81 | - owner: user, | ||
82 | - description: "Automatically created from Rake on #{Time.now.to_s}" | ||
83 | - ) | ||
84 | - end | ||
85 | - | ||
86 | - unless project.valid? | ||
87 | - puts " ERROR: Failed to create project #{project} because #{project.errors.first}" | ||
88 | - return false | ||
89 | - end | ||
90 | - | ||
91 | - # Add user as admin for project | ||
92 | - project.users_projects.create!( | ||
93 | - :project_access => UsersProject::MASTER, | ||
94 | - :user => user | 64 | + project = Project.create( |
65 | + name: project_name, | ||
66 | + code: project_name, | ||
67 | + path: project_name, | ||
68 | + owner: user, | ||
69 | + description: "Automatically created from 'import_projects' rake task on #{Time.now}" | ||
95 | ) | 70 | ) |
96 | 71 | ||
97 | - # Per projects_controller.rb#37 | ||
98 | - project.update_repository | ||
99 | - | ||
100 | if project.valid? | 72 | if project.valid? |
101 | - true | 73 | + # Add user as admin for project |
74 | + project.users_projects.create!(:project_access => UsersProject::MASTER, :user => user) | ||
75 | + project.update_repository | ||
102 | else | 76 | else |
103 | puts " ERROR: Failed to create project #{project} because #{project.errors.first}" | 77 | puts " ERROR: Failed to create project #{project} because #{project.errors.first}" |
104 | - false | ||
105 | end | 78 | end |
106 | end | 79 | end |
107 | else | 80 | else |
108 | - puts " ERROR: #{user_email} not found, skipping" | ||
109 | - false | 81 | + puts " ERROR: user with #{user_email} not found, skipping" |
110 | end | 82 | end |
111 | end | 83 | end |
lib/tasks/gitlab/backup.rake
@@ -2,22 +2,20 @@ require 'active_record/fixtures' | @@ -2,22 +2,20 @@ require 'active_record/fixtures' | ||
2 | 2 | ||
3 | namespace :gitlab do | 3 | namespace :gitlab do |
4 | namespace :app do | 4 | namespace :app do |
5 | - | ||
6 | - # Create backup of gitlab system | ||
7 | - desc "GITLAB | Create a backup of the gitlab system" | 5 | + # Create backup of GitLab system |
6 | + desc "GITLAB | Create a backup of the GitLab system" | ||
8 | task :backup_create => :environment do | 7 | task :backup_create => :environment do |
9 | - | ||
10 | Rake::Task["gitlab:app:db_dump"].invoke | 8 | Rake::Task["gitlab:app:db_dump"].invoke |
11 | Rake::Task["gitlab:app:repo_dump"].invoke | 9 | Rake::Task["gitlab:app:repo_dump"].invoke |
12 | 10 | ||
13 | Dir.chdir(Gitlab.config.backup_path) | 11 | Dir.chdir(Gitlab.config.backup_path) |
14 | 12 | ||
15 | # saving additional informations | 13 | # saving additional informations |
16 | - s = Hash.new | ||
17 | - s["db_version"] = "#{ActiveRecord::Migrator.current_version}" | ||
18 | - s["backup_created_at"] = "#{Time.now}" | ||
19 | - s["gitlab_version"] = %x{git rev-parse HEAD}.gsub(/\n/,"") | ||
20 | - s["tar_version"] = %x{tar --version | head -1}.gsub(/\n/,"") | 14 | + s = {} |
15 | + s[:db_version] = "#{ActiveRecord::Migrator.current_version}" | ||
16 | + s[:backup_created_at] = "#{Time.now}" | ||
17 | + s[:gitlab_version] = %x{git rev-parse HEAD}.gsub(/\n/,"") | ||
18 | + s[:tar_version] = %x{tar --version | head -1}.gsub(/\n/,"") | ||
21 | 19 | ||
22 | File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file| | 20 | File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file| |
23 | file << s.to_yaml.gsub(/^---\n/,'') | 21 | file << s.to_yaml.gsub(/^---\n/,'') |
@@ -32,7 +30,7 @@ namespace :gitlab do | @@ -32,7 +30,7 @@ namespace :gitlab do | ||
32 | end | 30 | end |
33 | 31 | ||
34 | # cleanup: remove tmp files | 32 | # cleanup: remove tmp files |
35 | - print "Deletion of tmp directories..." | 33 | + print "Deleting tmp directories..." |
36 | if Kernel.system("rm -rf repositories/ db/ backup_information.yml") | 34 | if Kernel.system("rm -rf repositories/ db/ backup_information.yml") |
37 | puts "[DONE]".green | 35 | puts "[DONE]".green |
38 | else | 36 | else |
@@ -52,26 +50,23 @@ namespace :gitlab do | @@ -52,26 +50,23 @@ namespace :gitlab do | ||
52 | else | 50 | else |
53 | puts "[SKIPPING]".yellow | 51 | puts "[SKIPPING]".yellow |
54 | end | 52 | end |
55 | - | ||
56 | end | 53 | end |
57 | 54 | ||
58 | - | ||
59 | - # Restore backup of gitlab system | 55 | + # Restore backup of GitLab system |
60 | desc "GITLAB | Restore a previously created backup" | 56 | desc "GITLAB | Restore a previously created backup" |
61 | task :backup_restore => :environment do | 57 | task :backup_restore => :environment do |
62 | - | ||
63 | Dir.chdir(Gitlab.config.backup_path) | 58 | Dir.chdir(Gitlab.config.backup_path) |
64 | 59 | ||
65 | # check for existing backups in the backup dir | 60 | # check for existing backups in the backup dir |
66 | file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i } | 61 | file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i } |
67 | - puts "no backup found" if file_list.count == 0 | 62 | + puts "no backups found" if file_list.count == 0 |
68 | if file_list.count > 1 && ENV["BACKUP"].nil? | 63 | if file_list.count > 1 && ENV["BACKUP"].nil? |
69 | puts "Found more than one backup, please specify which one you want to restore:" | 64 | puts "Found more than one backup, please specify which one you want to restore:" |
70 | puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup" | 65 | puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup" |
71 | exit 1; | 66 | exit 1; |
72 | end | 67 | end |
73 | 68 | ||
74 | - tar_file = ENV["BACKUP"].nil? ? File.join(file_list.first.to_s + "_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") | 69 | + tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") |
75 | 70 | ||
76 | unless File.exists?(tar_file) | 71 | unless File.exists?(tar_file) |
77 | puts "The specified backup doesn't exist!" | 72 | puts "The specified backup doesn't exist!" |
@@ -102,16 +97,14 @@ namespace :gitlab do | @@ -102,16 +97,14 @@ namespace :gitlab do | ||
102 | Rake::Task["gitlab:app:repo_restore"].invoke | 97 | Rake::Task["gitlab:app:repo_restore"].invoke |
103 | 98 | ||
104 | # cleanup: remove tmp files | 99 | # cleanup: remove tmp files |
105 | - print "Deletion of tmp directories..." | 100 | + print "Deleting tmp directories..." |
106 | if Kernel.system("rm -rf repositories/ db/ backup_information.yml") | 101 | if Kernel.system("rm -rf repositories/ db/ backup_information.yml") |
107 | puts "[DONE]".green | 102 | puts "[DONE]".green |
108 | else | 103 | else |
109 | puts "[FAILED]".red | 104 | puts "[FAILED]".red |
110 | end | 105 | end |
111 | - | ||
112 | end | 106 | end |
113 | 107 | ||
114 | - | ||
115 | ################################################################################ | 108 | ################################################################################ |
116 | ################################# invoked tasks ################################ | 109 | ################################# invoked tasks ################################ |
117 | 110 | ||
@@ -121,7 +114,7 @@ namespace :gitlab do | @@ -121,7 +114,7 @@ namespace :gitlab do | ||
121 | backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") | 114 | backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") |
122 | FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) | 115 | FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) |
123 | puts "Dumping repositories:" | 116 | puts "Dumping repositories:" |
124 | - project = Project.all.map { |n| [n.path,n.path_to_repo] } | 117 | + project = Project.all.map { |n| [n.path, n.path_to_repo] } |
125 | project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] | 118 | project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] |
126 | project.each do |project| | 119 | project.each do |project| |
127 | print "- Dumping repository #{project.first}... " | 120 | print "- Dumping repository #{project.first}... " |
@@ -136,11 +129,11 @@ namespace :gitlab do | @@ -136,11 +129,11 @@ namespace :gitlab do | ||
136 | task :repo_restore => :environment do | 129 | task :repo_restore => :environment do |
137 | backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") | 130 | backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") |
138 | puts "Restoring repositories:" | 131 | puts "Restoring repositories:" |
139 | - project = Project.all.map { |n| [n.path,n.path_to_repo] } | 132 | + project = Project.all.map { |n| [n.path, n.path_to_repo] } |
140 | project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] | 133 | project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] |
141 | project.each do |project| | 134 | project.each do |project| |
142 | print "- Restoring repository #{project.first}... " | 135 | print "- Restoring repository #{project.first}... " |
143 | - FileUtils.rm_rf(project.second) if File.dirname(project.second) # delet old stuff | 136 | + FileUtils.rm_rf(project.second) if File.dirname(project.second) # delete old stuff |
144 | if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1") | 137 | if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1") |
145 | permission_commands = [ | 138 | permission_commands = [ |
146 | "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}", | 139 | "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}", |
@@ -157,8 +150,9 @@ namespace :gitlab do | @@ -157,8 +150,9 @@ namespace :gitlab do | ||
157 | ###################################### DB ###################################### | 150 | ###################################### DB ###################################### |
158 | 151 | ||
159 | task :db_dump => :environment do | 152 | task :db_dump => :environment do |
160 | - backup_path_db = File.join(Gitlab.config.backup_path, "db") | ||
161 | - FileUtils.mkdir_p(backup_path_db) until Dir.exists?(backup_path_db) | 153 | + backup_path_db = File.join(Gitlab.config.backup_path, "db") |
154 | + FileUtils.mkdir_p(backup_path_db) unless Dir.exists?(backup_path_db) | ||
155 | + | ||
162 | puts "Dumping database tables:" | 156 | puts "Dumping database tables:" |
163 | ActiveRecord::Base.connection.tables.each do |tbl| | 157 | ActiveRecord::Base.connection.tables.each do |tbl| |
164 | print "- Dumping table #{tbl}... " | 158 | print "- Dumping table #{tbl}... " |
@@ -176,9 +170,11 @@ namespace :gitlab do | @@ -176,9 +170,11 @@ namespace :gitlab do | ||
176 | end | 170 | end |
177 | 171 | ||
178 | task :db_restore=> :environment do | 172 | task :db_restore=> :environment do |
179 | - backup_path_db = File.join(Gitlab.config.backup_path, "db") | 173 | + backup_path_db = File.join(Gitlab.config.backup_path, "db") |
174 | + | ||
180 | puts "Restoring database tables:" | 175 | puts "Restoring database tables:" |
181 | Rake::Task["db:reset"].invoke | 176 | Rake::Task["db:reset"].invoke |
177 | + | ||
182 | Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir| | 178 | Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir| |
183 | fixture_file = File.basename(dir, ".*" ) | 179 | fixture_file = File.basename(dir, ".*" ) |
184 | print "- Loading fixture #{fixture_file}..." | 180 | print "- Loading fixture #{fixture_file}..." |
lib/tasks/gitlab/enable_automerge.rake
1 | namespace :gitlab do | 1 | namespace :gitlab do |
2 | namespace :app do | 2 | namespace :app do |
3 | desc "GITLAB | Enable auto merge" | 3 | desc "GITLAB | Enable auto merge" |
4 | - task :enable_automerge => :environment do | 4 | + task :enable_automerge => :environment do |
5 | Gitlab::Gitolite.new.enable_automerge | 5 | Gitlab::Gitolite.new.enable_automerge |
6 | 6 | ||
7 | Project.find_each do |project| | 7 | Project.find_each do |project| |
lib/tasks/gitlab/gitolite_rebuild.rake
1 | namespace :gitlab do | 1 | namespace :gitlab do |
2 | namespace :gitolite do | 2 | namespace :gitolite do |
3 | desc "GITLAB | Rebuild each project at gitolite config" | 3 | desc "GITLAB | Rebuild each project at gitolite config" |
4 | - task :update_repos => :environment do | 4 | + task :update_repos => :environment do |
5 | puts "Starting Projects" | 5 | puts "Starting Projects" |
6 | Project.find_each(:batch_size => 100) do |project| | 6 | Project.find_each(:batch_size => 100) do |project| |
7 | - puts | ||
8 | - puts "=== #{project.name}" | 7 | + puts "\n=== #{project.name}" |
9 | project.update_repository | 8 | project.update_repository |
10 | puts | 9 | puts |
11 | end | 10 | end |
lib/tasks/gitlab/setup.rake
lib/tasks/gitlab/status.rake
1 | namespace :gitlab do | 1 | namespace :gitlab do |
2 | namespace :app do | 2 | namespace :app do |
3 | - desc "GITLAB | Check gitlab installation status" | 3 | + desc "GITLAB | Check GitLab installation status" |
4 | task :status => :environment do | 4 | task :status => :environment do |
5 | - puts "Starting diagnostic".yellow | 5 | + puts "Starting diagnostics".yellow |
6 | git_base_path = Gitlab.config.git_base_path | 6 | git_base_path = Gitlab.config.git_base_path |
7 | 7 | ||
8 | print "config/database.yml............" | 8 | print "config/database.yml............" |
9 | - if File.exists?(File.join Rails.root, "config", "database.yml") | 9 | + if File.exists?(Rails.root.join "config", "database.yml") |
10 | puts "exists".green | 10 | puts "exists".green |
11 | - else | 11 | + else |
12 | puts "missing".red | 12 | puts "missing".red |
13 | return | 13 | return |
14 | end | 14 | end |
15 | 15 | ||
16 | print "config/gitlab.yml............" | 16 | print "config/gitlab.yml............" |
17 | - if File.exists?(File.join Rails.root, "config", "gitlab.yml") | ||
18 | - puts "exists".green | 17 | + if File.exists?(Rails.root.join "config", "gitlab.yml") |
18 | + puts "exists".green | ||
19 | else | 19 | else |
20 | puts "missing".red | 20 | puts "missing".red |
21 | return | 21 | return |
22 | end | 22 | end |
23 | 23 | ||
24 | print "#{git_base_path}............" | 24 | print "#{git_base_path}............" |
25 | - if File.exists?(git_base_path) | ||
26 | - puts "exists".green | ||
27 | - else | 25 | + if File.exists?(git_base_path) |
26 | + puts "exists".green | ||
27 | + else | ||
28 | puts "missing".red | 28 | puts "missing".red |
29 | return | 29 | return |
30 | end | 30 | end |
31 | 31 | ||
32 | print "#{git_base_path} is writable?............" | 32 | print "#{git_base_path} is writable?............" |
33 | if File.stat(git_base_path).writable? | 33 | if File.stat(git_base_path).writable? |
34 | - puts "YES".green | 34 | + puts "YES".green |
35 | else | 35 | else |
36 | puts "NO".red | 36 | puts "NO".red |
37 | return | 37 | return |
@@ -41,16 +41,16 @@ namespace :gitlab do | @@ -41,16 +41,16 @@ namespace :gitlab do | ||
41 | `git clone #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test` | 41 | `git clone #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test` |
42 | FileUtils.rm_rf("/tmp/gitolite_gitlab_test") | 42 | FileUtils.rm_rf("/tmp/gitolite_gitlab_test") |
43 | print "Can clone gitolite-admin?............" | 43 | print "Can clone gitolite-admin?............" |
44 | - puts "YES".green | ||
45 | - rescue | 44 | + puts "YES".green |
45 | + rescue | ||
46 | print "Can clone gitolite-admin?............" | 46 | print "Can clone gitolite-admin?............" |
47 | puts "NO".red | 47 | puts "NO".red |
48 | return | 48 | return |
49 | end | 49 | end |
50 | 50 | ||
51 | print "UMASK for .gitolite.rc is 0007? ............" | 51 | print "UMASK for .gitolite.rc is 0007? ............" |
52 | - unless open("#{git_base_path}/../.gitolite.rc").grep(/UMASK([ \t]*)=([ \t>]*)0007/).empty? | ||
53 | - puts "YES".green | 52 | + if open("#{git_base_path}/../.gitolite.rc").grep(/UMASK([ \t]*)=([ \t>]*)0007/).any? |
53 | + puts "YES".green | ||
54 | else | 54 | else |
55 | puts "NO".red | 55 | puts "NO".red |
56 | return | 56 | return |
@@ -69,16 +69,15 @@ namespace :gitlab do | @@ -69,16 +69,15 @@ namespace :gitlab do | ||
69 | end | 69 | end |
70 | end | 70 | end |
71 | 71 | ||
72 | - | ||
73 | - if Project.count > 0 | 72 | + if Project.count > 0 |
74 | puts "Validating projects repositories:".yellow | 73 | puts "Validating projects repositories:".yellow |
75 | Project.find_each(:batch_size => 100) do |project| | 74 | Project.find_each(:batch_size => 100) do |project| |
76 | print "#{project.name}....." | 75 | print "#{project.name}....." |
77 | - hook_file = File.join(project.path_to_repo, 'hooks','post-receive') | 76 | + hook_file = File.join(project.path_to_repo, 'hooks', 'post-receive') |
78 | 77 | ||
79 | unless File.exists?(hook_file) | 78 | unless File.exists?(hook_file) |
80 | - puts "post-receive file missing".red | ||
81 | - next | 79 | + puts "post-receive file missing".red |
80 | + return | ||
82 | end | 81 | end |
83 | 82 | ||
84 | puts "post-receive file ok".green | 83 | puts "post-receive file ok".green |
lib/tasks/gitlab/write_hook.rake
@@ -4,7 +4,6 @@ namespace :gitlab do | @@ -4,7 +4,6 @@ namespace :gitlab do | ||
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") |
7 | - | ||
8 | gitlab_hook_files = ['post-receive'] | 7 | gitlab_hook_files = ['post-receive'] |
9 | 8 | ||
10 | gitlab_hook_files.each do |file_name| | 9 | gitlab_hook_files.each do |file_name| |
@@ -20,4 +19,3 @@ namespace :gitlab do | @@ -20,4 +19,3 @@ namespace :gitlab do | ||
20 | end | 19 | end |
21 | end | 20 | end |
22 | end | 21 | end |
23 | - |
spec/helpers/tree_helper_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper' | @@ -2,7 +2,7 @@ require 'spec_helper' | ||
2 | 2 | ||
3 | describe TreeHelper do | 3 | describe TreeHelper do |
4 | describe '#markup?' do | 4 | describe '#markup?' do |
5 | - %w(mdown md markdown textile rdoc org creole mediawiki rst asciidoc pod).each do |type| | 5 | + %w(textile rdoc org creole mediawiki rst asciidoc pod).each do |type| |
6 | it "returns true for #{type} files" do | 6 | it "returns true for #{type} files" do |
7 | markup?("README.#{type}").should be_true | 7 | markup?("README.#{type}").should be_true |
8 | end | 8 | end |
spec/models/user_spec.rb
@@ -73,4 +73,30 @@ describe User do | @@ -73,4 +73,30 @@ describe User do | ||
73 | user.authentication_token.should_not be_blank | 73 | user.authentication_token.should_not be_blank |
74 | end | 74 | end |
75 | end | 75 | end |
76 | + | ||
77 | + describe "attributes can be changed by a regular user" do | ||
78 | + before do | ||
79 | + @user = Factory :user | ||
80 | + @user.update_attributes(skype: "testskype", linkedin: "testlinkedin") | ||
81 | + end | ||
82 | + it { @user.skype.should == 'testskype' } | ||
83 | + it { @user.linkedin.should == 'testlinkedin' } | ||
84 | + end | ||
85 | + | ||
86 | + describe "attributes that shouldn't be changed by a regular user" do | ||
87 | + before do | ||
88 | + @user = Factory :user | ||
89 | + @user.update_attributes(projects_limit: 50) | ||
90 | + end | ||
91 | + it { @user.projects_limit.should_not == 50 } | ||
92 | + end | ||
93 | + | ||
94 | + describe "attributes can be changed by an admin user" do | ||
95 | + before do | ||
96 | + @admin_user = Factory :admin | ||
97 | + @admin_user.update_attributes({ skype: "testskype", projects_limit: 50 }, as: :admin) | ||
98 | + end | ||
99 | + it { @admin_user.skype.should == 'testskype' } | ||
100 | + it { @admin_user.projects_limit.should == 50 } | ||
101 | + end | ||
76 | end | 102 | end |
spec/requests/api/projects_spec.rb
@@ -111,42 +111,52 @@ describe Gitlab::API do | @@ -111,42 +111,52 @@ describe Gitlab::API do | ||
111 | end | 111 | end |
112 | end | 112 | end |
113 | 113 | ||
114 | - describe "GET /projects/:id/users" do | ||
115 | - it "should return project users" do | ||
116 | - get api("/projects/#{project.code}/users", user) | ||
117 | - | 114 | + describe "GET /projects/:id/members" do |
115 | + it "should return project team members" do | ||
116 | + get api("/projects/#{project.code}/members", user) | ||
118 | response.status.should == 200 | 117 | response.status.should == 200 |
119 | - | ||
120 | json_response.should be_an Array | 118 | json_response.should be_an Array |
121 | json_response.count.should == 2 | 119 | json_response.count.should == 2 |
122 | - json_response.first['user']['id'].should == user.id | 120 | + json_response.first['email'].should == user.email |
123 | end | 121 | end |
124 | end | 122 | end |
125 | 123 | ||
126 | - describe "POST /projects/:id/users" do | ||
127 | - it "should add users to project" do | ||
128 | - expect { | ||
129 | - post api("/projects/#{project.code}/users", user), | ||
130 | - user_ids: {"0" => user2.id}, project_access: UsersProject::DEVELOPER | ||
131 | - }.to change {project.users_projects.where(:project_access => UsersProject::DEVELOPER).count}.by(1) | 124 | + describe "GET /projects/:id/members/:user_id" do |
125 | + it "should return project team member" do | ||
126 | + get api("/projects/#{project.code}/members/#{user.id}", user) | ||
127 | + response.status.should == 200 | ||
128 | + json_response['email'].should == user.email | ||
129 | + json_response['access_level'].should == UsersProject::MASTER | ||
132 | end | 130 | end |
133 | end | 131 | end |
134 | 132 | ||
135 | - describe "PUT /projects/:id/users" do | ||
136 | - it "should update users to new access role" do | 133 | + describe "POST /projects/:id/members" do |
134 | + it "should add user to project team" do | ||
137 | expect { | 135 | expect { |
138 | - put api("/projects/#{project.code}/users", user), | ||
139 | - user_ids: {"0" => user3.id}, project_access: UsersProject::MASTER | ||
140 | - }.to change {project.users_projects.where(:project_access => UsersProject::MASTER).count}.by(1) | 136 | + post api("/projects/#{project.code}/members", user), user_id: user2.id, |
137 | + access_level: UsersProject::DEVELOPER | ||
138 | + }.to change { UsersProject.count }.by(1) | ||
139 | + | ||
140 | + response.status.should == 201 | ||
141 | + json_response['email'].should == user2.email | ||
142 | + json_response['access_level'].should == UsersProject::DEVELOPER | ||
143 | + end | ||
144 | + end | ||
145 | + | ||
146 | + describe "PUT /projects/:id/members/:user_id" do | ||
147 | + it "should update project team member" do | ||
148 | + put api("/projects/#{project.code}/members/#{user3.id}", user), access_level: UsersProject::MASTER | ||
149 | + response.status.should == 200 | ||
150 | + json_response['email'].should == user3.email | ||
151 | + json_response['access_level'].should == UsersProject::MASTER | ||
141 | end | 152 | end |
142 | end | 153 | end |
143 | 154 | ||
144 | - describe "DELETE /projects/:id/users" do | ||
145 | - it "should delete users from project" do | 155 | + describe "DELETE /projects/:id/members/:user_id" do |
156 | + it "should remove user from project team" do | ||
146 | expect { | 157 | expect { |
147 | - delete api("/projects/#{project.code}/users", user), | ||
148 | - user_ids: {"0" => user3.id} | ||
149 | - }.to change {project.users_projects.count}.by(-1) | 158 | + delete api("/projects/#{project.code}/members/#{user3.id}", user) |
159 | + }.to change { UsersProject.count }.by(-1) | ||
150 | end | 160 | end |
151 | end | 161 | end |
152 | 162 | ||
@@ -189,6 +199,27 @@ describe Gitlab::API do | @@ -189,6 +199,27 @@ describe Gitlab::API do | ||
189 | end | 199 | end |
190 | end | 200 | end |
191 | 201 | ||
202 | + describe "GET /projects/:id/repository/commits" do | ||
203 | + context "authorized user" do | ||
204 | + before { project.add_access(user2, :read) } | ||
205 | + | ||
206 | + it "should return project commits" do | ||
207 | + get api("/projects/#{project.code}/repository/commits", user) | ||
208 | + response.status.should == 200 | ||
209 | + | ||
210 | + json_response.should be_an Array | ||
211 | + json_response.first['id'].should == project.commit.id | ||
212 | + end | ||
213 | + end | ||
214 | + | ||
215 | + context "unauthorized user" do | ||
216 | + it "should not return project commits" do | ||
217 | + get api("/projects/#{project.code}/repository/commits") | ||
218 | + response.status.should == 401 | ||
219 | + end | ||
220 | + end | ||
221 | + end | ||
222 | + | ||
192 | describe "GET /projects/:id/snippets/:snippet_id" do | 223 | describe "GET /projects/:id/snippets/:snippet_id" do |
193 | it "should return a project snippet" do | 224 | it "should return a project snippet" do |
194 | get api("/projects/#{project.code}/snippets/#{snippet.id}", user) | 225 | get api("/projects/#{project.code}/snippets/#{snippet.id}", user) |
@@ -0,0 +1,39 @@ | @@ -0,0 +1,39 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Gitlab::API do | ||
4 | + include ApiHelpers | ||
5 | + | ||
6 | + let(:user) { Factory :user } | ||
7 | + | ||
8 | + describe "POST /session" do | ||
9 | + context "when valid password" do | ||
10 | + it "should return private token" do | ||
11 | + post api("/session"), email: user.email, password: '123456' | ||
12 | + response.status.should == 201 | ||
13 | + | ||
14 | + json_response['email'].should == user.email | ||
15 | + json_response['private_token'].should == user.private_token | ||
16 | + end | ||
17 | + end | ||
18 | + | ||
19 | + context "when invalid password" do | ||
20 | + it "should return authentication error" do | ||
21 | + post api("/session"), email: user.email, password: '123' | ||
22 | + response.status.should == 401 | ||
23 | + | ||
24 | + json_response['email'].should be_nil | ||
25 | + json_response['private_token'].should be_nil | ||
26 | + end | ||
27 | + end | ||
28 | + | ||
29 | + context "when empty password" do | ||
30 | + it "should return authentication error" do | ||
31 | + post api("/session"), email: user.email | ||
32 | + response.status.should == 401 | ||
33 | + | ||
34 | + json_response['email'].should be_nil | ||
35 | + json_response['private_token'].should be_nil | ||
36 | + end | ||
37 | + end | ||
38 | + end | ||
39 | +end |
spec/requests/api/ssh_keys_spec.rb
@@ -1,73 +0,0 @@ | @@ -1,73 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe Gitlab::Keys do | ||
4 | - include ApiHelpers | ||
5 | - let(:user) { | ||
6 | - user = Factory.create :user | ||
7 | - user.reset_authentication_token! | ||
8 | - user | ||
9 | - } | ||
10 | - let(:key) { Factory.create :key, { user: user}} | ||
11 | - | ||
12 | - describe "GET /keys" do | ||
13 | - context "when unauthenticated" do | ||
14 | - it "should return authentication error" do | ||
15 | - get api("/keys") | ||
16 | - response.status.should == 401 | ||
17 | - end | ||
18 | - end | ||
19 | - context "when authenticated" do | ||
20 | - it "should return array of ssh keys" do | ||
21 | - user.keys << key | ||
22 | - user.save | ||
23 | - get api("/keys", user) | ||
24 | - response.status.should == 200 | ||
25 | - json_response.should be_an Array | ||
26 | - json_response.first["title"].should == key.title | ||
27 | - end | ||
28 | - end | ||
29 | - end | ||
30 | - | ||
31 | - describe "GET /keys/:id" do | ||
32 | - it "should returm single key" do | ||
33 | - user.keys << key | ||
34 | - user.save | ||
35 | - get api("/keys/#{key.id}", user) | ||
36 | - response.status.should == 200 | ||
37 | - json_response["title"].should == key.title | ||
38 | - end | ||
39 | - it "should return 404 Not Found within invalid ID" do | ||
40 | - get api("/keys/42", user) | ||
41 | - response.status.should == 404 | ||
42 | - end | ||
43 | - end | ||
44 | - | ||
45 | - describe "POST /keys" do | ||
46 | - it "should not create invalid ssh key" do | ||
47 | - post api("/keys", user), { title: "invalid key" } | ||
48 | - response.status.should == 404 | ||
49 | - end | ||
50 | - it "should create ssh key" do | ||
51 | - key_attrs = Factory.attributes :key | ||
52 | - expect { | ||
53 | - post api("/keys", user), key_attrs | ||
54 | - }.to change{ user.keys.count }.by(1) | ||
55 | - end | ||
56 | - end | ||
57 | - | ||
58 | - describe "DELETE /keys/:id" do | ||
59 | - it "should delete existed key" do | ||
60 | - user.keys << key | ||
61 | - user.save | ||
62 | - expect { | ||
63 | - delete api("/keys/#{key.id}", user) | ||
64 | - }.to change{user.keys.count}.by(-1) | ||
65 | - end | ||
66 | - it "should return 404 Not Found within invalid ID" do | ||
67 | - delete api("/keys/42", user) | ||
68 | - response.status.should == 404 | ||
69 | - end | ||
70 | - end | ||
71 | - | ||
72 | -end | ||
73 | - |
spec/requests/api/users_spec.rb
@@ -3,7 +3,8 @@ require 'spec_helper' | @@ -3,7 +3,8 @@ require 'spec_helper' | ||
3 | describe Gitlab::API do | 3 | describe Gitlab::API do |
4 | include ApiHelpers | 4 | include ApiHelpers |
5 | 5 | ||
6 | - let(:user) { Factory :user } | 6 | + let(:user) { Factory :user } |
7 | + let(:key) { Factory :key, user: user } | ||
7 | 8 | ||
8 | describe "GET /users" do | 9 | describe "GET /users" do |
9 | context "when unauthenticated" do | 10 | context "when unauthenticated" do |
@@ -38,4 +39,68 @@ describe Gitlab::API do | @@ -38,4 +39,68 @@ describe Gitlab::API do | ||
38 | json_response['email'].should == user.email | 39 | json_response['email'].should == user.email |
39 | end | 40 | end |
40 | end | 41 | end |
42 | + | ||
43 | + describe "GET /user/keys" do | ||
44 | + context "when unauthenticated" do | ||
45 | + it "should return authentication error" do | ||
46 | + get api("/user/keys") | ||
47 | + response.status.should == 401 | ||
48 | + end | ||
49 | + end | ||
50 | + | ||
51 | + context "when authenticated" do | ||
52 | + it "should return array of ssh keys" do | ||
53 | + user.keys << key | ||
54 | + user.save | ||
55 | + get api("/user/keys", user) | ||
56 | + response.status.should == 200 | ||
57 | + json_response.should be_an Array | ||
58 | + json_response.first["title"].should == key.title | ||
59 | + end | ||
60 | + end | ||
61 | + end | ||
62 | + | ||
63 | + describe "GET /user/keys/:id" do | ||
64 | + it "should returm single key" do | ||
65 | + user.keys << key | ||
66 | + user.save | ||
67 | + get api("/user/keys/#{key.id}", user) | ||
68 | + response.status.should == 200 | ||
69 | + json_response["title"].should == key.title | ||
70 | + end | ||
71 | + | ||
72 | + it "should return 404 Not Found within invalid ID" do | ||
73 | + get api("/user/keys/42", user) | ||
74 | + response.status.should == 404 | ||
75 | + end | ||
76 | + end | ||
77 | + | ||
78 | + describe "POST /user/keys" do | ||
79 | + it "should not create invalid ssh key" do | ||
80 | + post api("/user/keys", user), { title: "invalid key" } | ||
81 | + response.status.should == 404 | ||
82 | + end | ||
83 | + | ||
84 | + it "should create ssh key" do | ||
85 | + key_attrs = Factory.attributes :key | ||
86 | + expect { | ||
87 | + post api("/user/keys", user), key_attrs | ||
88 | + }.to change{ user.keys.count }.by(1) | ||
89 | + end | ||
90 | + end | ||
91 | + | ||
92 | + describe "DELETE /user/keys/:id" do | ||
93 | + it "should delete existed key" do | ||
94 | + user.keys << key | ||
95 | + user.save | ||
96 | + expect { | ||
97 | + delete api("/user/keys/#{key.id}", user) | ||
98 | + }.to change{user.keys.count}.by(-1) | ||
99 | + end | ||
100 | + | ||
101 | + it "should return 404 Not Found within invalid ID" do | ||
102 | + delete api("/user/keys/42", user) | ||
103 | + response.status.should == 404 | ||
104 | + end | ||
105 | + end | ||
41 | end | 106 | end |