Commit 66ebf8d83f894ed361031eb9ede5a8b829fefd36

Authored by Lennart Rosam
2 parents dc13af90 bd948549

Merge remote-tracking branch 'github/master'

Showing 190 changed files with 4007 additions and 974 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 190 files displayed.

1 web: bundle exec unicorn_rails -p $PORT 1 web: bundle exec unicorn_rails -p $PORT
2 -worker: bundle exec sidekiq -q post_receive,mailer,system_hook,common,default 2 +worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default
app/assets/images/onion_skin_sprites.gif 0 → 100644

1.55 KB

app/assets/images/swipemode_sprites.gif 0 → 100644

1.5 KB

app/assets/javascripts/commit/file.js.coffee 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +class CommitFile
  2 +
  3 + constructor: (file) ->
  4 + if $('.image', file).length
  5 + new ImageFile(file)
  6 +
  7 +this.CommitFile = CommitFile
0 \ No newline at end of file 8 \ No newline at end of file
app/assets/javascripts/commit/image-file.js.coffee 0 → 100644
@@ -0,0 +1,128 @@ @@ -0,0 +1,128 @@
  1 +class ImageFile
  2 +
  3 + # Width where images must fits in, for 2-up this gets divided by 2
  4 + @availWidth = 900
  5 + @viewModes = ['two-up', 'swipe']
  6 +
  7 + constructor: (@file) ->
  8 + # Determine if old and new file has same dimensions, if not show 'two-up' view
  9 + this.requestImageInfo $('.two-up.view .frame.deleted img', @file), (deletedWidth, deletedHeight) =>
  10 + this.requestImageInfo $('.two-up.view .frame.added img', @file), (width, height) =>
  11 + if width == deletedWidth && height == deletedHeight
  12 + this.initViewModes()
  13 + else
  14 + this.initView('two-up')
  15 +
  16 + initViewModes: ->
  17 + viewMode = ImageFile.viewModes[0]
  18 +
  19 + $('.view-modes', @file).removeClass 'hide'
  20 + $('.view-modes-menu', @file).on 'click', 'li', (event) =>
  21 + unless $(event.currentTarget).hasClass('active')
  22 + this.activateViewMode(event.currentTarget.className)
  23 +
  24 + this.activateViewMode(viewMode)
  25 +
  26 + activateViewMode: (viewMode) ->
  27 + $('.view-modes-menu li', @file)
  28 + .removeClass('active')
  29 + .filter(".#{viewMode}").addClass 'active'
  30 + $(".view:visible:not(.#{viewMode})", @file).fadeOut 200, =>
  31 + $(".view.#{viewMode}", @file).fadeIn(200)
  32 + this.initView viewMode
  33 +
  34 + initView: (viewMode) ->
  35 + this.views[viewMode].call(this)
  36 +
  37 + prepareFrames = (view) ->
  38 + maxWidth = 0
  39 + maxHeight = 0
  40 + $('.frame', view).each (index, frame) =>
  41 + width = $(frame).width()
  42 + height = $(frame).height()
  43 + maxWidth = if width > maxWidth then width else maxWidth
  44 + maxHeight = if height > maxHeight then height else maxHeight
  45 + .css
  46 + width: maxWidth
  47 + height: maxHeight
  48 +
  49 + [maxWidth, maxHeight]
  50 +
  51 + views:
  52 + 'two-up': ->
  53 + $('.two-up.view .wrap', @file).each (index, wrap) =>
  54 + $('img', wrap).each ->
  55 + currentWidth = $(this).width()
  56 + if currentWidth > ImageFile.availWidth / 2
  57 + $(this).width ImageFile.availWidth / 2
  58 +
  59 + this.requestImageInfo $('img', wrap), (width, height) ->
  60 + $('.image-info .meta-width', wrap).text "#{width}px"
  61 + $('.image-info .meta-height', wrap).text "#{height}px"
  62 + $('.image-info', wrap).removeClass('hide')
  63 +
  64 + 'swipe': ->
  65 + maxWidth = 0
  66 + maxHeight = 0
  67 +
  68 + $('.swipe.view', @file).each (index, view) =>
  69 +
  70 + [maxWidth, maxHeight] = prepareFrames(view)
  71 +
  72 + $('.swipe-frame', view).css
  73 + width: maxWidth + 16
  74 + height: maxHeight + 28
  75 +
  76 + $('.swipe-wrap', view).css
  77 + width: maxWidth + 1
  78 + height: maxHeight + 2
  79 +
  80 + $('.swipe-bar', view).css
  81 + left: 0
  82 + .draggable
  83 + axis: 'x'
  84 + containment: 'parent'
  85 + drag: (event) ->
  86 + $('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left
  87 + stop: (event) ->
  88 + $('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left
  89 +
  90 + 'onion-skin': ->
  91 + maxWidth = 0
  92 + maxHeight = 0
  93 +
  94 + dragTrackWidth = $('.drag-track', @file).width() - $('.dragger', @file).width()
  95 +
  96 + $('.onion-skin.view', @file).each (index, view) =>
  97 +
  98 + [maxWidth, maxHeight] = prepareFrames(view)
  99 +
  100 + $('.onion-skin-frame', view).css
  101 + width: maxWidth + 16
  102 + height: maxHeight + 28
  103 +
  104 + $('.swipe-wrap', view).css
  105 + width: maxWidth + 1
  106 + height: maxHeight + 2
  107 +
  108 + $('.dragger', view).css
  109 + left: dragTrackWidth
  110 + .draggable
  111 + axis: 'x'
  112 + containment: 'parent'
  113 + drag: (event) ->
  114 + $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth)
  115 + stop: (event) ->
  116 + $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth)
  117 +
  118 +
  119 +
  120 + requestImageInfo: (img, callback) ->
  121 + domImg = img.get(0)
  122 + if domImg.complete
  123 + callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
  124 + else
  125 + img.on 'load', =>
  126 + callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
  127 +
  128 +this.ImageFile = ImageFile
0 \ No newline at end of file 129 \ No newline at end of file
app/assets/javascripts/commits.js
@@ -1,59 +0,0 @@ @@ -1,59 +0,0 @@
1 -var CommitsList = {  
2 - ref:null,  
3 - limit:0,  
4 - offset:0,  
5 - disable:false,  
6 -  
7 - init:  
8 - function(ref, limit) {  
9 - $(".day-commits-table li.commit").live('click', function(e){  
10 - if(e.target.nodeName != "A") {  
11 - location.href = $(this).attr("url");  
12 - e.stopPropagation();  
13 - return false;  
14 - }  
15 - });  
16 -  
17 - this.ref=ref;  
18 - this.limit=limit;  
19 - this.offset=limit;  
20 - this.initLoadMore();  
21 - $('.loading').show();  
22 - },  
23 -  
24 - getOld:  
25 - function() {  
26 - $('.loading').show();  
27 - $.ajax({  
28 - type: "GET",  
29 - url: location.href,  
30 - data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref,  
31 - complete: function(){ $('.loading').hide()},  
32 - dataType: "script"});  
33 - },  
34 -  
35 - append:  
36 - function(count, html) {  
37 - $("#commits_list").append(html);  
38 - if(count > 0) {  
39 - this.offset += count;  
40 - } else {  
41 - this.disable = true;  
42 - }  
43 - },  
44 -  
45 - initLoadMore:  
46 - function() {  
47 - $(document).endlessScroll({  
48 - bottomPixels: 400,  
49 - fireDelay: 1000,  
50 - fireOnce:true,  
51 - ceaseFire: function() {  
52 - return CommitsList.disable;  
53 - },  
54 - callback: function(i) {  
55 - CommitsList.getOld();  
56 - }  
57 - });  
58 - }  
59 -}  
app/assets/javascripts/commits.js.coffee 0 → 100644
@@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
  1 +class CommitsList
  2 + @data =
  3 + ref: null
  4 + limit: 0
  5 + offset: 0
  6 + @disable = false
  7 +
  8 + @showProgress: ->
  9 + $('.loading').show()
  10 +
  11 + @hideProgress: ->
  12 + $('.loading').hide()
  13 +
  14 + @init: (ref, limit) ->
  15 + $(".day-commits-table li.commit").live 'click', (event) ->
  16 + if event.target.nodeName != "A"
  17 + location.href = $(this).attr("url")
  18 + e.stopPropagation()
  19 + return false
  20 +
  21 + @data.ref = ref
  22 + @data.limit = limit
  23 + @data.offset = limit
  24 +
  25 + this.initLoadMore()
  26 + this.showProgress();
  27 +
  28 + @getOld: ->
  29 + this.showProgress()
  30 + $.ajax
  31 + type: "GET"
  32 + url: location.href
  33 + data: @data
  34 + complete: this.hideProgress
  35 + dataType: "script"
  36 +
  37 + @append: (count, html) ->
  38 + $("#commits-list").append(html)
  39 + if count > 0
  40 + @data.offset += count
  41 + else
  42 + @disable = true
  43 +
  44 + @initLoadMore: ->
  45 + $(document).endlessScroll
  46 + bottomPixels: 400
  47 + fireDelay: 1000
  48 + fireOnce: true
  49 + ceaseFire: =>
  50 + @disable
  51 + callback: =>
  52 + this.getOld()
  53 +
  54 +this.CommitsList = CommitsList
0 \ No newline at end of file 55 \ No newline at end of file
app/assets/javascripts/dashboard.js.coffee
@@ -4,11 +4,11 @@ window.dashboardPage = -> @@ -4,11 +4,11 @@ window.dashboardPage = ->
4 event.preventDefault() 4 event.preventDefault()
5 toggleFilter $(this) 5 toggleFilter $(this)
6 reloadActivities() 6 reloadActivities()
7 - 7 +
8 reloadActivities = -> 8 reloadActivities = ->
9 $(".content_list").html '' 9 $(".content_list").html ''
10 Pager.init 20, true 10 Pager.init 20, true
11 - 11 +
12 toggleFilter = (sender) -> 12 toggleFilter = (sender) ->
13 sender.parent().toggleClass "inactive" 13 sender.parent().toggleClass "inactive"
14 event_filters = $.cookie("event_filter") 14 event_filters = $.cookie("event_filter")
@@ -17,11 +17,11 @@ toggleFilter = (sender) -> @@ -17,11 +17,11 @@ toggleFilter = (sender) ->
17 event_filters = event_filters.split(",") 17 event_filters = event_filters.split(",")
18 else 18 else
19 event_filters = new Array() 19 event_filters = new Array()
20 - 20 +
21 index = event_filters.indexOf(filter) 21 index = event_filters.indexOf(filter)
22 if index is -1 22 if index is -1
23 event_filters.push filter 23 event_filters.push filter
24 else 24 else
25 event_filters.splice index, 1 25 event_filters.splice index, 1
26 - 26 +
27 $.cookie "event_filter", event_filters.join(",") 27 $.cookie "event_filter", event_filters.join(",")
app/assets/javascripts/merge_requests.js.coffee
1 # 1 #
2 # * Filter merge requests 2 # * Filter merge requests
3 -# 3 +#
4 @merge_requestsPage = -> 4 @merge_requestsPage = ->
5 $('#assignee_id').chosen() 5 $('#assignee_id').chosen()
6 $('#milestone_id').chosen() 6 $('#milestone_id').chosen()
@@ -8,16 +8,16 @@ @@ -8,16 +8,16 @@
8 $(this).closest('form').submit() 8 $(this).closest('form').submit()
9 9
10 class MergeRequest 10 class MergeRequest
11 - 11 +
12 constructor: (@opts) -> 12 constructor: (@opts) ->
13 this.$el = $('.merge-request') 13 this.$el = $('.merge-request')
14 @diffs_loaded = false 14 @diffs_loaded = false
15 @commits_loaded = false 15 @commits_loaded = false
16 - 16 +
17 this.activateTab(@opts.action) 17 this.activateTab(@opts.action)
18 - 18 +
19 this.bindEvents() 19 this.bindEvents()
20 - 20 +
21 this.initMergeWidget() 21 this.initMergeWidget()
22 this.$('.show-all-commits').on 'click', => 22 this.$('.show-all-commits').on 'click', =>
23 this.showAllCommits() 23 this.showAllCommits()
@@ -28,7 +28,7 @@ class MergeRequest @@ -28,7 +28,7 @@ class MergeRequest
28 28
29 initMergeWidget: -> 29 initMergeWidget: ->
30 this.showState( @opts.current_state ) 30 this.showState( @opts.current_state )
31 - 31 +
32 if this.$('.automerge_widget').length and @opts.check_enable 32 if this.$('.automerge_widget').length and @opts.check_enable
33 $.get @opts.url_to_automerge_check, (data) => 33 $.get @opts.url_to_automerge_check, (data) =>
34 this.showState( data.state ) 34 this.showState( data.state )
@@ -42,12 +42,12 @@ class MergeRequest @@ -42,12 +42,12 @@ class MergeRequest
42 bindEvents: -> 42 bindEvents: ->
43 this.$('.nav-tabs').on 'click', 'a', (event) => 43 this.$('.nav-tabs').on 'click', 'a', (event) =>
44 a = $(event.currentTarget) 44 a = $(event.currentTarget)
45 - 45 +
46 href = a.attr('href') 46 href = a.attr('href')
47 History.replaceState {path: href}, document.title, href 47 History.replaceState {path: href}, document.title, href
48 - 48 +
49 event.preventDefault() 49 event.preventDefault()
50 - 50 +
51 this.$('.nav-tabs').on 'click', 'li', (event) => 51 this.$('.nav-tabs').on 'click', 'li', (event) =>
52 this.activateTab($(event.currentTarget).data('action')) 52 this.activateTab($(event.currentTarget).data('action'))
53 53
app/assets/javascripts/pager.js
@@ -1,56 +0,0 @@ @@ -1,56 +0,0 @@
1 -var Pager = {  
2 - limit:0,  
3 - offset:0,  
4 - disable:false,  
5 -  
6 - init:  
7 - function(limit, preload) {  
8 - this.limit=limit;  
9 -  
10 - if(preload) {  
11 - this.offset = 0;  
12 - this.getOld();  
13 - } else {  
14 - this.offset = limit;  
15 - }  
16 -  
17 - this.initLoadMore();  
18 - },  
19 -  
20 - getOld:  
21 - function() {  
22 - $('.loading').show();  
23 - $.ajax({  
24 - type: "GET",  
25 - url: location.href,  
26 - data: "limit=" + this.limit + "&offset=" + this.offset,  
27 - complete: function(){ $('.loading').hide()},  
28 - dataType: "script"});  
29 - },  
30 -  
31 - append:  
32 - function(count, html) {  
33 - $(".content_list").append(html);  
34 - if(count > 0) {  
35 - this.offset += count;  
36 - } else {  
37 - this.disable = true;  
38 - }  
39 - },  
40 -  
41 - initLoadMore:  
42 - function() {  
43 - $(document).endlessScroll({  
44 - bottomPixels: 400,  
45 - fireDelay: 1000,  
46 - fireOnce:true,  
47 - ceaseFire: function() {  
48 - return Pager.disable;  
49 - },  
50 - callback: function(i) {  
51 - $('.loading').show();  
52 - Pager.getOld();  
53 - }  
54 - });  
55 - }  
56 -}  
app/assets/javascripts/pager.js.coffee 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  1 +@Pager =
  2 + limit: 0
  3 + offset: 0
  4 + disable: false
  5 + init: (limit, preload) ->
  6 + @limit = limit
  7 + if preload
  8 + @offset = 0
  9 + @getOld()
  10 + else
  11 + @offset = limit
  12 + @initLoadMore()
  13 +
  14 + getOld: ->
  15 + $(".loading").show()
  16 + $.ajax
  17 + type: "GET"
  18 + url: location.href
  19 + data: "limit=" + @limit + "&offset=" + @offset
  20 + complete: ->
  21 + $(".loading").hide()
  22 +
  23 + dataType: "script"
  24 +
  25 + append: (count, html) ->
  26 + $(".content_list").append html
  27 + if count > 0
  28 + @offset += count
  29 + else
  30 + @disable = true
  31 +
  32 + initLoadMore: ->
  33 + $(document).endlessScroll
  34 + bottomPixels: 400
  35 + fireDelay: 1000
  36 + fireOnce: true
  37 + ceaseFire: ->
  38 + Pager.disable
  39 +
  40 + callback: (i) ->
  41 + $(".loading").show()
  42 + Pager.getOld()
