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 | 4 | v 2.9.0 |
2 | 5 | - fixed inline notes bugs |
3 | 6 | - refactored rspecs |
... | ... | @@ -9,8 +12,10 @@ v 2.9.0 |
9 | 12 | - scss refactoring. gitlab_bootstrap/ dir |
10 | 13 | - fix git push http body bigger than 112k problem |
11 | 14 | - list of labels page under issues tab |
12 | - - API for milestones | |
15 | + - API for milestones, keys | |
13 | 16 | - restyled buttons |
17 | + - OAuth | |
18 | + - Comment order changed | |
14 | 19 | |
15 | 20 | v 2.8.1 |
16 | 21 | - ability to disable gravatars | ... | ... |
Gemfile
Gemfile.lock
... | ... | @@ -199,6 +199,7 @@ GEM |
199 | 199 | httpauth (0.1) |
200 | 200 | i18n (0.6.1) |
201 | 201 | journey (1.0.4) |
202 | + jquery-atwho-rails (0.1.6) | |
202 | 203 | jquery-rails (2.0.2) |
203 | 204 | railties (>= 3.2.0, < 5.0) |
204 | 205 | thor (~> 0.14) |
... | ... | @@ -441,6 +442,7 @@ DEPENDENCIES |
441 | 442 | haml-rails |
442 | 443 | headless |
443 | 444 | httparty |
445 | + jquery-atwho-rails (= 0.1.6) | |
444 | 446 | jquery-rails (= 2.0.2) |
445 | 447 | jquery-ui-rails (= 0.5.0) |
446 | 448 | kaminari | ... | ... |
Guardfile
... | ... | @@ -4,6 +4,7 @@ |
4 | 4 | guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do |
5 | 5 | watch(%r{^spec/.+_spec\.rb$}) |
6 | 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 | 8 | watch('spec/spec_helper.rb') { "spec" } |
8 | 9 | |
9 | 10 | # Rails example | ... | ... |
VERSION
app/assets/javascripts/application.js
app/assets/stylesheets/application.css
app/assets/stylesheets/common.scss
... | ... | @@ -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 | 188 | form { |
219 | 189 | @extend .form-horizontal; |
220 | 190 | |
... | ... | @@ -355,41 +325,6 @@ p.time { |
355 | 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 | 329 | .ico { |
395 | 330 | background: url("images.png") no-repeat -85px -77px; |
... | ... | @@ -639,22 +574,6 @@ li.note { |
639 | 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 | 578 | .supp_diff_link, |
660 | 579 | .mr_show_all_commits { | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/lists.scss
app/assets/stylesheets/main.scss
... | ... | @@ -143,6 +143,7 @@ $hover: #fdf5d9; |
143 | 143 | @import "sections/projects.scss"; |
144 | 144 | @import "sections/merge_requests.scss"; |
145 | 145 | @import "sections/graph.scss"; |
146 | +@import "sections/events.scss"; | |
146 | 147 | |
147 | 148 | /** |
148 | 149 | * This scss file redefine chozen selectbox styles for | ... | ... |
app/assets/stylesheets/sections/commits.scss
1 | 1 | .commit-box { |
2 | 2 | @extend .main_box; |
3 | 3 | |
4 | - .commit-head { | |
4 | + .commit-head { | |
5 | 5 | @extend .top_box_content; |
6 | 6 | |
7 | 7 | .commit-title { |
... | ... | @@ -29,11 +29,11 @@ |
29 | 29 | |
30 | 30 | .sha-block { |
31 | 31 | text-align:right; |
32 | - &:first-child { | |
32 | + &:first-child { | |
33 | 33 | padding-bottom:6px; |
34 | 34 | } |
35 | 35 | |
36 | - a { | |
36 | + a { | |
37 | 37 | border-bottom: 1px solid #aaa; |
38 | 38 | margin-left: 9px; |
39 | 39 | } |
... | ... | @@ -54,7 +54,7 @@ |
54 | 54 | } |
55 | 55 | |
56 | 56 | /** |
57 | - * | |
57 | + * | |
58 | 58 | * COMMIT SHOw |
59 | 59 | * |
60 | 60 | */ |
... | ... | @@ -71,7 +71,7 @@ |
71 | 71 | background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); |
72 | 72 | background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); |
73 | 73 | |
74 | - span { | |
74 | + span { | |
75 | 75 | font-size:14px; |
76 | 76 | } |
77 | 77 | } |
... | ... | @@ -111,8 +111,8 @@ |
111 | 111 | } |
112 | 112 | } |
113 | 113 | |
114 | - &.img_compared { | |
115 | - img { | |
114 | + &.img_compared { | |
115 | + img { | |
116 | 116 | max-width:300px; |
117 | 117 | } |
118 | 118 | } |
... | ... | @@ -120,12 +120,12 @@ |
120 | 120 | } |
121 | 121 | |
122 | 122 | .diff_file_content{ |
123 | - table { | |
123 | + table { | |
124 | 124 | border:none; |
125 | 125 | margin:0px; |
126 | 126 | padding:0px; |
127 | 127 | tr { |
128 | - td { | |
128 | + td { | |
129 | 129 | font-size:12px; |
130 | 130 | } |
131 | 131 | } |
... | ... | @@ -145,29 +145,29 @@ |
145 | 145 | moz-user-select: none; |
146 | 146 | -khtml-user-select: none; |
147 | 147 | user-select: none; |
148 | - a { | |
148 | + a { | |
149 | 149 | float:left; |
150 | 150 | width:35px; |
151 | 151 | font-weight:normal; |
152 | 152 | color:#666; |
153 | - &:hover { | |
153 | + &:hover { | |
154 | 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 | 160 | height:14px; |
161 | 161 | margin:0px; |
162 | 162 | padding:0px; |
163 | 163 | border:none; |
164 | - &.new { | |
164 | + &.new { | |
165 | 165 | background: #CFD; |
166 | 166 | } |
167 | - &.old { | |
167 | + &.old { | |
168 | 168 | background: #FDD; |
169 | 169 | } |
170 | - &.matched { | |
170 | + &.matched { | |
171 | 171 | color:#ccc; |
172 | 172 | background:#fafafa; |
173 | 173 | } |
... | ... | @@ -182,32 +182,32 @@ |
182 | 182 | |
183 | 183 | |
184 | 184 | /** COMMIT ROW **/ |
185 | -.commit { | |
185 | +.commit { | |
186 | 186 | @extend .wll; |
187 | 187 | |
188 | - .browse_code_link_holder { | |
188 | + .browse_code_link_holder { | |
189 | 189 | @extend .span2; |
190 | 190 | float:right; |
191 | 191 | } |
192 | 192 | |
193 | - .committed_ago { | |
193 | + .committed_ago { | |
194 | 194 | float:right; |
195 | 195 | @extend .cgray; |
196 | 196 | } |
197 | 197 | |
198 | - code { | |
198 | + code { | |
199 | 199 | background:#FCEEC1; |
200 | 200 | color:$style_color; |
201 | 201 | } |
202 | 202 | |
203 | - .commit_short_id { | |
203 | + .commit_short_id { | |
204 | 204 | float:left; |
205 | 205 | @extend .lined; |
206 | 206 | min-width:65px; |
207 | 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 | 211 | color: #777; |
212 | 212 | } |
213 | 213 | } | ... | ... |
... | ... | @@ -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 | 43 | padding: 8px 0; |
44 | 44 | overflow: hidden; |
45 | 45 | display: block; |
46 | + position:relative; | |
46 | 47 | img {float: left; margin-right: 10px;} |
48 | + img.emoji {float:none;margin:0;} | |
47 | 49 | .note-author cite{font-style: italic;} |
48 | 50 | p { color:$style_color; } |
49 | 51 | .note-author { color: $style_color;} |
... | ... | @@ -55,7 +57,9 @@ |
55 | 57 | |
56 | 58 | .delete-note { |
57 | 59 | display:none; |
58 | - float:right; | |
60 | + position:absolute; | |
61 | + right:0; | |
62 | + top:0; | |
59 | 63 | } |
60 | 64 | |
61 | 65 | &:hover { | ... | ... |
app/controllers/admin/users_controller.rb
... | ... | @@ -30,7 +30,7 @@ class Admin::UsersController < AdminController |
30 | 30 | |
31 | 31 | |
32 | 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 | 34 | end |
35 | 35 | |
36 | 36 | def edit |
... | ... | @@ -60,7 +60,7 @@ class Admin::UsersController < AdminController |
60 | 60 | def create |
61 | 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 | 64 | @admin_user.admin = (admin && admin.to_i > 0) |
65 | 65 | |
66 | 66 | respond_to do |format| |
... | ... | @@ -86,7 +86,7 @@ class Admin::UsersController < AdminController |
86 | 86 | @admin_user.admin = (admin && admin.to_i > 0) |
87 | 87 | |
88 | 88 | respond_to do |format| |
89 | - if @admin_user.update_attributes(params[:user]) | |
89 | + if @admin_user.update_attributes(params[:user], as: :admin) | |
90 | 90 | format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } |
91 | 91 | format.json { head :ok } |
92 | 92 | else | ... | ... |
app/controllers/commits_controller.rb
app/controllers/hooks_controller.rb
app/controllers/issues_controller.rb
app/controllers/labels_controller.rb
app/controllers/merge_requests_controller.rb
app/controllers/milestones_controller.rb
app/controllers/snippets_controller.rb
app/helpers/tree_helper.rb
... | ... | @@ -32,7 +32,11 @@ module TreeHelper |
32 | 32 | # |
33 | 33 | # Returns boolean |
34 | 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 | 41 | end |
38 | 42 | end | ... | ... |
app/models/commit.rb
... | ... | @@ -82,20 +82,24 @@ class Commit |
82 | 82 | end |
83 | 83 | |
84 | 84 | def compare(project, from, to) |
85 | - first = project.commit(to.try(:strip)) | |
86 | - last = project.commit(from.try(:strip)) | |
87 | - | |
88 | 85 | result = { |
89 | 86 | commits: [], |
90 | 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 | 97 | if first && last |
95 | 98 | commits = [first, last].sort_by(&:created_at) |
96 | 99 | younger = commits.first |
97 | 100 | older = commits.last |
98 | 101 | |
102 | + result[:same] = (younger.id == older.id) | |
99 | 103 | result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)} |
100 | 104 | result[:diffs] = project.repo.diff(younger.id, older.id) rescue [] |
101 | 105 | result[:commit] = Commit.new(older) | ... | ... |
app/models/event.rb
... | ... | @@ -132,6 +132,7 @@ class Event < ActiveRecord::Base |
132 | 132 | end |
133 | 133 | end |
134 | 134 | |
135 | + | |
135 | 136 | delegate :name, :email, to: :author, prefix: true, allow_nil: true |
136 | 137 | delegate :title, to: :issue, prefix: true, allow_nil: true |
137 | 138 | delegate :title, to: :merge_request, prefix: true, allow_nil: true | ... | ... |
app/models/user.rb
... | ... | @@ -6,8 +6,9 @@ class User < ActiveRecord::Base |
6 | 6 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable |
7 | 7 | |
8 | 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 | 13 | attr_accessor :force_random_password |
13 | 14 | ... | ... |
app/roles/repository.rb
... | ... | @@ -79,6 +79,14 @@ module Repository |
79 | 79 | @heads ||= repo.heads |
80 | 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 | 90 | def tree(fcommit, path = nil) |
83 | 91 | fcommit = commit if fcommit == :head |
84 | 92 | tree = fcommit.tree | ... | ... |
app/views/commits/compare.html.haml
1 | 1 | = render "head" |
2 | 2 | |
3 | -%h3 | |
3 | +%h3.page_title | |
4 | 4 | Compare View |
5 | 5 | %hr |
6 | 6 | |
7 | 7 | %div |
8 | - %p | |
8 | + %p.slead | |
9 | 9 | Fill input field with commit id like |
10 | - %code '4eedf23' | |
10 | + %code.label_branch 4eedf23 | |
11 | 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 | 15 | %br |
16 | 16 | |
... | ... | @@ -19,22 +19,24 @@ |
19 | 19 | = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" |
20 | 20 | = "..." |
21 | 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 | 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 | 29 | %div.ui-box |
28 | 30 | %h5.small Commits (#{@commits.count}) |
29 | 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 | 37 | :javascript |
36 | 38 | $(function() { |
37 | - var availableTags = #{@project.heads.map(&:name).to_json}; | |
39 | + var availableTags = #{@project.ref_names.to_json}; | |
38 | 40 | |
39 | 41 | $("#from").autocomplete({ |
40 | 42 | source: availableTags, |
... | ... | @@ -45,5 +47,7 @@ |
45 | 47 | source: availableTags, |
46 | 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 | 2 | %li.commit |
3 | 3 | %p |
4 | 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 | 7 | = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 |
8 | 8 | = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding" | ... | ... |
app/views/events/_event.html.haml
1 | 1 | - if event.allowed? |
2 | - - if event.issue? | |
3 | - .event_feed | |
2 | + %div.event-item | |
3 | + - if event.issue? | |
4 | 4 | = render "events/event_issue", event: event |
5 | 5 | |
6 | - - elsif event.merge_request? | |
7 | - .event_feed | |
6 | + - elsif event.merge_request? | |
8 | 7 | = render "events/event_merge_request", event: event |
9 | 8 | |
10 | - - elsif event.push? | |
11 | - .event_feed | |
9 | + - elsif event.push? | |
12 | 10 | = render "events/event_push", event: event |
13 | 11 | |
14 | - - elsif event.membership_changed? | |
15 | - .event_feed | |
12 | + - elsif event.membership_changed? | |
16 | 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 | 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 | 2 | .event_lp |
3 | 3 | %div |
4 | 4 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
5 | - %span Your pushed to | |
5 | + %span You pushed to | |
6 | 6 | = event.ref_type |
7 | 7 | = link_to project_commits_path(event.project, ref: event.ref_name) do |
8 | 8 | %strong= truncate(event.ref_name, length: 28) | ... | ... |
app/views/events/_event_membership_changed.html.haml
1 | 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 | 1 | - if event.action_name == "merged" |
2 | 2 | .event_icon= image_tag "event_mr_merged.png" |
3 | 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 | 1 | %div |
2 | 2 | .event_icon= image_tag "event_push.png" |
3 | 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 | 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 | 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 | 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 | 2 | .back_link |
3 | 3 | = link_to help_path do |
4 | 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 | 10 | %li |
9 | - %a{href: "#README"} README | |
11 | + = link_to "Projects", "#projects", 'data-toggle' => 'tab' | |
10 | 12 | %li |
11 | - %a{href: "#projects"} Projects | |
13 | + = link_to "Snippets", "#snippets", 'data-toggle' => 'tab' | |
12 | 14 | %li |
13 | - %a{href: "#snippets"} Snippets | |
15 | + = link_to "Repositories", "#repositories", 'data-toggle' => 'tab' | |
14 | 16 | %li |
15 | - %a{href: "#users"} Users | |
17 | + = link_to "Users", "#users", 'data-toggle' => 'tab' | |
16 | 18 | %li |
17 | - %a{href: "#issues"} Issues | |
19 | + = link_to "Session", "#session", 'data-toggle' => 'tab' | |
18 | 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 | 37 | = f.file_field :attachment, class: "input-file" |
38 | 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 | 43 | %i.icon-file |
44 | 44 | = content.name |
45 | 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 | 52 | :javascript |
49 | 53 | $(function(){ | ... | ... |
app/views/refs/_tree_file.html.haml
... | ... | @@ -9,7 +9,11 @@ |
9 | 9 | = link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small" |
10 | 10 | = link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small" |
11 | 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 | 17 | .file_content.wiki |
14 | 18 | = raw GitHub::Markup.render(name, file.data) |
15 | 19 | - else | ... | ... |
... | ... | @@ -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 | -Resque.watch_queue(PostReceive.instance_variable_get("@queue")) |
config/initializers/resque.rb
... | ... | @@ -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 | -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 | 0 | \ No newline at end of file |
config/initializers/resque_mailer.rb
... | ... | @@ -1 +0,0 @@ |
1 | -Resque::Mailer.excluded_environments = [] |
config/unicorn.rb.example
doc/api/README.md
... | ... | @@ -30,8 +30,9 @@ When listing resources you can pass the following parameters: |
30 | 30 | ## Contents |
31 | 31 | |
32 | 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 | 34 | + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md) |
34 | 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 | 37 | + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) |
36 | 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 | -## 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 | 102 | + `name` (required) - new project name |
103 | 103 | + `code` (optional) - new project code, uses project name if not set |
104 | 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 | 106 | + `default_branch` (optional) - 'master' by default |
107 | 107 | + `issues_enabled` (optional) - enabled by default |
108 | 108 | + `wall_enabled` (optional) - enabled by default |
... | ... | @@ -112,66 +112,89 @@ Parameters: |
112 | 112 | Will return created project with status `201 Created` on success, or `404 Not |
113 | 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 | 123 | Parameters: |
124 | 124 | |
125 | 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 | 160 | Parameters: |
138 | 161 | |
139 | 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 | 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 | 176 | Parameters: |
154 | 177 | |
155 | 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 | 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 | 192 | Parameters: |
170 | 193 | |
171 | 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 | 199 | ## Get project hooks |
177 | 200 | |
... | ... | @@ -216,135 +239,3 @@ Parameters: |
216 | 239 | + `hook_id` (required) - The ID of hook to delete |
217 | 240 | |
218 | 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 @@ |
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 @@ |
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 | 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 | 16 | end |
17 | 17 | |
18 | 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 | 20 | page.should have_link "Create Merge Request" |
21 | 21 | end |
22 | 22 | ... | ... |
lib/api.rb
lib/api/entities.rb
... | ... | @@ -9,6 +9,10 @@ module Gitlab |
9 | 9 | expose :id, :email, :name, :blocked, :created_at |
10 | 10 | end |
11 | 11 | |
12 | + class UserLogin < UserBasic | |
13 | + expose :private_token | |
14 | + end | |
15 | + | |
12 | 16 | class Hook < Grape::Entity |
13 | 17 | expose :id, :url |
14 | 18 | end |
... | ... | @@ -20,15 +24,20 @@ module Gitlab |
20 | 24 | expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at |
21 | 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 | 31 | end |
27 | 32 | |
28 | 33 | class RepoObject < Grape::Entity |
29 | 34 | expose :name, :commit |
30 | 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 | 41 | class ProjectSnippet < Grape::Entity |
33 | 42 | expose :id, :title, :file_name |
34 | 43 | expose :author, using: Entities::UserBasic |
... | ... | @@ -36,7 +45,9 @@ module Gitlab |
36 | 45 | end |
37 | 46 | |
38 | 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 | 51 | end |
41 | 52 | |
42 | 53 | class Issue < Grape::Entity |
... | ... | @@ -49,10 +60,8 @@ module Gitlab |
49 | 60 | expose :closed, :updated_at, :created_at |
50 | 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 | 65 | end |
57 | 66 | end |
58 | 67 | end | ... | ... |
lib/api/helpers.rb
lib/api/keys.rb
... | ... | @@ -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 | 11 | # Example Request: |
12 | 12 | # GET /projects/:id/milestones |
13 | 13 | get ":id/milestones" do |
14 | + authorize! :read_milestone, user_project | |
15 | + | |
14 | 16 | present paginate(user_project.milestones), with: Entities::Milestone |
15 | 17 | end |
16 | 18 | |
... | ... | @@ -22,6 +24,8 @@ module Gitlab |
22 | 24 | # Example Request: |
23 | 25 | # GET /projects/:id/milestones/:milestone_id |
24 | 26 | get ":id/milestones/:milestone_id" do |
27 | + authorize! :read_milestone, user_project | |
28 | + | |
25 | 29 | @milestone = user_project.milestones.find(params[:milestone_id]) |
26 | 30 | present @milestone, with: Entities::Milestone |
27 | 31 | end |
... | ... | @@ -36,6 +40,8 @@ module Gitlab |
36 | 40 | # Example Request: |
37 | 41 | # POST /projects/:id/milestones |
38 | 42 | post ":id/milestones" do |
43 | + authorize! :admin_milestone, user_project | |
44 | + | |
39 | 45 | attrs = attributes_for_keys [:title, :description, :due_date] |
40 | 46 | @milestone = user_project.milestones.new attrs |
41 | 47 | if @milestone.save | ... | ... |
lib/api/projects.rb
... | ... | @@ -40,14 +40,14 @@ module Gitlab |
40 | 40 | post do |
41 | 41 | params[:code] ||= params[:name] |
42 | 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 | 51 | :wiki_enabled] |
52 | 52 | @project = Project.create_by_user(attrs, current_user) |
53 | 53 | if @project.saved? |
... | ... | @@ -57,56 +57,83 @@ module Gitlab |
57 | 57 | end |
58 | 58 | end |
59 | 59 | |
60 | - # Get project users | |
60 | + # Get a project team members | |
61 | 61 | # |
62 | 62 | # Parameters: |
63 | 63 | # id (required) - The ID or code name of a project |
64 | 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 | 69 | end |
70 | 70 | |
71 | - # Add users to project with specified access level | |
71 | + # Get a project team members | |
72 | 72 | # |
73 | 73 | # Parameters: |
74 | 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 | 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 | 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 | 104 | end |
84 | 105 | |
85 | - # Update users to specified access level | |
106 | + # Update project team member | |
86 | 107 | # |
87 | 108 | # Parameters: |
88 | 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 | 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 | 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 | 124 | end |
98 | 125 | |
99 | - # Delete project users | |
126 | + # Remove a team member from project | |
100 | 127 | # |
101 | 128 | # Parameters: |
102 | 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 | 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 | 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 | 137 | end |
111 | 138 | |
112 | 139 | # Get project hooks |
... | ... | @@ -184,6 +211,24 @@ module Gitlab |
184 | 211 | present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject |
185 | 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 | 232 | # Get a project snippet |
188 | 233 | # |
189 | 234 | # Parameters: |
... | ... | @@ -207,6 +252,8 @@ module Gitlab |
207 | 252 | # Example Request: |
208 | 253 | # POST /projects/:id/snippets |
209 | 254 | post ":id/snippets" do |
255 | + authorize! :write_snippet, user_project | |
256 | + | |
210 | 257 | attrs = attributes_for_keys [:title, :file_name] |
211 | 258 | attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? |
212 | 259 | attrs[:content] = params[:code] if params[:code].present? |
... | ... | @@ -282,6 +329,8 @@ module Gitlab |
282 | 329 | # Example Request: |
283 | 330 | # GET /projects/:id/repository/commits/:sha/blob |
284 | 331 | get ":id/repository/commits/:sha/blob" do |
332 | + authorize! :download_code, user_project | |
333 | + | |
285 | 334 | ref = params[:sha] |
286 | 335 | |
287 | 336 | commit = user_project.commit ref | ... | ... |
... | ... | @@ -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 | 25 | end |
26 | 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 | 81 | end |
35 | 82 | end |
36 | 83 | end | ... | ... |
lib/gitlab/graph_commit.rb
... | ... | @@ -5,7 +5,7 @@ module Gitlab |
5 | 5 | attr_accessor :time, :space |
6 | 6 | attr_accessor :refs |
7 | 7 | |
8 | - include ActionView::Helpers::SanitizeHelper | |
8 | + include ActionView::Helpers::TagHelper | |
9 | 9 | |
10 | 10 | def self.to_graph(project) |
11 | 11 | @repo = project.repo |
... | ... | @@ -166,7 +166,7 @@ module Gitlab |
166 | 166 | h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? |
167 | 167 | h[:id] = sha |
168 | 168 | h[:date] = date |
169 | - h[:message] = sanitize(Gitlab::Encode.utf8(message)) | |
169 | + h[:message] = escape_once(Gitlab::Encode.utf8(message)) | |
170 | 170 | h[:login] = author.email |
171 | 171 | h |
172 | 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 | 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 | 11 | end |
16 | 12 | end |
17 | 13 | |
18 | 14 | desc "Add user to as a developer to all projects" |
19 | 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 | 20 | end | ... | ... |
lib/tasks/bulk_import.rake
1 | - | |
2 | 1 | desc "Imports existing Git repos from a directory into new projects in git_base_path" |
3 | 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 | 4 | repos_to_import = Dir.glob("#{import_directory}/*") |
7 | 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 | 10 | repos_to_import.each do |repo_path| |
14 | 11 | repo_name = File.basename repo_path |
12 | + clone_path = "#{git_base_path}#{repo_name}.git" | |
15 | 13 | |
16 | 14 | puts " Processing #{repo_name}" |
17 | - clone_path = "#{git_base_path}#{repo_name}.git" | |
18 | 15 | |
19 | 16 | if Dir.exists? clone_path |
20 | 17 | if Project.find_by_code(repo_name) |
... | ... | @@ -38,7 +35,6 @@ task :import_projects, [:directory,:email] => :environment do |t, args| |
38 | 35 | else |
39 | 36 | failed_count += 1 |
40 | 37 | end |
41 | - | |
42 | 38 | end |
43 | 39 | |
44 | 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 | 45 | git_user = Gitlab.config.ssh_user |
50 | 46 | begin |
51 | 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 | 52 | end |
59 | 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 | 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 | 60 | # Using find_by_code since that's the most important identifer to be unique |
67 | 61 | if Project.find_by_code(project_name) |
68 | 62 | puts " INFO: Project #{project_name} already exists in Gitlab, skipping." |
69 | - false | |
70 | 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 | 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 | 76 | else |
103 | 77 | puts " ERROR: Failed to create project #{project} because #{project.errors.first}" |
104 | - false | |
105 | 78 | end |
106 | 79 | end |
107 | 80 | else |
108 | - puts " ERROR: #{user_email} not found, skipping" | |
109 | - false | |
81 | + puts " ERROR: user with #{user_email} not found, skipping" | |
110 | 82 | end |
111 | 83 | end | ... | ... |
lib/tasks/gitlab/backup.rake
... | ... | @@ -2,22 +2,20 @@ require 'active_record/fixtures' |
2 | 2 | |
3 | 3 | namespace :gitlab do |
4 | 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 | 7 | task :backup_create => :environment do |
9 | - | |
10 | 8 | Rake::Task["gitlab:app:db_dump"].invoke |
11 | 9 | Rake::Task["gitlab:app:repo_dump"].invoke |
12 | 10 | |
13 | 11 | Dir.chdir(Gitlab.config.backup_path) |
14 | 12 | |
15 | 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 | 20 | File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file| |
23 | 21 | file << s.to_yaml.gsub(/^---\n/,'') |
... | ... | @@ -32,7 +30,7 @@ namespace :gitlab do |
32 | 30 | end |
33 | 31 | |
34 | 32 | # cleanup: remove tmp files |
35 | - print "Deletion of tmp directories..." | |
33 | + print "Deleting tmp directories..." | |
36 | 34 | if Kernel.system("rm -rf repositories/ db/ backup_information.yml") |
37 | 35 | puts "[DONE]".green |
38 | 36 | else |
... | ... | @@ -52,26 +50,23 @@ namespace :gitlab do |
52 | 50 | else |
53 | 51 | puts "[SKIPPING]".yellow |
54 | 52 | end |
55 | - | |
56 | 53 | end |
57 | 54 | |
58 | - | |
59 | - # Restore backup of gitlab system | |
55 | + # Restore backup of GitLab system | |
60 | 56 | desc "GITLAB | Restore a previously created backup" |
61 | 57 | task :backup_restore => :environment do |
62 | - | |
63 | 58 | Dir.chdir(Gitlab.config.backup_path) |
64 | 59 | |
65 | 60 | # check for existing backups in the backup dir |
66 | 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 | 63 | if file_list.count > 1 && ENV["BACKUP"].nil? |
69 | 64 | puts "Found more than one backup, please specify which one you want to restore:" |
70 | 65 | puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup" |
71 | 66 | exit 1; |
72 | 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 | 71 | unless File.exists?(tar_file) |
77 | 72 | puts "The specified backup doesn't exist!" |
... | ... | @@ -102,16 +97,14 @@ namespace :gitlab do |
102 | 97 | Rake::Task["gitlab:app:repo_restore"].invoke |
103 | 98 | |
104 | 99 | # cleanup: remove tmp files |
105 | - print "Deletion of tmp directories..." | |
100 | + print "Deleting tmp directories..." | |
106 | 101 | if Kernel.system("rm -rf repositories/ db/ backup_information.yml") |
107 | 102 | puts "[DONE]".green |
108 | 103 | else |
109 | 104 | puts "[FAILED]".red |
110 | 105 | end |
111 | - | |
112 | 106 | end |
113 | 107 | |
114 | - | |
115 | 108 | ################################################################################ |
116 | 109 | ################################# invoked tasks ################################ |
117 | 110 | |
... | ... | @@ -121,7 +114,7 @@ namespace :gitlab do |
121 | 114 | backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") |
122 | 115 | FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) |
123 | 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 | 118 | project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] |
126 | 119 | project.each do |project| |
127 | 120 | print "- Dumping repository #{project.first}... " |
... | ... | @@ -136,11 +129,11 @@ namespace :gitlab do |
136 | 129 | task :repo_restore => :environment do |
137 | 130 | backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") |
138 | 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 | 133 | project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] |
141 | 134 | project.each do |project| |
142 | 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 | 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 | 138 | permission_commands = [ |
146 | 139 | "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}", |
... | ... | @@ -157,8 +150,9 @@ namespace :gitlab do |
157 | 150 | ###################################### DB ###################################### |
158 | 151 | |
159 | 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 | 156 | puts "Dumping database tables:" |
163 | 157 | ActiveRecord::Base.connection.tables.each do |tbl| |
164 | 158 | print "- Dumping table #{tbl}... " |
... | ... | @@ -176,9 +170,11 @@ namespace :gitlab do |
176 | 170 | end |
177 | 171 | |
178 | 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 | 175 | puts "Restoring database tables:" |
181 | 176 | Rake::Task["db:reset"].invoke |
177 | + | |
182 | 178 | Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir| |
183 | 179 | fixture_file = File.basename(dir, ".*" ) |
184 | 180 | print "- Loading fixture #{fixture_file}..." | ... | ... |
lib/tasks/gitlab/enable_automerge.rake
lib/tasks/gitlab/gitolite_rebuild.rake
1 | 1 | namespace :gitlab do |
2 | 2 | namespace :gitolite do |
3 | 3 | desc "GITLAB | Rebuild each project at gitolite config" |
4 | - task :update_repos => :environment do | |
4 | + task :update_repos => :environment do | |
5 | 5 | puts "Starting Projects" |
6 | 6 | Project.find_each(:batch_size => 100) do |project| |
7 | - puts | |
8 | - puts "=== #{project.name}" | |
7 | + puts "\n=== #{project.name}" | |
9 | 8 | project.update_repository |
10 | 9 | puts |
11 | 10 | end | ... | ... |
lib/tasks/gitlab/setup.rake
lib/tasks/gitlab/status.rake
1 | 1 | namespace :gitlab do |
2 | 2 | namespace :app do |
3 | - desc "GITLAB | Check gitlab installation status" | |
3 | + desc "GITLAB | Check GitLab installation status" | |
4 | 4 | task :status => :environment do |
5 | - puts "Starting diagnostic".yellow | |
5 | + puts "Starting diagnostics".yellow | |
6 | 6 | git_base_path = Gitlab.config.git_base_path |
7 | 7 | |
8 | 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 | 10 | puts "exists".green |
11 | - else | |
11 | + else | |
12 | 12 | puts "missing".red |
13 | 13 | return |
14 | 14 | end |
15 | 15 | |
16 | 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 | 19 | else |
20 | 20 | puts "missing".red |
21 | 21 | return |
22 | 22 | end |
23 | 23 | |
24 | 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 | 28 | puts "missing".red |
29 | 29 | return |
30 | 30 | end |
31 | 31 | |
32 | 32 | print "#{git_base_path} is writable?............" |
33 | 33 | if File.stat(git_base_path).writable? |
34 | - puts "YES".green | |
34 | + puts "YES".green | |
35 | 35 | else |
36 | 36 | puts "NO".red |
37 | 37 | return |
... | ... | @@ -41,16 +41,16 @@ namespace :gitlab do |
41 | 41 | `git clone #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test` |
42 | 42 | FileUtils.rm_rf("/tmp/gitolite_gitlab_test") |
43 | 43 | print "Can clone gitolite-admin?............" |
44 | - puts "YES".green | |
45 | - rescue | |
44 | + puts "YES".green | |
45 | + rescue | |
46 | 46 | print "Can clone gitolite-admin?............" |
47 | 47 | puts "NO".red |
48 | 48 | return |
49 | 49 | end |
50 | 50 | |
51 | 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 | 54 | else |
55 | 55 | puts "NO".red |
56 | 56 | return |
... | ... | @@ -69,16 +69,15 @@ namespace :gitlab do |
69 | 69 | end |
70 | 70 | end |
71 | 71 | |
72 | - | |
73 | - if Project.count > 0 | |
72 | + if Project.count > 0 | |
74 | 73 | puts "Validating projects repositories:".yellow |
75 | 74 | Project.find_each(:batch_size => 100) do |project| |
76 | 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 | 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 | 81 | end |
83 | 82 | |
84 | 83 | puts "post-receive file ok".green | ... | ... |
lib/tasks/gitlab/write_hook.rake
... | ... | @@ -4,7 +4,6 @@ namespace :gitlab do |
4 | 4 | task :write_hooks => :environment do |
5 | 5 | gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common") |
6 | 6 | gitlab_hooks_path = Rails.root.join("lib", "hooks") |
7 | - | |
8 | 7 | gitlab_hook_files = ['post-receive'] |
9 | 8 | |
10 | 9 | gitlab_hook_files.each do |file_name| |
... | ... | @@ -20,4 +19,3 @@ namespace :gitlab do |
20 | 19 | end |
21 | 20 | end |
22 | 21 | end |
23 | - | ... | ... |
spec/helpers/tree_helper_spec.rb
... | ... | @@ -2,7 +2,7 @@ require 'spec_helper' |
2 | 2 | |
3 | 3 | describe TreeHelper do |
4 | 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 | 6 | it "returns true for #{type} files" do |
7 | 7 | markup?("README.#{type}").should be_true |
8 | 8 | end | ... | ... |
spec/models/user_spec.rb
... | ... | @@ -73,4 +73,30 @@ describe User do |
73 | 73 | user.authentication_token.should_not be_blank |
74 | 74 | end |
75 | 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 | 102 | end | ... | ... |
spec/requests/api/projects_spec.rb
... | ... | @@ -111,42 +111,52 @@ describe Gitlab::API do |
111 | 111 | end |
112 | 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 | 117 | response.status.should == 200 |
119 | - | |
120 | 118 | json_response.should be_an Array |
121 | 119 | json_response.count.should == 2 |
122 | - json_response.first['user']['id'].should == user.id | |
120 | + json_response.first['email'].should == user.email | |
123 | 121 | end |
124 | 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 | 130 | end |
133 | 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 | 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 | 152 | end |
142 | 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 | 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 | 160 | end |
151 | 161 | end |
152 | 162 | |
... | ... | @@ -189,6 +199,27 @@ describe Gitlab::API do |
189 | 199 | end |
190 | 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 | 223 | describe "GET /projects/:id/snippets/:snippet_id" do |
193 | 224 | it "should return a project snippet" do |
194 | 225 | get api("/projects/#{project.code}/snippets/#{snippet.id}", user) | ... | ... |
... | ... | @@ -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 | -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 | 3 | describe Gitlab::API do |
4 | 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 | 9 | describe "GET /users" do |
9 | 10 | context "when unauthenticated" do |
... | ... | @@ -38,4 +39,68 @@ describe Gitlab::API do |
38 | 39 | json_response['email'].should == user.email |
39 | 40 | end |
40 | 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 | 106 | end | ... | ... |