Commit 8045a81bcf5822f1992442750e1484a93c368229

Authored by Sebastian Ziebell
2 parents 5d8a99f1 2f0a75ab

Merge branch 'master' into fixes/api

Showing 262 changed files with 1808 additions and 1431 deletions   Show diff stats
CHANGELOG
  1 +v 5.0.0
  2 + - replaced gitolite with gitlab-shell
  3 +
1 4 v 4.2.0
2 5 - User show page. Via /u/username
3 6 - Show help contents on pages for better navigation
... ...
Gemfile
... ... @@ -32,9 +32,6 @@ gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
32 32 # Dump db to yml file. Mostly used to migrate from sqlite to mysql
33 33 gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db"
34 34  
35   -# Gitolite client (for work with gitolite-admin repo)
36   -gem "gitolite", '1.1.0'
37   -
38 35 # Syntax highlighter
39 36 gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", branch: "master"
40 37  
... ... @@ -165,5 +162,5 @@ group :test do
165 162 end
166 163  
167 164 group :production do
168   - gem "gitlab_meta", '4.0'
  165 + gem "gitlab_meta", '5.0'
169 166 end
... ...
Gemfile.lock
... ... @@ -107,7 +107,6 @@ GEM
107 107 coderay (>= 1.0.0)
108 108 erubis (>= 2.7.0)
109 109 binding_of_caller (0.6.8)
110   - blankslate (3.1.2)
111 110 bootstrap-sass (2.2.1.1)
112 111 sass (~> 3.2)
113 112 builder (3.0.4)
... ... @@ -188,17 +187,13 @@ GEM
188 187 mime-types (~> 1.19)
189 188 pygments.rb (>= 0.2.13)
190 189 github-markup (0.7.4)
191   - gitlab_meta (4.0)
  190 + gitlab_meta (5.0)
192 191 gitlab_omniauth-ldap (1.0.2)
193 192 net-ldap (~> 0.2.2)
194 193 omniauth (~> 1.0)
195 194 pyu-ruby-sasl (~> 0.0.3.1)
196 195 rubyntlm (~> 0.1.1)
197 196 gitlab_yaml_db (1.0.0)
198   - gitolite (1.1.0)
199   - gratr19 (~> 0.4.4.1)
200   - grit (~> 2.5.0)
201   - hashery (~> 1.5.0)
202 197 grape (0.2.2)
203 198 activesupport
204 199 hashie (~> 1.2)
... ... @@ -208,7 +203,6 @@ GEM
208 203 rack-accept
209 204 rack-mount
210 205 virtus
211   - gratr19 (0.4.4.1)
212 206 growl (1.0.3)
213 207 guard (1.5.4)
214 208 listen (>= 0.4.2)
... ... @@ -227,8 +221,6 @@ GEM
227 221 activesupport (>= 3.1, < 4.1)
228 222 haml (~> 3.1)
229 223 railties (>= 3.1, < 4.1)
230   - hashery (1.5.0)
231   - blankslate
232 224 hashie (1.2.0)
233 225 hike (1.2.1)
234 226 http_parser.rb (0.5.3)
... ... @@ -494,10 +486,9 @@ DEPENDENCIES
494 486 git
495 487 github-linguist (~> 2.3.4)
496 488 github-markup (~> 0.7.4)
497   - gitlab_meta (= 4.0)
  489 + gitlab_meta (= 5.0)
498 490 gitlab_omniauth-ldap (= 1.0.2)
499 491 gitlab_yaml_db (= 1.0.0)
500   - gitolite (= 1.1.0)
501 492 grack!
502 493 grape (~> 0.2.1)
503 494 grit!
... ...
ROADMAP.md
1 1 ## GitLab Roadmap
2 2  
3   -### v4.3 March 22
  3 +### v5.0 March 22
4 4  
5   -* Jenkins CI integration service
  5 +* Replace gitolite with gitlab-shell
6 6 * Usability improvements
7 7 * Notification improvements
8 8  
9 9 ### v4.2 February 22
10 10  
11   -* Campfire integration service
12 11 * Teams
13 12  
... ...
VERSION
1   -4.2.0pre
  1 +5.0.0pre
... ...
app/assets/images/home_icon.PNG

596 Bytes

app/assets/images/icon-attachment.png

450 Bytes

app/assets/javascripts/notes.js
... ... @@ -20,12 +20,12 @@ var NoteList = {
20 20  
21 21 if(NoteList.reversed) {
22 22 var form = $(".js-main-target-form");
23   - form.find(".buttons, .note_options").hide();
  23 + form.find(".note-form-actions").hide();
24 24 var textarea = form.find(".js-note-text");
25 25 textarea.css("height", "40px");
26 26 textarea.on("focus", function(){
27 27 textarea.css("height", "80px");
28   - form.find(".buttons, .note_options").show();
  28 + form.find(".note-form-actions").show();
29 29 });
30 30 }
31 31  
... ...
app/assets/stylesheets/common.scss
... ... @@ -338,10 +338,6 @@ li.note {
338 338 li {
339 339 border-bottom:none !important;
340 340 }
341   - .attachment {
342   - padding-left: 20px;
343   - background:url("icon-attachment.png") no-repeat left center;
344   - }
345 341 }
346 342 }
347 343  
... ...
app/assets/stylesheets/gitlab_bootstrap.scss
... ... @@ -17,6 +17,8 @@ $baseLineHeight: 18px !default;
17 17 @import "gitlab_bootstrap/variables.scss";
18 18 @import "gitlab_bootstrap/fonts.scss";
19 19 @import "gitlab_bootstrap/mixins.scss";
  20 +@import "gitlab_bootstrap/avatar.scss";
  21 +@import "gitlab_bootstrap/nav.scss";