app/assets/stylesheets/common.scss
1 html { 1 html {
2 - overflow-y: scroll; 2 + overflow-y: scroll;
3 } 3 }
4 4
5 /** LAYOUT **/ 5 /** LAYOUT **/
@@ -277,8 +277,20 @@ p.time { @@ -277,8 +277,20 @@ p.time {
277 } 277 }
278 } 278 }
279 279
  280 +.search-holder {
  281 + label, input {
  282 + height: 30px;
  283 + padding: 0;
  284 + font-size: 14px;
  285 + }
  286 + label {
  287 + line-height: 30px;
  288 + color: #666;
  289 + }
  290 +}
  291 +
280 .highlight_word { 292 .highlight_word {
281 - background: #EEDC94; 293 + border-bottom: 2px solid #F90;
282 } 294 }
283 295
284 .status_info { 296 .status_info {
@@ -326,7 +338,7 @@ li.note { @@ -326,7 +338,7 @@ li.note {
326 li { 338 li {
327 border-bottom:none !important; 339 border-bottom:none !important;
328 } 340 }
329 - .file { 341 + .attachment {
330 padding-left: 20px; 342 padding-left: 20px;
331 background:url("icon-attachment.png") no-repeat left center; 343 background:url("icon-attachment.png") no-repeat left center;
332 } 344 }
app/assets/stylesheets/gitlab_bootstrap/files.scss
@@ -135,7 +135,7 @@ @@ -135,7 +135,7 @@
135 pre { 135 pre {
136 border: none; 136 border: none;
137 border-radius: 0; 137 border-radius: 0;
138 - font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; 138 + font-family: $monospace_font;
139 font-size: 12px !important; 139 font-size: 12px !important;
140 line-height: 16px !important; 140 line-height: 16px !important;
141 margin: 0; 141 margin: 0;
app/assets/stylesheets/gitlab_bootstrap/fonts.scss
@@ -4,4 +4,4 @@ @@ -4,4 +4,4 @@
4 } 4 }
5 5
6 /** Typo **/ 6 /** Typo **/
7 -$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; 7 +$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
app/assets/stylesheets/gitlab_bootstrap/typography.scss
@@ -21,7 +21,7 @@ h6 { @@ -21,7 +21,7 @@ h6 {
21 21
22 /** CODE **/ 22 /** CODE **/
23 pre { 23 pre {
24 - font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; 24 + font-family: $monospace_font;
25 25
26 &.dark { 26 &.dark {
27 background: #333; 27 background: #333;
@@ -79,7 +79,7 @@ a:focus { @@ -79,7 +79,7 @@ a:focus {
79 } 79 }
80 80
81 .monospace { 81 .monospace {
82 - font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; 82 + font-family: $monospace_font;
83 } 83 }
84 84
85 /** 85 /**
app/assets/stylesheets/gitlab_bootstrap/variables.scss
1 -/** Colors **/ 1 +/**
  2 + * General Colors
  3 + */
2 $primary_color: #2FA0BB; 4 $primary_color: #2FA0BB;
3 $link_color: #3A89A3; 5 $link_color: #3A89A3;
4 $style_color: #474D57; 6 $style_color: #474D57;
5 $hover: #D9EDF7; 7 $hover: #D9EDF7;
  8 +
  9 +/**
  10 + * Commit Diff Colors
  11 + */
  12 +$added: #63c363;
  13 +$deleted: #f77;
app/assets/stylesheets/sections/commits.scss
1 /** 1 /**
2 - *  
3 - * COMMIT SHOw  
4 - * 2 + * Commit file
5 */ 3 */
6 .commit-committer-link, 4 .commit-committer-link,
7 .commit-author-link { 5 .commit-author-link {
@@ -12,11 +10,11 @@ @@ -12,11 +10,11 @@
12 } 10 }
13 } 11 }
14 12
15 -.diff_file { 13 +.file {
16 border: 1px solid #CCC; 14 border: 1px solid #CCC;
17 margin-bottom: 1em; 15 margin-bottom: 1em;
18 16
19 - .diff_file_header { 17 + .header {
20 @extend .clearfix; 18 @extend .clearfix;
21 padding: 5px 5px 5px 10px; 19 padding: 5px 5px 5px 10px;
22 color: #555; 20 color: #555;
@@ -28,32 +26,35 @@ @@ -28,32 +26,35 @@
28 background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); 26 background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
29 background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); 27 background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
30 28
  29 + a{
  30 + color: $style_color;
  31 + }
  32 +
31 > span { 33 > span {
32 - font-family: $monospace; 34 + font-family: $monospace_font;
33 font-size: 14px; 35 font-size: 14px;
34 line-height: 30px; 36 line-height: 30px;
35 } 37 }
36 38
37 - a.view-commit{ 39 + a.view-file{
38 font-weight: bold; 40 font-weight: bold;
39 } 41 }
40 42
41 .commit-short-id{ 43 .commit-short-id{
42 - font-family: $monospace; 44 + font-family: $monospace_font;
43 font-size: smaller; 45 font-size: smaller;
44 } 46 }
45 47
46 .file-mode{ 48 .file-mode{
47 - font-family: $monospace; 49 + font-family: $monospace_font;
48 } 50 }
49 } 51 }
50 - .diff_file_content { 52 + .content {
51 overflow: auto; 53 overflow: auto;
52 overflow-y: hidden; 54 overflow-y: hidden;
53 - background: #fff; 55 + background: #FFF;
54 color: #333; 56 color: #333;
55 font-size: 12px; 57 font-size: 12px;
56 - font-family: $monospace;  
57 .old{ 58 .old{
58 span.idiff{ 59 span.idiff{
59 background-color: #FAA; 60 background-color: #FAA;
@@ -66,114 +67,274 @@ @@ -66,114 +67,274 @@
66 } 67 }
67 68
68 table { 69 table {
  70 + font-family: $monospace_font;
  71 + border: none;
  72 + margin: 0px;
  73 + padding: 0px;
69 td { 74 td {
70 line-height: 18px; 75 line-height: 18px;
  76 + font-size: 12px;
  77 + }
  78 + }
  79 + .old_line, .new_line {
  80 + margin: 0px;
  81 + padding: 0px;
  82 + border: none;
  83 + background: #EEE;
  84 + color: #666;
  85 + padding: 0px 5px;
  86 + border-right: 1px solid #ccc;
  87 + text-align: right;
  88 + min-width: 35px;
  89 + max-width: 35px;
  90 + width: 35px;
  91 + @include user-select(none);
  92 + a {
  93 + float: left;
  94 + width: 35px;
  95 + font-weight: normal;
  96 + color: #666;
  97 + &:hover {
  98 + text-decoration: underline;
  99 + }
  100 + }
  101 + }
  102 + .line_content {
  103 + white-space: pre;
  104 + height: 14px;
  105 + margin: 0px;
  106 + padding: 0px;
  107 + border: none;
  108 + &.new {
  109 + background: #CFD;
  110 + }
  111 + &.old {
  112 + background: #FDD;
  113 + }
  114 + &.matched {
  115 + color: #ccc;
  116 + background: #fafafa;
71 } 117 }
72 } 118 }
73 } 119 }
74 - .diff_file_content_image {  
75 - background: #eee; 120 + .image {
  121 + background: #ddd;
76 text-align: center; 122 text-align: center;
77 - .image { 123 + padding: 30px;
  124 + .wrap{
78 display: inline-block; 125 display: inline-block;
79 - margin: 50px;  
80 - max-width: 400px;  
81 - 126 + }
  127 +
  128 + .frame {
  129 + display: inline-block;
  130 + background-color: #fff;
  131 + line-height: 0;
82 img{ 132 img{
  133 + border: 1px solid #FFF;
83 background: url('trans_bg.gif'); 134 background: url('trans_bg.gif');
84 } 135 }
  136 + &.deleted {
  137 + border: 1px solid $deleted;
  138 + }
85 139
86 - &.diff_removed {  
87 - img{  
88 - border: 1px solid #C00;  
89 - } 140 + &.added {
  141 + border: 1px solid $added;
90 } 142 }
  143 + }
  144 + .image-info{
  145 + font-size: 12px;
  146 + margin: 5px 0 0 0;
  147 + color: grey;
  148 + }
91 149
92 - &.diff_added {  
93 - img{  
94 - border: 1px solid #0C0; 150 + .view.swipe{
  151 + position: relative;
  152 +
  153 + .swipe-frame{
  154 + display: block;
  155 + margin: auto;
  156 + position: relative;
  157 + }
  158 + .swipe-wrap{
  159 + overflow: hidden;
  160 + border-left: 1px solid #999;
  161 + position: absolute;
  162 + display: block;
  163 + top: 13px;
  164 + right: 7px;
  165 + }
  166 + .frame{
  167 + top: 0;
  168 + right: 0;
  169 + position: absolute;
  170 + &.deleted{
  171 + margin: 0;
  172 + display: block;
  173 + top: 13px;
  174 + right: 7px;
95 } 175 }
96 } 176 }
97 -  
98 - .image-info{  
99 - margin: 5px 0 0 0; 177 + .swipe-bar{
  178 + display: block;
  179 + height: 100%;
  180 + width: 15px;
  181 + z-index: 100;
  182 + position: absolute;
  183 + cursor: pointer;
  184 + &:hover{
  185 + .top-handle{
  186 + background-position: -15px 3px;
  187 + }
  188 + .bottom-handle{
  189 + background-position: -15px -11px;
  190 + }
  191 + };
  192 + .top-handle{
  193 + display: block;
  194 + height: 14px;
  195 + width: 15px;
  196 + position: absolute;
  197 + top: 0px;
  198 + background: url('swipemode_sprites.gif') 0 3px no-repeat;
  199 + }
  200 + .bottom-handle{
  201 + display: block;
  202 + height: 14px;
  203 + width: 15px;
  204 + position: absolute;
  205 + bottom: 0px;
  206 + background: url('swipemode_sprites.gif') 0 -11px no-repeat;
  207 + }
100 } 208 }
101 - }  
102 -  
103 - &.img_compared {  
104 - .image {  
105 - max-width: 300px; 209 + } //.view.swipe
  210 + .view.onion-skin{
  211 + .onion-skin-frame{
  212 + display: block;
  213 + margin: auto;
  214 + position: relative;
106 } 215 }
107 - } 216 + .frame.added, .frame.deleted {
  217 + position: absolute;
  218 + display: block;
  219 + top: 0px;
  220 + left: 0px;
  221 + }
  222 + .controls{
  223 + display: block;
  224 + height: 14px;
  225 + width: 300px;
  226 + z-index: 100;
  227 + position: absolute;
  228 + bottom: 0px;
  229 + left: 50%;
  230 + margin-left: -150px;
  231 +
  232 + .drag-track{
  233 + display: block;
  234 + position: absolute;
  235 + left: 12px;
  236 + height: 10px;
  237 + width: 276px;
  238 + background: url('onion_skin_sprites.gif') -4px -20px repeat-x;
  239 + }
  240 +
  241 + .dragger {
  242 + display: block;
  243 + position: absolute;
  244 + left: 0px;
  245 + top: 0px;
  246 + height: 14px;
  247 + width: 14px;
  248 + background: url('onion_skin_sprites.gif') 0px -34px repeat-x;
  249 + cursor: pointer;
  250 + }
  251 +
  252 + .transparent {
  253 + display: block;
  254 + position: absolute;
  255 + top: 2px;
  256 + right: 0px;
  257 + height: 10px;
  258 + width: 10px;
  259 + background: url('onion_skin_sprites.gif') -2px 0px no-repeat;
  260 + }
  261 +
  262 + .opaque {
  263 + display: block;
  264 + position: absolute;
  265 + top: 2px;
  266 + left: 0px;
  267 + height: 10px;
  268 + width: 10px;
  269 + background: url('onion_skin_sprites.gif') -2px -10px no-repeat;
  270 + }
  271 + }
  272 + } //.view.onion-skin
108 } 273 }
109 -} 274 + .view-modes{
110 275
111 -.diff_file_content{  
112 - table {  
113 - border: none;  
114 - margin: 0px;  
115 - padding: 0px;  
116 - tr {  
117 - td {  
118 - font-size: 12px;  
119 - } 276 + padding: 10px;
  277 + text-align: center;
  278 +
  279 + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
  280 + background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
  281 + background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
  282 + background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
  283 +
  284 + ul, li{
  285 + list-style: none;
  286 + margin: 0;
  287 + padding: 0;
  288 + display: inline-block;
120 } 289 }
121 - }  
122 - .new_line,  
123 - .old_line,  
124 - .notes_line {  
125 - margin:0px;  
126 - padding:0px;  
127 - border:none;  
128 - background:#EEE;  
129 - color:#666;  
130 - padding: 0px 5px;  
131 - border-right: 1px solid #ccc;  
132 - text-align: right;  
133 - min-width: 35px;  
134 - max-width: 35px;  
135 - width: 35px;  
136 - moz-user-select: none;  
137 - -khtml-user-select: none;  
138 - user-select: none;  
139 -  
140 - a {  
141 - float: left;  
142 - width: 35px;  
143 - font-weight: normal;  
144 - color: #666;  
145 - &:hover { 290 +
  291 + li{
  292 + color: grey;
  293 + border-left: 1px solid #c1c1c1;
  294 + padding: 0 12px 0 16px;
  295 + cursor: pointer;
  296 + &:first-child{
  297 + border-left: none;
  298 + }
  299 + &:hover{
146 text-decoration: underline; 300 text-decoration: underline;
147 } 301 }
148 - }  
149 - }  
150 - .line_content {  
151 - white-space: pre;  
152 - height: 14px;  
153 - margin: 0px;  
154 - padding: 0px;  
155 - border: none;  
156 - &.new {  
157 - background: #CFD;  
158 - }  
159 - &.old {  
160 - background: #FDD;  
161 - }  
162 - &.matched {  
163 - color: #ccc;  
164 - background: #fafafa; 302 + &.active{
  303 + &:hover{
  304 + text-decoration: none;
  305 + }
  306 + cursor: default;
  307 + color: #333;
  308 + }
  309 + &.disabled{
  310 + display: none;
  311 + }
165 } 312 }
166 } 313 }
167 } 314 }
168 315
169 /** COMMIT BLOCK **/ 316 /** COMMIT BLOCK **/
170 -.commit-title{display: block;}  
171 -.commit-title{margin-bottom: 10px}  
172 -.commit-author, .commit-committer{display: block;color: #999; font-weight: normal; font-style: italic;}  
173 -.commit-author strong, .commit-committer strong{font-weight: bold; font-style: normal;} 317 +.commit-title{
  318 + display: block;
  319 +}
  320 +.commit-title{
  321 + margin-bottom: 10px;
  322 +}
  323 +.commit-author, .commit-committer{
  324 + display: block;
  325 + color: #999;
  326 + font-weight: normal;
  327 + font-style: italic;
  328 +}
  329 +.commit-author strong, .commit-committer strong{
  330 + font-weight: bold;
  331 + font-style: normal;
  332 +}
174 333
175 334
176 -/** COMMIT ROW **/ 335 +/**
  336 + * COMMIT ROW
  337 + */
177 .commit { 338 .commit {
178 .browse_code_link_holder { 339 .browse_code_link_holder {
179 @extend .span2; 340 @extend .span2;
@@ -199,11 +360,10 @@ @@ -199,11 +360,10 @@
199 float: left; 360 float: left;
200 @extend .lined; 361 @extend .lined;
201 min-width: 65px; 362 min-width: 65px;
202 - font-family: $monospace; 363 + font-family: $monospace_font;
203 } 364 }
204 } 365 }
205 366
206 -.diff_file_header a,  
207 .file-stats a { 367 .file-stats a {
208 color: $style_color; 368 color: $style_color;
209 } 369 }
@@ -237,7 +397,7 @@ @@ -237,7 +397,7 @@
237 font-size: 13px; 397 font-size: 13px;
238 background: #474D57; 398 background: #474D57;
239 color: #fff; 399 color: #fff;
240 - font-family: $monospace; 400 + font-family: $monospace_font;
241 } 401 }
242 402
243 403
app/assets/stylesheets/sections/merge_requests.scss
@@ -77,7 +77,7 @@ li.merge_request { @@ -77,7 +77,7 @@ li.merge_request {
77 font-size: 14px; 77 font-size: 14px;
78 background: #474D57; 78 background: #474D57;
79 color: #fff; 79 color: #fff;
80 - font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; 80 + font-family: $monospace_font;
81 } 81 }
82 82
83 .mr_source_commit, 83 .mr_source_commit,
app/assets/stylesheets/sections/notes.scss
@@ -40,13 +40,13 @@ ul.notes { @@ -40,13 +40,13 @@ ul.notes {
40 .discussion-body { 40 .discussion-body {
41 margin-left: 50px; 41 margin-left: 50px;
42 42
43 - .diff_file, 43 + .file,
44 .discussion-hidden, 44 .discussion-hidden,
45 .notes { 45 .notes {
46 @extend .borders; 46 @extend .borders;
47 background-color: #F9F9F9; 47 background-color: #F9F9F9;
48 } 48 }
49 - .diff_file .notes { 49 + .file .notes {
50 /* reset */ 50 /* reset */
51 background: inherit; 51 background: inherit;
52 border: none; 52 border: none;
@@ -109,7 +109,7 @@ ul.notes { @@ -109,7 +109,7 @@ ul.notes {
109 } 109 }
110 } 110 }
111 111
112 -.diff_file .notes_holder { 112 +.file .notes_holder {
113 font-family: $sansFontFamily; 113 font-family: $sansFontFamily;
114 font-size: 13px; 114 font-size: 13px;
115 line-height: 18px; 115 line-height: 18px;
@@ -134,8 +134,6 @@ ul.notes { @@ -134,8 +134,6 @@ ul.notes {
134 } 134 }
135 } 135 }
136 136
137 -  
138 -  
139 /** 137 /**
140 * Actions for Discussions/Notes 138 * Actions for Discussions/Notes
141 */ 139 */
@@ -171,7 +169,7 @@ ul.notes { @@ -171,7 +169,7 @@ ul.notes {
171 } 169 }
172 } 170 }
173 } 171 }
174 -.diff_file .note .note-actions { 172 +.file .note .note-actions {
175 right: 0; 173 right: 0;
176 top: 0; 174 top: 0;
177 } 175 }
@@ -182,7 +180,7 @@ ul.notes { @@ -182,7 +180,7 @@ ul.notes {
182 * Line note button on the side of diffs 180 * Line note button on the side of diffs
183 */ 181 */
184 182
185 -.diff_file tr.line_holder { 183 +.file tr.line_holder {
186 .add-diff-note { 184 .add-diff-note {
187 background: url("diff_note_add.png") no-repeat left 0; 185 background: url("diff_note_add.png") no-repeat left 0;
188 height: 22px; 186 height: 22px;
@@ -212,8 +210,6 @@ ul.notes { @@ -212,8 +210,6 @@ ul.notes {
212 } 210 }
213 } 211 }
214 212
215 -  
216 -  
217 /** 213 /**
218 * Note Form 214 * Note Form
219 */ 215 */
@@ -222,7 +218,12 @@ ul.notes { @@ -222,7 +218,12 @@ ul.notes {
222 .reply-btn { 218 .reply-btn {
223 @extend .save-btn; 219 @extend .save-btn;
224 } 220 }
225 -.diff_file, 221 +.file .content tr.line_holder:hover > td { background: $hover !important; }
  222 +.file .content tr.line_holder:hover > td .line_note_link {
  223 + opacity: 1.0;
  224 + filter: alpha(opacity=100);
  225 +}
  226 +.file,
226 .discussion { 227 .discussion {
227 .new_note { 228 .new_note {
228 margin: 8px 5px 8px 0; 229 margin: 8px 5px 8px 0;
app/assets/stylesheets/sections/projects.scss
@@ -6,7 +6,6 @@ @@ -6,7 +6,6 @@
6 .side { 6 .side {
7 @extend .right; 7 @extend .right;
8 8
9 - .groups_box,  
10 .projects_box { 9 .projects_box {
11 > .title { 10 > .title {
12 padding: 2px 15px; 11 padding: 2px 15px;
app/controllers/admin/application_controller.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +# Provides a base class for Admin controllers to subclass
  2 +#
  3 +# Automatically sets the layout and ensures an administrator is logged in
  4 +class Admin::ApplicationController < ApplicationController
  5 + layout 'admin'
  6 + before_filter :authenticate_admin!
  7 +
  8 + def authenticate_admin!
  9 + return render_404 unless current_user.is_admin?
  10 + end
  11 +end
app/controllers/admin/dashboard_controller.rb
1 -class Admin::DashboardController < AdminController 1 +class Admin::DashboardController < Admin::ApplicationController
2 def index 2 def index
3 @projects = Project.order("created_at DESC").limit(10) 3 @projects = Project.order("created_at DESC").limit(10)
4 @users = User.order("created_at DESC").limit(10) 4 @users = User.order("created_at DESC").limit(10)
app/controllers/admin/groups_controller.rb
1 -class Admin::GroupsController < AdminController 1 +class Admin::GroupsController < Admin::ApplicationController
2 before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update] 2 before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update]
3 3
4 def index 4 def index
app/controllers/admin/hooks_controller.rb
1 -class Admin::HooksController < AdminController 1 +class Admin::HooksController < Admin::ApplicationController
2 def index 2 def index
3 @hooks = SystemHook.all 3 @hooks = SystemHook.all
4 @hook = SystemHook.new 4 @hook = SystemHook.new
app/controllers/admin/logs_controller.rb
1 -class Admin::LogsController < AdminController 1 +class Admin::LogsController < Admin::ApplicationController
2 end 2 end
app/controllers/admin/projects/application_controller.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +# Provides a base class for Admin controllers to subclass
  2 +#
  3 +# Automatically sets the layout and ensures an administrator is logged in
  4 +class Admin::Projects::ApplicationController < Admin::ApplicationController
  5 +
  6 + protected
  7 +
  8 + def project
  9 + @project ||= Project.find_with_namespace(params[:project_id])
  10 + end
  11 +end
app/controllers/admin/projects/members_controller.rb 0 → 100644
@@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
  1 +class Admin::Projects::MembersController < Admin::Projects::ApplicationController
  2 + def edit
  3 + @member = team_member
  4 + @project = project
  5 + @team_member_relation = team_member_relation
  6 + end
  7 +
  8 + def update
  9 + if team_member_relation.update_attributes(params[:team_member])
  10 + redirect_to [:admin, project], notice: 'Project Access was successfully updated.'
  11 + else
  12 + render action: "edit"
  13 + end
  14 + end
  15 +
  16 + def destroy
  17 + team_member_relation.destroy
  18 +
  19 + redirect_to :back
  20 + end
  21 +
  22 + private
  23 +
  24 + def team_member
  25 + @member ||= project.users.find_by_username(params[:id])
  26 + end
  27 +
  28 + def team_member_relation
  29 + team_member.users_projects.find_by_project_id(project)
  30 + end
  31 +
  32 +end
app/controllers/admin/projects_controller.rb
1 -class Admin::ProjectsController < AdminController 1 +class Admin::ProjectsController < Admin::ApplicationController
2 before_filter :project, only: [:edit, :show, :update, :destroy, :team_update] 2 before_filter :project, only: [:edit, :show, :update, :destroy, :team_update]
3 3
4 def index 4 def index
@@ -29,7 +29,9 @@ class Admin::ProjectsController &lt; AdminController @@ -29,7 +29,9 @@ class Admin::ProjectsController &lt; AdminController
29 end 29 end
30 30
31 def update 31 def update
32 - status = Projects::UpdateContext.new(project, current_user, params).execute(:admin) 32 + project.creator = current_user unless project.creator
  33 +
  34 + status = ::Projects::UpdateContext.new(project, current_user, params).execute(:admin)
33 35
34 if status 36 if status
35 redirect_to [:admin, @project], notice: 'Project was successfully updated.' 37 redirect_to [:admin, @project], notice: 'Project was successfully updated.'
app/controllers/admin/resque_controller.rb
1 -class Admin::ResqueController < AdminController 1 +class Admin::ResqueController < Admin::ApplicationController
2 def show 2 def show
3 end 3 end
4 end 4 end
app/controllers/admin/team_members_controller.rb
@@ -1,22 +0,0 @@ @@ -1,22 +0,0 @@
1 -class Admin::TeamMembersController < AdminController  
2 - def edit  
3 - @admin_team_member = UsersProject.find(params[:id])  
4 - end  
5 -  
6 - def update  
7 - @admin_team_member = UsersProject.find(params[:id])  
8 -  
9 - if @admin_team_member.update_attributes(params[:team_member])  
10 - redirect_to [:admin, @admin_team_member.project], notice: 'Project Access was successfully updated.'  
11 - else  
12 - render action: "edit"  
13 - end  
14 - end  
15 -  
16 - def destroy  
17 - @admin_team_member = UsersProject.find(params[:id])  
18 - @admin_team_member.destroy  
19 -  
20 - redirect_to :back  
21 - end  
22 -end  
app/controllers/admin/teams/application_controller.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +# Provides a base class for Admin controllers to subclass
  2 +#
  3 +# Automatically sets the layout and ensures an administrator is logged in
  4 +class Admin::Teams::ApplicationController < Admin::ApplicationController
  5 +
  6 + private
  7 +
  8 + def user_team
  9 + @team = UserTeam.find_by_path(params[:team_id])
  10 + end
  11 +end
app/controllers/admin/teams/members_controller.rb 0 → 100644
@@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
  1 +class Admin::Teams::MembersController < Admin::Teams::ApplicationController
  2 + def new
  3 + @users = User.potential_team_members(user_team)
  4 + @users = UserDecorator.decorate @users
  5 + end
  6 +
  7 + def create
  8 + unless params[:user_ids].blank?
  9 + user_ids = params[:user_ids]
  10 + access = params[:default_project_access]
  11 + is_admin = params[:group_admin]
  12 + user_team.add_members(user_ids, access, is_admin)
  13 + end
  14 +
  15 + redirect_to admin_team_path(user_team), notice: 'Members was successfully added into Team of users.'
  16 + end
  17 +
  18 + def edit
  19 + team_member
  20 + end
  21 +
  22 + def update
  23 + options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]}
  24 + if user_team.update_membership(team_member, options)
  25 + redirect_to admin_team_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
  26 + else
  27 + render :edit
  28 + end
  29 + end
  30 +
  31 + def destroy
  32 + user_team.remove_member(team_member)
  33 + redirect_to admin_team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users."
  34 + end
  35 +
  36 + protected
  37 +
  38 + def team_member
  39 + @member ||= user_team.members.find_by_username(params[:id])
  40 + end
  41 +end
app/controllers/admin/teams/projects_controller.rb 0 → 100644
@@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
  1 +class Admin::Teams::ProjectsController < Admin::Teams::ApplicationController
  2 + def new
  3 + @projects = Project.scoped
  4 + @projects = @projects.without_team(user_team) if user_team.projects.any?
  5 + #@projects.reject!(&:empty_repo?)
  6 + end
  7 +
  8 + def create
  9 + unless params[:project_ids].blank?
  10 + project_ids = params[:project_ids]
  11 + access = params[:greatest_project_access]
  12 + user_team.assign_to_projects(project_ids, access)
  13 + end
  14 +
  15 + redirect_to admin_team_path(user_team), notice: 'Team of users was successfully assgned to projects.'
  16 + end
  17 +
  18 + def edit
  19 + team_project
  20 + end
  21 +
  22 + def update
  23 + if user_team.update_project_access(team_project, params[:greatest_project_access])
  24 + redirect_to admin_team_path(user_team), notice: 'Access was successfully updated.'
  25 + else
  26 + render :edit
  27 + end
  28 + end
  29 +
  30 + def destroy
  31 + user_team.resign_from_project(team_project)
  32 + redirect_to admin_team_path(user_team), notice: 'Team of users was successfully reassigned from project.'
  33 + end
  34 +
  35 + protected
  36 +
  37 + def team_project
  38 + @project ||= user_team.projects.find_with_namespace(params[:id])
  39 + end
  40 +
  41 +end
app/controllers/admin/teams_controller.rb 0 → 100644
@@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
  1 +class Admin::TeamsController < Admin::ApplicationController
  2 + def index
  3 + @teams = UserTeam.order('name ASC')
  4 + @teams = @teams.search(params[:name]) if params[:name].present?
  5 + @teams = @teams.page(params[:page]).per(20)
  6 + end
  7 +
  8 + def show
  9 + user_team
  10 + end
  11 +
  12 + def new
  13 + @team = UserTeam.new
  14 + end
  15 +
  16 + def edit
  17 + user_team
  18 + end
  19 +
  20 + def create
  21 + @team = UserTeam.new(params[:user_team])
  22 + @team.path = @team.name.dup.parameterize if @team.name
  23 + @team.owner = current_user
  24 +
  25 + if @team.save
  26 + redirect_to admin_team_path(@team), notice: 'Team of users was successfully created.'
  27 + else
  28 + render action: "new"
  29 + end
  30 + end
  31 +
  32 + def update
  33 + user_team_params = params[:user_team].dup
  34 + owner_id = user_team_params.delete(:owner_id)
  35 +
  36 + if owner_id
  37 + user_team.owner = User.find(owner_id)
  38 + end
  39 +
  40 + if user_team.update_attributes(user_team_params)
  41 + redirect_to admin_team_path(user_team), notice: 'Team of users was successfully updated.'
  42 + else
  43 + render action: "edit"
  44 + end
  45 + end
  46 +
  47 + def destroy
  48 + user_team.destroy
  49 +
  50 + redirect_to admin_teams_path, notice: 'Team of users was successfully deleted.'
  51 + end
  52 +
  53 + protected
  54 +
  55 + def user_team
  56 + @team ||= UserTeam.find_by_path(params[:id])
  57 + end
  58 +
  59 +end
app/controllers/admin/users_controller.rb
1 -class Admin::UsersController < AdminController 1 +class Admin::UsersController < Admin::ApplicationController
  2 + before_filter :admin_user, only: [:show, :edit, :update, :destroy]
  3 +
2 def index 4 def index
3 @admin_users = User.scoped 5 @admin_users = User.scoped
4 @admin_users = @admin_users.filter(params[:filter]) 6 @admin_users = @admin_users.filter(params[:filter])
@@ -7,25 +9,18 @@ class Admin::UsersController &lt; AdminController @@ -7,25 +9,18 @@ class Admin::UsersController &lt; AdminController
7 end 9 end
8 10
9 def show 11 def show
10 - @admin_user = User.find(params[:id])  
11 -  
12 - @projects = if @admin_user.authorized_projects.empty?  
13 - Project  
14 - else  
15 - Project.without_user(@admin_user)  
16 - end.all 12 + @projects = Project.scoped
  13 + @projects = @projects.without_user(admin_user) if admin_user.authorized_projects.present?
17 end 14 end
18 15
19 def team_update 16 def team_update
20 - @admin_user = User.find(params[:id])  
21 -  
22 UsersProject.add_users_into_projects( 17 UsersProject.add_users_into_projects(
23 params[:project_ids], 18 params[:project_ids],
24 - [@admin_user.id], 19 + [admin_user.id],
25 params[:project_access] 20 params[:project_access]
26 ) 21 )
27 22
28 - redirect_to [:admin, @admin_user], notice: 'Teams were successfully updated.' 23 + redirect_to [:admin, admin_user], notice: 'Teams were successfully updated.'
29 end 24 end
30 25
31 26
@@ -34,13 +29,11 @@ class Admin::UsersController &lt; AdminController @@ -34,13 +29,11 @@ class Admin::UsersController &lt; AdminController
34 end 29 end
35 30
36 def edit 31 def edit
37 - @admin_user = User.find(params[:id]) 32 + admin_user
38 end 33 end
39 34
40 def block 35 def block
41 - @admin_user = User.find(params[:id])  
42 -  
43 - if @admin_user.block 36 + if admin_user.block
44 redirect_to :back, alert: "Successfully blocked" 37 redirect_to :back, alert: "Successfully blocked"
45 else 38 else
46 redirect_to :back, alert: "Error occured. User was not blocked" 39 redirect_to :back, alert: "Error occured. User was not blocked"
@@ -48,9 +41,7 @@ class Admin::UsersController &lt; AdminController @@ -48,9 +41,7 @@ class Admin::UsersController &lt; AdminController
48 end 41 end
49 42
50 def unblock 43 def unblock
51 - @admin_user = User.find(params[:id])  
52 -  
53 - if @admin_user.update_attribute(:blocked, false) 44 + if admin_user.update_attribute(:blocked, false)
54 redirect_to :back, alert: "Successfully unblocked" 45 redirect_to :back, alert: "Successfully unblocked"
55 else 46 else
56 redirect_to :back, alert: "Error occured. User was not unblocked" 47 redirect_to :back, alert: "Error occured. User was not unblocked"
@@ -82,30 +73,34 @@ class Admin::UsersController &lt; AdminController @@ -82,30 +73,34 @@ class Admin::UsersController &lt; AdminController
82 params[:user].delete(:password_confirmation) 73 params[:user].delete(:password_confirmation)
83 end 74 end
84 75
85 - @admin_user = User.find(params[:id])  
86 - @admin_user.admin = (admin && admin.to_i > 0) 76 + admin_user.admin = (admin && admin.to_i > 0)
87 77
88 respond_to do |format| 78 respond_to do |format|
89 - if @admin_user.update_attributes(params[:user], as: :admin)  
90 - format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } 79 + if admin_user.update_attributes(params[:user], as: :admin)
  80 + format.html { redirect_to [:admin, admin_user], notice: 'User was successfully updated.' }
91 format.json { head :ok } 81 format.json { head :ok }
92 else 82 else
93 format.html { render action: "edit" } 83 format.html { render action: "edit" }
94 - format.json { render json: @admin_user.errors, status: :unprocessable_entity } 84 + format.json { render json: admin_user.errors, status: :unprocessable_entity }
95 end 85 end
96 end 86 end
97 end 87 end
98 88
99 def destroy 89 def destroy
100 - @admin_user = User.find(params[:id])  
101 - if @admin_user.personal_projects.count > 0 90 + if admin_user.personal_projects.count > 0
102 redirect_to admin_users_path, alert: "User is a project owner and can't be removed." and return 91 redirect_to admin_users_path, alert: "User is a project owner and can't be removed." and return
103 end 92 end
104 - @admin_user.destroy 93 + admin_user.destroy
105 94
106 respond_to do |format| 95 respond_to do |format|
107 - format.html { redirect_to admin_users_url } 96 + format.html { redirect_to admin_users_path }
108 format.json { head :ok } 97 format.json { head :ok }
109 end 98 end
110 end 99 end
  100 +
  101 + protected
  102 +
  103 + def admin_user
  104 + @admin_user ||= User.find_by_username!(params[:id])
  105 + end
111 end 106 end
app/controllers/admin_controller.rb
@@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
1 -# Provides a base class for Admin controllers to subclass  
2 -#  
3 -# Automatically sets the layout and ensures an administrator is logged in  
4 -class AdminController < ApplicationController  
5 - layout 'admin'  
6 - before_filter :authenticate_admin!  
7 -  
8 - def authenticate_admin!  
9 - return render_404 unless current_user.is_admin?  
10 - end  
11 -end  
app/controllers/application_controller.rb
@@ -94,6 +94,18 @@ class ApplicationController &lt; ActionController::Base @@ -94,6 +94,18 @@ class ApplicationController &lt; ActionController::Base
94 return access_denied! unless can?(current_user, :download_code, project) 94 return access_denied! unless can?(current_user, :download_code, project)
95 end 95 end
96 96
  97 + def authorize_create_team!
  98 + return access_denied! unless can?(current_user, :create_team, nil)
  99 + end
  100 +
  101 + def authorize_manage_user_team!
  102 + return access_denied! unless user_team.present? && can?(current_user, :manage_user_team, user_team)
  103 + end
  104 +
  105 + def authorize_admin_user_team!
  106 + return access_denied! unless user_team.present? && can?(current_user, :admin_user_team, user_team)
  107 + end
  108 +
97 def access_denied! 109 def access_denied!
98 render "errors/access_denied", layout: "errors", status: 404 110 render "errors/access_denied", layout: "errors", status: 404
99 end 111 end
@@ -135,4 +147,5 @@ class ApplicationController &lt; ActionController::Base @@ -135,4 +147,5 @@ class ApplicationController &lt; ActionController::Base
135 def dev_tools 147 def dev_tools
136 Rack::MiniProfiler.authorize_request 148 Rack::MiniProfiler.authorize_request
137 end 149 end
  150 +
138 end 151 end
app/controllers/dashboard_controller.rb
1 class DashboardController < ApplicationController 1 class DashboardController < ApplicationController
2 respond_to :html 2 respond_to :html
3 3
4 - before_filter :projects  
5 - before_filter :event_filter, only: :index 4 + before_filter :load_projects
  5 + before_filter :event_filter, only: :show
6 6
7 - def index 7 + def show
8 @groups = current_user.authorized_groups 8 @groups = current_user.authorized_groups
9 -  
10 @has_authorized_projects = @projects.count > 0 9 @has_authorized_projects = @projects.count > 0
11 -  
12 - @projects = case params[:scope]  
13 - when 'personal' then  
14 - @projects.personal(current_user)  
15 - when 'joined' then  
16 - @projects.joined(current_user)  
17 - else  
18 - @projects  
19 - end  
20 -  
21 - @projects = @projects.page(params[:page]).per(30) 10 + @teams = current_user.authorized_teams
  11 + @projects_count = @projects.count
  12 + @projects = @projects.limit(20)
22 13
23 @events = Event.in_projects(current_user.authorized_projects.pluck(:id)) 14 @events = Event.in_projects(current_user.authorized_projects.pluck(:id))
24 @events = @event_filter.apply_filter(@events) 15 @events = @event_filter.apply_filter(@events)
@@ -33,6 +24,19 @@ class DashboardController &lt; ApplicationController @@ -33,6 +24,19 @@ class DashboardController &lt; ApplicationController
33 end 24 end
34 end 25 end
35 26
  27 + def projects
  28 + @projects = case params[:scope]
  29 + when 'personal' then
  30 + @projects.personal(current_user)
  31 + when 'joined' then
  32 + @projects.joined(current_user)
  33 + else
  34 + @projects
  35 + end
  36 +
  37 + @projects = @projects.page(params[:page]).per(30)
  38 + end
  39 +
36 # Get authored or assigned open merge requests 40 # Get authored or assigned open merge requests
37 def merge_requests 41 def merge_requests
38 @merge_requests = current_user.cared_merge_requests 42 @merge_requests = current_user.cared_merge_requests
@@ -55,7 +59,7 @@ class DashboardController &lt; ApplicationController @@ -55,7 +59,7 @@ class DashboardController &lt; ApplicationController
55 59
56 protected 60 protected
57 61
58 - def projects 62 + def load_projects
59 @projects = current_user.authorized_projects.sorted_by_activity 63 @projects = current_user.authorized_projects.sorted_by_activity
60 end 64 end
61 65
app/controllers/groups_controller.rb
1 class GroupsController < ApplicationController 1 class GroupsController < ApplicationController
2 respond_to :html 2 respond_to :html
3 - layout 'group' 3 + layout 'group', except: [:new, :create]
4 4
5 - before_filter :group  
6 - before_filter :projects 5 + before_filter :group, except: [:new, :create]
7 6
8 # Authorize 7 # Authorize
9 - before_filter :authorize_read_group! 8 + before_filter :authorize_read_group!, except: [:new, :create]
  9 + before_filter :authorize_create_group!, only: [:new, :create]
  10 +
  11 + # Load group projects
  12 + before_filter :projects, except: [:new, :create]
  13 +
  14 + def new
  15 + @group = Group.new
  16 + end
  17 +
  18 + def create
  19 + @group = Group.new(params[:group])
  20 + @group.path = @group.name.dup.parameterize if @group.name
  21 + @group.owner = current_user
  22 +
  23 + if @group.save
  24 + redirect_to @group, notice: 'Group was successfully created.'
  25 + else
  26 + render action: "new"
  27 + end
  28 + end
10 29
11 def show 30 def show
12 @events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0) 31 @events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0)
@@ -85,4 +104,8 @@ class GroupsController &lt; ApplicationController @@ -85,4 +104,8 @@ class GroupsController &lt; ApplicationController
85 return render_404 104 return render_404
86 end 105 end
87 end 106 end
  107 +
  108 + def authorize_create_group!
  109 + can?(current_user, :create_group, nil)
  110 + end
88 end 111 end
app/controllers/projects/application_controller.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +class Projects::ApplicationController < ApplicationController
  2 +
  3 + before_filter :authorize_admin_team_member!
  4 +
  5 + protected
  6 +
  7 + def user_team
  8 + @team ||= UserTeam.find_by_path(params[:id])
  9 + end
  10 +
  11 +end
app/controllers/projects/teams_controller.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +class Projects::TeamsController < Projects::ApplicationController
  2 +
  3 + def available
  4 + @teams = current_user.is_admin? ? UserTeam.scoped : current_user.user_teams
  5 + @teams = @teams.without_project(project)
  6 + unless @teams.any?
  7 + redirect_to project_team_index_path(project), notice: "No avaliable teams for assigment."
  8 + end
  9 + end
  10 +
  11 + def assign
  12 + unless params[:team_id].blank?
  13 + team = UserTeam.find(params[:team_id])
  14 + access = params[:greatest_project_access]
  15 + team.assign_to_project(project, access)
  16 + end
  17 + redirect_to project_team_index_path(project)
  18 + end
  19 +
  20 + def resign
  21 + team = project.user_teams.find_by_path(params[:id])
  22 + team.resign_from_project(project)
  23 +
  24 + redirect_to project_team_index_path(project)
  25 + end
  26 +
  27 +end
app/controllers/projects_controller.rb
@@ -19,7 +19,7 @@ class ProjectsController &lt; ProjectResourceController @@ -19,7 +19,7 @@ class ProjectsController &lt; ProjectResourceController
19 end 19 end
20 20
21 def create 21 def create
22 - @project = Projects::CreateContext.new(current_user, params[:project]).execute 22 + @project = ::Projects::CreateContext.new(current_user, params[:project]).execute
23 23
24 respond_to do |format| 24 respond_to do |format|
25 flash[:notice] = 'Project was successfully created.' if @project.saved? 25 flash[:notice] = 'Project was successfully created.' if @project.saved?
@@ -35,7 +35,7 @@ class ProjectsController &lt; ProjectResourceController @@ -35,7 +35,7 @@ class ProjectsController &lt; ProjectResourceController
35 end 35 end
36 36
37 def update 37 def update
38 - status = Projects::UpdateContext.new(project, current_user, params).execute 38 + status = ::Projects::UpdateContext.new(project, current_user, params).execute
39 39
40 respond_to do |format| 40 respond_to do |format|
41 if status 41 if status
app/controllers/search_controller.rb
1 class SearchController < ApplicationController 1 class SearchController < ApplicationController
2 def show 2 def show
3 - result = SearchContext.new(current_user.authorized_projects.map(&:id), params).execute 3 + project_id = params[:project_id]
  4 + group_id = params[:group_id]
  5 +
  6 + project_ids = current_user.authorized_projects.map(&:id)
  7 +
  8 + if group_id.present?
  9 + group_project_ids = Group.find(group_id).projects.map(&:id)
  10 + project_ids.select! { |id| group_project_ids.include?(id)}
  11 + elsif project_id.present?
  12 + project_ids.select! { |id| id == project_id.to_i}
  13 + end
  14 +
  15 + result = SearchContext.new(project_ids, params).execute
4 16
5 @projects = result[:projects] 17 @projects = result[:projects]
6 @merge_requests = result[:merge_requests] 18 @merge_requests = result[:merge_requests]
app/controllers/team_members_controller.rb
@@ -4,15 +4,16 @@ class TeamMembersController &lt; ProjectResourceController @@ -4,15 +4,16 @@ class TeamMembersController &lt; ProjectResourceController
4 before_filter :authorize_admin_project!, except: [:index, :show] 4 before_filter :authorize_admin_project!, except: [:index, :show]
5 5
6 def index 6 def index
  7 + @teams = UserTeam.scoped
7 end 8 end
8 9
9 def show 10 def show
10 - @team_member = project.users_projects.find(params[:id])  
11 - @events = @team_member.user.recent_events.where(:project_id => @project.id).limit(7) 11 + @user_project_relation = project.users_projects.find_by_user_id(member)
  12 + @events = member.recent_events.in_projects(project).limit(7)
12 end 13 end
13 14
14 def new 15 def new
15 - @team_member = project.users_projects.new 16 + @user_project_relation = project.users_projects.new
16 end 17 end
17 18
18 def create 19 def create
@@ -28,18 +29,18 @@ class TeamMembersController &lt; ProjectResourceController @@ -28,18 +29,18 @@ class TeamMembersController &lt; ProjectResourceController
28 end 29 end
29 30
30 def update 31 def update
31 - @team_member = project.users_projects.find(params[:id])  
32 - @team_member.update_attributes(params[:team_member]) 32 + @user_project_relation = project.users_projects.find_by_user_id(member)
  33 + @user_project_relation.update_attributes(params[:team_member])
33 34
34 - unless @team_member.valid? 35 + unless @user_project_relation.valid?
35 flash[:alert] = "User should have at least one role" 36 flash[:alert] = "User should have at least one role"
36 end 37 end
37 redirect_to project_team_index_path(@project) 38 redirect_to project_team_index_path(@project)
38 end 39 end
39 40
40 def destroy 41 def destroy
41 - @team_member = project.users_projects.find(params[:id])  
42 - @team_member.destroy 42 + @user_project_relation = project.users_projects.find_by_user_id(member)
  43 + @user_project_relation.destroy
43 44
44 respond_to do |format| 45 respond_to do |format|
45 format.html { redirect_to project_team_index_path(@project) } 46 format.html { redirect_to project_team_index_path(@project) }
@@ -54,4 +55,10 @@ class TeamMembersController &lt; ProjectResourceController @@ -54,4 +55,10 @@ class TeamMembersController &lt; ProjectResourceController
54 55
55 redirect_to project_team_members_path(project), notice: notice 56 redirect_to project_team_members_path(project), notice: notice
56 end 57 end
  58 +
  59 + protected
  60 +
  61 + def member
  62 + @member ||= User.find_by_username(params[:id])
  63 + end
57 end 64 end
app/controllers/teams/application_controller.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class Teams::ApplicationController < ApplicationController
  2 +
  3 + layout 'user_team'
  4 +
  5 + before_filter :authorize_manage_user_team!
  6 +
  7 + protected
  8 +
  9 + def user_team
  10 + @team ||= UserTeam.find_by_path(params[:team_id])
  11 + end
  12 +
  13 +end
app/controllers/teams/members_controller.rb 0 → 100644
@@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
  1 +class Teams::MembersController < Teams::ApplicationController
  2 +
  3 + skip_before_filter :authorize_manage_user_team!, only: [:index]
  4 +
  5 + def index
  6 + @members = user_team.members
  7 + end
  8 +
  9 + def new
  10 + @users = User.potential_team_members(user_team)
  11 + @users = UserDecorator.decorate @users
  12 + end
  13 +
  14 + def create
  15 + unless params[:user_ids].blank?
  16 + user_ids = params[:user_ids]
  17 + access = params[:default_project_access]
  18 + is_admin = params[:group_admin]
  19 + user_team.add_members(user_ids, access, is_admin)
  20 + end
  21 +
  22 + redirect_to team_members_path(user_team), notice: 'Members was successfully added into Team of users.'
  23 + end
  24 +
  25 + def edit
  26 + team_member
  27 + end
  28 +
  29 + def update
  30 + options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]}
  31 + if user_team.update_membership(team_member, options)
  32 + redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
  33 + else
  34 + render :edit
  35 + end
  36 + end
  37 +
  38 + def destroy
  39 + user_team.remove_member(team_member)
  40 + redirect_to team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users."
  41 + end
  42 +
  43 + protected
  44 +
  45 + def team_member
  46 + @member ||= user_team.members.find_by_username(params[:id])
  47 + end
  48 +
  49 +end
app/controllers/teams/projects_controller.rb 0 → 100644
@@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
  1 +class Teams::ProjectsController < Teams::ApplicationController
  2 +
  3 + skip_before_filter :authorize_manage_user_team!, only: [:index]
  4 +
  5 + def index
  6 + @projects = user_team.projects
  7 + @avaliable_projects = current_user.admin? ? Project.without_team(user_team) : current_user.owned_projects.without_team(user_team)
  8 + end
  9 +
  10 + def new
  11 + user_team
  12 + @avaliable_projects = current_user.owned_projects.scoped
  13 + @avaliable_projects = @avaliable_projects.without_team(user_team) if user_team.projects.any?
  14 +
  15 + redirect_to team_projects_path(user_team), notice: "No avalible projects." unless @avaliable_projects.any?
  16 + end
  17 +
  18 + def create
  19 + redirect_to :back if params[:project_ids].blank?
  20 +
  21 + project_ids = params[:project_ids]
  22 + access = params[:greatest_project_access]
  23 +
  24 + # Reject non-allowed projects
  25 + allowed_project_ids = current_user.owned_projects.map(&:id)
  26 + project_ids.select! { |id| allowed_project_ids.include?(id.to_i) }
  27 +
  28 + # Assign projects to team
  29 + user_team.assign_to_projects(project_ids, access)
  30 +
  31 + redirect_to team_projects_path(user_team), notice: 'Team of users was successfully assigned to projects.'
  32 + end
  33 +
  34 + def edit
  35 + team_project
  36 + end
  37 +
  38 + def update
  39 + if user_team.update_project_access(team_project, params[:greatest_project_access])
  40 + redirect_to team_projects_path(user_team), notice: 'Access was successfully updated.'
  41 + else
  42 + render :edit
  43 + end
  44 + end
  45 +
  46 + def destroy
  47 + user_team.resign_from_project(team_project)
  48 + redirect_to team_projects_path(user_team), notice: 'Team of users was successfully reassigned from project.'
  49 + end
  50 +
  51 + private
  52 +
  53 + def team_project
  54 + @project ||= user_team.projects.find_with_namespace(params[:id])
  55 + end
  56 +
  57 +end
app/controllers/teams_controller.rb 0 → 100644
@@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
  1 +class TeamsController < ApplicationController
  2 + # Authorize
  3 + before_filter :authorize_create_team!, only: [:new, :create]
  4 + before_filter :authorize_manage_user_team!, only: [:edit, :update]
  5 + before_filter :authorize_admin_user_team!, only: [:destroy]
  6 +
  7 + before_filter :user_team, except: [:new, :create]
  8 +
  9 + layout 'user_team', except: [:new, :create]
  10 +
  11 + def show
  12 + user_team
  13 + projects
  14 + @events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0)
  15 + end
  16 +
  17 + def edit
  18 + user_team
  19 + end
  20 +
  21 + def update
  22 + if user_team.update_attributes(params[:user_team])
  23 + redirect_to team_path(user_team)
  24 + else
  25 + render action: :edit
  26 + end
  27 + end
  28 +
  29 + def destroy
  30 + user_team.destroy
  31 + redirect_to dashboard_path
  32 + end
  33 +
  34 + def new
  35 + @team = UserTeam.new
  36 + end
  37 +
  38 + def create
  39 + @team = UserTeam.new(params[:user_team])
  40 + @team.owner = current_user unless params[:owner]
  41 + @team.path = @team.name.dup.parameterize if @team.name
  42 +
  43 + if @team.save
  44 + redirect_to team_path(@team)
  45 + else
  46 + render action: :new
  47 + end
  48 + end
  49 +
  50 + # Get authored or assigned open merge requests
  51 + def merge_requests
  52 + projects
  53 + @merge_requests = MergeRequest.of_user_team(user_team)
  54 + @merge_requests = FilterContext.new(@merge_requests, params).execute
  55 + @merge_requests = @merge_requests.recent.page(params[:page]).per(20)
  56 + end
  57 +
  58 + # Get only assigned issues
  59 + def issues
  60 + projects
  61 + @issues = Issue.of_user_team(user_team)
  62 + @issues = FilterContext.new(@issues, params).execute
  63 + @issues = @issues.recent.page(params[:page]).per(20)
  64 + @issues = @issues.includes(:author, :project)
  65 + end
  66 +
  67 + protected
  68 +
  69 + def projects
  70 + @projects ||= user_team.projects.sorted_by_activity
  71 + end
  72 +
  73 + def user_team
  74 + @team ||= current_user.authorized_teams.find_by_path(params[:id])
  75 + end
  76 +end
app/decorators/user_decorator.rb
@@ -8,4 +8,8 @@ class UserDecorator &lt; ApplicationDecorator @@ -8,4 +8,8 @@ class UserDecorator &lt; ApplicationDecorator
8 def tm_of(project) 8 def tm_of(project)
9 project.team_member_by_id(self.id) 9 project.team_member_by_id(self.id)
10 end 10 end
  11 +
  12 + def name_with_email
  13 + "#{name} (#{email})"
  14 + end
11 end 15 end
app/helpers/admin/teams/members_helper.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +module Admin::Teams::MembersHelper
  2 + def member_since(team, member)
  3 + team.user_team_user_relationships.find_by_user_id(member).created_at
  4 + end
  5 +end
app/helpers/admin/teams/projects_helper.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +module Admin::Teams::ProjectsHelper
  2 + def assigned_since(team, project)
  3 + team.user_team_project_relationships.find_by_project_id(project).created_at
  4 + end
  5 +end
app/helpers/application_helper.rb
@@ -72,8 +72,9 @@ module ApplicationHelper @@ -72,8 +72,9 @@ module ApplicationHelper
72 end 72 end
73 73
74 def search_autocomplete_source 74 def search_autocomplete_source
75 - projects = current_user.authorized_projects.map { |p| { label: p.name_with_namespace, url: project_path(p) } }  
76 - groups = current_user.authorized_groups.map { |group| { label: "<group> #{group.name}", url: group_path(group) } } 75 + projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } }
  76 + groups = current_user.authorized_groups.map { |group| { label: "group: #{group.name}", url: group_path(group) } }
  77 + teams = current_user.authorized_teams.map { |team| { label: "team: #{team.name}", url: team_path(team) } }
77 78
78 default_nav = [ 79 default_nav = [
79 { label: "My Profile", url: profile_path }, 80 { label: "My Profile", url: profile_path },
@@ -83,29 +84,29 @@ module ApplicationHelper @@ -83,29 +84,29 @@ module ApplicationHelper
83 ] 84 ]
84 85
85 help_nav = [ 86 help_nav = [
86 - { label: "API Help", url: help_api_path },  
87 - { label: "Markdown Help", url: help_markdown_path },  
88 - { label: "Permissions Help", url: help_permissions_path },  
89 - { label: "Public Access Help", url: help_public_access_path },  
90 - { label: "Rake Tasks Help", url: help_raketasks_path },  
91 - { label: "SSH Keys Help", url: help_ssh_path },  
92 - { label: "System Hooks Help", url: help_system_hooks_path },  
93 - { label: "Web Hooks Help", url: help_web_hooks_path },  
94 - { label: "Workflow Help", url: help_workflow_path }, 87 + { label: "help: API Help", url: help_api_path },
  88 + { label: "help: Markdown Help", url: help_markdown_path },
  89 + { label: "help: Permissions Help", url: help_permissions_path },
  90 + { label: "help: Public Access Help", url: help_public_access_path },
  91 + { label: "help: Rake Tasks Help", url: help_raketasks_path },
  92 + { label: "help: SSH Keys Help", url: help_ssh_path },
  93 + { label: "help: System Hooks Help", url: help_system_hooks_path },
  94 + { label: "help: Web Hooks Help", url: help_web_hooks_path },
  95 + { label: "help: Workflow Help", url: help_workflow_path },
95 ] 96 ]
96 97
97 project_nav = [] 98 project_nav = []
98 if @project && @project.repository && @project.repository.root_ref 99 if @project && @project.repository && @project.repository.root_ref
99 project_nav = [ 100 project_nav = [
100 - { label: "#{@project.name} Issues", url: project_issues_path(@project) },  
101 - { label: "#{@project.name} Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },  
102 - { label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) },  
103 - { label: "#{@project.name} Milestones", url: project_milestones_path(@project) },  
104 - { label: "#{@project.name} Snippets", url: project_snippets_path(@project) },  
105 - { label: "#{@project.name} Team", url: project_team_index_path(@project) },  
106 - { label: "#{@project.name} Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) },  
107 - { label: "#{@project.name} Wall", url: wall_project_path(@project) },  
108 - { label: "#{@project.name} Wiki", url: project_wikis_path(@project) }, 101 + { label: "#{@project.name_with_namespace} - Issues", url: project_issues_path(@project) },
  102 + { label: "#{@project.name_with_namespace} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
  103 + { label: "#{@project.name_with_namespace} - Merge Requests", url: project_merge_requests_path(@project) },
  104 + { label: "#{@project.name_with_namespace} - Milestones", url: project_milestones_path(@project) },
  105 + { label: "#{@project.name_with_namespace} - Snippets", url: project_snippets_path(@project) },
  106 + { label: "#{@project.name_with_namespace} - Team", url: project_team_index_path(@project) },
  107 + { label: "#{@project.name_with_namespace} - Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) },
  108 + { label: "#{@project.name_with_namespace} - Wall", url: wall_project_path(@project) },
  109 + { label: "#{@project.name_with_namespace} - Wiki", url: project_wikis_path(@project) },
109 ] 110 ]
110 end 111 end
111 112
app/helpers/commits_helper.rb
@@ -59,9 +59,9 @@ module CommitsHelper @@ -59,9 +59,9 @@ module CommitsHelper
59 59
60 def image_diff_class(diff) 60 def image_diff_class(diff)
61 if diff.deleted_file 61 if diff.deleted_file
62 - "diff_removed" 62 + "deleted"
63 elsif diff.new_file 63 elsif diff.new_file
64 - "diff_added" 64 + "added"
65 else 65 else
66 nil 66 nil
67 end 67 end
app/helpers/dashboard_helper.rb
@@ -9,9 +9,9 @@ module DashboardHelper @@ -9,9 +9,9 @@ module DashboardHelper
9 9
10 case entity 10 case entity
11 when 'issue' then 11 when 'issue' then
12 - dashboard_issues_path(options) 12 + issues_dashboard_path(options)
13 when 'merge_request' 13 when 'merge_request'
14 - dashboard_merge_requests_path(options) 14 + merge_requests_dashboard_path(options)
15 end 15 end
16 end 16 end
17 17
app/helpers/projects_helper.rb
@@ -3,8 +3,12 @@ module ProjectsHelper @@ -3,8 +3,12 @@ module ProjectsHelper
3 @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) 3 @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
4 end 4 end
5 5
6 - def remove_from_team_message(project, member)  
7 - "You are going to remove #{member.user_name} from #{project.name}. Are you sure?" 6 + def grouper_project_teams(project)
  7 + @project.user_team_project_relationships.sort_by(&:greatest_access).reverse.group_by(&:greatest_access)
  8 + end
  9 +
  10 + def remove_from_project_team_message(project, user)
  11 + "You are going to remove #{user.name} from #{project.name} project team. Are you sure?"
8 end 12 end
9 13
10 def link_to_project project 14 def link_to_project project
@@ -51,7 +55,9 @@ module ProjectsHelper @@ -51,7 +55,9 @@ module ProjectsHelper
51 55
52 def project_title project 56 def project_title project
53 if project.group 57 if project.group
54 - project.name_with_namespace 58 + content_tag :span do
  59 + link_to(project.group.name, group_path(project.group)) + " / " + project.name
  60 + end
55 else 61 else
56 project.name 62 project.name
57 end 63 end
app/helpers/tab_helper.rb
@@ -39,7 +39,12 @@ module TabHelper @@ -39,7 +39,12 @@ module TabHelper
39 # Returns a list item element String 39 # Returns a list item element String
40 def nav_link(options = {}, &block) 40 def nav_link(options = {}, &block)
41 if path = options.delete(:path) 41 if path = options.delete(:path)
42 - c, a, _ = path.split('#') 42 + if path.respond_to?(:each)
  43 + c = path.map { |p| p.split('#').first }
  44 + a = path.map { |p| p.split('#').last }
  45 + else
  46 + c, a, _ = path.split('#')
  47 + end
43 else 48 else
44 c = options.delete(:controller) 49 c = options.delete(:controller)
45 a = options.delete(:action) 50 a = options.delete(:action)
app/helpers/user_teams_helper.rb 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +module UserTeamsHelper
  2 + def team_filter_path(entity, options={})
  3 + exist_opts = {
  4 + status: params[:status],
  5 + project_id: params[:project_id],
  6 + }
  7 +
  8 + options = exist_opts.merge(options)
  9 +
  10 + case entity
  11 + when 'issue' then
  12 + issues_team_path(@team, options)
  13 + when 'merge_request'
  14 + merge_requests_team_path(@team, options)
  15 + end
  16 + end
  17 +
  18 + def grouped_user_team_members(team)
  19 + team.user_team_user_relationships.sort_by(&:permission).reverse.group_by(&:permission)
  20 + end
  21 +
  22 + def remove_from_user_team_message(team, member)
  23 + "You are going to remove #{member.name} from #{team.name}. Are you sure?"
  24 + end
  25 +
  26 +end
app/models/ability.rb
1 class Ability 1 class Ability
2 class << self 2 class << self
3 - def allowed(object, subject) 3 + def allowed(user, subject)
  4 + return [] unless user.kind_of?(User)
  5 +
4 case subject.class.name 6 case subject.class.name
5 - when "Project" then project_abilities(object, subject)  
6 - when "Issue" then issue_abilities(object, subject)  
7 - when "Note" then note_abilities(object, subject)  
8 - when "Snippet" then snippet_abilities(object, subject)  
9 - when "MergeRequest" then merge_request_abilities(object, subject)  
10 - when "Group", "Namespace" then group_abilities(object, subject) 7 + when "Project" then project_abilities(user, subject)
  8 + when "Issue" then issue_abilities(user, subject)
  9 + when "Note" then note_abilities(user, subject)
  10 + when "Snippet" then snippet_abilities(user, subject)
  11 + when "MergeRequest" then merge_request_abilities(user, subject)
  12 + when "Group", "Namespace" then group_abilities(user, subject)
  13 + when "UserTeam" then user_team_abilities(user, subject)
11 else [] 14 else []
12 - end 15 + end.concat(global_abilities(user))
  16 + end
  17 +
  18 + def global_abilities(user)
  19 + rules = []
  20 + rules << :create_group if user.can_create_group
  21 + rules << :create_team if user.can_create_team
  22 + rules
13 end 23 end
14 24
15 def project_abilities(user, project) 25 def project_abilities(user, project)
@@ -110,6 +120,22 @@ class Ability @@ -110,6 +120,22 @@ class Ability
110 rules.flatten 120 rules.flatten
111 end 121 end
112 122
  123 + def user_team_abilities user, team
  124 + rules = []
  125 +
  126 + # Only group owner and administrators can manage group
  127 + if team.owner == user || team.admin?(user) || user.admin?
  128 + rules << [ :manage_user_team ]
  129 + end
  130 +
  131 + if team.owner == user || user.admin?
  132 + rules << [ :admin_user_team ]
  133 + end
  134 +
  135 + rules.flatten
  136 + end
  137 +
  138 +
113 [:issue, :note, :snippet, :merge_request].each do |name| 139 [:issue, :note, :snippet, :merge_request].each do |name|
114 define_method "#{name}_abilities" do |user, subject| 140 define_method "#{name}_abilities" do |user, subject|
115 if subject.author == user 141 if subject.author == user
app/models/concerns/issuable.rb
@@ -22,6 +22,7 @@ module Issuable @@ -22,6 +22,7 @@ module Issuable
22 scope :opened, where(closed: false) 22 scope :opened, where(closed: false)
23 scope :closed, where(closed: true) 23 scope :closed, where(closed: true)
24 scope :of_group, ->(group) { where(project_id: group.project_ids) } 24 scope :of_group, ->(group) { where(project_id: group.project_ids) }
  25 + scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
25 scope :assigned, ->(u) { where(assignee_id: u.id)} 26 scope :assigned, ->(u) { where(assignee_id: u.id)}
26 scope :recent, order("created_at DESC") 27 scope :recent, order("created_at DESC")
27 28
app/models/project.rb
@@ -33,28 +33,31 @@ class Project &lt; ActiveRecord::Base @@ -33,28 +33,31 @@ class Project &lt; ActiveRecord::Base
33 attr_accessor :error_code 33 attr_accessor :error_code
34 34
35 # Relations 35 # Relations
36 - belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" 36 + belongs_to :creator, foreign_key: "creator_id", class_name: "User"
  37 + belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
37 belongs_to :namespace 38 belongs_to :namespace
38 39
39 - belongs_to :creator,  
40 - class_name: "User",  
41 - foreign_key: "creator_id"  
42 -  
43 - has_many :users, through: :users_projects  
44 - has_many :events, dependent: :destroy  
45 - has_many :merge_requests, dependent: :destroy  
46 - has_many :issues, dependent: :destroy, order: "closed, created_at DESC"  
47 - has_many :milestones, dependent: :destroy  
48 - has_many :users_projects, dependent: :destroy  
49 - has_many :notes, dependent: :destroy  
50 - has_many :snippets, dependent: :destroy  
51 - has_many :deploy_keys, dependent: :destroy, foreign_key: "project_id", class_name: "Key"  
52 - has_many :hooks, dependent: :destroy, class_name: "ProjectHook"  
53 - has_many :wikis, dependent: :destroy  
54 - has_many :protected_branches, dependent: :destroy  
55 has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id' 40 has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
56 has_one :gitlab_ci_service, dependent: :destroy 41 has_one :gitlab_ci_service, dependent: :destroy
57 42
  43 + has_many :events, dependent: :destroy
  44 + has_many :merge_requests, dependent: :destroy
  45 + has_many :issues, dependent: :destroy, order: "closed, created_at DESC"
  46 + has_many :milestones, dependent: :destroy
  47 + has_many :users_projects, dependent: :destroy
  48 + has_many :notes, dependent: :destroy
  49 + has_many :snippets, dependent: :destroy
  50 + has_many :deploy_keys, dependent: :destroy, class_name: "Key", foreign_key: "project_id"
  51 + has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
  52 + has_many :wikis, dependent: :destroy
  53 + has_many :protected_branches, dependent: :destroy
  54 + has_many :user_team_project_relationships, dependent: :destroy
  55 +
  56 + has_many :users, through: :users_projects
  57 + has_many :user_teams, through: :user_team_project_relationships
  58 + has_many :user_team_user_relationships, through: :user_teams
  59 + has_many :user_teams_members, through: :user_team_user_relationships
  60 +
58 delegate :name, to: :owner, allow_nil: true, prefix: true 61 delegate :name, to: :owner, allow_nil: true, prefix: true
59 62
60 # Validations 63 # Validations
@@ -77,6 +80,8 @@ class Project &lt; ActiveRecord::Base @@ -77,6 +80,8 @@ class Project &lt; ActiveRecord::Base
77 # Scopes 80 # Scopes
78 scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) } 81 scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
79 scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) } 82 scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) }
  83 + scope :without_team, ->(team) { team.projects.present? ? where("id NOT IN (:ids)", ids: team.projects.map(&:id)) : scoped }
  84 + scope :in_team, ->(team) { where("id IN (:ids)", ids: team.projects.map(&:id)) }
80 scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) } 85 scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
81 scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") } 86 scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") }
82 scope :personal, ->(user) { where(namespace_id: user.namespace_id) } 87 scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
@@ -122,7 +127,7 @@ class Project &lt; ActiveRecord::Base @@ -122,7 +127,7 @@ class Project &lt; ActiveRecord::Base
122 end 127 end
123 128
124 def team 129 def team
125 - @team ||= Team.new(self) 130 + @team ||= ProjectTeam.new(self)
126 end 131 end
127 132
128 def repository 133 def repository
@@ -335,7 +340,7 @@ class Project &lt; ActiveRecord::Base @@ -335,7 +340,7 @@ class Project &lt; ActiveRecord::Base
335 end 340 end
336 341
337 def execute_hooks(data) 342 def execute_hooks(data)
338 - hooks.each { |hook| hook.execute(data) } 343 + hooks.each { |hook| hook.async_execute(data) }
339 end 344 end
340 345
341 def execute_services(data) 346 def execute_services(data)
@@ -489,6 +494,11 @@ class Project &lt; ActiveRecord::Base @@ -489,6 +494,11 @@ class Project &lt; ActiveRecord::Base
489 http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') 494 http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
490 end 495 end
491 496
  497 + def project_access_human(member)
  498 + project_user_relation = self.users_projects.find_by_user_id(member.id)
  499 + self.class.access_options.key(project_user_relation.project_access)
  500 + end
  501 +
