Commit c09d233611e00328f0e8d493a106737f0638d9a2

Authored by Alex Denisov
2 parents e6ce4729 3c132f2e

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
... ... @@ -96,6 +96,7 @@ group :assets do
96 96 gem "therubyracer"
97 97  
98 98 gem 'chosen-rails'
  99 + gem 'jquery-atwho-rails', '0.1.6'
99 100 gem "jquery-rails", "2.0.2"
100 101 gem "jquery-ui-rails", "0.5.0"
101 102 gem "modernizr", "2.5.3"
... ...
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
1   -2.9.0pre
  1 +2.9.1
... ...
app/assets/javascripts/application.js
... ... @@ -11,6 +11,7 @@
11 11 //= require jquery.endless-scroll
12 12 //= require jquery.highlight
13 13 //= require jquery.waitforimages
  14 +//= require jquery.atwho
14 15 //= require bootstrap
15 16 //= require modernizr
16 17 //= require chosen-jquery
... ...
app/assets/stylesheets/application.css
... ... @@ -4,6 +4,7 @@
4 4 * the top of the compiled file, but it's generally better to create a new file per style scope.
5 5 *= require jquery.ui.all
6 6 *= require jquery.ui.aristo
  7 + *= require jquery.atwho
7 8 *= require chosen
8 9 *= require_self
9 10 *= require main
... ...
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
1 1 /** LISTS **/
2 2  
3   -ul {
  3 +ul {
4 4 /**
5 5 * List li block element #1
6 6 *
... ... @@ -18,7 +18,7 @@ ul {
18 18 .author { color: #999; }
19 19  
20 20 p {
21   - padding-top:5px;
  21 + padding-top:5px;
22 22 margin:0;
23 23 color:#222;
24 24 img {
... ...
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 }
... ...
app/assets/stylesheets/sections/events.scss 0 → 100644
... ... @@ -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 &lt; 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 &lt; 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 &lt; 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
... ... @@ -52,6 +52,7 @@ class CommitsController &lt; ApplicationController
52 52 @commits = result[:commits]
53 53 @commit = result[:commit]
54 54 @diffs = result[:diffs]
  55 + @refs_are_same = result[:same]
55 56 @line_notes = []
56 57  
57 58 @commits = CommitDecorator.decorate(@commits)
... ...
app/controllers/hooks_controller.rb
1 1 class HooksController < ApplicationController
2   - before_filter :authenticate_user!
3 2 before_filter :project
4 3 layout "project"
5 4  
... ...
app/controllers/issues_controller.rb
1 1 class IssuesController < ApplicationController
2   - before_filter :authenticate_user!
3 2 before_filter :project
4 3 before_filter :module_enabled
5 4 before_filter :issue, only: [:edit, :update, :destroy, :show]
... ...
app/controllers/labels_controller.rb
1 1 class LabelsController < ApplicationController
2   - before_filter :authenticate_user!
3 2 before_filter :project
4 3 before_filter :module_enabled
5 4  
... ...
app/controllers/merge_requests_controller.rb
1 1 class MergeRequestsController < ApplicationController
2   - before_filter :authenticate_user!
3 2 before_filter :project
4 3 before_filter :module_enabled
5 4 before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw]
... ...
app/controllers/milestones_controller.rb
1 1 class MilestonesController < ApplicationController
2   - before_filter :authenticate_user!
3 2 before_filter :project
4 3 before_filter :module_enabled
5 4 before_filter :milestone, only: [:edit, :update, :destroy, :show]
... ...
app/controllers/snippets_controller.rb
1 1 class SnippetsController < ApplicationController
2   - before_filter :authenticate_user!
3 2 before_filter :project
4 3 before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw]
5 4 layout "project"
... ...
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 &lt; 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 &lt; 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   - &amp; 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 &ndash;
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   -&rarr;
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 + &rarr;
  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 &rarr; #{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 &larr; 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
... ...
config/initializers/4_resque.rb 0 → 100644
... ... @@ -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
... ... @@ -6,7 +6,7 @@ working_directory app_dir
6 6 # worker spawn times
7 7 preload_app true
8 8  
9   -# nuke workers after 60 seconds (the default)
  9 +# nuke workers after 30 seconds (60 is the default)
10 10 timeout 30
11 11  
12 12 # listen on a Unix domain socket and/or a TCP port,
... ...
doc/api/README.md
... ... @@ -30,8 +30,9 @@ When listing resources you can pass the following parameters:
30 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   -
... ...
doc/api/repositories.md 0 → 100644
... ... @@ -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.
... ...
doc/api/session.md 0 → 100644
... ... @@ -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 &lt; 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
... ... @@ -17,6 +17,6 @@ module Gitlab
17 17 mount Projects
18 18 mount Issues
19 19 mount Milestones
20   - mount Keys
  20 + mount Session
21 21 end
22 22 end
... ...
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
... ... @@ -61,7 +61,7 @@ module Gitlab
61 61 error!({'message' => message}, status)
62 62 end
63 63  
64   - private
  64 + private
65 65  
66 66 def abilities
67 67 @abilities ||= begin
... ...
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
... ...
lib/api/session.rb 0 → 100644
... ... @@ -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] =&gt; :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 &#39;active_record/fixtures&#39;
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
1 1 namespace :gitlab do
2 2 namespace :app do
3 3 desc "GITLAB | Enable auto merge"
4   - task :enable_automerge => :environment do
  4 + task :enable_automerge => :environment do
5 5 Gitlab::Gitolite.new.enable_automerge
6 6  
7 7 Project.find_each do |project|
... ...
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
... ... @@ -4,8 +4,7 @@ namespace :gitlab do
4 4 task :setup => [
5 5 'db:setup',
6 6 'db:seed_fu',
7   - 'gitlab:app:enable_automerge'
  7 + 'gitlab:app:enable_automerge'
8 8 ]
9 9 end
10 10 end
11   -
... ...
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 &#39;spec_helper&#39;
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)
... ...
spec/requests/api/session_spec.rb 0 → 100644
... ... @@ -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 &#39;spec_helper&#39;
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
... ...