20 22 @import "gitlab_bootstrap/common.scss";
21 23 @import "gitlab_bootstrap/typography.scss";
22 24 @import "gitlab_bootstrap/buttons.scss";
... ...
app/assets/stylesheets/gitlab_bootstrap/avatar.scss 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +/** AVATARS **/
  2 +img.avatar { float: left; margin-right: 12px; width: 40px; border: 1px solid #ddd; padding: 1px; }
  3 +img.avatar.s16 { width: 16px; height: 16px; margin-right: 6px; }
  4 +img.avatar.s24 { width: 24px; height: 24px; margin-right: 8px; }
  5 +img.avatar.s32 { width: 32px; height: 32px; margin-right: 10px; }
  6 +img.avatar.s90 { width: 90px; height: 90px; margin-right: 15px; }
  7 +img.lil_av { padding-left: 4px; padding-right: 3px; }
  8 +img.small { width: 80px; }
... ...
app/assets/stylesheets/gitlab_bootstrap/blocks.scss
... ... @@ -95,7 +95,11 @@
95 95  
96 96 form {
97 97 margin-bottom: 0;
98   - margin-top: 3px;
  98 + margin-top: 0;
  99 + }
  100 +
  101 + .btn-tiny {
  102 + @include box-shadow(0 0px 0px 1px #f1f1f1);
99 103 }
100 104  
101 105 .nav-pills {
... ...
app/assets/stylesheets/gitlab_bootstrap/buttons.scss
1 1 .btn {
2   - @include linear-gradient(#f7f7f7, #d5d5d5);
  2 + @include linear-gradient(#f1f1f1, #e1e1e1);
  3 + text-shadow: 0 1px 1px #FFF;
3 4 border-color: #BBB;
  5 +
4 6 &:hover {
5   - @include bg-gray-gradient;
6   - border-color: #bbb;
  7 + background: #f1f1f1;
  8 + @include linear-gradient(#fAfAfA, #f1f1f1);
  9 + border-color: #AAA;
7 10 color: #333;
8 11 }
9 12  
10   - &.btn-white {
11   - background: #FFF;
12   - }
13   -
14   - &.primary {
  13 + &.btn-primary {
15 14 background: #2a79A3;
16 15 @include linear-gradient(#47A7b7, #2585b5);
17 16 border-color: #2A79A3;
... ... @@ -58,21 +57,18 @@
58 57 }
59 58 }
60 59  
61   - &.save-btn {
  60 + &.btn-create {
62 61 @extend .wide;
63   - @extend .primary;
  62 + @extend .success;
64 63 }
65 64  
66   - &.cancel-btn {
67   - float: right;
68   - }
69   -
70   - &.wide {
71   - padding-left: 30px;
72   - padding-right: 30px;
  65 + &.btn-save {
  66 + @extend .wide;
  67 + @extend .btn-primary;
73 68 }
74 69  
75   - &.danger {
  70 + &.btn-close,
  71 + &.btn-remove {
76 72 @extend .btn-danger;
77 73 border-color: #BD362F;
78 74  
... ... @@ -82,8 +78,13 @@
82 78 }
83 79 }
84 80  
85   - &.danger {
86   - @extend .btn-danger;
  81 + &.btn-cancel {
  82 + float: right;
  83 + }
  84 +
  85 + &.wide {
  86 + padding-left: 20px;
  87 + padding-right: 20px;
87 88 }
88 89  
89 90 &.small {
... ... @@ -95,7 +96,7 @@
95 96 background-color: #ccc;
96 97 }
97 98  
98   - &.very_small {
  99 + &.btn-tiny {
99 100 font-size: 11px;
100 101 padding: 2px 6px;
101 102 line-height: 16px;
... ...
app/assets/stylesheets/gitlab_bootstrap/common.scss
... ... @@ -9,7 +9,6 @@
9 9  
10 10 /** COMMON CLASSES **/
11 11 .left { float:left }
12   -.right { float:right!important }
13 12 .append-bottom-10 { margin-bottom:10px }
14 13 .append-bottom-20 { margin-bottom:20px }
15 14 .prepend-top-10 { margin-top:10px }
... ... @@ -22,82 +21,13 @@
22 21 .light { color: #888 }
23 22 .tiny { font-weight: normal }
24 23  
25   -/** PILLS & TABS**/
26   -.nav-pills {
27   - .active a {
28   - background: $primary_color;
29   - }
30   -
31   - > li > a {
32   - @include border-radius(0);
33   - }
34   - &.nav-stacked {
35   - > li > a {
36   - border-left: 4px solid #EEE;
37   - padding: 12px;
38   - }
39   - > .active > a {
40   - border-color: #29B;
41   - border-radius: 0;
42   - background: #F1F1F1;
43   - color: $style_color;
44   - font-weight: bold;
45   - }
46   - }
47   -}
48   -
49   -.nav-pills > .active > a > i[class^="icon-"] { background: inherit; }
50   -
51   -
52   -
53   -/**
54   - * nav-tabs
55   - *
56   - */
57   -.nav-tabs > li > a, .nav-pills > li > a { color: $style_color; }
58   -.nav.nav-tabs {
59   - li {
60   - > a {
61   - padding: 8px 20px;
62   - margin-right: 7px;
63   - line-height: 20px;
64   - border-color: #EEE;
65   - color: #888;
66   - border-bottom: 1px solid #ddd;
67   - .badge {
68   - background-color: #eee;
69   - color: #888;
70   - text-shadow: 0 1px 1px #fff;
71   - }
72   - i[class^="icon-"] {
73   - line-height: 14px;
74   - }
75   - }
76   - &.active {
77   - > a {
78   - border-color: #CCC;
79   - border-bottom: 1px solid #fff;
80   - color: #333;
81   - }
82   - }
83   - }
84   -
85   - &.nav-small-tabs > li > a { padding: 6px 9px; }
86   -}
87 24  
88 25 /** ALERT MESSAGES **/
89   -.alert-message { @extend .alert; }
90   -.alert-messag.success { @extend .alert-success; }
91   -.alert-message.error { @extend .alert-error; }
92   -
93   -/** AVATARS **/
94   -img.avatar { float: left; margin-right: 12px; width: 40px; border: 1px solid #ddd; padding: 1px; }
95   -img.avatar.s16 { width: 16px; height: 16px; margin-right: 6px; }
96   -img.avatar.s24 { width: 24px; height: 24px; margin-right: 8px; }
97   -img.avatar.s32 { width: 32px; height: 32px; margin-right: 10px; }
98   -img.avatar.s90 { width: 90px; height: 90px; margin-right: 15px; }
99   -img.lil_av { padding-left: 4px; padding-right: 3px; }
100   -img.small { width: 80px; }
  26 +.alert.alert-disabled {
  27 + background: #EEE;
  28 + color: #777;
  29 + border-color: #DDD;
  30 +}
101 31  
102 32 /** HELPERS **/
103 33 .nothing_here_message {
... ...
app/assets/stylesheets/gitlab_bootstrap/nav.scss 0 → 100644
... ... @@ -0,0 +1,65 @@
  1 +/**
  2 + * nav-pills
  3 + *
  4 + */
  5 +.nav-pills {
  6 + .active a {
  7 + background: $primary_color;
  8 + }
  9 +
  10 + > li > a {
  11 + @include border-radius(0);
  12 + }
  13 + &.nav-stacked {
  14 + > li > a {
  15 + border-left: 4px solid #EEE;
  16 + padding: 12px;
  17 + }
  18 + > .active > a {
  19 + border-color: #29B;
  20 + border-radius: 0;
  21 + background: #F1F1F1;
  22 + color: $style_color;
  23 + font-weight: bold;
  24 + }
  25 + }
  26 +}
  27 +
  28 +.nav-pills > .active > a > i[class^="icon-"] { background: inherit; }
  29 +
  30 +
  31 +
  32 +/**
  33 + * nav-tabs
  34 + *
  35 + */
  36 +.nav-tabs > li > a, .nav-pills > li > a { color: $style_color; }
  37 +.nav.nav-tabs {
  38 + li {
  39 + > a {
  40 + padding: 8px 20px;
  41 + margin-right: 7px;
  42 + line-height: 20px;
  43 + border-color: #EEE;
  44 + color: #888;
  45 + border-bottom: 1px solid #ddd;
  46 + .badge {
  47 + background-color: #eee;
  48 + color: #888;
  49 + text-shadow: 0 1px 1px #fff;
  50 + }
  51 + i[class^="icon-"] {
  52 + line-height: 14px;
  53 + }
  54 + }
  55 + &.active {
  56 + > a {
  57 + border-color: #CCC;
  58 + border-bottom: 1px solid #fff;
  59 + color: #333;
  60 + }
  61 + }
  62 + }
  63 +
  64 + &.nav-small-tabs > li > a { padding: 6px 9px; }
  65 +}
... ...
app/assets/stylesheets/sections/events.scss
... ... @@ -127,7 +127,7 @@
127 127 .btn-new-mr {
128 128 @extend .btn-info;
129 129 @extend .small;
130   - @extend .right;
  130 + @extend .pull-right;
131 131 margin: -3px;
132 132 }
133 133 }
... ...
app/assets/stylesheets/sections/login.scss
1 1 /* Login Page */
2   -body.login-page{
3   - padding-top: 10%;
4   - background: #f1f1f1;
  2 +body.login-page{
  3 + padding-top: 7%;
  4 + background: #666;
5 5 }
6 6  
7 7 .login-box{
... ...
app/assets/stylesheets/sections/nav.scss
... ... @@ -6,8 +6,7 @@ ul.main_menu {
6 6 margin: auto;
7 7 margin: 30px 0;
8 8 margin-top: 10px;
9   - border-bottom: 1px solid #DDD;
10   - height: 37px;
  9 + height: 38px;
11 10 position: relative;
12 11 overflow: hidden;
13 12 .count {
... ... @@ -33,6 +32,7 @@ ul.main_menu {
33 32 margin: 0;
34 33 display: table-cell;
35 34 width: 1%;
  35 + border-bottom: 2px solid #EEE;
36 36 &.active {
37 37 border-bottom: 2px solid #474D57;
38 38 a {
... ... @@ -42,10 +42,8 @@ ul.main_menu {
42 42  
43 43 &.home {
44 44 a {
45   - background: url(home_icon.PNG) no-repeat center center;
46   - text-indent:-9999px;
47   - min-width: 20px;
48   - img {
  45 + i {
  46 + font-size: 20px;
49 47 position: relative;
50 48 top: 4px;
51 49 }
... ... @@ -56,7 +54,7 @@ ul.main_menu {
56 54 display: block;
57 55 text-align: center;
58 56 font-weight: normal;
59   - height: 35px;
  57 + height: 36px;
60 58 line-height: 36px;
61 59 color: #777;
62 60 text-shadow: 0 1px 1px white;
... ...
app/assets/stylesheets/sections/notes.scss
... ... @@ -81,14 +81,6 @@ ul.notes {
81 81 .attachment {
82 82 font-size: 14px;
83 83 margin-top: -20px;
84   -
85   - .icon-attachment {
86   - @extend .icon-paper-clip;
87   - font-size: 24px;
88   - position: relative;
89   - text-align: right;
90   - top: 6px;
91   - }
92 84 }
93 85 .note-body {
94 86 margin-left: 45px;
... ... @@ -214,9 +206,11 @@ ul.notes {
214 206 * Note Form
215 207 */
216 208  
217   -.comment-btn,
  209 +.comment-btn {
  210 + @extend .btn-create;
  211 +}
218 212 .reply-btn {
219   - @extend .save-btn;
  213 + @extend .btn-primary;
220 214 }
221 215 .file .content tr.line_holder:hover > td { background: $hover !important; }
222 216 .file .content tr.line_holder:hover > td .line_note_link {
... ... @@ -227,11 +221,6 @@ ul.notes {
227 221 .discussion {
228 222 .new_note {
229 223 margin: 8px 5px 8px 0;
230   -
231   - .note_options {
232   - // because of the smaller width and the extra "cancel" button
233   - margin-top: 8px;
234   - }
235 224 }
236 225 }
237 226 .new_note {
... ... @@ -244,37 +233,6 @@ ul.notes {
244 233 .clearfix {
245 234 margin-bottom: 0;
246 235 }
247   - .note_options {
248   - h6 {
249   - @extend .left;
250   - line-height: 20px;
251   - padding-right: 16px;
252   - padding-bottom: 16px;
253   - }
254   - label {
255   - padding: 0;
256   - }
257   -
258   - .attachment {
259   - @extend .right;
260   - position: relative;
261   - width: 350px;
262   - height: 50px;
263   - margin:0 0 5px !important;
264   -
265   - // hide the actual file field
266   - input {
267   - display: none;
268   - }
269   -
270   - .choose-btn {
271   - float: right;
272   - }
273   - }
274   - .notify_options {
275   - @extend .right;
276   - }
277   - }
278 236 .note_text_and_preview {
279 237 // makes the "absolute" position for links relative to this
280 238 position: relative;
... ... @@ -313,3 +271,17 @@ ul.notes {
313 271 @extend .thumbnail;
314 272 margin-left: 45px;
315 273 }
  274 +
  275 +
  276 +.note-form-actions {
  277 + background: #F9F9F9;
  278 + height: 45px;
  279 + padding: 0 5px;
  280 +
  281 + .note-form-option {
  282 + margin-top: 8px;
  283 + margin-left: 15px;
  284 + @extend .pull-left;
  285 + @extend .span4;
  286 + }
  287 +}
... ...
app/assets/stylesheets/sections/projects.scss
... ... @@ -4,7 +4,7 @@
4 4 }
5 5  
6 6 .side {
7   - @extend .right;
  7 + @extend .pull-right;
8 8  
9 9 .projects_box {
10 10 > .title {
... ...
app/controllers/application_controller.rb
... ... @@ -4,16 +4,12 @@ class ApplicationController &lt; ActionController::Base
4 4 before_filter :set_current_user_for_observers
5 5 before_filter :add_abilities
6 6 before_filter :dev_tools if Rails.env == 'development'
  7 + before_filter :default_headers
7 8  
8 9 protect_from_forgery
9 10  
10 11 helper_method :abilities, :can?
11 12  
12   - rescue_from Gitlab::Gitolite::AccessDenied do |exception|
13   - log_exception(exception)
14   - render "errors/gitolite", layout: "errors", status: 500
15   - end
16   -
17 13 rescue_from Encoding::CompatibilityError do |exception|
18 14 log_exception(exception)
19 15 render "errors/encoding", layout: "errors", status: 500
... ... @@ -148,4 +144,8 @@ class ApplicationController &lt; ActionController::Base
148 144 Rack::MiniProfiler.authorize_request
149 145 end
150 146  
  147 + def default_headers
  148 + headers['X-Frame-Options'] = 'DENY'
  149 + headers['X-XSS-Protection'] = '1; mode=block'
  150 + end
151 151 end
... ...
app/controllers/graph_controller.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +class GraphController < ProjectResourceController
  2 + include ExtractsPath
  3 +
  4 + # Authorize
  5 + before_filter :authorize_read_project!
  6 + before_filter :authorize_code_access!
  7 + before_filter :require_non_empty_project
  8 +
  9 + def show
  10 + respond_to do |format|
  11 + format.html
  12 + format.json do
  13 + graph = Gitlab::Graph::JsonBuilder.new(project, @ref)
  14 + render :json => graph.to_json
  15 + end
  16 + end
  17 + end
  18 +end
... ...
app/controllers/groups_controller.rb
... ... @@ -6,6 +6,7 @@ class GroupsController &lt; ApplicationController
6 6  
7 7 # Authorize
8 8 before_filter :authorize_read_group!, except: [:new, :create]
  9 + before_filter :authorize_admin_group!, only: [:edit, :update, :destroy]
9 10 before_filter :authorize_create_group!, only: [:new, :create]
10 11  
11 12 # Load group projects
... ... @@ -84,6 +85,31 @@ class GroupsController &lt; ApplicationController
84 85 redirect_to people_group_path(@group), notice: 'Users was successfully added.'
85 86 end
86 87  
  88 + def edit
  89 + end
  90 +
  91 + def update
  92 + group_params = params[:group].dup
  93 + owner_id =group_params.delete(:owner_id)
  94 +
  95 + if owner_id
  96 + @group.owner = User.find(owner_id)
  97 + end
  98 +
  99 + if @group.update_attributes(group_params)
  100 + redirect_to @group, notice: 'Group was successfully updated.'
  101 + else
  102 + render action: "edit"
  103 + end
  104 + end
  105 +
  106 + def destroy
  107 + @group.truncate_teams
  108 + @group.destroy
  109 +
  110 + redirect_to root_path, notice: 'Group was removed.'
  111 + end
  112 +
87 113 protected
88 114  
89 115 def group
... ... @@ -106,6 +132,14 @@ class GroupsController &lt; ApplicationController
106 132 end
107 133  
108 134 def authorize_create_group!
109   - can?(current_user, :create_group, nil)
  135 + unless can?(current_user, :create_group, nil)
  136 + return render_404
  137 + end
  138 + end
  139 +
  140 + def authorize_admin_group!
  141 + unless can?(current_user, :manage_group, group)
  142 + return render_404
  143 + end
110 144 end
111 145 end
... ...
app/controllers/projects_controller.rb
... ... @@ -90,16 +90,6 @@ class ProjectsController &lt; ProjectResourceController
90 90 end
91 91 end
92 92  
93   - def graph
94   - respond_to do |format|
95   - format.html
96   - format.json do
97   - graph = Gitlab::Graph::JsonBuilder.new(project)
98   - render :json => graph.to_json
99   - end
100   - end
101   - end
102   -
103 93 def destroy
104 94 return access_denied! unless can?(current_user, :remove_project, project)
105 95  
... ...
app/controllers/refs_controller.rb
... ... @@ -13,6 +13,8 @@ class RefsController &lt; ProjectResourceController
13 13 format.html do
14 14 new_path = if params[:destination] == "tree"
15 15 project_tree_path(@project, (@ref + "/" + params[:path]))
  16 + elsif params[:destination] == "graph"
  17 + project_graph_path(@project, @ref)
16 18 else
17 19 project_commits_path(@project, @ref)
18 20 end
... ...
app/helpers/projects_helper.rb
... ... @@ -43,7 +43,7 @@ module ProjectsHelper
43 43 tm = project.team_member_by_id(author)
44 44  
45 45 if tm
46   - link_to author_html, project_team_member_path(project, tm), class: "author_link"
  46 + link_to author_html, project_team_member_path(project, tm.user_username), class: "author_link"
47 47 else
48 48 author_html
49 49 end.html_safe
... ...
app/mailers/notify.rb
... ... @@ -10,6 +10,10 @@ class Notify &lt; ActionMailer::Base
10 10  
11 11 default from: Gitlab.config.gitlab.email_from
12 12  
  13 + # Just send email with 3 seconds delay
  14 + def self.delay
  15 + delay_for(2.seconds)
  16 + end
13 17  
14 18  
15 19 #
... ... @@ -63,12 +67,12 @@ class Notify &lt; ActionMailer::Base
63 67 # Note
64 68 #
65 69  
66   - def note_commit_email(commit_autor_email, note_id)
  70 + def note_commit_email(recipient_id, note_id)
67 71 @note = Note.find(note_id)
68 72 @commit = @note.noteable
69 73 @commit = CommitDecorator.decorate(@commit)
70 74 @project = @note.project
71   - mail(to: commit_autor_email, subject: subject("note for commit #{@commit.short_id}", @commit.title))
  75 + mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
72 76 end
73 77  
74 78 def note_issue_email(recipient_id, note_id)
... ...
app/models/key.rb
... ... @@ -24,8 +24,8 @@ class Key &lt; ActiveRecord::Base
24 24 before_save :set_identifier
25 25  
26 26 validates :title, presence: true, length: { within: 0..255 }
27   - validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }
28   - validate :unique_key, :fingerprintable_key
  27 + validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }, uniqueness: true
  28 + validate :fingerprintable_key
29 29  
30 30 delegate :name, :email, to: :user, prefix: true
31 31  
... ... @@ -33,14 +33,6 @@ class Key &lt; ActiveRecord::Base
33 33 self.key = self.key.strip unless self.key.blank?
34 34 end
35 35  
36   - def unique_key
37   - query = Key.where(key: key)
38   - query = query.where('(project_id IS NULL OR project_id = ?)', project_id) if project_id
39   - if (query.count > 0)
40   - errors.add :key, 'already exist.'
41   - end
42   - end
43   -
44 36 def fingerprintable_key
45 37 return true unless key # Don't test if there is no key.
46 38 # `ssh-keygen -lf /dev/stdin <<< "#{key}"` errors with: redirection unexpected
... ... @@ -65,7 +57,7 @@ class Key &lt; ActiveRecord::Base
65 57 end
66 58  
67 59 def is_deploy_key
68   - true if project_id
  60 + !!project_id
69 61 end
70 62  
71 63 # projects that has this key
... ... @@ -77,7 +69,7 @@ class Key &lt; ActiveRecord::Base
77 69 end
78 70 end
79 71  
80   - def last_deploy?
81   - Key.where(identifier: identifier).count == 0
  72 + def shell_id
  73 + "key-#{self.id}"
82 74 end
83 75 end
... ...
app/models/namespace.rb
... ... @@ -27,7 +27,6 @@ class Namespace &lt; ActiveRecord::Base
27 27  
28 28 after_create :ensure_dir_exist
29 29 after_update :move_dir
30   - after_commit :update_gitolite, on: :update, if: :require_update_gitolite
31 30 after_destroy :rm_dir
32 31  
33 32 scope :root, where('type IS NULL')
... ... @@ -89,11 +88,6 @@ class Namespace &lt; ActiveRecord::Base
89 88 end
90 89 end
91 90  
92   - def update_gitolite
93   - @require_update_gitolite = false
94   - projects.each(&:update_repository)
95   - end
96   -
97 91 def rm_dir
98 92 dir_path = File.join(Gitlab.config.gitolite.repos_path, path)
99 93 FileUtils.rm_r( dir_path, force: true )
... ...
app/models/project.rb
... ... @@ -8,7 +8,6 @@
8 8 # description :text
9 9 # created_at :datetime not null
10 10 # updated_at :datetime not null
11   -# private_flag :boolean default(TRUE), not null
12 11 # creator_id :integer
13 12 # default_branch :string(255)
14 13 # issues_enabled :boolean default(TRUE), not null
... ... @@ -16,6 +15,7 @@
16 15 # merge_requests_enabled :boolean default(TRUE), not null
17 16 # wiki_enabled :boolean default(TRUE), not null
18 17 # namespace_id :integer
  18 +# public :boolean default(FALSE), not null
19 19 #
20 20  
21 21 require "grit"
... ... @@ -262,8 +262,6 @@ class Project &lt; ActiveRecord::Base
262 262  
263 263 Gitlab::ProjectMover.new(self, old_dir, new_dir).execute
264 264  
265   - gitolite.move_repository(old_repo, self)
266   -
267 265 save!
268 266 end
269 267 rescue Gitlab::ProjectMover::ProjectMoveError => ex
... ... @@ -459,20 +457,6 @@ class Project &lt; ActiveRecord::Base
459 457 namespace.try(:path) || ''
460 458 end
461 459  
462   - def update_repository
463   - GitoliteWorker.perform_async(
464   - :update_repository,
465   - self.id
466   - )
467   - end
468   -
469   - def destroy_repository
470   - GitoliteWorker.perform_async(
471   - :remove_repository,
472   - self.path_with_namespace
473   - )
474   - end
475   -
476 460 def repo_exists?
477 461 @repo_exists ||= (repository && repository.branches.present?)
478 462 rescue
... ...
app/models/project_team.rb
... ... @@ -112,7 +112,6 @@ class ProjectTeam
112 112 source_team.each do |tm|
113 113 tm.save
114 114 end
115   - target_project.update_repository
116 115 end
117 116  
118 117 true
... ...
app/models/protected_branch.rb
... ... @@ -18,13 +18,6 @@ class ProtectedBranch &lt; ActiveRecord::Base
18 18 validates :name, presence: true
19 19 validates :project, presence: true
20 20  
21   - after_save :update_repository
22   - after_destroy :update_repository
23   -
24   - def update_repository
25   - project.update_repository
26   - end
27   -
28 21 def commit
29 22 project.repository.commit(self.name)
30 23 end
... ...
app/models/user.rb
... ... @@ -31,6 +31,8 @@
31 31 # extern_uid :string(255)
32 32 # provider :string(255)
33 33 # username :string(255)
  34 +# can_create_group :boolean default(TRUE), not null
  35 +# can_create_team :boolean default(TRUE), not null
34 36 #
35 37  
36 38 class User < ActiveRecord::Base
... ...
app/models/user_team.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_teams
  4 +#
  5 +# id :integer not null, primary key
  6 +# name :string(255)
  7 +# path :string(255)
  8 +# owner_id :integer
  9 +# created_at :datetime not null
  10 +# updated_at :datetime not null
  11 +#
  12 +
1 13 class UserTeam < ActiveRecord::Base
2 14 attr_accessible :name, :owner_id, :path
3 15  
... ...
app/models/user_team_project_relationship.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_team_project_relationships
  4 +#
  5 +# id :integer not null, primary key
  6 +# project_id :integer
  7 +# user_team_id :integer
  8 +# greatest_access :integer
  9 +# created_at :datetime not null
  10 +# updated_at :datetime not null
  11 +#
  12 +
1 13 class UserTeamProjectRelationship < ActiveRecord::Base
2 14 attr_accessible :greatest_access, :project_id, :user_team_id
3 15  
... ...
app/models/user_team_user_relationship.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_team_user_relationships
  4 +#
  5 +# id :integer not null, primary key
  6 +# user_id :integer
  7 +# user_team_id :integer
  8 +# group_admin :boolean
  9 +# permission :integer
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +#
  13 +
1 14 class UserTeamUserRelationship < ActiveRecord::Base
2 15 attr_accessible :group_admin, :permission, :user_id, :user_team_id
3 16  
... ...
app/models/users_project.rb
... ... @@ -25,15 +25,12 @@ class UsersProject &lt; ActiveRecord::Base
25 25  
26 26 attr_accessor :skip_git
27 27  
28   - after_save :update_repository, unless: :skip_git?
29   - after_destroy :update_repository, unless: :skip_git?
30   -
31 28 validates :user, presence: true
32 29 validates :user_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
33 30 validates :project_access, inclusion: { in: [GUEST, REPORTER, DEVELOPER, MASTER] }, presence: true
34 31 validates :project, presence: true
35 32  
36   - delegate :name, :email, to: :user, prefix: true
  33 + delegate :name, :username, :email, to: :user, prefix: true
37 34  
38 35 scope :guests, where(project_access: GUEST)
39 36 scope :reporters, where(project_access: REPORTER)
... ... @@ -84,11 +81,6 @@ class UsersProject &lt; ActiveRecord::Base
84 81 end
85 82 end
86 83  
87   - GitoliteWorker.perform_async(
88   - :update_repositories,
89   - project_ids
90   - )
91   -
92 84 true
93 85 rescue
94 86 false
... ... @@ -103,11 +95,6 @@ class UsersProject &lt; ActiveRecord::Base
103 95 end
104 96 end
105 97  
106   - GitoliteWorker.perform_async(
107   - :update_repositories,
108   - project_ids
109   - )
110   -
111 98 true
112 99 rescue
113 100 false
... ... @@ -136,10 +123,6 @@ class UsersProject &lt; ActiveRecord::Base
136 123 end
137 124 end
138 125  
139   - def update_repository
140   - project.update_repository
141   - end
142   -
143 126 def project_access_human
144 127 Project.access_options.key(self.project_access)
145 128 end
... ...
app/observers/key_observer.rb
... ... @@ -3,20 +3,17 @@ class KeyObserver &lt; ActiveRecord::Observer
3 3  
4 4 def after_save(key)
5 5 GitoliteWorker.perform_async(
6   - :set_key,
7   - key.identifier,
8   - key.key,
9   - key.projects.map(&:id)
  6 + :add_key,
  7 + key.shell_id,
  8 + key.key
10 9 )
11 10 end
12 11  
13 12 def after_destroy(key)
14   - return if key.is_deploy_key && !key.last_deploy?
15   -
16 13 GitoliteWorker.perform_async(
17 14 :remove_key,
18   - key.identifier,
19   - key.projects.map(&:id)
  15 + key.shell_id,
  16 + key.key,
20 17 )
21 18 end
22 19 end
... ...
app/observers/note_observer.rb
... ... @@ -11,7 +11,9 @@ class NoteObserver &lt; ActiveRecord::Observer
11 11 notify_team(note)
12 12 elsif note.notify_author
13 13 # Notify only author of resource
14   - Notify.delay.note_commit_email(note.noteable.author_email, note.id)
  14 + if note.commit_author
  15 + Notify.delay.note_commit_email(note.commit_author.id, note.id)
  16 + end
15 17 else
16 18 # Otherwise ignore it
17 19 nil
... ...
app/observers/project_observer.rb
1 1 class ProjectObserver < ActiveRecord::Observer
2 2 def after_create(project)
3   - project.update_repository
  3 + GitoliteWorker.perform_async(
  4 + :add_repository,
  5 + project.path_with_namespace
  6 + )
  7 +
  8 + log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
4 9 end
5 10  
6 11 def after_update(project)
... ... @@ -8,14 +13,14 @@ class ProjectObserver &lt; ActiveRecord::Observer
8 13 end
9 14  
10 15 def after_destroy(project)
11   - log_info("Project \"#{project.name}\" was removed")
  16 + GitoliteWorker.perform_async(
  17 + :remove_repository,
  18 + project.path_with_namespace
  19 + )
12 20  
13 21 project.satellite.destroy
14   - project.destroy_repository
15   - end
16 22  
17   - def after_create project
18   - log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
  23 + log_info("Project \"#{project.name}\" was removed")
19 24 end
20 25  
21 26 protected
... ...
app/views/admin/dashboard/index.html.haml
... ... @@ -6,7 +6,7 @@
6 6 = link_to admin_projects_path do
7 7 %h1= Project.count
8 8 %hr
9   - = link_to 'New Project', new_project_path, class: "btn small"
  9 + = link_to 'New Project', new_project_path, class: "btn btn-small"
10 10 .span4
11 11 .ui-box
12 12 %h5.title Groups
... ... @@ -14,7 +14,7 @@
14 14 = link_to admin_groups_path do
15 15 %h1= Group.count
16 16 %hr
17   - = link_to 'New Group', new_admin_group_path, class: "btn small"
  17 + = link_to 'New Group', new_admin_group_path, class: "btn btn-small"
18 18 .span4
19 19 .ui-box
20 20 %h5.title Users
... ... @@ -22,7 +22,7 @@
22 22 = link_to admin_users_path do
23 23 %h1= User.count
24 24 %hr
25   - = link_to 'New User', new_admin_user_path, class: "btn small"
  25 + = link_to 'New User', new_admin_user_path, class: "btn btn-small"
26 26  
27 27 .row
28 28 .span4
... ... @@ -31,7 +31,7 @@
31 31 - @projects.each do |project|
32 32 %p
33 33 = link_to project.name_with_namespace, [:admin, project]
34   - %span.light.right
  34 + %span.light.pull-right
35 35 = time_ago_in_words project.created_at
36 36 ago
37 37  
... ... @@ -42,7 +42,7 @@
42 42 %p
43 43 = link_to [:admin, user] do
44 44 = user.name
45   - %span.light.right
  45 + %span.light.pull-right
46 46 = time_ago_in_words user.created_at
47 47 ago
48 48  
... ... @@ -51,25 +51,25 @@
51 51 %hr
52 52 %p
53 53 Issues
54   - %span.light.right
  54 + %span.light.pull-right
55 55 = Issue.count
56 56 %p
57 57 Merge Requests
58   - %span.light.right
  58 + %span.light.pull-right
59 59 = MergeRequest.count
60 60 %p
61 61 Notes
62   - %span.light.right
  62 + %span.light.pull-right
63 63 = Note.count
64 64 %p
65 65 Snippets
66   - %span.light.right
  66 + %span.light.pull-right
67 67 = Snippet.count
68 68 %p
69 69 SSH Keys
70   - %span.light.right
  70 + %span.light.pull-right
71 71 = Key.count
72 72 %p
73 73 Milestones
74   - %span.light.right
  74 + %span.light.pull-right
75 75 = Milestone.count
... ...
app/views/admin/groups/edit.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %hr
3 3 = form_for [:admin, @group] do |f|
4 4 - if @group.errors.any?
5   - .alert-message.block-message.error
  5 + .alert.alert-error
6 6 %span= @group.errors.full_messages.first
7 7 .clearfix.group_name_holder
8 8 = f.label :name do
... ... @@ -24,5 +24,5 @@
24 24 %li It will change the git path to repositories under this group.
25 25  
26 26 .form-actions
27   - = f.submit 'Rename group', class: "btn danger"
28   - = link_to 'Cancel', admin_groups_path, class: "btn cancel-btn"
  27 + = f.submit 'Rename group', class: "btn btn-remove"
  28 + = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
... ...
app/views/admin/groups/index.html.haml
... ... @@ -4,11 +4,11 @@
4 4 allows you to keep projects organized.
5 5 Use groups for uniting related projects.
6 6  
7   - = link_to 'New Group', new_admin_group_path, class: "btn small right"
  7 + = link_to 'New Group', new_admin_group_path, class: "btn btn-small pull-right"
8 8 %br
9 9 = form_tag admin_groups_path, method: :get, class: 'form-inline' do
10 10 = text_field_tag :name, params[:name], class: "xlarge"
11   - = submit_tag "Search", class: "btn submit primary"
  11 + = submit_tag "Search", class: "btn submit btn-primary"
12 12  
13 13 %table
14 14 %thead
... ... @@ -30,6 +30,6 @@
30 30 %td
31 31 = link_to group.owner_name, admin_user_path(group.owner_id)
32 32 %td.bgred
33   - = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn small"
34   - = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn small danger"
  33 + = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
  34 + = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
35 35 = paginate @groups, theme: "admin"
... ...
app/views/admin/groups/new.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %hr
3 3 = form_for [:admin, @group] do |f|
4 4 - if @group.errors.any?
5   - .alert-message.block-message.error
  5 + .alert.alert-error
6 6 %span= @group.errors.full_messages.first
7 7 .clearfix
8 8 = f.label :name do
... ... @@ -10,7 +10,7 @@
10 10 .input
11 11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
12 12 &nbsp;
13   - = f.submit 'Create group', class: "btn primary"
  13 + = f.submit 'Create group', class: "btn btn-primary"
14 14 %hr
15 15 .padded
16 16 %ul
... ...
app/views/admin/groups/show.html.haml
... ... @@ -14,7 +14,7 @@
14 14 %td
15 15 = @group.name
16 16 &nbsp;
17   - = link_to edit_admin_group_path(@group), class: "btn btn-small right" do
  17 + = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do
18 18 %i.icon-edit
19 19 Rename
20 20 %tr
... ... @@ -29,7 +29,7 @@
29 29 Owner:
30 30 %td
31 31 = @group.owner_name
32   - .right
  32 + .pull-right
33 33 = link_to "#", class: "btn btn-small change-owner-link" do
34 34 %i.icon-edit
35 35 Change owner
... ... @@ -42,7 +42,7 @@
42 42 = form_for [:admin, @group] do |f|
43 43 = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
44 44 %div
45   - = f.submit 'Change Owner', class: "btn danger"
  45 + = f.submit 'Change Owner', class: "btn btn-remove"
46 46 = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
47 47  
48 48 - if @group.projects.any?
... ... @@ -63,7 +63,7 @@
63 63 %span.monospace= project.path_with_namespace + ".git"
64 64 %td= project.users.count
65 65 %td.bgred
66   - = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn danger small"
  66 + = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn btn-remove small"
67 67  
68 68 = form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do
69 69 %table.zebra-striped
... ... @@ -88,7 +88,7 @@
88 88 %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
89 89  
90 90 %tr
91   - %td= submit_tag 'Add user to projects in group', class: "btn primary"
  91 + %td= submit_tag 'Add user to projects in group', class: "btn btn-primary"
92 92 %td
93 93 Read more about project permissions
94 94 %strong= link_to "here", help_permissions_path, class: "vlink"
... ... @@ -110,7 +110,7 @@
110 110 .input
111 111 = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
112 112 .form-actions
113   - = submit_tag 'Add', class: "btn primary"
  113 + = submit_tag 'Add', class: "btn btn-primary"
114 114  
115 115 :javascript
116 116 $(function(){
... ...
app/views/admin/hooks/index.html.haml
... ... @@ -7,7 +7,7 @@
7 7  
8 8 = form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f|
9 9 -if @hook.errors.any?
10   - .alert-message.block-message.error
  10 + .alert.alert-error
11 11 - @hook.errors.full_messages.each do |msg|
12 12 %p= msg
13 13 .clearfix
... ... @@ -15,7 +15,7 @@
15 15 .input
16 16 = f.text_field :url, class: "text_field xxlarge"
17 17 &nbsp;
18   - = f.submit "Add System Hook", class: "btn primary"
  18 + = f.submit "Add System Hook", class: "btn btn-primary"
19 19 %hr
20 20  
21 21 -if @hooks.any?
... ... @@ -33,7 +33,7 @@
33 33 %td
34 34 = link_to admin_hook_path(hook) do
35 35 %strong= hook.url
36   - = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn small right"
  36 + = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small pull-right"
37 37 %td POST
38 38 %td
39   - = link_to 'Remove', admin_hook_path(hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small right"
  39 + = link_to 'Remove', admin_hook_path(hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small pull-right"
... ...
app/views/admin/logs/show.html.haml
... ... @@ -15,7 +15,7 @@
15 15 .file_title
16 16 %i.icon-file
17 17 githost.log
18   - .right
  18 + .pull-right
19 19 = link_to '#', class: 'log-bottom' do
20 20 %i.icon-arrow-down
21 21 Scroll down
... ... @@ -29,7 +29,7 @@
29 29 .file_title
30 30 %i.icon-file
31 31 application.log
32   - .right
  32 + .pull-right
33 33 = link_to '#', class: 'log-bottom' do
34 34 %i.icon-arrow-down
35 35 Scroll down
... ... @@ -43,7 +43,7 @@
43 43 .file_title
44 44 %i.icon-file
45 45 production.log
46   - .right
  46 + .pull-right
47 47 = link_to '#', class: 'log-bottom' do
48 48 %i.icon-arrow-down
49 49 Scroll down
... ... @@ -57,7 +57,7 @@
57 57 .file_title
58 58 %i.icon-file
59 59 sidekiq.log
60   - .right
  60 + .pull-right
61 61 = link_to '#', class: 'log-bottom' do
62 62 %i.icon-arrow-down
63 63 Scroll down
... ...
app/views/admin/projects/_form.html.haml
1 1 = form_for [:admin, project] do |f|
2 2 -if project.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %ul
5 5 - project.errors.full_messages.each do |msg|
6 6 %li= msg
... ... @@ -65,8 +65,8 @@
65 65  
66 66  
67 67 .actions
68   - = f.submit 'Save Project', class: "btn save-btn"
69   - = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn"
  68 + = f.submit 'Save Project', class: "btn btn-save"
  69 + = link_to 'Cancel', admin_projects_path, class: "btn btn-cancel"
70 70  
71 71  
72 72  
... ...
app/views/admin/projects/index.html.haml
1 1 %h3.page_title
2 2 Projects
3   - = link_to 'New Project', new_project_path, class: "btn small right"
  3 + = link_to 'New Project', new_project_path, class: "btn btn-small pull-right"
4 4  
5 5 %hr
6 6  
... ... @@ -37,7 +37,7 @@
37 37  
38 38  
39 39 .form-actions
40   - = submit_tag "Search", class: "btn submit primary"
  40 + = submit_tag "Search", class: "btn submit btn-primary"
41 41 = link_to "Reset", admin_projects_path, class: "btn"
42 42 .span8
43 43 .ui-box
... ... @@ -51,9 +51,9 @@
51 51 - else
52 52 %i.icon-lock.cgreen
53 53 = link_to project.name_with_namespace, [:admin, project]
54   - .right
55   - = link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn small"
56   - = link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn small danger"
  54 + .pull-right
  55 + = link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
  56 + = link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
57 57 - if @projects.blank?
58 58 %p.nothing_here_message 0 projects matches
59 59 - else
... ...
app/views/admin/projects/members/_form.html.haml
1 1 = form_for @team_member_relation, as: :team_member, url: admin_project_member_path(@project, @member) do |f|
2 2 -if @team_member_relation.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %ul
5 5 - @team_member_relation.errors.full_messages.each do |msg|
6 6 %li= msg
... ... @@ -12,5 +12,5 @@
12 12  
13 13 %br
14 14 .actions
15   - = f.submit 'Save', class: "btn primary"
  15 + = f.submit 'Save', class: "btn btn-primary"
16 16 = link_to 'Cancel', :back, class: "btn"
... ...
app/views/admin/projects/show.html.haml
1 1 %h3.page_title
2 2 Project: #{@project.name_with_namespace}
3   - = link_to edit_admin_project_path(@project), class: "btn right" do
  3 + = link_to edit_admin_project_path(@project), class: "btn pull-right" do
4 4 %i.icon-edit
5 5 Edit
6 6  
... ... @@ -129,8 +129,8 @@
129 129 %td
130 130 = link_to tm.name, admin_user_path(tm)
131 131 %td= @project.project_access_human(tm)
132   - %td= link_to 'Edit Access', edit_admin_project_member_path(@project, tm), class: "btn small"
133   - %td= link_to 'Remove from team', admin_project_member_path(@project, tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
  132 + %td= link_to 'Edit Access', edit_admin_project_member_path(@project, tm), class: "btn btn-small"
  133 + %td= link_to 'Remove from team', admin_project_member_path(@project, tm), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove small"
134 134  
135 135 %br
136 136 %h5 Add new team member
... ... @@ -147,7 +147,7 @@
147 147 %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
148 148  
149 149 %tr
150   - %td= submit_tag 'Add', class: "btn primary"
  150 + %td= submit_tag 'Add', class: "btn btn-primary"
151 151 %td
152 152 Read more about project permissions
153 153 %strong= link_to "here", help_permissions_path, class: "vlink"
... ...
app/views/admin/teams/edit.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %hr
3 3 = form_for @team, url: admin_team_path(@team), method: :put do |f|
4 4 - if @team.errors.any?
5   - .alert-message.block-message.error
  5 + .alert.alert-error
6 6 %span= @team.errors.full_messages.first
7 7 .clearfix.team_name_holder
8 8 = f.label :name do
... ... @@ -19,5 +19,5 @@
19 19 %li It will change web url for access team and team projects.
20 20  
21 21 .form-actions
22   - = f.submit 'Rename team', class: "btn danger"
23   - = link_to 'Cancel', admin_teams_path, class: "btn cancel-btn"
  22 + = f.submit 'Rename team', class: "btn btn-remove"
  23 + = link_to 'Cancel', admin_teams_path, class: "btn btn-cancel"
... ...
app/views/admin/teams/index.html.haml
... ... @@ -3,12 +3,12 @@
3 3 %small
4 4 simple Teams description
5 5  
6   - = link_to 'New Team', new_admin_team_path, class: "btn small right"
  6 + = link_to 'New Team', new_admin_team_path, class: "btn btn-small pull-right"
7 7 %br
8 8  
9 9 = form_tag admin_teams_path, method: :get, class: 'form-inline' do
10 10 = text_field_tag :name, params[:name], class: "xlarge"
11   - = submit_tag "Search", class: "btn submit primary"
  11 + = submit_tag "Search", class: "btn submit btn-primary"
12 12  
13 13 %table
14 14 %thead
... ... @@ -32,7 +32,7 @@
32 32 %td
33 33 = link_to team.owner.name, admin_user_path(team.owner_id)
34 34 %td.bgred
35   - = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn small"
36   - = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn small danger"
  35 + = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
  36 + = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
37 37  
38 38 = paginate @teams, theme: "admin"
... ...
app/views/admin/teams/members/_form.html.haml
1 1 = form_tag admin_team_member_path(@team, @member), method: :put do
2 2 -if @member.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %ul
5 5 - @member.errors.full_messages.each do |msg|
6 6 %li= msg
... ... @@ -16,5 +16,5 @@
16 16  
17 17 %br
18 18 .actions
19   - = submit_tag 'Save', class: "btn primary"
  19 + = submit_tag 'Save', class: "btn btn-primary"
20 20 = link_to 'Cancel', :back, class: "btn"
... ...
app/views/admin/teams/members/new.html.haml
... ... @@ -26,4 +26,4 @@
26 26 %td
27 27 %span= check_box_tag :group_admin
28 28 %span Admin?
29   - %td= submit_tag 'Add', class: "btn primary", id: :add_members_to_team
  29 + %td= submit_tag 'Add', class: "btn btn-primary", id: :add_members_to_team
... ...
app/views/admin/teams/new.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %hr
3 3 = form_for @team, url: admin_teams_path do |f|
4 4 - if @team.errors.any?
5   - .alert-message.block-message.error
  5 + .alert.alert-error
6 6 %span= @team.errors.full_messages.first
7 7 .clearfix
8 8 = f.label :name do
... ... @@ -10,7 +10,7 @@
10 10 .input
11 11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
12 12 &nbsp;
13   - = f.submit 'Create team', class: "btn primary"
  13 + = f.submit 'Create team', class: "btn btn-primary"
14 14 %hr
15 15 .padded
16 16 %ul
... ...
app/views/admin/teams/projects/_form.html.haml
1 1 = form_tag admin_team_project_path(@team, @project), method: :put do
2 2 -if @project.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %ul
5 5 - @project.errors.full_messages.each do |msg|
6 6 %li= msg
... ... @@ -12,5 +12,5 @@
12 12  
13 13 %br
14 14 .actions
15   - = submit_tag 'Save', class: "btn primary"
  15 + = submit_tag 'Save', class: "btn btn-primary"
16 16 = link_to 'Cancel', :back, class: "btn"
... ...
app/views/admin/teams/projects/new.html.haml
... ... @@ -20,4 +20,4 @@
20 20 %tr
21 21 %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
22 22 %td= select_tag :greatest_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
23   - %td= submit_tag 'Add', class: "btn primary", id: :assign_projects_to_team
  23 + %td= submit_tag 'Add', class: "btn btn-primary", id: :assign_projects_to_team
... ...
app/views/admin/teams/show.html.haml
... ... @@ -14,7 +14,7 @@
14 14 %td
15 15 = @team.name
16 16 &nbsp;
17   - = link_to edit_admin_team_path(@team), class: "btn btn-small right" do
  17 + = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do
18 18 %i.icon-edit
19 19 Rename
20 20 %tr
... ... @@ -23,7 +23,7 @@
23 23 Owner:
24 24 %td
25 25 = @team.owner.name
26   - .right
  26 + .pull-right
27 27 = link_to "#", class: "btn btn-small change-owner-link" do
28 28 %i.icon-edit
29 29 Change owner
... ... @@ -36,13 +36,13 @@
36 36 = form_for @team, url: admin_team_path(@team) do |f|
37 37 = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
38 38 %div
39   - = f.submit 'Change Owner', class: "btn danger"
  39 + = f.submit 'Change Owner', class: "btn btn-remove"
40 40 = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
41 41  
42 42 %fieldset
43 43 %legend
44 44 Members (#{@team.members.count})
45   - %span= link_to 'Add members', new_admin_team_member_path(@team), class: "btn success small right", id: :add_members_to_team
  45 + %span= link_to 'Add members', new_admin_team_member_path(@team), class: "btn btn-primary btn-small pull-right", id: :add_members_to_team
46 46 - if @team.members.any?
47 47 %table#members_list
48 48 %thead
... ... @@ -60,14 +60,14 @@
60 60 %td= @team.human_default_projects_access(member)
61 61 %td= @team.admin?(member) ? "Admin" : "Member"
62 62 %td.bgred
63   - = link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn small"
  63 + = link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn btn-small"
64 64 &nbsp;
65   - = link_to 'Remove', admin_team_member_path(@team, member), confirm: 'Remove member from team. Are you sure?', method: :delete, class: "btn danger small", id: "remove_member_#{member.id}"
  65 + = link_to 'Remove', admin_team_member_path(@team, member), confirm: 'Remove member from team. Are you sure?', method: :delete, class: "btn btn-remove btn-small", id: "remove_member_#{member.id}"
66 66  
67 67 %fieldset
68 68 %legend
69 69 Projects (#{@team.projects.count})
70   - %span= link_to 'Add projects', new_admin_team_project_path(@team), class: "btn success small right", id: :assign_projects_to_team
  70 + %span= link_to 'Add projects', new_admin_team_project_path(@team), class: "btn btn-primary btn-small pull-right", id: :assign_projects_to_team
71 71 - if @team.projects.any?
72 72 %table#projects_list
73 73 %thead
... ... @@ -82,9 +82,9 @@
82 82 %td
83 83 %span= @team.human_max_project_access(project)
84 84 %td.bgred
85   - = link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn small"
  85 + = link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn btn-small"
86 86 &nbsp;
87   - = link_to 'Relegate', admin_team_project_path(@team, project), confirm: 'Remove project from team. Are you sure?', method: :delete, class: "btn danger small", id: "relegate_project_#{project.id}"
  87 + = link_to 'Relegate', admin_team_project_path(@team, project), confirm: 'Remove project from team. Are you sure?', method: :delete, class: "btn btn-remove small", id: "relegate_project_#{project.id}"
88 88  
89 89 :javascript
90 90 $(function(){
... ...
app/views/admin/users/_form.html.haml
... ... @@ -63,10 +63,10 @@
63 63 .alert.alert-error
64 64 - if @admin_user.blocked
65 65 %p This user is blocked and is not able to login to GitLab
66   - = link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn small"
  66 + = link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small"
67 67 - else
68 68 %p Blocked users will be removed from all projects &amp; will not be able to login to GitLab.
69   - = link_to 'Block User', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger"
  69 + = link_to 'Block User', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"
70 70 %fieldset
71 71 %legend Profile
72 72 .clearfix
... ... @@ -80,8 +80,8 @@
80 80 .input= f.text_field :twitter
81 81  
82 82 .actions
83   - = f.submit 'Save', class: "btn save-btn"
  83 + = f.submit 'Save', class: "btn btn-save"
84 84 - if @admin_user.new_record?
85   - = link_to 'Cancel', admin_users_path, class: "btn cancel-btn"
  85 + = link_to 'Cancel', admin_users_path, class: "btn btn-cancel"
86 86 - else
87   - = link_to 'Cancel', admin_user_path(@admin_user), class: "btn cancel-btn"
  87 + = link_to 'Cancel', admin_user_path(@admin_user), class: "btn btn-cancel"
... ...
app/views/admin/users/index.html.haml
1 1 %h3.page_title
2 2 Users
3   - = link_to 'New User', new_admin_user_path, class: "btn small right"
  3 + = link_to 'New User', new_admin_user_path, class: "btn btn-small pull-right"
4 4 %br
5 5  
6 6 = form_tag admin_users_path, method: :get, class: 'form-inline' do
7 7 = text_field_tag :name, params[:name], class: "xlarge"
8   - = submit_tag "Search", class: "btn submit primary"
  8 + = submit_tag "Search", class: "btn submit btn-primary"
9 9 %ul.nav.nav-tabs
10 10 %li{class: "#{'active' unless params[:filter]}"}
11 11 = link_to admin_users_path do
... ... @@ -44,15 +44,15 @@
44 44 %td= user.username
45 45 %td= user.email
46 46 %td= user.users_projects.count
47   - %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn small"
  47 + %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-small"
48 48 %td.bgred
49 49 - if user == current_user
50 50 %span.cred It's you!
51 51 - else
52 52 - if user.blocked
53   - = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success"
  53 + = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-small success"
54 54 - else
55   - = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger"
56   - = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger"
  55 + = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"
  56 + = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove"
57 57  
58 58 = paginate @admin_users, theme: "admin"
... ...
app/views/admin/users/show.html.haml
... ... @@ -4,7 +4,7 @@
4 4 %small Blocked
5 5 - if @admin_user.admin
6 6 %small Administrator
7   - = link_to edit_admin_user_path(@admin_user), class: "btn right" do
  7 + = link_to edit_admin_user_path(@admin_user), class: "btn pull-right" do
8 8 %i.icon-edit
9 9 Edit
10 10  
... ... @@ -86,7 +86,7 @@
86 86 %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3"
87 87  
88 88 %tr
89   - %td= submit_tag 'Add', class: "btn primary"
  89 + %td= submit_tag 'Add', class: "btn btn-primary"
90 90 %td
91 91 Read more about project permissions
92 92 %strong= link_to "here", help_permissions_path, class: "vlink"
... ... @@ -123,5 +123,5 @@
123 123 %tr
124 124 %td= link_to project.name_with_namespace, admin_project_path(project)
125 125 %td= tm.project_access_human
126   - %td= link_to 'Edit Access', edit_admin_project_member_path(project, tm.user), class: "btn small"
127   - %td= link_to 'Remove from team', admin_project_member_path(project, tm.user), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
  126 + %td= link_to 'Edit Access', edit_admin_project_member_path(project, tm.user), class: "btn btn-small"
  127 + %td= link_to 'Remove from team', admin_project_member_path(project, tm.user), confirm: 'Are you sure?', method: :delete, class: "btn btn-small btn-remove"
... ...
app/views/blame/_head.html.haml
... ... @@ -3,5 +3,5 @@
3 3 = render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: params[:path]}
4 4 = nav_link(controller: :refs) do
5 5 = link_to 'Source', project_tree_path(@project, @ref)
6   - %li.right
  6 + %li.pull-right
7 7 = render "shared/clone_panel"
... ...
app/views/commit/huge_commit.html.haml
1 1 = render "commits/commit_box"
2   -.alert-message.block-message.error
  2 +.alert.alert-error
3 3 %h4 Commit diffs are too big to be displayed
... ...
app/views/commit/show.html.haml
1 1 = render "commits/commit_box"
2 2  
3   -%p.right.cgray
  3 +%p.pull-right.cgray
4 4 This commit has
5 5 %span.cgreen #{@commit.stats.additions} additions
6 6 and
... ...
app/views/commits/_commit_box.html.haml
1 1 .ui-box.ui-box-show
2 2 .ui-box-head
3   - .right
  3 + .pull-right
4 4 - if @notes_count > 0
5 5 %span.btn.disabled.grouped
6 6 %i.icon-comment
... ... @@ -13,7 +13,7 @@
13 13 %ul.dropdown-menu
14 14 %li= link_to "Email Patches", project_commit_path(@project, @commit, format: :patch)
15 15 %li= link_to "Plain Diff", project_commit_path(@project, @commit, format: :diff)
16   - = link_to project_tree_path(@project, @commit), class: "btn primary grouped" do
  16 + = link_to project_tree_path(@project, @commit), class: "btn btn-primary grouped" do
17 17 %span Browse Code »
18 18 %h3.commit-title.page_title
19 19 = gfm escape_once(@commit.title)
... ...
app/views/commits/_diffs.html.haml
1 1 - if @suppress_diff
2   - .alert-message.block-message
  2 + .alert.alert-block
3 3 %p
4   - %strong Warning! Large commit with more then #{Commit::DIFF_SAFE_SIZE} files changed.
  4 + %strong Warning! Large commit with more than #{Commit::DIFF_SAFE_SIZE} files changed.
5 5 %p To prevent performance issue we rejected diff information.
6 6 %p
7 7 But if you still want to see diff
... ... @@ -25,7 +25,7 @@
25 25 %span= diff.old_path
26 26  
27 27 - if @commit.prev_commit
28   - = link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn right view-file'} do
  28 + = link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn pull-right view-file'} do
29 29 View file @
30 30 %span.commit-short-id= @commit.short_id(6)
31 31 - else
... ... @@ -33,7 +33,7 @@
33 33 - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
34 34 %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"
35 35  
36   - = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn very_small right view-file'} do
  36 + = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do
37 37 View file @
38 38 %span.commit-short-id= @commit.short_id(6)
39 39  
... ...
app/views/commits/_head.html.haml
... ... @@ -22,7 +22,7 @@
22 22  
23 23  
24 24 - if current_controller?(:commits) && current_user.private_token
25   - %li.right
  25 + %li.pull-right
26 26 %span.rss-icon
27 27 = link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed" do
28 28 = image_tag "rss_ui.png", title: "feed"
... ...
app/views/compare/_form.html.haml
... ... @@ -19,7 +19,7 @@
19 19 = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge"
20 20 .pull-left
21 21 &nbsp;
22   - = submit_tag "Compare", class: "btn primary wide commits-compare-btn"
  22 + = submit_tag "Compare", class: "btn btn-primary wide commits-compare-btn"
23 23 - if @refs_are_same
24 24 .alert
25 25 %span Refs are the same
... ...
app/views/dashboard/_filter.html.haml
... ... @@ -25,9 +25,9 @@
25 25 %li{class: ("active" if params[:project_id] == project.id.to_s)}
26 26 = link_to dashboard_filter_path(entity, project_id: project.id) do
27 27 = project.name_with_namespace
28   - %small.right= entities_per_project(project, entity)
  28 + %small.pull-right= entities_per_project(project, entity)
29 29  
30 30 %fieldset
31 31 %hr
32   - = link_to "Reset", dashboard_filter_path(entity), class: 'btn right'
  32 + = link_to "Reset", dashboard_filter_path(entity), class: 'btn pull-right'
33 33  
... ...
app/views/dashboard/_groups.html.haml
... ... @@ -4,8 +4,8 @@
4 4 %small
5 5 (#{groups.count})
6 6 - if current_user.can_create_group?
7   - %span.right
8   - = link_to new_group_path, class: "btn very_small info" do
  7 + %span.pull-right
  8 + = link_to new_group_path, class: "btn btn-tiny info" do
9 9 %i.icon-plus
10 10 New Group
11 11 %ul.well-list
... ... @@ -13,6 +13,6 @@
13 13 %li
14 14 = link_to group_path(id: group.path), class: dom_class(group) do
15 15 %strong.well-title= truncate(group.name, length: 35)
16   - %span.right.light
  16 + %span.pull-right.light
17 17 - if group.owner == current_user
18 18 %i.icon-wrench
... ...
app/views/dashboard/_projects.html.haml
... ... @@ -4,8 +4,8 @@
4 4 %small
5 5 (#{@projects_count})
6 6 - if current_user.can_create_project?
7   - %span.right
8   - = link_to new_project_path, class: "btn very_small info" do
  7 + %span.pull-right
  8 + = link_to new_project_path, class: "btn btn-tiny info" do
9 9 %i.icon-plus
10 10 New Project
11 11  
... ...
app/views/dashboard/_teams.html.haml
... ... @@ -3,8 +3,8 @@
3 3 Teams
4 4 %small
5 5 (#{@teams.count})
6   - %span.right
7   - = link_to new_team_path, class: "btn very_small info" do
  6 + %span.pull-right
  7 + = link_to new_team_path, class: "btn btn-tiny info" do
8 8 %i.icon-plus
9 9 New Team
10 10 %ul.well-list
... ... @@ -12,7 +12,7 @@
12 12 %li
13 13 = link_to team_path(id: team.path), class: dom_class(team) do
14 14 %strong.well-title= truncate(team.name, length: 35)
15   - %span.right.light
  15 + %span.pull-right.light
16 16 - if team.owner == current_user
17 17 %i.icon-wrench
18 18 - tm = current_user.user_team_user_relationships.find_by_user_team_id(team.id)
... ...
app/views/dashboard/_zero_authorized_projects.html.haml
... ... @@ -6,7 +6,7 @@
6 6 = current_user.projects_limit
7 7 projects. Click on button below to add a new one
8 8 .link_holder
9   - = link_to new_project_path, class: "btn primary" do
  9 + = link_to new_project_path, class: "btn btn-primary" do
10 10 New Project »
11 11 - else
12 12 If you will be added to project - it will be displayed here
... ...
app/views/dashboard/issues.html.haml
1 1 %h3.page_title
2 2 Issues
3 3 %small (assigned to you)
4   - %small.right #{@issues.total_count} issues
  4 + %small.pull-right #{@issues.total_count} issues
5 5  
6 6 %hr
7 7  
... ...
app/views/dashboard/merge_requests.html.haml
1 1 %h3.page_title
2 2 Merge Requests
3 3 %small (authored by or assigned to you)
4   - %small.right #{@merge_requests.total_count} merge requests
  4 + %small.pull-right #{@merge_requests.total_count} merge requests
5 5  
6 6 %hr
7 7 .row
... ...
app/views/dashboard/projects.html.haml
... ... @@ -3,8 +3,8 @@
3 3 %span
4 4 (#{@projects.total_count})
5 5 - if current_user.can_create_project?
6   - %span.right
7   - = link_to new_project_path, class: "btn very_small info" do
  6 + %span.pull-right
  7 + = link_to new_project_path, class: "btn btn-tiny info" do
8 8 %i.icon-plus
9 9 New Project
10 10  
... ... @@ -42,7 +42,7 @@
42 42 %small.light
43 43 %strong Last activity:
44 44 %span= project_last_activity(project)
45   - .right.light
  45 + .pull-right.light
46 46 - if project.owner == current_user
47 47 %i.icon-wrench
48 48 - tm = project.team.get_tm(current_user.id)
... ...
app/views/deploy_keys/_form.html.haml
1 1 %div
2 2 = form_for [@project, @key], url: project_deploy_keys_path do |f|
3 3 -if @key.errors.any?
4   - .alert-message.block-message.error
  4 + .alert.alert-error
5 5 %ul
6 6 - @key.errors.full_messages.each do |msg|
7 7 %li= msg
... ... @@ -18,6 +18,6 @@
18 18 = link_to "here", help_ssh_path
19 19  
20 20 .actions
21   - = f.submit 'Save', class: "save-btn btn"
22   - = link_to "Cancel", project_deploy_keys_path(@project), class: "btn cancel-btn"
  21 + = f.submit 'Save', class: "btn-save btn"
  22 + = link_to "Cancel", project_deploy_keys_path(@project), class: "btn btn-cancel"
23 23  
... ...
app/views/deploy_keys/_show.html.haml
... ... @@ -8,5 +8,5 @@
8 8 = time_ago_in_words(key.created_at)
9 9 ago
10 10 %td
11   - = link_to 'Remove', project_deploy_key_path(key.project, key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key small right"
  11 + = link_to 'Remove', project_deploy_key_path(key.project, key), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key btn-small pull-right"
12 12  
... ...
app/views/deploy_keys/index.html.haml
... ... @@ -4,7 +4,7 @@
4 4 Deploy keys allow read-only access to repository. It matches perfectly for CI, staging or production servers.
5 5  
6 6 - if can? current_user, :admin_project, @project
7   - = link_to new_project_deploy_key_path(@project), class: "btn small", title: "New Deploy Key" do
  7 + = link_to new_project_deploy_key_path(@project), class: "btn btn-small", title: "New Deploy Key" do
8 8 Add Deploy Key
9 9 - if @keys.any?
10 10 %table
... ...
app/views/deploy_keys/show.html.haml
... ... @@ -10,5 +10,5 @@
10 10 &larr; To keys list
11 11 %hr
12 12 %pre= @key.key
13   -.right
14   - = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key"
  13 +.pull-right
  14 + = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "btn-remove btn delete-key"
... ...
app/views/devise/passwords/edit.html.haml
... ... @@ -8,5 +8,5 @@
8 8 %div
9 9 = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password"
10 10 %div
11   - = f.submit "Change my password", class: "btn primary"
12   - .right= render partial: "devise/shared/links"
  11 + = f.submit "Change my password", class: "btn btn-primary"
  12 + .pull-right= render partial: "devise/shared/links"
... ...
app/views/devise/passwords/new.html.erb
1   -<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :class => "login-box", :method => :post }) do |f| %>
2   - <%= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" %>
  1 +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { class: "login-box", method: :post }) do |f| %>
  2 + <%= image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" %>
3 3 <%= devise_error_messages! %>
4   - <%= f.email_field :email, :placeholder => "Email", :class => "text" %>
  4 + <%= f.email_field :email, placeholder: "Email", class: "text" %>
5 5 <br/>
6 6 <br/>
7   - <%= f.submit "Reset password", :class => "primary btn" %>
8   - <div class="right"> <%= link_to "Sign in", new_session_path(resource_name), :class => "btn" %><br /></div>
  7 + <%= f.submit "Reset password", class: "btn-primary btn" %>
  8 + <div class="pull-right"> <%= link_to "Sign in", new_session_path(resource_name), class: "btn" %><br /></div>
9 9 <% end %>
... ...
app/views/devise/registrations/new.html.haml
1   -= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => "login-box" }) do |f|
2   - = image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo"
  1 += form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "login-box" }) do |f|
  2 + = image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo"
3 3 = devise_error_messages!
4 4 %div
5   - = f.text_field :name, :class => "text top", :placeholder => "Name", :required => true
  5 + = f.text_field :name, class: "text top", placeholder: "Name", required: true
6 6 %div
7   - = f.text_field :username, :class => "text middle", :placeholder => "Username", :required => true
  7 + = f.text_field :username, class: "text middle", placeholder: "Username", required: true
8 8 %div
9   - = f.email_field :email, :class => "text middle", :placeholder => "Email", :required => true
  9 + = f.email_field :email, class: "text middle", placeholder: "Email", required: true
10 10 %div
11   - = f.password_field :password, :class => "text middle", :placeholder => "Password", :required => true
  11 + = f.password_field :password, class: "text middle", placeholder: "Password", required: true
12 12 %div
13   - = f.password_field :password_confirmation, :class => "text bottom", :placeholder => "Confirm password", :required => true
  13 + = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm password", required: true
14 14 %div
15   - = f.submit "Sign up", :class => "primary btn wide"
16   - %br
  15 + = f.submit "Sign up", class: "btn-create btn"
17 16 %hr
18 17 = link_to "Sign in", new_session_path(resource_name)
19   - = link_to "Forgot your password?", new_password_path(resource_name), :class => "right"
  18 + = link_to "Forgot your password?", new_password_path(resource_name), class: "pull-right"
... ...
app/views/devise/sessions/_new_ldap.html.haml
... ... @@ -3,11 +3,11 @@
3 3 = text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login"}
4 4 = password_field_tag :password, nil, {:class => "text bottom", :placeholder => "Password"}
5 5 %br/
6   - = submit_tag "LDAP Sign in", :class => "primary btn"
  6 + = submit_tag "LDAP Sign in", :class => "btn-primary btn"
7 7 - if devise_mapping.omniauthable?
8 8 - (resource_class.omniauth_providers - [:ldap]).each do |provider|
9 9 %hr/
10   - = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary"
  10 + = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn btn-primary"
11 11 %br/
12 12 %hr/
13 13 %a#other_form_toggle{:href => "#", :onclick => "javascript:$('#new_user').toggle();"} Other Sign in
... ... @@ -24,6 +24,6 @@
24 24 = f.check_box :remember_me
25 25 %span Remember me
26 26 %br/
27   - = f.submit "Sign in", :class => "primary btn"
28   - .right
  27 + = f.submit "Sign in", :class => "btn-primary btn"
  28 + .pull-right
29 29 = render :partial => "devise/shared/links"
... ...
app/views/devise/sessions/new.html.haml
... ... @@ -11,18 +11,18 @@
11 11 = f.check_box :remember_me
12 12 %span Remember me
13 13 %br/
14   - = f.submit "Sign in", :class => "primary btn wide"
15   - .right
  14 + = f.submit "Sign in", :class => "btn-create btn"
  15 + .pull-right
16 16 = link_to "Forgot your password?", new_password_path(resource_name), :class => "btn"
17 17 %br/
18   - %br/
19 18 - if Gitlab.config.gitlab.signup_enabled
20 19 %hr/
21 20 Don't have an account?
22 21 = link_to "Sign up", new_registration_path(resource_name)
23   - .clearfix
24 22 - if devise_mapping.omniauthable? && resource_class.omniauth_providers.present?
  23 + %hr
25 24 %div
  25 + %span Sign in with: &nbsp;
26 26 - resource_class.omniauth_providers.each do |provider|
27 27 %span
28 28 = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
... ...
app/views/events/_event.html.haml
1 1 - if event.proper?
2 2 %div.event-item
3   - %span.cgray.right
  3 + %span.cgray.pull-right
4 4 #{time_ago_in_words(event.created_at)} ago.
5 5  
6 6 = image_tag gravatar_icon(event.author_email), class: "avatar s24"
... ...
app/views/graph/show.html.haml 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +%h3.page_title Project Network Graph
  2 +%br
  3 += render partial: 'shared/ref_switcher', locals: {destination: 'graph', path: @path}
  4 +%br
  5 +.graph_holder
  6 + %h4
  7 + %small You can move around the graph by using the arrow keys.
  8 + #holder.graph
  9 + .loading.loading-gray
  10 +
  11 +:javascript
  12 + var branch_graph;
  13 + $(function(){
  14 + branch_graph = new BranchGraph($("#holder"), {
  15 + url: '#{project_graph_path(@project, @ref, format: :json)}',
  16 + commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}',
  17 + ref: '#{@ref}'
  18 + });
  19 + });
... ...
app/views/groups/_filter.html.haml
... ... @@ -25,9 +25,9 @@
25 25 %li{class: ("active" if params[:project_id] == project.id.to_s)}
26 26 = link_to group_filter_path(entity, project_id: project.id) do
27 27 = project.name_with_namespace
28   - %small.right= entities_per_project(project, entity)
  28 + %small.pull-right= entities_per_project(project, entity)
29 29  
30 30 %fieldset
31 31 %hr
32   - = link_to "Reset", group_filter_path(entity), class: 'btn right'
  32 + = link_to "Reset", group_filter_path(entity), class: 'btn pull-right'
33 33  
... ...
app/views/groups/_new_group_member.html.haml
... ... @@ -14,5 +14,5 @@
14 14  
15 15 .form-actions
16 16 = hidden_field_tag :redirect_to, people_group_path(@group)
17   - = f.submit 'Add', class: "btn save-btn"
  17 + = f.submit 'Add', class: "btn btn-save"
18 18  
... ...
app/views/groups/_new_member.html.haml
... ... @@ -14,5 +14,5 @@
14 14  
15 15 .form-actions
16 16 = hidden_field_tag :redirect_to, people_group_path(@group, project_id: @project.id)
17   - = f.submit 'Add', class: "btn save-btn"
  17 + = f.submit 'Add', class: "btn btn-save"
18 18  
... ...
app/views/groups/_people_filter.html.haml
... ... @@ -6,9 +6,9 @@
6 6 %li{class: ("active" if params[:project_id] == project.id.to_s)}
7 7 = link_to people_group_path(@group, project_id: project.id) do
8 8 = project.name_with_namespace
9   - %small.right= project.users.count
  9 + %small.pull-right= project.users.count
10 10  
11 11 %fieldset
12 12 %hr
13   - = link_to "Reset", people_group_path(@group), class: 'btn right'
  13 + = link_to "Reset", people_group_path(@group), class: 'btn pull-right'
14 14  
... ...
app/views/groups/_projects.html.haml
... ... @@ -4,8 +4,8 @@
4 4 %small
5 5 (#{projects.count})
6 6 - if can? current_user, :manage_group, @group
7   - %span.right
8   - = link_to new_project_path(namespace_id: @group.id), class: "btn very_small info" do
  7 + %span.pull-right
  8 + = link_to new_project_path(namespace_id: @group.id), class: "btn btn-tiny info" do
9 9 %i.icon-plus
10 10 New Project
11 11 %ul.well-list
... ...
app/views/groups/edit.html.haml 0 → 100644
... ... @@ -0,0 +1,50 @@
  1 +%h3.page_title Edit Group
  2 +%hr
  3 += form_for @group do |f|
  4 + - if @group.errors.any?
  5 + .alert.alert-error
  6 + %span= @group.errors.full_messages.first
  7 + .clearfix
  8 + = f.label :name do
  9 + Group name is
  10 + .input
  11 + = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
  12 + &nbsp;
  13 + = f.submit 'Save group', class: "btn btn-save"
  14 +%hr
  15 +
  16 +
  17 +.row
  18 + .span7
  19 + .ui-box
  20 + %h5.title Projects
  21 + %ul.well-list
  22 + - @group.projects.each do |project|
  23 + %li
  24 + - if project.public
  25 + %i.icon-share
  26 + - else
  27 + %i.icon-lock.cgreen
  28 + = link_to project.name_with_namespace, project
  29 + .pull-right
  30 + = link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
  31 + = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
  32 + = link_to 'Remove', project, confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
  33 +
  34 + .span5
  35 + .ui-box
  36 + %h5.title Transfer group
  37 + .padded
  38 + %p
  39 + Transferring group will cause loss of admin control over group and all child projects
  40 + = form_for @group do |f|
  41 + = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
  42 + = f.submit 'Transfer group', class: "btn btn-small"
  43 + .ui-box
  44 + %h5.title Remove group
  45 + .padded.bgred
  46 + %p
  47 + Remove of group will cause removing all child projects and resources
  48 + %br
  49 + Removed group can not be restored!
  50 + = link_to 'Remove Group', @group, confirm: 'Removed group can not be restored! Are you sure?', method: :delete, class: "btn btn-remove btn-small"
... ...
app/views/groups/issues.html.haml
1 1 %h3.page_title
2 2 Issues
3 3 %small (assigned to you)
4   - %small.right #{@issues.total_count} issues
  4 + %small.pull-right #{@issues.total_count} issues
5 5  
6 6 %hr
7 7 .row
... ...
app/views/groups/merge_requests.html.haml
1 1 %h3.page_title
2 2 Merge Requests
3 3 %small (authored by or assigned to you)
4   - %small.right #{@merge_requests.total_count} merge requests
  4 + %small.pull-right #{@merge_requests.total_count} merge requests
5 5  
6 6 %hr
7 7 .row
... ...
app/views/groups/new.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %hr
3 3 = form_for @group do |f|
4 4 - if @group.errors.any?
5   - .alert-message.block-message.error
  5 + .alert.alert-error
6 6 %span= @group.errors.full_messages.first
7 7 .clearfix
8 8 = f.label :name do
... ... @@ -10,7 +10,7 @@
10 10 .input
11 11 = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
12 12 &nbsp;
13   - = f.submit 'Create group', class: "btn primary"
  13 + = f.submit 'Create group', class: "btn btn-create"
14 14 %hr
15 15 .padded
16 16 %ul
... ...
app/views/groups/people.html.haml
... ... @@ -16,5 +16,5 @@
16 16 %strong= user.name
17 17 %span.cgray= user.email
18 18 - if @group.owner == user
19   - %span.btn.btn-small.disabled.right Group Owner
  19 + %span.btn.btn-small.disabled.pull-right Group Owner
20 20  
... ...
app/views/groups/search.html.haml
... ... @@ -4,6 +4,6 @@
4 4 %strong Looking for
5 5 .input
6 6 = search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search"
7   - = submit_tag 'Search', class: "btn primary wide"
  7 + = submit_tag 'Search', class: "btn btn-primary wide"
8 8 - if params[:search].present?
9 9 = render 'search/result'
... ...
app/views/groups/show.html.haml
1 1 .projects
2 2 .activities.span8
3 3 = render "events/event_last_push", event: @last_push
4   - = link_to dashboard_path, class: 'btn very_small' do
  4 + = link_to dashboard_path, class: 'btn btn-tiny' do
5 5 &larr; To dashboard
6 6 &nbsp;
7 7 %span.cgray You will only see events from projects in this group
... ...
app/views/help/_layout.html.haml
... ... @@ -30,5 +30,5 @@
30 30 %li
31 31 %strong= link_to "Public Access", help_public_access_path
32 32  
33   - .span9.right
  33 + .span9.pull-right
34 34 = yield
... ...
app/views/help/index.html.haml
1 1 %h3.page_title
2 2 GITLAB
3   - .right
  3 + .pull-right
4 4 %span= Gitlab::Version
5 5 %small= Gitlab::Revision
6 6 %hr
... ...
app/views/hooks/index.html.haml
... ... @@ -10,7 +10,7 @@
10 10  
11 11 = form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-inline' } do |f|
12 12 -if @hook.errors.any?
13   - .alert-message.block-message.error
  13 + .alert.alert-error
14 14 - @hook.errors.full_messages.each do |msg|
15 15 %p= msg
16 16 .clearfix
... ... @@ -18,7 +18,7 @@
18 18 .input
19 19 = f.text_field :url, class: "text_field xxlarge"
20 20 &nbsp;
21   - = f.submit "Add Web Hook", class: "btn primary"
  21 + = f.submit "Add Web Hook", class: "btn btn-primary"
22 22 %hr
23 23  
24 24 -if @hooks.any?
... ... @@ -37,6 +37,6 @@
37 37 &rarr;
38 38 %span.monospace= hook.url
39 39 %td
40   - .right
41   - = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn small grouped"
42   - = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small grouped"
  40 + .pull-right
  41 + = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped"
  42 + = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped"
... ...
app/views/issues/_filter.html.haml
... ... @@ -16,5 +16,5 @@
16 16  
17 17 %fieldset
18 18 %hr
19   - = link_to "Reset", project_issues_path(@project), class: 'btn right'
  19 + = link_to "Reset", project_issues_path(@project), class: 'btn pull-right'
20 20  
... ...
app/views/issues/_form.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %h3.page_title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}"
3 3 = form_for [@project, @issue] do |f|
4 4 -if @issue.errors.any?
5   - .alert-message.block-message.error
  5 + .alert.alert-error
6 6 - @issue.errors.full_messages.each do |msg|
7 7 %span= msg
8 8 %br
... ... @@ -44,12 +44,12 @@
44 44  
45 45 .actions
46 46 - if @issue.new_record?
47   - = f.submit 'Submit new issue', class: "btn success"
  47 + = f.submit 'Submit new issue', class: "btn btn-create"
48 48 -else
49   - = f.submit 'Save changes', class: "save-btn btn"
  49 + = f.submit 'Save changes', class: "btn-save btn"
50 50  
51 51 - cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue)
52   - = link_to "Cancel", cancel_path, class: 'btn cancel-btn'
  52 + = link_to "Cancel", cancel_path, class: 'btn btn-cancel'
53 53  
54 54  
55 55  
... ...
app/views/issues/_head.html.haml
... ... @@ -5,7 +5,7 @@
5 5 = link_to 'Milestones', project_milestones_path(@project), class: "tab"
6 6 = nav_link(controller: :labels) do
7 7 = link_to 'Labels', project_labels_path(@project), class: "tab"
8   - %li.right
  8 + %li.pull-right
9 9 %span.rss-icon
10 10 = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
11 11 = image_tag "rss_ui.png", title: "feed"
... ...
app/views/issues/_issues.html.haml
... ... @@ -4,7 +4,7 @@
4 4 - if @issues.present?
5 5 %li.bottom
6 6 .left= paginate @issues, remote: true, theme: "gitlab"
7   - .right
  7 + .pull-right
8 8 %span.issue_counter #{@issues.total_count}
9 9 issues for this filter
10 10 - else
... ...
app/views/issues/_show.html.haml
... ... @@ -2,17 +2,17 @@
2 2 - if controller.controller_name == 'issues'
3 3 .issue_check
4 4 = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
5   - .right
  5 + .pull-right
6 6 - if issue.notes.any?
7   - %span.btn.small.disabled.grouped
  7 + %span.btn.btn-small.disabled.grouped
8 8 %i.icon-comment
9 9 = issue.notes.count
10 10 - if can? current_user, :modify_issue, issue
11 11 - if issue.closed
12   - = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small grouped reopen_issue", remote: true
  12 + = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {closed: false }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true
13 13 - else
14   - = link_to 'Close', project_issue_path(issue.project, issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small grouped close_issue", remote: true
15   - = link_to edit_project_issue_path(issue.project, issue), class: "btn small edit-issue-link grouped" do
  14 + = link_to 'Close', project_issue_path(issue.project, issue, issue: {closed: true }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true
  15 + = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do
16 16 %i.icon-edit
17 17 Edit
18 18  
... ...
app/views/issues/index.html.haml
... ... @@ -3,16 +3,16 @@
3 3 %h3.page_title
4 4 Issues
5 5 %span (<span class=issue_counter>#{@issues.total_count}</span>)
6   - .right
  6 + .pull-right
7 7 .span5
8 8 - if can? current_user, :write_issue, @project
9   - = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "right btn primary", title: "New Issue", id: "new_issue_link" do
  9 + = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-primary pull-right", title: "New Issue", id: "new_issue_link" do
10 10 %i.icon-plus
11 11 New Issue
12   - = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do
  12 + = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: 'pull-right' do
13 13 = hidden_field_tag :project_id, @project.id, { id: 'project_id' }
14 14 = hidden_field_tag :status, params[:status]
15   - = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib search-text-input' }
  15 + = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 pull-right neib search-text-input' }
16 16  
17 17 .clearfix
18 18  
... ... @@ -33,7 +33,7 @@
33 33 = select_tag('update[milestone_id]', options_from_collection_for_select(issues_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone")
34 34 = hidden_field_tag 'update[issues_ids]', []
35 35 = hidden_field_tag :status, params[:status]
36   - = button_tag "Save", class: "btn update_selected_issues btn-small save-btn"
  36 + = button_tag "Save", class: "btn update_selected_issues btn-small btn-save"
37 37 .issues_filters
38 38 = form_tag project_issues_path(@project), method: :get do
39 39 = select_tag(:label_name, options_for_select(issue_tags, params[:label_name]), prompt: "Labels")
... ...
app/views/issues/show.html.haml
... ... @@ -5,7 +5,7 @@
5 5 created at
6 6 = @issue.created_at.stamp("Aug 21, 2011")
7 7  
8   - %span.right
  8 + %span.pull-right
9 9 - if can?(current_user, :admin_project, @project) || @issue.author == current_user
10 10 - if @issue.closed
11 11 = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped reopen_issue"
... ... @@ -16,7 +16,7 @@
16 16 %i.icon-edit
17 17 Edit
18 18  
19   -.right
  19 +.pull-right
20 20 .span3#votes= render 'votes/votes_block', votable: @issue
21 21  
22 22 .back_link
... ... @@ -42,7 +42,7 @@
42 42 %cite.cgray and attached to milestone
43 43 %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
44 44  
45   - .right
  45 + .pull-right
46 46 - @issue.labels.each do |label|
47 47 %span.label
48 48 %i.icon-tag
... ...
app/views/keys/_form.html.haml
1 1 %div
2 2 = form_for @key do |f|
3 3 -if @key.errors.any?
4   - .alert-message.block-message.error
  4 + .alert.alert-error
5 5 %ul
6 6 - @key.errors.full_messages.each do |msg|
7 7 %li= msg
... ... @@ -19,6 +19,6 @@
19 19  
20 20  
21 21 .actions
22   - = f.submit 'Save', class: "btn save-btn"
23   - = link_to "Cancel", keys_path, class: "btn cancel-btn"
  22 + = f.submit 'Save', class: "btn btn-save"
  23 + = link_to "Cancel", keys_path, class: "btn btn-cancel"
24 24  
... ...
app/views/keys/_show.html.haml
... ... @@ -8,5 +8,5 @@
8 8 = time_ago_in_words(key.created_at)
9 9 ago
10 10 %td
11   - = link_to 'Remove', key, confirm: 'Are you sure?', method: :delete, class: "btn small danger delete-key right"
  11 + = link_to 'Remove', key, confirm: 'Are you sure?', method: :delete, class: "btn btn-small btn-remove delete-key pull-right"
12 12  
... ...
app/views/keys/index.html.haml
1 1 %h3.page_title
2 2 SSH Keys
3   - = link_to "Add new", new_key_path, class: "btn right"
  3 + = link_to "Add new", new_key_path, class: "btn pull-right"
4 4  
5 5 %hr
6 6 %p.slead
... ...
app/views/keys/show.html.haml
... ... @@ -10,5 +10,5 @@
10 10 %hr
11 11  
12 12 %pre= @key.key
13   -.right
14   - = link_to 'Remove', @key, confirm: 'Are you sure?', method: :delete, class: "btn danger delete-key"
  13 +.pull-right
  14 + = link_to 'Remove', @key, confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key"
... ...
app/views/labels/_label.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %strong
3 3 %i.icon-tag
4 4 = label.name
5   - .right
  5 + .pull-right
6 6 = link_to project_issues_path(label_name: label.name) do
7 7 %strong
8 8 = pluralize(label.count, 'issue')
... ...
app/views/layouts/admin.html.haml
... ... @@ -7,7 +7,8 @@
7 7 .container
8 8 %ul.main_menu
9 9 = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
10   - = link_to "Stats", admin_root_path
  10 + = link_to admin_root_path, title: "Stats" do
  11 + %i.icon-home
11 12 = nav_link(controller: :projects) do
12 13 = link_to "Projects", admin_projects_path
13 14 = nav_link(controller: :teams) do
... ...
app/views/layouts/application.html.haml
... ... @@ -7,7 +7,8 @@
7 7 .container
8 8 %ul.main_menu
9 9 = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
10   - = link_to "Home", root_path, title: "Home"
  10 + = link_to root_path, title: "Home" do
  11 + %i.icon-home
11 12 = nav_link(path: 'dashboard#projects') do
12 13 = link_to projects_dashboard_path do
13 14 Projects
... ...
app/views/layouts/group.html.haml
... ... @@ -7,7 +7,8 @@
7 7 .container
8 8 %ul.main_menu
9 9 = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
10   - = link_to "Home", group_path(@group), title: "Home"
  10 + = link_to group_path(@group), title: "Home" do
  11 + %i.icon-home
11 12 = nav_link(path: 'groups#issues') do
12 13 = link_to issues_group_path(@group) do
13 14 Issues
... ... @@ -21,4 +22,10 @@
21 22 = nav_link(path: 'groups#people') do
22 23 = link_to "People", people_group_path(@group)
23 24  
  25 + - if can?(current_user, :manage_group, @group)
  26 + = nav_link(path: 'groups#edit') do
  27 + = link_to edit_group_path(@group), class: "tab " do
  28 + %i.icon-edit
  29 + Edit Group
  30 +
24 31 .content= yield
... ...
app/views/layouts/profile.html.haml
... ... @@ -7,7 +7,8 @@
7 7 .container
8 8 %ul.main_menu
9 9 = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
10   - = link_to "Profile", profile_path
  10 + = link_to profile_path, title: "Profile" do
  11 + %i.icon-home
11 12 = nav_link(path: 'profiles#account') do
12 13 = link_to "Account", account_profile_path
13 14 = nav_link(controller: :keys) do
... ...
app/views/layouts/project_resource.html.haml
... ... @@ -12,7 +12,8 @@
12 12 .container
13 13 %ul.main_menu
14 14 = nav_link(html_options: {class: "home #{project_tab_class}"}) do
15   - = link_to @project.path, project_path(@project), title: "Project"
  15 + = link_to project_path(@project), title: "Project" do
  16 + %i.icon-home
16 17  
17 18 - if @project.repo_exists?
18 19 - if can? current_user, :download_code, @project
... ... @@ -20,8 +21,8 @@
20 21 = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
21 22 = nav_link(controller: %w(commit commits compare repositories protected_branches)) do
22 23 = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref)
23   - = nav_link(path: 'projects#graph') do
24   - = link_to "Network", graph_project_path(@project)
  24 + = nav_link(controller: %w(graph)) do
  25 + = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref)
25 26  
26 27 - if @project.issues_enabled
27 28 = nav_link(controller: %w(issues milestones labels)) do
... ...
app/views/layouts/user_team.html.haml
... ... @@ -7,7 +7,8 @@
7 7 .container
8 8 %ul.main_menu
9 9 = nav_link(path: 'teams#show', html_options: {class: 'home'}) do
10   - = link_to "Home", team_path(@team), title: "Home"
  10 + = link_to team_path(@team), title: "Home" do
  11 + %i.icon-home
11 12  
12 13 = nav_link(path: 'teams#issues') do
13 14 = link_to issues_team_path(@team) do
... ...
app/views/merge_requests/_filter.html.haml
... ... @@ -16,5 +16,5 @@
16 16  
17 17 %fieldset
18 18 %hr
19   - = link_to "Reset", project_merge_requests_path(@project), class: 'btn right'
  19 + = link_to "Reset", project_merge_requests_path(@project), class: 'btn pull-right'
20 20  
... ...
app/views/merge_requests/_form.html.haml
1 1 = form_for [@project, @merge_request], html: { class: "#{controller.action_name}-merge-request form-horizontal" } do |f|
2 2 -if @merge_request.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %ul
5 5 - @merge_request.errors.full_messages.each do |msg|
6 6 %li= msg
... ... @@ -51,19 +51,19 @@
51 51  
52 52 .form-actions
53 53 - if @merge_request.new_record?
54   - = f.submit 'Submit merge request', class: "btn success"
  54 + = f.submit 'Submit merge request', class: "btn btn-create"
55 55 -else
56   - = f.submit 'Save changes', class: "save-btn btn"
  56 + = f.submit 'Save changes', class: "btn btn-save"
57 57 - if @merge_request.new_record?
58   - = link_to project_merge_requests_path(@project), class: "btn cancel-btn" do
  58 + = link_to project_merge_requests_path(@project), class: "btn btn-cancel" do
59 59 Cancel
60 60 - else
61   - = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do
  61 + = link_to project_merge_request_path(@project, @merge_request), class: "btn btn-cancel" do
62 62 Cancel
63 63  
64 64 :javascript
65 65 $(function(){
66   - disableButtonIfEmptyField("#merge_request_title", ".save-btn");
  66 + disableButtonIfEmptyField("#merge_request_title", ".btn-save");
67 67  
68 68 var source_branch = $("#merge_request_source_branch")
69 69 , target_branch = $("#merge_request_target_branch");
... ...
app/views/merge_requests/_merge_request.html.haml
1 1 %li{ class: mr_css_classes(merge_request) }
2   - .right
  2 + .pull-right
3 3 .left
4 4 - if merge_request.merged?
5   - %span.btn.small.disabled.grouped
  5 + %span.btn.btn-small.disabled.grouped
6 6 %strong
7 7 %i.icon-ok
8 8 = "MERGED"
9 9 - if merge_request.notes.any?
10   - %span.btn.small.disabled.grouped
  10 + %span.btn.btn-small.disabled.grouped
11 11 %i.icon-comment
12 12 = merge_request.mr_and_commit_notes.count
13 13 - if merge_request.milestone_id?
14   - %span.btn.small.disabled.grouped
  14 + %span.btn.btn-small.disabled.grouped
15 15 %i.icon-time
16 16 = merge_request.milestone.title
17   - %span.btn.small.disabled.grouped
  17 + %span.btn.btn-small.disabled.grouped
18 18 = merge_request.source_branch
19 19 &rarr;
20 20 = merge_request.target_branch
... ...
app/views/merge_requests/index.html.haml
1 1 - if can? current_user, :write_merge_request, @project
2   - = link_to new_project_merge_request_path(@project), class: "right btn primary", title: "New Merge Request" do
  2 + = link_to new_project_merge_request_path(@project), class: "pull-right btn btn-primary", title: "New Merge Request" do
3 3 %i.icon-plus
4 4 New Merge Request
5 5 %h3.page_title
... ... @@ -28,8 +28,8 @@
28 28 - if @merge_requests.present?
29 29 %li.bottom
30 30 .left= paginate @merge_requests, theme: "gitlab"
31   - .right
32   - %span.cgray.right #{@merge_requests.total_count} merge requests for this filter
  31 + .pull-right
  32 + %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter
33 33  
34 34 :javascript
35 35 $(merge_requestsPage);
... ...
app/views/merge_requests/show/_mr_accept.html.haml
1 1 - unless can?(current_user, :accept_mr, @project)
2   - .alert-message
  2 + .alert
3 3 %strong Only masters can accept MR
4 4  
5 5  
... ... @@ -29,14 +29,14 @@
29 29 %strong This repository does not have satellite. Ask administrator to fix this issue
30 30  
31 31 .automerge_widget.cannot_be_merged{style: "display:none"}
32   - .alert.alert-info
  32 + .alert.alert-disabled
33 33 %span
34   - = link_to "Show how to merge", "#", class: "how_to_merge_link btn small padded", title: "How To Merge"
  34 + = link_to "Show how to merge", "#", class: "how_to_merge_link btn btn-small padded", title: "How To Merge"
35 35 &nbsp;
36 36 %strong This request can't be merged with GitLab. You should do it manually
37 37  
38 38 .automerge_widget.unchecked
39   - .alert-message
  39 + .alert
40 40 %strong
41 41 %i.icon-refresh
42 42 Checking for ability to automatically merge…
... ...
app/views/merge_requests/show/_mr_ci.html.haml
... ... @@ -23,7 +23,7 @@
23 23 = link_to "Build page", ci_build_details_path(@merge_request)
24 24  
25 25 .ci_widget
26   - .alert-message
  26 + .alert
27 27 %strong
28 28 %i.icon-refresh
29 29 Checking for CI status for #{@merge_request.last_commit_short_sha}
... ...
app/views/merge_requests/show/_mr_title.html.haml
... ... @@ -5,7 +5,7 @@
5 5 &rarr;
6 6 %span.label_branch= @merge_request.target_branch
7 7  
8   - %span.right
  8 + %span.pull-right
9 9 - if can?(current_user, :modify_merge_request, @merge_request)
10 10 - if @merge_request.open?
11 11 .left.btn-group
... ... @@ -17,13 +17,13 @@
17 17 %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch)
18 18 %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff)
19 19  
20   - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {closed: true }, status_only: true), method: :put, class: "btn grouped danger", title: "Close merge request"
  20 + = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {closed: true }, status_only: true), method: :put, class: "btn grouped btn-close", title: "Close merge request"
21 21  
22 22 = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do
23 23 %i.icon-edit
24 24 Edit
25 25  
26   -.right
  26 +.pull-right
27 27 .span3#votes= render 'votes/votes_block', votable: @merge_request
28 28  
29 29 .back_link
... ...
app/views/milestones/_form.html.haml
... ... @@ -7,7 +7,7 @@
7 7  
8 8 = form_for [@project, @milestone], html: {class: "new_milestone form-horizontal"} do |f|
9 9 -if @milestone.errors.any?
10   - .alert-message.block-message.error
  10 + .alert.alert-error
11 11 %ul
12 12 - @milestone.errors.full_messages.each do |msg|
13 13 %li= msg
... ... @@ -32,16 +32,16 @@
32 32  
33 33 .form-actions
34 34 - if @milestone.new_record?
35   - = f.submit 'Create milestone', class: "save-btn btn"
36   - = link_to "Cancel", project_milestones_path(@project), class: "btn cancel-btn"
  35 + = f.submit 'Create milestone', class: "btn-save btn"
  36 + = link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel"
37 37 -else
38   - = f.submit 'Save changes', class: "save-btn btn"
39   - = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn cancel-btn"
  38 + = f.submit 'Save changes', class: "btn-save btn"
  39 + = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn btn-cancel"
40 40  
41 41  
42 42 :javascript
43 43 $(function() {
44   - disableButtonIfEmptyField("#milestone_title", ".save-btn");
  44 + disableButtonIfEmptyField("#milestone_title", ".btn-save");
45 45 $( ".datepicker" ).datepicker({
46 46 dateFormat: "yy-mm-dd",
47 47 onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
... ...
app/views/milestones/_milestone.html.haml
1 1 %li{class: "milestone milestone-#{milestone.closed ? 'closed' : 'open'}", id: dom_id(milestone) }
2   - .right
  2 + .pull-right
3 3 - if can?(current_user, :admin_milestone, milestone.project) and milestone.open?
4   - = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn small edit-milestone-link grouped" do
  4 + = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link grouped" do
5 5 %i.icon-edit
6 6 Edit
7 7 %h4
... ...
app/views/milestones/index.html.haml
... ... @@ -3,7 +3,7 @@
3 3 %h3.page_title
4 4 Milestones
5 5 - if can? current_user, :admin_milestone, @project
6   - = link_to "New Milestone", new_project_milestone_path(@project), class: "right btn small", title: "New Milestone"
  6 + = link_to "New Milestone", new_project_milestone_path(@project), class: "pull-right btn btn-small", title: "New Milestone"
7 7 %br
8 8 %div.ui-box
9 9 .title
... ...
app/views/milestones/show.html.haml
... ... @@ -8,14 +8,14 @@
8 8 = link_to project_milestones_path(@project) do
9 9 &larr; To milestones list
10 10 .span6
11   - .right
  11 + .pull-right
12 12 - unless @milestone.closed
13   - = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn small grouped", title: "New Issue" do
  13 + = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small grouped", title: "New Issue" do
14 14 %i.icon-plus
15 15 New Issue
16 16 = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link small grouped"
17 17 - if can?(current_user, :admin_milestone, @project)
18   - = link_to edit_project_milestone_path(@project, @milestone), class: "btn small grouped" do
  18 + = link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-small grouped" do
19 19 %i.icon-edit
20 20 Edit
21 21  
... ... @@ -25,7 +25,7 @@
25 25 %hr
26 26 %p
27 27 %span All issues for this milestone are closed. You may close milestone now.
28   - = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {closed: true }), method: :put, class: "btn small danger"
  28 + = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {closed: true }), method: :put, class: "btn btn-small btn-remove"
29 29  
30 30 .ui-box.ui-box-show
31 31 .ui-box-head
... ... @@ -43,7 +43,7 @@
43 43 #{@milestone.closed_items_count} closed
44 44 &ndash;
45 45 #{@milestone.open_items_count} open
46   - %span.right= @milestone.expires_at
  46 + %span.pull-right= @milestone.expires_at
47 47 .progress.progress-info
48 48 .bar{style: "width: #{@milestone.percent_complete}%;"}
49 49  
... ...
app/views/notes/_form.html.haml
... ... @@ -15,30 +15,30 @@
15 15 = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on'
16 16 .note_preview.js-note-preview.turn-off
17 17  
18   - .buttons
19   - = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button"
20   - %a.btn.grouped.js-close-discussion-note-form Cancel
21 18 .hint
22   - .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  19 + .pull-right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
23 20 .clearfix
24 21  
25   - .note_options
26   - .attachment
27   - %h6 Attachment:
28   - .file_name.js-attachment-filename File name...
29   - %a.choose-btn.btn.small.js-choose-note-attachment-button Choose File ...
30   - .hint Any file up to 10 MB
  22 + .note-form-actions
  23 + .buttons
  24 + = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button"
  25 + %a.btn.grouped.js-close-discussion-note-form Cancel
31 26  
32   - = f.file_field :attachment, class: "js-note-attachment-input"
33   -
34   - .notify_options
35   - %h6 Notify via email:
  27 + .note-form-option
36 28 = label_tag :notify do
37 29 = check_box_tag :notify, 1, !@note.for_commit?
38   - Project team
  30 + %span.light Notify team via email
39 31  
40 32 .js-notify-commit-author
41 33 = label_tag :notify_author do
42 34 = check_box_tag :notify_author, 1 , @note.for_commit?
43   - Commit author
44   - .clearfix
  35 + %span.light Notify commit author
  36 + .note-form-option
  37 + %a.choose-btn.btn.btn-small.js-choose-note-attachment-button
  38 + %i.icon-paper-clip
  39 + %span Choose File ...
  40 + &nbsp;
  41 + %span.file_name.js-attachment-filename File name...
  42 + = f.file_field :attachment, class: "js-note-attachment-input hide"
  43 +
  44 + .clearfix
... ...
app/views/notes/_note.html.haml
... ... @@ -30,8 +30,8 @@
30 30 - if note.attachment.url
31 31 - if note.attachment.image?
32 32 = image_tag note.attachment.url, class: 'note-image-attach'
33   - .attachment.right
  33 + .attachment.pull-right
34 34 = link_to note.attachment.url, target: "_blank" do
35   - %i.icon-attachment
  35 + %i.icon-paper-clip
36 36 = note.attachment_identifier
37 37 .clear
... ...
app/views/profiles/account.html.haml
... ... @@ -12,7 +12,7 @@
12 12 %fieldset
13 13 %legend
14 14 Private token
15   - %span.cred.right
  15 + %span.cred.pull-right
16 16 keep it secret!
17 17 .padded
18 18 = form_for @user, url: reset_private_token_profile_path, method: :put do |f|
... ... @@ -24,7 +24,7 @@
24 24 %p.cgray
25 25 - if current_user.private_token
26 26 = text_field_tag "token", current_user.private_token, class: "xxlarge large_text"
27   - = f.submit 'Reset', confirm: "Are you sure?", class: "btn primary btn-build-token"
  27 + = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token"
28 28 - else
29 29 %span You don`t have one yet. Click generate to fix it.
30 30 = f.submit 'Generate', class: "btn success btn-build-token"
... ... @@ -35,7 +35,7 @@
35 35 .padded
36 36 %p.slead After successful password update you will be redirected to login page where you should login with new password
37 37 -if @user.errors.any?
38   - .alert-message.block-message.error
  38 + .alert.alert-error
39 39 %ul
40 40 - @user.errors.full_messages.each do |msg|
41 41 %li= msg
... ... @@ -49,14 +49,14 @@
49 49 = f.password_field :password_confirmation, required: true
50 50 .clearfix
51 51 .input
52   - = f.submit 'Save password', class: "btn save-btn"
  52 + = f.submit 'Save password', class: "btn btn-save"
53 53  
54 54  
55 55  
56 56 %fieldset.update-username
57 57 %legend
58 58 Username
59   - %small.cred.right
  59 + %small.cred.pull-right
60 60 Changing your username can have unintended side effects!
61 61 = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
62 62 .padded
... ... @@ -75,6 +75,6 @@
75 75 %li It will change web url for personal projects.
76 76 %li It will change the git path to repositories for personal projects.
77 77 .input
78   - = f.submit 'Save username', class: "btn save-btn"
  78 + = f.submit 'Save username', class: "btn btn-save"
79 79  
80 80  
... ...
app/views/profiles/show.html.haml
... ... @@ -6,7 +6,7 @@
6 6 %small
7 7 = @user.email
8 8  
9   - .right
  9 + .pull-right
10 10 = link_to destroy_user_session_path, class: "logout", method: :delete do
11 11 %small
12 12 %i.icon-signout
... ... @@ -15,7 +15,7 @@
15 15  
16 16 = form_for @user, url: profile_path, method: :put, html: { class: "edit_user form-horizontal" } do |f|
17 17 -if @user.errors.any?
18   - %div.alert-message.block-message.error
  18 + %div.alert.alert-error
19 19 %ul
20 20 - @user.errors.full_messages.each do |msg|
21 21 %li= msg
... ... @@ -46,7 +46,7 @@
46 46 = f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250
47 47 %span.help-block Tell us about yourself in fewer than 250 characters.
48 48  
49   - .span5.right
  49 + .span5.pull-right
50 50 %fieldset.tips
51 51 %legend Tips:
52 52 %ul
... ... @@ -65,18 +65,18 @@
65 65 %li
66 66 %p
67 67 Need a group for several dependent projects?
68   - = link_to new_group_path, class: "btn very_small" do
  68 + = link_to new_group_path, class: "btn btn-tiny" do
69 69 Create a group
70 70 - if current_user.can_create_team?
71 71 %li
72 72 %p
73 73 Want to share a team between projects?
74   - = link_to new_team_path, class: "btn very_small" do
  74 + = link_to new_team_path, class: "btn btn-tiny" do
75 75 Create a team
76 76 %fieldset
77 77 %legend
78 78 Personal projects:
79   - %small.right
  79 + %small.pull-right
80 80 %span= current_user.personal_projects.count
81 81 of
82 82 %span= current_user.projects_limit
... ... @@ -87,10 +87,10 @@
87 87 %fieldset
88 88 %legend
89 89 SSH public keys:
90   - %span.right
  90 + %span.pull-right
91 91 = link_to pluralize(current_user.keys.count, 'key'), keys_path
92 92 .padded
93   - = link_to "Add Public Key", new_key_path, class: "btn small"
  93 + = link_to "Add Public Key", new_key_path, class: "btn btn-small"
94 94  
95 95 .form-actions
96   - = f.submit 'Save', class: "btn save-btn"
  96 + = f.submit 'Save', class: "btn btn-save"
... ...
app/views/projects/_clone_panel.html.haml
... ... @@ -2,8 +2,8 @@
2 2 .row
3 3 .span7
4 4 .form-horizontal= render "shared/clone_panel"
5   - .span4.right
6   - .right
  5 + .span4.pull-right
  6 + .pull-right
7 7 - unless @project.empty_repo?
8 8 - if can? current_user, :download_code, @project
9 9 = link_to archive_project_repository_path(@project), class: "btn-small btn grouped" do
... ...
app/views/projects/_form.html.haml
1 1 = form_for(@project, remote: true) do |f|
2 2 - if @project.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %ul
5 5 - @project.errors.full_messages.each do |msg|
6 6 %li= msg
... ... @@ -42,7 +42,7 @@
42 42 = f.check_box :wiki_enabled
43 43 %span.descr Pages for project documentation
44 44  
45   - - if can? current_user, :change_public_mode, @project
  45 + - if can?(current_user, :change_public_mode, @project)
46 46 %fieldset.features
47 47 %legend
48 48 %i.icon-share
... ... @@ -77,9 +77,9 @@
77 77 %br
78 78  
79 79 .actions
80   - = f.submit 'Save', class: "btn save-btn"
  80 + = f.submit 'Save', class: "btn btn-save"
81 81 = link_to 'Cancel', @project, class: "btn"
82 82 - unless @project.new_record?
83 83 - if can?(current_user, :remove_project, @project)
84   - .right
85   - = link_to 'Remove Project', @project, confirm: 'Removed project can not be restored! Are you sure?', method: :delete, class: "btn danger"
  84 + .pull-right
  85 + = link_to 'Remove Project', @project, confirm: 'Removed project can not be restored! Are you sure?', method: :delete, class: "btn btn-remove"
... ...
app/views/projects/_new_form.html.haml
1 1 = form_for(@project, remote: true) do |f|
2 2 - if @project.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %span= @project.errors.full_messages.first
5 5 .clearfix.project_name_holder
6 6 = f.label :name do
7 7 Project name is
8 8 .input
9 9 = f.text_field :name, placeholder: "Example Project", class: "xxlarge"
10   - = f.submit 'Create project', class: "btn success project-submit"
  10 + = f.submit 'Create project', class: "btn btn-create project-submit"
11 11  
12 12 - if current_user.can_select_namespace?
13 13 .clearfix
... ... @@ -24,11 +24,11 @@
24 24 .clearfix
25 25 .input.light
26 26 Need a group for several dependent projects?
27   - = link_to new_group_path, class: "btn very_small" do
  27 + = link_to new_group_path, class: "btn btn-tiny" do
28 28 Create a group
29 29 - if current_user.can_create_team?
30 30 .clearfix
31 31 .input.light
32 32 Want to share a project between team?
33   - = link_to new_team_path, class: "btn very_small" do
  33 + = link_to new_team_path, class: "btn btn-tiny" do
34 34 Create a team
... ...
app/views/projects/_project_head.html.haml
... ... @@ -13,19 +13,19 @@
13 13 = link_to 'Snippets', project_snippets_path(@project), class: "snippets-tab tab"
14 14  
15 15 - if can? current_user, :admin_project, @project
16   - = nav_link(controller: :deploy_keys, html_options: {class: 'right'}) do
  16 + = nav_link(controller: :deploy_keys, html_options: {class: 'pull-right'}) do
17 17 = link_to project_deploy_keys_path(@project) do
18 18 %span
19 19 Deploy Keys
20   - = nav_link(controller: :hooks, html_options: {class: 'right'}) do
  20 + = nav_link(controller: :hooks, html_options: {class: 'pull-right'}) do
21 21 = link_to project_hooks_path(@project) do
22 22 %span
23 23 Hooks
24   - = nav_link(controller: :services, html_options: {class: 'right'}) do
  24 + = nav_link(controller: :services, html_options: {class: 'pull-right'}) do
25 25 = link_to project_services_path(@project) do
26 26 %span
27 27 Services
28   - = nav_link(path: 'projects#edit', html_options: {class: 'right'}) do
  28 + = nav_link(path: 'projects#edit', html_options: {class: 'pull-right'}) do
29 29 = link_to edit_project_path(@project), class: "stat-tab tab " do
30 30 %i.icon-edit
31 31 Edit
... ...
app/views/projects/empty.html.haml
... ... @@ -31,4 +31,4 @@
31 31  
32 32 - if can? current_user, :remove_project, @project
33 33 .prepend-top-20
34   - = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right"
  34 + = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn btn-remove pull-right"
... ...
app/views/projects/graph.html.haml
... ... @@ -1,17 +0,0 @@
1   -%h3.page_title Project Network Graph
2   -%br
3   -
4   -.graph_holder
5   - %h4
6   - %small You can move around the graph by using the arrow keys.
7   - #holder.graph
8   - .loading.loading-gray
9   -
10   -:javascript
11   - var branch_graph;
12   - $(function(){
13   - branch_graph = new BranchGraph($("#holder"), {
14   - url: '#{url_for controller: 'projects', action: 'graph', format: :json}',
15   - commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}'
16   - });
17   - });
app/views/projects/teams/available.html.haml
... ... @@ -17,6 +17,6 @@
17 17  
18 18  
19 19 .actions
20   - = submit_tag 'Assign', class: "btn save-btn"
21   - = link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn"
  20 + = submit_tag 'Assign', class: "btn btn-create"
  21 + = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel"
22 22  
... ...
app/views/protected_branches/index.html.haml
... ... @@ -14,7 +14,7 @@
14 14 - if can? current_user, :admin_project, @project
15 15 = form_for [@project, @protected_branch] do |f|
16 16 -if @protected_branch.errors.any?
17   - .alert-message.block-message.error
  17 + .alert.alert-error
18 18 %ul
19 19 - @protected_branch.errors.full_messages.each do |msg|
20 20 %li= msg
... ... @@ -24,7 +24,7 @@
24 24 .span3
25 25 = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "chosen span3"})
26 26 &nbsp;
27   - = f.submit 'Protect', class: "primary btn"
  27 + = f.submit 'Protect', class: "btn-primary btn"
28 28  
29 29 - unless @branches.empty?
30 30 %table
... ... @@ -51,4 +51,4 @@
51 51 (branch was removed from repository)
52 52 %td
53 53 - if can? current_user, :admin_project, @project
54   - = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "danger btn small"
  54 + = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small"
... ...
app/views/public/projects/index.html.haml
... ... @@ -9,7 +9,7 @@
9 9 %h5
10 10 %i.icon-share
11 11 = project.name_with_namespace
12   - .right
  12 + .pull-right
13 13 %pre.dark.tiny git clone #{project.http_url_to_repo}
14 14  
15 15  
... ...
app/views/repositories/_feed.html.haml
... ... @@ -15,6 +15,6 @@
15 15 = image_tag gravatar_icon(commit.author_email), class: "", width: 16
16 16 = gfm escape_once(truncate(commit.title, length: 40))
17 17 %td
18   - %span.right.cgray
  18 + %span.pull-right.cgray
19 19 = time_ago_in_words(commit.committed_date)
20 20 ago
... ...
app/views/repositories/stats.html.haml
... ... @@ -23,7 +23,7 @@
23 23 = image_tag gravatar_icon(author.email, 16), class: 'avatar s16'
24 24 = author.name
25 25 %small.light= author.email
26   - .right
  26 + .pull-right
27 27 = author.commits
28 28  
29 29  
... ...
app/views/search/show.html.haml
... ... @@ -4,7 +4,7 @@
4 4 %span Looking for
5 5 .input
6 6 = search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search"
7   - = submit_tag 'Search', class: "btn primary wide"
  7 + = submit_tag 'Search', class: "btn btn-primary wide"
8 8 .clearfix
9 9 .row
10 10 .span3
... ...
app/views/services/_gitlab_ci.html.haml
1 1 %h3.page_title
2 2 GitLab CI
3 3 %small Continuous integration server from GitLab
4   - .right
  4 + .pull-right
5 5 - if @service.active
6 6 %small.cgreen Enabled
7 7 - else
... ... @@ -16,7 +16,7 @@
16 16 %hr
17 17 = form_for(@service, :as => :service, :url => project_service_path(@project, :gitlab_ci), :method => :put) do |f|
18 18 - if @service.errors.any?
19   - .alert-message.block-message.error
  19 + .alert.alert-error
20 20 %ul
21 21 - @service.errors.full_messages.each do |msg|
22 22 %li= msg
... ... @@ -40,7 +40,7 @@
40 40  
41 41  
42 42 .form-actions
43   - = f.submit 'Save', class: 'btn save-btn'
  43 + = f.submit 'Save', class: 'btn btn-save'
44 44 &nbsp;
45 45 - if @service.valid? && @service.active
46 46 = link_to 'Test settings', test_project_service_path(@project), class: 'btn btn-small'
... ...
app/views/services/index.html.haml
... ... @@ -8,7 +8,7 @@
8 8 = link_to edit_project_service_path(@project, :gitlab_ci) do
9 9 GitLab CI
10 10 %small Continuous integration server from GitLab
11   - .right
  11 + .pull-right
12 12 - if @gitlab_ci_service.try(:active)
13 13 %small.cgreen
14 14 %i.icon-ok
... ... @@ -21,11 +21,11 @@
21 21 %h4
22 22 Jenkins CI
23 23 %small An extendable open source continuous integration server
24   - .right
  24 + .pull-right
25 25 %small Not implemented yet
26 26 %li.disabled
27 27 %h4
28 28 Campfire
29 29 %small Web-based group chat tool
30   - .right
  30 + .pull-right
31 31 %small Not implemented yet
... ...
app/views/snippets/_blob.html.haml
... ... @@ -3,7 +3,7 @@
3 3 %i.icon-file
4 4 %strong= @snippet.file_name
5 5 %span.options
6   - = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn very_small", target: "_blank"
  6 + = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn btn-tiny", target: "_blank"
7 7 .file_content.code
8 8 - unless @snippet.content.empty?
9 9 %div{class: user_color_scheme_class}
... ...
app/views/snippets/_form.html.haml
... ... @@ -4,7 +4,7 @@
4 4 .snippet-form-holder
5 5 = form_for [@project, @snippet] do |f|
6 6 -if @snippet.errors.any?
7   - .alert-message.block-message.error
  7 + .alert.alert-error
8 8 %ul
9 9 - @snippet.errors.full_messages.each do |msg|
10 10 %li= msg
... ... @@ -27,10 +27,10 @@
27 27 = f.hidden_field :content, class: 'snippet-file-content'
28 28  
29 29 .form-actions
30   - = f.submit 'Save', class: "save-btn btn"
  30 + = f.submit 'Save', class: "btn-save btn"
31 31 = link_to "Cancel", project_snippets_path(@project), class: " btn"
32 32 - unless @snippet.new_record?
33   - .right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
  33 + .pull-right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
34 34  
35 35  
36 36 :javascript
... ...
app/views/snippets/index.html.haml
... ... @@ -5,7 +5,7 @@
5 5 %small share code pastes with others out of git repository
6 6  
7 7 - if can? current_user, :write_snippet, @project
8   - = link_to new_project_snippet_path(@project), class: "btn small add_new right", title: "New Snippet" do
  8 + = link_to new_project_snippet_path(@project), class: "btn btn-small add_new pull-right", title: "New Snippet" do
9 9 Add new snippet
10 10 %br
11 11 %table
... ...
app/views/snippets/show.html.haml
... ... @@ -4,7 +4,7 @@
4 4 = @snippet.title
5 5 %small= @snippet.file_name
6 6 - if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
7   - = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn small right"
  7 + = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right"
8 8  
9 9 %br
10 10 %div= render 'blob'
... ...
app/views/team_members/_form.html.haml
... ... @@ -3,7 +3,7 @@
3 3 %hr
4 4 = form_for @user_project_relation, as: :team_member, url: project_team_members_path(@project) do |f|
5 5 -if @user_project_relation.errors.any?
6   - .alert-message.block-message.error
  6 + .alert.alert-error
7 7 %ul
8 8 - @user_project_relation.errors.full_messages.each do |msg|
9 9 %li= msg
... ... @@ -19,5 +19,5 @@
19 19 .input= select_tag :project_access, options_for_select(Project.access_options, @user_project_relation.project_access), class: "project-access-select chosen"
20 20  
21 21 .actions
22   - = f.submit 'Save', class: "btn save-btn"
23   - = link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn"
  22 + = f.submit 'Add users', class: "btn btn-create"
  23 + = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel"
... ...
app/views/team_members/_show.html.haml
... ... @@ -10,19 +10,19 @@
10 10 %br
11 11 %small.cgray= user.email
12 12  
13   - .span5.right
  13 + .span5.pull-right
14 14 - if allow_admin
15 15 .left
16 16 = form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f|
17 17 = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2"
18   - .right
  18 + .pull-right
19 19 - if current_user == user
20 20 %span.btn.disabled This is you!
21 21 - if @project.namespace_owner == user
22   - %span.btn.disabled.success Owner
  22 + %span.btn.disabled Owner
23 23 - elsif user.blocked
24 24 %span.btn.disabled.blocked Blocked
25 25 - elsif allow_admin
26   - = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "very_small btn danger" do
  26 + = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do
27 27 %i.icon-minus.icon-white
28 28  
... ...
app/views/team_members/_show_team.html.haml
... ... @@ -7,9 +7,9 @@
7 7 %br
8 8 %small.cgray Members: #{team.members.count}
9 9  
10   - .span5.right
11   - .right
  10 + .span5.pull-right
  11 + .pull-right
12 12 - if allow_admin
13 13 .left
14   - = link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn danger small" do
  14 + = link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn btn-remove small" do
15 15 %i.icon-minus.icon-white
... ...
app/views/team_members/import.html.haml
... ... @@ -12,6 +12,6 @@
12 12 .input= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "chosen xxlarge", required: true)
13 13  
14 14 .actions
15   - = submit_tag 'Import', class: "btn save-btn"
16   - = link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn"
  15 + = submit_tag 'Import', class: "btn btn-save"
  16 + = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel"
17 17  
... ...
app/views/team_members/index.html.haml
... ... @@ -7,12 +7,12 @@
7 7 %strong= link_to "here", help_permissions_path, class: "vlink"
8 8  
9 9 - if can? current_user, :admin_team_member, @project
10   - %span.right
11   - = link_to import_project_team_members_path(@project), class: "btn small grouped", title: "Import team from another project" do
  10 + %span.pull-right
  11 + = link_to import_project_team_members_path(@project), class: "btn btn-small grouped", title: "Import team from another project" do
12 12 Import team from another project
13   - = link_to available_project_teams_path(@project), class: "btn small grouped", title: "Assign project to team of users" do
  13 + = link_to available_project_teams_path(@project), class: "btn btn-small grouped", title: "Assign project to team of users" do
14 14 Assign project to Team of users
15   - = link_to new_project_team_member_path(@project), class: "btn success small grouped", title: "New Team Member" do
  15 + = link_to new_project_team_member_path(@project), class: "btn btn-primary small grouped", title: "New Team Member" do
16 16 New Team Member
17 17  
18 18 %hr
... ...
app/views/team_members/show.html.haml
... ... @@ -2,7 +2,7 @@
2 2  
3 3 .team_member_show
4 4 - if can? current_user, :admin_project, @project
5   - = link_to 'Remove from team', project_team_member_path(@project, @member), confirm: 'Are you sure?', method: :delete, class: "right btn danger"
  5 + = link_to 'Remove from team', project_team_member_path(@project, @member), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove pull-right"
6 6 .profile_avatar_holder
7 7 = image_tag gravatar_icon(@member.email, 60), class: "borders"
8 8 %h3.page_title
... ...
app/views/teams/_filter.html.haml
... ... @@ -25,9 +25,9 @@
25 25 %li{class: ("active" if params[:project_id] == project.id.to_s)}
26 26 = link_to team_filter_path(entity, project_id: project.id) do
27 27 = project.name_with_namespace
28   - %small.right= entities_per_project(project, entity)
  28 + %small.pull-right= entities_per_project(project, entity)
29 29  
30 30 %fieldset
31 31 %hr
32   - = link_to "Reset", team_filter_path(entity), class: 'btn right'
  32 + = link_to "Reset", team_filter_path(entity), class: 'btn pull-right'
33 33  
... ...
app/views/teams/_projects.html.haml
... ... @@ -4,8 +4,8 @@
4 4 %small
5 5 (#{projects.count})
6 6 - if can? current_user, :manage_user_team, @team
7   - %span.right
8   - = link_to new_team_project_path(@team), class: "btn very_small info" do
  7 + %span.pull-right
  8 + = link_to new_team_project_path(@team), class: "btn btn-tiny info" do
9 9 %i.icon-plus
10 10 Assign Project
11 11 %ul.well-list
... ...
app/views/teams/edit.html.haml
1 1 %h3.page_title= "Edit Team #{@team.name}"
2 2 %hr
3   -= form_for @team, url: teams_path do |f|
  3 += form_for @team, url: team_path(@team) do |f|
4 4 - if @team.errors.any?
5   - .alert-message.block-message.error
  5 + .alert.alert-error
6 6 %span= @team.errors.full_messages.first
7 7 .clearfix
8 8 = f.label :name do
... ... @@ -15,8 +15,6 @@
15 15 Team path is
16 16 .input
17 17 = f.text_field :path, placeholder: "opensource", class: "xxlarge left"
18   - .clearfix
19   - .input.span3.center
20   - = f.submit 'Save team changes', class: "btn primary"
21   - .input.span3.center
22   - = link_to 'Delete team', team_path(@team), method: :delete, confirm: "You are shure?", class: "btn danger"
  18 + .form-actions
  19 + = f.submit 'Save team changes', class: "btn btn-primary"
  20 + = link_to 'Delete team', team_path(@team), method: :delete, confirm: "You are shure?", class: "btn btn-remove pull-right"
... ...
app/views/teams/issues.html.haml
1 1 %h3.page_title
2 2 Issues
3 3 %small (in Team projects assigned to Team members)
4   - %small.right #{@issues.total_count} issues
  4 + %small.pull-right #{@issues.total_count} issues
5 5  
6 6 %hr
7 7 .row
... ...
app/views/teams/members/_form.html.haml
1 1 = form_tag admin_team_member_path(@team, @member), method: :put do
2 2 -if @member.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %ul
5 5 - @member.errors.full_messages.each do |msg|
6 6 %li= msg
... ... @@ -16,5 +16,5 @@
16 16  
17 17 %br
18 18 .actions
19   - = submit_tag 'Save', class: "btn primary"
  19 + = submit_tag 'Save', class: "btn btn-save"
20 20 = link_to 'Cancel', :back, class: "btn"
... ...
app/views/teams/members/_show.html.haml
... ... @@ -10,22 +10,22 @@
10 10 %br
11 11 %small.cgray= user.email
12 12  
13   - .span6.right
  13 + .span6.pull-right
14 14 - if allow_admin
15 15 .left.span2
16 16 = form_for(member, as: :team_member, url: team_member_path(@team, user)) do |f|
17 17 = f.select :permission, options_for_select(UsersProject.access_roles, @team.default_projects_access(user)), {}, class: "medium project-access-select span2"
18 18 .left.span2
19 19 %span
20   - Admin access
21 20 = check_box_tag :group_admin, true, @team.admin?(user)
22   - .right
  21 + Admin access
  22 + .pull-right
23 23 - if current_user == user
24 24 %span.btn.disabled This is you!
25 25 - if @team.owner == user
26   - %span.btn.disabled.success Owner
  26 + %span.btn.disabled.btn-success Owner
27 27 - elsif user.blocked
28 28 %span.btn.disabled.blocked Blocked
29 29 - elsif allow_admin
30   - = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "very_small btn danger" do
  30 + = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove" do
31 31 %i.icon-minus.icon-white
... ...
app/views/teams/members/index.html.haml
... ... @@ -6,8 +6,8 @@
6 6 %strong= link_to "here", help_permissions_path, class: "vlink"
7 7  
8 8 - if can? current_user, :manage_user_team, @team
9   - %span.right
10   - = link_to new_team_member_path(@team), class: "btn success small grouped", title: "New Team Member" do
  9 + %span.pull-right
  10 + = link_to new_team_member_path(@team), class: "btn btn-primary small grouped", title: "New Team Member" do
11 11 New Team Member
12 12 %hr
13 13  
... ...
app/views/teams/members/new.html.haml
... ... @@ -25,4 +25,4 @@
25 25 %td
26 26 %span= check_box_tag :group_admin
27 27 %span Admin?
28   - %td= submit_tag 'Add', class: "btn primary", id: :add_members_to_team
  28 + %td= submit_tag 'Add User', class: "btn btn-create", id: :add_members_to_team
... ...
app/views/teams/members/show.html.haml
... ... @@ -3,7 +3,7 @@
3 3  
4 4 .team_member_show
5 5 - if can? current_user, :admin_project, @project
6   - = link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "right btn danger"
  6 + = link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "pull-right btn btn-remove"
7 7 .profile_avatar_holder
8 8 = image_tag gravatar_icon(user.email, 60), class: "borders"
9 9 %h3.page_title
... ...
app/views/teams/merge_requests.html.haml
1 1 %h3.page_title
2 2 Merge Requests
3 3 %small (authored by or assigned to Team members)
4   - %small.right #{@merge_requests.total_count} merge requests
  4 + %small.pull-right #{@merge_requests.total_count} merge requests
5 5  
6 6 %hr
7 7 .row
... ...
app/views/teams/new.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %hr
3 3 = form_for @team, url: teams_path do |f|
4 4 - if @team.errors.any?
5   - .alert-message.block-message.error
  5 + .alert.alert-error
6 6 %span= @team.errors.full_messages.first
7 7 .clearfix
8 8 = f.label :name do
... ... @@ -10,7 +10,7 @@
10 10 .input
11 11 = f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left"
12 12 &nbsp;
13   - = f.submit 'Create team', class: "btn primary"
  13 + = f.submit 'Create team', class: "btn btn-create"
14 14 %hr
15 15 .padded
16 16 %ul
... ...
app/views/teams/projects/_form.html.haml
1 1 = form_tag team_project_path(@team, @project), method: :put do
2 2 -if @project.errors.any?
3   - .alert-message.block-message.error
  3 + .alert.alert-error
4 4 %ul
5 5 - @project.errors.full_messages.each do |msg|
6 6 %li= msg
... ... @@ -12,5 +12,5 @@
12 12  
13 13 %br
14 14 .actions
15   - = submit_tag 'Save', class: "btn primary"
16   - = link_to 'Cancel', :back, class: "btn"
  15 + = submit_tag 'Save', class: "btn btn-save"
  16 + = link_to 'Cancel', :back, class: "btn btn-cancel"
... ...
app/views/teams/projects/edit.html.haml
1   -%h3
2   - Edit max access in #{@project.name} for #{@team.name} team
  1 +%h3.page_title
  2 + Edit max access in #{link_to @project.name_with_namespace, @project} for #{link_to(@team.name, team_path(@team))} team
3 3  
4 4 %hr
5   -%table.zebra-striped
6   - %tr
7   - %td Project:
8   - %td= @project.name
9   - %tr
10   - %td Team:
11   - %td= @team.name
12   - %tr
13   - %td Since:
14   - %td= assigned_since(@team, @project).stamp("Nov 11, 2010")
15 5  
16 6 = render 'form'
... ...
app/views/teams/projects/index.html.haml
... ... @@ -5,8 +5,8 @@
5 5 %strong= link_to "here", help_permissions_path, class: "vlink"
6 6  
7 7 - if current_user.can?(:manage_user_team, @team) && @avaliable_projects.any?
8   - %span.right
9   - = link_to new_team_project_path(@team), class: "btn success small grouped", title: "New Team Member" do
  8 + %span.pull-right
  9 + = link_to new_team_project_path(@team), class: "btn btn-primary small grouped", title: "New Team Member" do
10 10 Assign project to Team
11 11  
12 12 %hr
... ... @@ -29,8 +29,8 @@
29 29  
30 30 - if current_user.can?(:admin_user_team, @team)
31 31 %td.bgred
32   - = link_to 'Edit max access', edit_team_project_path(@team, project), class: "btn small"
33   - = link_to 'Relegate', team_project_path(@team, project), confirm: 'Remove project from team and move to global namespace. Are you sure?', method: :delete, class: "btn danger small"
  32 + = link_to 'Edit max access', edit_team_project_path(@team, project), class: "btn btn-small"
  33 + = link_to 'Relegate', team_project_path(@team, project), confirm: 'Remove project from team and move to global namespace. Are you sure?', method: :delete, class: "btn btn-remove small"
34 34  
35 35 - else
36 36 %p.nothing_here_message This team has no projects yet
... ...
app/views/teams/projects/new.html.haml
... ... @@ -20,4 +20,4 @@
20 20 %tr
21 21 %td= select_tag :project_ids, options_from_collection_for_select(@avaliable_projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
22 22 %td= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen span3" }
23   - %td= submit_tag 'Add', class: "btn primary", id: :assign_projects_to_team
  23 + %td= submit_tag 'Add Project', class: "btn btn-create", id: :assign_projects_to_team
... ...
app/views/teams/show.html.haml
1 1 .projects
2 2 .activities.span8
3   - = link_to dashboard_path, class: 'btn very_small' do
  3 + = link_to dashboard_path, class: 'btn btn-tiny' do
4 4 &larr; To dashboard
5 5 &nbsp;
6 6 %span.cgray Events and projects are filtered in scope of team
... ...
app/views/tree/_blob_actions.html.haml
1 1 .btn-group.tree-btn-group
2 2 -# only show edit link for text files
3 3 - if @tree.text?
4   - = link_to "edit", edit_project_tree_path(@project, @id), class: "btn very_small", disabled: !allowed_tree_edit?
5   - = link_to "raw", project_blob_path(@project, @id), class: "btn very_small", target: "_blank"
  4 + = link_to "edit", edit_project_tree_path(@project, @id), class: "btn btn-tiny", disabled: !allowed_tree_edit?
  5 + = link_to "raw", project_blob_path(@project, @id), class: "btn btn-tiny", target: "_blank"
6 6 -# only show normal/blame view links for text files
7 7 - if @tree.text?
8 8 - if current_page? project_blame_path(@project, @id)
9   - = link_to "normal view", project_tree_path(@project, @id), class: "btn very_small"
  9 + = link_to "normal view", project_tree_path(@project, @id), class: "btn btn-tiny"
10 10 - else
11   - = link_to "blame", project_blame_path(@project, @id), class: "btn very_small"
12   - = link_to "history", project_commits_path(@project, @id), class: "btn very_small"
  11 + = link_to "blame", project_blame_path(@project, @id), class: "btn btn-tiny"
  12 + = link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny"
... ...
app/views/tree/_head.html.haml
... ... @@ -3,5 +3,5 @@
3 3 = render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: @path}
4 4 = nav_link(controller: :tree) do
5 5 = link_to 'Source', project_tree_path(@project, @ref)
6   - %li.right
  6 + %li.pull-right
7 7 = render "shared/clone_panel"
... ...
app/views/tree/_tree.html.haml
... ... @@ -24,7 +24,7 @@
24 24 %th Name
25 25 %th Last Update
26 26 %th Last Commit
27   - %th= link_to "history", project_commits_path(@project, @id), class: "btn very_small right"
  27 + %th= link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny pull-right"
28 28  
29 29 - if tree.up_dir?
30 30 %tr.tree-item
... ...
app/views/tree/edit.html.haml
... ... @@ -10,7 +10,7 @@
10 10 %strong= @ref
11 11 %span.options
12 12 .btn-group.tree-btn-group
13   - = link_to "Cancel", project_tree_path(@project, @id), class: "btn very_small cancel-btn", confirm: "Are you sure?"
  13 + = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-tiny btn-cancel", confirm: "Are you sure?"
14 14 .file_content.code
15 15 %pre#editor= @tree.data
16 16  
... ... @@ -27,7 +27,7 @@
27 27 .message
28 28 to branch
29 29 %strong= @ref
30   - = link_to "Cancel", project_tree_path(@project, @id), class: "btn cancel-btn", confirm: "Are you sure?"
  30 + = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-cancel", confirm: "Are you sure?"
31 31  
32 32 :javascript
33 33 var ace_mode = "#{@tree.language.try(:ace_mode)}";
... ...
app/views/users/_profile.html.haml
... ... @@ -4,20 +4,20 @@
4 4 %ul.well-list
5 5 %li
6 6 %strong Email
7   - %span.right= mail_to @user.email
  7 + %span.pull-right= mail_to @user.email
8 8 - unless @user.skype.blank?
9 9 %li
10 10 %strong Skype
11   - %span.right= @user.skype
  11 + %span.pull-right= @user.skype
12 12 - unless @user.linkedin.blank?
13 13 %li
14 14 %strong LinkedIn
15   - %span.right= @user.linkedin
  15 + %span.pull-right= @user.linkedin
16 16 - unless @user.twitter.blank?
17 17 %li
18 18 %strong Twitter
19   - %span.right= @user.twitter
  19 + %span.pull-right= @user.twitter
20 20 - unless @user.bio.blank?
21 21 %li
22 22 %strong Bio
23   - %span.right= @user.bio
  23 + %span.pull-right= @user.bio
... ...
app/views/users/_projects.html.haml
... ... @@ -9,7 +9,7 @@
9 9 \/
10 10 %strong.well-title
11 11 = truncate(project.name, length: 45)
12   - %span.right.light
  12 + %span.pull-right.light
13 13 - if project.owner == @user
14 14 %i.icon-wrench
15 15 - tm = project.team.get_tm(@user.id)
... ...
app/views/users/show.html.haml
... ... @@ -4,8 +4,8 @@
4 4 = image_tag gravatar_icon(@user.email, 90), class: "avatar s90"
5 5 = @user.name
6 6 - if @user == current_user
7   - .right
8   - = link_to profile_path, class: 'btn small' do
  7 + .pull-right
  8 + = link_to profile_path, class: 'btn btn-small' do
9 9 %i.icon-edit
10 10 Edit Profile
11 11 %br
... ...
app/views/wikis/_form.html.haml
... ... @@ -23,5 +23,5 @@
23 23 = f.label :content
24 24 .input= f.text_area :content, class: 'span8 js-gfm-input'
25 25 .actions
26   - = f.submit 'Save', class: "save-btn btn"
27   - = link_to "Cancel", project_wiki_path(@project, :index), class: "btn cancel-btn"
  26 + = f.submit 'Save', class: "btn-save btn"
  27 + = link_to "Cancel", project_wiki_path(@project, :index), class: "btn btn-cancel"
... ...
app/views/wikis/edit.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %hr
3 3 = render 'form'
4 4  
5   -.right
  5 +.pull-right
6 6 - if can? current_user, :admin_wiki, @project
7   - = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn small danger" do
  7 + = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn btn-small btn-remove" do
8 8 Delete this page
9 9 \ No newline at end of file
... ...
app/views/wikis/show.html.haml
1 1 %h3.page_title
2 2 = @wiki.title
3   - %span.right
4   - = link_to pages_project_wikis_path(@project), class: "btn small grouped" do
  3 + %span.pull-right
  4 + = link_to pages_project_wikis_path(@project), class: "btn btn-small grouped" do
5 5 Pages
6 6 - if can? current_user, :write_wiki, @project
7   - = link_to history_project_wiki_path(@project, @wiki), class: "btn small grouped" do
  7 + = link_to history_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do
8 8 History
9   - = link_to edit_project_wiki_path(@project, @wiki), class: "btn small grouped" do
  9 + = link_to edit_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do
10 10 %i.icon-edit
11 11 Edit
12 12 %br
... ...
app/workers/post_receive.rb
... ... @@ -27,11 +27,15 @@ class PostReceive
27 27 User.find_by_email(email) if email
28 28 elsif /^[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}$/.match(identifier)
29 29 User.find_by_email(identifier)
30   - else
31   - Key.find_by_identifier(identifier).try(:user)
  30 + elsif identifier =~ /key/
  31 + key_id = identifier.gsub("key-", "")
  32 + Key.find_by_id(key_id).try(:user)
32 33 end
33 34  
34   - return false unless user
  35 + unless user
  36 + Gitlab::GitLogger.error("POST-RECEIVE: Triggered hook for non-existing user \"#{identifier} \"")
  37 + return false
  38 + end
35 39  
36 40 project.trigger_post_receive(oldrev, newrev, ref, user)
37 41 end
... ...
config/database.yml.postgresql
... ... @@ -6,7 +6,7 @@ production:
6 6 encoding: unicode
7 7 database: gitlabhq_production
8 8 pool: 5
9   - username: postgres
  9 + username: gitlab
10 10 password:
11 11 # host: localhost
12 12 # port: 5432
... ...
config/gitlab.yml.example
... ... @@ -96,7 +96,7 @@ omniauth:
96 96 # GitLab Satellites
97 97 satellites:
98 98 # Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
99   - path: /home/gitlab/gitlab-satellites/
  99 + path: /home/git/gitlab-satellites/
100 100  
101 101 ## Backup settings
102 102 backup:
... ... @@ -105,8 +105,6 @@ backup:
105 105  
106 106 ## Gitolite settings
107 107 gitolite:
108   - admin_uri: git@localhost:gitolite-admin
109   -
110 108 # REPOS_PATH MUST NOT BE A SYMLINK!!!
111 109 repos_path: /home/git/repositories/
112 110 hooks_path: /home/git/.gitolite/hooks/
... ...
config/initializers/1_settings.rb
... ... @@ -51,7 +51,7 @@ Settings.gitlab[&#39;protocol&#39;] ||= Settings.gitlab.https ? &quot;https&quot; : &quot;http&quot;
51 51 Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}"
52 52 Settings.gitlab['support_email'] ||= Settings.gitlab.email_from
53 53 Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url)
54   -Settings.gitlab['user'] ||= 'gitlab'
  54 +Settings.gitlab['user'] ||= 'git'
55 55 Settings.gitlab['signup_enabled'] ||= false
56 56  
57 57 Settings['gravatar'] ||= Settingslogic.new({})
... ...
config/initializers/5_backend.rb
1 1 # GIT over HTTP
2 2 require Rails.root.join("lib", "gitlab", "backend", "grack_auth")
3 3  
4   -# GITOLITE backend
5   -require Rails.root.join("lib", "gitlab", "backend", "gitolite")
  4 +# GIT over SSH
  5 +require Rails.root.join("lib", "gitlab", "backend", "shell")
... ...
config/routes.rb
... ... @@ -130,7 +130,7 @@ Gitlab::Application.routes.draw do
130 130 #
131 131 # Groups Area
132 132 #
133   - resources :groups, constraints: { id: /[^\/]+/ }, only: [:show, :new, :create] do
  133 + resources :groups, constraints: { id: /[^\/]+/ } do
134 134 member do
135 135 get :issues
136 136 get :merge_requests
... ... @@ -164,7 +164,6 @@ Gitlab::Application.routes.draw do
164 164 resources :projects, constraints: { id: /[a-zA-Z.0-9_\-\/]+/ }, except: [:new, :create, :index], path: "/" do
165 165 member do
166 166 get "wall"
167   - get "graph"
168 167 get "files"
169 168 end
170 169  
... ... @@ -174,6 +173,7 @@ Gitlab::Application.routes.draw do
174 173 resources :compare, only: [:index, :create]
175 174 resources :blame, only: [:show], constraints: {id: /.+/}
176 175 resources :blob, only: [:show], constraints: {id: /.+/}
  176 + resources :graph, only: [:show], constraints: {id: /.+/}
177 177 match "/compare/:from...:to" => "compare#show", as: "compare",
178 178 :via => [:get, :post], constraints: {from: /.+/, to: /.+/}
179 179  
... ...
config/unicorn.rb.example
... ... @@ -2,7 +2,7 @@
2 2 # note that config/gitlab.yml web path should also be changed
3 3 # ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
4 4  
5   -app_dir = "/home/gitlab/gitlab/"
  5 +app_dir = "/home/git/gitlab/"
6 6 worker_processes 2
7 7 working_directory app_dir
8 8  
... ...
db/migrate/20130131070232_remove_private_flag_from_project.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class RemovePrivateFlagFromProject < ActiveRecord::Migration
  2 + def up
  3 + remove_column :projects, :private_flag
  4 + end
  5 +
  6 + def down
  7 + add_column :projects, :private_flag, :boolean, default: true, null: false
  8 + end
  9 +end
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12 # It's strongly recommended to check this file into your version control system.
13 13  
14   -ActiveRecord::Schema.define(:version => 20130125090214) do
  14 +ActiveRecord::Schema.define(:version => 20130131070232) do
15 15  
16 16 create_table "events", :force => true do |t|
17 17 t.string "target_type"
... ... @@ -147,7 +147,6 @@ ActiveRecord::Schema.define(:version =&gt; 20130125090214) do
147 147 t.text "description"
148 148 t.datetime "created_at", :null => false
149 149 t.datetime "updated_at", :null => false
150   - t.boolean "private_flag", :default => true, :null => false
151 150 t.integer "creator_id"
152 151 t.string "default_branch"
153 152 t.boolean "issues_enabled", :default => true, :null => false
... ...
doc/api/README.md
... ... @@ -32,6 +32,7 @@ When listing resources you can pass the following parameters:
32 32 + [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md)
33 33 + [Session](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/session.md)
34 34 + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md)
  35 ++ [Groups](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/groups.md)
35 36 + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md)
36 37 + [Repositories](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/repositories.md)
37 38 + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md)
... ...
doc/api/groups.md 0 → 100644
... ... @@ -0,0 +1,45 @@
  1 +## List project groups
  2 +
  3 +Get a list of groups. (As user: my groups, as admin: all groups)
  4 +
  5 +```
  6 +GET /groups
  7 +```
  8 +
  9 +```json
  10 +[
  11 + {
  12 + "id": 1,
  13 + "name": "Foobar Group",
  14 + "path": "foo-bar",
  15 + "owner_id": 18
  16 + }
  17 +]
  18 +```
  19 +
  20 +## Details of group
  21 +
  22 +Get all details of a group.
  23 +
  24 +```
  25 +GET /groups/:id
  26 +```
  27 +
  28 +Parameters:
  29 +
  30 ++ `id` (required) - The ID of a group
  31 +
  32 +## New group
  33 +
  34 +Create a new project group. Available only for admin
  35 +
  36 +```
  37 +POST /groups
  38 +```
  39 +
  40 +Parameters:
  41 ++ `name` (required) - Email
  42 ++ `path` - Password
  43 +
  44 +Will return created group with status `201 Created` on success, or `404 Not found` on fail.
  45 +
... ...
doc/api/notes.md
... ... @@ -30,6 +30,19 @@ Parameters:
30 30  
31 31 + `id` (required) - The ID of a project
32 32  
  33 +### List merge request notes
  34 +
  35 +Get a list of merge request notes.
  36 +
  37 +```
  38 +GET /projects/:id/merge_requests/:merge_request_id/notes
  39 +```
  40 +
  41 +Parameters:
  42 +
  43 ++ `id` (required) - The ID of a project
  44 ++ `merge_request_id` (required) - The ID of an merge request
  45 +
33 46 ### List issue notes
34 47  
35 48 Get a list of issue notes.
... ...
doc/api/projects.md
... ... @@ -22,6 +22,8 @@ GET /projects
22 22 "created_at": "2012-05-23T08:00:58Z"
23 23 },
24 24 "private": true,
  25 + "path": "rails",
  26 + "path_with_namespace": "rails/rails",
25 27 "issues_enabled": false,
26 28 "merge_requests_enabled": false,
27 29 "wall_enabled": true,
... ... @@ -42,6 +44,8 @@ GET /projects
42 44 "created_at": "2012-05-23T08:00:58Z"
43 45 },
44 46 "private": true,
  47 + "path": "gitlab",
  48 + "path_with_namespace": "randx/gitlab",
45 49 "issues_enabled": true,
46 50 "merge_requests_enabled": true,
47 51 "wall_enabled": true,
... ... @@ -78,6 +82,8 @@ Parameters:
78 82 "created_at": "2012-05-23T08:00:58Z"
79 83 },
80 84 "private": true,
  85 + "path": "gitlab",
  86 + "path_with_namespace": "randx/gitlab",
81 87 "issues_enabled": true,
82 88 "merge_requests_enabled": true,
83 89 "wall_enabled": true,
... ...
doc/api/repositories.md
... ... @@ -33,7 +33,8 @@ Parameters:
33 33 },
34 34 "authored_date": "2012-06-27T05:51:39-07:00",
35 35 "committed_date": "2012-06-28T03:44:20-07:00"
36   - }
  36 + },
  37 + "protected": true
37 38 }
38 39 ]
39 40 ```
... ... @@ -73,7 +74,88 @@ Parameters:
73 74 },
74 75 "authored_date": "2012-06-27T05:51:39-07:00",
75 76 "committed_date": "2012-06-28T03:44:20-07:00"
76   - }
  77 + },
  78 + "protected": true
  79 +}
  80 +```
  81 +
  82 +## Protect a project repository branch
  83 +
  84 +Protect a single project repository branch.
  85 +
  86 +```
  87 +PUT /projects/:id/repository/branches/:branch/protect
  88 +```
  89 +
  90 +Parameters:
  91 +
  92 ++ `id` (required) - The ID of a project
  93 ++ `branch` (required) - The name of the branch
  94 +
  95 +```json
  96 +{
  97 + "name": "master",
  98 + "commit": {
  99 + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
  100 + "parents": [
  101 + {
  102 + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
  103 + }
  104 + ],
  105 + "tree": "46e82de44b1061621357f24c05515327f2795a95",
  106 + "message": "add projects API",
  107 + "author": {
  108 + "name": "John Smith",
  109 + "email": "john@example.com"
  110 + },
  111 + "committer": {
  112 + "name": "John Smith",
  113 + "email": "john@example.com"
  114 + },
  115 + "authored_date": "2012-06-27T05:51:39-07:00",
  116 + "committed_date": "2012-06-28T03:44:20-07:00"
  117 + },
  118 + "protected": true
  119 +}
  120 +```
  121 +
  122 +## Unprotect a project repository branch
  123 +
  124 +Unprotect a single project repository branch.
  125 +
  126 +```
  127 +PUT /projects/:id/repository/branches/:branch/unprotect
  128 +```
  129 +
  130 +Parameters:
  131 +
  132 ++ `id` (required) - The ID of a project
  133 ++ `branch` (required) - The name of the branch
  134 +
  135 +```json
  136 +{
  137 + "name": "master",
  138 + "commit": {
  139 + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
  140 + "parents": [
  141 + {
  142 + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
  143 + }
  144 + ],
  145 + "tree": "46e82de44b1061621357f24c05515327f2795a95",
  146 + "message": "add projects API",
  147 + "author": {
  148 + "name": "John Smith",
  149 + "email": "john@example.com"
  150 + },
  151 + "committer": {
  152 + "name": "John Smith",
  153 + "email": "john@example.com"
  154 + },
  155 + "authored_date": "2012-06-27T05:51:39-07:00",
  156 + "committed_date": "2012-06-28T03:44:20-07:00"
  157 + },
  158 + "protected": false
77 159 }
78 160 ```
79 161  
... ... @@ -110,7 +192,8 @@ Parameters:
110 192 },
111 193 "authored_date": "2012-05-28T04:42:42-07:00",
112 194 "committed_date": "2012-05-28T04:42:42-07:00"
113   - }
  195 + },
  196 + "protected": null
114 197 }
115 198 ]
116 199 ```
... ...
doc/api/users.md
... ... @@ -20,6 +20,8 @@ GET /users
20 20 "linkedin": "",
21 21 "twitter": "",
22 22 "dark_scheme": false,
  23 + "extern_uid": "john.smith",
  24 + "provider": "provider_name",
23 25 "theme_id": 1
24 26 },
25 27 {
... ... @@ -34,6 +36,8 @@ GET /users
34 36 "linkedin": "",
35 37 "twitter": "",
36 38 "dark_scheme": true,
  39 + "extern_uid": "jack.smith",
  40 + "provider": "provider_name",
37 41 "theme_id": 1
38 42 }
39 43 ]
... ... @@ -64,6 +68,8 @@ Parameters:
64 68 "linkedin": "",
65 69 "twitter": "",
66 70 "dark_scheme": false,
  71 + "extern_uid": "john.smith",
  72 + "provider": "provider_name",
67 73 "theme_id": 1
68 74 }
69 75 ```
... ... @@ -84,10 +90,47 @@ Parameters:
84 90 + `linkedin` - Linkedin
85 91 + `twitter` - Twitter account
86 92 + `projects_limit` - Number of projects user can create
  93 ++ `extern_uid` - External UID
  94 ++ `provider` - External provider name
  95 ++ `bio` - User's bio
87 96  
88 97 Will return created user with status `201 Created` on success, or `404 Not
89 98 found` on fail.
90 99  
  100 +## User modification
  101 +Modify user. Available only for admin
  102 +
  103 +```
  104 +PUT /users/:id
  105 +```
  106 +
  107 +Parameters:
  108 ++ `email` - Email
  109 ++ `username` - Username
  110 ++ `name` - Name
  111 ++ `password` - Password
  112 ++ `skype` - Skype ID
  113 ++ `linkedin` - Linkedin
  114 ++ `twitter` - Twitter account
  115 ++ `projects_limit` - Limit projects wich user can create
  116 ++ `extern_uid` - External UID
  117 ++ `provider` - External provider name
  118 ++ `bio` - User's bio
  119 +
  120 +
  121 +Will return created user with status `200 OK` on success, or `404 Not
  122 +found` on fail.
  123 +
  124 +## User deletion
  125 +Delete user. Available only for admin
  126 +
  127 +```
  128 +DELETE /users/:id
  129 +```
  130 +
  131 +Will return deleted user with status `200 OK` on success, or `404 Not
  132 +found` on fail.
  133 +
91 134 ## Current user
92 135  
93 136 Get currently authenticated user.
... ...
doc/install/installation.md
... ... @@ -90,134 +90,75 @@ Install the Bundler Gem:
90 90  
91 91 # 3. System Users
92 92  
93   -Create a user for Git and Gitolite:
  93 +Create a `git` user for Gitlab:
94 94  
95   - sudo adduser \
96   - --system \
97   - --shell /bin/sh \
98   - --gecos 'Git Version Control' \
99   - --group \
100   - --disabled-password \
101   - --home /home/git \
102   - git
  95 + sudo adduser --disabled-login --gecos 'GitLab' git
103 96  
104   -Create a user for GitLab:
  97 +# 4. GitLab shell
105 98  
106   - sudo adduser --disabled-login --gecos 'GitLab' gitlab
107   -
108   - # Add it to the git group
109   - sudo usermod -a -G git gitlab
110   -
111   - # Generate the SSH key
112   - sudo -u gitlab -H ssh-keygen -q -N '' -t rsa -f /home/gitlab/.ssh/id_rsa
113   -
114   -
115   -# 4. Gitolite
116   -
117   -Clone GitLab's fork of the Gitolite source code:
  99 + # login as git
  100 + sudo su git
118 101  
  102 + # go to home directory
119 103 cd /home/git
120   - sudo -u git -H git clone -b gl-v320 https://github.com/gitlabhq/gitolite.git /home/git/gitolite
121   -
122   -Setup Gitolite with GitLab as its admin:
123   -
124   -**Important Note:**
125   -GitLab assumes *full and unshared* control over this Gitolite installation.
126   -
127   - # Add Gitolite scripts to $PATH
128   - sudo -u git -H mkdir /home/git/bin
129   - sudo -u git -H sh -c 'printf "%b\n%b\n" "PATH=\$PATH:/home/git/bin" "export PATH" >> /home/git/.profile'
130   - sudo -u git -H sh -c 'gitolite/install -ln /home/git/bin'
131   -
132   - # Copy the gitlab user's (public) SSH key ...
133   - sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub
134   - sudo chmod 0444 /home/git/gitlab.pub
135   -
136   - # ... and use it as the admin key for the Gitolite setup
137   - sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gitolite setup -pk /home/git/gitlab.pub"
138   -
139   -Fix the directory permissions for the configuration directory:
140   -
141   - # Make sure the Gitolite config dir is owned by git
142   - sudo chmod 750 /home/git/.gitolite/
143   - sudo chown -R git:git /home/git/.gitolite/
144 104  
145   -Fix the directory permissions for the repositories:
  105 + # clone gitlab shell
  106 + git clone https://github.com/gitlabhq/gitlab-shell.git
146 107  
147   - # Make sure the repositories dir is owned by git and it stays that way
148   - sudo chmod -R ug+rwXs,o-rwx /home/git/repositories/
149   - sudo chown -R git:git /home/git/repositories/
  108 + # setup
  109 + cd gitlab-shell
  110 + cp config.yml.example config.yml
  111 + ./bin/install
150 112  
151 113  
152   -## Add domains to list to the list of known hosts
153   -
154   - sudo -u gitlab -H ssh git@localhost
155   - sudo -u gitlab -H ssh git@YOUR_DOMAIN_NAME
156   - sudo -u gitlab -H ssh git@YOUR_GITOLITE_DOMAIN_NAME
157   -
158   -
159   -## Test if everything works so far
160   -
161   - # Clone the admin repo so SSH adds localhost to known_hosts ...
162   - # ... and to be sure your users have access to Gitolite
163   - sudo -u gitlab -H git clone git@localhost:gitolite-admin.git /tmp/gitolite-admin
164   -
165   - # If it succeeded without errors you can remove the cloned repo
166   - sudo rm -rf /tmp/gitolite-admin
167   -
168   -**Important Note:**
169   -If you can't clone the `gitolite-admin` repository: **DO NOT PROCEED WITH INSTALLATION**!
170   -Check the [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide)
171   -and make sure you have followed all of the above steps carefully.
172   -
173 114  
174 115 # 5. Database
175 116  
176   -See `doc/install/databases.md`
  117 +To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install/databases.md`](./databases.md) .
177 118  
178 119  
179 120 # 6. GitLab
180 121  
181   - # We'll install GitLab into home directory of the user "gitlab"
182   - cd /home/gitlab
  122 + # We'll install GitLab into home directory of the user "git"
  123 + cd /home/git
183 124  
184 125 ## Clone the Source
185 126  
186 127 # Clone GitLab repository
187   - sudo -u gitlab -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab
  128 + sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab
188 129  
189 130 # Go to gitlab dir
190   - cd /home/gitlab/gitlab
  131 + cd /home/git/gitlab
191 132  
192 133 # Checkout to stable release
193   - sudo -u gitlab -H git checkout 4-1-stable
  134 + sudo -u git -H git checkout 5-0-stable
194 135  
195 136 **Note:**
196   -You can change `4-1-stable` to `master` if you want the *bleeding edge* version, but
  137 +You can change `5-0-stable` to `master` if you want the *bleeding edge* version, but
197 138 do so with caution!
198 139  
199 140 ## Configure it
200 141  
201   - cd /home/gitlab/gitlab
  142 + cd /home/git/gitlab
202 143  
203 144 # Copy the example GitLab config
204   - sudo -u gitlab -H cp config/gitlab.yml.example config/gitlab.yml
  145 + sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml
205 146  
206 147 # Make sure to change "localhost" to the fully-qualified domain name of your
207 148 # host serving GitLab where necessary
208   - sudo -u gitlab -H vim config/gitlab.yml
  149 + sudo -u git -H vim config/gitlab.yml
209 150  
210 151 # Make sure GitLab can write to the log/ and tmp/ directories
211   - sudo chown -R gitlab log/
212   - sudo chown -R gitlab tmp/
  152 + sudo chown -R git log/
  153 + sudo chown -R git tmp/
213 154 sudo chmod -R u+rwX log/
214 155 sudo chmod -R u+rwX tmp/
215 156  
216 157 # Make directory for satellites
217   - sudo -u gitlab -H mkdir /home/gitlab/gitlab-satellites
  158 + sudo -u git -H mkdir /home/git/gitlab-satellites
218 159  
219 160 # Copy the example Unicorn config
220   - sudo -u gitlab -H cp config/unicorn.rb.example config/unicorn.rb
  161 + sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
221 162  
222 163 **Important Note:**
223 164 Make sure to edit both files to match your setup.
... ... @@ -225,42 +166,29 @@ Make sure to edit both files to match your setup.
225 166 ## Configure GitLab DB settings
226 167  
227 168 # Mysql
228   - sudo -u gitlab cp config/database.yml.mysql config/database.yml
  169 + sudo -u git cp config/database.yml.mysql config/database.yml
229 170  
230 171 # PostgreSQL
231   - sudo -u gitlab cp config/database.yml.postgresql config/database.yml
  172 + sudo -u git cp config/database.yml.postgresql config/database.yml
232 173  
233 174 Make sure to update username/password in config/database.yml.
234 175  
235 176 ## Install Gems
236 177  
237   - cd /home/gitlab/gitlab
  178 + cd /home/git/gitlab
238 179  
239 180 sudo gem install charlock_holmes --version '0.6.9'
240 181  
241 182 # For MySQL (note, the option says "without")
242   - sudo -u gitlab -H bundle install --deployment --without development test postgres
  183 + sudo -u git -H bundle install --deployment --without development test postgres
243 184  
244 185 # Or for PostgreSQL
245   - sudo -u gitlab -H bundle install --deployment --without development test mysql
246   -
247   -## Configure Git
248   -
249   -GitLab needs to be able to commit and push changes to Gitolite. In order to do
250   -that Git requires a username and email. (We recommend using the same address
251   -used for the `email.from` setting in `config/gitlab.yml`)
252   -
253   - sudo -u gitlab -H git config --global user.name "GitLab"
254   - sudo -u gitlab -H git config --global user.email "gitlab@localhost"
255   -
256   -## Setup GitLab Hooks
  186 + sudo -u git -H bundle install --deployment --without development test mysql
257 187  
258   - sudo cp ./lib/hooks/post-receive /home/git/.gitolite/hooks/common/post-receive
259   - sudo chown git:git /home/git/.gitolite/hooks/common/post-receive
260 188  
261 189 ## Initialise Database and Activate Advanced Features
262 190  
263   - sudo -u gitlab -H bundle exec rake gitlab:setup RAILS_ENV=production
  191 + sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
264 192  
265 193  
266 194 ## Install Init Script
... ... @@ -279,11 +207,11 @@ Make GitLab start on boot:
279 207  
280 208 Check if GitLab and its environment is configured correctly:
281 209  
282   - sudo -u gitlab -H bundle exec rake gitlab:env:info RAILS_ENV=production
  210 + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
283 211  
284 212 To make sure you didn't miss anything run a more thorough check with:
285 213  
286   - sudo -u gitlab -H bundle exec rake gitlab:check RAILS_ENV=production
  214 + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
287 215  
288 216 If all items are green, then congratulations on successfully installing GitLab!
289 217 However there are still a few steps left.
... ... @@ -356,7 +284,7 @@ a different host, you can configure its connection string via the
356 284  
357 285 If you are running SSH on a non-standard port, you must change the gitlab user'S SSH config.
358 286  
359   - # Add to /home/gitlab/.ssh/config
  287 + # Add to /home/git/.ssh/config
360 288 host localhost # Give your setup a name (here: override localhost)
361 289 user git # Your remote git user
362 290 port 2222 # Your port number
... ...
doc/install/structure.md
... ... @@ -3,37 +3,23 @@
3 3 This is the directory structure you will end up with following the instructions in the Installation Guide.
4 4  
5 5 |-- home
6   - | |-- gitlab
  6 + | |-- git
7 7 | |-- .ssh
8 8 | |-- gitlab
9 9 | |-- gitlab-satellites
10   - | |-- git
11   - | |-- .gitolite
12   - | |-- .ssh
13   - | |-- bin
14   - | |-- gitolite
  10 + | |-- gitlab-shell
15 11 | |-- repositories
16 12  
17 13  
18   -**/home/gitlab/.ssh**
19   - Contains the Gitolite admin key GitLab uses to configure Gitolite.
  14 +**/home/git/.ssh**
20 15  
21   -**/home/gitlab/gitlab**
  16 +**/home/git/gitlab**
22 17 This is where GitLab lives.
23 18  
24   -**/home/gitlab/gitlab-satellites**
  19 +**/home/git/gitlab-satellites**
25 20 Contains a copy of all repositories with a working tree.
26 21 It's used for merge requests, editing files, etc.
27 22  
28   -**/home/git/.ssh**
29   - Contains the SSH access configuration managed by Gitolite.
30   -
31   -**/home/git/bin**
32   - Contains Gitolite executables.
33   -
34   -**/home/git/gitolite**
35   - This is where Gitolite lives.
36   -
37 23 **/home/git/repositories**
38 24 Holds all your repositories in bare format.
39 25 This is the place Git uses when you pull/push to your projects.
... ...
doc/raketasks/maintenance.md
... ... @@ -81,7 +81,7 @@ Config directory owned by git:git? ... yes
81 81 Config directory access is drwxr-x---? ... yes
82 82 Repo base directory exists? ... yes
83 83 Repo base owned by git:git? ... yes
84   -Repo base access is drwsrws---? ... yes
  84 +Repo base access is drwxrws---? ... yes
85 85 Can clone gitolite-admin? ... yes
86 86 Can commit to gitolite-admin? ... yes
87 87 post-receive hook exists? ... yes
... ...
features/group/group.feature
... ... @@ -24,3 +24,9 @@ Feature: Groups
24 24 When I visit group people page
25 25 And I select user "John" from list with role "Reporter"
26 26 Then I should see user "John" in team list
  27 +
  28 + Scenario: I should see edit group page
  29 + When I visit group settings page
  30 + And I change group name
  31 + Then I should see new group name
  32 +
... ...
features/steps/admin/admin_active_tab.rb
... ... @@ -4,7 +4,7 @@ class AdminActiveTab &lt; Spinach::FeatureSteps
4 4 include SharedActiveTab
5 5  
6 6 Then 'the active main tab should be Home' do
7   - ensure_active_main_tab('Stats')
  7 + ensure_active_main_tab('Home')
8 8 end
9 9  
10 10 Then 'the active main tab should be Projects' do
... ...
features/steps/group/group.rb
... ... @@ -82,6 +82,17 @@ class Groups &lt; Spinach::FeatureSteps
82 82 current_path.should == group_path(Group.last)
83 83 end
84 84  
  85 + And 'I change group name' do
  86 + fill_in 'group_name', :with => 'new-name'
  87 + click_button "Save group"
  88 + end
  89 +
  90 + Then 'I should see new group name' do
  91 + within ".navbar-gitlab" do
  92 + page.should have_content "group: new-name"
  93 + end
  94 + end
  95 +
85 96 protected
86 97  
87 98 def current_group
... ...
features/steps/profile/profile_active_tab.rb
... ... @@ -4,7 +4,7 @@ class ProfileActiveTab &lt; Spinach::FeatureSteps
4 4 include SharedActiveTab
5 5  
6 6 Then 'the active main tab should be Home' do
7   - ensure_active_main_tab('Profile')
  7 + ensure_active_main_tab('Home')
8 8 end
9 9  
10 10 Then 'the active main tab should be Account' do
... ...
features/steps/project/project_active_tab.rb
... ... @@ -7,7 +7,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps
7 7 # Main Tabs
8 8  
9 9 Then 'the active main tab should be Home' do
10   - ensure_active_main_tab(@project.name)
  10 + ensure_active_main_tab('Home')
11 11 end
12 12  
13 13 Then 'the active main tab should be Files' do
... ...
features/steps/project/project_network_graph.rb
... ... @@ -14,6 +14,6 @@ class ProjectNetworkGraph &lt; Spinach::FeatureSteps
14 14 Gitlab::Graph::JsonBuilder.stub(max_count: 10)
15 15  
16 16 project = Project.find_by_name("Shop")
17   - visit graph_project_path(project)
  17 + visit project_graph_path(project, "master")
18 18 end
19 19 end
... ...
features/steps/project/project_team_management.rb
... ... @@ -24,7 +24,7 @@ class ProjectTeamManagement &lt; Spinach::FeatureSteps
24 24 select user.name, :from => "user_ids"
25 25 select "Reporter", :from => "project_access"
26 26 end
27   - click_button "Save"
  27 + click_button "Add users"
28 28 end
29 29  
30 30 Then 'I should see "Mike" in team list as "Reporter"' do
... ...
features/steps/shared/active_tab.rb
... ... @@ -2,7 +2,11 @@ module SharedActiveTab
2 2 include Spinach::DSL
3 3  
4 4 def ensure_active_main_tab(content)
5   - page.find('ul.main_menu li.active').should have_content(content)
  5 + if content == "Home"
  6 + page.find('ul.main_menu li.active').should have_css('i.icon-home')
  7 + else
  8 + page.find('ul.main_menu li.active').should have_content(content)
  9 + end
6 10 end
7 11  
8 12 def ensure_active_sub_tab(content)
... ...
features/steps/shared/paths.rb
... ... @@ -25,6 +25,10 @@ module SharedPaths
25 25 visit people_group_path(current_group)
26 26 end
27 27  
  28 + When 'I visit group settings page' do
  29 + visit edit_group_path(current_group)
  30 + end
  31 +
28 32 # ----------------------------------------
29 33 # Dashboard
30 34 # ----------------------------------------
... ... @@ -141,7 +145,7 @@ module SharedPaths
141 145 # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650)
142 146 Gitlab::Graph::JsonBuilder.stub(max_count: 10)
143 147  
144   - visit graph_project_path(@project)
  148 + visit project_graph_path(@project, root_ref)
145 149 end
146 150  
147 151 Given "I visit my project's issues page" do
... ...
features/support/env.rb
... ... @@ -9,17 +9,12 @@ require &#39;spinach/capybara&#39;
9 9 require 'sidekiq/testing/inline'
10 10  
11 11  
12   -%w(gitolite_stub stubbed_repository valid_commit).each do |f|
  12 +%w(stubbed_repository valid_commit).each do |f|
13 13 require Rails.root.join('spec', 'support', f)
14 14 end
15 15  
16 16 Dir["#{Rails.root}/features/steps/shared/*.rb"].each {|file| require file}
17 17  
18   -#
19   -# Stub gitolite
20   -#
21   -include GitoliteStub
22   -
23 18 WebMock.allow_net_connect!
24 19 #
25 20 # JS driver
... ... @@ -49,6 +44,4 @@ Spinach.hooks.before_run do
49 44 RSpec::Mocks::setup self
50 45  
51 46 include FactoryGirl::Syntax::Methods
52   -
53   - stub_gitolite!
54 47 end
... ...
lib/api.rb
... ... @@ -24,7 +24,8 @@ module Gitlab
24 24 format :json
25 25 error_format :json
26 26 helpers APIHelpers
27   -
  27 +
  28 + mount Groups
28 29 mount Users
29 30 mount Projects
30 31 mount Issues
... ... @@ -32,5 +33,6 @@ module Gitlab
32 33 mount Session
33 34 mount MergeRequests
34 35 mount Notes
  36 + mount Internal
35 37 end
36 38 end
... ...
lib/api/entities.rb
... ... @@ -2,7 +2,7 @@ module Gitlab
2 2 module Entities
3 3 class User < Grape::Entity
4 4 expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter,
5   - :dark_scheme, :theme_id, :blocked, :created_at
  5 + :dark_scheme, :theme_id, :blocked, :created_at, :extern_uid, :provider
6 6 end
7 7  
8 8 class UserBasic < Grape::Entity
... ... @@ -21,6 +21,7 @@ module Gitlab
21 21 expose :id, :name, :description, :default_branch
22 22 expose :owner, using: Entities::UserBasic
23 23 expose :private_flag, as: :private
  24 + expose :path, :path_with_namespace
24 25 expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at
25 26 expose :namespace
26 27 end
... ... @@ -31,8 +32,22 @@ module Gitlab
31 32 end
32 33 end
33 34  
  35 + class Group < Grape::Entity
  36 + expose :id, :name, :path, :owner_id
  37 + end
  38 +
  39 + class GroupDetail < Group
  40 + expose :projects, using: Entities::Project
  41 + end
  42 +
  43 +
34 44 class RepoObject < Grape::Entity
35 45 expose :name, :commit
  46 + expose :protected do |repo, options|
  47 + if options[:project]
  48 + options[:project].protected_branch? repo.name
  49 + end
  50 + end
36 51 end
37 52  
38 53 class RepoCommit < Grape::Entity
... ...
lib/api/groups.rb 0 → 100644
... ... @@ -0,0 +1,56 @@
  1 +module Gitlab
  2 + # groups API
  3 + class Groups < Grape::API
  4 + before { authenticate! }
  5 +
  6 + resource :groups do
  7 + # Get a groups list
  8 + #
  9 + # Example Request:
  10 + # GET /groups
  11 + get do
  12 + if current_user.admin
  13 + @groups = paginate Group
  14 + else
  15 + @groups = paginate current_user.groups
  16 + end
  17 + present @groups, with: Entities::Group
  18 + end
  19 +
  20 + # Create group. Available only for admin
  21 + #
  22 + # Parameters:
  23 + # name (required) - Name
  24 + # path (required) - Path
  25 + # Example Request:
  26 + # POST /groups
  27 + post do
  28 + authenticated_as_admin!
  29 + attrs = attributes_for_keys [:name, :path]
  30 + @group = Group.new(attrs)
  31 + @group.owner = current_user
  32 +
  33 + if @group.save
  34 + present @group, with: Entities::Group
  35 + else
  36 + not_found!
  37 + end
  38 + end
  39 +
  40 + # Get a single group, with containing projects
  41 + #
  42 + # Parameters:
  43 + # id (required) - The ID of a group
  44 + # Example Request:
  45 + # GET /groups/:id
  46 + get ":id" do
  47 + @group = Group.find(params[:id])
  48 + if current_user.admin or current_user.groups.include? @group
  49 + present @group, with: Entities::GroupDetail
  50 + else
  51 + not_found!
  52 + end
  53 + end
  54 + end
  55 + end
  56 +end
... ...
lib/api/internal.rb 0 → 100644
... ... @@ -0,0 +1,49 @@
  1 +module Gitlab
  2 + # Internal access API
  3 + class Internal < Grape::API
  4 + namespace 'internal' do
  5 + #
  6 + # Check if ssh key has access to project code
  7 + #
  8 + get "/allowed" do
  9 + key = Key.find(params[:key_id])
  10 + project = Project.find_with_namespace(params[:project])
  11 + git_cmd = params[:action]
  12 +
  13 + if key.is_deploy_key
  14 + project == key.project && git_cmd == 'git-upload-pack'
  15 + else
  16 + user = key.user
  17 + action = case git_cmd
  18 + when 'git-upload-pack'
  19 + then :download_code
  20 + when 'git-receive-pack'
  21 + then
  22 + if project.protected_branch?(params[:ref])
  23 + :push_code_to_protected_branches
  24 + else
  25 + :push_code
  26 + end
  27 + end
  28 +
  29 + user.can?(action, project)
  30 + end
  31 + end
  32 +
  33 + #
  34 + # Discover user by ssh key
  35 + #
  36 + get "/discover" do
  37 + key = Key.find(params[:key_id])
  38 + present key.user, with: Entities::User
  39 + end
  40 +
  41 + get "/check" do
  42 + {
  43 + api_version: '3'
  44 + }
  45 + end
  46 + end
  47 + end
  48 +end
  49 +
... ...
lib/api/notes.rb
... ... @@ -3,7 +3,7 @@ module Gitlab
3 3 class Notes < Grape::API
4 4 before { authenticate! }
5 5  
6   - NOTEABLE_TYPES = [Issue, Snippet]
  6 + NOTEABLE_TYPES = [Issue, MergeRequest, Snippet]
7 7  
8 8 resource :projects do
9 9 # Get a list of project wall notes
... ...
lib/api/projects.rb
... ... @@ -222,7 +222,7 @@ module Gitlab
222 222 # Example Request:
223 223 # GET /projects/:id/repository/branches
224 224 get ":id/repository/branches" do
225   - present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject
  225 + present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project
226 226 end
227 227  
228 228 # Get a single branch
... ... @@ -234,7 +234,43 @@ module Gitlab
234 234 # GET /projects/:id/repository/branches/:branch
235 235 get ":id/repository/branches/:branch" do
236 236 @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
237   - present @branch, with: Entities::RepoObject
  237 + present @branch, with: Entities::RepoObject, project: user_project
  238 + end
  239 +
  240 + # Protect a single branch
  241 + #
  242 + # Parameters:
  243 + # id (required) - The ID of a project
  244 + # branch (required) - The name of the branch
  245 + # Example Request:
  246 + # PUT /projects/:id/repository/branches/:branch/protect
  247 + put ":id/repository/branches/:branch/protect" do
  248 + @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
  249 + protected = user_project.protected_branches.find_by_name(@branch.name)
  250 +
  251 + unless protected
  252 + user_project.protected_branches.create(:name => @branch.name)
  253 + end
  254 +
  255 + present @branch, with: Entities::RepoObject, project: user_project
  256 + end
  257 +
  258 + # Unprotect a single branch
  259 + #
  260 + # Parameters:
  261 + # id (required) - The ID of a project
  262 + # branch (required) - The name of the branch
  263 + # Example Request:
  264 + # PUT /projects/:id/repository/branches/:branch/unprotect
  265 + put ":id/repository/branches/:branch/unprotect" do
  266 + @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
  267 + protected = user_project.protected_branches.find_by_name(@branch.name)
  268 +
  269 + if protected
  270 + protected.destroy
  271 + end
  272 +
  273 + present @branch, with: Entities::RepoObject, project: user_project
238 274 end
239 275  
240 276 # Get a project repository tags
... ...
lib/api/users.rb
... ... @@ -34,11 +34,14 @@ module Gitlab
34 34 # linkedin - Linkedin
35 35 # twitter - Twitter account
36 36 # projects_limit - Number of projects user can create
  37 + # extern_uid - External authentication provider UID
  38 + # provider - External provider
  39 + # bio - Bio
37 40 # Example Request:
38 41 # POST /users
39 42 post do
40 43 authenticated_as_admin!
41   - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username]
  44 + attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio]
42 45 user = User.new attrs, as: :admin
43 46 if user.save
44 47 present user, with: Entities::User
... ... @@ -46,6 +49,48 @@ module Gitlab
46 49 not_found!
47 50 end
48 51 end
  52 +
  53 + # Update user. Available only for admin
  54 + #
  55 + # Parameters:
  56 + # email - Email
  57 + # name - Name
  58 + # password - Password
  59 + # skype - Skype ID
  60 + # linkedin - Linkedin
  61 + # twitter - Twitter account
  62 + # projects_limit - Limit projects wich user can create
  63 + # extern_uid - External authentication provider UID
  64 + # provider - External provider
  65 + # bio - Bio
  66 + # Example Request:
  67 + # PUT /users/:id
  68 + put ":id" do
  69 + authenticated_as_admin!
  70 + attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio]
  71 + user = User.find_by_id(params[:id])
  72 +
  73 + if user && user.update_attributes(attrs)
  74 + present user, with: Entities::User
  75 + else
  76 + not_found!
  77 + end
  78 + end
  79 +
  80 + # Delete user. Available only for admin
  81 + #
  82 + # Example Request:
  83 + # DELETE /users/:id
  84 + delete ":id" do
  85 + authenticated_as_admin!
  86 + user = User.find_by_id(params[:id])
  87 +
  88 + if user
  89 + user.destroy
  90 + else
  91 + not_found!
  92 + end
  93 + end
49 94 end
50 95  
51 96 resource :user do
... ...
lib/extracts_path.rb
... ... @@ -54,9 +54,10 @@ module ExtractsPath
54 54 input.gsub!(/^#{Gitlab.config.gitlab.relative_url_root}/, "")
55 55 # Remove project, actions and all other staff from path
56 56 input.gsub!(/^\/#{Regexp.escape(@project.path_with_namespace)}/, "")
57   - input.gsub!(/^\/(tree|commits|blame|blob|refs)\//, "") # remove actions
  57 + input.gsub!(/^\/(tree|commits|blame|blob|refs|graph)\//, "") # remove actions
58 58 input.gsub!(/\?.*$/, "") # remove stamps suffix
59 59 input.gsub!(/.atom$/, "") # remove rss feed
  60 + input.gsub!(/.json$/, "") # remove json suffix
60 61 input.gsub!(/\/edit$/, "") # remove edit route part
61 62  
62 63 if input.match(/^([[:alnum:]]{40})(.+)/)
... ...
lib/gitlab/backend/gitolite.rb
... ... @@ -1,90 +0,0 @@
1   -require_relative 'gitolite_config'
2   -
3   -module Gitlab
4   - class Gitolite
5   - class AccessDenied < StandardError; end
6   -
7   - def config
8   - Gitlab::GitoliteConfig.new
9   - end
10   -
11   - # Update gitolite config with new key
12   - #
13   - # Ex.
14   - # set_key("m_gitlab_com_12343", "sha-rsa ...", [2, 3, 6])
15   - #
16   - def set_key(key_id, key_content, project_ids)
17   - projects = Project.where(id: project_ids)
18   -
19   - config.apply do |config|
20   - config.write_key(key_id, key_content)
21   - config.update_projects(projects)
22   - end
23   - end
24   -
25   - # Remove ssh key from gitolite config
26   - #
27   - # Ex.
28   - # remove_key("m_gitlab_com_12343", [2, 3, 6])
29   - #
30   - def remove_key(key_id, project_ids)
31   - projects = Project.where(id: project_ids)
32   -
33   - config.apply do |config|
34   - config.rm_key(key_id)
35   - config.update_projects(projects)
36   - end
37   - end
38   -
39   - # Update project config in gitolite by project id
40   - #
41   - # Ex.
42   - # update_repository(23)
43   - #
44   - def update_repository(project_id)
45   - project = Project.find(project_id)
46   - config.update_project!(project)
47   - end
48   -
49   - def move_repository(old_repo, project)
50   - config.apply do |config|
51   - config.clean_repo(old_repo)
52   - config.update_project(project)
53   - end
54   - end
55   -
56   - # Remove repository from gitolite
57   - #
58   - # name - project path with namespace
59   - #
60   - # Ex.
61   - # remove_repository("gitlab/gitlab-ci")
62   - #
63   - def remove_repository(name)
64   - config.destroy_project!(name)
65   - end
66   -
67   - # Update projects configs in gitolite by project ids
68   - #
69   - # Ex.
70   - # update_repositories([1, 4, 6])
71   - #
72   - def update_repositories(project_ids)
73   - projects = Project.where(id: project_ids)
74   -
75   - config.apply do |config|
76   - config.update_projects(projects)
77   - end
78   - end
79   -
80   - def url_to_repo path
81   - Gitlab.config.gitolite.ssh_path_prefix + "#{path}.git"
82   - end
83   -
84   - def enable_automerge
85   - config.admin_all_repo!
86   - end
87   -
88   - alias_method :create_repository, :update_repository
89   - end
90   -end
lib/gitlab/backend/gitolite_config.rb
... ... @@ -1,241 +0,0 @@
1   -require 'gitolite'
2   -require 'timeout'
3   -require 'fileutils'
4   -
5   -module Gitlab
6   - class GitoliteConfig
7   - include Gitlab::Popen
8   -
9   - class PullError < StandardError; end
10   - class PushError < StandardError; end
11   - class BrokenGitolite < StandardError; end
12   -
13   - attr_reader :config_tmp_dir, :tmp_dir, :ga_repo, :conf
14   -
15   - def initialize
16   - @tmp_dir = Rails.root.join("tmp").to_s
17   - @config_tmp_dir = File.join(@tmp_dir,"gitlabhq-gitolite-#{Time.now.to_i}")
18   - end
19   -
20   - def ga_repo
21   - @ga_repo ||= ::Gitolite::GitoliteAdmin.new(
22   - File.join(config_tmp_dir,'gitolite'),
23   - conf: Gitlab.config.gitolite.config_file
24   - )
25   - end
26   -
27   - def apply
28   - Timeout::timeout(30) do
29   - File.open(File.join(tmp_dir, "gitlabhq-gitolite.lock"), "w+") do |f|
30   - begin
31   - # Set exclusive lock
32   - # to prevent race condition
33   - f.flock(File::LOCK_EX)
34   -
35   - # Pull gitolite-admin repo
36   - # in tmp dir before do any changes
37   - pull
38   -
39   - # Build ga_repo object and @conf
40   - # to access gitolite-admin configuration
41   - @conf = ga_repo.config
42   -
43   - # Do any changes
44   - # in gitolite-admin
45   - # config here
46   - yield(self)
47   -
48   - # Save changes in
49   - # gitolite-admin repo
50   - # before push it
51   - ga_repo.save
52   -
53   - # Push gitolite-admin repo
54   - # to apply all changes
55   - push
56   - ensure
57   - # Remove tmp dir
58   - # removing the gitolite folder first is important to avoid
59   - # NFS issues.
60   - FileUtils.rm_rf(File.join(config_tmp_dir, 'gitolite'))
61   -
62   - # Remove parent tmp dir
63   - FileUtils.rm_rf(config_tmp_dir)
64   -
65   - # Unlock so other task can access
66   - # gitolite configuration
67   - f.flock(File::LOCK_UN)
68   - end
69   - end
70   - end
71   - rescue PullError => ex
72   - log("Pull error -> " + ex.message)
73   - raise Gitolite::AccessDenied, ex.message
74   -
75   - rescue PushError => ex
76   - log("Push error -> " + " " + ex.message)
77   - raise Gitolite::AccessDenied, ex.message
78   -
79   - rescue BrokenGitolite => ex
80   - log("Gitolite error -> " + " " + ex.message)
81   - raise Gitolite::AccessDenied, ex.message
82   -
83   - rescue Exception => ex
84   - log(ex.class.name + " " + ex.message)
85   - raise Gitolite::AccessDenied.new("gitolite timeout")
86   - end
87   -
88   - def log message
89   - Gitlab::GitLogger.error(message)
90   - end
91   -
92   - def path_to_repo(name)
93   - File.join(Gitlab.config.gitolite.repos_path, "#{name}.git")
94   - end
95   -
96   - def destroy_project(name)
97   - full_path = path_to_repo(name)
98   - FileUtils.rm_rf(full_path) if File.exists?(full_path)
99   - conf.rm_repo(name)
100   - end
101   -
102   - def clean_repo repo_name
103   - conf.rm_repo(repo_name)
104   - end
105   -
106   - def destroy_project!(project)
107   - apply do |config|
108   - config.destroy_project(project)
109   - end
110   - end
111   -
112   - def write_key(id, key)
113   - File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f|
114   - f.write(key.gsub(/\n/,''))
115   - end
116   - end
117   -
118   - def rm_key(user)
119   - key_path = File.join(config_tmp_dir, 'gitolite/keydir', "#{user}.pub")
120   - ga_key = ::Gitolite::SSHKey.from_file(key_path)
121   - ga_repo.rm_key(ga_key)
122   - end
123   -
124   - # update or create
125   - def update_project(project)
126   - repo = update_project_config(project, conf)
127   - conf.add_repo(repo, true)
128   - end
129   -
130   - def update_project!( project)
131   - apply do |config|
132   - config.update_project(project)
133   - end
134   - end
135   -
136   - # Updates many projects and uses project.path_with_namespace as the repo path
137   - # An order of magnitude faster than update_project
138   - def update_projects(projects)
139   - projects.each do |project|
140   - repo = update_project_config(project, conf)
141   - conf.add_repo(repo, true)
142   - end
143   - end
144   -
145   - def update_project_config(project, conf)
146   - repo_name = project.path_with_namespace
147   -
148   - repo = if conf.has_repo?(repo_name)
149   - conf.get_repo(repo_name)
150   - else
151   - ::Gitolite::Config::Repo.new(repo_name)
152   - end
153   -
154   - name_readers = project.team.repository_readers
155   - name_writers = project.team.repository_writers
156   - name_masters = project.team.repository_masters
157   -
158   - pr_br = project.protected_branches.map(&:name).join("$ ")
159   -
160   - repo.clean_permissions
161   -
162   - # Deny access to protected branches for writers
163   - unless name_writers.blank? || pr_br.blank?
164   - repo.add_permission("-", pr_br.strip + "$ ", name_writers)
165   - end
166   -
167   - # Add read permissions
168   - repo.add_permission("R", "", name_readers) unless name_readers.blank?
169   -
170   - # Add write permissions
171   - repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
172   - repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
173   -
174   - # Add sharedRepository config
175   - repo.set_git_config("core.sharedRepository", "0660")
176   -
177   - repo
178   - end
179   -
180   - # Enable access to all repos for gitolite admin.
181   - # We use it for accept merge request feature
182   - def admin_all_repo
183   - owner_name = Gitlab.config.gitolite.admin_key
184   -
185   - # @ALL repos premission for gitolite owner
186   - repo_name = "@all"
187   - repo = if conf.has_repo?(repo_name)
188   - conf.get_repo(repo_name)
189   - else
190   - ::Gitolite::Config::Repo.new(repo_name)
191   - end
192   -
193   - repo.add_permission("RW+", "", owner_name)
194   - conf.add_repo(repo, true)
195   - end
196   -
197   - def admin_all_repo!
198   - apply { |config| config.admin_all_repo }
199   - end
200   -
201   - private
202   -
203   - def pull
204   - # Create config tmp dir like "RAILS_ROOT/tmp/gitlabhq-gitolite-132545"
205   - Dir.mkdir config_tmp_dir
206   -
207   - # Clone gitolite-admin repo into tmp dir
208   - popen("git clone #{Gitlab.config.gitolite.admin_uri} #{config_tmp_dir}/gitolite", tmp_dir)
209   -
210   - # Ensure file with config presents after cloning
211   - unless File.exists?(File.join(config_tmp_dir, 'gitolite', 'conf', 'gitolite.conf'))
212   - raise PullError, "unable to clone gitolite-admin repo"
213   - end
214   - end
215   -
216   - def push
217   - output, status = popen('git add -A', tmp_conf_path)
218   - raise "Git add failed." unless status.zero?
219   -
220   - # git commit returns 0 on success, and 1 if there is nothing to commit
221   - output, status = popen('git commit -m "GitLab"', tmp_conf_path)
222   - raise "Git add failed." unless [0,1].include?(status)
223   -
224   - output, status = popen('git push', tmp_conf_path)
225   -
226   - if output =~ /remote\: FATAL/
227   - raise BrokenGitolite, output
228   - end
229   -
230   - if status.zero? || output =~ /Everything up\-to\-date/
231   - return true
232   - else
233   - raise PushError, "unable to push gitolite-admin repo"
234   - end
235   - end
236   -
237   - def tmp_conf_path
238   - File.join(config_tmp_dir,'gitolite')
239   - end
240   - end
241   -end
lib/gitlab/backend/shell.rb 0 → 100644
... ... @@ -0,0 +1,50 @@
  1 +module Gitlab
  2 + class Shell
  3 + class AccessDenied < StandardError; end
  4 +
  5 + # Init new repository
  6 + #
  7 + # name - project path with namespace
  8 + #
  9 + # Ex.
  10 + # add_repository("gitlab/gitlab-ci")
  11 + #
  12 + def add_repository(name)
  13 + system("/home/git/gitlab-shell/bin/gitlab-projects add-project #{name}.git")
  14 + end
  15 +
  16 + # Remove repository from file system
  17 + #
  18 + # name - project path with namespace
  19 + #
  20 + # Ex.
  21 + # remove_repository("gitlab/gitlab-ci")
  22 + #
  23 + def remove_repository(name)
  24 + system("/home/git/gitlab-shell/bin/gitlab-projects rm-project #{name}.git")
  25 + end
  26 +
  27 + # Add new key to gitlab-shell
  28 + #
  29 + # Ex.
  30 + # add_key("key-42", "sha-rsa ...")
  31 + #
  32 + def add_key(key_id, key_content)
  33 + system("/home/git/gitlab-shell/bin/gitlab-keys add-key #{key_id} \"#{key_content}\"")
  34 + end
  35 +
  36 + # Remove ssh key from gitlab shell
  37 + #
  38 + # Ex.
  39 + # remove_key("key-342", "sha-rsa ...")
  40 + #
  41 + def remove_key(key_id, key_content)
  42 + system("/home/git/gitlab-shell/bin/gitlab-keys rm-key #{key_id} \"#{key_content}\"")
  43 + end
  44 +
  45 +
  46 + def url_to_repo path
  47 + Gitlab.config.gitolite.ssh_path_prefix + "#{path}.git"
  48 + end
  49 + end
  50 +end
... ...
lib/gitlab/graph/commit.rb
... ... @@ -5,12 +5,13 @@ module Gitlab
5 5 class Commit
6 6 include ActionView::Helpers::TagHelper
7 7  
8   - attr_accessor :time, :space, :refs
  8 + attr_accessor :time, :space, :refs, :parent_spaces
9 9  
10 10 def initialize(commit)
11 11 @_commit = commit
12 12 @time = -1
13 13 @space = 0
  14 + @parent_spaces = []
14 15 end
15 16  
16 17 def method_missing(m, *args, &block)
... ... @@ -28,6 +29,7 @@ module Gitlab
28 29 }
29 30 h[:time] = time
30 31 h[:space] = space
  32 + h[:parent_spaces] = parent_spaces
31 33 h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
32 34 h[:id] = sha
33 35 h[:date] = date
... ...
lib/gitlab/graph/json_builder.rb
... ... @@ -9,14 +9,14 @@ module Gitlab
9 9 @max_count ||= 650
10 10 end
11 11  
12   - def initialize project
  12 + def initialize project, ref
13 13 @project = project
  14 + @ref = ref
14 15 @repo = project.repo
15 16 @ref_cache = {}
16 17  
17 18 @commits = collect_commits
18 19 @days = index_commits
19   - @space = 0
20 20 end
21 21  
22 22 def to_json(*args)
... ... @@ -53,7 +53,7 @@ module Gitlab
53 53 #
54 54 # @return [Array<TimeDate>] list of commit dates corelated with time on commits
55 55 def index_commits
56   - days, heads = [], []
  56 + days, heads, times = [], [], []
57 57 map = {}
58 58  
59 59 commits.reverse.each_with_index do |c,i|
... ... @@ -61,14 +61,15 @@ module Gitlab
61 61 days[i] = c.committed_date
62 62 map[c.id] = c
63 63 heads += c.refs unless c.refs.nil?
  64 + times[i] = c
64 65 end
65 66  
66 67 heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote}
67 68 # sort heads so the master is top and current branches are closer
68 69 heads.sort! do |a,b|
69   - if a.name == "master"
  70 + if a.name == @ref
70 71 -1
71   - elsif b.name == "master"
  72 + elsif b.name == @ref
72 73 1
73 74 else
74 75 b.commit.committed_date <=> a.commit.committed_date
... ... @@ -86,9 +87,62 @@ module Gitlab
86 87 end
87 88 end
88 89  
  90 + # find parent spaces for not overlap lines
  91 + times.each do |c|
  92 + c.parent_spaces.concat(find_free_parent_spaces(c, map, times))
  93 + end
  94 +
89 95 days
90 96 end
91 97  
  98 + def find_free_parent_spaces(commit, map, times)
  99 + spaces = []
  100 +
  101 + commit.parents.each do |p|
  102 + if map.include?(p.id) then
  103 + parent = map[p.id]
  104 +
  105 + range = if commit.time < parent.time then
  106 + commit.time..parent.time
  107 + else
  108 + parent.time..commit.time
  109 + end
  110 +
  111 + space = if commit.space >= parent.space then
  112 + find_free_parent_space(range, parent.space, 1, commit.space, times)
  113 + else
  114 + find_free_parent_space(range, parent.space, -1, parent.space, times)
  115 + end
  116 +
  117 + mark_reserved(range, space)
  118 + spaces << space
  119 + end
  120 + end
  121 +
  122 + spaces
  123 + end
  124 +
  125 + def find_free_parent_space(range, space_base, space_step, space_default, times)
  126 + if is_overlap?(range, times, space_default) then
  127 + find_free_space(range, space_base, space_step)
  128 + else
  129 + space_default
  130 + end
  131 + end
  132 +
  133 + def is_overlap?(range, times, overlap_space)
  134 + range.each do |i|
  135 + if i != range.first &&
  136 + i != range.last &&
  137 + times[i].space == overlap_space then
  138 +
  139 + return true;
  140 + end
  141 + end
  142 +
  143 + false
  144 + end
  145 +
92 146 # Add space mark on commit and its parents
93 147 #
94 148 # @param [Graph::Commit] the commit object.
... ... @@ -98,10 +152,9 @@ module Gitlab
98 152 if leaves.empty?
99 153 return
100 154 end
101   - @space = find_free_space(leaves, map)
102   - leaves.each{|l| l.space = @space}
103 155 # and mark it as reserved
104 156 min_time = leaves.last.time
  157 + max_space = 1
105 158 parents = leaves.last.parents.collect
106 159 parents.each do |p|
107 160 if map.include? p.id
... ... @@ -109,6 +162,9 @@ module Gitlab
109 162 if parent.time < min_time
110 163 min_time = parent.time
111 164 end
  165 + if max_space < parent.space then
  166 + max_space = parent.space
  167 + end
112 168 end
113 169 end
114 170 if parent_time.nil?
... ... @@ -116,7 +172,12 @@ module Gitlab
116 172 else
117 173 max_time = parent_time - 1
118 174 end
119   - mark_reserved(min_time..max_time, @space)
  175 +
  176 + time_range = leaves.last.time..leaves.first.time
  177 + space = find_free_space(time_range, max_space, 2)
  178 + leaves.each{|l| l.space = space}
  179 +
  180 + mark_reserved(min_time..max_time, space)
120 181  
121 182 # Visit branching chains
122 183 leaves.each do |l|
... ... @@ -133,30 +194,25 @@ module Gitlab
133 194 end
134 195 end
135 196  
136   - def find_free_space(leaves, map)
137   - time_range = leaves.last.time..leaves.first.time
  197 + def find_free_space(time_range, space_base, space_step)
138 198 reserved = []
139 199 for day in time_range
140 200 reserved += @_reserved[day]
141 201 end
142   - space = base_space(leaves, map)
143   - while (reserved.include? space) || (space == @space) do
144   - space += 1
  202 + reserved.uniq!
  203 +
  204 + space = space_base
  205 + while reserved.include?(space) do
  206 + space += space_step
  207 + if space <= 0 then
  208 + space_step *= -1
  209 + space = space_base + space_step
  210 + end
145 211 end
146 212  
147 213 space
148 214 end
149 215  
150   - def base_space(leaves, map)
151   - parents = []
152   - leaves.each do |l|
153   - parents.concat l.parents.collect.select{|p| map.include? p.id and map[p.id].space.nonzero?}
154   - end
155   -
156   - space = parents.map{|p| map[p.id].space}.max || 0
157   - space += 1
158   - end
159   -
160 216 # Takes most left subtree branch of commits
161 217 # which don't have space mark yet.
162 218 #
... ...
lib/gitlab/satellite/satellite.rb
... ... @@ -30,10 +30,10 @@ module Gitlab
30 30 end
31 31  
32 32 def create
33   - output, status = popen("git clone #{project.url_to_repo} #{path}",
  33 + output, status = popen("git clone #{project.repository.path_to_repo} #{path}",
34 34 Gitlab.config.satellites.path)
35 35  
36   - log("PID: #{project.id}: git clone #{project.url_to_repo} #{path}")
  36 + log("PID: #{project.id}: git clone #{project.repository.path_to_repo} #{path}")
37 37 log("PID: #{project.id}: -> #{output}")
38 38  
39 39 if status.zero?
... ...
lib/gitolited.rb
... ... @@ -6,6 +6,6 @@
6 6 #
7 7 module Gitolited
8 8 def gitolite
9   - Gitlab::Gitolite.new
  9 + Gitlab::Shell.new
10 10 end
11 11 end
... ...
lib/hooks/post-receive
... ... @@ -1,12 +0,0 @@
1   -#!/usr/bin/env bash
2   -
3   -# Version 4.1
4   -# This file was placed here by GitLab. It makes sure that your pushed commits
5   -# will be processed properly.
6   -
7   -while read oldrev newrev ref
8   -do
9   - # For every branch or tag that was pushed, create a Resque job in redis.
10   - repo_path=`pwd`
11   - env -i redis-cli rpush "resque:gitlab:queue:post_receive" "{\"class\":\"PostReceive\",\"args\":[\"$repo_path\",\"$oldrev\",\"$newrev\",\"$ref\",\"$GL_USER\"]}" > /dev/null 2>&1
12   -done
lib/support/rewrite-hooks.sh
... ... @@ -1,32 +0,0 @@
1   -#!/bin/bash
2   -
3   -src="/home/git/repositories"
4   -
5   -for dir in `ls "$src/"`
6   -do
7   - if [ -d "$src/$dir" ]; then
8   -
9   - if [ "$dir" = "gitolite-admin.git" ]
10   - then
11   - continue
12   - fi
13   -
14   - if [[ "$dir" =~ ^.*.git$ ]]
15   - then
16   - project_hook="$src/$dir/hooks/post-receive"
17   - gitolite_hook="/home/git/.gitolite/hooks/common/post-receive"
18   -
19   - ln -s -f $gitolite_hook $project_hook
20   - else
21   - for subdir in `ls "$src/$dir/"`
22   - do
23   - if [ -d "$src/$dir/$subdir" ] && [[ "$subdir" =~ ^.*.git$ ]]; then
24   - project_hook="$src/$dir/$subdir/hooks/post-receive"
25   - gitolite_hook="/home/git/.gitolite/hooks/common/post-receive"
26   -
27   - ln -s -f $gitolite_hook $project_hook
28   - fi
29   - done
30   - fi
31   - fi
32   -done
lib/support/truncate_repositories.sh
... ... @@ -1,11 +0,0 @@
1   -#!/bin/bash
2   -
3   -echo "Danger!!! Data Loss"
4   -while true; do
5   - read -p "Do you wish to all directories except gitolite-admin.git from /home/git/repositories/ (y/n) ?: " yn
6   - case $yn in
7   - [Yy]* ) sh -c "find /home/git/repositories/. -maxdepth 1 -not -name 'gitolite-admin.git' -not -name '.' | xargs sudo rm -rf"; break;;
8   - [Nn]* ) exit;;
9   - * ) echo "Please answer yes or no.";;
10   - esac
11   -done
lib/tasks/gitlab/check.rake
... ... @@ -716,7 +716,7 @@ namespace :gitlab do
716 716 end
717 717  
718 718 def check_repo_base_permissions
719   - print "Repo base access is drwsrws---? ... "
  719 + print "Repo base access is drwxrws---? ... "
720 720  
721 721 repo_base_path = Gitlab.config.gitolite.repos_path
722 722 unless File.exists?(repo_base_path)
... ... @@ -724,12 +724,14 @@ namespace :gitlab do
724 724 return
725 725 end
726 726  
727   - if File.stat(repo_base_path).mode.to_s(8).ends_with?("6770")
  727 + if File.stat(repo_base_path).mode.to_s(8).ends_with?("2770")
728 728 puts "yes".green
729 729 else
730 730 puts "no".red
731 731 try_fixing_it(
732   - "sudo chmod -R ug+rwXs,o-rwx #{repo_base_path}"
  732 + "sudo chmod -R ug+rwX,o-rwx #{repo_base_path}",
  733 + "sudo chmod -R ug-s #{repo_base_path}",
  734 + "find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s"
733 735 )
734 736 for_more_information(
735 737 see_installation_guide_section "Gitolite"
... ... @@ -780,21 +782,25 @@ namespace :gitlab do
780 782 Project.find_each(batch_size: 100) do |project|
781 783 print "#{project.name_with_namespace.yellow} ... "
782 784  
783   - correct_options = options.map do |name, value|
784   - run("git --git-dir=\"#{project.repository.path_to_repo}\" config --get #{name}").try(:chomp) == value
785   - end
786   -
787   - if correct_options.all?
788   - puts "ok".green
  785 + if project.empty_repo?
  786 + puts "repository is empty".magenta
789 787 else
790   - puts "wrong or missing".red
791   - try_fixing_it(
792   - sudo_gitlab("bundle exec rake gitlab:gitolite:update_repos RAILS_ENV=production")
793   - )
794   - for_more_information(
795   - "doc/raketasks/maintenance.md"
796   - )
797   - fix_and_rerun
  788 + correct_options = options.map do |name, value|
  789 + run("git --git-dir=\"#{project.repository.path_to_repo}\" config --get #{name}").try(:chomp) == value
  790 + end
  791 +
  792 + if correct_options.all?
  793 + puts "ok".green
  794 + else
  795 + puts "wrong or missing".red
  796 + try_fixing_it(
  797 + sudo_gitlab("bundle exec rake gitlab:gitolite:update_repos RAILS_ENV=production")
  798 + )
  799 + for_more_information(
  800 + "doc/raketasks/maintenance.md"
  801 + )
  802 + fix_and_rerun
  803 + end
798 804 end
799 805 end
800 806 end
... ... @@ -820,32 +826,37 @@ namespace :gitlab do
820 826  
821 827 Project.find_each(batch_size: 100) do |project|
822 828 print "#{project.name_with_namespace.yellow} ... "
823   - project_hook_file = File.join(project.repository.path_to_repo, "hooks", hook_file)
824 829  
825   - unless File.exists?(project_hook_file)
826   - puts "missing".red
827   - try_fixing_it(
828   - "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}"
829   - )
830   - for_more_information(
831   - "lib/support/rewrite-hooks.sh"
832   - )
833   - fix_and_rerun
834   - next
835   - end
836   -
837   - if File.lstat(project_hook_file).symlink? &&
838   - File.realpath(project_hook_file) == File.realpath(gitolite_hook_file)
839   - puts "ok".green
  830 + if project.empty_repo?
  831 + puts "repository is empty".magenta
840 832 else
841   - puts "not a link to Gitolite's hook".red
842   - try_fixing_it(
843   - "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}"
844   - )
845   - for_more_information(
846   - "lib/support/rewrite-hooks.sh"
847   - )
848   - fix_and_rerun
  833 + project_hook_file = File.join(project.repository.path_to_repo, "hooks", hook_file)
  834 +
  835 + unless File.exists?(project_hook_file)
  836 + puts "missing".red
  837 + try_fixing_it(
  838 + "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}"
  839 + )
  840 + for_more_information(
  841 + "lib/support/rewrite-hooks.sh"
  842 + )
  843 + fix_and_rerun
  844 + next
  845 + end
  846 +
  847 + if File.lstat(project_hook_file).symlink? &&
  848 + File.realpath(project_hook_file) == File.realpath(gitolite_hook_file)
  849 + puts "ok".green
  850 + else
  851 + puts "not a link to Gitolite's hook".red
  852 + try_fixing_it(
  853 + "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}"
  854 + )
  855 + for_more_information(
  856 + "lib/support/rewrite-hooks.sh"
  857 + )
  858 + fix_and_rerun
  859 + end
849 860 end
850 861 end
851 862 end
... ...
lib/tasks/gitlab/enable_automerge.rake
... ... @@ -3,11 +3,6 @@ namespace :gitlab do
3 3 task :enable_automerge => :environment do
4 4 warn_user_is_not_gitlab
5 5  
6   - puts "Updating repo permissions ..."
7   - Gitlab::Gitolite.new.enable_automerge
8   - puts "... #{"done".green}"
9   - puts ""
10   -
11 6 print "Creating satellites for ..."
12 7 unless Project.count > 0
13 8 puts "skipping, because you have no projects".magenta
... ...
lib/tasks/gitlab/shell.rake 0 → 100644
... ... @@ -0,0 +1,32 @@
  1 +namespace :gitlab do
  2 + namespace :shell do
  3 + desc "GITLAB | Setup gitlab-shell"
  4 + task :setup => :environment do
  5 + setup
  6 + end
  7 + end
  8 +
  9 + def setup
  10 + warn_user_is_not_gitlab
  11 +
  12 + puts "This will rebuild an authorized_keys file."
  13 + puts "You will lose any data stored in /home/git/.ssh/authorized_keys."
  14 + ask_to_continue
  15 + puts ""
  16 +
  17 + system("echo '# Managed by gitlab-shell' > /home/git/.ssh/authorized_keys")
  18 +
  19 + Key.find_each(:batch_size => 1000) do |key|
  20 + if Gitlab::Shell.new.add_key(key.shell_id, key.key)
  21 + print '.'
  22 + else
  23 + print 'F'
  24 + end
  25 + end
  26 +
  27 + rescue Gitlab::TaskAbortedByUserError
  28 + puts "Quitting...".red
  29 + exit 1
  30 + end
  31 +end
  32 +
... ...
spec/factories/user_team_project_relationships.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_team_project_relationships
  4 +#
  5 +# id :integer not null, primary key
  6 +# project_id :integer
  7 +# user_team_id :integer
  8 +# greatest_access :integer
  9 +# created_at :datetime not null
  10 +# updated_at :datetime not null
  11 +#
  12 +
1 13 # Read about factories at https://github.com/thoughtbot/factory_girl
2 14  
3 15 FactoryGirl.define do
... ...
spec/factories/user_team_user_relationships.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_team_user_relationships
  4 +#
  5 +# id :integer not null, primary key
  6 +# user_id :integer
  7 +# user_team_id :integer
  8 +# group_admin :boolean
  9 +# permission :integer
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +#
  13 +
1 14 # Read about factories at https://github.com/thoughtbot/factory_girl
2 15  
3 16 FactoryGirl.define do
... ...
spec/factories/user_teams.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_teams
  4 +#
  5 +# id :integer not null, primary key
  6 +# name :string(255)
  7 +# path :string(255)
  8 +# owner_id :integer
  9 +# created_at :datetime not null
  10 +# updated_at :datetime not null
  11 +#
  12 +
1 13 # Read about factories at https://github.com/thoughtbot/factory_girl
2 14  
3 15 FactoryGirl.define do
... ...
spec/lib/gitolite_config_spec.rb
... ... @@ -1,16 +0,0 @@
1   -require 'spec_helper'
2   -
3   -describe Gitlab::GitoliteConfig do
4   - let(:gitolite) { Gitlab::GitoliteConfig.new }
5   -
6   - it { should respond_to :write_key }
7   - it { should respond_to :rm_key }
8   - it { should respond_to :update_project }
9   - it { should respond_to :update_project! }
10   - it { should respond_to :update_projects }
11   - it { should respond_to :destroy_project }
12   - it { should respond_to :destroy_project! }
13   - it { should respond_to :apply }
14   - it { should respond_to :admin_all_repo }
15   - it { should respond_to :admin_all_repo! }
16   -end
spec/lib/gitolite_spec.rb
... ... @@ -1,26 +0,0 @@
1   -require 'spec_helper'
2   -
3   -describe Gitlab::Gitolite do
4   - let(:project) { double('Project', id: 7, path: 'diaspora') }
5   - let(:gitolite_config) { double('Gitlab::GitoliteConfig') }
6   - let(:gitolite) { Gitlab::Gitolite.new }
7   -
8   - before do
9   - gitolite.stub(config: gitolite_config)
10   - Project.stub(find: project)
11   - end
12   -
13   - it { should respond_to :set_key }
14   - it { should respond_to :remove_key }
15   -
16   - it { should respond_to :update_repository }
17   - it { should respond_to :create_repository }
18   - it { should respond_to :remove_repository }
19   -
20   - it { gitolite.url_to_repo('diaspora').should == Gitlab.config.gitolite.ssh_path_prefix + "diaspora.git" }
21   -
22   - it "should call config update" do
23   - gitolite_config.should_receive(:update_project!)
24   - gitolite.update_repository(project.id)
25   - end
26   -end
spec/lib/shell_spec.rb 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Gitlab::Shell do
  4 + let(:project) { double('Project', id: 7, path: 'diaspora') }
  5 + let(:gitolite) { Gitlab::Shell.new }
  6 +
  7 + before do
  8 + Project.stub(find: project)
  9 + end
  10 +
  11 + it { should respond_to :add_key }
  12 + it { should respond_to :remove_key }
  13 + it { should respond_to :add_repository }
  14 + it { should respond_to :remove_repository }
  15 +
  16 + it { gitolite.url_to_repo('diaspora').should == Gitlab.config.gitolite.ssh_path_prefix + "diaspora.git" }
  17 +end
... ...
spec/mailers/notify_spec.rb
... ... @@ -266,7 +266,7 @@ describe Notify do
266 266  
267 267 before(:each) { note.stub(:noteable).and_return(commit) }
268 268  
269   - subject { Notify.note_commit_email(recipient.email, note.id) }
  269 + subject { Notify.note_commit_email(recipient.id, note.id) }
270 270  
271 271 it_behaves_like 'a note email'
272 272  
... ...
spec/models/key_spec.rb
... ... @@ -46,9 +46,9 @@ describe Key do
46 46 key.should_not be_valid
47 47 end
48 48  
49   - it "does accept the same key for another project" do
  49 + it "does not accept the same key for another project" do
50 50 key = build(:key, project_id: 0)
51   - key.should be_valid
  51 + key.should_not be_valid
52 52 end
53 53 end
54 54  
... ...
spec/models/project_spec.rb
... ... @@ -8,7 +8,6 @@
8 8 # description :text
9 9 # created_at :datetime not null
10 10 # updated_at :datetime not null
11   -# private_flag :boolean default(TRUE), not null
12 11 # creator_id :integer
13 12 # default_branch :string(255)
14 13 # issues_enabled :boolean default(TRUE), not null
... ... @@ -16,6 +15,7 @@
16 15 # merge_requests_enabled :boolean default(TRUE), not null
17 16 # wiki_enabled :boolean default(TRUE), not null
18 17 # namespace_id :integer
  18 +# public :boolean default(FALSE), not null
19 19 #
20 20  
21 21 require 'spec_helper'
... ... @@ -42,7 +42,6 @@ describe Project do
42 42 describe "Mass assignment" do
43 43 it { should_not allow_mass_assignment_of(:namespace_id) }
44 44 it { should_not allow_mass_assignment_of(:creator_id) }
45   - it { should_not allow_mass_assignment_of(:private_flag) }
46 45 end
47 46  
48 47 describe "Validation" do
... ... @@ -78,8 +77,6 @@ describe Project do
78 77 it { should respond_to(:url_to_repo) }
79 78 it { should respond_to(:repo_exists?) }
80 79 it { should respond_to(:satellite) }
81   - it { should respond_to(:update_repository) }
82   - it { should respond_to(:destroy_repository) }
83 80 it { should respond_to(:observe_push) }
84 81 it { should respond_to(:update_merge_requests) }
85 82 it { should respond_to(:execute_hooks) }
... ...
spec/models/protected_branch_spec.rb
... ... @@ -24,19 +24,4 @@ describe ProtectedBranch do
24 24 it { should validate_presence_of(:project) }
25 25 it { should validate_presence_of(:name) }
26 26 end
27   -
28   - describe 'Callbacks' do
29   - let(:branch) { build(:protected_branch) }
30   -
31   - it 'call update_repository after save' do
32   - branch.should_receive(:update_repository)
33   - branch.save
34   - end
35   -
36   - it 'call update_repository after destroy' do
37   - branch.save
38   - branch.should_receive(:update_repository)
39   - branch.destroy
40   - end
41   - end
42 27 end
... ...
spec/models/user_spec.rb
... ... @@ -31,6 +31,8 @@
31 31 # extern_uid :string(255)
32 32 # provider :string(255)
33 33 # username :string(255)
  34 +# can_create_group :boolean default(TRUE), not null
  35 +# can_create_team :boolean default(TRUE), not null
34 36 #
35 37  
36 38 require 'spec_helper'
... ...
spec/models/user_team_project_relationship_spec.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_team_project_relationships
  4 +#
  5 +# id :integer not null, primary key
  6 +# project_id :integer
  7 +# user_team_id :integer
  8 +# greatest_access :integer
  9 +# created_at :datetime not null
  10 +# updated_at :datetime not null
  11 +#
  12 +
1 13 require 'spec_helper'
2 14  
3 15 describe UserTeamProjectRelationship do
... ...
spec/models/user_team_spec.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_teams
  4 +#
  5 +# id :integer not null, primary key
  6 +# name :string(255)
  7 +# path :string(255)
  8 +# owner_id :integer
  9 +# created_at :datetime not null
  10 +# updated_at :datetime not null
  11 +#
  12 +
1 13 require 'spec_helper'
2 14  
3 15 describe UserTeam do
... ...
spec/models/user_team_user_relationship_spec.rb
  1 +# == Schema Information
  2 +#
  3 +# Table name: user_team_user_relationships
  4 +#
  5 +# id :integer not null, primary key
  6 +# user_id :integer
  7 +# user_team_id :integer
  8 +# group_admin :boolean
  9 +# permission :integer
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +#
  13 +
1 14 require 'spec_helper'
2 15  
3 16 describe UserTeamUserRelationship do
... ...
spec/observers/key_observer_spec.rb
... ... @@ -3,7 +3,7 @@ require &#39;spec_helper&#39;
3 3 describe KeyObserver do
4 4 before do
5 5 @key = double('Key',
6   - identifier: 'admin_654654',
  6 + shell_id: 'key-32',
7 7 key: '== a vaild ssh key',
8 8 projects: [],
9 9 is_deploy_key: false
... ... @@ -14,14 +14,14 @@ describe KeyObserver do
14 14  
15 15 context :after_save do
16 16 it do
17   - GitoliteWorker.should_receive(:perform_async).with(:set_key, @key.identifier, @key.key, @key.projects.map(&:id))
  17 + GitoliteWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key)
18 18 @observer.after_save(@key)
19 19 end
20 20 end
21 21  
22 22 context :after_destroy do
23 23 it do
24   - GitoliteWorker.should_receive(:perform_async).with(:remove_key, @key.identifier, @key.projects.map(&:id))
  24 + GitoliteWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key)
25 25 @observer.after_destroy(@key)
26 26 end
27 27 end
... ...
spec/requests/api/groups_spec.rb 0 → 100644
... ... @@ -0,0 +1,93 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Gitlab::API do
  4 + include ApiHelpers
  5 +
  6 + let(:user1) { create(:user) }
  7 + let(:user2) { create(:user) }
  8 + let(:admin) { create(:admin) }
  9 + let!(:group1) { create(:group, owner: user1) }
  10 + let!(:group2) { create(:group, owner: user2) }
  11 +
  12 + describe "GET /groups" do
  13 + context "when unauthenticated" do
  14 + it "should return authentication error" do
  15 + get api("/groups")
  16 + response.status.should == 401
  17 + end
  18 + end
  19 +
  20 + context "when authenticated as user" do
  21 + it "normal user: should return an array of groups of user1" do
  22 + get api("/groups", user1)
  23 + response.status.should == 200
  24 + json_response.should be_an Array
  25 + json_response.length.should == 1
  26 + json_response.first['name'].should == group1.name
  27 + end
  28 + end
  29 +
  30 + context "when authenticated as admin" do
  31 + it "admin: should return an array of all groups" do
  32 + get api("/groups", admin)
  33 + response.status.should == 200
  34 + json_response.should be_an Array
  35 + json_response.length.should == 2
  36 + end
  37 + end
  38 + end
  39 +
  40 + describe "GET /groups/:id" do
  41 + context "when authenticated as user" do
  42 + it "should return one of user1's groups" do
  43 + get api("/groups/#{group1.id}", user1)
  44 + response.status.should == 200
  45 + json_response['name'] == group1.name
  46 + end
  47 +
  48 + it "should not return a non existing group" do
  49 + get api("/groups/1328", user1)
  50 + response.status.should == 404
  51 + end
  52 +
  53 + it "should not return a group not attached to user1" do
  54 + get api("/groups/#{group2.id}", user1)
  55 + response.status.should == 404
  56 + end
  57 + end
  58 +
  59 + context "when authenticated as admin" do
  60 + it "should return any existing group" do
  61 + get api("/groups/#{group2.id}", admin)
  62 + response.status.should == 200
  63 + json_response['name'] == group2.name
  64 + end
  65 +
  66 + it "should not return a non existing group" do
  67 + get api("/groups/1328", admin)
  68 + response.status.should == 404
  69 + end
  70 + end
  71 + end
  72 +
  73 + describe "POST /groups" do
  74 + context "when authenticated as user" do
  75 + it "should not create group" do
  76 + post api("/groups", user1), attributes_for(:group)
  77 + response.status.should == 403
  78 + end
  79 + end
  80 +
  81 + context "when authenticated as admin" do
  82 + it "should create group" do
  83 + post api("/groups", admin), attributes_for(:group)
  84 + response.status.should == 201
  85 + end
  86 +
  87 + it "should not create group, duplicate" do
  88 + post api("/groups", admin), {:name => "Duplicate Test", :path => group2.path}
  89 + response.status.should == 404
  90 + end
  91 + end
  92 + end
  93 +end
... ...
spec/requests/api/notes_spec.rb
... ... @@ -6,8 +6,10 @@ describe Gitlab::API do
6 6 let(:user) { create(:user) }
7 7 let!(:project) { create(:project, namespace: user.namespace ) }
8 8 let!(:issue) { create(:issue, project: project, author: user) }
  9 + let!(:merge_request) { create(:merge_request, project: project, author: user) }
9 10 let!(:snippet) { create(:snippet, project: project, author: user) }
10 11 let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) }
  12 + let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) }
11 13 let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) }
12 14 let!(:wall_note) { create(:note, project: project, author: user) }
13 15 before { project.team << [user, :reporter] }
... ... @@ -84,6 +86,15 @@ describe Gitlab::API do
84 86 response.status.should == 404
85 87 end
86 88 end
  89 +
  90 + context "when noteable is a Merge Request" do
  91 + it "should return an array of merge_requests notes" do
  92 + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user)
  93 + response.status.should == 200
  94 + json_response.should be_an Array
  95 + json_response.first['body'].should == merge_request_note.note
  96 + end
  97 + end
87 98 end
88 99  
89 100 describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do
... ...
spec/requests/api/projects_spec.rb
... ... @@ -113,6 +113,29 @@ describe Gitlab::API do
113 113  
114 114 json_response['name'].should == 'new_design'
115 115 json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
  116 + json_response['protected'].should == false
  117 + end
  118 + end
  119 +
  120 + describe "PUT /projects/:id/repository/branches/:branch/protect" do
  121 + it "should protect a single branch" do
  122 + put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
  123 + response.status.should == 200
  124 +
  125 + json_response['name'].should == 'new_design'
  126 + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
  127 + json_response['protected'].should == true
  128 + end
  129 + end
  130 +
  131 + describe "PUT /projects/:id/repository/branches/:branch/unprotect" do
  132 + it "should unprotect a single branch" do
  133 + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
  134 + response.status.should == 200
  135 +
  136 + json_response['name'].should == 'new_design'
  137 + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
  138 + json_response['protected'].should == false
116 139 end
117 140 end
118 141  
... ...
spec/requests/api/users_spec.rb
... ... @@ -83,6 +83,54 @@ describe Gitlab::API do
83 83 end
84 84 end
85 85  
  86 + describe "PUT /users/:id" do
  87 + before { admin }
  88 +
  89 + it "should update user" do
  90 + put api("/users/#{user.id}", admin), {bio: 'new test bio'}
  91 + response.status.should == 200
  92 + json_response['bio'].should == 'new test bio'
  93 + user.reload.bio.should == 'new test bio'
  94 + end
  95 +
  96 + it "should not allow invalid update" do
  97 + put api("/users/#{user.id}", admin), {email: 'invalid email'}
  98 + response.status.should == 404
  99 + user.reload.email.should_not == 'invalid email'
  100 + end
  101 +
  102 + it "shouldn't available for non admin users" do
  103 + put api("/users/#{user.id}", user), attributes_for(:user)
  104 + response.status.should == 403
  105 + end
  106 +
  107 + it "should return 404 for non-existing user" do
  108 + put api("/users/999999", admin), {bio: 'update should fail'}
  109 + response.status.should == 404
  110 + end
  111 + end
  112 +
  113 + describe "DELETE /users/:id" do
  114 + before { admin }
  115 +
  116 + it "should delete user" do
  117 + delete api("/users/#{user.id}", admin)
  118 + response.status.should == 200
  119 + expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
  120 + json_response['email'].should == user.email
  121 + end
  122 +
  123 + it "shouldn't available for non admin users" do
  124 + delete api("/users/#{user.id}", user)
  125 + response.status.should == 403
  126 + end
  127 +
  128 + it "should return 404 for non-existing user" do
  129 + delete api("/users/999999", admin)
  130 + response.status.should == 404
  131 + end
  132 + end
  133 +
86 134 describe "GET /user" do
87 135 it "should return current user" do
88 136 get api("/user", user)
... ...
spec/requests/notes_on_merge_requests_spec.rb
... ... @@ -22,9 +22,9 @@ describe &quot;On a merge request&quot;, js: true do
22 22 it { within(".js-main-target-form") { should_not have_link("Cancel") } }
23 23  
24 24 # notifiactions
25   - it { within(".js-main-target-form") { should have_checked_field("Project team") } }
26   - it { within(".js-main-target-form") { should_not have_checked_field("Commit author") } }
27   - it { within(".js-main-target-form") { should_not have_unchecked_field("Commit author") } }
  25 + it { within(".js-main-target-form") { should have_checked_field("Notify team via email") } }
  26 + it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } }
  27 + it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } }
28 28  
29 29 describe "without text" do
30 30 it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }
... ... @@ -125,7 +125,7 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do
125 125 it { should have_css(".js-close-discussion-note-form", text: "Cancel") }
126 126  
127 127 # notification options
128   - it { should have_checked_field("Project team") }
  128 + it { should have_checked_field("Notify team via email") }
129 129  
130 130 it "shouldn't add a second form for same row" do
131 131 find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click")
... ...
spec/requests/notes_on_wall_spec.rb
... ... @@ -21,9 +21,9 @@ describe &quot;On the project wall&quot;, js: true do
21 21 it { within(".js-main-target-form") { should_not have_link("Cancel") } }
22 22  
23 23 # notifiactions
24   - it { within(".js-main-target-form") { should have_checked_field("Project team") } }
25   - it { within(".js-main-target-form") { should_not have_checked_field("Commit author") } }
26   - it { within(".js-main-target-form") { should_not have_unchecked_field("Commit author") } }
  24 + it { within(".js-main-target-form") { should have_checked_field("Notify team via email") } }
  25 + it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } }
  26 + it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } }
27 27  
28 28 describe "without text" do
29 29 it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }
... ...
spec/routing/project_routing_spec.rb
... ... @@ -76,7 +76,7 @@ describe ProjectsController, &quot;routing&quot; do
76 76 end
77 77  
78 78 it "to #graph" do
79   - get("/gitlabhq/graph").should route_to('projects#graph', id: 'gitlabhq')
  79 + get("/gitlabhq/graph/master").should route_to('graph#show', project_id: 'gitlabhq', id: 'master')
80 80 end
81 81  
82 82 it "to #files" do
... ...
spec/spec_helper.rb
... ... @@ -24,7 +24,6 @@ RSpec.configure do |config|
24 24 config.mock_with :rspec
25 25  
26 26 config.include LoginHelpers, type: :request
27   - config.include GitoliteStub
28 27 config.include FactoryGirl::Syntax::Methods
29 28 config.include Devise::TestHelpers, type: :controller
30 29  
... ... @@ -34,8 +33,6 @@ RSpec.configure do |config|
34 33 config.use_transactional_fixtures = false
35 34  
36 35 config.before do
37   - stub_gitolite!
38   -
39 36 # Use tmp dir for FS manipulations
40 37 temp_repos_path = Rails.root.join('tmp', 'test-git-base-path')
41 38 Gitlab.config.gitolite.stub(repos_path: temp_repos_path)
... ...
spec/support/gitolite_stub.rb
... ... @@ -1,21 +0,0 @@
1   -module GitoliteStub
2   - def stub_gitolite!
3   - stub_gitlab_gitolite
4   - stub_gitolite_admin
5   - end
6   -
7   - def stub_gitolite_admin
8   - gitolite_admin = double('Gitolite::GitoliteAdmin')
9   - gitolite_admin.as_null_object
10   -
11   - Gitolite::GitoliteAdmin.stub(new: gitolite_admin)
12   - end
13   -
14   - def stub_gitlab_gitolite
15   - gitolite_config = double('Gitlab::GitoliteConfig')
16   - gitolite_config.stub(apply: ->() { yield(self) })
17   - gitolite_config.as_null_object
18   -
19   - Gitlab::GitoliteConfig.stub(new: gitolite_config)
20   - end
21   -end
spec/support/stubbed_repository.rb
1 1 require "repository"
2 2 require "project"
  3 +require "shell"
3 4  
4 5 # Stubs out all Git repository access done by models so that specs can run
5 6 # against fake repositories without Grit complaining that they don't exist.
... ... @@ -36,3 +37,23 @@ class GitLabTestRepo &lt; Repository
36 37 @repo ||= Grit::Repo.new(Rails.root.join('tmp', 'repositories', 'gitlabhq'))
37 38 end
38 39 end
  40 +
  41 +module Gitlab
  42 + class Shell
  43 + def add_repository name
  44 + true
  45 + end
  46 +
  47 + def remove_repository name
  48 + true
  49 + end
  50 +
  51 + def add_key id, key
  52 + true
  53 + end
  54 +
  55 + def remove_key id, key
  56 + true
  57 + end
  58 + end
  59 +end
... ...
spec/workers/post_receive_spec.rb
... ... @@ -11,7 +11,7 @@ describe PostReceive do
11 11 context "web hook" do
12 12 let(:project) { create(:project) }
13 13 let(:key) { create(:key, user: project.owner) }
14   - let(:key_id) { key.identifier }
  14 + let(:key_id) { key.shell_id }
15 15  
16 16 it "fetches the correct project" do
17 17 Project.should_receive(:find_with_namespace).with(project.path_with_namespace).and_return(project)
... ... @@ -19,7 +19,7 @@ describe PostReceive do
19 19 end
20 20  
21 21 it "does not run if the author is not in the project" do
22   - Key.stub(find_by_identifier: nil)
  22 + Key.stub(find_by_id: nil)
23 23  
24 24 project.should_not_receive(:observe_push)
25 25 project.should_not_receive(:execute_hooks)
... ...
vendor/assets/javascripts/branch-graph.js
... ... @@ -73,7 +73,8 @@
73 73 , cumonth = ""
74 74 , offsetX = 20
75 75 , offsetY = 60
76   - , barWidth = Math.max(graphWidth, this.dayCount * 20 + 320);
  76 + , barWidth = Math.max(graphWidth, this.dayCount * 20 + 320)
  77 + , scrollLeft = cw;
77 78  
78 79 this.raphael = r;
79 80  
... ... @@ -103,8 +104,9 @@
103 104  
104 105 for (i = 0; i < this.commitCount; i++) {
105 106 var x = offsetX + 20 * this.commits[i].time
106   - , y = offsetY + 20 * this.commits[i].space
107   - , c;
  107 + , y = offsetY + 10 * this.commits[i].space
  108 + , c
  109 + , ps;
108 110  
109 111 // Draw dot
110 112 r.circle(x, y, 3).attr({
... ... @@ -115,10 +117,12 @@
115 117 // Draw lines
116 118 for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
117 119 c = this.preparedCommits[this.commits[i].parents[j][0]];
  120 + ps = this.commits[i].parent_spaces[j];
118 121 if (c) {
119 122 var cx = offsetX + 20 * c.time
120   - , cy = offsetY + 20 * c.space;
121   - if (c.space == this.commits[i].space) {
  123 + , cy = offsetY + 10 * c.space
  124 + , psy = offsetY + 10 * ps;
  125 + if (c.space == this.commits[i].space && c.space == ps) {
122 126 r.path([
123 127 "M", x, y,
124 128 "L", cx, cy
... ... @@ -128,13 +132,25 @@
128 132 });
129 133  
130 134 } else if (c.space < this.commits[i].space) {
131   - r.path(["M", x - 5, y + .0001, "l-5-2,0,4,5,-2C", x - 5, y, x - 17, y + 2, x - 20, y - 5, "L", cx, y - 5, cx, cy])
  135 + r.path([
  136 + "M", x - 5, y,
  137 + "l-5-2,0,4,5,-2",
  138 + "L", x - 10, y,
  139 + "L", x - 15, psy,
  140 + "L", cx + 5, psy,
  141 + "L", cx, cy])
132 142 .attr({
133 143 stroke: this.colors[this.commits[i].space],
134 144 "stroke-width": 2
135 145 });
136 146 } else {
137   - r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5L", x - 10, y + 20, "L", x - 10, cy, cx, cy])
  147 + r.path([
  148 + "M", x - 3, y + 6,
  149 + "l-4,3,4,2,0,-5",
  150 + "L", x - 5, y + 10,
  151 + "L", x - 10, psy,
  152 + "L", cx + 5, psy,
  153 + "L", cx, cy])
138 154 .attr({
139 155 stroke: this.colors[c.space],
140 156 "stroke-width": 2
... ... @@ -145,12 +161,18 @@
145 161  
146 162 if (this.commits[i].refs) {
147 163 this.appendLabel(x, y, this.commits[i].refs);
  164 +
  165 + // The main branch is displayed in the center.
  166 + re = new RegExp('(^| )' + this.options.ref + '( |$)');
  167 + if (this.commits[i].refs.match(re)) {
  168 + scrollLeft = x - graphWidth / 2;
  169 + }
148 170 }
149 171  
150 172 this.appendAnchor(top, this.commits[i], x, y);
151 173 }
152 174 top.toFront();
153   - this.element.scrollLeft(cw);
  175 + this.element.scrollLeft(scrollLeft);
154 176 this.bindEvents();
155 177 };
156 178  
... ... @@ -260,7 +282,7 @@
260 282 cursor: "pointer"
261 283 })
262 284 .click(function(){
263   - window.location = options.commit_url.replace('%s', commit.id);
  285 + window.open(options.commit_url.replace('%s', commit.id), '_blank');
264 286 })
265 287 .hover(function(){
266 288 this.tooltip = r.commitTooltip(x, y + 5, commit);
... ...