492 # Check if current branch name is marked as protected in the system 502 # Check if current branch name is marked as protected in the system
493 def protected_branch? branch_name 503 def protected_branch? branch_name
494 protected_branches.map(&:name).include?(branch_name) 504 protected_branches.map(&:name).include?(branch_name)
app/models/project_team.rb 0 → 100644
@@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
  1 +class ProjectTeam
  2 + attr_accessor :project
  3 +
  4 + def initialize(project)
  5 + @project = project
  6 + end
  7 +
  8 + # Shortcut to add users
  9 + #
  10 + # Use:
  11 + # @team << [@user, :master]
  12 + # @team << [@users, :master]
  13 + #
  14 + def << args
  15 + users = args.first
  16 +
  17 + if users.respond_to?(:each)
  18 + add_users(users, args.second)
  19 + else
  20 + add_user(users, args.second)
  21 + end
  22 + end
  23 +
  24 + def get_tm user_id
  25 + project.users_projects.find_by_user_id(user_id)
  26 + end
  27 +
  28 + def add_user(user, access)
  29 + add_users_ids([user.id], access)
  30 + end
  31 +
  32 + def add_users(users, access)
  33 + add_users_ids(users.map(&:id), access)
  34 + end
  35 +
  36 + def add_users_ids(user_ids, access)
  37 + UsersProject.add_users_into_projects(
  38 + [project.id],
  39 + user_ids,
  40 + access
  41 + )
  42 + end
  43 +
  44 + # Remove all users from project team
  45 + def truncate
  46 + UsersProject.truncate_team(project)
  47 + end
  48 +
  49 + def members
  50 + project.users_projects
  51 + end
  52 +
  53 + def guests
  54 + members.guests.map(&:user)
  55 + end
  56 +
  57 + def reporters
  58 + members.reporters.map(&:user)
  59 + end
  60 +
  61 + def developers
  62 + members.developers.map(&:user)
  63 + end
  64 +
  65 + def masters
  66 + members.masters.map(&:user)
  67 + end
  68 +
  69 + def repository_readers
  70 + repository_members[UsersProject::REPORTER]
  71 + end
  72 +
  73 + def repository_writers
  74 + repository_members[UsersProject::DEVELOPER]
  75 + end
  76 +
  77 + def repository_masters
  78 + repository_members[UsersProject::MASTER]
  79 + end
  80 +
  81 + def repository_members
  82 + keys = Hash.new {|h,k| h[k] = [] }
  83 + UsersProject.select("keys.identifier, project_access").
  84 + joins(user: :keys).where(project_id: project.id).
  85 + each {|row| keys[row.project_access] << [row.identifier] }
  86 +
  87 + keys[UsersProject::REPORTER] += project.deploy_keys.pluck(:identifier)
  88 + keys
  89 + end
  90 +
  91 + def import(source_project)
  92 + target_project = project
  93 +
  94 + source_team = source_project.users_projects.all
  95 + target_team = target_project.users_projects.all
  96 + target_user_ids = target_team.map(&:user_id)
  97 +
  98 + source_team.reject! do |tm|
  99 + # Skip if user already present in team
  100 + target_user_ids.include?(tm.user_id)
  101 + end
  102 +
  103 + source_team.map! do |tm|
  104 + new_tm = tm.dup
  105 + new_tm.id = nil
  106 + new_tm.project_id = target_project.id
  107 + new_tm.skip_git = true
  108 + new_tm
  109 + end
  110 +
  111 + UsersProject.transaction do
  112 + source_team.each do |tm|
  113 + tm.save
  114 + end
  115 + target_project.update_repository
  116 + end
  117 +
  118 + true
  119 + rescue
  120 + false
  121 + end
  122 +end
app/models/team.rb
@@ -1,122 +0,0 @@ @@ -1,122 +0,0 @@
1 -class Team  
2 - attr_accessor :project  
3 -  
4 - def initialize(project)  
5 - @project = project  
6 - end  
7 -  
8 - # Shortcut to add users  
9 - #  
10 - # Use:  
11 - # @team << [@user, :master]  
12 - # @team << [@users, :master]  
13 - #  
14 - def << args  
15 - users = args.first  
16 -  
17 - if users.respond_to?(:each)  
18 - add_users(users, args.second)  
19 - else  
20 - add_user(users, args.second)  
21 - end  
22 - end  
23 -  
24 - def get_tm user_id  
25 - project.users_projects.find_by_user_id(user_id)  
26 - end  
27 -  
28 - def add_user(user, access)  
29 - add_users_ids([user.id], access)  
30 - end  
31 -  
32 - def add_users(users, access)  
33 - add_users_ids(users.map(&:id), access)  
34 - end  
35 -  
36 - def add_users_ids(user_ids, access)  
37 - UsersProject.add_users_into_projects(  
38 - [project.id],  
39 - user_ids,  
40 - access  
41 - )  
42 - end  
43 -  
44 - # Remove all users from project team  
45 - def truncate  
46 - UsersProject.truncate_team(project)  
47 - end  
48 -  
49 - def members  
50 - project.users_projects  
51 - end  
52 -  
53 - def guests  
54 - members.guests.map(&:user)  
55 - end  
56 -  
57 - def reporters  
58 - members.reporters.map(&:user)  
59 - end  
60 -  
61 - def developers  
62 - members.developers.map(&:user)  
63 - end  
64 -  
65 - def masters  
66 - members.masters.map(&:user)  
67 - end  
68 -  
69 - def repository_readers  
70 - repository_members[UsersProject::REPORTER]  
71 - end  
72 -  
73 - def repository_writers  
74 - repository_members[UsersProject::DEVELOPER]  
75 - end  
76 -  
77 - def repository_masters  
78 - repository_members[UsersProject::MASTER]  
79 - end  
80 -  
81 - def repository_members  
82 - keys = Hash.new {|h,k| h[k] = [] }  
83 - UsersProject.select("keys.identifier, project_access").  
84 - joins(user: :keys).where(project_id: project.id).  
85 - each {|row| keys[row.project_access] << [row.identifier] }  
86 -  
87 - keys[UsersProject::REPORTER] += project.deploy_keys.pluck(:identifier)  
88 - keys  
89 - end  
90 -  
91 - def import(source_project)  
92 - target_project = project  
93 -  
94 - source_team = source_project.users_projects.all  
95 - target_team = target_project.users_projects.all  
96 - target_user_ids = target_team.map(&:user_id)  
97 -  
98 - source_team.reject! do |tm|  
99 - # Skip if user already present in team  
100 - target_user_ids.include?(tm.user_id)  
101 - end  
102 -  
103 - source_team.map! do |tm|  
104 - new_tm = tm.dup  
105 - new_tm.id = nil  
106 - new_tm.project_id = target_project.id  
107 - new_tm.skip_git = true  
108 - new_tm  
109 - end  
110 -  
111 - UsersProject.transaction do  
112 - source_team.each do |tm|  
113 - tm.save  
114 - end  
115 - target_project.update_repository  
116 - end  
117 -  
118 - true  
119 - rescue  
120 - false  
121 - end  
122 -end  
app/models/user.rb
@@ -40,23 +40,32 @@ class User &lt; ActiveRecord::Base @@ -40,23 +40,32 @@ class User &lt; ActiveRecord::Base
40 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, 40 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
41 :skype, :linkedin, :twitter, :dark_scheme, :theme_id, :force_random_password, 41 :skype, :linkedin, :twitter, :dark_scheme, :theme_id, :force_random_password,
42 :extern_uid, :provider, as: [:default, :admin] 42 :extern_uid, :provider, as: [:default, :admin]
43 - attr_accessible :projects_limit, as: :admin 43 + attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin
44 44
45 attr_accessor :force_random_password 45 attr_accessor :force_random_password
46 46
47 # Namespace for personal projects 47 # Namespace for personal projects
48 - has_one :namespace, class_name: "Namespace", foreign_key: :owner_id, conditions: 'type IS NULL', dependent: :destroy  
49 - has_many :groups, class_name: "Group", foreign_key: :owner_id  
50 -  
51 - has_many :keys, dependent: :destroy  
52 - has_many :users_projects, dependent: :destroy  
53 - has_many :issues, foreign_key: :author_id, dependent: :destroy  
54 - has_many :notes, foreign_key: :author_id, dependent: :destroy  
55 - has_many :merge_requests, foreign_key: :author_id, dependent: :destroy  
56 - has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy  
57 - has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"  
58 - has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy  
59 - has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy 48 + has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL'
  49 +
  50 + has_many :keys, dependent: :destroy
  51 + has_many :users_projects, dependent: :destroy
  52 + has_many :issues, dependent: :destroy, foreign_key: :author_id
  53 + has_many :notes, dependent: :destroy, foreign_key: :author_id
  54 + has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
  55 + has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
  56 + has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
  57 + has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
  58 +
  59 + has_many :groups, class_name: "Group", foreign_key: :owner_id
  60 + has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
  61 +
  62 + has_many :projects, through: :users_projects
  63 +
  64 + has_many :user_team_user_relationships, dependent: :destroy
  65 +
  66 + has_many :user_teams, through: :user_team_user_relationships
  67 + has_many :user_team_project_relationships, through: :user_teams
  68 + has_many :team_projects, through: :user_team_project_relationships
60 69
61 validates :name, presence: true 70 validates :name, presence: true
62 validates :bio, length: { within: 0..255 } 71 validates :bio, length: { within: 0..255 }
@@ -80,6 +89,9 @@ class User &lt; ActiveRecord::Base @@ -80,6 +89,9 @@ class User &lt; ActiveRecord::Base
80 scope :blocked, where(blocked: true) 89 scope :blocked, where(blocked: true)
81 scope :active, where(blocked: false) 90 scope :active, where(blocked: false)
82 scope :alphabetically, order('name ASC') 91 scope :alphabetically, order('name ASC')
  92 + scope :in_team, ->(team){ where(id: team.member_ids) }
  93 + scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
  94 + scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active }
83 95
84 # 96 #
85 # Class methods 97 # Class methods
@@ -131,6 +143,11 @@ class User &lt; ActiveRecord::Base @@ -131,6 +143,11 @@ class User &lt; ActiveRecord::Base
131 # 143 #
132 # Instance methods 144 # Instance methods
133 # 145 #
  146 +
  147 + def to_param
  148 + username
  149 + end
  150 +
134 def generate_password 151 def generate_password
135 if self.force_random_password 152 if self.force_random_password
136 self.password = self.password_confirmation = Devise.friendly_token.first(8) 153 self.password = self.password_confirmation = Devise.friendly_token.first(8)
@@ -220,7 +237,7 @@ class User &lt; ActiveRecord::Base @@ -220,7 +237,7 @@ class User &lt; ActiveRecord::Base
220 end 237 end
221 238
222 def can_create_group? 239 def can_create_group?
223 - is_admin? 240 + can?(:create_group, nil)
224 end 241 end
225 242
226 def abilities 243 def abilities
@@ -283,4 +300,15 @@ class User &lt; ActiveRecord::Base @@ -283,4 +300,15 @@ class User &lt; ActiveRecord::Base
283 def namespace_id 300 def namespace_id
284 namespace.try :id 301 namespace.try :id
285 end 302 end
  303 +
  304 + def authorized_teams
  305 + @authorized_teams ||= begin
  306 + ids = []
  307 + ids << UserTeam.with_member(self).pluck('user_teams.id')
  308 + ids << UserTeam.created_by(self).pluck('user_teams.id')
  309 + ids.flatten
  310 +
  311 + UserTeam.where(id: ids)
  312 + end
  313 + end
286 end 314 end
app/models/user_team.rb 0 → 100644
@@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
  1 +class UserTeam < ActiveRecord::Base
  2 + attr_accessible :name, :owner_id, :path
  3 +
  4 + belongs_to :owner, class_name: User
  5 +
  6 + has_many :user_team_project_relationships, dependent: :destroy
  7 + has_many :user_team_user_relationships, dependent: :destroy
  8 +
  9 + has_many :projects, through: :user_team_project_relationships
  10 + has_many :members, through: :user_team_user_relationships, source: :user
  11 +
  12 + validates :name, presence: true, uniqueness: true
  13 + validates :owner, presence: true
  14 + validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
  15 + format: { with: Gitlab::Regex.path_regex,
  16 + message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
  17 +
  18 + scope :with_member, ->(user){ joins(:user_team_user_relationships).where(user_team_user_relationships: {user_id: user.id}) }
  19 + scope :with_project, ->(project){ joins(:user_team_project_relationships).where(user_team_project_relationships: {project_id: project})}
  20 + scope :without_project, ->(project){ where("user_teams.id NOT IN (:ids)", ids: (a = with_project(project); a.blank? ? 0 : a))}
  21 + scope :created_by, ->(user){ where(owner_id: user) }
  22 +
  23 + class << self
  24 + def search query
  25 + where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
  26 + end
  27 +
  28 + def global_id
  29 + 'GLN'
  30 + end
  31 +
  32 + def access_roles
  33 + UsersProject.access_roles
  34 + end
  35 + end
  36 +
  37 + def to_param
  38 + path
  39 + end
  40 +
  41 + def assign_to_projects(projects, access)
  42 + projects.each do |project|
  43 + assign_to_project(project, access)
  44 + end
  45 + end
  46 +
  47 + def assign_to_project(project, access)
  48 + Gitlab::UserTeamManager.assign(self, project, access)
  49 + end
  50 +
  51 + def resign_from_project(project)
  52 + Gitlab::UserTeamManager.resign(self, project)
  53 + end
  54 +
  55 + def add_members(users, access, group_admin)
  56 + users.each do |user|
  57 + add_member(user, access, group_admin)
  58 + end
  59 + end
  60 +
  61 + def add_member(user, access, group_admin)
  62 + Gitlab::UserTeamManager.add_member_into_team(self, user, access, group_admin)
  63 + end
  64 +
  65 + def remove_member(user)
  66 + Gitlab::UserTeamManager.remove_member_from_team(self, user)
  67 + end
  68 +
  69 + def update_membership(user, options)
  70 + Gitlab::UserTeamManager.update_team_user_membership(self, user, options)
  71 + end
  72 +
  73 + def update_project_access(project, permission)
  74 + Gitlab::UserTeamManager.update_project_greates_access(self, project, permission)
  75 + end
  76 +
  77 + def max_project_access(project)
  78 + user_team_project_relationships.find_by_project_id(project).greatest_access
  79 + end
  80 +
  81 + def human_max_project_access(project)
  82 + self.class.access_roles.invert[max_project_access(project)]
  83 + end
  84 +
  85 + def default_projects_access(member)
  86 + user_team_user_relationships.find_by_user_id(member).permission
  87 + end
  88 +
  89 + def human_default_projects_access(member)
  90 + self.class.access_roles.invert[default_projects_access(member)]
  91 + end
  92 +
  93 + def admin?(member)
  94 + user_team_user_relationships.with_user(member).first.group_admin?
  95 + end
  96 +
  97 +end
app/models/user_team_project_relationship.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +class UserTeamProjectRelationship < ActiveRecord::Base
  2 + attr_accessible :greatest_access, :project_id, :user_team_id
  3 +
  4 + belongs_to :user_team
  5 + belongs_to :project
  6 +
  7 + validates :project, presence: true
  8 + validates :user_team, presence: true
  9 + validate :check_greatest_access
  10 +
  11 + scope :with_project, ->(project){ where(project_id: project.id) }
  12 +
  13 + def team_name
  14 + user_team.name
  15 + end
  16 +
  17 + private
  18 +
  19 + def check_greatest_access
  20 + errors.add(:base, :incorrect_access_code) unless correct_access?
  21 + end
  22 +
  23 + def correct_access?
  24 + return false if greatest_access.blank?
  25 + return true if UsersProject.access_roles.has_value?(greatest_access)
  26 + false
  27 + end
  28 +end
app/models/user_team_user_relationship.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +class UserTeamUserRelationship < ActiveRecord::Base
  2 + attr_accessible :group_admin, :permission, :user_id, :user_team_id
  3 +
  4 + belongs_to :user_team
  5 + belongs_to :user
  6 +
  7 + validates :user_team, presence: true
  8 + validates :user, presence: true
  9 +
  10 + scope :with_user, ->(user) { where(user_id: user.id) }
  11 +
  12 + def user_name
  13 + user.name
  14 + end
  15 +
  16 + def access_human
  17 + UsersProject.access_roles.invert[permission]
  18 + end
  19 +end
app/models/users_project.rb
@@ -39,7 +39,10 @@ class UsersProject &lt; ActiveRecord::Base @@ -39,7 +39,10 @@ class UsersProject &lt; ActiveRecord::Base
39 scope :reporters, where(project_access: REPORTER) 39 scope :reporters, where(project_access: REPORTER)
40 scope :developers, where(project_access: DEVELOPER) 40 scope :developers, where(project_access: DEVELOPER)
41 scope :masters, where(project_access: MASTER) 41 scope :masters, where(project_access: MASTER)
  42 +
42 scope :in_project, ->(project) { where(project_id: project.id) } 43 scope :in_project, ->(project) { where(project_id: project.id) }
  44 + scope :in_projects, ->(projects) { where(project_id: project_ids) }
  45 + scope :with_user, ->(user) { where(user_id: user.id) }
43 46
44 class << self 47 class << self
45 48
app/models/web_hook.rb
@@ -34,4 +34,8 @@ class WebHook &lt; ActiveRecord::Base @@ -34,4 +34,8 @@ class WebHook &lt; ActiveRecord::Base
34 basic_auth: {username: parsed_url.user, password: parsed_url.password}) 34 basic_auth: {username: parsed_url.user, password: parsed_url.password})
35 end 35 end
36 end 36 end
  37 +
  38 + def async_execute(data)
  39 + Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data)
  40 + end
37 end 41 end
app/views/admin/groups/show.html.haml
@@ -72,16 +72,17 @@ @@ -72,16 +72,17 @@
72 %th Users 72 %th Users
73 %th Project Access: 73 %th Project Access:
74 74
75 - - @group.users.each do |u|  
76 - %tr{class: "user_#{u.id}"}  
77 - %td.name= link_to u.name, admin_user_path(u) 75 + - @group.users.each do |user|
  76 + - next unless user
  77 + %tr{class: "user_#{user.id}"}
  78 + %td.name= link_to user.name, admin_user_path(user)
78 %td.projects_access 79 %td.projects_access
79 - - u.authorized_projects.in_namespace(@group).each do |project|  
80 - - u_p = u.users_projects.in_project(project).first 80 + - user.authorized_projects.in_namespace(@group).each do |project|
  81 + - u_p = user.users_projects.in_project(project).first
81 - next unless u_p 82 - next unless u_p
82 %span 83 %span
83 - = project.name  
84 - = link_to "(#{ u_p.project_access_human })", edit_admin_team_member_path(u_p) 84 + = project.name_with_namespace
  85 + = link_to "(#{ u_p.project_access_human })", edit_admin_project_member_path(project, user)
85 %tr 86 %tr
86 %td.input= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' 87 %td.input= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
87 %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"} 88 %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
app/views/admin/projects/members/_form.html.haml 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 += form_for @team_member_relation, as: :team_member, url: admin_project_member_path(@project, @member) do |f|
  2 + -if @team_member_relation.errors.any?
  3 + .alert-message.block-message.error
  4 + %ul
  5 + - @team_member_relation.errors.full_messages.each do |msg|
  6 + %li= msg
  7 +
  8 + .clearfix
  9 + %label Project Access:
  10 + .input
  11 + = f.select :project_access, options_for_select(Project.access_options, @team_member_relation.project_access), {}, class: "project-access-select chosen span3"
  12 +
  13 + %br
  14 + .actions
  15 + = f.submit 'Save', class: "btn primary"
  16 + = link_to 'Cancel', :back, class: "btn"
app/views/admin/projects/members/edit.html.haml 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +%p.slead
  2 + Edit access for
  3 + = link_to @member.name, admin_user_path(@member)
  4 + in
  5 + = link_to @project.name_with_namespace, admin_project_path(@project)
  6 +
  7 +%hr
  8 += render 'form'
app/views/admin/projects/show.html.haml
@@ -114,9 +114,9 @@ @@ -114,9 +114,9 @@
114 %h5 114 %h5
115 Team 115 Team
116 %small 116 %small
117 - (#{@project.users_projects.count}) 117 + (#{@project.users.count})
118 %br 118 %br
119 -%table.zebra-striped 119 +%table.zebra-striped.team_members
120 %thead 120 %thead
121 %tr 121 %tr
122 %th Name 122 %th Name
@@ -124,13 +124,13 @@ @@ -124,13 +124,13 @@
124 %th Repository Access 124 %th Repository Access
125 %th 125 %th
126 126
127 - - @project.users_projects.each do |tm| 127 + - @project.users.each do |tm|
128 %tr 128 %tr
129 %td 129 %td
130 - = link_to tm.user_name, admin_user_path(tm.user)  
131 - %td= tm.project_access_human  
132 - %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"  
133 - %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small" 130 + = link_to tm.name, admin_user_path(tm)
  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"
134 134
135 %br 135 %br
136 %h5 Add new team member 136 %h5 Add new team member
app/views/admin/team_members/_form.html.haml
@@ -1,16 +0,0 @@ @@ -1,16 +0,0 @@
1 -= form_for @admin_team_member, as: :team_member, url: admin_team_member_path(@admin_team_member) do |f|  
2 - -if @admin_team_member.errors.any?  
3 - .alert-message.block-message.error  
4 - %ul  
5 - - @admin_team_member.errors.full_messages.each do |msg|  
6 - %li= msg  
7 -  
8 - .clearfix  
9 - %label Project Access:  
10 - .input  
11 - = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3"  
12 -  
13 - %br  
14 - .actions  
15 - = f.submit 'Save', class: "btn primary"  
16 - = link_to 'Cancel', :back, class: "btn"  
app/views/admin/team_members/edit.html.haml
@@ -1,8 +0,0 @@ @@ -1,8 +0,0 @@
1 -%p.slead  
2 - Edit access for  
3 - = link_to @admin_team_member.user_name, admin_user_path(@admin_team_member)  
4 - in  
5 - = link_to @admin_team_member.project.name_with_namespace, admin_project_path(@admin_team_member)  
6 -  
7 -%hr  
8 -= render 'form'  
app/views/admin/teams/edit.html.haml 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +%h3.page_title Rename Team
  2 +%hr
  3 += form_for @team, url: admin_team_path(@team), method: :put do |f|
  4 + - if @team.errors.any?
  5 + .alert-message.block-message.error
  6 + %span= @team.errors.full_messages.first
  7 + .clearfix.team_name_holder
  8 + = f.label :name do
  9 + Team name is
  10 + .input
  11 + = f.text_field :name, placeholder: "Example Team", class: "xxlarge"
  12 +
  13 + .clearfix.team_name_holder
  14 + = f.label :path do
  15 + %span.cred Team path is
  16 + .input
  17 + = f.text_field :path, placeholder: "example-team", class: "xxlarge danger"
  18 + %ul.cred
  19 + %li It will change web url for access team and team projects.
  20 +
  21 + .form-actions
  22 + = f.submit 'Rename team', class: "btn danger"
  23 + = link_to 'Cancel', admin_teams_path, class: "btn cancel-btn"
app/views/admin/teams/index.html.haml 0 → 100644
@@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
  1 +%h3.page_title
  2 + Teams
  3 + %small
  4 + simple Teams description
  5 +
  6 + = link_to 'New Team', new_admin_team_path, class: "btn small right"
  7 + %br
  8 +
  9 += form_tag admin_teams_path, method: :get, class: 'form-inline' do
  10 + = text_field_tag :name, params[:name], class: "xlarge"
  11 + = submit_tag "Search", class: "btn submit primary"
  12 +
  13 +%table
  14 + %thead
  15 + %tr
  16 + %th
  17 + Name
  18 + %i.icon-sort-down
  19 + %th Path
  20 + %th Projects
  21 + %th Members
  22 + %th Owner
  23 + %th.cred Danger Zone!
  24 +
  25 + - @teams.each do |team|
  26 + %tr
  27 + %td
  28 + %strong= link_to team.name, admin_team_path(team)
  29 + %td= team.path
  30 + %td= team.projects.count
  31 + %td= team.members.count
  32 + %td
  33 + = link_to team.owner.name, admin_user_path(team.owner_id)
  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"
  37 +
  38 += paginate @teams, theme: "admin"
app/views/admin/teams/members/_form.html.haml 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 += form_tag admin_team_member_path(@team, @member), method: :put do
  2 + -if @member.errors.any?
  3 + .alert-message.block-message.error
  4 + %ul
  5 + - @member.errors.full_messages.each do |msg|
  6 + %li= msg
  7 +
  8 + .clearfix
  9 + %label Default access for Team projects:
  10 + .input
  11 + = select_tag :default_project_access, options_for_select(UserTeam.access_roles, @team.default_projects_access(@member)), class: "project-access-select chosen span3"
  12 + .clearfix
  13 + %label Team admin?
  14 + .input
  15 + = check_box_tag :group_admin, true, @team.admin?(@member)
  16 +
  17 + %br
  18 + .actions
  19 + = submit_tag 'Save', class: "btn primary"
  20 + = link_to 'Cancel', :back, class: "btn"
app/views/admin/teams/members/edit.html.haml 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +%h3
  2 + Edit access #{@member.name} in #{@team.name} team
  3 +
  4 +%hr
  5 +%table.zebra-striped
  6 + %tr
  7 + %td User:
  8 + %td= @member.name
  9 + %tr
  10 + %td Team:
  11 + %td= @team.name
  12 + %tr
  13 + %td Since:
  14 + %td= member_since(@team, @member).stamp("Nov 11, 2010")
  15 +
  16 += render 'form'
app/views/admin/teams/members/new.html.haml 0 → 100644
@@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
  1 +%h3.page_title
  2 + Team: #{@team.name}
  3 +
  4 +%fieldset
  5 + %legend Members (#{@team.members.count})
  6 + = form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do
  7 + %table#members_list
  8 + %thead
  9 + %tr
  10 + %th User name
  11 + %th Default project access
  12 + %th Team access
  13 + %th
  14 + - @team.members.each do |member|
  15 + %tr.member
  16 + %td
  17 + = link_to [:admin, member] do
  18 + = member.name
  19 + %small= "(#{member.email})"
  20 + %td= @team.human_default_projects_access(member)
  21 + %td= @team.admin?(member) ? "Admin" : "Member"
  22 + %td
  23 + %tr
  24 + %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_email), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
  25 + %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
  26 + %td
  27 + %span= check_box_tag :group_admin
  28 + %span Admin?
  29 + %td= submit_tag 'Add', class: "btn primary", id: :add_members_to_team
app/views/admin/teams/new.html.haml 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +%h3.page_title New Team
  2 +%hr
  3 += form_for @team, url: admin_teams_path do |f|
  4 + - if @team.errors.any?
  5 + .alert-message.block-message.error
  6 + %span= @team.errors.full_messages.first
  7 + .clearfix
  8 + = f.label :name do
  9 + Team name is
  10 + .input
  11 + = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
  12 + &nbsp;
  13 + = f.submit 'Create team', class: "btn primary"
  14 + %hr
  15 + .padded
  16 + %ul
  17 + %li All created teams are public (users can view who enter into team and which project are assigned for this team)
  18 + %li People within a team see only projects they have access to
  19 + %li You will be able to assign existing projects for team
app/views/admin/teams/projects/_form.html.haml 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 += form_tag admin_team_project_path(@team, @project), method: :put do
  2 + -if @project.errors.any?
  3 + .alert-message.block-message.error
  4 + %ul
  5 + - @project.errors.full_messages.each do |msg|
  6 + %li= msg
  7 +
  8 + .clearfix
  9 + %label Max access for Team members:
  10 + .input
  11 + = select_tag :greatest_project_access, options_for_select(UserTeam.access_roles, @team.max_project_access(@project)), class: "project-access-select chosen span3"
  12 +
  13 + %br
  14 + .actions
  15 + = submit_tag 'Save', class: "btn primary"
  16 + = link_to 'Cancel', :back, class: "btn"
app/views/admin/teams/projects/edit.html.haml 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +%h3
  2 + Edit max access in #{@project.name} for #{@team.name} team
  3 +
  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 +
  16 += render 'form'
app/views/admin/teams/projects/new.html.haml 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +%h3.page_title
  2 + Team: #{@team.name}
  3 +
  4 +%fieldset
  5 + %legend Projects (#{@team.projects.count})
  6 + = form_tag admin_team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do
  7 + %table#projects_list
  8 + %thead
  9 + %tr
  10 + %th Project name
  11 + %th Max access
  12 + %th
  13 + - @team.projects.each do |project|
  14 + %tr.project
  15 + %td
  16 + = link_to project.name_with_namespace, [:admin, project]
  17 + %td
  18 + %span= @team.human_max_project_access(project)
  19 + %td
  20 + %tr
  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 + %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
app/views/admin/teams/show.html.haml 0 → 100644
@@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
  1 +%h3.page_title
  2 + Team: #{@team.name}
  3 +
  4 +%br
  5 +%table.zebra-striped
  6 + %thead
  7 + %tr
  8 + %th Team
  9 + %th
  10 + %tr
  11 + %td
  12 + %b
  13 + Name:
  14 + %td
  15 + = @team.name
  16 + &nbsp;
  17 + = link_to edit_admin_team_path(@team), class: "btn btn-small right" do
  18 + %i.icon-edit
  19 + Rename
  20 + %tr
  21 + %td
  22 + %b
  23 + Owner:
  24 + %td
  25 + = @team.owner.name
  26 + .right
  27 + = link_to "#", class: "btn btn-small change-owner-link" do
  28 + %i.icon-edit
  29 + Change owner
  30 +
  31 + %tr.change-owner-holder.hide
  32 + %td.bgred
  33 + %b.cred
  34 + New Owner:
  35 + %td.bgred
  36 + = form_for @team, url: admin_team_path(@team) do |f|
  37 + = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
  38 + %div
  39 + = f.submit 'Change Owner', class: "btn danger"
  40 + = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
  41 +
  42 +%fieldset
  43 + %legend
  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
  46 + - if @team.members.any?
  47 + %table#members_list
  48 + %thead
  49 + %tr
  50 + %th User name
  51 + %th Default project access
  52 + %th Team access
  53 + %th.cred.span3 Danger Zone!
  54 + - @team.members.each do |member|
  55 + %tr.member{ class: "user_#{member.id}"}
  56 + %td
  57 + = link_to [:admin, member] do
  58 + = member.name
  59 + %small= "(#{member.email})"
  60 + %td= @team.human_default_projects_access(member)
  61 + %td= @team.admin?(member) ? "Admin" : "Member"
  62 + %td.bgred
  63 + = link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn small"
  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}"
  66 +
  67 +%fieldset
  68 + %legend
  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
  71 + - if @team.projects.any?
  72 + %table#projects_list
  73 + %thead
  74 + %tr
  75 + %th Project name
  76 + %th Max access
  77 + %th.cred.span3 Danger Zone!
  78 + - @team.projects.each do |project|
  79 + %tr.project
  80 + %td
  81 + = link_to project.name_with_namespace, [:admin, project]
  82 + %td
  83 + %span= @team.human_max_project_access(project)
  84 + %td.bgred
  85 + = link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn small"
  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}"
  88 +
  89 +:javascript
  90 + $(function(){
  91 + var modal = $('.change-owner-holder');
  92 + $('.change-owner-link').bind("click", function(){
  93 + $(this).hide();
  94 + modal.show();
  95 + });
  96 + $('.change-owner-cancel-link').bind("click", function(){
  97 + modal.hide();
  98 + $('.change-owner-link').show();
  99 + })
  100 + })
  101 +
app/views/admin/users/_form.html.haml
@@ -47,6 +47,14 @@ @@ -47,6 +47,14 @@
47 .input= f.number_field :projects_limit 47 .input= f.number_field :projects_limit
48 48
49 .clearfix 49 .clearfix
  50 + = f.label :can_create_group
  51 + .input= f.check_box :can_create_group
  52 +
  53 + .clearfix
  54 + = f.label :can_create_team
  55 + .input= f.check_box :can_create_team
  56 +
  57 + .clearfix
50 = f.label :admin do 58 = f.label :admin do
51 %strong.cred Administrator 59 %strong.cred Administrator
52 .input= f.check_box :admin 60 .input= f.check_box :admin
app/views/admin/users/show.html.haml
@@ -123,5 +123,5 @@ @@ -123,5 +123,5 @@
123 %tr 123 %tr
124 %td= link_to project.name_with_namespace, admin_project_path(project) 124 %td= link_to project.name_with_namespace, admin_project_path(project)
125 %td= tm.project_access_human 125 %td= tm.project_access_human
126 - %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"  
127 - %td= link_to 'Remove from team', admin_team_member_path(tm), 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 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"
app/views/commit/show.html.haml
@@ -11,19 +11,7 @@ @@ -11,19 +11,7 @@
11 11
12 :javascript 12 :javascript
13 $(function(){ 13 $(function(){
14 - var w, h;  
15 - $('.diff_file').each(function(){  
16 - $('.image.diff_removed img', this).on('load', $.proxy(function(event){  
17 - var w = event.currentTarget.naturalWidth  
18 - , h = event.currentTarget.naturalHeight;  
19 - $('.image.diff_removed .image-info', this).append(' | <b>W:</b> ' + w + 'px | <b>H:</b> ' + h + 'px');  
20 - }, this));  
21 - $('.image.diff_added img', this).on('load', $.proxy(function(event){  
22 - var w = event.currentTarget.naturalWidth  
23 - , h = event.currentTarget.naturalHeight;  
24 - $('.image.diff_added .image-info', this).append(' | <b>W:</b> ' + w + 'px | <b>H:</b> ' + h + 'px');  
25 - }, this));  
26 - 14 + $('.files .file').each(function(){
  15 + new CommitFile(this);
27 }); 16 });
28 -  
29 }); 17 });
app/views/commits/_commits.html.haml
@@ -2,5 +2,5 @@ @@ -2,5 +2,5 @@
2 %div.ui-box 2 %div.ui-box
3 %h5.title 3 %h5.title
4 %i.icon-calendar 4 %i.icon-calendar
5 - = day.stamp("28 Aug, 2010") 5 + %span= day.stamp("28 Aug, 2010")
6 %ul.well-list= render commits 6 %ul.well-list= render commits
app/views/commits/_diffs.html.haml
@@ -12,50 +12,38 @@ @@ -12,50 +12,38 @@
12 .file-stats 12 .file-stats
13 = render "commits/diff_head", diffs: diffs 13 = render "commits/diff_head", diffs: diffs
14 14
15 -- unless @suppress_diff  
16 - - diffs.each_with_index do |diff, i|  
17 - - next if diff.diff.empty?  
18 - - file = (@commit.tree / diff.new_path)  
19 - - file = (@commit.prev_commit.tree / diff.old_path) unless file  
20 - - next unless file  
21 - .diff_file{id: "diff-#{i}"}  
22 - .diff_file_header  
23 - - if diff.deleted_file  
24 - %span= diff.old_path 15 +.files
  16 + - unless @suppress_diff
  17 + - diffs.each_with_index do |diff, i|
  18 + - next if diff.diff.empty?
  19 + - file = (@commit.tree / diff.new_path)
  20 + - file = (@commit.prev_commit.tree / diff.old_path) unless file
  21 + - next unless file
  22 + .file{id: "diff-#{i}"}
  23 + .header
  24 + - if diff.deleted_file
  25 + %span= diff.old_path
25 26
26 - - if @commit.prev_commit  
27 - = link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn right view-commit'} do 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
  29 + View file @
  30 + %span.commit-short-id= @commit.short_id(6)
  31 + - else
  32 + %span= diff.new_path
  33 + - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
  34 + %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"
  35 +
  36 + = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn very_small right view-file'} do
28 View file @ 37 View file @
29 %span.commit-short-id= @commit.short_id(6) 38 %span.commit-short-id= @commit.short_id(6)
30 - - else  
31 - %span= diff.new_path  
32 - - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode  
33 - %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"  
34 -  
35 - = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn very_small right view-commit'} do  
36 - View file @  
37 - %span.commit-short-id= @commit.short_id(6)  
38 39
39 - %br/  
40 - .diff_file_content  
41 - -# Skip all non-supported blobs  
42 - - next unless file.respond_to?('text?')  
43 - - if file.text?  
44 - = render "commits/text_diff", diff: diff, index: i  
45 - - elsif file.image?  
46 - - old_file = (@commit.prev_commit.tree / diff.old_path) if !@commit.prev_commit.nil?  
47 - - if diff.renamed_file || diff.new_file || diff.deleted_file  
48 - .diff_file_content_image  
49 - .image{class: image_diff_class(diff)}  
50 - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}  
51 - %div.image-info= "#{number_to_human_size file.size}" 40 + .content
  41 + -# Skipp all non non-supported blobs
  42 + - next unless file.respond_to?('text?')
  43 + - if file.text?
  44 + = render "commits/text_file", diff: diff, index: i
  45 + - elsif file.image?
  46 + - old_file = (@commit.prev_commit.tree / diff.old_path) if !@commit.prev_commit.nil?
  47 + = render "commits/image", diff: diff, old_file: old_file, file: file, index: i
52 - else 48 - else
53 - .diff_file_content_image.img_compared  
54 - .image.diff_removed  
55 - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"}  
56 - %div.image-info= "#{number_to_human_size file.size}"  
57 - .image.diff_added  
58 - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}  
59 - %div.image-info= "#{number_to_human_size file.size}"  
60 - - else  
61 - %p.nothing_here_message No preview for this file type 49 + %p.nothing_here_message No preview for this file type
app/views/commits/_image.html.haml 0 → 100644
@@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
  1 +- if diff.renamed_file || diff.new_file || diff.deleted_file
  2 + .image
  3 + %span.wrap
  4 + .frame{class: image_diff_class(diff)}
  5 + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
  6 + %p.image-info= "#{number_to_human_size file.size}"
  7 +- else
  8 + .image
  9 + %div.two-up.view
  10 + %span.wrap
  11 + .frame.deleted
  12 + %a{href: project_tree_path(@project, tree_join(@commit.id, diff.old_path))}
  13 + %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
  14 + %p.image-info.hide
  15 + %span.meta-filesize= "#{number_to_human_size old_file.size}"
  16 + |
  17 + %b W:
  18 + %span.meta-width
  19 + |
  20 + %b H:
  21 + %span.meta-height
  22 + %span.wrap
  23 + .frame.added
  24 + %a{href: project_tree_path(@project, tree_join(@commit.id, diff.new_path))}
  25 + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
  26 + %p.image-info.hide
  27 + %span.meta-filesize= "#{number_to_human_size file.size}"
  28 + |
  29 + %b W:
  30 + %span.meta-width
  31 + |
  32 + %b H:
  33 + %span.meta-height
  34 +
  35 + %div.swipe.view.hide
  36 + .swipe-frame
  37 + .frame.deleted
  38 + %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
  39 + .swipe-wrap
  40 + .frame.added
  41 + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
  42 + %span.swipe-bar
  43 + %span.top-handle
  44 + %span.bottom-handle
  45 +
  46 + %div.onion-skin.view.hide
  47 + .onion-skin-frame
  48 + .frame.deleted
  49 + %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
  50 + .frame.added
  51 + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
  52 + .controls
  53 + .transparent
  54 + .drag-track
  55 + .dragger{:style => "left: 0px;"}
  56 + .opaque
  57 +
  58 +
  59 + .view-modes.hide
  60 + %ul.view-modes-menu
  61 + %li.two-up{data: {mode: 'two-up'}} 2-up
  62 + %li.swipe{data: {mode: 'swipe'}} Swipe
  63 + %li.onion-skin{data: {mode: 'onion-skin'}} Onion skin
0 \ No newline at end of file 64 \ No newline at end of file
app/views/commits/_text_diff.html.haml
@@ -1,23 +0,0 @@ @@ -1,23 +0,0 @@
1 -- too_big = diff.diff.lines.count > 1000  
2 -- if too_big  
3 - %a.supp_diff_link Diff suppressed. Click to show  
4 -  
5 -%table{class: "#{'hide' if too_big}"}  
6 - - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old|  
7 - %tr.line_holder{ id: line_code }  
8 - - if type == "match"  
9 - %td.old_line= "..."  
10 - %td.new_line= "..."  
11 - %td.line_content.matched= line  
12 - - else  
13 - %td.old_line  
14 - = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code  
15 - - if @comments_allowed  
16 - = render "notes/diff_note_link", line_code: line_code  
17 - %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code  
18 - %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)  
19 -  
20 - - if @reply_allowed  
21 - - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)  
22 - - unless comments.empty?  
23 - = render "notes/diff_notes_with_reply", notes: comments  
app/views/commits/_text_file.html.haml 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +- too_big = diff.diff.lines.count > 1000
  2 +- if too_big
  3 + %a.supp_diff_link Diff suppressed. Click to show
  4 +
  5 +%table.text-file{class: "#{'hide' if too_big}"}
  6 + - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old|
  7 + %tr.line_holder{ id: line_code }
  8 + - if type == "match"
  9 + %td.old_line= "..."
  10 + %td.new_line= "..."
  11 + %td.line_content.matched= line
  12 + - else
  13 + %td.old_line
  14 + = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
  15 + - if @comments_allowed
  16 + = render "notes/diff_note_link", line_code: line_code
  17 + %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
  18 + %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)
  19 +
  20 + - if @reply_allowed
  21 + - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
  22 + - unless comments.empty?
  23 + = render "notes/diff_notes_with_reply", notes: comments
app/views/commits/show.html.haml
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 = breadcrumbs 5 = breadcrumbs
6 6
7 %div{id: dom_id(@project)} 7 %div{id: dom_id(@project)}
8 - #commits_list= render "commits" 8 + #commits-list= render "commits"
9 .clear 9 .clear
10 .loading{ style: "display:none;"} 10 .loading{ style: "display:none;"}
11 11
app/views/dashboard/_groups.html.haml
1 -.groups_box 1 +.ui-box
2 %h5.title 2 %h5.title
3 Groups 3 Groups
4 %small 4 %small
5 (#{groups.count}) 5 (#{groups.count})
6 - if current_user.can_create_group? 6 - if current_user.can_create_group?
7 %span.right 7 %span.right
8 - = link_to new_admin_group_path, class: "btn very_small info" do 8 + = link_to new_group_path, class: "btn very_small info" do
9 %i.icon-plus 9 %i.icon-plus
10 New Group 10 New Group
11 %ul.well-list 11 %ul.well-list
@@ -13,8 +13,6 @@ @@ -13,8 +13,6 @@
13 %li 13 %li
14 = link_to group_path(id: group.path), class: dom_class(group) do 14 = link_to group_path(id: group.path), class: dom_class(group) do
15 %strong.well-title= truncate(group.name, length: 35) 15 %strong.well-title= truncate(group.name, length: 35)
16 - %span.arrow  
17 - &rarr;  
18 - %span.last_activity  
19 - %strong Projects:  
20 - %span= current_user.authorized_projects.where(namespace_id: group.id).count 16 + %span.right.light
  17 + - if group.owner == current_user
  18 + %i.icon-wrench
app/views/dashboard/_projects.html.haml
@@ -2,19 +2,12 @@ @@ -2,19 +2,12 @@
2 %h5.title 2 %h5.title
3 Projects 3 Projects
4 %small 4 %small
5 - (#{projects.total_count}) 5 + (#{@projects_count})
6 - if current_user.can_create_project? 6 - if current_user.can_create_project?
7 %span.right 7 %span.right
8 = link_to new_project_path, class: "btn very_small info" do 8 = link_to new_project_path, class: "btn very_small info" do
9 %i.icon-plus 9 %i.icon-plus
10 New Project 10 New Project
11 - %ul.nav.nav-projects-tabs  
12 - = nav_tab :scope, nil do  
13 - = link_to "All", dashboard_path  
14 - = nav_tab :scope, 'personal' do  
15 - = link_to "Personal", dashboard_path(scope: 'personal')  
16 - = nav_tab :scope, 'joined' do  
17 - = link_to "Joined", dashboard_path(scope: 'joined')  
18 11
19 %ul.well-list 12 %ul.well-list
20 - projects.each do |project| 13 - projects.each do |project|
@@ -33,4 +26,6 @@ @@ -33,4 +26,6 @@
33 - if projects.blank? 26 - if projects.blank?
34 %li 27 %li
35 %h3.nothing_here_message There are no projects here. 28 %h3.nothing_here_message There are no projects here.
36 - .bottom= paginate projects, theme: "gitlab" 29 + - if @projects_count > 20
  30 + %li.bottom
  31 + %strong= link_to "show all projects", projects_dashboard_path
app/views/dashboard/_sidebar.html.haml
  1 +- if @teams.present?
  2 + = render "teams", teams: @teams
1 - if @groups.present? 3 - if @groups.present?
2 = render "groups", groups: @groups 4 = render "groups", groups: @groups
3 = render "projects", projects: @projects 5 = render "projects", projects: @projects
app/views/dashboard/_teams.html.haml 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +.ui-box.teams-box
  2 + %h5.title
  3 + Teams
  4 + %small
  5 + (#{@teams.count})
  6 + %span.right
  7 + = link_to new_team_path, class: "btn very_small info" do
  8 + %i.icon-plus
  9 + New Team
  10 + %ul.well-list
  11 + - @teams.each do |team|
  12 + %li
  13 + = link_to team_path(id: team.path), class: dom_class(team) do
  14 + %strong.well-title= truncate(team.name, length: 35)
  15 + %span.right.light
  16 + - if team.owner == current_user
  17 + %i.icon-wrench
  18 + - tm = current_user.user_team_user_relationships.find_by_user_team_id(team.id)
  19 + - if tm
  20 + = tm.access_human
app/views/dashboard/index.atom.builder
@@ -1,30 +0,0 @@ @@ -1,30 +0,0 @@
1 -xml.instruct!  
2 -xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do  
3 - xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}"  
4 - xml.link :href => projects_url(:atom), :rel => "self", :type => "application/atom+xml"  
5 - xml.link :href => projects_url, :rel => "alternate", :type => "text/html"  
6 - xml.id projects_url  
7 - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?  
8 -  
9 - @events.each do |event|  
10 - if event.proper?  
11 - event = EventDecorator.decorate(event)  
12 - xml.entry do  
13 - event_link = event.feed_url  
14 - event_title = event.feed_title  
15 - event_summary = event.feed_summary  
16 -  
17 - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"  
18 - xml.link :href => event_link  
19 - xml.title truncate(event_title, :length => 80)  
20 - xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")  
21 - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email)  
22 - xml.author do |author|  
23 - xml.name event.author_name  
24 - xml.email event.author_email  
25 - end  
26 - xml.summary(:type => "xhtml") { |x| x << event_summary unless event_summary.nil? }  
27 - end  
28 - end  
29 - end  
30 -end  
app/views/dashboard/index.html.haml
@@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
1 -- if @has_authorized_projects  
2 - .projects  
3 - .activities.span8  
4 - = render 'activities'  
5 - .side.span4  
6 - = render 'sidebar'  
7 -  
8 -- else  
9 - = render "zero_authorized_projects"  
10 -  
11 -:javascript  
12 - dashboardPage();  
app/views/dashboard/index.js.haml
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -:plain  
2 - Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");